@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.
- package/ciel_runtime.py +2553 -9635
- package/ciel_runtime_support/advisor_request_builder.py +8 -21
- package/ciel_runtime_support/anthropic_tool_turns.py +13 -8
- package/ciel_runtime_support/architecture.py +68 -0
- package/ciel_runtime_support/architecture_budget.py +1 -1
- package/ciel_runtime_support/channel_connection_context.py +233 -0
- package/ciel_runtime_support/channel_delivery_context.py +332 -0
- package/ciel_runtime_support/channel_mcp_context.py +313 -0
- package/ciel_runtime_support/channel_mcp_discovery.py +47 -0
- package/ciel_runtime_support/channel_mcp_transport.py +5 -1
- package/ciel_runtime_support/channel_message_context.py +212 -0
- package/ciel_runtime_support/channel_message_repository.py +14 -3
- package/ciel_runtime_support/channel_pending_injection.py +9 -0
- package/ciel_runtime_support/channel_probe_launch_context.py +213 -0
- package/ciel_runtime_support/channel_replay_policy.py +38 -0
- package/ciel_runtime_support/channel_runtime_environment.py +8 -0
- package/ciel_runtime_support/channel_session_context.py +236 -0
- package/ciel_runtime_support/channel_terminal_context.py +350 -0
- package/ciel_runtime_support/channel_wake_context.py +532 -0
- package/ciel_runtime_support/claude_environment.py +60 -0
- package/ciel_runtime_support/claude_launch_assembly.py +249 -0
- package/ciel_runtime_support/claude_router.py +62 -12
- package/ciel_runtime_support/cli_application_context.py +132 -0
- package/ciel_runtime_support/cli_assembly.py +50 -0
- package/ciel_runtime_support/codex_backend_context.py +363 -0
- package/ciel_runtime_support/codex_config.py +13 -1
- package/ciel_runtime_support/codex_launch_assembly.py +213 -0
- package/ciel_runtime_support/codex_launch_configuration.py +30 -1
- package/ciel_runtime_support/codex_mcp_integration.py +90 -8
- package/ciel_runtime_support/codex_model_catalog.py +4 -1
- package/ciel_runtime_support/codex_reasoning_rejects.py +225 -0
- package/ciel_runtime_support/codex_router.py +38 -8
- package/ciel_runtime_support/codex_turn_recovery.py +154 -0
- package/ciel_runtime_support/config_migrations.py +103 -0
- package/ciel_runtime_support/configuration_cli.py +38 -0
- package/ciel_runtime_support/context_compaction.py +9 -4
- package/ciel_runtime_support/credential_management.py +12 -0
- package/ciel_runtime_support/credentials.py +12 -0
- package/ciel_runtime_support/github_copilot_oauth.py +2 -2
- package/ciel_runtime_support/hosted_formula_tools.py +216 -0
- package/ciel_runtime_support/kimi_runtime_context.py +208 -0
- package/ciel_runtime_support/llm_preset_context.py +338 -0
- package/ciel_runtime_support/managed_mcp_config.py +8 -4
- package/ciel_runtime_support/mcp_configuration_context.py +291 -0
- package/ciel_runtime_support/mcp_http_proxy.py +14 -8
- package/ciel_runtime_support/mcp_probe_transport.py +47 -15
- package/ciel_runtime_support/mcp_transport.py +14 -1
- package/ciel_runtime_support/native_context_recovery.py +72 -0
- package/ciel_runtime_support/ollama_catalog_context.py +213 -0
- package/ciel_runtime_support/ollama_stream_collection.py +103 -0
- package/ciel_runtime_support/ollama_thinking.py +6 -1
- package/ciel_runtime_support/ollama_wire_projection.py +157 -0
- package/ciel_runtime_support/openai_forwarding.py +32 -10
- package/ciel_runtime_support/openai_responses_router.py +12 -0
- package/ciel_runtime_support/package_lifecycle.py +39 -0
- package/ciel_runtime_support/prelaunch_assembly.py +37 -0
- package/ciel_runtime_support/prelaunch_panel_context.py +418 -0
- package/ciel_runtime_support/prelaunch_shell_context.py +394 -0
- package/ciel_runtime_support/prompt_compaction.py +144 -0
- package/ciel_runtime_support/prompt_injection.py +45 -0
- package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +1 -1
- package/ciel_runtime_support/protocols/chat_projection.py +85 -5
- package/ciel_runtime_support/protocols/conversation_turn_policy.py +43 -0
- package/ciel_runtime_support/protocols/ollama_chat.py +31 -0
- package/ciel_runtime_support/protocols/ollama_response.py +57 -5
- package/ciel_runtime_support/protocols/openai_reasoning.py +5 -2
- package/ciel_runtime_support/protocols/openai_responses.py +61 -15
- package/ciel_runtime_support/provider_adapters.py +26 -0
- package/ciel_runtime_support/provider_administration_context.py +207 -0
- package/ciel_runtime_support/provider_config_mutations.py +3 -0
- package/ciel_runtime_support/provider_model_catalog_context.py +137 -0
- package/ciel_runtime_support/provider_model_context.py +107 -0
- package/ciel_runtime_support/provider_model_metadata_context.py +197 -0
- package/ciel_runtime_support/provider_model_selection.py +10 -3
- package/ciel_runtime_support/provider_models.py +45 -2
- package/ciel_runtime_support/provider_option_cli.py +19 -0
- package/ciel_runtime_support/provider_policy.py +1 -1
- package/ciel_runtime_support/provider_readiness_context.py +189 -0
- package/ciel_runtime_support/provider_request_builder.py +64 -28
- package/ciel_runtime_support/provider_responses_passthrough.py +21 -2
- package/ciel_runtime_support/provider_timeout_policy.py +54 -0
- package/ciel_runtime_support/provider_tool_policy.py +9 -1
- package/ciel_runtime_support/providers/__init__.py +6 -0
- package/ciel_runtime_support/providers/alibaba.py +634 -0
- package/ciel_runtime_support/providers/catalog.py +24 -16
- package/ciel_runtime_support/providers/deepseek.py +73 -0
- package/ciel_runtime_support/providers/github_copilot_oauth.py +22 -1
- package/ciel_runtime_support/providers/kimi.py +69 -9
- package/ciel_runtime_support/providers/ollama.py +8 -0
- package/ciel_runtime_support/providers/ollama_context.py +21 -2
- package/ciel_runtime_support/providers/vllm.py +7 -1
- package/ciel_runtime_support/response_collection.py +68 -18
- package/ciel_runtime_support/response_collection_context.py +391 -0
- package/ciel_runtime_support/response_stream_context.py +555 -0
- package/ciel_runtime_support/responses_input_compatibility.py +121 -0
- package/ciel_runtime_support/responses_usage_observer.py +83 -0
- package/ciel_runtime_support/router_client_lifecycle.py +1 -0
- package/ciel_runtime_support/router_http.py +239 -3
- package/ciel_runtime_support/router_observability_context.py +251 -0
- package/ciel_runtime_support/router_process_context.py +200 -0
- package/ciel_runtime_support/router_process_lifecycle.py +2 -0
- package/ciel_runtime_support/router_request_assembly.py +399 -0
- package/ciel_runtime_support/router_request_context.py +215 -0
- package/ciel_runtime_support/router_server_context.py +82 -0
- package/ciel_runtime_support/runaway_output_guard.py +488 -0
- package/ciel_runtime_support/runtime_asset_assembly.py +147 -0
- package/ciel_runtime_support/runtime_asset_context.py +297 -0
- package/ciel_runtime_support/runtime_constants.py +16 -1
- package/ciel_runtime_support/runtime_launch.py +9 -5
- package/ciel_runtime_support/runtime_launch_context.py +130 -0
- package/ciel_runtime_support/runtime_maintenance_assembly.py +60 -0
- package/ciel_runtime_support/runtime_maintenance_context.py +309 -0
- package/ciel_runtime_support/runtime_maintenance_services.py +265 -0
- package/ciel_runtime_support/runtime_paths.py +60 -40
- package/ciel_runtime_support/runtime_primitives.py +78 -0
- package/ciel_runtime_support/sse_stream_collection.py +236 -0
- package/ciel_runtime_support/statusline_script.py +57 -8
- package/ciel_runtime_support/streaming_anthropic.py +361 -24
- package/ciel_runtime_support/tool_schema.py +40 -2
- package/ciel_runtime_support/tool_side_effect_dedupe.py +117 -12
- package/ciel_runtime_support/upstream_dump.py +68 -0
- package/ciel_runtime_support/upstream_retry_context.py +259 -0
- package/ciel_runtime_support/workspace_router_selection.py +86 -0
- package/docs/Configuration.md +50 -0
- package/docs/Test-Suite.md +1 -0
- package/package.json +1 -1
|
@@ -37,12 +37,7 @@ class AdvisorBudgetPorts:
|
|
|
37
37
|
reserve: Callable[[dict[str, Any], int], int]
|
|
38
38
|
compact_messages: Callable[..., list[dict[str, Any]]]
|
|
39
39
|
configured_output: Callable[..., int]
|
|
40
|
-
|
|
41
|
-
positive_int: Callable[[Any], int]
|
|
42
|
-
ollama_num_ctx: Callable[..., int]
|
|
43
|
-
think_value: Callable[
|
|
44
|
-
[str, str | None, dict[str, Any], dict[str, Any]], bool | str | None
|
|
45
|
-
]
|
|
40
|
+
apply_ollama_optional: Callable[..., dict[str, Any]]
|
|
46
41
|
|
|
47
42
|
|
|
48
43
|
@dataclass(frozen=True, slots=True)
|
|
@@ -208,22 +203,14 @@ class AdvisorRequestBuilder:
|
|
|
208
203
|
"messages": messages,
|
|
209
204
|
"stream": False,
|
|
210
205
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
206
|
+
return self.budget.apply_ollama_optional(
|
|
207
|
+
request,
|
|
208
|
+
provider,
|
|
209
|
+
upstream_model,
|
|
210
|
+
config,
|
|
211
|
+
body,
|
|
212
|
+
output_limit=4096,
|
|
218
213
|
)
|
|
219
|
-
num_ctx = self.budget.ollama_num_ctx(
|
|
220
|
-
config, {"messages": messages, "tools": []}
|
|
221
|
-
)
|
|
222
|
-
if num_ctx:
|
|
223
|
-
options.setdefault("num_ctx", num_ctx)
|
|
224
|
-
if options:
|
|
225
|
-
request["options"] = options
|
|
226
|
-
return request
|
|
227
214
|
|
|
228
215
|
@staticmethod
|
|
229
216
|
def _sampling_options(
|
|
@@ -9,8 +9,6 @@ from typing import Any
|
|
|
9
9
|
|
|
10
10
|
@dataclass(frozen=True, slots=True)
|
|
11
11
|
class AnthropicToolTurnServices:
|
|
12
|
-
tool_use_as_text: Callable[[dict[str, Any]], dict[str, str]]
|
|
13
|
-
tool_result_as_text: Callable[[dict[str, Any]], dict[str, str]]
|
|
14
12
|
log: Callable[[str, str], Any]
|
|
15
13
|
|
|
16
14
|
|
|
@@ -19,7 +17,14 @@ def normalize_historical_anthropic_tool_turns(
|
|
|
19
17
|
body: dict[str, Any],
|
|
20
18
|
services: AnthropicToolTurnServices,
|
|
21
19
|
) -> dict[str, Any]:
|
|
22
|
-
"""
|
|
20
|
+
"""Discard unmatched historical tool blocks instead of replaying them as text.
|
|
21
|
+
|
|
22
|
+
An unmatched tool block cannot be sent on provider wires that require strict
|
|
23
|
+
tool-use/result pairing. Turning it into conversation text is also unsafe:
|
|
24
|
+
the model can treat the diagnostic, command, or stale result as a new user
|
|
25
|
+
instruction. Keep valid pairs verbatim and report discarded history only
|
|
26
|
+
through the runtime log.
|
|
27
|
+
"""
|
|
23
28
|
messages = body.get("messages")
|
|
24
29
|
if not isinstance(messages, list):
|
|
25
30
|
return body
|
|
@@ -57,12 +62,12 @@ def normalize_historical_anthropic_tool_turns(
|
|
|
57
62
|
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
58
63
|
tool_id = str(block.get("id") or "")
|
|
59
64
|
if not tool_id or tool_id not in retained:
|
|
60
|
-
next_content.append(services.tool_use_as_text(block))
|
|
61
65
|
converted_tool_uses += 1
|
|
62
66
|
content_changed = True
|
|
63
67
|
continue
|
|
64
68
|
next_content.append(block)
|
|
65
|
-
|
|
69
|
+
if next_content:
|
|
70
|
+
normalized_messages.append(_with_content(message, next_content) if content_changed else message)
|
|
66
71
|
changed = changed or content_changed
|
|
67
72
|
retained_tool_ids_for_next_user = retained
|
|
68
73
|
continue
|
|
@@ -76,12 +81,12 @@ def normalize_historical_anthropic_tool_turns(
|
|
|
76
81
|
if tool_id and tool_id in retained_tool_ids_for_next_user:
|
|
77
82
|
next_content.append(block)
|
|
78
83
|
else:
|
|
79
|
-
next_content.append(services.tool_result_as_text(block))
|
|
80
84
|
converted_tool_results += 1
|
|
81
85
|
content_changed = True
|
|
82
86
|
continue
|
|
83
87
|
next_content.append(block)
|
|
84
|
-
|
|
88
|
+
if next_content:
|
|
89
|
+
normalized_messages.append(_with_content(message, next_content) if content_changed else message)
|
|
85
90
|
changed = changed or content_changed
|
|
86
91
|
retained_tool_ids_for_next_user = set()
|
|
87
92
|
continue
|
|
@@ -95,7 +100,7 @@ def normalize_historical_anthropic_tool_turns(
|
|
|
95
100
|
out["messages"] = normalized_messages
|
|
96
101
|
services.log(
|
|
97
102
|
"WARN",
|
|
98
|
-
"
|
|
103
|
+
"discarded unmatched historical Anthropic tool blocks for provider=%s tool_uses=%d tool_results=%d"
|
|
99
104
|
% (provider, converted_tool_uses, converted_tool_results),
|
|
100
105
|
)
|
|
101
106
|
return out
|
|
@@ -152,6 +152,8 @@ class ProviderModelCatalogPolicy:
|
|
|
152
152
|
allow_configured_fallback: bool = False
|
|
153
153
|
allow_public_without_auth: bool = False
|
|
154
154
|
use_bundled_catalog_fallback: bool = False
|
|
155
|
+
authoritative_upstream_catalog: bool = False
|
|
156
|
+
supplemental_model_aliases: tuple[tuple[str, str], ...] = ()
|
|
155
157
|
|
|
156
158
|
|
|
157
159
|
@dataclass(frozen=True)
|
|
@@ -220,6 +222,14 @@ class ProviderOptionPresentationPolicy:
|
|
|
220
222
|
show_ip_family_control: bool = False
|
|
221
223
|
|
|
222
224
|
|
|
225
|
+
@dataclass(frozen=True)
|
|
226
|
+
class HostedToolPolicy:
|
|
227
|
+
"""Provider-owned hosted tool catalog and execution endpoint."""
|
|
228
|
+
|
|
229
|
+
base_url: str = ""
|
|
230
|
+
formulas: tuple[str, ...] = ()
|
|
231
|
+
|
|
232
|
+
|
|
223
233
|
@dataclass(frozen=True)
|
|
224
234
|
class ProviderUiPolicy:
|
|
225
235
|
"""Provider-owned labels used by shared menus."""
|
|
@@ -269,6 +279,12 @@ class ProviderAdapter(ABC):
|
|
|
269
279
|
def default_base_url(self) -> str:
|
|
270
280
|
"""Return the provider default API base URL."""
|
|
271
281
|
|
|
282
|
+
def anthropic_base_url(self, config: ProviderConfig) -> str:
|
|
283
|
+
"""Return the base URL used for native Anthropic Messages requests."""
|
|
284
|
+
|
|
285
|
+
del config
|
|
286
|
+
return self.default_base_url()
|
|
287
|
+
|
|
272
288
|
def normalize_base_url(self, value: str) -> str:
|
|
273
289
|
"""Normalize a user-supplied endpoint before it is persisted."""
|
|
274
290
|
|
|
@@ -365,6 +381,17 @@ class ProviderAdapter(ABC):
|
|
|
365
381
|
del model
|
|
366
382
|
return self.capabilities(config).supports_tool_choice
|
|
367
383
|
|
|
384
|
+
def supports_tool_choice_for_request(
|
|
385
|
+
self,
|
|
386
|
+
config: ProviderConfig,
|
|
387
|
+
model: str | None,
|
|
388
|
+
request: Mapping[str, Any],
|
|
389
|
+
) -> bool:
|
|
390
|
+
"""Return whether this request may include an explicit tool choice."""
|
|
391
|
+
|
|
392
|
+
del request
|
|
393
|
+
return self.supports_tool_choice(config, model)
|
|
394
|
+
|
|
368
395
|
def normalizes_anthropic_tool_use(
|
|
369
396
|
self,
|
|
370
397
|
config: ProviderConfig,
|
|
@@ -392,6 +419,28 @@ class ProviderAdapter(ABC):
|
|
|
392
419
|
del config, model
|
|
393
420
|
return False
|
|
394
421
|
|
|
422
|
+
def reasoning_passback_enabled(
|
|
423
|
+
self, config: ProviderConfig, model: str | None = None
|
|
424
|
+
) -> bool:
|
|
425
|
+
"""Whether provider-native reasoning should survive history conversion."""
|
|
426
|
+
|
|
427
|
+
return self.openai_reasoning_passback_enabled(config, model)
|
|
428
|
+
|
|
429
|
+
def hosted_tool_policy(self, config: ProviderConfig) -> HostedToolPolicy:
|
|
430
|
+
del config
|
|
431
|
+
return HostedToolPolicy()
|
|
432
|
+
|
|
433
|
+
def should_omit_openai_tool_choice(
|
|
434
|
+
self,
|
|
435
|
+
config: ProviderConfig,
|
|
436
|
+
model: str | None,
|
|
437
|
+
request: Mapping[str, Any],
|
|
438
|
+
) -> bool:
|
|
439
|
+
"""Whether this provider/model cannot combine reasoning with tool_choice."""
|
|
440
|
+
|
|
441
|
+
del request
|
|
442
|
+
return self.openai_reasoning_passback_enabled(config, model)
|
|
443
|
+
|
|
395
444
|
def model_catalog_policy(self, config: ProviderConfig) -> ProviderModelCatalogPolicy:
|
|
396
445
|
del config
|
|
397
446
|
return ProviderModelCatalogPolicy()
|
|
@@ -589,6 +638,12 @@ class ProviderAdapter(ABC):
|
|
|
589
638
|
del config
|
|
590
639
|
return False
|
|
591
640
|
|
|
641
|
+
def supports_server_web_tools(self, config: ProviderConfig) -> bool:
|
|
642
|
+
"""Whether this provider can execute Claude's server-side web tools."""
|
|
643
|
+
|
|
644
|
+
del config
|
|
645
|
+
return False
|
|
646
|
+
|
|
592
647
|
def context_compaction_available(self, config: ProviderConfig) -> bool:
|
|
593
648
|
"""Whether this configured provider can run an auxiliary summary request."""
|
|
594
649
|
|
|
@@ -617,6 +672,19 @@ class ProviderAdapter(ABC):
|
|
|
617
672
|
del config
|
|
618
673
|
return request
|
|
619
674
|
|
|
675
|
+
def anthropic_system_role_strategy(
|
|
676
|
+
self, config: ProviderConfig
|
|
677
|
+
) -> Literal["inline_user", "hoist_top_level"]:
|
|
678
|
+
"""Choose how non-standard system-role history is made wire-valid.
|
|
679
|
+
|
|
680
|
+
Keeping the item at its original position protects the stable prefix
|
|
681
|
+
used by provider-side context caches. Compatibility servers that only
|
|
682
|
+
accept top-level system context may opt into hoisting explicitly.
|
|
683
|
+
"""
|
|
684
|
+
|
|
685
|
+
del config
|
|
686
|
+
return "inline_user"
|
|
687
|
+
|
|
620
688
|
def openai_reasoning_effort(
|
|
621
689
|
self, config: ProviderConfig, model: str, request: Mapping[str, Any]
|
|
622
690
|
) -> str | None:
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""Channel connection registry, worker, and lifecycle bounded context."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
|
|
8
|
+
from .channel_connection_lifecycle import (
|
|
9
|
+
ChannelConnectionLifecycle,
|
|
10
|
+
ChannelConnectionLifecycleEffects,
|
|
11
|
+
ChannelConnectionLifecyclePolicy,
|
|
12
|
+
ChannelConnectionLifecycleStore,
|
|
13
|
+
)
|
|
14
|
+
from .channel_connection_registry import ChannelConnectionRegistry
|
|
15
|
+
from .channel_connection_worker import (
|
|
16
|
+
ChannelConnectionWorker,
|
|
17
|
+
ChannelWorkerEffects,
|
|
18
|
+
ChannelWorkerPolicy,
|
|
19
|
+
ChannelWorkerStateStore,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True, slots=True)
|
|
24
|
+
class ChannelConnectionStatePorts:
|
|
25
|
+
connections: dict[str, dict[str, Any]]
|
|
26
|
+
lock: Any
|
|
27
|
+
rpc_condition: Any
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class ChannelConnectionWorkerPorts:
|
|
32
|
+
log: Callable[[str, str], None]
|
|
33
|
+
dispatch: Callable[..., Any]
|
|
34
|
+
initialize_streamable: Callable[[str], None]
|
|
35
|
+
close_state_session: Callable[[dict[str, Any], str], bool]
|
|
36
|
+
streamable_headers: Callable[..., dict[str, str]]
|
|
37
|
+
session_not_found: Callable[..., bool]
|
|
38
|
+
http_error_body: Callable[..., str]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class ChannelConnectionLifecyclePorts:
|
|
43
|
+
safe_segment: Callable[[Any], str]
|
|
44
|
+
close_session: Callable[[dict[str, Any], str], bool]
|
|
45
|
+
cleanup_stale_sessions: Callable[..., None]
|
|
46
|
+
parse_bool: Callable[..., bool]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class ChannelConnectionProtocol:
|
|
51
|
+
streamable_version: str
|
|
52
|
+
legacy_sse_version: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, slots=True)
|
|
56
|
+
class ChannelConnectionContext:
|
|
57
|
+
state: ChannelConnectionStatePorts
|
|
58
|
+
worker_ports: ChannelConnectionWorkerPorts
|
|
59
|
+
lifecycle_ports: ChannelConnectionLifecyclePorts
|
|
60
|
+
protocol: ChannelConnectionProtocol
|
|
61
|
+
|
|
62
|
+
def registry(self) -> ChannelConnectionRegistry:
|
|
63
|
+
return ChannelConnectionRegistry(
|
|
64
|
+
states=self.state.connections,
|
|
65
|
+
lock=self.state.lock,
|
|
66
|
+
rpc_condition=self.state.rpc_condition,
|
|
67
|
+
log=self.worker_ports.log,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def statuses(self) -> dict[str, Any]:
|
|
71
|
+
return self.registry().statuses()
|
|
72
|
+
|
|
73
|
+
def update(self, name: str, **updates: Any) -> None:
|
|
74
|
+
self.registry().update(name, **updates)
|
|
75
|
+
|
|
76
|
+
def mark_session_lost(self, name: str, reason: str) -> None:
|
|
77
|
+
self.registry().mark_session_lost(name, reason)
|
|
78
|
+
|
|
79
|
+
def store_rpc_response(self, name: str, data_text: str) -> bool:
|
|
80
|
+
return self.registry().store_rpc_response(name, data_text)
|
|
81
|
+
|
|
82
|
+
def take_rpc_response(
|
|
83
|
+
self, name: str, rpc_id: Any, timeout: float
|
|
84
|
+
) -> dict[str, Any] | None:
|
|
85
|
+
return self.registry().take_rpc_response(name, rpc_id, timeout)
|
|
86
|
+
|
|
87
|
+
def state_name_for_mcp_server(self, server_name: str) -> str | None:
|
|
88
|
+
return self.registry().state_name_for_mcp_server(server_name)
|
|
89
|
+
|
|
90
|
+
def connection_matches(
|
|
91
|
+
self, state: dict[str, Any], connection_id: str | None
|
|
92
|
+
) -> bool:
|
|
93
|
+
if not connection_id:
|
|
94
|
+
return True
|
|
95
|
+
return str(state.get("connection_id") or "") == str(connection_id)
|
|
96
|
+
|
|
97
|
+
def worker_running(self, name: str, connection_id: str | None) -> bool:
|
|
98
|
+
with self.state.lock:
|
|
99
|
+
state = self.state.connections.get(name)
|
|
100
|
+
return bool(
|
|
101
|
+
state
|
|
102
|
+
and state.get("running")
|
|
103
|
+
and self.connection_matches(state, connection_id)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def worker(self) -> ChannelConnectionWorker:
|
|
107
|
+
ports = self.worker_ports
|
|
108
|
+
return ChannelConnectionWorker(
|
|
109
|
+
state_store=ChannelWorkerStateStore(
|
|
110
|
+
self.state.connections, self.state.lock
|
|
111
|
+
),
|
|
112
|
+
effects=ChannelWorkerEffects(
|
|
113
|
+
log=ports.log,
|
|
114
|
+
dispatch=ports.dispatch,
|
|
115
|
+
set_state=self.update,
|
|
116
|
+
initialize_streamable=ports.initialize_streamable,
|
|
117
|
+
close_state_session=ports.close_state_session,
|
|
118
|
+
streamable_headers=ports.streamable_headers,
|
|
119
|
+
session_not_found=ports.session_not_found,
|
|
120
|
+
http_error_body=ports.http_error_body,
|
|
121
|
+
),
|
|
122
|
+
policy=ChannelWorkerPolicy(
|
|
123
|
+
streamable_protocol_version=self.protocol.streamable_version,
|
|
124
|
+
legacy_sse_protocol_version=self.protocol.legacy_sse_version,
|
|
125
|
+
parse_bool=self.lifecycle_ports.parse_bool,
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def run_sse_worker(
|
|
130
|
+
self, name: str, connection_id: str | None = None
|
|
131
|
+
) -> None:
|
|
132
|
+
self.worker().run_sse(name, connection_id)
|
|
133
|
+
|
|
134
|
+
def run_streamable_http_worker(
|
|
135
|
+
self, name: str, connection_id: str | None = None
|
|
136
|
+
) -> None:
|
|
137
|
+
self.worker().run_streamable_http(name, connection_id)
|
|
138
|
+
|
|
139
|
+
def lifecycle(self) -> ChannelConnectionLifecycle:
|
|
140
|
+
ports = self.lifecycle_ports
|
|
141
|
+
return ChannelConnectionLifecycle(
|
|
142
|
+
store=ChannelConnectionLifecycleStore(
|
|
143
|
+
self.state.connections, self.state.lock
|
|
144
|
+
),
|
|
145
|
+
effects=ChannelConnectionLifecycleEffects(
|
|
146
|
+
safe_segment=ports.safe_segment,
|
|
147
|
+
close_session=ports.close_session,
|
|
148
|
+
cleanup_stale_sessions=ports.cleanup_stale_sessions,
|
|
149
|
+
public_status=ChannelConnectionRegistry.public_status,
|
|
150
|
+
all_statuses=self.statuses,
|
|
151
|
+
sse_worker=self.run_sse_worker,
|
|
152
|
+
streamable_http_worker=self.run_streamable_http_worker,
|
|
153
|
+
),
|
|
154
|
+
policy=ChannelConnectionLifecyclePolicy(
|
|
155
|
+
streamable_protocol_version=self.protocol.streamable_version,
|
|
156
|
+
legacy_sse_protocol_version=self.protocol.legacy_sse_version,
|
|
157
|
+
parse_bool=ports.parse_bool,
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def start(self, config: dict[str, Any]) -> dict[str, Any]:
|
|
162
|
+
return self.lifecycle().start(config)
|
|
163
|
+
|
|
164
|
+
def stop(self, name: str | None = None) -> dict[str, Any]:
|
|
165
|
+
return self.lifecycle().stop(name)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(frozen=True, slots=True)
|
|
169
|
+
class ChannelConnectionCompatibilityApi:
|
|
170
|
+
context: Callable[[], ChannelConnectionContext]
|
|
171
|
+
|
|
172
|
+
def registry(self) -> ChannelConnectionRegistry:
|
|
173
|
+
return self.context().registry()
|
|
174
|
+
|
|
175
|
+
def statuses(self) -> dict[str, Any]:
|
|
176
|
+
return self.context().statuses()
|
|
177
|
+
|
|
178
|
+
def update(self, name: str, **updates: Any) -> None:
|
|
179
|
+
self.context().update(name, **updates)
|
|
180
|
+
|
|
181
|
+
def mark_session_lost(self, name: str, reason: str) -> None:
|
|
182
|
+
self.context().mark_session_lost(name, reason)
|
|
183
|
+
|
|
184
|
+
def store_rpc_response(self, name: str, data_text: str) -> bool:
|
|
185
|
+
return self.context().store_rpc_response(name, data_text)
|
|
186
|
+
|
|
187
|
+
def take_rpc_response(
|
|
188
|
+
self, name: str, rpc_id: Any, timeout: float
|
|
189
|
+
) -> dict[str, Any] | None:
|
|
190
|
+
return self.context().take_rpc_response(name, rpc_id, timeout)
|
|
191
|
+
|
|
192
|
+
def state_name_for_mcp_server(self, server_name: str) -> str | None:
|
|
193
|
+
return self.context().state_name_for_mcp_server(server_name)
|
|
194
|
+
|
|
195
|
+
def connection_matches(
|
|
196
|
+
self, state: dict[str, Any], connection_id: str | None
|
|
197
|
+
) -> bool:
|
|
198
|
+
return self.context().connection_matches(state, connection_id)
|
|
199
|
+
|
|
200
|
+
def worker_running(self, name: str, connection_id: str | None) -> bool:
|
|
201
|
+
return self.context().worker_running(name, connection_id)
|
|
202
|
+
|
|
203
|
+
def worker(self) -> ChannelConnectionWorker:
|
|
204
|
+
return self.context().worker()
|
|
205
|
+
|
|
206
|
+
def run_sse_worker(
|
|
207
|
+
self, name: str, connection_id: str | None = None
|
|
208
|
+
) -> None:
|
|
209
|
+
self.context().run_sse_worker(name, connection_id)
|
|
210
|
+
|
|
211
|
+
def run_streamable_http_worker(
|
|
212
|
+
self, name: str, connection_id: str | None = None
|
|
213
|
+
) -> None:
|
|
214
|
+
self.context().run_streamable_http_worker(name, connection_id)
|
|
215
|
+
|
|
216
|
+
def lifecycle(self) -> ChannelConnectionLifecycle:
|
|
217
|
+
return self.context().lifecycle()
|
|
218
|
+
|
|
219
|
+
def start(self, config: dict[str, Any]) -> dict[str, Any]:
|
|
220
|
+
return self.context().start(config)
|
|
221
|
+
|
|
222
|
+
def stop(self, name: str | None = None) -> dict[str, Any]:
|
|
223
|
+
return self.context().stop(name)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
__all__ = [
|
|
227
|
+
"ChannelConnectionCompatibilityApi",
|
|
228
|
+
"ChannelConnectionContext",
|
|
229
|
+
"ChannelConnectionLifecyclePorts",
|
|
230
|
+
"ChannelConnectionProtocol",
|
|
231
|
+
"ChannelConnectionStatePorts",
|
|
232
|
+
"ChannelConnectionWorkerPorts",
|
|
233
|
+
]
|