Client examples
Two complete clients that connect, parse a Bell circuit, execute it, and
print the measurement histogram. Both implement the one pattern every
kernel client needs: drain messages until the terminal type arrives,
relaying any interleaved output/stderr/error along the way (see
the streaming model). For
scripted hardware_submit loops built on the same pattern, see
pipelines.
Start a kernel first:
cd kernel && python server.py# Nuclei kernel ready on ws://localhost:9742Requires pip install websockets. This exact file is executed by the kernel
test suite against a live server on every CI run
(kernel/tests/test_docs_fixtures.py), so it is guaranteed to speak the
current protocol.
#!/usr/bin/env python3"""Minimal Nuclei kernel client: parse a Bell circuit, run it, print counts.
Usage: python example_client.py [ws://localhost:9742]Requires: pip install websockets
This file is replay-tested: kernel/tests/test_docs_fixtures.py runs it as asubprocess against a live kernel server and asserts the histogram output."""import asyncioimport jsonimport sys
import websockets
BELL = """from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 2)qc.h(0)qc.cx(0, 1)qc.measure([0, 1], [0, 1])"""
async def drain_until(ws, terminal_type: str) -> dict: """Read messages until `terminal_type` arrives, relaying streamed text.
Every request produces an ordered SEQUENCE of messages — output/stderr may interleave before the terminal message, so a robust client loops. """ while True: message = json.loads(await ws.recv()) if message["type"] == "output": print(message["text"], end="") elif message["type"] == "stderr": print(message["text"], end="", file=sys.stderr) elif message["type"] == "error": print(f"kernel error: {message['message']}", file=sys.stderr) if message["type"] == terminal_type: return message
async def main(url: str) -> int: async with websockets.connect(url) as ws: await ws.send(json.dumps({"type": "parse", "code": BELL, "language": "python"})) snapshot = await drain_until(ws, "snapshot") if snapshot["data"] is None: return 1 print(f"circuit: {len(snapshot['data']['gates'])} gates, " f"depth {snapshot['data']['depth']}")
await ws.send(json.dumps( {"type": "execute", "code": BELL, "shots": 512, "language": "python"} )) result = await drain_until(ws, "result") if result["data"] is None: # an error message already explained why return 1 for bitstring, count in sorted(result["data"]["measurements"].items()): print(f"{bitstring}: {count}") return 0
if __name__ == "__main__": url = sys.argv[1] if len(sys.argv) > 1 else "ws://localhost:9742" sys.exit(asyncio.run(main(url)))python example_client.py # default ws://localhost:9742python example_client.py ws://localhost:9743Requires the ws package (npm install ws); run directly with
tsx — no build step. This client mirrors the Python one,
which is the replay-tested variant.
// Minimal Nuclei kernel client: parse a Bell circuit, run it, print counts.//// Run with: npx tsx example_client.ts [ws://localhost:9742]// Requires the `ws` package: npm install ws// (This file is documentation — it mirrors example_client.py, which is the// variant exercised by the kernel test suite.)import WebSocket from 'ws';
const BELL = `from qiskit import QuantumCircuit
qc = QuantumCircuit(2, 2)qc.h(0)qc.cx(0, 1)qc.measure([0, 1], [0, 1])`;
const url = process.argv[2] ?? 'ws://localhost:9742';const ws = new WebSocket(url);
type KernelMessage = { type: string; [key: string]: unknown };const queue: KernelMessage[] = [];let wake: (() => void) | null = null;
ws.on('message', (raw) => { queue.push(JSON.parse(raw.toString()) as KernelMessage); wake?.();});
// Every request produces an ordered SEQUENCE of messages — output/stderr// may interleave before the terminal message, so a robust client loops.async function drainUntil(terminalType: string): Promise<KernelMessage> { for (;;) { while (queue.length === 0) { await new Promise<void>((resolve) => { wake = resolve; }); } const message = queue.shift()!; if (message.type === 'output') process.stdout.write(message.text as string); if (message.type === 'stderr') process.stderr.write(message.text as string); if (message.type === 'error') console.error(`kernel error: ${message.message}`); if (message.type === terminalType) return message; }}
ws.on('open', async () => { ws.send(JSON.stringify({ type: 'parse', code: BELL, language: 'python' })); const snapshot = await drainUntil('snapshot'); const data = snapshot.data as { gates: unknown[]; depth: number } | null; if (!data) process.exit(1); console.log(`circuit: ${data.gates.length} gates, depth ${data.depth}`);
ws.send(JSON.stringify({ type: 'execute', code: BELL, shots: 512, language: 'python' })); const result = await drainUntil('result'); const sim = result.data as { measurements: Record<string, number> } | null; if (!sim) process.exit(1); for (const [bitstring, count] of Object.entries(sim.measurements).sort()) { console.log(`${bitstring}: ${count}`); } ws.close();});npx tsx example_client.ts # default ws://localhost:9742Expected output for either client (counts vary run to run):
circuit: 4 gates, depth 300: 25111: 261One-off requests from the shell
Section titled “One-off requests from the shell”For poking at the protocol without writing code,
websocat works well with the fixture files
from docs-site/fixtures/ — every documented request is sitting there as
ready-to-send JSON:
# jq -c compacts the pretty-printed fixture to one line (websocat sends one# frame per input line); --no-close keeps the connection open after stdin# EOF so the response frames have time to arrive.jq -c . docs-site/fixtures/run_python_hello.request.json | websocat --no-close ws://localhost:9742Each response frame prints on its own line; exit with Ctrl-C.