@oneciel-ai/ciel-runtime 0.2.2 → 0.2.3

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 (126) hide show
  1. package/ciel_runtime.py +2553 -9635
  2. package/ciel_runtime_support/advisor_request_builder.py +8 -21
  3. package/ciel_runtime_support/anthropic_tool_turns.py +13 -8
  4. package/ciel_runtime_support/architecture.py +68 -0
  5. package/ciel_runtime_support/architecture_budget.py +1 -1
  6. package/ciel_runtime_support/channel_connection_context.py +233 -0
  7. package/ciel_runtime_support/channel_delivery_context.py +332 -0
  8. package/ciel_runtime_support/channel_mcp_context.py +313 -0
  9. package/ciel_runtime_support/channel_mcp_discovery.py +47 -0
  10. package/ciel_runtime_support/channel_mcp_transport.py +5 -1
  11. package/ciel_runtime_support/channel_message_context.py +212 -0
  12. package/ciel_runtime_support/channel_message_repository.py +14 -3
  13. package/ciel_runtime_support/channel_pending_injection.py +9 -0
  14. package/ciel_runtime_support/channel_probe_launch_context.py +213 -0
  15. package/ciel_runtime_support/channel_replay_policy.py +38 -0
  16. package/ciel_runtime_support/channel_runtime_environment.py +8 -0
  17. package/ciel_runtime_support/channel_session_context.py +236 -0
  18. package/ciel_runtime_support/channel_terminal_context.py +350 -0
  19. package/ciel_runtime_support/channel_wake_context.py +532 -0
  20. package/ciel_runtime_support/claude_environment.py +60 -0
  21. package/ciel_runtime_support/claude_launch_assembly.py +249 -0
  22. package/ciel_runtime_support/claude_router.py +62 -12
  23. package/ciel_runtime_support/cli_application_context.py +132 -0
  24. package/ciel_runtime_support/cli_assembly.py +50 -0
  25. package/ciel_runtime_support/codex_backend_context.py +363 -0
  26. package/ciel_runtime_support/codex_config.py +13 -1
  27. package/ciel_runtime_support/codex_launch_assembly.py +213 -0
  28. package/ciel_runtime_support/codex_launch_configuration.py +30 -1
  29. package/ciel_runtime_support/codex_mcp_integration.py +90 -8
  30. package/ciel_runtime_support/codex_model_catalog.py +4 -1
  31. package/ciel_runtime_support/codex_reasoning_rejects.py +225 -0
  32. package/ciel_runtime_support/codex_router.py +38 -8
  33. package/ciel_runtime_support/codex_turn_recovery.py +154 -0
  34. package/ciel_runtime_support/config_migrations.py +103 -0
  35. package/ciel_runtime_support/configuration_cli.py +38 -0
  36. package/ciel_runtime_support/context_compaction.py +9 -4
  37. package/ciel_runtime_support/credential_management.py +12 -0
  38. package/ciel_runtime_support/credentials.py +12 -0
  39. package/ciel_runtime_support/github_copilot_oauth.py +2 -2
  40. package/ciel_runtime_support/hosted_formula_tools.py +216 -0
  41. package/ciel_runtime_support/kimi_runtime_context.py +208 -0
  42. package/ciel_runtime_support/llm_preset_context.py +338 -0
  43. package/ciel_runtime_support/managed_mcp_config.py +8 -4
  44. package/ciel_runtime_support/mcp_configuration_context.py +291 -0
  45. package/ciel_runtime_support/mcp_http_proxy.py +14 -8
  46. package/ciel_runtime_support/mcp_probe_transport.py +47 -15
  47. package/ciel_runtime_support/mcp_transport.py +14 -1
  48. package/ciel_runtime_support/native_context_recovery.py +72 -0
  49. package/ciel_runtime_support/ollama_catalog_context.py +213 -0
  50. package/ciel_runtime_support/ollama_stream_collection.py +103 -0
  51. package/ciel_runtime_support/ollama_thinking.py +6 -1
  52. package/ciel_runtime_support/ollama_wire_projection.py +157 -0
  53. package/ciel_runtime_support/openai_forwarding.py +32 -10
  54. package/ciel_runtime_support/openai_responses_router.py +12 -0
  55. package/ciel_runtime_support/package_lifecycle.py +39 -0
  56. package/ciel_runtime_support/prelaunch_assembly.py +37 -0
  57. package/ciel_runtime_support/prelaunch_panel_context.py +418 -0
  58. package/ciel_runtime_support/prelaunch_shell_context.py +394 -0
  59. package/ciel_runtime_support/prompt_compaction.py +144 -0
  60. package/ciel_runtime_support/prompt_injection.py +45 -0
  61. package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +1 -1
  62. package/ciel_runtime_support/protocols/chat_projection.py +85 -5
  63. package/ciel_runtime_support/protocols/conversation_turn_policy.py +43 -0
  64. package/ciel_runtime_support/protocols/ollama_chat.py +31 -0
  65. package/ciel_runtime_support/protocols/ollama_response.py +57 -5
  66. package/ciel_runtime_support/protocols/openai_reasoning.py +5 -2
  67. package/ciel_runtime_support/protocols/openai_responses.py +61 -15
  68. package/ciel_runtime_support/provider_adapters.py +26 -0
  69. package/ciel_runtime_support/provider_administration_context.py +207 -0
  70. package/ciel_runtime_support/provider_config_mutations.py +3 -0
  71. package/ciel_runtime_support/provider_model_catalog_context.py +137 -0
  72. package/ciel_runtime_support/provider_model_context.py +107 -0
  73. package/ciel_runtime_support/provider_model_metadata_context.py +197 -0
  74. package/ciel_runtime_support/provider_model_selection.py +10 -3
  75. package/ciel_runtime_support/provider_models.py +45 -2
  76. package/ciel_runtime_support/provider_option_cli.py +19 -0
  77. package/ciel_runtime_support/provider_policy.py +1 -1
  78. package/ciel_runtime_support/provider_readiness_context.py +189 -0
  79. package/ciel_runtime_support/provider_request_builder.py +64 -28
  80. package/ciel_runtime_support/provider_responses_passthrough.py +21 -2
  81. package/ciel_runtime_support/provider_timeout_policy.py +54 -0
  82. package/ciel_runtime_support/provider_tool_policy.py +9 -1
  83. package/ciel_runtime_support/providers/__init__.py +6 -0
  84. package/ciel_runtime_support/providers/alibaba.py +634 -0
  85. package/ciel_runtime_support/providers/catalog.py +24 -16
  86. package/ciel_runtime_support/providers/deepseek.py +73 -0
  87. package/ciel_runtime_support/providers/github_copilot_oauth.py +22 -1
  88. package/ciel_runtime_support/providers/kimi.py +69 -9
  89. package/ciel_runtime_support/providers/ollama.py +8 -0
  90. package/ciel_runtime_support/providers/ollama_context.py +21 -2
  91. package/ciel_runtime_support/providers/vllm.py +7 -1
  92. package/ciel_runtime_support/response_collection.py +68 -18
  93. package/ciel_runtime_support/response_collection_context.py +391 -0
  94. package/ciel_runtime_support/response_stream_context.py +555 -0
  95. package/ciel_runtime_support/responses_input_compatibility.py +121 -0
  96. package/ciel_runtime_support/responses_usage_observer.py +83 -0
  97. package/ciel_runtime_support/router_client_lifecycle.py +1 -0
  98. package/ciel_runtime_support/router_http.py +239 -3
  99. package/ciel_runtime_support/router_observability_context.py +251 -0
  100. package/ciel_runtime_support/router_process_context.py +200 -0
  101. package/ciel_runtime_support/router_process_lifecycle.py +2 -0
  102. package/ciel_runtime_support/router_request_assembly.py +399 -0
  103. package/ciel_runtime_support/router_request_context.py +215 -0
  104. package/ciel_runtime_support/router_server_context.py +82 -0
  105. package/ciel_runtime_support/runaway_output_guard.py +488 -0
  106. package/ciel_runtime_support/runtime_asset_assembly.py +147 -0
  107. package/ciel_runtime_support/runtime_asset_context.py +297 -0
  108. package/ciel_runtime_support/runtime_constants.py +16 -1
  109. package/ciel_runtime_support/runtime_launch.py +9 -5
  110. package/ciel_runtime_support/runtime_launch_context.py +130 -0
  111. package/ciel_runtime_support/runtime_maintenance_assembly.py +60 -0
  112. package/ciel_runtime_support/runtime_maintenance_context.py +309 -0
  113. package/ciel_runtime_support/runtime_maintenance_services.py +265 -0
  114. package/ciel_runtime_support/runtime_paths.py +60 -40
  115. package/ciel_runtime_support/runtime_primitives.py +78 -0
  116. package/ciel_runtime_support/sse_stream_collection.py +236 -0
  117. package/ciel_runtime_support/statusline_script.py +57 -8
  118. package/ciel_runtime_support/streaming_anthropic.py +361 -24
  119. package/ciel_runtime_support/tool_schema.py +40 -2
  120. package/ciel_runtime_support/tool_side_effect_dedupe.py +117 -12
  121. package/ciel_runtime_support/upstream_dump.py +68 -0
  122. package/ciel_runtime_support/upstream_retry_context.py +259 -0
  123. package/ciel_runtime_support/workspace_router_selection.py +86 -0
  124. package/docs/Configuration.md +50 -0
  125. package/docs/Test-Suite.md +1 -0
  126. package/package.json +1 -1
