qdsv-bridge 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QDSV / Qruba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: qdsv-bridge
3
+ Version: 0.1.0
4
+ Summary: Lightweight Python client for QDSV Bridge, a controlled semantic-to-IR/oracle/circuit compiler.
5
+ Project-URL: Homepage, https://qdsv.cloud
6
+ Project-URL: Documentation, https://qdsv.cloud/#bridge
7
+ Project-URL: Source, https://github.com/qdsvquantum-afk/qdsv-bridge
8
+ Author: QDSV / Qruba
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: circuit,qasm,qdsv,quantum,sdk,semantic-computation
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: requests>=2.31
22
+ Description-Content-Type: text/markdown
23
+
24
+ # QDSV Bridge Developer Preview
25
+
26
+ Lightweight Python SDK for **QDSV Bridge**, a controlled semantic compiler that turns supported problem-family specifications into QDSV IR, oracle specs, QASM blueprints, or integration-ready circuit scaffolds.
27
+
28
+ QDSV Bridge is not a free circuit generator. It is a restricted semantic-to-circuit bridge for supported problem families.
29
+
30
+ ```text
31
+ problem family spec
32
+ -> family ontology validation
33
+ -> semantic ProblemSpec / IR
34
+ -> oracle specification
35
+ -> materialization policy
36
+ -> QASM / Qiskit blueprint when the target requires circuits
37
+ ```
38
+
39
+ The SDK is a client only. It does not include the private QDSV Runtime, CAP, backend selector, lowering internals, QuEST/Aer/IBM adapters, or advanced orchestration.
40
+
41
+ ```bash
42
+ pip install qdsv-bridge
43
+ ```
44
+
45
+ ## Why This Exists
46
+
47
+ Traditional quantum workflows often start by asking users to choose a circuit, encoding, ansatz, or measurement pattern. That can force the data to adapt to a circuit.
48
+
49
+ QDSV Bridge starts from a controlled semantic family. The family defines what kind of problem is allowed, what concepts belong to it, what patterns are excluded, and what evidence must be produced. Circuit materialization is only one possible export when another platform requires it.
50
+
51
+ For example, in signal classification:
52
+
53
+ ```text
54
+ prepared signals
55
+ -> semantic_signal_classification
56
+ -> preserve signal geometry
57
+ -> oracle / IR / QASM blueprint
58
+ ```
59
+
60
+ This is designed to avoid a fixed pattern such as:
61
+
62
+ ```text
63
+ forced reduction -> angle encoding -> fixed ansatz -> fixed measurement
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ```python
69
+ from qdsv_bridge import QDSVBridgeClient
70
+
71
+ client = QDSVBridgeClient()
72
+
73
+ spec = {
74
+ "family": "semantic_signal_classification",
75
+ "state_space": {
76
+ "kind": "finite_candidates",
77
+ "candidate_count": 300,
78
+ "candidate_id": "eeg_window",
79
+ },
80
+ "signals": [
81
+ "dwt_cD3_std_score",
82
+ "std_score",
83
+ "activity_score",
84
+ "energy_score",
85
+ "dwt_cD2_std_score",
86
+ "complexity_score",
87
+ ],
88
+ "goal": {
89
+ "kind": "binary_marking",
90
+ "positive_state": "ictal",
91
+ },
92
+ "materialization_policy": {
93
+ "preserve_signal_geometry": True,
94
+ "avoid_fixed_ansatz": True,
95
+ "avoid_forced_dimensionality_reduction": True,
96
+ "report_information_loss": True,
97
+ },
98
+ "target": {
99
+ "format": "qasm3",
100
+ "backend_family": "qiskit",
101
+ },
102
+ "limits": {
103
+ "max_qubits": 10,
104
+ "max_depth": 300,
105
+ },
106
+ }
107
+
108
+ result = client.export(spec)
109
+
110
+ print(result["status"])
111
+ print(result["semantic_preservation_report"])
112
+ print(result["artifact"]["content"])
113
+ ```
114
+
115
+ ## Public Endpoints
116
+
117
+ The SDK calls the QDSV API:
118
+
119
+ - `GET /api/bridge/families`
120
+ - `POST /api/bridge/validate`
121
+ - `POST /api/bridge/compile`
122
+ - `POST /api/bridge/explain`
123
+ - `POST /api/bridge/export`
124
+
125
+ Default cloud endpoint:
126
+
127
+ ```python
128
+ client = QDSVBridgeClient(api_url="https://api.qdsv.cloud/api")
129
+ ```
130
+
131
+ Local/private Docker endpoint:
132
+
133
+ ```python
134
+ client = QDSVBridgeClient.local()
135
+ ```
136
+
137
+ ## Supported Families
138
+
139
+ Developer Preview families:
140
+
141
+ - `semantic_signal_classification`
142
+ - `predicate_marking`
143
+ - `state_similarity`
144
+ - `combinatorial_relation`
145
+ - `distribution_sampling`
146
+
147
+ Each family has:
148
+
149
+ - ontology boundary;
150
+ - allowed state-space kinds;
151
+ - allowed goal kinds;
152
+ - allowed semantic operations;
153
+ - excluded patterns;
154
+ - public limits;
155
+ - export targets;
156
+ - evidence / digest contract.
157
+
158
+ ## What It Exports
159
+
160
+ Depending on `target.format`, QDSV Bridge can export:
161
+
162
+ - `problem_spec`
163
+ - `ir`
164
+ - `oracle_spec`
165
+ - `qasm2`
166
+ - `qasm3`
167
+ - `qiskit_blueprint`
168
+
169
+ QASM outputs are bridge blueprints with explicit semantic oracle insertion points and QDSV digests. External platforms should inspect the returned preservation report, qubit/depth limits and oracle digest before executing or optimizing the generated artifact.
170
+
171
+ ## Important Boundaries
172
+
173
+ QDSV Bridge does not expose:
174
+
175
+ - QDSV Runtime internals;
176
+ - CAP internals;
177
+ - backend selection heuristics;
178
+ - private QuEST/Aer/IBM adapters;
179
+ - native lowering internals;
180
+ - unrestricted circuit generation;
181
+ - arbitrary Python execution;
182
+ - user-supplied free circuits as family contracts.
183
+
184
+ The product principle is:
185
+
186
+ ```text
187
+ controlled semantic family
188
+ -> flexible materialization
189
+ -> auditable export
190
+ ```
191
+
192
+ Not:
193
+
194
+ ```text
195
+ any input
196
+ -> arbitrary circuit
197
+ ```
198
+
199
+ ## Open SDK, Private Runtime
200
+
201
+ This repository is open-core:
202
+
203
+ - **Open under MIT:** Python client SDK, examples, docs and tests.
204
+ - **Not included:** QDSV Runtime, ontology compiler internals, CAP, lowering, advanced materialization, backend adapters, private endpoints, secrets or production configuration.
205
+
206
+ QDSV, QIntent and Qruba names and marks are project marks of their respective owners. The MIT License for this repository does not grant trademark rights.
@@ -0,0 +1,183 @@
1
+ # QDSV Bridge Developer Preview
2
+
3
+ Lightweight Python SDK for **QDSV Bridge**, a controlled semantic compiler that turns supported problem-family specifications into QDSV IR, oracle specs, QASM blueprints, or integration-ready circuit scaffolds.
4
+
5
+ QDSV Bridge is not a free circuit generator. It is a restricted semantic-to-circuit bridge for supported problem families.
6
+
7
+ ```text
8
+ problem family spec
9
+ -> family ontology validation
10
+ -> semantic ProblemSpec / IR
11
+ -> oracle specification
12
+ -> materialization policy
13
+ -> QASM / Qiskit blueprint when the target requires circuits
14
+ ```
15
+
16
+ The SDK is a client only. It does not include the private QDSV Runtime, CAP, backend selector, lowering internals, QuEST/Aer/IBM adapters, or advanced orchestration.
17
+
18
+ ```bash
19
+ pip install qdsv-bridge
20
+ ```
21
+
22
+ ## Why This Exists
23
+
24
+ Traditional quantum workflows often start by asking users to choose a circuit, encoding, ansatz, or measurement pattern. That can force the data to adapt to a circuit.
25
+
26
+ QDSV Bridge starts from a controlled semantic family. The family defines what kind of problem is allowed, what concepts belong to it, what patterns are excluded, and what evidence must be produced. Circuit materialization is only one possible export when another platform requires it.
27
+
28
+ For example, in signal classification:
29
+
30
+ ```text
31
+ prepared signals
32
+ -> semantic_signal_classification
33
+ -> preserve signal geometry
34
+ -> oracle / IR / QASM blueprint
35
+ ```
36
+
37
+ This is designed to avoid a fixed pattern such as:
38
+
39
+ ```text
40
+ forced reduction -> angle encoding -> fixed ansatz -> fixed measurement
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from qdsv_bridge import QDSVBridgeClient
47
+
48
+ client = QDSVBridgeClient()
49
+
50
+ spec = {
51
+ "family": "semantic_signal_classification",
52
+ "state_space": {
53
+ "kind": "finite_candidates",
54
+ "candidate_count": 300,
55
+ "candidate_id": "eeg_window",
56
+ },
57
+ "signals": [
58
+ "dwt_cD3_std_score",
59
+ "std_score",
60
+ "activity_score",
61
+ "energy_score",
62
+ "dwt_cD2_std_score",
63
+ "complexity_score",
64
+ ],
65
+ "goal": {
66
+ "kind": "binary_marking",
67
+ "positive_state": "ictal",
68
+ },
69
+ "materialization_policy": {
70
+ "preserve_signal_geometry": True,
71
+ "avoid_fixed_ansatz": True,
72
+ "avoid_forced_dimensionality_reduction": True,
73
+ "report_information_loss": True,
74
+ },
75
+ "target": {
76
+ "format": "qasm3",
77
+ "backend_family": "qiskit",
78
+ },
79
+ "limits": {
80
+ "max_qubits": 10,
81
+ "max_depth": 300,
82
+ },
83
+ }
84
+
85
+ result = client.export(spec)
86
+
87
+ print(result["status"])
88
+ print(result["semantic_preservation_report"])
89
+ print(result["artifact"]["content"])
90
+ ```
91
+
92
+ ## Public Endpoints
93
+
94
+ The SDK calls the QDSV API:
95
+
96
+ - `GET /api/bridge/families`
97
+ - `POST /api/bridge/validate`
98
+ - `POST /api/bridge/compile`
99
+ - `POST /api/bridge/explain`
100
+ - `POST /api/bridge/export`
101
+
102
+ Default cloud endpoint:
103
+
104
+ ```python
105
+ client = QDSVBridgeClient(api_url="https://api.qdsv.cloud/api")
106
+ ```
107
+
108
+ Local/private Docker endpoint:
109
+
110
+ ```python
111
+ client = QDSVBridgeClient.local()
112
+ ```
113
+
114
+ ## Supported Families
115
+
116
+ Developer Preview families:
117
+
118
+ - `semantic_signal_classification`
119
+ - `predicate_marking`
120
+ - `state_similarity`
121
+ - `combinatorial_relation`
122
+ - `distribution_sampling`
123
+
124
+ Each family has:
125
+
126
+ - ontology boundary;
127
+ - allowed state-space kinds;
128
+ - allowed goal kinds;
129
+ - allowed semantic operations;
130
+ - excluded patterns;
131
+ - public limits;
132
+ - export targets;
133
+ - evidence / digest contract.
134
+
135
+ ## What It Exports
136
+
137
+ Depending on `target.format`, QDSV Bridge can export:
138
+
139
+ - `problem_spec`
140
+ - `ir`
141
+ - `oracle_spec`
142
+ - `qasm2`
143
+ - `qasm3`
144
+ - `qiskit_blueprint`
145
+
146
+ QASM outputs are bridge blueprints with explicit semantic oracle insertion points and QDSV digests. External platforms should inspect the returned preservation report, qubit/depth limits and oracle digest before executing or optimizing the generated artifact.
147
+
148
+ ## Important Boundaries
149
+
150
+ QDSV Bridge does not expose:
151
+
152
+ - QDSV Runtime internals;
153
+ - CAP internals;
154
+ - backend selection heuristics;
155
+ - private QuEST/Aer/IBM adapters;
156
+ - native lowering internals;
157
+ - unrestricted circuit generation;
158
+ - arbitrary Python execution;
159
+ - user-supplied free circuits as family contracts.
160
+
161
+ The product principle is:
162
+
163
+ ```text
164
+ controlled semantic family
165
+ -> flexible materialization
166
+ -> auditable export
167
+ ```
168
+
169
+ Not:
170
+
171
+ ```text
172
+ any input
173
+ -> arbitrary circuit
174
+ ```
175
+
176
+ ## Open SDK, Private Runtime
177
+
178
+ This repository is open-core:
179
+
180
+ - **Open under MIT:** Python client SDK, examples, docs and tests.
181
+ - **Not included:** QDSV Runtime, ontology compiler internals, CAP, lowering, advanced materialization, backend adapters, private endpoints, secrets or production configuration.
182
+
183
+ QDSV, QIntent and Qruba names and marks are project marks of their respective owners. The MIT License for this repository does not grant trademark rights.
@@ -0,0 +1,32 @@
1
+ from qdsv_bridge import QDSVBridgeClient
2
+
3
+
4
+ spec = {
5
+ "family": "semantic_signal_classification",
6
+ "state_space": {"kind": "finite_candidates", "candidate_count": 300, "candidate_id": "eeg_window"},
7
+ "signals": [
8
+ "dwt_cD3_std_score",
9
+ "std_score",
10
+ "activity_score",
11
+ "energy_score",
12
+ "dwt_cD2_std_score",
13
+ "complexity_score",
14
+ ],
15
+ "goal": {"kind": "binary_marking", "positive_state": "ictal"},
16
+ "materialization_policy": {
17
+ "preserve_signal_geometry": True,
18
+ "avoid_fixed_ansatz": True,
19
+ "avoid_forced_dimensionality_reduction": True,
20
+ "report_information_loss": True,
21
+ },
22
+ "target": {"format": "qasm3", "backend_family": "qiskit"},
23
+ "limits": {"max_qubits": 10, "max_depth": 300},
24
+ }
25
+
26
+
27
+ client = QDSVBridgeClient()
28
+ result = client.export(spec)
29
+
30
+ print(result["status"])
31
+ print(result["semantic_preservation_report"])
32
+ print(result["artifact"]["content"])
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "qdsv-bridge"
7
+ version = "0.1.0"
8
+ description = "Lightweight Python client for QDSV Bridge, a controlled semantic-to-IR/oracle/circuit compiler."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "QDSV / Qruba" }
14
+ ]
15
+ keywords = ["qdsv", "semantic-computation", "quantum", "circuit", "qasm", "sdk"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: Science/Research",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Topic :: Scientific/Engineering",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ dependencies = [
27
+ "requests>=2.31",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://qdsv.cloud"
32
+ Documentation = "https://qdsv.cloud/#bridge"
33
+ Source = "https://github.com/qdsvquantum-afk/qdsv-bridge"
34
+
35
+ [project.scripts]
36
+ qdsv-bridge = "qdsv_bridge.cli:main"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/qdsv_bridge"]
@@ -0,0 +1,4 @@
1
+ from .client import QDSVBridgeClient
2
+ from .exceptions import QDSVBridgeAPIError, QDSVBridgeError, QDSVBridgeHTTPError
3
+
4
+ __all__ = ["QDSVBridgeClient", "QDSVBridgeError", "QDSVBridgeAPIError", "QDSVBridgeHTTPError"]
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+
7
+ from .client import QDSVBridgeClient
8
+
9
+
10
+ def main() -> None:
11
+ parser = argparse.ArgumentParser(description="QDSV Bridge SDK CLI")
12
+ parser.add_argument("command", choices=["families", "validate", "compile", "explain", "export"])
13
+ parser.add_argument("spec", nargs="?", help="Path to a Semantic Circuit Spec JSON file.")
14
+ parser.add_argument("--api-url", default=None)
15
+ parser.add_argument("--local", action="store_true", help="Use http://localhost:18080/api")
16
+ args = parser.parse_args()
17
+
18
+ client = QDSVBridgeClient.local() if args.local else QDSVBridgeClient(api_url=args.api_url)
19
+ if args.command == "families":
20
+ print(json.dumps(client.families(), indent=2, ensure_ascii=False))
21
+ return
22
+ if not args.spec:
23
+ parser.error("spec JSON file is required for this command")
24
+ spec = json.loads(Path(args.spec).read_text(encoding="utf-8"))
25
+ result = getattr(client, args.command)(spec)
26
+ print(json.dumps(result, indent=2, ensure_ascii=False))
27
+
28
+
29
+ if __name__ == "__main__":
30
+ main()
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any, Mapping
5
+
6
+ import requests
7
+
8
+ from .exceptions import QDSVBridgeAPIError, QDSVBridgeHTTPError
9
+
10
+
11
+ DEFAULT_API_URL = "https://api.qdsv.cloud/api"
12
+ PRIVATE_NODE_UNAVAILABLE_MESSAGE = (
13
+ "Private QDSV node temporarily unavailable. It may be offline, reserved for "
14
+ "private processing, or busy. Try again later or use QDSVBridgeClient() for "
15
+ "public cloud examples."
16
+ )
17
+
18
+
19
+ class QDSVBridgeClient:
20
+ """Client for QDSV Bridge public API endpoints."""
21
+
22
+ def __init__(
23
+ self,
24
+ api_url: str | None = None,
25
+ api_key: str | None = None,
26
+ *,
27
+ timeout: float = 30.0,
28
+ license_key: str | None = None,
29
+ sdk_name: str = "qdsv-bridge",
30
+ ) -> None:
31
+ self.api_url = self._normalize_api_url(
32
+ api_url or os.getenv("QDSV_BRIDGE_API_URL") or os.getenv("QDSV_API_URL") or DEFAULT_API_URL
33
+ )
34
+ self.api_key = api_key or os.getenv("QDSV_BRIDGE_API_KEY") or os.getenv("QDSV_API_KEY")
35
+ self.license_key = license_key or os.getenv("QDSV_LICENSE_KEY")
36
+ self.timeout = timeout
37
+ self.sdk_name = sdk_name
38
+ self._private_node = self._looks_like_private_node(self.api_url)
39
+
40
+ @classmethod
41
+ def local(
42
+ cls,
43
+ *,
44
+ api_url: str = "http://localhost:18080/api",
45
+ api_key: str | None = None,
46
+ timeout: float = 30.0,
47
+ license_key: str | None = None,
48
+ ) -> "QDSVBridgeClient":
49
+ return cls(api_url=api_url, api_key=api_key, timeout=timeout, license_key=license_key)
50
+
51
+ @staticmethod
52
+ def _normalize_api_url(value: str) -> str:
53
+ clean = str(value or "").strip().rstrip("/")
54
+ if not clean:
55
+ return DEFAULT_API_URL
56
+ return clean if clean.lower().endswith("/api") else f"{clean}/api"
57
+
58
+ @staticmethod
59
+ def _looks_like_private_node(api_url: str) -> bool:
60
+ clean = str(api_url or "").lower()
61
+ return "localhost" in clean or "127.0.0.1" in clean or "qintent-local.qdsv.cloud" in clean or "qruba.site" in clean
62
+
63
+ def _headers(self) -> dict[str, str]:
64
+ headers = {"Content-Type": "application/json", "x-sdk-name": self.sdk_name}
65
+ if self.api_key:
66
+ headers["Authorization"] = f"Bearer {self.api_key}"
67
+ if self.license_key:
68
+ headers["x-license-key"] = self.license_key
69
+ return headers
70
+
71
+ def _request(self, method: str, path: str, *, json: Mapping[str, Any] | None = None) -> dict[str, Any]:
72
+ url = f"{self.api_url}{path}"
73
+ kwargs: dict[str, Any] = {"headers": self._headers(), "timeout": self.timeout}
74
+ if json is not None:
75
+ kwargs["json"] = dict(json)
76
+ try:
77
+ response = requests.request(method, url, **kwargs)
78
+ except requests.RequestException as exc:
79
+ if self._private_node:
80
+ raise QDSVBridgeAPIError(PRIVATE_NODE_UNAVAILABLE_MESSAGE) from exc
81
+ raise QDSVBridgeAPIError(str(exc)) from exc
82
+ try:
83
+ payload = response.json()
84
+ except ValueError:
85
+ payload = {"status": "ERROR", "message": response.text}
86
+ if not response.ok:
87
+ raise QDSVBridgeHTTPError(response.status_code, payload)
88
+ if not isinstance(payload, dict):
89
+ raise QDSVBridgeAPIError(f"Unexpected API response type: {type(payload).__name__}")
90
+ return payload
91
+
92
+ def families(self) -> dict[str, Any]:
93
+ return self._request("GET", "/bridge/families")
94
+
95
+ def validate(self, spec: Mapping[str, Any]) -> dict[str, Any]:
96
+ return self._request("POST", "/bridge/validate", json={"spec": dict(spec)})
97
+
98
+ def compile(self, spec: Mapping[str, Any]) -> dict[str, Any]:
99
+ return self._request("POST", "/bridge/compile", json={"spec": dict(spec)})
100
+
101
+ def explain(self, spec: Mapping[str, Any]) -> dict[str, Any]:
102
+ return self._request("POST", "/bridge/explain", json={"spec": dict(spec)})
103
+
104
+ def export(self, spec: Mapping[str, Any]) -> dict[str, Any]:
105
+ return self._request("POST", "/bridge/export", json={"spec": dict(spec)})
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class QDSVBridgeError(Exception):
7
+ """Base exception for the QDSV Bridge SDK."""
8
+
9
+
10
+ class QDSVBridgeHTTPError(QDSVBridgeError):
11
+ """Raised when the API returns an HTTP error response."""
12
+
13
+ def __init__(self, status_code: int, payload: Any) -> None:
14
+ self.status_code = status_code
15
+ self.payload = payload
16
+ super().__init__(f"QDSV Bridge API HTTP {status_code}: {payload}")
17
+
18
+
19
+ class QDSVBridgeAPIError(QDSVBridgeError):
20
+ """Raised when a transport error prevents calling the API."""
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+ import requests
5
+
6
+ from qdsv_bridge import QDSVBridgeClient
7
+ from qdsv_bridge.exceptions import QDSVBridgeAPIError, QDSVBridgeHTTPError
8
+
9
+
10
+ def test_normalizes_api_url() -> None:
11
+ assert QDSVBridgeClient("https://api.qdsv.cloud").api_url == "https://api.qdsv.cloud/api"
12
+ assert QDSVBridgeClient("https://api.qdsv.cloud/api").api_url == "https://api.qdsv.cloud/api"
13
+ assert QDSVBridgeClient.local().api_url == "http://localhost:18080/api"
14
+
15
+
16
+ def test_families_get_has_no_json_body(monkeypatch: pytest.MonkeyPatch) -> None:
17
+ calls = {}
18
+
19
+ class FakeResponse:
20
+ ok = True
21
+ status_code = 200
22
+
23
+ @staticmethod
24
+ def json():
25
+ return {"status": "SUCCESS", "families": {}}
26
+
27
+ def fake_request(method, url, **kwargs):
28
+ calls["method"] = method
29
+ calls["url"] = url
30
+ calls["kwargs"] = kwargs
31
+ return FakeResponse()
32
+
33
+ monkeypatch.setattr("qdsv_bridge.client.requests.request", fake_request)
34
+ result = QDSVBridgeClient().families()
35
+
36
+ assert result["status"] == "SUCCESS"
37
+ assert calls["method"] == "GET"
38
+ assert calls["url"].endswith("/bridge/families")
39
+ assert "json" not in calls["kwargs"]
40
+
41
+
42
+ def test_export_posts_spec(monkeypatch: pytest.MonkeyPatch) -> None:
43
+ calls = {}
44
+
45
+ class FakeResponse:
46
+ ok = True
47
+ status_code = 200
48
+
49
+ @staticmethod
50
+ def json():
51
+ return {"status": "SUCCESS", "artifact": {"format": "qasm3"}}
52
+
53
+ def fake_request(method, url, **kwargs):
54
+ calls["method"] = method
55
+ calls["url"] = url
56
+ calls["kwargs"] = kwargs
57
+ return FakeResponse()
58
+
59
+ monkeypatch.setattr("qdsv_bridge.client.requests.request", fake_request)
60
+ result = QDSVBridgeClient().export({"family": "semantic_signal_classification"})
61
+
62
+ assert result["artifact"]["format"] == "qasm3"
63
+ assert calls["method"] == "POST"
64
+ assert calls["url"].endswith("/bridge/export")
65
+ assert calls["kwargs"]["json"]["spec"]["family"] == "semantic_signal_classification"
66
+
67
+
68
+ def test_private_node_transport_error_is_user_friendly(monkeypatch: pytest.MonkeyPatch) -> None:
69
+ def fake_request(method, url, **kwargs):
70
+ raise requests.ConnectionError("connection refused")
71
+
72
+ monkeypatch.setattr("qdsv_bridge.client.requests.request", fake_request)
73
+ with pytest.raises(QDSVBridgeAPIError) as exc:
74
+ QDSVBridgeClient.local().families()
75
+
76
+ assert "Private QDSV node temporarily unavailable" in str(exc.value)
77
+
78
+
79
+ def test_http_error_is_not_hidden(monkeypatch: pytest.MonkeyPatch) -> None:
80
+ class FakeResponse:
81
+ ok = False
82
+ status_code = 400
83
+
84
+ @staticmethod
85
+ def json():
86
+ return {"detail": {"error_code": "E_BRIDGE_UNSUPPORTED_FAMILY"}}
87
+
88
+ def fake_request(method, url, **kwargs):
89
+ return FakeResponse()
90
+
91
+ monkeypatch.setattr("qdsv_bridge.client.requests.request", fake_request)
92
+ with pytest.raises(QDSVBridgeHTTPError) as exc:
93
+ QDSVBridgeClient.local().families()
94
+
95
+ assert exc.value.status_code == 400
96
+ assert exc.value.payload["detail"]["error_code"] == "E_BRIDGE_UNSUPPORTED_FAMILY"