@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
@@ -7,98 +7,142 @@ import os
7
7
  import time
8
8
  import urllib.error
9
9
  from collections.abc import Mapping
10
+ from dataclasses import dataclass
10
11
  from typing import Any, Callable
11
12
 
12
13
  from .agent_router import COMMON_RUNTIME_ROUTER_CAPABILITIES, RouterCapability
13
14
 
14
15
 
15
- CLAUDE_RUNTIME_DEPENDENCIES: tuple[str, ...] = (
16
- "EVENT_BUS",
17
- "OPENCODE_PROVIDER_NAMES",
18
- "PROVIDER_LABELS",
19
- "_rebatch_anthropic_sse_text",
20
- "_update_tool_schema_registry",
21
- "append_synthetic_tasklist_to_message",
22
- "apply_provider_request_options",
23
- "apply_router_rate_limit",
24
- "begin_pending_channel_delivery",
25
- "body_with_channel_tool_result_context",
26
- "body_with_pending_channel_messages",
27
- "body_without_ciel_runtime_internal_metadata",
28
- "cap_anthropic_body_for_provider",
29
- "commit_pending_channel_delivery_cursors",
30
- "dump_request_for_trace",
31
- "estimate_tokens",
32
- "filter_blocked_tools",
33
- "forward_ollama_api_chat",
34
- "forward_openai_compatible_chat",
35
- "is_client_disconnect_error",
36
- "join_url",
37
- "key_from_request_headers",
38
- "mark_pending_channel_delivery_failed",
39
- "mark_pending_channel_delivery_success",
40
- "maybe_handle_advisor_request",
41
- "maybe_handle_channel_clear_request",
42
- "maybe_handle_import_session_request",
43
- "maybe_handle_live_api_keys_request",
44
- "maybe_handle_live_llm_options_request",
45
- "maybe_handle_plan_mode_tool_choice",
46
- "maybe_handle_router_debug_request",
47
- "maybe_handle_version_request",
48
- "native_anthropic_base_url",
49
- "ncp_model_id_for_nvidia_hosted",
50
- "normalize_anthropic_model_request_options",
51
- "normalize_anthropic_system_role_messages",
52
- "normalize_request_for_provider_wire",
53
- "normalize_response_thinking_for_non_anthropic_provider",
54
- "normalize_thinking_for_non_anthropic_provider",
55
- "normalize_tool_choice_for_provider",
56
- "open_provider_request_with_key_retry",
57
- "opencode_endpoint_kind",
58
- "prepend_anthropic_text",
59
- "preserves_anthropic_thinking_contract",
60
- "provider_headers",
61
- "provider_native_compat_enabled",
62
- "provider_openai_router_enabled",
63
- "provider_request_timeout_seconds",
64
- "provider_stream_idle_timeout_seconds",
65
- "provider_upstream_request_base",
66
- "rate_limit_notice",
67
- "register_api_key_cooldown",
68
- "rehydrate_suppressed_thinking_passback",
69
- "resolve_requested_model",
70
- "resolve_tool_model_references",
71
- "router_event_message_preview",
72
- "router_log",
73
- "set_upstream_stream_read_timeout",
74
- "should_normalize_anthropic_stream_tool_use",
75
- "strip_autonomous_advisor_server_tools",
76
- "try_write_json",
77
- "upstream_messages_query",
78
- "write_context_usage",
79
- "write_json",
80
- "write_router_activity",
81
- )
82
-
83
-
84
- def missing_claude_runtime_dependencies(deps: Mapping[str, Any]) -> list[str]:
85
- return [name for name in CLAUDE_RUNTIME_DEPENDENCIES if name not in deps]
16
+ @dataclass(frozen=True, slots=True)
17
+ class ClaudeRouterCore:
18
+ event_bus: Any
19
+ log: Callable[..., Any]
20
+ try_write_json: Callable[..., Any]
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class ClaudeRouterCountTokens:
25
+ estimate_tokens: Callable[..., Any]
26
+ write_context_usage: Callable[..., Any]
27
+ write_json: Callable[..., Any]
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class ClaudeRouterPipeline:
32
+ update_tool_schema_registry: Callable[..., Any]
33
+ router_event_message_preview: Callable[..., Any]
34
+ dump_request_for_trace: Callable[..., Any]
35
+ filter_blocked_tools: Callable[..., Any]
36
+ normalize_tool_choice: Callable[..., Any]
37
+ write_context_usage: Callable[..., Any]
38
+ strip_advisor_tools: Callable[..., Any]
39
+ inject_channel_context: Callable[..., Any]
40
+ inject_tool_result_context: Callable[..., Any]
41
+
42
+
43
+ @dataclass(frozen=True, slots=True)
44
+ class ClaudeRouterShortcuts:
45
+ plan_mode: Callable[..., Any]
46
+ router_debug: Callable[..., Any]
47
+ version: Callable[..., Any]
48
+ channel_clear: Callable[..., Any]
49
+ import_session: Callable[..., Any]
50
+ llm_options: Callable[..., Any]
51
+ api_keys: Callable[..., Any]
52
+ advisor: Callable[..., Any]
53
+
54
+
55
+ @dataclass(frozen=True, slots=True)
56
+ class ClaudeRouterDelivery:
57
+ begin: Callable[..., Any]
58
+ commit: Callable[..., Any]
59
+ mark_failed: Callable[..., Any]
60
+ mark_success: Callable[..., Any]
61
+ is_client_disconnect: Callable[..., Any]
62
+ write_activity: Callable[..., Any]
63
+
64
+
65
+ @dataclass(frozen=True, slots=True)
66
+ class ClaudeRouterRouting:
67
+ forward_ollama: Callable[..., Any]
68
+ forward_openai: Callable[..., Any]
69
+ select_protocol: Callable[..., Any]
70
+ request_policy: Callable[..., Any]
71
+ resolve_model: Callable[..., Any]
72
+ provider_labels: Mapping[str, str]
73
+ write_json: Callable[..., Any]
74
+
75
+
76
+ @dataclass(frozen=True, slots=True)
77
+ class ClaudeRouterNativeNormalization:
78
+ normalize_provider_wire: Callable[..., Any]
79
+ normalize_thinking: Callable[..., Any]
80
+ normalize_system_roles: Callable[..., Any]
81
+ cap_body: Callable[..., Any]
82
+ apply_request_options: Callable[..., Any]
83
+ rehydrate_thinking: Callable[..., Any]
84
+ ncp_model_id: Callable[..., Any]
85
+ resolve_tool_models: Callable[..., Any]
86
+ normalize_model_options: Callable[..., Any]
87
+ strip_internal_metadata: Callable[..., Any]
88
+
89
+
90
+ @dataclass(frozen=True, slots=True)
91
+ class ClaudeRouterTransport:
92
+ native_base_url: Callable[..., Any]
93
+ native_compat_enabled: Callable[..., Any]
94
+ upstream_base: Callable[..., Any]
95
+ join_url: Callable[..., Any]
96
+ upstream_query: Callable[..., Any]
97
+ provider_headers: Callable[..., Any]
98
+ apply_rate_limit: Callable[..., Any]
99
+ open_request: Callable[..., Any]
100
+ request_timeout: Callable[..., Any]
101
+ idle_timeout: Callable[..., Any]
102
+
103
+
104
+ @dataclass(frozen=True, slots=True)
105
+ class ClaudeRouterResponse:
106
+ rebatch_sse: Callable[..., Any]
107
+ preserves_thinking: Callable[..., Any]
108
+ normalize_stream_tool_use: Callable[..., Any]
109
+ set_stream_timeout: Callable[..., Any]
110
+ normalize_thinking: Callable[..., Any]
111
+ append_tasklist: Callable[..., Any]
112
+ prepend_text: Callable[..., Any]
113
+ rate_limit_notice: Callable[..., Any]
114
+ register_key_cooldown: Callable[..., Any]
115
+ key_from_headers: Callable[..., Any]
116
+
117
+
118
+ @dataclass(frozen=True, slots=True)
119
+ class ClaudeRouterServices:
120
+ core: ClaudeRouterCore
121
+ count_tokens: ClaudeRouterCountTokens
122
+ pipeline: ClaudeRouterPipeline
123
+ shortcuts: ClaudeRouterShortcuts
124
+ delivery: ClaudeRouterDelivery
125
+ routing: ClaudeRouterRouting
126
+ normalization: ClaudeRouterNativeNormalization
127
+ transport: ClaudeRouterTransport
128
+ response: ClaudeRouterResponse
86
129
 
