@oneciel-ai/ciel-runtime 0.1.1 → 0.2.0

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 (287) hide show
  1. package/README.md +132 -0
  2. package/ciel-runtime-menu.py +56 -6
  3. package/ciel_runtime.py +9379 -35909
  4. package/ciel_runtime_support/advisor_client.py +193 -0
  5. package/ciel_runtime_support/advisor_policy.py +320 -0
  6. package/ciel_runtime_support/advisor_refinement.py +160 -0
  7. package/ciel_runtime_support/advisor_request_builder.py +261 -0
  8. package/ciel_runtime_support/agy_installer.py +169 -0
  9. package/ciel_runtime_support/agy_mcp_restore.py +182 -0
  10. package/ciel_runtime_support/anthropic_model_policy.py +186 -0
  11. package/ciel_runtime_support/anthropic_response_writer.py +255 -0
  12. package/ciel_runtime_support/anthropic_tool_turns.py +130 -0
  13. package/ciel_runtime_support/api_key_cooldown.py +159 -0
  14. package/ciel_runtime_support/architecture.py +488 -1
  15. package/ciel_runtime_support/architecture_budget.py +42 -0
  16. package/ciel_runtime_support/channel_backlog.py +90 -0
  17. package/ciel_runtime_support/channel_cli.py +119 -0
  18. package/ciel_runtime_support/channel_compact_injection.py +82 -0
  19. package/ciel_runtime_support/channel_compact_poll.py +67 -0
  20. package/ciel_runtime_support/channel_compact_request_repository.py +113 -0
  21. package/ciel_runtime_support/channel_config_service.py +281 -0
  22. package/ciel_runtime_support/channel_connection_lifecycle.py +180 -0
  23. package/ciel_runtime_support/channel_connection_registry.py +128 -0
  24. package/ciel_runtime_support/channel_connection_worker.py +284 -0
  25. package/ciel_runtime_support/channel_cursor_recovery.py +92 -0
  26. package/ciel_runtime_support/channel_cursor_repository.py +89 -0
  27. package/ciel_runtime_support/channel_cursor_service.py +178 -0
  28. package/ciel_runtime_support/channel_event_identity.py +212 -0
  29. package/ciel_runtime_support/channel_event_projection.py +315 -0
  30. package/ciel_runtime_support/channel_inflight.py +127 -0
  31. package/ciel_runtime_support/channel_injection.py +115 -0
  32. package/ciel_runtime_support/channel_launch_guard_repository.py +58 -0
  33. package/ciel_runtime_support/channel_launch_policy.py +180 -0
  34. package/ciel_runtime_support/channel_llm_context.py +156 -0
  35. package/ciel_runtime_support/channel_mcp_discovery.py +186 -0
  36. package/ciel_runtime_support/channel_mcp_http_controller.py +239 -0
  37. package/ciel_runtime_support/channel_mcp_ownership.py +148 -0
  38. package/ciel_runtime_support/channel_mcp_tools.py +240 -0
  39. package/ciel_runtime_support/channel_mcp_transport.py +394 -0
  40. package/ciel_runtime_support/channel_message_dedupe.py +65 -0
  41. package/ciel_runtime_support/channel_message_policy.py +256 -0
  42. package/ciel_runtime_support/channel_message_prompt.py +305 -0
  43. package/ciel_runtime_support/channel_message_repository.py +234 -0
  44. package/ciel_runtime_support/channel_notification_projection.py +217 -0
  45. package/ciel_runtime_support/channel_panel.py +162 -0
  46. package/ciel_runtime_support/channel_pending_injection.py +209 -0
  47. package/ciel_runtime_support/channel_pending_poll.py +109 -0
  48. package/ciel_runtime_support/channel_probe_cache.py +433 -0
  49. package/ciel_runtime_support/channel_probe_report.py +101 -0
  50. package/ciel_runtime_support/channel_runtime_environment.py +181 -0
  51. package/ciel_runtime_support/channel_session_lifecycle.py +114 -0
  52. package/ciel_runtime_support/channel_session_repository.py +90 -0
  53. package/ciel_runtime_support/channel_terminal_dispatch.py +115 -0
  54. package/ciel_runtime_support/channel_terminal_input.py +277 -0
  55. package/ciel_runtime_support/channel_terminal_proxy.py +447 -0
  56. package/ciel_runtime_support/channel_tool_context.py +166 -0
  57. package/ciel_runtime_support/channel_transcript.py +414 -0
  58. package/ciel_runtime_support/channel_transcript_repository.py +96 -0
  59. package/ciel_runtime_support/channel_wake_claim_repository.py +126 -0
  60. package/ciel_runtime_support/channel_wake_delivery_repository.py +88 -0
  61. package/ciel_runtime_support/chat_files.py +138 -0
  62. package/ciel_runtime_support/chat_http_controller.py +235 -0
  63. package/ciel_runtime_support/claude_environment.py +375 -0
  64. package/ciel_runtime_support/claude_router.py +247 -193
  65. package/ciel_runtime_support/cli_dispatch.py +792 -0
  66. package/ciel_runtime_support/cli_parser.py +165 -0
  67. package/ciel_runtime_support/cli_usage.py +100 -0
  68. package/ciel_runtime_support/codex_app_server.py +20 -5
  69. package/ciel_runtime_support/codex_channel_sse_launch.py +87 -0
  70. package/ciel_runtime_support/codex_cli.py +42 -6
  71. package/ciel_runtime_support/codex_config.py +323 -0
  72. package/ciel_runtime_support/codex_launch_configuration.py +240 -0
  73. package/ciel_runtime_support/codex_launch_policy.py +66 -0
  74. package/ciel_runtime_support/codex_mcp_integration.py +195 -0
  75. package/ciel_runtime_support/codex_mcp_restore.py +304 -0
  76. package/ciel_runtime_support/codex_model_catalog.py +133 -0
  77. package/ciel_runtime_support/codex_process_lifecycle.py +271 -0
  78. package/ciel_runtime_support/codex_router.py +147 -1
  79. package/ciel_runtime_support/codex_session_repository.py +115 -0
  80. package/ciel_runtime_support/codex_session_selection.py +114 -0
  81. package/ciel_runtime_support/command_asset_installer.py +103 -0
  82. package/ciel_runtime_support/compatibility_probe.py +295 -0
  83. package/ciel_runtime_support/compatibility_protocol.py +251 -0
  84. package/ciel_runtime_support/compatibility_runtime.py +166 -0
  85. package/ciel_runtime_support/compatibility_test.py +370 -0
  86. package/ciel_runtime_support/config_migrations.py +307 -0
  87. package/ciel_runtime_support/config_repository.py +175 -0
  88. package/ciel_runtime_support/config_value_codec.py +64 -0
  89. package/ciel_runtime_support/configuration_cli.py +374 -0
  90. package/ciel_runtime_support/context_compaction.py +280 -0
  91. package/ciel_runtime_support/context_setup.py +208 -0
  92. package/ciel_runtime_support/context_summary_policy.py +392 -0
  93. package/ciel_runtime_support/credential_cli.py +104 -0
  94. package/ciel_runtime_support/credential_management.py +261 -0
  95. package/ciel_runtime_support/credentials.py +269 -0
  96. package/ciel_runtime_support/executable_discovery.py +141 -0
  97. package/ciel_runtime_support/github_copilot_oauth.py +335 -0
  98. package/ciel_runtime_support/github_copilot_oauth_runtime.py +213 -0
  99. package/ciel_runtime_support/header_forwarding.py +73 -0
  100. package/ciel_runtime_support/headless_config.py +221 -0
  101. package/ciel_runtime_support/http_response.py +129 -0
  102. package/ciel_runtime_support/install_diagnostics.py +149 -0
  103. package/ciel_runtime_support/kimi_identity.py +123 -0
  104. package/ciel_runtime_support/launch_diagnostics.py +204 -0
  105. package/ciel_runtime_support/launch_state.py +127 -0
  106. package/ciel_runtime_support/live_api_key_controller.py +58 -0
  107. package/ciel_runtime_support/llm_config_http.py +148 -0
  108. package/ciel_runtime_support/llm_option_config.py +259 -0
  109. package/ciel_runtime_support/llm_presentation_data.py +447 -0
  110. package/ciel_runtime_support/llm_presets.py +773 -0
  111. package/ciel_runtime_support/lm_studio_runtime.py +401 -0
  112. package/ciel_runtime_support/managed_mcp_config.py +144 -0
  113. package/ciel_runtime_support/managed_mcp_discovery.py +151 -0
  114. package/ciel_runtime_support/managed_service_cleanup.py +89 -0
  115. package/ciel_runtime_support/mcp_config_reader.py +230 -0
  116. package/ciel_runtime_support/mcp_http_proxy.py +607 -0
  117. package/ciel_runtime_support/mcp_inventory.py +59 -0
  118. package/ciel_runtime_support/mcp_notification_wait_policy.py +159 -0
  119. package/ciel_runtime_support/mcp_probe_codec.py +136 -0
  120. package/ciel_runtime_support/mcp_probe_transport.py +328 -0
  121. package/ciel_runtime_support/mcp_proxy_codec.py +345 -0
  122. package/ciel_runtime_support/mcp_proxy_config.py +107 -0
  123. package/ciel_runtime_support/mcp_proxy_notifications.py +261 -0
  124. package/ciel_runtime_support/mcp_proxy_process.py +560 -0
  125. package/ciel_runtime_support/mcp_split_proxy_http.py +165 -0
  126. package/ciel_runtime_support/mcp_stdio_probe.py +240 -0
  127. package/ciel_runtime_support/mcp_transport.py +146 -0
  128. package/ciel_runtime_support/model_cache_lifecycle.py +78 -0
  129. package/ciel_runtime_support/model_catalog_projection.py +61 -0
  130. package/ciel_runtime_support/model_context_hints.py +109 -0
  131. package/ciel_runtime_support/model_panel.py +147 -0
  132. package/ciel_runtime_support/model_registry_repository.py +231 -0
  133. package/ciel_runtime_support/npm_runtime.py +191 -0
  134. package/ciel_runtime_support/ollama_catalog.py +462 -0
  135. package/ciel_runtime_support/ollama_catalog_cli.py +41 -0
  136. package/ciel_runtime_support/ollama_catalog_repository.py +75 -0
  137. package/ciel_runtime_support/ollama_context_sync.py +87 -0
  138. package/ciel_runtime_support/ollama_forwarding.py +449 -0
  139. package/ciel_runtime_support/openai_chat_passthrough.py +67 -0
  140. package/ciel_runtime_support/openai_chat_router.py +64 -0
  141. package/ciel_runtime_support/openai_forwarding.py +194 -0
  142. package/ciel_runtime_support/openai_responses_router.py +291 -0
  143. package/ciel_runtime_support/openai_responses_stream.py +135 -0
  144. package/ciel_runtime_support/output_budget.py +89 -0
  145. package/ciel_runtime_support/package_lifecycle.py +217 -0
  146. package/ciel_runtime_support/plan_artifact_controller.py +104 -0
  147. package/ciel_runtime_support/prelaunch.py +959 -0
  148. package/ciel_runtime_support/prelaunch_launch_preference.py +75 -0
  149. package/ciel_runtime_support/prelaunch_panel_projection.py +396 -0
  150. package/ciel_runtime_support/prelaunch_terminal.py +764 -0
  151. package/ciel_runtime_support/process_control.py +708 -0
  152. package/ciel_runtime_support/prompt_compaction.py +322 -0
  153. package/ciel_runtime_support/prompt_injection.py +176 -0
  154. package/ciel_runtime_support/protocols/__init__.py +24 -0
  155. package/ciel_runtime_support/protocols/anthropic_content.py +29 -0
  156. package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +336 -0
  157. package/ciel_runtime_support/protocols/chat_projection.py +315 -0
  158. package/ciel_runtime_support/protocols/conversation_policy.py +217 -0
  159. package/ciel_runtime_support/protocols/conversation_turn_policy.py +972 -0
  160. package/ciel_runtime_support/protocols/ollama_chat.py +111 -0
  161. package/ciel_runtime_support/protocols/ollama_response.py +231 -0
  162. package/ciel_runtime_support/protocols/openai_reasoning.py +75 -0
  163. package/ciel_runtime_support/protocols/openai_responses.py +271 -0
  164. package/ciel_runtime_support/protocols/pseudo_tool_history.py +248 -0
  165. package/ciel_runtime_support/protocols/tool_result_projection.py +112 -0
  166. package/ciel_runtime_support/provider_adapters.py +160 -0
  167. package/ciel_runtime_support/provider_catalog_sources.py +316 -0
  168. package/ciel_runtime_support/provider_choice.py +201 -0
  169. package/ciel_runtime_support/provider_compatibility.py +165 -0
  170. package/ciel_runtime_support/provider_config_mutations.py +361 -0
  171. package/ciel_runtime_support/provider_configuration_service.py +162 -0
  172. package/ciel_runtime_support/provider_context.py +308 -0
  173. package/ciel_runtime_support/provider_contract_projection.py +73 -0
  174. package/ciel_runtime_support/provider_descriptor.py +82 -0
  175. package/ciel_runtime_support/provider_endpoint_policy.py +130 -0
  176. package/ciel_runtime_support/provider_endpoint_probe.py +165 -0
  177. package/ciel_runtime_support/provider_launch_endpoint.py +109 -0
  178. package/ciel_runtime_support/provider_limits.py +457 -0
  179. package/ciel_runtime_support/provider_model_identity.py +139 -0
  180. package/ciel_runtime_support/provider_model_selection.py +431 -0
  181. package/ciel_runtime_support/provider_model_specs.py +142 -0
  182. package/ciel_runtime_support/provider_models.py +263 -0
  183. package/ciel_runtime_support/provider_network.py +176 -0
  184. package/ciel_runtime_support/provider_option_cli.py +238 -0
  185. package/ciel_runtime_support/provider_option_panel.py +275 -0
  186. package/ciel_runtime_support/provider_option_status.py +192 -0
  187. package/ciel_runtime_support/provider_policy.py +101 -0
  188. package/ciel_runtime_support/provider_query_policy.py +67 -0
  189. package/ciel_runtime_support/provider_readiness.py +112 -0
  190. package/ciel_runtime_support/provider_request_access.py +131 -0
  191. package/ciel_runtime_support/provider_request_builder.py +250 -0
  192. package/ciel_runtime_support/provider_responses_passthrough.py +81 -0
  193. package/ciel_runtime_support/provider_runtime_info.py +113 -0
  194. package/ciel_runtime_support/provider_runtime_modes.py +150 -0
  195. package/ciel_runtime_support/provider_sampling_policy.py +46 -0
  196. package/ciel_runtime_support/provider_status.py +145 -0
  197. package/ciel_runtime_support/provider_timeout_policy.py +184 -0
  198. package/ciel_runtime_support/provider_tool_policy.py +145 -0
  199. package/ciel_runtime_support/providers/__init__.py +65 -0
  200. package/ciel_runtime_support/providers/anthropic.py +160 -0
  201. package/ciel_runtime_support/providers/anthropic_catalog.py +119 -0
  202. package/ciel_runtime_support/providers/base.py +232 -0
  203. package/ciel_runtime_support/providers/catalog.py +326 -0
  204. package/ciel_runtime_support/providers/cloud.py +194 -0
  205. package/ciel_runtime_support/providers/constants.py +54 -0
  206. package/ciel_runtime_support/providers/deepseek.py +115 -0
  207. package/ciel_runtime_support/providers/fireworks.py +158 -0
  208. package/ciel_runtime_support/providers/github_copilot_oauth.py +199 -0
  209. package/ciel_runtime_support/providers/kimi.py +297 -0
  210. package/ciel_runtime_support/providers/lm_studio.py +76 -0
  211. package/ciel_runtime_support/providers/meta.py +257 -0
  212. package/ciel_runtime_support/providers/native.py +194 -0
  213. package/ciel_runtime_support/providers/nim.py +69 -0
  214. package/ciel_runtime_support/providers/nvidia.py +158 -0
  215. package/ciel_runtime_support/providers/nvidia_runtime.py +285 -0
  216. package/ciel_runtime_support/providers/ollama.py +181 -0
  217. package/ciel_runtime_support/providers/ollama_context.py +195 -0
  218. package/ciel_runtime_support/providers/ollama_runtime.py +213 -0
  219. package/ciel_runtime_support/providers/opencode.py +220 -0
  220. package/ciel_runtime_support/providers/opencode_go.py +37 -0
  221. package/ciel_runtime_support/providers/openrouter.py +57 -0
  222. package/ciel_runtime_support/providers/vllm.py +65 -0
  223. package/ciel_runtime_support/providers/zai.py +119 -0
  224. package/ciel_runtime_support/pseudo_tool_parser.py +115 -0
  225. package/ciel_runtime_support/rate_limit_policy.py +117 -0
  226. package/ciel_runtime_support/rate_limit_repository.py +154 -0
  227. package/ciel_runtime_support/registry.py +46 -0
  228. package/ciel_runtime_support/request_shortcuts.py +253 -0
  229. package/ciel_runtime_support/request_trace.py +323 -0
  230. package/ciel_runtime_support/response_collection.py +209 -0
  231. package/ciel_runtime_support/router_access.py +238 -0
  232. package/ciel_runtime_support/router_client_lifecycle.py +366 -0
  233. package/ciel_runtime_support/router_health_policy.py +101 -0
  234. package/ciel_runtime_support/router_http.py +513 -0
  235. package/ciel_runtime_support/router_process_lifecycle.py +401 -0
  236. package/ciel_runtime_support/router_rate_limit_service.py +285 -0
  237. package/ciel_runtime_support/router_server_runtime.py +103 -0
  238. package/ciel_runtime_support/router_shortcuts.py +201 -0
  239. package/ciel_runtime_support/routing_fallback.py +73 -0
  240. package/ciel_runtime_support/runtime_activity_repository.py +143 -0
  241. package/ciel_runtime_support/runtime_adapters.py +104 -0
  242. package/ciel_runtime_support/runtime_command_factory.py +73 -0
  243. package/ciel_runtime_support/runtime_compatibility.py +50 -0
  244. package/ciel_runtime_support/runtime_constants.py +178 -0
  245. package/ciel_runtime_support/runtime_launch.py +1602 -0
  246. package/ciel_runtime_support/runtime_llm_options.py +312 -0
  247. package/ciel_runtime_support/runtime_logging.py +161 -0
  248. package/ciel_runtime_support/runtime_paths.py +157 -0
  249. package/ciel_runtime_support/runtime_restart.py +84 -0
  250. package/ciel_runtime_support/runtime_upgrade.py +149 -0
  251. package/ciel_runtime_support/secure_json_repository.py +55 -0
  252. package/ciel_runtime_support/session_import.py +356 -0
  253. package/ciel_runtime_support/settings_repository.py +8 -0
  254. package/ciel_runtime_support/slash_command_assets.py +211 -0
  255. package/ciel_runtime_support/sse_stream.py +57 -0
  256. package/ciel_runtime_support/sse_trace.py +225 -0
  257. package/ciel_runtime_support/statusline_script.py +593 -0
  258. package/ciel_runtime_support/statusline_settings.py +53 -0
  259. package/ciel_runtime_support/stream_chunk_policy.py +18 -0
  260. package/ciel_runtime_support/streaming_anthropic.py +1955 -0
  261. package/ciel_runtime_support/synthetic_tool_policy.py +105 -0
  262. package/ciel_runtime_support/terminal_platform_io.py +127 -0
  263. package/ciel_runtime_support/timeout_profile.py +196 -0
  264. package/ciel_runtime_support/tool_dialects.py +85 -0
  265. package/ciel_runtime_support/tool_exposure_policy.py +63 -0
  266. package/ciel_runtime_support/tool_guard_hooks.py +218 -0
  267. package/ciel_runtime_support/tool_request_projection.py +96 -0
  268. package/ciel_runtime_support/tool_schema.py +483 -0
  269. package/ciel_runtime_support/tool_side_effect_dedupe.py +95 -0
  270. package/ciel_runtime_support/ui_text.py +266 -0
  271. package/ciel_runtime_support/upstream_error_policy.py +104 -0
  272. package/ciel_runtime_support/upstream_retry.py +419 -0
  273. package/ciel_runtime_support/upstream_stream_io.py +106 -0
  274. package/ciel_runtime_support/usage_events.py +96 -0
  275. package/ciel_runtime_support/visible_stream_filters.py +130 -0
  276. package/ciel_runtime_support/web_endpoints.py +447 -0
  277. package/ciel_runtime_support/web_ui.py +915 -0
  278. package/ciel_runtime_support/web_ui_controller.py +189 -0
  279. package/ciel_runtime_support/windows_console_input.py +137 -0
  280. package/ciel_runtime_support/windows_console_mode.py +112 -0
  281. package/docs/Architecture.md +54 -0
  282. package/docs/Configuration.md +17 -0
  283. package/docs/Module-Map.md +1093 -26
  284. package/docs/Providers.md +46 -1
  285. package/docs/adr/0001-runtime-bounded-contexts.md +295 -0
  286. package/npm-bin/run-ciel-runtime.js +22 -2
  287. package/package.json +9 -2
