graphite-code 0.3.0__py3-none-any.whl

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.
Files changed (112) hide show
  1. graphite/__init__.py +41 -0
  2. graphite/__main__.py +7 -0
  3. graphite/_cleanup_worker.py +525 -0
  4. graphite/activation.py +164 -0
  5. graphite/agent_hooks.py +577 -0
  6. graphite/agent_settings.py +226 -0
  7. graphite/analyze.py +146 -0
  8. graphite/answer_contract.py +420 -0
  9. graphite/bootstrap.py +210 -0
  10. graphite/buildlock.py +99 -0
  11. graphite/cache.py +131 -0
  12. graphite/channel.py +1325 -0
  13. graphite/cli.py +3053 -0
  14. graphite/cluster.py +111 -0
  15. graphite/config.py +209 -0
  16. graphite/context.py +355 -0
  17. graphite/daemon.py +745 -0
  18. graphite/daemon_health.py +733 -0
  19. graphite/debt.py +118 -0
  20. graphite/dependency_install.py +1597 -0
  21. graphite/detach.py +33 -0
  22. graphite/doctor.py +678 -0
  23. graphite/doctor_probes.py +2100 -0
  24. graphite/engine_identity.py +238 -0
  25. graphite/export/__init__.py +6 -0
  26. graphite/export/html.py +244 -0
  27. graphite/export/json.py +39 -0
  28. graphite/export/md.py +68 -0
  29. graphite/extract/__init__.py +4 -0
  30. graphite/extract/ast.py +1964 -0
  31. graphite/freshness.py +127 -0
  32. graphite/git.py +406 -0
  33. graphite/graph.py +117 -0
  34. graphite/graph_io.py +188 -0
  35. graphite/health.py +147 -0
  36. graphite/hook_entry.py +68 -0
  37. graphite/hookinstall.py +224 -0
  38. graphite/hookshim.py +86 -0
  39. graphite/incident_ledger.py +247 -0
  40. graphite/ingest.py +279 -0
  41. graphite/init.py +791 -0
  42. graphite/io.py +32 -0
  43. graphite/listing.py +51 -0
  44. graphite/llm.py +518 -0
  45. graphite/llm_probe.py +157 -0
  46. graphite/mcp.py +7 -0
  47. graphite/mcp_server.py +450 -0
  48. graphite/natural_query.py +252 -0
  49. graphite/overlays.py +713 -0
  50. graphite/probe_process.py +879 -0
  51. graphite/probe_workspace.py +728 -0
  52. graphite/process_contracts.py +22 -0
  53. graphite/provider_observer.py +397 -0
  54. graphite/query.py +646 -0
  55. graphite/query_plan.py +97 -0
  56. graphite/replacement_audit.py +291 -0
  57. graphite/resolve.py +660 -0
  58. graphite/review.py +782 -0
  59. graphite/routing/__init__.py +5 -0
  60. graphite/routing/approval.py +362 -0
  61. graphite/routing/classifier.py +169 -0
  62. graphite/routing/claude_executor.py +419 -0
  63. graphite/routing/claude_probe.py +102 -0
  64. graphite/routing/cli_identity.py +84 -0
  65. graphite/routing/codex_executor.py +383 -0
  66. graphite/routing/codex_probe.py +93 -0
  67. graphite/routing/context_builder.py +327 -0
  68. graphite/routing/contracts.py +802 -0
  69. graphite/routing/diff_policy.py +468 -0
  70. graphite/routing/edit_apply.py +166 -0
  71. graphite/routing/effort.py +43 -0
  72. graphite/routing/lifecycle.py +771 -0
  73. graphite/routing/lifecycle_operator.py +227 -0
  74. graphite/routing/lifecycle_service.py +555 -0
  75. graphite/routing/lifecycle_storage.py +977 -0
  76. graphite/routing/ollama_executor.py +341 -0
  77. graphite/routing/ollama_probe.py +72 -0
  78. graphite/routing/openrouter_executor.py +338 -0
  79. graphite/routing/openrouter_probe.py +188 -0
  80. graphite/routing/policy.py +815 -0
  81. graphite/routing/probe_runner.py +543 -0
  82. graphite/routing/process_runner.py +523 -0
  83. graphite/routing/profiles.py +554 -0
  84. graphite/routing/prompt.py +58 -0
  85. graphite/routing/registry.py +444 -0
  86. graphite/routing/route_pool.py +629 -0
  87. graphite/routing/route_pool_execution.py +275 -0
  88. graphite/routing/schema_validation.py +169 -0
  89. graphite/routing/service.py +1263 -0
  90. graphite/routing/settings.py +99 -0
  91. graphite/routing/shadow.py +201 -0
  92. graphite/routing/storage.py +4001 -0
  93. graphite/routing/telemetry.py +346 -0
  94. graphite/routing/worktree.py +259 -0
  95. graphite/routing/zai_edit.py +113 -0
  96. graphite/routing/zai_executor.py +191 -0
  97. graphite/routing/zai_probe.py +126 -0
  98. graphite/savings.py +84 -0
  99. graphite/ts_bridge.py +142 -0
  100. graphite/ts_resolver.mjs +314 -0
  101. graphite/typescript_activation.py +1586 -0
  102. graphite/usage_ledger.py +156 -0
  103. graphite/validation.py +148 -0
  104. graphite/watch.py +167 -0
  105. graphite/windows_job.py +368 -0
  106. graphite/windows_startup.py +144 -0
  107. graphite/windows_task.py +212 -0
  108. graphite_code-0.3.0.dist-info/METADATA +743 -0
  109. graphite_code-0.3.0.dist-info/RECORD +112 -0
  110. graphite_code-0.3.0.dist-info/WHEEL +4 -0
  111. graphite_code-0.3.0.dist-info/entry_points.txt +3 -0
  112. graphite_code-0.3.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,338 @@