87
130
 
88
131
  def handle_claude_count_tokens_post(
89
- deps: Mapping[str, Any],
132
+ services: ClaudeRouterServices,
90
133
  handler: Any,
91
134
  provider: str,
92
135
  pcfg: dict[str, Any],
93
136
  body: dict[str, Any],
94
137
  ) -> None:
95
- tokens = deps["estimate_tokens"](body)
96
- deps["write_context_usage"](provider, pcfg, body, "count_tokens")
97
- deps["write_json"](handler, {"input_tokens": tokens})
138
+ count = services.count_tokens
139
+ tokens = count.estimate_tokens(body)
140
+ count.write_context_usage(provider, pcfg, body, "count_tokens")
141
+ count.write_json(handler, {"input_tokens": tokens})
98
142
 
99
143
 
100
144
  def handle_claude_messages_post(
101
- deps: Mapping[str, Any],
145
+ services: ClaudeRouterServices,
102
146
  handler: Any,
103
147
  cfg: dict[str, Any],
104
148
  provider: str,
@@ -106,70 +150,77 @@ def handle_claude_messages_post(
106
150
  path: str,
107
151
  body: dict[str, Any],
108
152
  ) -> None:
109
- _rebatch_anthropic_sse_text = deps["_rebatch_anthropic_sse_text"]
110
- _update_tool_schema_registry = deps["_update_tool_schema_registry"]
111
- append_synthetic_tasklist_to_message = deps["append_synthetic_tasklist_to_message"]
112
- apply_provider_request_options = deps["apply_provider_request_options"]
113
- apply_router_rate_limit = deps["apply_router_rate_limit"]
114
- begin_pending_channel_delivery = deps["begin_pending_channel_delivery"]
115
- body_with_channel_tool_result_context = deps["body_with_channel_tool_result_context"]
116
- body_with_pending_channel_messages = deps["body_with_pending_channel_messages"]
117
- body_without_ciel_runtime_internal_metadata = deps["body_without_ciel_runtime_internal_metadata"]
118
- cap_anthropic_body_for_provider = deps["cap_anthropic_body_for_provider"]
119
- commit_pending_channel_delivery_cursors = deps["commit_pending_channel_delivery_cursors"]
120
- dump_request_for_trace = deps["dump_request_for_trace"]
121
- event_bus = deps["EVENT_BUS"]
122
- filter_blocked_tools = deps["filter_blocked_tools"]
123
- forward_ollama_api_chat = deps["forward_ollama_api_chat"]
124
- forward_openai_compatible_chat = deps["forward_openai_compatible_chat"]
125
- is_client_disconnect_error = deps["is_client_disconnect_error"]
126
- join_url = deps["join_url"]
127
- key_from_request_headers = deps["key_from_request_headers"]
128
- mark_pending_channel_delivery_failed = deps["mark_pending_channel_delivery_failed"]
129
- mark_pending_channel_delivery_success = deps["mark_pending_channel_delivery_success"]
130
- maybe_handle_advisor_request = deps["maybe_handle_advisor_request"]
131
- maybe_handle_channel_clear_request = deps["maybe_handle_channel_clear_request"]
132
- maybe_handle_import_session_request = deps["maybe_handle_import_session_request"]
133
- maybe_handle_live_api_keys_request = deps["maybe_handle_live_api_keys_request"]
134
- maybe_handle_live_llm_options_request = deps["maybe_handle_live_llm_options_request"]
135
- maybe_handle_plan_mode_tool_choice = deps["maybe_handle_plan_mode_tool_choice"]
136
- maybe_handle_router_debug_request = deps["maybe_handle_router_debug_request"]
137
- maybe_handle_version_request = deps["maybe_handle_version_request"]
138
- native_anthropic_base_url = deps["native_anthropic_base_url"]
139
- ncp_model_id_for_nvidia_hosted = deps["ncp_model_id_for_nvidia_hosted"]
140
- normalize_anthropic_model_request_options = deps["normalize_anthropic_model_request_options"]
141
- normalize_anthropic_system_role_messages = deps["normalize_anthropic_system_role_messages"]
142
- normalize_request_for_provider_wire = deps["normalize_request_for_provider_wire"]
143
- normalize_response_thinking_for_non_anthropic_provider = deps["normalize_response_thinking_for_non_anthropic_provider"]
144
- normalize_thinking_for_non_anthropic_provider = deps["normalize_thinking_for_non_anthropic_provider"]
145
- normalize_tool_choice_for_provider = deps["normalize_tool_choice_for_provider"]
146
- open_provider_request_with_key_retry = deps["open_provider_request_with_key_retry"]
147
- opencode_endpoint_kind = deps["opencode_endpoint_kind"]
148
- opencode_provider_names = deps["OPENCODE_PROVIDER_NAMES"]
149
- prepend_anthropic_text = deps["prepend_anthropic_text"]
150
- preserves_anthropic_thinking_contract = deps["preserves_anthropic_thinking_contract"]
151
- provider_headers = deps["provider_headers"]
152
- provider_labels = deps["PROVIDER_LABELS"]
153
- provider_native_compat_enabled = deps["provider_native_compat_enabled"]
154
- provider_openai_router_enabled = deps["provider_openai_router_enabled"]
155
- provider_request_timeout_seconds = deps["provider_request_timeout_seconds"]
156
- provider_stream_idle_timeout_seconds = deps["provider_stream_idle_timeout_seconds"]
157
- provider_upstream_request_base = deps["provider_upstream_request_base"]
158
- rate_limit_notice = deps["rate_limit_notice"]
159
- register_api_key_cooldown = deps["register_api_key_cooldown"]
160
- rehydrate_suppressed_thinking_passback = deps["rehydrate_suppressed_thinking_passback"]
161
- resolve_requested_model = deps["resolve_requested_model"]
162
- resolve_tool_model_references = deps["resolve_tool_model_references"]
163
- router_event_message_preview = deps["router_event_message_preview"]
164
- router_log = deps["router_log"]
165
- set_upstream_stream_read_timeout = deps["set_upstream_stream_read_timeout"]
166
- should_normalize_anthropic_stream_tool_use = deps["should_normalize_anthropic_stream_tool_use"]
167
- strip_autonomous_advisor_server_tools = deps["strip_autonomous_advisor_server_tools"]
168
- try_write_json = deps["try_write_json"]
169
- upstream_messages_query = deps["upstream_messages_query"]
170
- write_context_usage = deps["write_context_usage"]
171
- write_json = deps["write_json"]
172
- write_router_activity = deps["write_router_activity"]
153
+ core = services.core
154
+ pipeline = services.pipeline
155
+ shortcuts = services.shortcuts
156
+ delivery = services.delivery
157
+ routing = services.routing
158
+ normalization = services.normalization
159
+ transport = services.transport
160
+ response = services.response
161
+ _rebatch_anthropic_sse_text = response.rebatch_sse
162
+ _update_tool_schema_registry = pipeline.update_tool_schema_registry
163
+ append_synthetic_tasklist_to_message = response.append_tasklist
164
+ apply_provider_request_options = normalization.apply_request_options
165
+ apply_router_rate_limit = transport.apply_rate_limit
166
+ begin_pending_channel_delivery = delivery.begin
167
+ body_with_channel_tool_result_context = pipeline.inject_tool_result_context
168
+ body_with_pending_channel_messages = pipeline.inject_channel_context
169
+ body_without_ciel_runtime_internal_metadata = normalization.strip_internal_metadata
170
+ cap_anthropic_body_for_provider = normalization.cap_body
171
+ commit_pending_channel_delivery_cursors = delivery.commit
172
+ dump_request_for_trace = pipeline.dump_request_for_trace
173
+ event_bus = core.event_bus
174
+ filter_blocked_tools = pipeline.filter_blocked_tools
175
+ forward_ollama_api_chat = routing.forward_ollama
176
+ forward_openai_compatible_chat = routing.forward_openai
177
+ is_client_disconnect_error = delivery.is_client_disconnect
178
+ join_url = transport.join_url
179
+ key_from_request_headers = response.key_from_headers
180
+ mark_pending_channel_delivery_failed = delivery.mark_failed
181
+ mark_pending_channel_delivery_success = delivery.mark_success
182
+ maybe_handle_advisor_request = shortcuts.advisor
183
+ maybe_handle_channel_clear_request = shortcuts.channel_clear
184
+ maybe_handle_import_session_request = shortcuts.import_session
185
+ maybe_handle_live_api_keys_request = shortcuts.api_keys
186
+ maybe_handle_live_llm_options_request = shortcuts.llm_options
187
+ maybe_handle_plan_mode_tool_choice = shortcuts.plan_mode
188
+ maybe_handle_router_debug_request = shortcuts.router_debug
189
+ maybe_handle_version_request = shortcuts.version
190
+ native_anthropic_base_url = transport.native_base_url
191
+ ncp_model_id_for_nvidia_hosted = normalization.ncp_model_id
192
+ normalize_anthropic_model_request_options = normalization.normalize_model_options
193
+ normalize_anthropic_system_role_messages = normalization.normalize_system_roles
194
+ normalize_request_for_provider_wire = normalization.normalize_provider_wire
195
+ normalize_response_thinking_for_non_anthropic_provider = response.normalize_thinking
196
+ normalize_thinking_for_non_anthropic_provider = normalization.normalize_thinking
197
+ normalize_tool_choice_for_provider = pipeline.normalize_tool_choice
198
+ open_provider_request_with_key_retry = transport.open_request
199
+ prepend_anthropic_text = response.prepend_text
200
+ preserves_anthropic_thinking_contract = response.preserves_thinking
201
+ provider_headers = transport.provider_headers
202
+ provider_labels = routing.provider_labels
203
+ provider_native_compat_enabled = transport.native_compat_enabled
204
+ provider_request_policy = routing.request_policy
205
+ provider_request_timeout_seconds = transport.request_timeout
206
+ provider_stream_idle_timeout_seconds = transport.idle_timeout
207
+ provider_upstream_request_base = transport.upstream_base
208
+ rate_limit_notice = response.rate_limit_notice
209
+ register_api_key_cooldown = response.register_key_cooldown
210
+ rehydrate_suppressed_thinking_passback = normalization.rehydrate_thinking
211
+ resolve_requested_model = routing.resolve_model
212
+ select_provider_protocol = routing.select_protocol
213
+ resolve_tool_model_references = normalization.resolve_tool_models
214
+ router_event_message_preview = pipeline.router_event_message_preview
215
+ router_log = core.log
216
+ set_upstream_stream_read_timeout = response.set_stream_timeout
217
+ should_normalize_anthropic_stream_tool_use = response.normalize_stream_tool_use
218
+ strip_autonomous_advisor_server_tools = pipeline.strip_advisor_tools
219
+ try_write_json = core.try_write_json
220
+ upstream_messages_query = transport.upstream_query
221
+ write_context_usage = pipeline.write_context_usage
222
+ write_json = routing.write_json
223
+ write_router_activity = delivery.write_activity
173
224
 
174
225
  self = handler
175
226
  _update_tool_schema_registry(body.get("tools"))
@@ -224,55 +275,50 @@ def handle_claude_messages_post(
224
275
  body = normalize_request_for_provider_wire(provider, pcfg, body)
225
276
  router_log("DEBUG", f"POST {path} provider={provider} model={body.get('model')} tools={len(body.get('tools') or [])} msgs={len(body.get('messages') or [])}")
226
277
  try:
227
- if provider in ("ollama", "ollama-cloud"):
228
- event_bus.publish(level="info", category="upstream.request", message="forwarding to Ollama-compatible provider", request_id=request_id, provider=provider, model=str(body.get("model") or ""))
278
+ upstream_model = resolve_requested_model(provider, pcfg, body.get("model"))
279
+ selected_protocol = select_provider_protocol(provider, pcfg, "anthropic_messages", upstream_model)
280
+ provider_label = provider_labels.get(provider, provider)
281
+ if selected_protocol == "ollama_chat":
282
+ event_bus.publish(level="info", category="upstream.request", message="forwarding to Ollama-compatible provider", request_id=request_id, provider=provider, model=upstream_model)
229
283
  forward_ollama_api_chat(self, provider, pcfg, body)
230
284
  commit_pending_channel_delivery_cursors(body, self)
231
285
  return
232
- if provider in opencode_provider_names:
233
- upstream_model = resolve_requested_model(provider, pcfg, body.get("model"))
234
- endpoint_kind = opencode_endpoint_kind(provider, upstream_model, pcfg)
235
- provider_label = provider_labels.get(provider, provider)
236
- if endpoint_kind == "openai-chat":
237
- event_bus.publish(
238
- level="info",
239
- category="upstream.request",
240
- message=f"forwarding to {provider_label} chat-compatible provider",
241
- request_id=request_id,
242
- provider=provider,
243
- model=upstream_model,
244
- )
245
- forward_openai_compatible_chat(self, provider, pcfg, body)
246
- commit_pending_channel_delivery_cursors(body, self)
247
- return
248
- if endpoint_kind not in ("anthropic-messages",):
249
- write_json(
250
- self,
251
- {
252
- "type": "error",
253
- "error": {
254
- "type": "unsupported_model_endpoint",
255
- "message": (
256
- f"{provider_label} model {upstream_model!r} uses the {endpoint_kind} endpoint family. "
257
- f"ciel-runtime currently routes {provider_label} /v1/messages and /v1/chat/completions models."
258
- ),
259
- },
260
- },
261
- 400,
262
- )
263
- return
264
- if provider_openai_router_enabled(provider, pcfg):
265
- event_bus.publish(level="info", category="upstream.request", message="forwarding to OpenAI-compatible provider", request_id=request_id, provider=provider, model=str(body.get("model") or ""))
286
+ if selected_protocol == "openai_chat":
287
+ event_bus.publish(
288
+ level="info",
289
+ category="upstream.request",
290
+ message=f"forwarding to {provider_label} chat-compatible provider",
291
+ request_id=request_id,
292
+ provider=provider,
293
+ model=upstream_model,
294
+ )
266
295
  forward_openai_compatible_chat(self, provider, pcfg, body)
267
296
  commit_pending_channel_delivery_cursors(body, self)
268
297
  return
298
+ if selected_protocol != "anthropic_messages":
299
+ endpoint = str(selected_protocol).replace("_", "-")
300
+ write_json(
301
+ self,
302
+ {
303
+ "type": "error",
304
+ "error": {
305
+ "type": "unsupported_model_endpoint",
306
+ "message": (
307
+ f"{provider_label} model {upstream_model!r} uses the {endpoint} endpoint family. "
308
+ f"ciel-runtime currently routes Anthropic Messages and OpenAI Chat models."
309
+ ),
310
+ },
311
+ },
312
+ 400,
313
+ )
314
+ return
269
315
  body = normalize_thinking_for_non_anthropic_provider(provider, pcfg, body)
270
316
  body = normalize_anthropic_system_role_messages(body)
271
317
  body = cap_anthropic_body_for_provider(provider, pcfg, body)
272
318
  body = apply_provider_request_options(provider, pcfg, body)
273
319
  body = rehydrate_suppressed_thinking_passback(provider, pcfg, body)
274
320
  upstream_model = resolve_requested_model(provider, pcfg, body.get("model"))
275
- if provider == "nvidia-hosted":
321
+ if provider_request_policy(provider, pcfg).model_alias_strategy == "ncp":
276
322
  upstream_model = ncp_model_id_for_nvidia_hosted(upstream_model)
277
323
  body["model"] = upstream_model
278
324
  body = resolve_tool_model_references(provider, pcfg, body)
@@ -287,10 +333,9 @@ def handle_claude_messages_post(
287
333
  upstream_query = upstream_messages_query(pcfg, self.path, provider)
288
334
  if upstream_query:
289
335
  url = f"{url}?{upstream_query}"
290
- headers = provider_headers(provider, pcfg, self.headers)
291
- for h in ("anthropic-beta", "anthropic-dangerous-direct-browser-access"):
292
- if self.headers.get(h):
293
- headers[h] = self.headers[h]
336
+ headers = provider_headers(
337
+ provider, pcfg, self.headers, "anthropic_messages"
338
+ )
294
339
  waited, rpm_used, rpm_limit = apply_router_rate_limit(provider, pcfg, upstream_model)
295
340
  try:
296
341
  event_bus.publish(level="info", category="upstream.request", message="forwarding to Anthropic-compatible provider", request_id=request_id, provider=provider, model=upstream_model, data={"url": url, "stream": bool(body.get("stream", stream_enabled))})
@@ -339,8 +384,12 @@ def handle_claude_messages_post(
339
384
  if notice:
340
385
  payload = prepend_anthropic_text(payload, notice)
341
386
  raw_resp = json.dumps(payload, ensure_ascii=False).encode("utf-8")
342
- except Exception:
343
- pass
387
+ except (UnicodeDecodeError, json.JSONDecodeError, TypeError, ValueError) as exc:
388
+ router_log(
389
+ "WARN",
390
+ "anthropic_response_normalization_skipped "
391
+ f"provider={provider} model={upstream_model} error={type(exc).__name__}: {exc}",
392
+ )
344
393
  self.wfile.write(raw_resp)
345
394
  self.wfile.flush()
346
395
  mark_pending_channel_delivery_success(self, "anthropic_json")
@@ -403,17 +452,14 @@ class ClaudeRouter:
403
452
  def __init__(
404
453
  self,
405
454
  *,
406
- runtime_deps: Mapping[str, Any] | None = None,
455
+ services: ClaudeRouterServices | None = None,
407
456
  handle_count_tokens_post: Callable[[Any, str, dict[str, Any], dict[str, Any]], None] | None = None,
408
457
  handle_messages_post: Callable[[Any, dict[str, Any], str, dict[str, Any], str, dict[str, Any]], None] | None = None,
409
458
  ) -> None:
410
- if runtime_deps is not None:
411
- missing = missing_claude_runtime_dependencies(runtime_deps)
412
- if missing:
413
- raise KeyError(f"ClaudeRouter runtime_deps missing: {', '.join(missing)}")
459
+ if services is not None:
414
460
  self._handle_count_tokens_post = (
415
461
  lambda handler, provider, pcfg, body: handle_claude_count_tokens_post(
416
- runtime_deps,
462
+ services,
417
463
  handler,
418
464
  provider,
419
465
  pcfg,
@@ -422,7 +468,7 @@ class ClaudeRouter:
422
468
  )
423
469
  self._handle_messages_post = (
424
470
  lambda handler, cfg, provider, pcfg, path, body: handle_claude_messages_post(
425
- runtime_deps,
471
+ services,
426
472
  handler,
427
473
  cfg,
428
474
  provider,
@@ -433,7 +479,7 @@ class ClaudeRouter:
433
479
  )
434
480
  return
435
481
  if handle_count_tokens_post is None or handle_messages_post is None:
436
- raise TypeError("ClaudeRouter requires runtime_deps or both post handlers")
482
+ raise TypeError("ClaudeRouter requires services or both post handlers")
437
483
  self._handle_count_tokens_post = handle_count_tokens_post
438
484
  self._handle_messages_post = handle_messages_post
439
485
 
@@ -471,9 +517,17 @@ assert all(any(capability.name == required for capability in ClaudeRouter.capabi
471
517
 
472
518
 
473
519
  __all__ = [
474
- "CLAUDE_RUNTIME_DEPENDENCIES",
475
520
  "ClaudeRouter",
521
+ "ClaudeRouterCore",
522
+ "ClaudeRouterCountTokens",
523
+ "ClaudeRouterDelivery",
524
+ "ClaudeRouterNativeNormalization",
525
+ "ClaudeRouterPipeline",
526
+ "ClaudeRouterResponse",
527
+ "ClaudeRouterRouting",
528
+ "ClaudeRouterServices",
529
+ "ClaudeRouterShortcuts",
530
+ "ClaudeRouterTransport",
476
531
  "handle_claude_count_tokens_post",
477
532
  "handle_claude_messages_post",
478
- "missing_claude_runtime_dependencies",
479
533
  ]