@@ -0,0 +1,165 @@
1
+ """Provider endpoint route adapter and compatibility probe policy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class ProviderEndpointRoutePorts:
12
+ decorate_headers: Callable[[dict[str, str]], dict[str, str]]
13
+ request: Callable[..., Any]
14
+ urlopen: Callable[..., Any]
15
+ http_error: type[BaseException]
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class ProviderEndpointRouteAdapter:
20
+ ports: ProviderEndpointRoutePorts
21
+
22
+ def exists(
23
+ self,
24
+ url: str,
25
+ headers: dict[str, str],
26
+ timeout: float = 1.5,
27
+ ) -> bool | None:
28
+ request = self.ports.request(
29
+ url,
30
+ data=b"{}",
31
+ headers=self.ports.decorate_headers(headers),
32
+ method="POST",
33
+ )
34
+ try:
35
+ with self.ports.urlopen(request, timeout=timeout):
36
+ return True
37
+ except self.ports.http_error as exc:
38
+ try:
39
+ exc.read() # type: ignore[attr-defined]
40
+ except Exception:
41
+ pass
42
+ code = getattr(exc, "code", None)
43
+ if code == 404:
44
+ return False
45
+ if code in {400, 401, 403, 405, 422}:
46
+ return True
47
+ return None
48
+ except Exception:
49
+ return None
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class ProviderEndpointProbeProjection:
54
+ upstream_base: Callable[[str, dict[str, Any]], str]
55
+ native_base: Callable[[str, dict[str, Any]], str]
56
+ join_url: Callable[[str, str], str]
57
+
58
+
59
+ @dataclass(frozen=True, slots=True)
60
+ class ProviderEndpointProbeQueries:
61
+ primary_headers: Callable[[str, dict[str, Any]], dict[str, str]]
62
+ fallback_headers: Callable[[str, dict[str, Any]], dict[str, str]]
63
+ route_exists: Callable[[str, dict[str, str], float], bool | None]
64
+
65
+
66
+ @dataclass(frozen=True, slots=True)
67
+ class ProviderEndpointProbePolicy:
68
+ projection: ProviderEndpointProbeProjection
69
+ query: ProviderEndpointProbeQueries
70
+
71
+ @staticmethod
72
+ def status_label(value: bool | None) -> str:
73
+ if value is True:
74
+ return "available"
75
+ if value is False:
76
+ return "missing"
77
+ return "inconclusive"
78
+
79
+ def headers_for(
80
+ self,
81
+ provider: str,
82
+ config: dict[str, Any],
83
+ ) -> dict[str, str]:
84
+ try:
85
+ return self.query.primary_headers(provider, config)
86
+ except Exception:
87
+ return self.query.fallback_headers(provider, config)
88
+
89
+ def _routes(
90
+ self,
91
+ provider: str,
92
+ config: dict[str, Any],
93
+ timeout: float,
94
+ headers: dict[str, str] | None = None,
95
+ ) -> tuple[str, str, bool | None, bool | None]:
96
+ request_headers = (
97
+ headers
98
+ if headers is not None
99
+ else self.headers_for(provider, config)
100
+ )
101
+ anthropic_url = self.projection.join_url(
102
+ self.projection.native_base(provider, config),
103
+ "/v1/messages",
104
+ )
105
+ openai_url = self.projection.join_url(
106
+ self.projection.upstream_base(provider, config),
107
+ "/v1/chat/completions",
108
+ )
109
+ return (
110
+ anthropic_url,
111
+ openai_url,
112
+ self.query.route_exists(anthropic_url, request_headers, timeout),
113
+ self.query.route_exists(openai_url, request_headers, timeout),
114
+ )
115
+
116
+ def detect_native_compat(
117
+ self,
118
+ provider: str,
119
+ config: dict[str, Any],
120
+ supported_providers: frozenset[str],
121
+ ) -> tuple[bool | None, str]:
122
+ if provider not in supported_providers:
123
+ return None, ""
124
+ if not self.projection.upstream_base(provider, config):
125
+ return None, "missing base URL"
126
+ _, _, anthropic, openai = self._routes(
127
+ provider,
128
+ config,
129
+ 1.5,
130
+ self.query.fallback_headers(provider, config),
131
+ )
132
+ if anthropic is True:
133
+ return True, "Anthropic Messages route detected"
134
+ if openai is True and anthropic is False:
135
+ return False, "OpenAI chat completions route detected"
136
+ if openai is True:
137
+ return (
138
+ None,
139
+ "OpenAI route detected, Anthropic route inconclusive; "
140
+ "keeping Anthropic default",
141
+ )
142
+ return None, "endpoint family inconclusive; keeping Anthropic default"
143
+
144
+ def report(
145
+ self,
146
+ provider: str,
147
+ config: dict[str, Any],
148
+ excluded_providers: frozenset[str],
149
+ timeout: float = 1.5,
150
+ ) -> list[str]:
151
+ if provider in excluded_providers:
152
+ return []
153
+ probe_timeout = max(0.25, min(float(timeout or 1.5), 3.0))
154
+ anthropic_url, openai_url, anthropic, openai = self._routes(
155
+ provider,
156
+ config,
157
+ probe_timeout,
158
+ )
159
+ return [
160
+ "Endpoint probes:",
161
+ "- Anthropic Messages (/v1/messages): %s (%s)"
162
+ % (self.status_label(anthropic), anthropic_url),
163
+ "- OpenAI Chat (/v1/chat/completions): %s (%s)"
164
+ % (self.status_label(openai), openai_url),
165
+ ]
@@ -0,0 +1,109 @@
1
+ """Runtime-specific provider endpoint selection policy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+ from ciel_runtime_support.providers.constants import OPENCODE_PROVIDER_NAMES
10
+ from ciel_runtime_support.runtime_constants import (
11
+ AUTO_DETECT_NATIVE_COMPAT_PROVIDERS,
12
+ CLAUDE_ANTHROPIC_ENDPOINT_PROVIDERS,
13
+ CODEX_OPENAI_COMPATIBLE_ROUTER_PROVIDERS,
14
+ )
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class ProviderLaunchEndpointGroups:
19
+ native_runtimes: frozenset[str]
20
+ auto_detect: frozenset[str]
21
+ claude_anthropic: frozenset[str]
22
+ codex_openai: frozenset[str]
23
+ model_specific: frozenset[str]
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class ProviderLaunchEndpointQueries:
28
+ detect_native_compat: Callable[
29
+ [str, dict[str, Any]], tuple[bool | None, str]
30
+ ]
31
+ endpoint_kind: Callable[[str, str, dict[str, Any]], str]
32
+
33
+
34
+ def build_default_provider_launch_endpoint_policy(
35
+ query: ProviderLaunchEndpointQueries,
36
+ ) -> ProviderLaunchEndpointPolicy:
37
+ """Build immutable provider families for runtime endpoint selection."""
38
+ return ProviderLaunchEndpointPolicy(
39
+ groups=ProviderLaunchEndpointGroups(
40
+ native_runtimes=frozenset(
41
+ {"anthropic", "codex", "agy", "ollama", "ollama-cloud"}
42
+ ),
43
+ auto_detect=frozenset(AUTO_DETECT_NATIVE_COMPAT_PROVIDERS),
44
+ claude_anthropic=frozenset(CLAUDE_ANTHROPIC_ENDPOINT_PROVIDERS),
45
+ codex_openai=frozenset(CODEX_OPENAI_COMPATIBLE_ROUTER_PROVIDERS),
46
+ model_specific=frozenset(OPENCODE_PROVIDER_NAMES),
47
+ ),
48
+ query=query,
49
+ )
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class ProviderLaunchEndpointPolicy:
54
+ groups: ProviderLaunchEndpointGroups
55
+ query: ProviderLaunchEndpointQueries
56
+
57
+ def preferred_native_compat(
58
+ self,
59
+ runtime: str,
60
+ provider: str,
61
+ config: dict[str, Any],
62
+ ) -> tuple[bool | None, str]:
63
+ runtime = str(runtime or "").strip().casefold()
64
+ if provider in self.groups.native_runtimes:
65
+ return None, ""
66
+ if runtime == "claude":
67
+ if provider in self.groups.auto_detect:
68
+ return self.query.detect_native_compat(provider, config)
69
+ if provider in self.groups.claude_anthropic:
70
+ return (
71
+ True,
72
+ "Claude Code prefers the provider's Anthropic Messages "
73
+ "compatible endpoint",
74
+ )
75
+ if provider in self.groups.model_specific:
76
+ endpoint = self.query.endpoint_kind(
77
+ provider,
78
+ str(config.get("current_model") or ""),
79
+ config,
80
+ )
81
+ if endpoint == "anthropic-messages":
82
+ return (
83
+ True,
84
+ "Claude Code prefers the model's Anthropic Messages "
85
+ "endpoint",
86
+ )
87
+ if endpoint == "openai-chat":
88
+ return False, "selected model uses an OpenAI Chat endpoint"
89
+ return None, ""
90
+ if runtime in {"codex", "codex-app-server"}:
91
+ if provider in self.groups.model_specific:
92
+ endpoint = self.query.endpoint_kind(
93
+ provider,
94
+ str(config.get("current_model") or ""),
95
+ config,
96
+ )
97
+ if endpoint == "openai-chat":
98
+ return (
99
+ False,
100
+ "Codex prefers the model's OpenAI Chat compatible "
101
+ "endpoint",
102
+ )
103
+ return None, ""
104
+ if provider in self.groups.codex_openai:
105
+ return (
106
+ False,
107
+ "Codex prefers OpenAI Chat compatible upstream routing",
108
+ )
109
+ return None, ""