@oneciel-ai/ciel-runtime 0.2.37 → 0.2.38
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/CHANGELOG.md +78 -0
- package/README.md +5 -2
- package/ciel_runtime.py +94 -96
- package/ciel_runtime_support/anthropic_model_policy.py +20 -3
- package/ciel_runtime_support/architecture.py +5 -0
- package/ciel_runtime_support/channel_inflight.py +4 -4
- package/ciel_runtime_support/channel_injection.py +56 -1
- package/ciel_runtime_support/channel_mcp_tools.py +34 -0
- package/ciel_runtime_support/channel_message_policy.py +9 -1
- package/ciel_runtime_support/channel_message_prompt.py +23 -0
- package/ciel_runtime_support/channel_pending_injection.py +86 -32
- package/ciel_runtime_support/channel_pending_poll.py +6 -2
- package/ciel_runtime_support/channel_runtime_environment.py +0 -11
- package/ciel_runtime_support/channel_terminal_context.py +0 -6
- package/ciel_runtime_support/channel_terminal_proxy.py +3 -42
- package/ciel_runtime_support/channel_transcript_repository.py +16 -1
- package/ciel_runtime_support/channel_wake_claim_repository.py +0 -19
- package/ciel_runtime_support/channel_wake_context.py +6 -13
- package/ciel_runtime_support/channel_wake_delivery_repository.py +50 -0
- package/ciel_runtime_support/chat_http_controller.py +88 -5
- package/ciel_runtime_support/claude_environment.py +100 -3
- package/ciel_runtime_support/claude_launch_assembly.py +4 -0
- package/ciel_runtime_support/claude_session_socket.py +181 -0
- package/ciel_runtime_support/cli_application_context.py +18 -10
- package/ciel_runtime_support/cli_dispatch.py +19 -3
- package/ciel_runtime_support/cli_parser.py +2 -0
- package/ciel_runtime_support/cli_usage.py +3 -2
- package/ciel_runtime_support/codex_backend_context.py +8 -3
- package/ciel_runtime_support/codex_completion_gate.py +198 -0
- package/ciel_runtime_support/codex_turn_recovery.py +240 -83
- package/ciel_runtime_support/compatibility_protocol.py +5 -2
- package/ciel_runtime_support/config_migrations.py +79 -0
- package/ciel_runtime_support/config_repository.py +7 -0
- package/ciel_runtime_support/context_summary_policy.py +24 -4
- package/ciel_runtime_support/external_event_menu.py +107 -0
- package/ciel_runtime_support/external_event_receiver.py +19 -1
- package/ciel_runtime_support/launch_state.py +2 -0
- package/ciel_runtime_support/managed_tool_injection.py +33 -0
- package/ciel_runtime_support/muse_runtime_context.py +291 -0
- package/ciel_runtime_support/ollama_thinking.py +12 -26
- package/ciel_runtime_support/prelaunch.py +13 -0
- package/ciel_runtime_support/prelaunch_launch_panel.py +2 -1
- package/ciel_runtime_support/prompt_compaction.py +39 -0
- package/ciel_runtime_support/provider_files_proxy.py +248 -0
- package/ciel_runtime_support/provider_model_identity.py +10 -2
- package/ciel_runtime_support/provider_option_cli.py +1 -1
- package/ciel_runtime_support/provider_request_access.py +54 -1
- package/ciel_runtime_support/provider_responses_passthrough.py +152 -15
- package/ciel_runtime_support/providers/alibaba.py +34 -1
- package/ciel_runtime_support/providers/anthropic.py +18 -1
- package/ciel_runtime_support/providers/meta.py +252 -21
- package/ciel_runtime_support/pseudo_tool_parser.py +17 -1
- package/ciel_runtime_support/remote_bridge.py +4 -0
- package/ciel_runtime_support/remote_instructions.py +5 -0
- package/ciel_runtime_support/remote_memory.py +1 -1
- package/ciel_runtime_support/responses_cache_diagnostics.py +82 -0
- package/ciel_runtime_support/responses_custom_tool_bridge.py +282 -0
- package/ciel_runtime_support/responses_input_compatibility.py +19 -3
- package/ciel_runtime_support/router_http.py +301 -4
- package/ciel_runtime_support/router_observability_context.py +26 -0
- package/ciel_runtime_support/router_server_context.py +1 -0
- package/ciel_runtime_support/runtime_adapters.py +31 -0
- package/ciel_runtime_support/runtime_constants.py +23 -1
- package/ciel_runtime_support/runtime_input_gateway.py +85 -5
- package/ciel_runtime_support/runtime_input_status.py +150 -0
- package/ciel_runtime_support/runtime_launch.py +27 -4
- package/ciel_runtime_support/runtime_paths.py +1 -0
- package/ciel_runtime_support/speech_http_controller.py +3 -2
- package/ciel_runtime_support/streaming_anthropic.py +3 -0
- package/ciel_runtime_support/tool_call_events.py +98 -0
- package/ciel_runtime_support/transcript_delta_delivery.py +140 -1
- package/ciel_runtime_support/ui_text.py +1 -0
- package/ciel_runtime_support/web_search_result_events.py +130 -0
- package/ciel_runtime_support/web_ui.py +1 -0
- package/ciel_runtime_support/windows_conpty.py +92 -8
- package/ciel_runtime_support/workspace_mcp.py +4 -1
- package/ciel_runtime_support/workspace_state.py +1 -0
- package/docs/Configuration.md +20 -4
- package/docs/MCP-Channels.md +79 -6
- package/docs/Managed-Tool-Injection.md +25 -0
- package/docs/Module-Map.md +4 -0
- package/docs/Muse-Code.md +73 -0
- package/docs/Observability.md +39 -0
- package/docs/Providers.md +29 -2
- package/docs/Remote-Bridge.md +12 -0
- package/docs/Router.md +1 -0
- package/docs/journal/2026/09/01/diagnostics/anthropic/router/fable51-usage-credits.okf +70 -0
- package/docs/journal/2026/09/01/implementation/claude/session-socket/all-input-paths/default-delivery.okf +133 -0
- package/docs/journal/2026/09/01/implementation/claude/session-socket/windows-direct-input.okf +112 -0
- package/docs/journal/2026/09/02/diagnostics/cache/alibaba/qwen38-post-restart-hit-rate.okf +107 -0
- package/docs/journal/2026/09/02/diagnostics/cache/ollama-cloud/kimi-k3-codex-hit-rate.okf +91 -0
- package/docs/journal/2026/09/02/diagnostics/codex/tui/statusline-cache-metrics.okf +44 -0
- package/docs/journal/2026/09/02/diagnostics/input/transport/fallback-behavior.okf +31 -0
- package/docs/journal/2026/09/02/diagnostics/providers/ollama-cloud/kimi-k3-agent-turn-recovery.okf +125 -0
- package/docs/journal/2026/09/02/diagnostics/providers/ollama-cloud/kimi-k3-parameters.okf +128 -0
- package/docs/journal/2026/09/02/diagnostics/remote/mia/socket-tui-visibility.okf +47 -0
- package/docs/journal/2026/09/02/diagnostics/runtime/alibaba/token-plan/qwen38-latency.okf +108 -0
- package/docs/journal/2026/09/02/diagnostics/runtime/alibaba/token-plan/qwen38-vs-gpt56-latency.okf +92 -0
- package/docs/journal/2026/09/02/implementation/anthropic/context/one-million-defaults.okf +85 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/qwen38-cache-hit-improvement.okf +104 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/qwen38-cache-hit-improvement.png +0 -0
- package/docs/journal/2026/09/02/implementation/cache/alibaba/responses-session-cache.okf +97 -0
- package/docs/journal/2026/09/02/implementation/providers/alibaba/qwen38/0902-parameter-alignment.okf +97 -0
- package/docs/journal/2026/09/02/implementation/providers/meta/multimodal-tools/contributor-protocol-adoption.okf +140 -0
- package/docs/journal/2026/09/02/implementation/providers/meta/muse-spark-1.3-support.okf +53 -0
- package/docs/journal/2026/09/02/monitoring/cache/alibaba/cielarvis-qwen38-live-hit-rate.okf +858 -0
- package/docs/journal/2026/09/02/operations/release/main-merge-local-deploy.okf +36 -0
- package/docs/journal/2026/09/02/operations/release/nightly/local-cache-deployment.okf +54 -0
- package/docs/journal/2026/09/02/operations/release/nightly/ollama-kimi-recovery-model-audit-deployment.okf +58 -0
- package/docs/journal/2026/09/02/operations/release/nightly/qwen38-cache-improvement-deployment.okf +72 -0
- package/docs/journal/2026/09/02/research/cache/alibaba/qwen38-hit-rate-improvement.okf +233 -0
- package/docs/journal/2026/09/02/research/cache/codex/provider-scope.okf +77 -0
- package/docs/journal/2026/09/02/research/providers/ollama-cloud/desktop-model-exhaustive-audit.okf +159 -0
- package/docs/journal/2026/09/03/diagnostics/providers/meta/muse-spark-contributor-cache-hit.okf +148 -0
- package/docs/journal/2026/09/03/diagnostics/providers/meta/muse-spark-contributor-required-schema.okf +127 -0
- package/docs/journal/2026/09/03/diagnostics/providers/ollama-cloud/kimi-k3-resumed-session-stall.okf +160 -0
- package/docs/journal/2026/09/03/diagnostics/providers/ollama-cloud/kimi-k3-substantive-dangling-action.okf +94 -0
- package/docs/journal/2026/09/03/diagnostics/remote/kevin/codex-gpt-early-turn-completion.okf +162 -0
- package/docs/journal/2026/09/03/implementation/runtimes/meta/muse-code/default-yolo.okf +40 -0
- package/docs/journal/2026/09/03/implementation/runtimes/meta/muse-code/native-subscription-router.okf +86 -0
- package/docs/journal/2026/09/03/operations/deployment/local/session-socket-default-fallback.okf +41 -0
- package/docs/journal/2026/09/03/research/deployment/colab/tailscale/credential-storage.okf +41 -0
- package/docs/journal/2026/09/03/research/providers/meta/muse-code/subscription-billing-boundary.okf +64 -0
- package/docs/journal/2026/09/03/research/web-chat/voice/instruction-format.okf +135 -0
- package/docs/journal/2026/09/04/diagnostics/claude/context/early-auto-compaction.okf +75 -0
- package/docs/journal/2026/09/04/diagnostics/claude/context/fable-51-status-200k.okf +54 -0
- package/docs/journal/2026/09/04/diagnostics/runtime/codex/responses/replayed-item-id-validation.okf +74 -0
- package/docs/journal/2026/09/04/diagnostics/workspaces/onecieldmsui/codex/replay-stall.okf +100 -0
- package/docs/journal/2026/09/04/implementation/observability/tool-calls/websocket-stream.okf +49 -0
- package/docs/journal/2026/09/04/implementation/web-chat/input/raw-injection.okf +42 -0
- package/docs/journal/2026/09/05/diagnostics/tty/claude-raw-idle-render.okf +25 -0
- package/docs/journal/2026/09/05/implementation/events/search-response-urls.okf +23 -0
- package/docs/journal/2026/09/05/implementation/tools/native-injection.okf +19 -0
- package/docs/journal/2026/09/05/implementation/windows/conpty/prompt-delivery-lifecycle.okf +43 -0
- package/docs/journal/2026/09/05/release/nightly/conpty-native-tools.okf +13 -0
- package/docs/journal/2026/09/05/release/nightly/search-result-urls.okf +14 -0
- package/docs/journal/2026/09/06/implementation/codex/inherited-web-mcp.okf +22 -0
- package/docs/journal/2026/09/07/integration/main/nightly-merge.okf +13 -0
- package/docs/journal/2026/09/07/release/stable/0.2.38.okf +10 -0
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
6
|
+
from copy import deepcopy
|
|
5
7
|
from dataclasses import dataclass, field
|
|
6
8
|
from typing import Any, Mapping
|
|
7
9
|
|
|
@@ -19,9 +21,34 @@ from .base import HttpBearerProviderAdapter, provider_configuration
|
|
|
19
21
|
from .constants import DEFAULT_REQUEST_TIMEOUT_MS, PROVIDER_DEFAULT_BASE_URLS
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
MUSE_SPARK_MODEL = "muse-spark-1.
|
|
24
|
+
MUSE_SPARK_MODEL = "muse-spark-1.3"
|
|
25
|
+
MUSE_SPARK_MODELS = (
|
|
26
|
+
MUSE_SPARK_MODEL,
|
|
27
|
+
"muse-spark-1.3-contributor",
|
|
28
|
+
"muse-spark-1.2",
|
|
29
|
+
"muse-spark-1.2-contributor",
|
|
30
|
+
"muse-spark-1.1",
|
|
31
|
+
)
|
|
23
32
|
MUSE_SPARK_CONTEXT_WINDOW = 1_048_576
|
|
24
33
|
MUSE_SPARK_AUTO_COMPACT_LIMIT = 900_000
|
|
34
|
+
MUSE_SPARK_CODEX_CATALOG = {
|
|
35
|
+
"context_window": MUSE_SPARK_CONTEXT_WINDOW,
|
|
36
|
+
"max_context_window": MUSE_SPARK_CONTEXT_WINDOW,
|
|
37
|
+
"input_modalities": ["text", "image"],
|
|
38
|
+
"support_verbosity": False,
|
|
39
|
+
"supports_reasoning_summaries": True,
|
|
40
|
+
"supported_reasoning_levels": [
|
|
41
|
+
{"effort": "minimal", "description": "Shortest reasoning pass"},
|
|
42
|
+
{"effort": "low", "description": "Light reasoning"},
|
|
43
|
+
{"effort": "medium", "description": "Moderate reasoning depth"},
|
|
44
|
+
{"effort": "high", "description": "Deep reasoning"},
|
|
45
|
+
{
|
|
46
|
+
"effort": "xhigh",
|
|
47
|
+
"description": "Accepted alias; currently the same strength as high",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
"default_reasoning_level": "high",
|
|
51
|
+
}
|
|
25
52
|
|
|
26
53
|
|
|
27
54
|
@dataclass(frozen=True)
|
|
@@ -33,21 +60,29 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
33
60
|
configuration_defaults_value: dict = field(
|
|
34
61
|
default_factory=lambda: provider_configuration(
|
|
35
62
|
MUSE_SPARK_MODEL,
|
|
36
|
-
custom_models=
|
|
63
|
+
custom_models=MUSE_SPARK_MODELS,
|
|
37
64
|
native_compat=True,
|
|
38
65
|
preserve_anthropic_thinking=True,
|
|
39
66
|
normalize_anthropic_tool_use=True,
|
|
40
67
|
supports_tool_choice=True,
|
|
41
|
-
claude_code_supported_capabilities=[
|
|
68
|
+
claude_code_supported_capabilities=[
|
|
69
|
+
"effort",
|
|
70
|
+
"xhigh_effort",
|
|
71
|
+
"thinking",
|
|
72
|
+
"adaptive_thinking",
|
|
73
|
+
],
|
|
42
74
|
context_window=MUSE_SPARK_CONTEXT_WINDOW,
|
|
43
75
|
max_model_len=MUSE_SPARK_CONTEXT_WINDOW,
|
|
44
76
|
auto_compact_window=MUSE_SPARK_AUTO_COMPACT_LIMIT,
|
|
45
77
|
codex_auto_compact_window=MUSE_SPARK_AUTO_COMPACT_LIMIT,
|
|
78
|
+
codex_model_catalog=deepcopy(MUSE_SPARK_CODEX_CATALOG),
|
|
46
79
|
request_timeout_ms=DEFAULT_REQUEST_TIMEOUT_MS,
|
|
47
80
|
stream_enabled=True,
|
|
48
81
|
stream_word_chunking=False,
|
|
49
82
|
effort_level="high",
|
|
50
83
|
enable_tool_search=True,
|
|
84
|
+
responses_custom_tools_as_functions=True,
|
|
85
|
+
prompt_cache_retention="24h",
|
|
51
86
|
haiku_model=MUSE_SPARK_MODEL,
|
|
52
87
|
opus_model=MUSE_SPARK_MODEL,
|
|
53
88
|
sonnet_model=MUSE_SPARK_MODEL,
|
|
@@ -78,7 +113,7 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
78
113
|
model_catalog_policy_value: ProviderModelCatalogPolicy = field(
|
|
79
114
|
default_factory=lambda: ProviderModelCatalogPolicy(
|
|
80
115
|
kind="openai",
|
|
81
|
-
fallback_models=
|
|
116
|
+
fallback_models=MUSE_SPARK_MODELS,
|
|
82
117
|
allow_configured_fallback=True,
|
|
83
118
|
)
|
|
84
119
|
)
|
|
@@ -97,11 +132,17 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
97
132
|
del model
|
|
98
133
|
return bool(config.options.get("native_compat", True))
|
|
99
134
|
|
|
135
|
+
def supports_server_web_tools(self, config: ProviderConfig) -> bool:
|
|
136
|
+
del config
|
|
137
|
+
return True
|
|
138
|
+
|
|
100
139
|
def supported_protocols(
|
|
101
140
|
self, config: ProviderConfig, model: str | None = None
|
|
102
141
|
) -> frozenset[MessageProtocol]:
|
|
103
142
|
del config, model
|
|
104
|
-
return frozenset(
|
|
143
|
+
return frozenset(
|
|
144
|
+
{"anthropic_messages", "openai_chat", "openai_responses"}
|
|
145
|
+
)
|
|
105
146
|
|
|
106
147
|
def select_protocol(
|
|
107
148
|
self,
|
|
@@ -109,29 +150,33 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
109
150
|
config: ProviderConfig,
|
|
110
151
|
model: str | None = None,
|
|
111
152
|
) -> MessageProtocol:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if operation == "openai_responses"
|
|
116
|
-
else "anthropic_messages"
|
|
117
|
-
)
|
|
153
|
+
if operation in self.supported_protocols(config, model):
|
|
154
|
+
return operation
|
|
155
|
+
return "anthropic_messages"
|
|
118
156
|
|
|
119
157
|
def model_configuration_profile(
|
|
120
158
|
self, config: ProviderConfig
|
|
121
159
|
) -> tuple[Mapping[str, Any], str | None]:
|
|
122
|
-
|
|
160
|
+
model = self.normalize_model_id(config.model)
|
|
161
|
+
if model not in MUSE_SPARK_MODELS:
|
|
123
162
|
return {}, None
|
|
163
|
+
contributor_notice = (
|
|
164
|
+
" Contributor tier permits Meta to train on prompts and completions."
|
|
165
|
+
if model.endswith("-contributor")
|
|
166
|
+
else ""
|
|
167
|
+
)
|
|
124
168
|
return (
|
|
125
169
|
{
|
|
126
170
|
"context_window": MUSE_SPARK_CONTEXT_WINDOW,
|
|
127
171
|
"max_model_len": MUSE_SPARK_CONTEXT_WINDOW,
|
|
128
172
|
"auto_compact_window": MUSE_SPARK_AUTO_COMPACT_LIMIT,
|
|
129
173
|
"effort_level": "high",
|
|
130
|
-
"
|
|
174
|
+
"codex_model_catalog": deepcopy(MUSE_SPARK_CODEX_CATALOG),
|
|
175
|
+
"model_profile": f"{model}-1m",
|
|
131
176
|
},
|
|
132
|
-
"
|
|
177
|
+
f"{model} profile applied: 1M context, high reasoning effort, "
|
|
133
178
|
"and 900K automatic compaction. Start a new session after changing "
|
|
134
|
-
"model, context, or reasoning effort.",
|
|
179
|
+
f"model, context, or reasoning effort.{contributor_notice}",
|
|
135
180
|
)
|
|
136
181
|
|
|
137
182
|
def model_selection_config_updates(
|
|
@@ -169,10 +214,28 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
169
214
|
def normalize_request_options(
|
|
170
215
|
self, config: ProviderConfig, request: Mapping[str, Any]
|
|
171
216
|
) -> Mapping[str, Any]:
|
|
172
|
-
del config
|
|
173
217
|
normalized = dict(request)
|
|
174
218
|
if "input" in normalized and "messages" not in normalized:
|
|
175
|
-
self._normalize_responses_request(normalized)
|
|
219
|
+
self._normalize_responses_request(normalized, config)
|
|
220
|
+
else:
|
|
221
|
+
self._normalize_messages_request(normalized)
|
|
222
|
+
return normalized
|
|
223
|
+
|
|
224
|
+
def normalize_request_options_for_protocol(
|
|
225
|
+
self,
|
|
226
|
+
config: ProviderConfig,
|
|
227
|
+
request: Mapping[str, Any],
|
|
228
|
+
protocol: MessageProtocol | None,
|
|
229
|
+
) -> Mapping[str, Any]:
|
|
230
|
+
normalized = dict(request)
|
|
231
|
+
if protocol == "openai_responses":
|
|
232
|
+
self._normalize_responses_request(normalized, config)
|
|
233
|
+
elif protocol == "openai_chat":
|
|
234
|
+
self._normalize_chat_request(normalized)
|
|
235
|
+
elif protocol == "anthropic_messages":
|
|
236
|
+
self._normalize_messages_request(normalized)
|
|
237
|
+
elif "input" in normalized and "messages" not in normalized:
|
|
238
|
+
self._normalize_responses_request(normalized, config)
|
|
176
239
|
else:
|
|
177
240
|
self._normalize_messages_request(normalized)
|
|
178
241
|
return normalized
|
|
@@ -192,7 +255,15 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
192
255
|
return False
|
|
193
256
|
|
|
194
257
|
@classmethod
|
|
195
|
-
def _normalize_responses_request(
|
|
258
|
+
def _normalize_responses_request(
|
|
259
|
+
cls, request: dict[str, Any], config: ProviderConfig
|
|
260
|
+
) -> None:
|
|
261
|
+
if "prompt_cache_retention" not in request and request.get("prompt_cache_key"):
|
|
262
|
+
retention = str(
|
|
263
|
+
config.options.get("prompt_cache_retention") or ""
|
|
264
|
+
).strip()
|
|
265
|
+
if retention in {"in_memory", "24h"}:
|
|
266
|
+
request["prompt_cache_retention"] = retention
|
|
196
267
|
include = request.get("include")
|
|
197
268
|
if request.get("previous_response_id"):
|
|
198
269
|
if isinstance(include, list):
|
|
@@ -216,8 +287,150 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
216
287
|
reasoning = request.get("reasoning")
|
|
217
288
|
if isinstance(reasoning, Mapping):
|
|
218
289
|
projected = dict(reasoning)
|
|
219
|
-
projected["effort"] = cls.
|
|
290
|
+
projected["effort"] = cls._responses_effort(projected.get("effort"))
|
|
291
|
+
# Match the official OpenCode Meta path: keep a concise visible
|
|
292
|
+
# reasoning summary while encrypted_content carries replay state.
|
|
293
|
+
projected.setdefault("summary", "auto")
|
|
220
294
|
request["reasoning"] = projected
|
|
295
|
+
tools = request.get("tools")
|
|
296
|
+
if isinstance(tools, list):
|
|
297
|
+
request["tools"] = [cls._normalize_responses_tool(tool) for tool in tools]
|
|
298
|
+
raw_input = request.get("input")
|
|
299
|
+
if isinstance(raw_input, list):
|
|
300
|
+
request["input"] = [cls._normalize_responses_input(item) for item in raw_input]
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
def _normalize_responses_tool(cls, tool: Any) -> Any:
|
|
304
|
+
"""Project tool parameters to Meta's strict Responses schema contract."""
|
|
305
|
+
|
|
306
|
+
if not isinstance(tool, Mapping):
|
|
307
|
+
return deepcopy(tool)
|
|
308
|
+
projected = deepcopy(dict(tool))
|
|
309
|
+
if str(projected.get("type") or "") == "custom":
|
|
310
|
+
description = str(projected.get("description") or "").strip()
|
|
311
|
+
format_value = projected.get("format")
|
|
312
|
+
if isinstance(format_value, Mapping):
|
|
313
|
+
definition = str(format_value.get("definition") or "").strip()
|
|
314
|
+
if definition:
|
|
315
|
+
description = "\n\n".join(
|
|
316
|
+
part
|
|
317
|
+
for part in (
|
|
318
|
+
description,
|
|
319
|
+
"Raw input must satisfy this grammar:\n" + definition,
|
|
320
|
+
)
|
|
321
|
+
if part
|
|
322
|
+
)
|
|
323
|
+
projected = {
|
|
324
|
+
"type": "function",
|
|
325
|
+
"name": str(projected.get("name") or ""),
|
|
326
|
+
"description": description,
|
|
327
|
+
"parameters": {
|
|
328
|
+
"type": "object",
|
|
329
|
+
"properties": {"input": {"type": "string"}},
|
|
330
|
+
"required": ["input"],
|
|
331
|
+
"additionalProperties": False,
|
|
332
|
+
},
|
|
333
|
+
"strict": True,
|
|
334
|
+
}
|
|
335
|
+
parameters = projected.get("parameters")
|
|
336
|
+
if isinstance(parameters, Mapping):
|
|
337
|
+
projected["parameters"] = cls._strict_responses_schema(parameters)
|
|
338
|
+
if (
|
|
339
|
+
str(tool.get("type") or "") == "function"
|
|
340
|
+
and isinstance(parameters, Mapping)
|
|
341
|
+
):
|
|
342
|
+
projected["strict"] = True
|
|
343
|
+
return projected
|
|
344
|
+
|
|
345
|
+
@staticmethod
|
|
346
|
+
def _normalize_responses_input(item: Any) -> Any:
|
|
347
|
+
if not isinstance(item, Mapping):
|
|
348
|
+
return deepcopy(item)
|
|
349
|
+
projected = deepcopy(dict(item))
|
|
350
|
+
item_type = str(projected.get("type") or "")
|
|
351
|
+
if item_type == "custom_tool_call":
|
|
352
|
+
projected["type"] = "function_call"
|
|
353
|
+
projected["arguments"] = json.dumps(
|
|
354
|
+
{"input": str(projected.pop("input", ""))},
|
|
355
|
+
ensure_ascii=False,
|
|
356
|
+
separators=(",", ":"),
|
|
357
|
+
)
|
|
358
|
+
elif item_type == "custom_tool_call_output":
|
|
359
|
+
projected["type"] = "function_call_output"
|
|
360
|
+
return projected
|
|
361
|
+
|
|
362
|
+
@classmethod
|
|
363
|
+
def _strict_responses_schema(cls, schema: Mapping[str, Any]) -> dict[str, Any]:
|
|
364
|
+
"""Require every object property while keeping former optionals nullable."""
|
|
365
|
+
|
|
366
|
+
projected = deepcopy(dict(schema))
|
|
367
|
+
properties = projected.get("properties")
|
|
368
|
+
if isinstance(properties, Mapping):
|
|
369
|
+
originally_required = {
|
|
370
|
+
str(name)
|
|
371
|
+
for name in projected.get("required") or []
|
|
372
|
+
if isinstance(name, str)
|
|
373
|
+
}
|
|
374
|
+
strict_properties: dict[str, Any] = {}
|
|
375
|
+
for raw_name, raw_property in properties.items():
|
|
376
|
+
name = str(raw_name)
|
|
377
|
+
if isinstance(raw_property, Mapping):
|
|
378
|
+
normalized_property = cls._strict_responses_schema(raw_property)
|
|
379
|
+
if name not in originally_required:
|
|
380
|
+
normalized_property = cls._nullable_schema(normalized_property)
|
|
381
|
+
strict_properties[name] = normalized_property
|
|
382
|
+
else:
|
|
383
|
+
strict_properties[name] = deepcopy(raw_property)
|
|
384
|
+
projected["properties"] = strict_properties
|
|
385
|
+
projected["required"] = list(strict_properties)
|
|
386
|
+
projected["additionalProperties"] = False
|
|
387
|
+
|
|
388
|
+
items = projected.get("items")
|
|
389
|
+
if isinstance(items, Mapping):
|
|
390
|
+
projected["items"] = cls._strict_responses_schema(items)
|
|
391
|
+
for keyword in ("anyOf", "oneOf", "allOf"):
|
|
392
|
+
variants = projected.get(keyword)
|
|
393
|
+
if isinstance(variants, list):
|
|
394
|
+
projected[keyword] = [
|
|
395
|
+
cls._strict_responses_schema(item)
|
|
396
|
+
if isinstance(item, Mapping)
|
|
397
|
+
else deepcopy(item)
|
|
398
|
+
for item in variants
|
|
399
|
+
]
|
|
400
|
+
definitions = projected.get("$defs")
|
|
401
|
+
if isinstance(definitions, Mapping):
|
|
402
|
+
projected["$defs"] = {
|
|
403
|
+
str(name): cls._strict_responses_schema(value)
|
|
404
|
+
if isinstance(value, Mapping)
|
|
405
|
+
else deepcopy(value)
|
|
406
|
+
for name, value in definitions.items()
|
|
407
|
+
}
|
|
408
|
+
return projected
|
|
409
|
+
|
|
410
|
+
@staticmethod
|
|
411
|
+
def _nullable_schema(schema: Mapping[str, Any]) -> dict[str, Any]:
|
|
412
|
+
projected = deepcopy(dict(schema))
|
|
413
|
+
raw_type = projected.get("type")
|
|
414
|
+
if isinstance(raw_type, str):
|
|
415
|
+
if raw_type != "null":
|
|
416
|
+
projected["type"] = [raw_type, "null"]
|
|
417
|
+
elif isinstance(raw_type, list):
|
|
418
|
+
if "null" not in raw_type:
|
|
419
|
+
projected["type"] = [*raw_type, "null"]
|
|
420
|
+
elif isinstance(projected.get("anyOf"), list):
|
|
421
|
+
variants = list(projected["anyOf"])
|
|
422
|
+
if not any(
|
|
423
|
+
isinstance(item, Mapping) and item.get("type") == "null"
|
|
424
|
+
for item in variants
|
|
425
|
+
):
|
|
426
|
+
variants.append({"type": "null"})
|
|
427
|
+
projected["anyOf"] = variants
|
|
428
|
+
else:
|
|
429
|
+
projected = {"anyOf": [projected, {"type": "null"}]}
|
|
430
|
+
enum = projected.get("enum")
|
|
431
|
+
if isinstance(enum, list) and None not in enum:
|
|
432
|
+
projected["enum"] = [*enum, None]
|
|
433
|
+
return projected
|
|
221
434
|
|
|
222
435
|
@classmethod
|
|
223
436
|
def _normalize_messages_request(cls, request: dict[str, Any]) -> None:
|
|
@@ -234,11 +447,22 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
234
447
|
output_config = request.get("output_config")
|
|
235
448
|
if isinstance(output_config, Mapping) and output_config.get("effort") is not None:
|
|
236
449
|
projected_output = dict(output_config)
|
|
237
|
-
projected_output["effort"] = cls.
|
|
450
|
+
projected_output["effort"] = cls._messages_effort(
|
|
451
|
+
projected_output.get("effort")
|
|
452
|
+
)
|
|
238
453
|
request["output_config"] = projected_output
|
|
239
454
|
|
|
240
455
|
@staticmethod
|
|
241
|
-
def
|
|
456
|
+
def _normalize_chat_request(request: dict[str, Any]) -> None:
|
|
457
|
+
# Meta Chat Completions documents only the automatic tool-selection
|
|
458
|
+
# mode. OpenAI clients can send a named function choice object; retain
|
|
459
|
+
# the tools but project that unsupported selector to Meta's auto mode.
|
|
460
|
+
tool_choice = request.get("tool_choice")
|
|
461
|
+
if isinstance(tool_choice, Mapping):
|
|
462
|
+
request["tool_choice"] = "auto"
|
|
463
|
+
|
|
464
|
+
@staticmethod
|
|
465
|
+
def _responses_effort(value: Any) -> str:
|
|
242
466
|
effort = str(value or "high").strip().lower()
|
|
243
467
|
if effort in {"none", "minimal"}:
|
|
244
468
|
return "minimal"
|
|
@@ -248,10 +472,17 @@ class MetaModelProviderAdapter(HttpBearerProviderAdapter):
|
|
|
248
472
|
return "xhigh"
|
|
249
473
|
return "high"
|
|
250
474
|
|
|
475
|
+
@classmethod
|
|
476
|
+
def _messages_effort(cls, value: Any) -> str:
|
|
477
|
+
effort = cls._responses_effort(value)
|
|
478
|
+
return "low" if effort == "minimal" else effort
|
|
479
|
+
|
|
251
480
|
|
|
252
481
|
__all__ = [
|
|
253
482
|
"MUSE_SPARK_AUTO_COMPACT_LIMIT",
|
|
483
|
+
"MUSE_SPARK_CODEX_CATALOG",
|
|
254
484
|
"MUSE_SPARK_CONTEXT_WINDOW",
|
|
255
485
|
"MUSE_SPARK_MODEL",
|
|
486
|
+
"MUSE_SPARK_MODELS",
|
|
256
487
|
"MetaModelProviderAdapter",
|
|
257
488
|
]
|
|
@@ -18,7 +18,23 @@ PSEUDO_CALL_END = "<|tool_call_end|>"
|
|
|
18
18
|
|
|
19
19
|
def normalize_tool_arguments(tool_name: str, arguments: Any) -> dict[str, Any]:
|
|
20
20
|
if isinstance(arguments, dict):
|
|
21
|
-
|
|
21
|
+
normalized = dict(arguments)
|
|
22
|
+
# Kimi occasionally abbreviates Codex's ``exec_command`` tool to
|
|
23
|
+
# ``exec`` and emits the shell payload under a generic ``input`` (or
|
|
24
|
+
# OpenAI-style ``command``/``script``) key. Name canonicalization has
|
|
25
|
+
# already resolved the tool before this function runs, so repair only
|
|
26
|
+
# non-empty string aliases whose semantics are still a shell command.
|
|
27
|
+
# A ``js`` field is deliberately not repaired: JavaScript intended for
|
|
28
|
+
# a Node REPL is not a valid PowerShell command.
|
|
29
|
+
if str(tool_name or "").casefold() == "exec_command":
|
|
30
|
+
cmd = normalized.get("cmd")
|
|
31
|
+
if not isinstance(cmd, str) or not cmd.strip():
|
|
32
|
+
for alias in ("command", "script", "input"):
|
|
33
|
+
value = normalized.get(alias)
|
|
34
|
+
if isinstance(value, str) and value.strip():
|
|
35
|
+
normalized["cmd"] = value
|
|
36
|
+
break
|
|
37
|
+
return normalized
|
|
22
38
|
if isinstance(arguments, str):
|
|
23
39
|
text = arguments.strip()
|
|
24
40
|
if not text:
|
|
@@ -20,6 +20,7 @@ REMOTE_LLM_PATHS = frozenset(
|
|
|
20
20
|
REMOTE_GENERATION_PATHS = frozenset(
|
|
21
21
|
{"/v1/chat/completions", "/v1/messages", "/v1/responses"}
|
|
22
22
|
)
|
|
23
|
+
REMOTE_FILES_PATH = "/v1/files"
|
|
23
24
|
|
|
24
25
|
PROVIDER_HEADER = "x-ciel-runtime-provider"
|
|
25
26
|
MODEL_HEADER = "x-ciel-runtime-model"
|
|
@@ -200,6 +201,8 @@ def remote_bridge_path_allowed(path: str) -> bool:
|
|
|
200
201
|
normalized in REMOTE_LLM_PATHS
|
|
201
202
|
or normalized in {"/ca/bridge", "/v1/models"}
|
|
202
203
|
or normalized.startswith("/v1/models/")
|
|
204
|
+
or normalized == REMOTE_FILES_PATH
|
|
205
|
+
or normalized.startswith(REMOTE_FILES_PATH + "/")
|
|
203
206
|
)
|
|
204
207
|
|
|
205
208
|
|
|
@@ -218,6 +221,7 @@ __all__ = [
|
|
|
218
221
|
"REQUEST_API_KEY_MARKER",
|
|
219
222
|
"REMOTE_BRIDGE_INCOMPATIBLE_PROVIDERS",
|
|
220
223
|
"REMOTE_GENERATION_PATHS",
|
|
224
|
+
"REMOTE_FILES_PATH",
|
|
221
225
|
"REMOTE_LLM_PATHS",
|
|
222
226
|
"ROUTER_MANAGED_CREDENTIAL_PROVIDERS",
|
|
223
227
|
"RemoteBridgeRoute",
|
|
@@ -25,6 +25,8 @@ RUNTIME_FILES = {
|
|
|
25
25
|
"grok": "AGENTS.md",
|
|
26
26
|
# ZCode discovers workspace instructions from AGENTS.md.
|
|
27
27
|
"zcode": "AGENTS.md",
|
|
28
|
+
# Muse Code loads AGENTS.md before other project instruction files.
|
|
29
|
+
"muse": "AGENTS.md",
|
|
28
30
|
}
|
|
29
31
|
URL_KEYS = {
|
|
30
32
|
"claude": "claude_url",
|
|
@@ -33,6 +35,7 @@ URL_KEYS = {
|
|
|
33
35
|
"agy": "agy_url",
|
|
34
36
|
"kimi": "kimi_url",
|
|
35
37
|
"grok": "grok_url",
|
|
38
|
+
"muse": "muse_url",
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
_ENV_REFERENCE = re.compile(r"%([A-Za-z_][A-Za-z0-9_]*)%|\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
|
@@ -229,6 +232,7 @@ def panel_rows(config: dict[str, Any]) -> tuple[list[str], list[str]]:
|
|
|
229
232
|
f"AGY URL → GEMINI.md [{compact(current.get('agy_url') or 'unset')}]",
|
|
230
233
|
f"Kimi URL → AGENTS.md [{compact(current.get('kimi_url') or 'unset')}]",
|
|
231
234
|
f"Grok URL → AGENTS.md [{compact(current.get('grok_url') or 'unset')}]",
|
|
235
|
+
f"Muse URL → AGENTS.md [{compact(current.get('muse_url') or 'unset')}]",
|
|
232
236
|
f"Authorization header [{'configured' if current.get('authorization') else 'unset'}]",
|
|
233
237
|
f"HTTP timeout seconds [{current.get('timeout_seconds') or 5}]",
|
|
234
238
|
"Sync configured instruction files now",
|
|
@@ -241,6 +245,7 @@ def panel_rows(config: dict[str, Any]) -> tuple[list[str], list[str]]:
|
|
|
241
245
|
"agy_url",
|
|
242
246
|
"kimi_url",
|
|
243
247
|
"grok_url",
|
|
248
|
+
"muse_url",
|
|
244
249
|
"authorization",
|
|
245
250
|
"timeout_seconds",
|
|
246
251
|
"sync",
|
|
@@ -993,7 +993,7 @@ def sync_all_memory_pointers(
|
|
|
993
993
|
"""Download once and project the verified pointer for every runtime."""
|
|
994
994
|
|
|
995
995
|
result = synchronizer.sync("codex", reason="manual")
|
|
996
|
-
for runtime in ("codex-app-server", "claude", "agy", "kimi", "grok"):
|
|
996
|
+
for runtime in ("codex-app-server", "claude", "agy", "kimi", "grok", "muse"):
|
|
997
997
|
try:
|
|
998
998
|
synchronizer.project_current_pointer(runtime)
|
|
999
999
|
except (OSError, UnicodeError, ValueError) as exc:
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Privacy-safe request fingerprints for native Responses cache diagnosis."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _fingerprint(value: Any) -> str:
|
|
11
|
+
encoded = json.dumps(
|
|
12
|
+
value,
|
|
13
|
+
ensure_ascii=False,
|
|
14
|
+
sort_keys=True,
|
|
15
|
+
separators=(",", ":"),
|
|
16
|
+
).encode("utf-8")
|
|
17
|
+
return hashlib.sha256(encoded).hexdigest()[:16]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def request_cache_profile(
|
|
21
|
+
body: Mapping[str, Any], request_bytes: int
|
|
22
|
+
) -> dict[str, Any]:
|
|
23
|
+
raw_input = body.get("input")
|
|
24
|
+
input_items = raw_input if isinstance(raw_input, list) else [raw_input]
|
|
25
|
+
input_head = input_items[:8]
|
|
26
|
+
cache_key = str(body.get("prompt_cache_key") or "")
|
|
27
|
+
return {
|
|
28
|
+
"cache_hit_percent": 0.0,
|
|
29
|
+
"prompt_cache_key_fingerprint": (
|
|
30
|
+
hashlib.sha256(cache_key.encode("utf-8")).hexdigest()[:16]
|
|
31
|
+
if cache_key
|
|
32
|
+
else ""
|
|
33
|
+
),
|
|
34
|
+
"prompt_cache_retention": str(body.get("prompt_cache_retention") or ""),
|
|
35
|
+
"request_instructions_fingerprint": _fingerprint(body.get("instructions")),
|
|
36
|
+
"request_tools_fingerprint": _fingerprint(body.get("tools")),
|
|
37
|
+
"request_input_head_fingerprint": _fingerprint(input_head),
|
|
38
|
+
"request_input_items": len(input_items) if raw_input is not None else 0,
|
|
39
|
+
"request_tools": len(body.get("tools") or []),
|
|
40
|
+
"request_bytes": max(0, int(request_bytes)),
|
|
41
|
+
"request_uses_previous_response_id": bool(body.get("previous_response_id")),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def usage_with_cache_profile(
|
|
46
|
+
usage: Mapping[str, Any], profile: Mapping[str, Any]
|
|
47
|
+
) -> dict[str, Any]:
|
|
48
|
+
observed = dict(usage)
|
|
49
|
+
input_tokens = max(0, int(observed.get("input_tokens") or 0))
|
|
50
|
+
cache_read = max(0, int(observed.get("cache_read_tokens") or 0))
|
|
51
|
+
observed.update(profile)
|
|
52
|
+
observed["cache_hit_percent"] = (
|
|
53
|
+
round(cache_read * 100.0 / input_tokens, 2) if input_tokens else 0.0
|
|
54
|
+
)
|
|
55
|
+
return observed
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def cache_trace(
|
|
59
|
+
provider: str, model: str, observation: Mapping[str, Any]
|
|
60
|
+
) -> tuple[str, str]:
|
|
61
|
+
hit = float(observation.get("cache_hit_percent") or 0.0)
|
|
62
|
+
level = "WARN" if int(observation.get("input_tokens") or 0) and hit < 90.0 else "INFO"
|
|
63
|
+
fields = (
|
|
64
|
+
"input_tokens",
|
|
65
|
+
"cache_read_tokens",
|
|
66
|
+
"uncached_input_tokens",
|
|
67
|
+
"cache_hit_percent",
|
|
68
|
+
"prompt_cache_key_fingerprint",
|
|
69
|
+
"prompt_cache_retention",
|
|
70
|
+
"request_instructions_fingerprint",
|
|
71
|
+
"request_tools_fingerprint",
|
|
72
|
+
"request_input_head_fingerprint",
|
|
73
|
+
"request_input_items",
|
|
74
|
+
"request_tools",
|
|
75
|
+
"request_bytes",
|
|
76
|
+
"request_uses_previous_response_id",
|
|
77
|
+
)
|
|
78
|
+
detail = " ".join(f"{name}={observation.get(name)}" for name in fields)
|
|
79
|
+
return level, f"provider_responses_cache provider={provider} model={model} {detail}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
__all__ = ["cache_trace", "request_cache_profile", "usage_with_cache_profile"]
|