Ahmed Samir Nagy Mohammed commited on
Commit
18d21ca
·
0 Parent(s):
Files changed (5) hide show
  1. README.md +19 -0
  2. main.py +88 -0
  3. mock_k2_api.py +49 -0
  4. quyaml_parser.py +70 -0
  5. requirements.txt +6 -0
README.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: QThink Agentic POC
3
+ emoji: ⚛️
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: fastapi
7
+ app_file: main.py
8
+ ---
9
+
10
+ # QThink Agentic Proof-of-Concept API
11
+
12
+ This repository contains the proof-of-concept (POC) API for the QThink hackathon project. This service is intended to be submitted as our "Inference Endpoint" link.
13
+
14
+ It demonstrates the two core pillars of our project:
15
+
16
+ 1. **`/tools/parse-quyaml-to-qasm`**: A tool endpoint that our agent uses. It accepts our novel, token-efficient QuYAML format and returns valid QASM 2.0. This proves our custom parser works.
17
+ 2. **`/solve/agentic-trace`**: The **main demo endpoint**. This simulates our full agentic MCP (Multi-Context Protocol) workflow. It takes a user prompt and returns a complete "trace" of the conversation between our (mocked) Planner, Reasoner, and Verifier agents as they collaborate to solve the problem.
18
+
19
+ This POC proves our architecture is sound and ready for the real K2 Think API in Stage 2. See the `/docs` endpoint for a full FastAPI interactive demo.
main.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Body, HTTPException
2
+ from pydantic import BaseModel, Field
3
+ from typing import List, Dict, Any
4
+ import time
5
+
6
+ from mock_k2_api import call_mock_planner, call_mock_reasoner, call_mock_verifier
7
+ from quyaml_parser import parse_quyaml_to_qiskit
8
+ from qiskit.qasm2 import dumps as to_qasm2_str
9
+
10
+ app = FastAPI(
11
+ title="QThink POC Demo - Agentic Workflow",
12
+ description="Proof-of-concept for the QThink Hackathon Project. This API demonstrates the full agentic MCP workflow.",
13
+ version="0.2.0"
14
+ )
15
+
16
+ # --- Pydantic Models ---
17
+ class SimulationPrompt(BaseModel):
18
+ prompt: str
19
+
20
+ class AgentStep(BaseModel):
21
+ agent: str
22
+ thought: str
23
+ output: Any = Field(..., description="The output of the agent, can be a string, list, or dict.")
24
+
25
+ # --- Endpoints ---
26
+ @app.post("/tools/parse-quyaml-to-qasm")
27
+ async def parse_quyaml_endpoint(quyaml_body: str = Body(..., media_type="application/yaml",
28
+ example="# QYAML v0.1: Bell State...")):
29
+ """
30
+ **TOOL ENDPOINT:** Proves the QuYAML parser (SDS) is realistic.
31
+ An agent would call this tool to convert QuYAML to QASM.
32
+ """
33
+ try:
34
+ quantum_circuit = parse_quyaml_to_qiskit(quyaml_body)
35
+ qasm_output = to_qasm2_str(quantum_circuit)
36
+ return {
37
+ "status": "success",
38
+ "qasm_2_0_output": qasm_output,
39
+ "text_diagram": str(quantum_circuit)
40
+ }
41
+ except Exception as e:
42
+ raise HTTPException(status_code=400, detail=f"Error parsing QuYAML: {str(e)}")
43
+
44
+ @app.post("/solve/agentic-trace", response_model=Dict[str, Any])
45
+ async def agentic_trace_endpoint(body: SimulationPrompt):
46
+ """
47
+ **MAIN ENDPOINT: Proves the Agentic MCP (SRS) is sound.**
48
+
49
+ This endpoint simulates the full Orchestrator workflow, showing the
50
+ step-by-step 'trace' of agents (Planner, Reasoner, Verifier)
51
+ collaborating to solve the user's prompt.
52
+ """
53
+ start_time = time.time()
54
+ agent_trace: List[AgentStep] = []
55
+
56
+ try:
57
+ # --- Orchestrator: Step 1 - Call Planner Agent ---
58
+ agent_trace.append(AgentStep(agent="Orchestrator", thought="Prompt received. Sending to Planner Agent.", output=f"Prompt: {body.prompt}"))
59
+ plan = call_mock_planner(body.prompt)
60
+ agent_trace.append(AgentStep(agent="Planner", thought="I have analyzed the prompt and created a step-by-step plan.", output=plan))
61
+
62
+ # --- Orchestrator: Step 2 - Call Reasoner Agent in a loop ---
63
+ reasoner_results = []
64
+ for task in plan:
65
+ agent_trace.append(AgentStep(agent="Orchestrator", thought=f"Dispatching task to Reasoner Agent: {task}", output=""))
66
+ time.sleep(0.1) # Simulate network latency
67
+ result = call_mock_reasoner(task)
68
+ reasoner_results.append({"task": task, "result": result})
69
+ agent_trace.append(AgentStep(agent="Reasoner", thought=f"I have completed task: {task}", output=result))
70
+
71
+ # --- Orchestrator: Step 3 - Call Verifier Agent ---
72
+ agent_trace.append(AgentStep(agent="Orchestrator", thought="All reasoning steps complete. Sending full results to Verifier Agent.", output=reasoner_results))
73
+ verification = call_mock_verifier(agent_trace)
74
+ agent_trace.append(AgentStep(agent="Verifier", thought="I have analyzed the full trace for correctness.", output=verification))
75
+
76
+ end_time = time.time()
77
+
78
+ return {
79
+ "status": "success",
80
+ "total_time_seconds": round(end_time - start_time, 2),
81
+ "agent_trace": [step.dict() for step in agent_trace]
82
+ }
83
+ except Exception as e:
84
+ raise HTTPException(status_code=500, detail=str(e))
85
+
86
+ @app.get("/")
87
+ async def root():
88
+ return {"message": "Welcome to the QThink Agentic POC API. See the /docs for interactive endpoints."}
mock_k2_api.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file simulates the K2 Think API for each agent in the MCP.
2
+
3
+ MOCK_QFT_PLAN = [
4
+ "Task 1: Define initial 8D state vector for |101⟩.",
5
+ "Task 2: Apply Hadamard on qubit 0.",
6
+ "Task 3: Apply CPHASE(pi/2) from q[1] to q[0].",
7
+ "Task 4: Apply CPHASE(pi/4) from q[2] to q[0].",
8
+ "Task 5: Apply Hadamard on qubit 1.",
9
+ "Task 6: Apply CPHASE(pi/2) from q[2] to q[1].",
10
+ "Task 7: Apply Hadamard on qubit 2.",
11
+ "Task 8: Apply SWAP(0, 2).",
12
+ "Task 9: Consolidate and present final state vector."
13
+ ]
14
+
15
+ MOCK_QFT_STEP_RESULTS = {
16
+ "Task 1": "Initial state |101⟩ is index 5. Vector: [0, 0, 0, 0, 0, 1, 0, 0]",
17
+ "Task 2": "Applying H(0). State becomes (1/sqrt(2)) * [0, 0, 0, 0, 1, -1, 0, 0]",
18
+ "Task 3": "Applying CPHASE(pi/2). Control q[1] is 0 for all non-zero states. Vector is unchanged.",
19
+ "Task 4": "Applying CPHASE(pi/4). Control q[2]=1, Target q[0]=1 for index 5. Phase e^(i*pi/4) is applied.",
20
+ "Task 5": "Applying H(1). Amplitudes for indices 4,5,6,7 are calculated.",
21
+ "Task 6": "Applying CPHASE(pi/2). Phase 'i' applied to indices 6 and 7.",
22
+ "Task 7": "Applying H(2). Final amplitudes calculated across all 8 indices.",
23
+ "Task 8": "Applying SWAP(0, 2). Indices [1,4], [3,6] are permuted.",
24
+ "Task 9": "Final state vector consolidated. Ready for verification."
25
+ }
26
+
27
+ MOCK_QFT_VERIFICATION = {
28
+ "status": "Verified",
29
+ "steps_checked": 9,
30
+ "mathematical_consistency": "High",
31
+ "final_state_hash": "a3f4b01e2c5d6f7a... (mock hash)",
32
+ "final_state_preview": "[0.353, -0.353, 0.353i, -0.353i, -0.25-0.25i, 0.25+0.25i, ...]"
33
+ }
34
+
35
+ def call_mock_planner(prompt: str) -> list:
36
+ """Simulates the Planner Agent."""
37
+ if "QFT" in prompt and "|101⟩" in prompt:
38
+ return MOCK_QFT_PLAN
39
+ return ["Task 1: Understand user prompt.", "Task 2: Formulate generic plan."]
40
+
41
+ def call_mock_reasoner(task: str) -> str:
42
+ """Simulates the Reasoner Agent executing one task."""
43
+ return MOCK_QFT_STEP_RESULTS.get(task, f"Mock execution result for: {task}")
44
+
45
+ def call_mock_verifier(full_trace: list) -> dict:
46
+ """Simulates the Verifier Agent checking the whole process."""
47
+ if len(full_trace) > 5:
48
+ return MOCK_QFT_VERIFICATION
49
+ return {"status": "Verification Failed", "reason": "Trace too short."}
quyaml_parser.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yaml
2
+ from qiskit import QuantumCircuit
3
+ import re
4
+ import numpy as np
5
+
6
+ def parse_quyaml_to_qiskit(quyaml_string: str) -> QuantumCircuit:
7
+ """
8
+ Parses a QuYAML string into a Qiskit QuantumCircuit object.
9
+ """
10
+ try:
11
+ data = yaml.safe_load(quyaml_string)
12
+ except yaml.YAMLError as e:
13
+ raise ValueError(f"Invalid QuYAML format: {e}")
14
+
15
+ circuit_name = data.get('circuit', 'my_circuit')
16
+
17
+ def get_reg_size(reg_str):
18
+ match = re.search(r'\[(\d+)\]', reg_str)
19
+ return int(match.group(1)) if match else 0
20
+
21
+ q_size = get_reg_size(data.get('qreg', 'q[0]'))
22
+ c_size = get_reg_size(data.get('creg', 'c[0]'))
23
+
24
+ if q_size == 0:
25
+ raise ValueError("QuYAML must define at least one quantum register (e.g., qreg: q[1])")
26
+
27
+ qc = QuantumCircuit(q_size, c_size, name=circuit_name)
28
+
29
+ instructions = data.get('instructions', [])
30
+ for inst_str in instructions:
31
+ apply_instruction(qc, inst_str)
32
+
33
+ return qc
34
+
35
+ def apply_instruction(qc: QuantumCircuit, inst_str: str):
36
+ """
37
+ Parses a single QuYAML instruction string and applies it to the circuit.
38
+ """
39
+ parts = inst_str.split()
40
+ gate = parts[0].lower()
41
+
42
+ def get_indices(target_strings):
43
+ indices = []
44
+ for s in target_strings:
45
+ match = re.search(r'\[(\d+)\]', s)
46
+ if match:
47
+ indices.append(int(match.group(1)))
48
+ return indices
49
+
50
+ targets = [p.replace(',', '') for p in parts[1:]]
51
+ q_indices = get_indices(targets)
52
+
53
+ try:
54
+ if gate == 'h':
55
+ qc.h(q_indices[0])
56
+ elif gate == 'x':
57
+ qc.x(q_indices[0])
58
+ elif gate == 'cx':
59
+ qc.cx(q_indices[0], q_indices[1])
60
+ elif gate == 'swap':
61
+ qc.swap(q_indices[0], q_indices[1])
62
+ elif gate.startswith('cphase'):
63
+ angle_str = re.search(r'\((.*?)\)', gate).group(1)
64
+ angle_map = {'pi/2': np.pi / 2, 'pi/4': np.pi / 4, 'pi': np.pi}
65
+ angle = angle_map.get(angle_str, float(angle_str))
66
+ qc.cp(angle, q_indices[0], q_indices[1])
67
+ elif gate == 'measure':
68
+ qc.measure(range(qc.num_qubits), range(qc.num_clbits))
69
+ except Exception as e:
70
+ raise ValueError(f"Could not parse instruction '{inst_str}'. Error: {e}")
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ pyyaml
4
+ qiskit
5
+ pydantic
6
+ numpy