@@ -0,0 +1,200 @@
1
+ """Router shutdown and listener-replacement bounded context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Any, Callable
8
+
9
+ from .router_process_lifecycle import (
10
+ ClockPorts,
11
+ RouterProcessConfig,
12
+ RouterStatePorts,
13
+ RouterTerminationPorts,
14
+ ensure_port_available,
15
+ stop_router_processes,
16
+ stop_with_guarantee,
17
+ terminate_health_pid,
18
+ terminate_pid_file,
19
+ )
20
+
21
+
22
+ @dataclass(frozen=True, slots=True)
23
+ class RouterListenerPorts:
24
+ platform_name: str
25
+ bind_host: Callable[[], str]
26
+ windows_pids: Callable[[int, str], list[int]]
27
+ posix_pids: Callable[[int, str], list[int]]
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class RouterProcessEffects:
32
+ terminate_pid: Callable[[int, str, bool], bool]
33
+ pid_is_running: Callable[[int], bool]
34
+ protected_pids: Callable[[], tuple[int, int]]
35
+ now: Callable[[], float]
36
+ sleep: Callable[[float], None]
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class RouterProcessCompatibilityPorts:
41
+ terminate_pid_file: Callable[[Path, str, bool], bool]
42
+ terminate_health: Callable[[dict[str, Any] | None, bool], bool]
43
+ stop_processes: Callable[[bool], bool]
44
+ listener_pids: Callable[[], list[int]]
45
+
46
+
47
+ @dataclass(frozen=True, slots=True)
48
+ class RouterProcessContext:
49
+ config: RouterProcessConfig
50
+ state: RouterStatePorts
51
+ listener: RouterListenerPorts
52
+ effects: RouterProcessEffects
53
+ compatibility: RouterProcessCompatibilityPorts
54
+
55
+ def listener_pids(self) -> list[int]:
56
+ host = self.listener.bind_host()
57
+ if self.listener.platform_name == "nt":
58
+ return self.listener.windows_pids(self.config.router_port, host)
59
+ return self.listener.posix_pids(self.config.router_port, host)
60
+
61
+ def termination_ports(self) -> RouterTerminationPorts:
62
+ return RouterTerminationPorts(
63
+ terminate_pid=self.effects.terminate_pid,
64
+ terminate_pid_file=self.compatibility.terminate_pid_file,
65
+ terminate_health=self.compatibility.terminate_health,
66
+ stop_processes=self.compatibility.stop_processes,
67
+ listener_pids=self.compatibility.listener_pids,
68
+ )
69
+
70
+ def terminate_pid_file(
71
+ self,
72
+ path: Path,
73
+ label: str,
74
+ quiet: bool = False,
75
+ ) -> bool:
76
+ return terminate_pid_file(
77
+ path,
78
+ label,
79
+ quiet,
80
+ terminate_pid=self.effects.terminate_pid,
81
+ pid_is_running=self.effects.pid_is_running,
82
+ )
83
+
84
+ def terminate_health_pid(
85
+ self,
86
+ health: dict[str, Any] | None,
87
+ quiet: bool = True,
88
+ ) -> bool:
89
+ return terminate_health_pid(
90
+ health,
91
+ quiet,
92
+ config=self.config,
93
+ state=self.state,
94
+ terminate_pid=self.effects.terminate_pid,
95
+ protected_pids=self.effects.protected_pids(),
96
+ )
97
+
98
+ def stop_processes(self, quiet: bool = False) -> bool:
99
+ return stop_router_processes(
100
+ quiet,
101
+ config=self.config,
102
+ state=self.state,
103
+ termination=self.termination_ports(),
104
+ )
105
+
106
+ def ensure_port_available(
107
+ self,
108
+ reason: str,
109
+ health: dict[str, Any] | None = None,
110
+ max_wait_seconds: float = 5.0,
111
+ ) -> None:
112
+ ensure_port_available(
113
+ reason,
114
+ health,
115
+ max_wait_seconds,
116
+ config=self.config,
117
+ state=self.state,
118
+ termination=self.termination_ports(),
119
+ clock=ClockPorts(now=self.effects.now, sleep=self.effects.sleep),
120
+ )
121
+
122
+ def stop_with_guarantee(
123
+ self,
124
+ reason: str,
125
+ max_wait_seconds: float = 5.0,
126
+ quiet: bool = True,
127
+ ) -> bool:
128
+ return stop_with_guarantee(
129
+ reason,
130
+ max_wait_seconds,
131
+ quiet,
132
+ config=self.config,
133
+ state=self.state,
134
+ termination=self.termination_ports(),
135
+ clock=ClockPorts(now=self.effects.now, sleep=self.effects.sleep),
136
+ )
137
+
138
+
139
+ @dataclass(frozen=True, slots=True)
140
+ class RouterProcessCompatibilityApi:
141
+ context: Callable[[], RouterProcessContext]
142
+
143
+ def config(self) -> RouterProcessConfig:
144
+ return self.context().config
145
+
146
+ def state_ports(self) -> RouterStatePorts:
147
+ return self.context().state
148
+
149
+ def termination_ports(self) -> RouterTerminationPorts:
150
+ return self.context().termination_ports()
151
+
152
+ def listener_pids(self) -> list[int]:
153
+ return self.context().listener_pids()
154
+
155
+ def terminate_pid_file(
156
+ self,
157
+ path: Path,
158
+ label: str,
159
+ quiet: bool = False,
160
+ ) -> bool:
161
+ return self.context().terminate_pid_file(path, label, quiet)
162
+
163
+ def terminate_health_pid(
164
+ self,
165
+ health: dict[str, Any] | None,
166
+ quiet: bool = True,
167
+ ) -> bool:
168
+ return self.context().terminate_health_pid(health, quiet)
169
+
170
+ def stop_processes(self, quiet: bool = False) -> bool:
171
+ return self.context().stop_processes(quiet)
172
+
173
+ def ensure_port_available(
174
+ self,
175
+ reason: str,
176
+ health: dict[str, Any] | None = None,
177
+ max_wait_seconds: float = 5.0,
178
+ ) -> None:
179
+ self.context().ensure_port_available(reason, health, max_wait_seconds)
180
+
181
+ def stop_with_guarantee(
182
+ self,
183
+ reason: str,
184
+ max_wait_seconds: float = 5.0,
185
+ quiet: bool = True,
186
+ ) -> bool:
187
+ return self.context().stop_with_guarantee(
188
+ reason,
189
+ max_wait_seconds,
190
+ quiet,
191
+ )
192
+
193
+
194
+ __all__ = [
195
+ "RouterListenerPorts",
196
+ "RouterProcessCompatibilityApi",
197
+ "RouterProcessCompatibilityPorts",
198
+ "RouterProcessContext",
199
+ "RouterProcessEffects",
200
+ ]
@@ -194,6 +194,8 @@ def start_router_if_needed(
194
194
  environment = spawn.environment()
195
195
  environment["CIEL_RUNTIME_MANAGED_ROUTER"] = "1"
196
196
  environment["CIEL_RUNTIME_ROUTER_OWNER_PID"] = str(spawn.process_id())
197
+ environment["CIEL_RUNTIME_LAUNCH_CWD"] = str(Path.cwd())
198
+ environment["CIEL_RUNTIME_ROUTER_PORT"] = str(config.router_port)
197
199
  with log_path.open("ab", buffering=0) as log:
198
200
  spawn.popen(
199
201
  command,
@@ -0,0 +1,399 @@
1
+ """Compose OpenAI Responses and Claude router request pipelines from typed ports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Callable, Mapping
7
+
8
+ from . import claude_router, openai_responses_router
9
+ from .router_request_context import (
10
+ RouterRequestContext,
11
+ RouterRequestPorts,
12
+ RuntimeRouterPorts,
13
+ )
14
+
15
+
16
+ Callback = Callable[..., Any]
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class OpenAIResponseCorePorts:
21
+ event_bus: Any
22
+ request_id: Callback
23
+ input_as_list: Callback
24
+ is_client_disconnect: Callback
25
+ log: Callback
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class OpenAIResponseConversionPorts:
30
+ to_anthropic: Callback
31
+ current_alias: Callback
32
+ update_tool_schema: Callback
33
+ normalize_thinking: Callback
34
+ filter_blocked_tools: Callback
35
+ normalize_tool_choice: Callback
36
+ write_context_usage: Callback
37
+ strip_advisor_tools: Callback
38
+ inject_channel_context: Callback
39
+ inject_tool_result_context: Callback
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class OpenAIResponseRoutingPorts:
44
+ maybe_import_session: Callback
45
+ codex_routed_enabled: Callback
46
+ forward_codex: Callback
47
+ select_protocol: Callback
48
+ forward_provider_responses: Callback
49
+ dump_request: Callback
50
+ normalize_provider_wire: Callback
51
+ collect_message: Callback
52
+ apply_codex_compat_instructions: Callback
53
+ recover_preamble_only_turn: Callback
54
+
55
+
56
+ @dataclass(frozen=True, slots=True)
57
+ class OpenAIResponseDeliveryPorts:
58
+ begin: Callback
59
+ mark_success: Callback
60
+ mark_failed: Callback
61
+ commit: Callback
62
+
63
+
64
+ @dataclass(frozen=True, slots=True)
65
+ class OpenAIResponseOutputPorts:
66
+ write_response: Callback
67
+ write_error: Callback
68
+ upstream_error_message: Callback
69
+ codex_auth_error_message: Callback
70
+ event_preview: Callback
71
+
72
+
73
+ @dataclass(frozen=True, slots=True)
74
+ class OpenAIResponseAssembly:
75
+ core: OpenAIResponseCorePorts
76
+ conversion: OpenAIResponseConversionPorts
77
+ routing: OpenAIResponseRoutingPorts
78
+ delivery: OpenAIResponseDeliveryPorts
79
+ output: OpenAIResponseOutputPorts
80
+
81
+ def services(self) -> openai_responses_router.OpenAIResponsesServices:
82
+ return openai_responses_router.OpenAIResponsesServices(
83
+ core=openai_responses_router.OpenAIResponsesCore(
84
+ event_bus=self.core.event_bus,
85
+ request_id=self.core.request_id,
86
+ input_as_list=self.core.input_as_list,
87
+ is_client_disconnect=self.core.is_client_disconnect,
88
+ log=self.core.log,
89
+ ),
90
+ conversion=openai_responses_router.OpenAIResponsesConversion(
91
+ to_anthropic=self.conversion.to_anthropic,
92
+ current_alias=self.conversion.current_alias,
93
+ update_tool_schema=self.conversion.update_tool_schema,
94
+ normalize_thinking=self.conversion.normalize_thinking,
95
+ filter_blocked_tools=self.conversion.filter_blocked_tools,
96
+ normalize_tool_choice=self.conversion.normalize_tool_choice,
97
+ write_context_usage=self.conversion.write_context_usage,
98
+ strip_advisor_tools=self.conversion.strip_advisor_tools,
99
+ inject_channel_context=self.conversion.inject_channel_context,
100
+ inject_tool_result_context=self.conversion.inject_tool_result_context,
101
+ ),
102
+ routing=openai_responses_router.OpenAIResponsesRouting(
103
+ maybe_import_session=self.routing.maybe_import_session,
104
+ codex_routed_enabled=self.routing.codex_routed_enabled,
105
+ forward_codex=self.routing.forward_codex,
106
+ select_protocol=self.routing.select_protocol,
107
+ forward_provider_responses=self.routing.forward_provider_responses,
108
+ dump_request=self.routing.dump_request,
109
+ normalize_provider_wire=self.routing.normalize_provider_wire,
110
+ collect_message=self.routing.collect_message,
111
+ apply_codex_compat_instructions=self.routing.apply_codex_compat_instructions,
112
+ recover_preamble_only_turn=self.routing.recover_preamble_only_turn,
113
+ ),
114
+ delivery=openai_responses_router.OpenAIResponsesDelivery(
115
+ begin=self.delivery.begin,
116
+ mark_success=self.delivery.mark_success,
117
+ mark_failed=self.delivery.mark_failed,
118
+ commit=self.delivery.commit,
119
+ ),
120
+ output=openai_responses_router.OpenAIResponsesOutput(
121
+ write_response=self.output.write_response,
122
+ write_error=self.output.write_error,
123
+ upstream_error_message=self.output.upstream_error_message,
124
+ codex_auth_error_message=self.output.codex_auth_error_message,
125
+ event_preview=self.output.event_preview,
126
+ ),
127
+ )
128
+
129
+
130
+ @dataclass(frozen=True, slots=True)
131
+ class ClaudeRouterCorePorts:
132
+ event_bus: Any
133
+ log: Callback
134
+ try_write_json: Callback
135
+
136
+
137
+ @dataclass(frozen=True, slots=True)
138
+ class ClaudeRouterCountPorts:
139
+ estimate_tokens: Callback
140
+ write_context_usage: Callback
141
+ write_json: Callback
142
+
143
+
144
+ @dataclass(frozen=True, slots=True)
145
+ class ClaudeRouterPipelinePorts:
146
+ update_tool_schema: Callback
147
+ event_preview: Callback
148
+ dump_request: Callback
149
+ filter_blocked_tools: Callback
150
+ normalize_tool_choice: Callback
151
+ write_context_usage: Callback
152
+ strip_advisor_tools: Callback
153
+ inject_channel_context: Callback
154
+ inject_tool_result_context: Callback
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class ClaudeRouterShortcutPorts:
159
+ plan_mode: Callback
160
+ router_debug: Callback
161
+ version: Callback
162
+ channel_clear: Callback
163
+ import_session: Callback
164
+ llm_options: Callback
165
+ api_keys: Callback
166
+ advisor: Callback
167
+
168
+
169
+ @dataclass(frozen=True, slots=True)
170
+ class ClaudeRouterDeliveryPorts:
171
+ begin: Callback
172
+ commit: Callback
173
+ mark_failed: Callback
174
+ mark_success: Callback
175
+ is_client_disconnect: Callback
176
+ write_activity: Callback
177
+
178
+
179
+ @dataclass(frozen=True, slots=True)
180
+ class ClaudeRouterRoutingPorts:
181
+ forward_ollama: Callback
182
+ forward_openai: Callback
183
+ select_protocol: Callback
184
+ request_policy: Callback
185
+ resolve_model: Callback
186
+ provider_labels: Mapping[str, str]
187
+ write_json: Callback
188
+
189
+
190
+ @dataclass(frozen=True, slots=True)
191
+ class ClaudeRouterNormalizationPorts:
192
+ normalize_provider_wire: Callback
193
+ normalize_thinking: Callback
194
+ normalize_system_roles: Callback
195
+ cap_body: Callback
196
+ apply_request_options: Callback
197
+ rehydrate_thinking: Callback
198
+ ncp_model_id: Callback
199
+ resolve_tool_models: Callback
200
+ normalize_model_options: Callback
201
+ strip_internal_metadata: Callback
202
+
203
+
204
+ @dataclass(frozen=True, slots=True)
205
+ class ClaudeRouterTransportPorts:
206
+ native_base_url: Callback
207
+ native_compat_enabled: Callback
208
+ upstream_base: Callback
209
+ join_url: Callback
210
+ upstream_query: Callback
211
+ provider_headers: Callback
212
+ apply_rate_limit: Callback
213
+ open_request: Callback
214
+ request_timeout: Callback
215
+ idle_timeout: Callback
216
+
217
+
218
+ @dataclass(frozen=True, slots=True)
219
+ class ClaudeRouterResponsePorts:
220
+ rebatch_sse: Callback
221
+ preserves_thinking: Callback
222
+ normalize_stream_tool_use: Callback
223
+ set_stream_timeout: Callback
224
+ normalize_thinking: Callback
225
+ append_tasklist: Callback
226
+ prepend_text: Callback
227
+ rate_limit_notice: Callback
228
+ register_key_cooldown: Callback
229
+ key_from_headers: Callback
230
+
231
+
232
+ @dataclass(frozen=True, slots=True)
233
+ class ClaudeRouterContextRecoveryPorts:
234
+ recover_output_budget: Callback
235
+
236
+
237
+ @dataclass(frozen=True, slots=True)
238
+ class ClaudeRouterAssembly:
239
+ core: ClaudeRouterCorePorts
240
+ count_tokens: ClaudeRouterCountPorts
241
+ pipeline: ClaudeRouterPipelinePorts
242
+ shortcuts: ClaudeRouterShortcutPorts
243
+ delivery: ClaudeRouterDeliveryPorts
244
+ routing: ClaudeRouterRoutingPorts
245
+ normalization: ClaudeRouterNormalizationPorts
246
+ transport: ClaudeRouterTransportPorts
247
+ response: ClaudeRouterResponsePorts
248
+ context_recovery: ClaudeRouterContextRecoveryPorts
249
+
250
+ def services(self) -> claude_router.ClaudeRouterServices:
251
+ return claude_router.ClaudeRouterServices(
252
+ core=claude_router.ClaudeRouterCore(
253
+ event_bus=self.core.event_bus,
254
+ log=self.core.log,
255
+ try_write_json=self.core.try_write_json,
256
+ ),
257
+ count_tokens=claude_router.ClaudeRouterCountTokens(
258
+ estimate_tokens=self.count_tokens.estimate_tokens,
259
+ write_context_usage=self.count_tokens.write_context_usage,
260
+ write_json=self.count_tokens.write_json,
261
+ ),
262
+ pipeline=claude_router.ClaudeRouterPipeline(
263
+ update_tool_schema_registry=self.pipeline.update_tool_schema,
264
+ router_event_message_preview=self.pipeline.event_preview,
265
+ dump_request_for_trace=self.pipeline.dump_request,
266
+ filter_blocked_tools=self.pipeline.filter_blocked_tools,
267
+ normalize_tool_choice=self.pipeline.normalize_tool_choice,
268
+ write_context_usage=self.pipeline.write_context_usage,
269
+ strip_advisor_tools=self.pipeline.strip_advisor_tools,
270
+ inject_channel_context=self.pipeline.inject_channel_context,
271
+ inject_tool_result_context=self.pipeline.inject_tool_result_context,
272
+ ),
273
+ shortcuts=claude_router.ClaudeRouterShortcuts(
274
+ plan_mode=self.shortcuts.plan_mode,
275
+ router_debug=self.shortcuts.router_debug,
276
+ version=self.shortcuts.version,
277
+ channel_clear=self.shortcuts.channel_clear,
278
+ import_session=self.shortcuts.import_session,
279
+ llm_options=self.shortcuts.llm_options,
280
+ api_keys=self.shortcuts.api_keys,
281
+ advisor=self.shortcuts.advisor,
282
+ ),
283
+ delivery=claude_router.ClaudeRouterDelivery(
284
+ begin=self.delivery.begin,
285
+ commit=self.delivery.commit,
286
+ mark_failed=self.delivery.mark_failed,
287
+ mark_success=self.delivery.mark_success,
288
+ is_client_disconnect=self.delivery.is_client_disconnect,
289
+ write_activity=self.delivery.write_activity,
290
+ ),
291
+ routing=claude_router.ClaudeRouterRouting(
292
+ forward_ollama=self.routing.forward_ollama,
293
+ forward_openai=self.routing.forward_openai,
294
+ select_protocol=self.routing.select_protocol,
295
+ request_policy=self.routing.request_policy,
296
+ resolve_model=self.routing.resolve_model,
297
+ provider_labels=self.routing.provider_labels,
298
+ write_json=self.routing.write_json,
299
+ ),
300
+ normalization=claude_router.ClaudeRouterNativeNormalization(
301
+ normalize_provider_wire=self.normalization.normalize_provider_wire,
302
+ normalize_thinking=self.normalization.normalize_thinking,
303
+ normalize_system_roles=self.normalization.normalize_system_roles,
304
+ cap_body=self.normalization.cap_body,
305
+ apply_request_options=self.normalization.apply_request_options,
306
+ rehydrate_thinking=self.normalization.rehydrate_thinking,
307
+ ncp_model_id=self.normalization.ncp_model_id,
308
+ resolve_tool_models=self.normalization.resolve_tool_models,
309
+ normalize_model_options=self.normalization.normalize_model_options,
310
+ strip_internal_metadata=self.normalization.strip_internal_metadata,
311
+ ),
312
+ transport=claude_router.ClaudeRouterTransport(
313
+ native_base_url=self.transport.native_base_url,
314
+ native_compat_enabled=self.transport.native_compat_enabled,
315
+ upstream_base=self.transport.upstream_base,
316
+ join_url=self.transport.join_url,
317
+ upstream_query=self.transport.upstream_query,
318
+ provider_headers=self.transport.provider_headers,
319
+ apply_rate_limit=self.transport.apply_rate_limit,
320
+ open_request=self.transport.open_request,
321
+ request_timeout=self.transport.request_timeout,
322
+ idle_timeout=self.transport.idle_timeout,
323
+ ),
324
+ response=claude_router.ClaudeRouterResponse(
325
+ rebatch_sse=self.response.rebatch_sse,
326
+ preserves_thinking=self.response.preserves_thinking,
327
+ normalize_stream_tool_use=self.response.normalize_stream_tool_use,
328
+ set_stream_timeout=self.response.set_stream_timeout,
329
+ normalize_thinking=self.response.normalize_thinking,
330
+ append_tasklist=self.response.append_tasklist,
331
+ prepend_text=self.response.prepend_text,
332
+ rate_limit_notice=self.response.rate_limit_notice,
333
+ register_key_cooldown=self.response.register_key_cooldown,
334
+ key_from_headers=self.response.key_from_headers,
335
+ ),
336
+ context_recovery=claude_router.ClaudeRouterContextRecovery(
337
+ recover_output_budget=self.context_recovery.recover_output_budget,
338
+ ),
339
+ )
340
+
341
+
342
+ @dataclass(frozen=True, slots=True)
343
+ class RouterRequestOuterPorts:
344
+ forward_backend_json: Callback
345
+ forward_backend_get: Callback
346
+ write_responses_error: Callback
347
+ write_json: Callback
348
+ upstream_error_message: Callback
349
+ is_client_disconnect: Callback
350
+
351
+
352
+ @dataclass(frozen=True, slots=True)
353
+ class RouterRequestAssembly:
354
+ openai: OpenAIResponseAssembly
355
+ outer: RouterRequestOuterPorts
356
+ codex_routed_enabled: Callback
357
+ forward_provider_chat: Callback
358
+ claude: ClaudeRouterAssembly
359
+
360
+ def context(self) -> RouterRequestContext:
361
+ return RouterRequestContext(
362
+ request=RouterRequestPorts(
363
+ openai_responses=self.openai.services(),
364
+ forward_backend_json=self.outer.forward_backend_json,
365
+ forward_backend_get=self.outer.forward_backend_get,
366
+ write_responses_error=self.outer.write_responses_error,
367
+ write_json=self.outer.write_json,
368
+ upstream_error_message=self.outer.upstream_error_message,
369
+ is_client_disconnect=self.outer.is_client_disconnect,
370
+ ),
371
+ runtime=RuntimeRouterPorts(
372
+ codex_routed_enabled=self.codex_routed_enabled,
373
+ forward_provider_chat=self.forward_provider_chat,
374
+ claude_services=self.claude.services(),
375
+ ),
376
+ )
377
+
378
+
379
+ __all__ = [
380
+ "ClaudeRouterAssembly",
381
+ "ClaudeRouterContextRecoveryPorts",
382
+ "ClaudeRouterCorePorts",
383
+ "ClaudeRouterCountPorts",
384
+ "ClaudeRouterDeliveryPorts",
385
+ "ClaudeRouterNormalizationPorts",
386
+ "ClaudeRouterPipelinePorts",
387
+ "ClaudeRouterResponsePorts",
388
+ "ClaudeRouterRoutingPorts",
389
+ "ClaudeRouterShortcutPorts",
390
+ "ClaudeRouterTransportPorts",
391
+ "OpenAIResponseAssembly",
392
+ "OpenAIResponseConversionPorts",
393
+ "OpenAIResponseCorePorts",
394
+ "OpenAIResponseDeliveryPorts",
395
+ "OpenAIResponseOutputPorts",
396
+ "OpenAIResponseRoutingPorts",
397
+ "RouterRequestAssembly",
398
+ "RouterRequestOuterPorts",
399
+ ]