1
+ """Hardened adapter for governed OpenRouter development execution."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ import math
7
+ from collections.abc import Mapping
8
+ from dataclasses import dataclass, field
9
+ from typing import Final
10
+
11
+ from .claude_executor import AdapterError
12
+ from .contracts import CliIdentity, Effort, ProviderId
13
+ from .edit_apply import (
14
+ EDIT_RESULT_MARKER,
15
+ MAX_EDIT_FILE_BYTES,
16
+ apply_whole_file_edit,
17
+ )
18
+ from .lifecycle import LifecycleProviderId, ProviderRuntimeIdentity
19
+ from .openrouter_probe import (
20
+ CANONICAL_ENDPOINT,
21
+ HttpProbe,
22
+ OpenRouterPricing,
23
+ completion_cost_microunits,
24
+ observe_openrouter_with_pricing,
25
+ )
26
+ from .probe_runner import (
27
+ MAX_INFERENCE_REQUEST_BYTES,
28
+ MAX_INFERENCE_RESPONSE_BYTES,
29
+ MAX_INFERENCE_TIMEOUT_SECONDS,
30
+ HttpProbeEndpoint,
31
+ HttpProbeResult,
32
+ ProbeEndpointPurpose,
33
+ ProviderProbeError,
34
+ run_http_probe,
35
+ )
36
+ from .schema_validation import is_supported_schema, matches_schema
37
+
38
+ ADAPTER_PROTOCOL_VERSION: Final = "1.0.0"
39
+ API_CONTRACT_VERSION: Final = "1.0.0"
40
+ MAX_TOKEN_COUNT: Final = 10_000_000
41
+ MAX_OUTPUT_SCHEMA_BYTES: Final = 65_536
42
+ MAX_COST_MICROUNITS: Final = 1_000_000_000
43
+ _EXECUTION_EFFORTS: Final = frozenset({Effort.LOW, Effort.MEDIUM, Effort.HIGH})
44
+ _PROBE_FAILURE_CODES: Final = {
45
+ "probe_model_unavailable": "model_unavailable",
46
+ "probe_auth_unhealthy": "auth_required",
47
+ }
48
+ _EXECUTION_TRANSPORT_CODES: Final = {
49
+ "probe_response_limit": "response_limit",
50
+ "probe_timeout": "timeout",
51
+ # A non-2xx / redirect from the completions POST is a provider-side
52
+ # rejection of the request (e.g. an unsupported response_format), NOT a
53
+ # transient network failure -- surface it distinctly so it is not
54
+ # conflated with dns_busy/unavailable/failed under one opaque code.
55
+ "probe_http_status": "http_status",
56
+ "probe_redirect_rejected": "http_status",
57
+ }
58
+
59
+ __all__ = [
60
+ "ADAPTER_PROTOCOL_VERSION",
61
+ "API_CONTRACT_VERSION",
62
+ "CANONICAL_ENDPOINT",
63
+ "EDIT_RESULT_MARKER",
64
+ "MAX_EDIT_FILE_BYTES",
65
+ "OpenRouterExecutionResult",
66
+ "OpenRouterPreflight",
67
+ "apply_whole_file_edit",
68
+ "execute_openrouter",
69
+ "preflight_openrouter",
70
+ ]
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class OpenRouterPreflight:
75
+ """Endpoint runtime identity plus pinned pricing for one governed model."""
76
+
77
+ identity: CliIdentity
78
+ runtime: ProviderRuntimeIdentity
79
+ pricing: OpenRouterPricing
80
+
81
+
82
+ def _canonical_sha256(value: object) -> str:
83
+ return hashlib.sha256(
84
+ json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
85
+ ).hexdigest()
86
+
87
+
88
+ def preflight_openrouter(
89
+ *,
90
+ api_key: str,
91
+ model_id: str,
92
+ routing_policy: Mapping[str, object],
93
+ observed_at: int,
94
+ policy_version: str,
95
+ transport: HttpProbe = run_http_probe,
96
+ ) -> OpenRouterPreflight:
97
+ """Bind endpoint, model, routing policy, and pricing into one identity digest."""
98
+ try:
99
+ observation = observe_openrouter_with_pricing(
100
+ endpoint=CANONICAL_ENDPOINT,
101
+ api_key=api_key,
102
+ model_id=model_id,
103
+ routing_policy=routing_policy,
104
+ observed_at=observed_at,
105
+ policy_version=policy_version,
106
+ transport=transport,
107
+ )
108
+ except ProviderProbeError as exc:
109
+ raise AdapterError(_PROBE_FAILURE_CODES.get(exc.code, "unavailable")) from None
110
+ runtime = observation.identity
111
+ composite = _canonical_sha256(
112
+ {
113
+ "endpoint": runtime.runtime_digest,
114
+ "model": runtime.model_identity_digest,
115
+ "pricing": observation.pricing.digest,
116
+ "routing": runtime.routing_policy_digest,
117
+ }
118
+ )
119
+ identity = CliIdentity(
120
+ ProviderId.OPENROUTER,
121
+ composite,
122
+ API_CONTRACT_VERSION,
123
+ ADAPTER_PROTOCOL_VERSION,
124
+ )
125
+ return OpenRouterPreflight(identity, runtime, observation.pricing)
126
+
127
+
128
+ @dataclass(frozen=True, slots=True)
129
+ class OpenRouterExecutionResult:
130
+ """Sanitized outcome of one schema-bound OpenRouter execution."""
131
+
132
+ effective_model: str
133
+ message: str = field(repr=False)
134
+ input_tokens: int
135
+ output_tokens: int
136
+ cost_microunits: int
137
+ duration_seconds: float
138
+ request_sha256: str
139
+ response_sha256: str
140
+
141
+
142
+ def _model_name(value: object) -> str:
143
+ if (
144
+ not isinstance(value, str)
145
+ or not value
146
+ or len(value) > 128
147
+ or any(character.isspace() for character in value)
148
+ ):
149
+ raise AdapterError("request_invalid")
150
+ return value
151
+
152
+
153
+ def _token(value: object) -> int:
154
+ if isinstance(value, bool) or not isinstance(value, int) or not 0 <= value <= MAX_TOKEN_COUNT:
155
+ raise AdapterError("protocol")
156
+ return value
157
+
158
+
159
+ def execute_openrouter(
160
+ *,
161
+ api_key: str,
162
+ prompt: bytes,
163
+ requested_model: str,
164
+ expected_effective_model: str,
165
+ effort: Effort,
166
+ output_schema: dict[str, object],
167
+ output_schema_sha256: str,
168
+ pricing: OpenRouterPricing,
169
+ max_output_tokens: int,
170
+ max_cost_microunits: int,
171
+ timeout_seconds: float,
172
+ response_format_type: str = "json_schema",
173
+ transport: HttpProbe = run_http_probe,
174
+ ) -> OpenRouterExecutionResult:
175
+ """Perform exactly one bounded, schema-bound chat completion; no retries.
176
+
177
+ ``response_format_type`` selects the provider response contract:
178
+ ``"json_schema"`` (default) sends the strict json_schema block;
179
+ ``"json_object"`` sends only ``{"type": "json_object"}``. The output
180
+ schema is canonicalized and its digest pinned in both modes -- json_object
181
+ simply does not forward the schema to the provider as an enforcement
182
+ mechanism. Response parsing (``json.loads`` of the completion content) is
183
+ identical for both, so switching modes changes exactly one variable.
184
+ """
185
+ if not isinstance(api_key, str) or not api_key:
186
+ raise AdapterError("auth_required")
187
+ if len(api_key) > 4096 or any(character in api_key for character in "\r\n\x00"):
188
+ raise AdapterError("request_invalid")
189
+ if (
190
+ not isinstance(prompt, bytes)
191
+ or not prompt
192
+ or len(prompt) > MAX_INFERENCE_REQUEST_BYTES
193
+ ):
194
+ raise AdapterError("request_invalid")
195
+ try:
196
+ prompt_text = prompt.decode("utf-8")
197
+ except UnicodeDecodeError:
198
+ raise AdapterError("request_invalid") from None
199
+ requested = _model_name(requested_model)
200
+ expected = _model_name(expected_effective_model)
201
+ try:
202
+ normalized_effort = Effort(effort)
203
+ except (TypeError, ValueError):
204
+ raise AdapterError("request_invalid") from None
205
+ if normalized_effort not in _EXECUTION_EFFORTS:
206
+ raise AdapterError("request_invalid")
207
+ if response_format_type not in ("json_schema", "json_object"):
208
+ raise AdapterError("request_invalid")
209
+ if not isinstance(output_schema, dict) or not output_schema:
210
+ raise AdapterError("request_invalid")
211
+ if not is_supported_schema(output_schema):
212
+ raise AdapterError("request_invalid")
213
+ try:
214
+ schema_canonical = json.dumps(
215
+ output_schema, sort_keys=True, separators=(",", ":"), allow_nan=False
216
+ )
217
+ except (TypeError, ValueError, RecursionError):
218
+ raise AdapterError("request_invalid") from None
219
+ if len(schema_canonical.encode()) > MAX_OUTPUT_SCHEMA_BYTES:
220
+ raise AdapterError("request_invalid")
221
+ if (
222
+ not isinstance(output_schema_sha256, str)
223
+ or hashlib.sha256(schema_canonical.encode()).hexdigest() != output_schema_sha256
224
+ ):
225
+ raise AdapterError("request_invalid")
226
+ if not isinstance(pricing, OpenRouterPricing):
227
+ raise AdapterError("request_invalid")
228
+ for value, maximum in (
229
+ (max_output_tokens, MAX_TOKEN_COUNT),
230
+ (max_cost_microunits, MAX_COST_MICROUNITS),
231
+ ):
232
+ if isinstance(value, bool) or not isinstance(value, int) or not 1 <= value <= maximum:
233
+ raise AdapterError("request_invalid")
234
+ if (
235
+ isinstance(timeout_seconds, bool)
236
+ or not isinstance(timeout_seconds, (int, float))
237
+ or not math.isfinite(timeout_seconds)
238
+ or not 0.1 <= timeout_seconds <= MAX_INFERENCE_TIMEOUT_SECONDS
239
+ ):
240
+ raise AdapterError("request_invalid")
241
+ if response_format_type == "json_schema":
242
+ response_format: dict[str, object] = {
243
+ "json_schema": {
244
+ "name": "graphite_response",
245
+ "schema": output_schema,
246
+ "strict": True,
247
+ },
248
+ "type": "json_schema",
249
+ }
250
+ else:
251
+ response_format = {"type": "json_object"}
252
+ payload = {
253
+ "max_tokens": max_output_tokens,
254
+ "messages": [{"content": prompt_text, "role": "user"}],
255
+ "model": requested,
256
+ "reasoning": {"effort": normalized_effort.value},
257
+ "response_format": response_format,
258
+ "stream": False,
259
+ "temperature": 0,
260
+ "usage": {"include": True},
261
+ }
262
+ body = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
263
+ if len(body) > MAX_INFERENCE_REQUEST_BYTES:
264
+ raise AdapterError("request_invalid")
265
+ endpoint = HttpProbeEndpoint(
266
+ LifecycleProviderId.OPENROUTER,
267
+ "https",
268
+ "openrouter.ai",
269
+ 443,
270
+ ProbeEndpointPurpose.OPENROUTER_CHAT_COMPLETIONS,
271
+ )
272
+ try:
273
+ result = transport(
274
+ endpoint=endpoint,
275
+ timeout_seconds=timeout_seconds,
276
+ request_body=body,
277
+ authorization=f"Bearer {api_key}",
278
+ max_response_bytes=MAX_INFERENCE_RESPONSE_BYTES,
279
+ )
280
+ except ProviderProbeError as error:
281
+ raise AdapterError(
282
+ _EXECUTION_TRANSPORT_CODES.get(error.code, "unavailable")
283
+ ) from None
284
+ except Exception:
285
+ raise AdapterError("unavailable") from None
286
+ if not isinstance(result, HttpProbeResult):
287
+ raise AdapterError("unavailable")
288
+ try:
289
+ envelope = json.loads(result.body.decode("utf-8"))
290
+ except (UnicodeDecodeError, json.JSONDecodeError, RecursionError):
291
+ raise AdapterError("unavailable") from None
292
+ if not isinstance(envelope, dict):
293
+ raise AdapterError("protocol")
294
+ reported_model = envelope.get("model")
295
+ if not isinstance(reported_model, str) or not reported_model:
296
+ raise AdapterError("model_identity_unverified")
297
+ if reported_model != expected:
298
+ raise AdapterError("model_mismatch")
299
+ choices = envelope.get("choices")
300
+ if not isinstance(choices, list) or len(choices) != 1 or not isinstance(choices[0], dict):
301
+ raise AdapterError("protocol")
302
+ message = choices[0].get("message")
303
+ if not isinstance(message, dict):
304
+ raise AdapterError("protocol")
305
+ content = message.get("content")
306
+ if not isinstance(content, str):
307
+ raise AdapterError("protocol")
308
+ try:
309
+ structured = json.loads(content)
310
+ except (json.JSONDecodeError, RecursionError):
311
+ raise AdapterError("response_contract_invalid") from None
312
+ if not isinstance(structured, dict):
313
+ raise AdapterError("response_contract_invalid")
314
+ if not matches_schema(structured, output_schema):
315
+ raise AdapterError("response_contract_invalid")
316
+ usage = envelope.get("usage")
317
+ if not isinstance(usage, dict):
318
+ raise AdapterError("protocol")
319
+ input_tokens = _token(usage.get("prompt_tokens"))
320
+ output_tokens = _token(usage.get("completion_tokens"))
321
+ try:
322
+ cost = completion_cost_microunits(
323
+ pricing, input_tokens=input_tokens, output_tokens=output_tokens
324
+ )
325
+ except ProviderProbeError:
326
+ raise AdapterError("protocol") from None
327
+ if cost > max_cost_microunits:
328
+ raise AdapterError("cost_ceiling_exceeded")
329
+ return OpenRouterExecutionResult(
330
+ expected,
331
+ json.dumps(structured, sort_keys=True, separators=(",", ":")),
332
+ input_tokens,
333
+ output_tokens,
334
+ cost,
335
+ result.duration_seconds,
336
+ hashlib.sha256(body).hexdigest(),
337
+ hashlib.sha256(result.body).hexdigest(),
338
+ )
@@ -0,0 +1,188 @@
1
+ """Bounded, non-inference OpenRouter lifecycle observation."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ import math
7
+ import re
8
+ import time
9
+ from collections.abc import Callable, Mapping
10
+ from dataclasses import dataclass
11
+ from decimal import Decimal, InvalidOperation
12
+
13
+ from .lifecycle import LifecycleProviderId, ProviderRuntimeIdentity, RuntimeKind
14
+ from .probe_runner import (
15
+ MAX_CATALOG_RESPONSE_BYTES,
16
+ HttpProbeEndpoint,
17
+ HttpProbeResult,
18
+ ProbeEndpointPurpose,
19
+ ProviderProbeError,
20
+ run_http_probe,
21
+ )
22
+
23
+ CANONICAL_ENDPOINT = "https://openrouter.ai/api/v1"
24
+ API_CONTRACT_VERSION = "1.0.0"
25
+ _MODEL_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}/[A-Za-z0-9][A-Za-z0-9._:+-]{0,127}$")
26
+ _PRICE = re.compile(r"^(0|[0-9]{1,10}(\.[0-9]{1,18})?|\.[0-9]{1,18})$")
27
+ HttpProbe = Callable[..., HttpProbeResult]
28
+
29
+
30
+ def _digest(value: object) -> str:
31
+ try:
32
+ encoded = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True, allow_nan=False).encode()
33
+ except (TypeError, ValueError, RecursionError):
34
+ raise ProviderProbeError("probe_request_invalid") from None
35
+ if len(encoded) > 16 * 1024:
36
+ raise ProviderProbeError("probe_request_invalid")
37
+ return hashlib.sha256(encoded).hexdigest()
38
+
39
+
40
+ def _json(result: HttpProbeResult) -> object:
41
+ if not isinstance(result, HttpProbeResult):
42
+ raise ProviderProbeError("probe_protocol_invalid")
43
+ try:
44
+ return json.loads(result.body.decode("utf-8"))
45
+ except (UnicodeDecodeError, json.JSONDecodeError, RecursionError):
46
+ raise ProviderProbeError("probe_protocol_invalid") from None
47
+
48
+
49
+ @dataclass(frozen=True, slots=True)
50
+ class OpenRouterPricing:
51
+ """Catalog-reported per-token USD prices as exact decimal strings."""
52
+
53
+ prompt: str
54
+ completion: str
55
+
56
+ def __post_init__(self) -> None:
57
+ for value in (self.prompt, self.completion):
58
+ if (
59
+ not isinstance(value, str)
60
+ or len(value) > 64
61
+ or _PRICE.fullmatch(value) is None
62
+ ):
63
+ raise ProviderProbeError("probe_protocol_invalid")
64
+ try:
65
+ parsed = Decimal(value)
66
+ except InvalidOperation:
67
+ raise ProviderProbeError("probe_protocol_invalid") from None
68
+ if not Decimal(0) <= parsed <= Decimal(1):
69
+ raise ProviderProbeError("probe_protocol_invalid")
70
+
71
+ @property
72
+ def digest(self) -> str:
73
+ return _digest({"completion": self.completion, "prompt": self.prompt})
74
+
75
+
76
+ @dataclass(frozen=True, slots=True)
77
+ class OpenRouterObservation:
78
+ identity: ProviderRuntimeIdentity
79
+ pricing: OpenRouterPricing
80
+
81
+
82
+ def completion_cost_microunits(
83
+ pricing: OpenRouterPricing, *, input_tokens: int, output_tokens: int
84
+ ) -> int:
85
+ """Ceiling of the exact-decimal USD cost expressed in microunits."""
86
+ if not isinstance(pricing, OpenRouterPricing):
87
+ raise ProviderProbeError("probe_request_invalid")
88
+ for value in (input_tokens, output_tokens):
89
+ if isinstance(value, bool) or not isinstance(value, int) or not 0 <= value <= 100_000_000:
90
+ raise ProviderProbeError("probe_request_invalid")
91
+ cost = (
92
+ Decimal(input_tokens) * Decimal(pricing.prompt)
93
+ + Decimal(output_tokens) * Decimal(pricing.completion)
94
+ ) * Decimal(1_000_000)
95
+ whole = int(cost)
96
+ return whole if cost == whole else whole + 1
97
+
98
+
99
+ def _observe(
100
+ *, endpoint: str, api_key: str, model_id: str, routing_policy: Mapping[str, object],
101
+ observed_at: int, policy_version: str, timeout_seconds: float = 10.0,
102
+ transport: HttpProbe = run_http_probe, clock: Callable[[], float] = time.monotonic,
103
+ ) -> tuple[ProviderRuntimeIdentity, dict[str, object]]:
104
+ if endpoint != CANONICAL_ENDPOINT:
105
+ raise ProviderProbeError("probe_endpoint_invalid")
106
+ if not isinstance(api_key, str) or not api_key or len(api_key) > 4096 or any(char in api_key for char in "\r\n\x00") or not isinstance(model_id, str) or _MODEL_ID.fullmatch(model_id) is None or not isinstance(routing_policy, Mapping):
107
+ raise ProviderProbeError("probe_request_invalid")
108
+ if isinstance(timeout_seconds, bool) or not isinstance(timeout_seconds, (int, float)) or not math.isfinite(timeout_seconds) or not 0.1 <= timeout_seconds <= 30:
109
+ raise ProviderProbeError("probe_request_invalid")
110
+ routing_digest = _digest(dict(routing_policy))
111
+ deadline = clock() + float(timeout_seconds)
112
+ authorization = f"Bearer {api_key}"
113
+
114
+ def call(purpose: ProbeEndpointPurpose) -> object:
115
+ remaining = deadline - clock()
116
+ if remaining <= 0:
117
+ raise ProviderProbeError("probe_timeout")
118
+ target = HttpProbeEndpoint(LifecycleProviderId.OPENROUTER, "https", "openrouter.ai", 443, purpose)
119
+ allowance: dict[str, int] = (
120
+ {"max_response_bytes": MAX_CATALOG_RESPONSE_BYTES}
121
+ if purpose is ProbeEndpointPurpose.OPENROUTER_MODELS
122
+ else {}
123
+ )
124
+ try:
125
+ result = transport(endpoint=target, timeout_seconds=remaining, authorization=authorization, **allowance)
126
+ except ProviderProbeError as exc:
127
+ if purpose is ProbeEndpointPurpose.OPENROUTER_AUTH_KEY and exc.code == "probe_http_status":
128
+ raise ProviderProbeError("probe_auth_unhealthy") from None
129
+ raise
130
+ except Exception:
131
+ raise ProviderProbeError("probe_failed") from None
132
+ return _json(result)
133
+
134
+ auth = call(ProbeEndpointPurpose.OPENROUTER_AUTH_KEY)
135
+ if not isinstance(auth, dict):
136
+ raise ProviderProbeError("probe_auth_unhealthy")
137
+ models = call(ProbeEndpointPurpose.OPENROUTER_MODELS)
138
+ data = models.get("data") if isinstance(models, dict) else None
139
+ if not isinstance(data, list) or len(data) > 2048:
140
+ raise ProviderProbeError("probe_protocol_invalid")
141
+ matched = next(
142
+ (
143
+ item
144
+ for item in data
145
+ if isinstance(item, dict) and item.get("id") == model_id
146
+ ),
147
+ None,
148
+ )
149
+ if matched is None:
150
+ raise ProviderProbeError("probe_model_unavailable")
151
+ try:
152
+ identity = ProviderRuntimeIdentity(LifecycleProviderId.OPENROUTER, RuntimeKind.REMOTE_HTTPS, API_CONTRACT_VERSION, hashlib.sha256(endpoint.encode("ascii")).hexdigest(), _digest(model_id), routing_digest, ("credential_health", "models_metadata"), policy_version, observed_at)
153
+ except ValueError:
154
+ raise ProviderProbeError("probe_request_invalid") from None
155
+ return identity, matched
156
+
157
+
158
+ def observe_openrouter(
159
+ *, endpoint: str, api_key: str, model_id: str, routing_policy: Mapping[str, object],
160
+ observed_at: int, policy_version: str, timeout_seconds: float = 10.0,
161
+ transport: HttpProbe = run_http_probe, clock: Callable[[], float] = time.monotonic,
162
+ ) -> ProviderRuntimeIdentity:
163
+ """Observe exact OpenRouter auth/model metadata; never invoke inference."""
164
+ return _observe(
165
+ endpoint=endpoint, api_key=api_key, model_id=model_id,
166
+ routing_policy=routing_policy, observed_at=observed_at,
167
+ policy_version=policy_version, timeout_seconds=timeout_seconds,
168
+ transport=transport, clock=clock,
169
+ )[0]
170
+
171
+
172
+ def observe_openrouter_with_pricing(
173
+ *, endpoint: str, api_key: str, model_id: str, routing_policy: Mapping[str, object],
174
+ observed_at: int, policy_version: str, timeout_seconds: float = 10.0,
175
+ transport: HttpProbe = run_http_probe, clock: Callable[[], float] = time.monotonic,
176
+ ) -> OpenRouterObservation:
177
+ """Observe auth/model metadata and bind the catalog's exact pricing; never invoke inference."""
178
+ identity, entry = _observe(
179
+ endpoint=endpoint, api_key=api_key, model_id=model_id,
180
+ routing_policy=routing_policy, observed_at=observed_at,
181
+ policy_version=policy_version, timeout_seconds=timeout_seconds,
182
+ transport=transport, clock=clock,
183
+ )
184
+ raw = entry.get("pricing")
185
+ if not isinstance(raw, dict):
186
+ raise ProviderProbeError("probe_protocol_invalid")
187
+ pricing = OpenRouterPricing(prompt=raw.get("prompt"), completion=raw.get("completion"))
188
+ return OpenRouterObservation(identity, pricing)