@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.
- package/README.md +132 -0
- package/ciel-runtime-menu.py +56 -6
- package/ciel_runtime.py +9379 -35909
- package/ciel_runtime_support/advisor_client.py +193 -0
- package/ciel_runtime_support/advisor_policy.py +320 -0
- package/ciel_runtime_support/advisor_refinement.py +160 -0
- package/ciel_runtime_support/advisor_request_builder.py +261 -0
- package/ciel_runtime_support/agy_installer.py +169 -0
- package/ciel_runtime_support/agy_mcp_restore.py +182 -0
- package/ciel_runtime_support/anthropic_model_policy.py +186 -0
- package/ciel_runtime_support/anthropic_response_writer.py +255 -0
- package/ciel_runtime_support/anthropic_tool_turns.py +130 -0
- package/ciel_runtime_support/api_key_cooldown.py +159 -0
- package/ciel_runtime_support/architecture.py +488 -1
- package/ciel_runtime_support/architecture_budget.py +42 -0
- package/ciel_runtime_support/channel_backlog.py +90 -0
- package/ciel_runtime_support/channel_cli.py +119 -0
- package/ciel_runtime_support/channel_compact_injection.py +82 -0
- package/ciel_runtime_support/channel_compact_poll.py +67 -0
- package/ciel_runtime_support/channel_compact_request_repository.py +113 -0
- package/ciel_runtime_support/channel_config_service.py +281 -0
- package/ciel_runtime_support/channel_connection_lifecycle.py +180 -0
- package/ciel_runtime_support/channel_connection_registry.py +128 -0
- package/ciel_runtime_support/channel_connection_worker.py +284 -0
- package/ciel_runtime_support/channel_cursor_recovery.py +92 -0
- package/ciel_runtime_support/channel_cursor_repository.py +89 -0
- package/ciel_runtime_support/channel_cursor_service.py +178 -0
- package/ciel_runtime_support/channel_event_identity.py +212 -0
- package/ciel_runtime_support/channel_event_projection.py +315 -0
- package/ciel_runtime_support/channel_inflight.py +127 -0
- package/ciel_runtime_support/channel_injection.py +115 -0
- package/ciel_runtime_support/channel_launch_guard_repository.py +58 -0
- package/ciel_runtime_support/channel_launch_policy.py +180 -0
- package/ciel_runtime_support/channel_llm_context.py +156 -0
- package/ciel_runtime_support/channel_mcp_discovery.py +186 -0
- package/ciel_runtime_support/channel_mcp_http_controller.py +239 -0
- package/ciel_runtime_support/channel_mcp_ownership.py +148 -0
- package/ciel_runtime_support/channel_mcp_tools.py +240 -0
- package/ciel_runtime_support/channel_mcp_transport.py +394 -0
- package/ciel_runtime_support/channel_message_dedupe.py +65 -0
- package/ciel_runtime_support/channel_message_policy.py +256 -0
- package/ciel_runtime_support/channel_message_prompt.py +305 -0
- package/ciel_runtime_support/channel_message_repository.py +234 -0
- package/ciel_runtime_support/channel_notification_projection.py +217 -0
- package/ciel_runtime_support/channel_panel.py +162 -0
- package/ciel_runtime_support/channel_pending_injection.py +209 -0
- package/ciel_runtime_support/channel_pending_poll.py +109 -0
- package/ciel_runtime_support/channel_probe_cache.py +433 -0
- package/ciel_runtime_support/channel_probe_report.py +101 -0
- package/ciel_runtime_support/channel_runtime_environment.py +181 -0
- package/ciel_runtime_support/channel_session_lifecycle.py +114 -0
- package/ciel_runtime_support/channel_session_repository.py +90 -0
- package/ciel_runtime_support/channel_terminal_dispatch.py +115 -0
- package/ciel_runtime_support/channel_terminal_input.py +277 -0
- package/ciel_runtime_support/channel_terminal_proxy.py +447 -0
- package/ciel_runtime_support/channel_tool_context.py +166 -0
- package/ciel_runtime_support/channel_transcript.py +414 -0
- package/ciel_runtime_support/channel_transcript_repository.py +96 -0
- package/ciel_runtime_support/channel_wake_claim_repository.py +126 -0
- package/ciel_runtime_support/channel_wake_delivery_repository.py +88 -0
- package/ciel_runtime_support/chat_files.py +138 -0
- package/ciel_runtime_support/chat_http_controller.py +235 -0
- package/ciel_runtime_support/claude_environment.py +375 -0
- package/ciel_runtime_support/claude_router.py +247 -193
- package/ciel_runtime_support/cli_dispatch.py +792 -0
- package/ciel_runtime_support/cli_parser.py +165 -0
- package/ciel_runtime_support/cli_usage.py +100 -0
- package/ciel_runtime_support/codex_app_server.py +20 -5
- package/ciel_runtime_support/codex_channel_sse_launch.py +87 -0
- package/ciel_runtime_support/codex_cli.py +42 -6
- package/ciel_runtime_support/codex_config.py +323 -0
- package/ciel_runtime_support/codex_launch_configuration.py +240 -0
- package/ciel_runtime_support/codex_launch_policy.py +66 -0
- package/ciel_runtime_support/codex_mcp_integration.py +195 -0
- package/ciel_runtime_support/codex_mcp_restore.py +304 -0
- package/ciel_runtime_support/codex_model_catalog.py +133 -0
- package/ciel_runtime_support/codex_process_lifecycle.py +271 -0
- package/ciel_runtime_support/codex_router.py +147 -1
- package/ciel_runtime_support/codex_session_repository.py +115 -0
- package/ciel_runtime_support/codex_session_selection.py +114 -0
- package/ciel_runtime_support/command_asset_installer.py +103 -0
- package/ciel_runtime_support/compatibility_probe.py +295 -0
- package/ciel_runtime_support/compatibility_protocol.py +251 -0
- package/ciel_runtime_support/compatibility_runtime.py +166 -0
- package/ciel_runtime_support/compatibility_test.py +370 -0
- package/ciel_runtime_support/config_migrations.py +307 -0
- package/ciel_runtime_support/config_repository.py +175 -0
- package/ciel_runtime_support/config_value_codec.py +64 -0
- package/ciel_runtime_support/configuration_cli.py +374 -0
- package/ciel_runtime_support/context_compaction.py +280 -0
- package/ciel_runtime_support/context_setup.py +208 -0
- package/ciel_runtime_support/context_summary_policy.py +392 -0
- package/ciel_runtime_support/credential_cli.py +104 -0
- package/ciel_runtime_support/credential_management.py +261 -0
- package/ciel_runtime_support/credentials.py +269 -0
- package/ciel_runtime_support/executable_discovery.py +141 -0
- package/ciel_runtime_support/github_copilot_oauth.py +335 -0
- package/ciel_runtime_support/github_copilot_oauth_runtime.py +213 -0
- package/ciel_runtime_support/header_forwarding.py +73 -0
- package/ciel_runtime_support/headless_config.py +221 -0
- package/ciel_runtime_support/http_response.py +129 -0
- package/ciel_runtime_support/install_diagnostics.py +149 -0
- package/ciel_runtime_support/kimi_identity.py +123 -0
- package/ciel_runtime_support/launch_diagnostics.py +204 -0
- package/ciel_runtime_support/launch_state.py +127 -0
- package/ciel_runtime_support/live_api_key_controller.py +58 -0
- package/ciel_runtime_support/llm_config_http.py +148 -0
- package/ciel_runtime_support/llm_option_config.py +259 -0
- package/ciel_runtime_support/llm_presentation_data.py +447 -0
- package/ciel_runtime_support/llm_presets.py +773 -0
- package/ciel_runtime_support/lm_studio_runtime.py +401 -0
- package/ciel_runtime_support/managed_mcp_config.py +144 -0
- package/ciel_runtime_support/managed_mcp_discovery.py +151 -0
- package/ciel_runtime_support/managed_service_cleanup.py +89 -0
- package/ciel_runtime_support/mcp_config_reader.py +230 -0
- package/ciel_runtime_support/mcp_http_proxy.py +607 -0
- package/ciel_runtime_support/mcp_inventory.py +59 -0
- package/ciel_runtime_support/mcp_notification_wait_policy.py +159 -0
- package/ciel_runtime_support/mcp_probe_codec.py +136 -0
- package/ciel_runtime_support/mcp_probe_transport.py +328 -0
- package/ciel_runtime_support/mcp_proxy_codec.py +345 -0
- package/ciel_runtime_support/mcp_proxy_config.py +107 -0
- package/ciel_runtime_support/mcp_proxy_notifications.py +261 -0
- package/ciel_runtime_support/mcp_proxy_process.py +560 -0
- package/ciel_runtime_support/mcp_split_proxy_http.py +165 -0
- package/ciel_runtime_support/mcp_stdio_probe.py +240 -0
- package/ciel_runtime_support/mcp_transport.py +146 -0
- package/ciel_runtime_support/model_cache_lifecycle.py +78 -0
- package/ciel_runtime_support/model_catalog_projection.py +61 -0
- package/ciel_runtime_support/model_context_hints.py +109 -0
- package/ciel_runtime_support/model_panel.py +147 -0
- package/ciel_runtime_support/model_registry_repository.py +231 -0
- package/ciel_runtime_support/npm_runtime.py +191 -0
- package/ciel_runtime_support/ollama_catalog.py +462 -0
- package/ciel_runtime_support/ollama_catalog_cli.py +41 -0
- package/ciel_runtime_support/ollama_catalog_repository.py +75 -0
- package/ciel_runtime_support/ollama_context_sync.py +87 -0
- package/ciel_runtime_support/ollama_forwarding.py +449 -0
- package/ciel_runtime_support/openai_chat_passthrough.py +67 -0
- package/ciel_runtime_support/openai_chat_router.py +64 -0
- package/ciel_runtime_support/openai_forwarding.py +194 -0
- package/ciel_runtime_support/openai_responses_router.py +291 -0
- package/ciel_runtime_support/openai_responses_stream.py +135 -0
- package/ciel_runtime_support/output_budget.py +89 -0
- package/ciel_runtime_support/package_lifecycle.py +217 -0
- package/ciel_runtime_support/plan_artifact_controller.py +104 -0
- package/ciel_runtime_support/prelaunch.py +959 -0
- package/ciel_runtime_support/prelaunch_launch_preference.py +75 -0
- package/ciel_runtime_support/prelaunch_panel_projection.py +396 -0
- package/ciel_runtime_support/prelaunch_terminal.py +764 -0
- package/ciel_runtime_support/process_control.py +708 -0
- package/ciel_runtime_support/prompt_compaction.py +322 -0
- package/ciel_runtime_support/prompt_injection.py +176 -0
- package/ciel_runtime_support/protocols/__init__.py +24 -0
- package/ciel_runtime_support/protocols/anthropic_content.py +29 -0
- package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +336 -0
- package/ciel_runtime_support/protocols/chat_projection.py +315 -0
- package/ciel_runtime_support/protocols/conversation_policy.py +217 -0
- package/ciel_runtime_support/protocols/conversation_turn_policy.py +972 -0
- package/ciel_runtime_support/protocols/ollama_chat.py +111 -0
- package/ciel_runtime_support/protocols/ollama_response.py +231 -0
- package/ciel_runtime_support/protocols/openai_reasoning.py +75 -0
- package/ciel_runtime_support/protocols/openai_responses.py +271 -0
- package/ciel_runtime_support/protocols/pseudo_tool_history.py +248 -0
- package/ciel_runtime_support/protocols/tool_result_projection.py +112 -0
- package/ciel_runtime_support/provider_adapters.py +160 -0
- package/ciel_runtime_support/provider_catalog_sources.py +316 -0
- package/ciel_runtime_support/provider_choice.py +201 -0
- package/ciel_runtime_support/provider_compatibility.py +165 -0
- package/ciel_runtime_support/provider_config_mutations.py +361 -0
- package/ciel_runtime_support/provider_configuration_service.py +162 -0
- package/ciel_runtime_support/provider_context.py +308 -0
- package/ciel_runtime_support/provider_contract_projection.py +73 -0
- package/ciel_runtime_support/provider_descriptor.py +82 -0
- package/ciel_runtime_support/provider_endpoint_policy.py +130 -0
- package/ciel_runtime_support/provider_endpoint_probe.py +165 -0
- package/ciel_runtime_support/provider_launch_endpoint.py +109 -0
- package/ciel_runtime_support/provider_limits.py +457 -0
- package/ciel_runtime_support/provider_model_identity.py +139 -0
- package/ciel_runtime_support/provider_model_selection.py +431 -0
- package/ciel_runtime_support/provider_model_specs.py +142 -0
- package/ciel_runtime_support/provider_models.py +263 -0
- package/ciel_runtime_support/provider_network.py +176 -0
- package/ciel_runtime_support/provider_option_cli.py +238 -0
- package/ciel_runtime_support/provider_option_panel.py +275 -0
- package/ciel_runtime_support/provider_option_status.py +192 -0
- package/ciel_runtime_support/provider_policy.py +101 -0
- package/ciel_runtime_support/provider_query_policy.py +67 -0
- package/ciel_runtime_support/provider_readiness.py +112 -0
- package/ciel_runtime_support/provider_request_access.py +131 -0
- package/ciel_runtime_support/provider_request_builder.py +250 -0
- package/ciel_runtime_support/provider_responses_passthrough.py +81 -0
- package/ciel_runtime_support/provider_runtime_info.py +113 -0
- package/ciel_runtime_support/provider_runtime_modes.py +150 -0
- package/ciel_runtime_support/provider_sampling_policy.py +46 -0
- package/ciel_runtime_support/provider_status.py +145 -0
- package/ciel_runtime_support/provider_timeout_policy.py +184 -0
- package/ciel_runtime_support/provider_tool_policy.py +145 -0
- package/ciel_runtime_support/providers/__init__.py +65 -0
- package/ciel_runtime_support/providers/anthropic.py +160 -0
- package/ciel_runtime_support/providers/anthropic_catalog.py +119 -0
- package/ciel_runtime_support/providers/base.py +232 -0
- package/ciel_runtime_support/providers/catalog.py +326 -0
- package/ciel_runtime_support/providers/cloud.py +194 -0
- package/ciel_runtime_support/providers/constants.py +54 -0
- package/ciel_runtime_support/providers/deepseek.py +115 -0
- package/ciel_runtime_support/providers/fireworks.py +158 -0
- package/ciel_runtime_support/providers/github_copilot_oauth.py +199 -0
- package/ciel_runtime_support/providers/kimi.py +297 -0
- package/ciel_runtime_support/providers/lm_studio.py +76 -0
- package/ciel_runtime_support/providers/meta.py +257 -0
- package/ciel_runtime_support/providers/native.py +194 -0
- package/ciel_runtime_support/providers/nim.py +69 -0
- package/ciel_runtime_support/providers/nvidia.py +158 -0
- package/ciel_runtime_support/providers/nvidia_runtime.py +285 -0
- package/ciel_runtime_support/providers/ollama.py +181 -0
- package/ciel_runtime_support/providers/ollama_context.py +195 -0
- package/ciel_runtime_support/providers/ollama_runtime.py +213 -0
- package/ciel_runtime_support/providers/opencode.py +220 -0
- package/ciel_runtime_support/providers/opencode_go.py +37 -0
- package/ciel_runtime_support/providers/openrouter.py +57 -0
- package/ciel_runtime_support/providers/vllm.py +65 -0
- package/ciel_runtime_support/providers/zai.py +119 -0
- package/ciel_runtime_support/pseudo_tool_parser.py +115 -0
- package/ciel_runtime_support/rate_limit_policy.py +117 -0
- package/ciel_runtime_support/rate_limit_repository.py +154 -0
- package/ciel_runtime_support/registry.py +46 -0
- package/ciel_runtime_support/request_shortcuts.py +253 -0
- package/ciel_runtime_support/request_trace.py +323 -0
- package/ciel_runtime_support/response_collection.py +209 -0
- package/ciel_runtime_support/router_access.py +238 -0
- package/ciel_runtime_support/router_client_lifecycle.py +366 -0
- package/ciel_runtime_support/router_health_policy.py +101 -0
- package/ciel_runtime_support/router_http.py +513 -0
- package/ciel_runtime_support/router_process_lifecycle.py +401 -0
- package/ciel_runtime_support/router_rate_limit_service.py +285 -0
- package/ciel_runtime_support/router_server_runtime.py +103 -0
- package/ciel_runtime_support/router_shortcuts.py +201 -0
- package/ciel_runtime_support/routing_fallback.py +73 -0
- package/ciel_runtime_support/runtime_activity_repository.py +143 -0
- package/ciel_runtime_support/runtime_adapters.py +104 -0
- package/ciel_runtime_support/runtime_command_factory.py +73 -0
- package/ciel_runtime_support/runtime_compatibility.py +50 -0
- package/ciel_runtime_support/runtime_constants.py +178 -0
- package/ciel_runtime_support/runtime_launch.py +1602 -0
- package/ciel_runtime_support/runtime_llm_options.py +312 -0
- package/ciel_runtime_support/runtime_logging.py +161 -0
- package/ciel_runtime_support/runtime_paths.py +157 -0
- package/ciel_runtime_support/runtime_restart.py +84 -0
- package/ciel_runtime_support/runtime_upgrade.py +149 -0
- package/ciel_runtime_support/secure_json_repository.py +55 -0
- package/ciel_runtime_support/session_import.py +356 -0
- package/ciel_runtime_support/settings_repository.py +8 -0
- package/ciel_runtime_support/slash_command_assets.py +211 -0
- package/ciel_runtime_support/sse_stream.py +57 -0
- package/ciel_runtime_support/sse_trace.py +225 -0
- package/ciel_runtime_support/statusline_script.py +593 -0
- package/ciel_runtime_support/statusline_settings.py +53 -0
- package/ciel_runtime_support/stream_chunk_policy.py +18 -0
- package/ciel_runtime_support/streaming_anthropic.py +1955 -0
- package/ciel_runtime_support/synthetic_tool_policy.py +105 -0
- package/ciel_runtime_support/terminal_platform_io.py +127 -0
- package/ciel_runtime_support/timeout_profile.py +196 -0
- package/ciel_runtime_support/tool_dialects.py +85 -0
- package/ciel_runtime_support/tool_exposure_policy.py +63 -0
- package/ciel_runtime_support/tool_guard_hooks.py +218 -0
- package/ciel_runtime_support/tool_request_projection.py +96 -0
- package/ciel_runtime_support/tool_schema.py +483 -0
- package/ciel_runtime_support/tool_side_effect_dedupe.py +95 -0
- package/ciel_runtime_support/ui_text.py +266 -0
- package/ciel_runtime_support/upstream_error_policy.py +104 -0
- package/ciel_runtime_support/upstream_retry.py +419 -0
- package/ciel_runtime_support/upstream_stream_io.py +106 -0
- package/ciel_runtime_support/usage_events.py +96 -0
- package/ciel_runtime_support/visible_stream_filters.py +130 -0
- package/ciel_runtime_support/web_endpoints.py +447 -0
- package/ciel_runtime_support/web_ui.py +915 -0
- package/ciel_runtime_support/web_ui_controller.py +189 -0
- package/ciel_runtime_support/windows_console_input.py +137 -0
- package/ciel_runtime_support/windows_console_mode.py +112 -0
- package/docs/Architecture.md +54 -0
- package/docs/Configuration.md +17 -0
- package/docs/Module-Map.md +1093 -26
- package/docs/Providers.md +46 -1
- package/docs/adr/0001-runtime-bounded-contexts.md +295 -0
- package/npm-bin/run-ciel-runtime.js +22 -2
- package/package.json +9 -2
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Runtime diagnostics and cache repository for compatibility probes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class CompatibilityRuntimePorts:
|
|
12
|
+
provider_policy: Callable[[str], Any]
|
|
13
|
+
runtime_info: Callable[..., dict[str, Any] | None]
|
|
14
|
+
positive_int: Callable[[Any], int | None]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class CompatibilityCachePorts:
|
|
19
|
+
save_config: Callable[[dict[str, Any]], None]
|
|
20
|
+
timestamp: Callable[[], int]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CompatibilityRuntimeProjection:
|
|
24
|
+
def __init__(self, ports: CompatibilityRuntimePorts) -> None:
|
|
25
|
+
self.ports = ports
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def vllm_tool_parser_hint(model: str) -> str | None:
|
|
29
|
+
normalized = model.lower()
|
|
30
|
+
if "qwen3-coder" in normalized or "qwen3_coder" in normalized:
|
|
31
|
+
return "vLLM hint: Qwen3-Coder models should be served with --enable-auto-tool-choice --tool-call-parser qwen3_xml."
|
|
32
|
+
if any(marker in normalized for marker in ("qwen2.5", "qwen2_5", "qwq")):
|
|
33
|
+
return "vLLM hint: Qwen2.5/QwQ tool templates usually use --enable-auto-tool-choice --tool-call-parser hermes."
|
|
34
|
+
if "glm-4.7" in normalized or "glm4.7" in normalized:
|
|
35
|
+
return "vLLM hint: GLM-4.7 models should be served with --enable-auto-tool-choice --tool-call-parser glm47."
|
|
36
|
+
if any(
|
|
37
|
+
marker in normalized
|
|
38
|
+
for marker in ("glm-4.5", "glm4.5", "glm-4.6", "glm4.6")
|
|
39
|
+
):
|
|
40
|
+
return "vLLM hint: GLM-4.5/4.6 models should be served with --enable-auto-tool-choice --tool-call-parser glm45."
|
|
41
|
+
if "deepseek-v3.1" in normalized:
|
|
42
|
+
return "vLLM hint: DeepSeek-V3.1 models should be served with --enable-auto-tool-choice --tool-call-parser deepseek_v31."
|
|
43
|
+
if "deepseek-v3" in normalized or "deepseek-r1" in normalized:
|
|
44
|
+
return "vLLM hint: DeepSeek-V3/R1 models require the matching DeepSeek tool parser and chat template from vLLM examples."
|
|
45
|
+
if "llama-3" in normalized or "llama3" in normalized:
|
|
46
|
+
return "vLLM hint: Llama 3.x models usually need --enable-auto-tool-choice --tool-call-parser llama3_json and the matching tool chat template."
|
|
47
|
+
if "hermes" in normalized:
|
|
48
|
+
return "vLLM hint: Hermes models should be served with --enable-auto-tool-choice --tool-call-parser hermes."
|
|
49
|
+
if "qwen3" in normalized or "qwen-3" in normalized:
|
|
50
|
+
return (
|
|
51
|
+
"vLLM hint: this looks like a Qwen3-family model. Verify its model card/tool format; "
|
|
52
|
+
"Qwen3-Coder uses qwen3_xml, while older Hermes-style Qwen templates use hermes."
|
|
53
|
+
)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def lines(
|
|
57
|
+
self,
|
|
58
|
+
provider: str,
|
|
59
|
+
config: dict[str, Any],
|
|
60
|
+
native: bool,
|
|
61
|
+
) -> list[str]:
|
|
62
|
+
policy = self.ports.provider_policy(provider)
|
|
63
|
+
if not policy.exposes_runtime_info:
|
|
64
|
+
return []
|
|
65
|
+
lines: list[str] = []
|
|
66
|
+
info = self.ports.runtime_info(provider, config, timeout=4.0)
|
|
67
|
+
configured_context = self.ports.positive_int(config.get("context_window"))
|
|
68
|
+
configured_output = self.ports.positive_int(config.get("max_output_tokens"))
|
|
69
|
+
if info:
|
|
70
|
+
lines.append(f"Runtime models URL: {info.get('models_url')}")
|
|
71
|
+
if info.get("runtime_model"):
|
|
72
|
+
lines.append(f"Runtime model id: {info.get('runtime_model')}")
|
|
73
|
+
runtime_limit = self.ports.positive_int(info.get("max_model_len"))
|
|
74
|
+
if runtime_limit:
|
|
75
|
+
lines.append(f"Runtime max_model_len: {runtime_limit}")
|
|
76
|
+
else:
|
|
77
|
+
lines.append("Runtime max_model_len: not reported by /v1/models")
|
|
78
|
+
lines.extend(policy.runtime_metadata(info))
|
|
79
|
+
else:
|
|
80
|
+
runtime_limit = None
|
|
81
|
+
lines.append(
|
|
82
|
+
"Runtime max_model_len: unavailable (/v1/models did not return model metadata)"
|
|
83
|
+
)
|
|
84
|
+
if configured_context:
|
|
85
|
+
lines.append(f"Configured context_window: {configured_context}")
|
|
86
|
+
if configured_output:
|
|
87
|
+
lines.append(f"Configured max_output_tokens: {configured_output}")
|
|
88
|
+
if runtime_limit and configured_context and configured_context != runtime_limit:
|
|
89
|
+
lines.append(
|
|
90
|
+
f"Context warning: configured context_window {configured_context} "
|
|
91
|
+
f"differs from runtime max_model_len {runtime_limit}."
|
|
92
|
+
)
|
|
93
|
+
if runtime_limit and configured_output and configured_output >= runtime_limit:
|
|
94
|
+
lines.append(
|
|
95
|
+
"Context warning: max_output_tokens is greater than or equal to the full runtime context length."
|
|
96
|
+
)
|
|
97
|
+
if native:
|
|
98
|
+
lines.append(
|
|
99
|
+
"Runtime mode note: native mode sends Claude Code requests directly; "
|
|
100
|
+
"ciel-runtime cannot shrink max_tokens per request."
|
|
101
|
+
)
|
|
102
|
+
else:
|
|
103
|
+
lines.append(
|
|
104
|
+
"Runtime mode note: router mode can cap max_tokens based on configured context_window."
|
|
105
|
+
)
|
|
106
|
+
return lines
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CompatibilityCacheRepository:
|
|
110
|
+
def __init__(self, ports: CompatibilityCachePorts) -> None:
|
|
111
|
+
self.ports = ports
|
|
112
|
+
|
|
113
|
+
def record(
|
|
114
|
+
self,
|
|
115
|
+
config: dict[str, Any],
|
|
116
|
+
provider: str,
|
|
117
|
+
model: str,
|
|
118
|
+
ok: bool,
|
|
119
|
+
code: int | None = None,
|
|
120
|
+
message: str = "",
|
|
121
|
+
diagnosis: str = "",
|
|
122
|
+
) -> None:
|
|
123
|
+
cache = config.setdefault("compatibility_cache", {})
|
|
124
|
+
if not isinstance(cache, dict):
|
|
125
|
+
cache = {}
|
|
126
|
+
config["compatibility_cache"] = cache
|
|
127
|
+
provider_cache = cache.setdefault(provider, {})
|
|
128
|
+
if not isinstance(provider_cache, dict):
|
|
129
|
+
provider_cache = {}
|
|
130
|
+
cache[provider] = provider_cache
|
|
131
|
+
provider_cache[model] = {
|
|
132
|
+
"ok": ok,
|
|
133
|
+
"code": code,
|
|
134
|
+
"message": message[:500],
|
|
135
|
+
"diagnosis": diagnosis[:500],
|
|
136
|
+
"tested_at": self.ports.timestamp(),
|
|
137
|
+
}
|
|
138
|
+
self.ports.save_config(config)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True, slots=True)
|
|
142
|
+
class ClaudeCliCapabilityProbe:
|
|
143
|
+
cache: dict[str, bool]
|
|
144
|
+
run: Callable[..., Any]
|
|
145
|
+
|
|
146
|
+
def supports_permission_mode(self, executable: str) -> bool:
|
|
147
|
+
cache_key = str(executable or "")
|
|
148
|
+
if cache_key in self.cache:
|
|
149
|
+
return self.cache[cache_key]
|
|
150
|
+
try:
|
|
151
|
+
process = self.run(
|
|
152
|
+
[executable, "--help"],
|
|
153
|
+
capture_output=True,
|
|
154
|
+
text=True,
|
|
155
|
+
timeout=5,
|
|
156
|
+
check=False,
|
|
157
|
+
)
|
|
158
|
+
help_text = process.stdout or ""
|
|
159
|
+
supported = (
|
|
160
|
+
"--permission-mode" in help_text
|
|
161
|
+
and "bypassPermissions" in help_text
|
|
162
|
+
)
|
|
163
|
+
except Exception:
|
|
164
|
+
supported = False
|
|
165
|
+
self.cache[cache_key] = supported
|
|
166
|
+
return supported
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"""Provider compatibility-test application service."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Any, Callable
|
|
9
|
+
import urllib.error
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class CompatibilityTestConstants:
|
|
14
|
+
api_key_probe_error: type[Exception]
|
|
15
|
+
compatibility_test_header: str
|
|
16
|
+
lm_studio_min_context: int
|
|
17
|
+
opencode_provider_names: tuple[str, ...]
|
|
18
|
+
router_base: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class CompatibilityTestConfig:
|
|
23
|
+
current_alias: Callable[..., Any]
|
|
24
|
+
current_upstream_model_id: Callable[..., Any]
|
|
25
|
+
ensure_current_model: Callable[..., Any]
|
|
26
|
+
get_current_provider: Callable[..., Any]
|
|
27
|
+
launch_model_id: Callable[..., Any]
|
|
28
|
+
load_config: Callable[..., Any]
|
|
29
|
+
normalize_model_id: Callable[..., Any]
|
|
30
|
+
positive_int: Callable[..., Any]
|
|
31
|
+
save_config: Callable[..., Any]
|
|
32
|
+
upstream_model_runtime_info: Callable[..., Any]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class CompatibilityTestMode:
|
|
37
|
+
ensure_lm_studio_model_loaded: Callable[..., Any]
|
|
38
|
+
lm_studio_native_enabled: Callable[..., Any]
|
|
39
|
+
native_anthropic_base_url: Callable[..., Any]
|
|
40
|
+
nim_native_enabled: Callable[..., Any]
|
|
41
|
+
nvidia_native_enabled: Callable[..., Any]
|
|
42
|
+
ollama_native_enabled: Callable[..., Any]
|
|
43
|
+
provider_native_enabled: Callable[..., Any]
|
|
44
|
+
upstream_api_model_id: Callable[..., Any]
|
|
45
|
+
vllm_native_enabled: Callable[..., Any]
|
|
46
|
+
vllm_tool_parser_hint: Callable[..., Any]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class CompatibilityTestRequest:
|
|
51
|
+
compatibility_endpoint_probe_lines: Callable[..., Any]
|
|
52
|
+
compatibility_failure_diagnosis: Callable[..., Any]
|
|
53
|
+
compatibility_http_error_message: Callable[..., Any]
|
|
54
|
+
post_json: Callable[..., Any]
|
|
55
|
+
provider_headers: Callable[..., Any]
|
|
56
|
+
provider_ip_family_probe_lines: Callable[..., Any]
|
|
57
|
+
run_api_key_probes: Callable[..., Any]
|
|
58
|
+
start_router: Callable[..., Any]
|
|
59
|
+
stop_router: Callable[..., Any]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True)
|
|
63
|
+
class CompatibilityTestProtocol:
|
|
64
|
+
compatibility_text_request: Callable[..., Any]
|
|
65
|
+
compatibility_tool_request: Callable[..., Any]
|
|
66
|
+
compatibility_tool_result_request: Callable[..., Any]
|
|
67
|
+
find_compat_tool_use: Callable[..., Any]
|
|
68
|
+
known_tool_use_blocker: Callable[..., Any]
|
|
69
|
+
normalize_thinking: Callable[..., Any]
|
|
70
|
+
normalize_tool_choice: Callable[..., Any]
|
|
71
|
+
ollama_chat_request: Callable[..., Any]
|
|
72
|
+
resolve_requested_model: Callable[..., Any]
|
|
73
|
+
response_text_preview: Callable[..., Any]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(frozen=True, slots=True)
|
|
77
|
+
class CompatibilityTestOutput:
|
|
78
|
+
compatibility_runtime_lines: Callable[..., Any]
|
|
79
|
+
join_url: Callable[..., Any]
|
|
80
|
+
set_compatibility_cache: Callable[..., Any]
|
|
81
|
+
summarize_compat_response: Callable[..., Any]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True, slots=True)
|
|
85
|
+
class CompatibilityTestServices:
|
|
86
|
+
constants: CompatibilityTestConstants
|
|
87
|
+
config: CompatibilityTestConfig
|
|
88
|
+
mode: CompatibilityTestMode
|
|
89
|
+
request: CompatibilityTestRequest
|
|
90
|
+
protocol: CompatibilityTestProtocol
|
|
91
|
+
output: CompatibilityTestOutput
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def run_compatibility_test(
|
|
95
|
+
args: argparse.Namespace, *, services: CompatibilityTestServices
|
|
96
|
+
) -> None:
|
|
97
|
+
constants = services.constants
|
|
98
|
+
config = services.config
|
|
99
|
+
mode = services.mode
|
|
100
|
+
request = services.request
|
|
101
|
+
protocol = services.protocol
|
|
102
|
+
output = services.output
|
|
103
|
+
COMPATIBILITY_TEST_HEADER = constants.compatibility_test_header
|
|
104
|
+
CompatibilityApiKeyProbeError = constants.api_key_probe_error
|
|
105
|
+
LM_STUDIO_MIN_CLAUDE_CODE_CONTEXT = constants.lm_studio_min_context
|
|
106
|
+
OPENCODE_PROVIDER_NAMES = constants.opencode_provider_names
|
|
107
|
+
ROUTER_BASE = constants.router_base
|
|
108
|
+
current_alias = config.current_alias
|
|
109
|
+
current_upstream_model_id = config.current_upstream_model_id
|
|
110
|
+
ensure_current_model_from_provider_list = config.ensure_current_model
|
|
111
|
+
get_current_provider = config.get_current_provider
|
|
112
|
+
launch_model_id = config.launch_model_id
|
|
113
|
+
load_config = config.load_config
|
|
114
|
+
normalize_model_id = config.normalize_model_id
|
|
115
|
+
positive_int = config.positive_int
|
|
116
|
+
save_config = config.save_config
|
|
117
|
+
upstream_model_runtime_info = config.upstream_model_runtime_info
|
|
118
|
+
ensure_lm_studio_model_loaded_for_context = mode.ensure_lm_studio_model_loaded
|
|
119
|
+
lm_studio_native_compat_enabled = mode.lm_studio_native_enabled
|
|
120
|
+
native_anthropic_base_url = mode.native_anthropic_base_url
|
|
121
|
+
nim_native_compat_enabled = mode.nim_native_enabled
|
|
122
|
+
nvidia_hosted_native_compat_enabled = mode.nvidia_native_enabled
|
|
123
|
+
ollama_native_compat_enabled = mode.ollama_native_enabled
|
|
124
|
+
provider_native_compat_enabled = mode.provider_native_enabled
|
|
125
|
+
upstream_api_model_id = mode.upstream_api_model_id
|
|
126
|
+
vllm_native_compat_enabled = mode.vllm_native_enabled
|
|
127
|
+
vllm_tool_parser_hint = mode.vllm_tool_parser_hint
|
|
128
|
+
compatibility_endpoint_probe_lines = request.compatibility_endpoint_probe_lines
|
|
129
|
+
compatibility_failure_diagnosis = request.compatibility_failure_diagnosis
|
|
130
|
+
compatibility_http_error_message = request.compatibility_http_error_message
|
|
131
|
+
post_json = request.post_json
|
|
132
|
+
provider_headers = request.provider_headers
|
|
133
|
+
provider_ip_family_probe_lines = request.provider_ip_family_probe_lines
|
|
134
|
+
run_compatibility_api_key_probes = request.run_api_key_probes
|
|
135
|
+
start_router_if_needed = request.start_router
|
|
136
|
+
stop_router_processes = request.stop_router
|
|
137
|
+
compatibility_text_request = protocol.compatibility_text_request
|
|
138
|
+
compatibility_tool_request = protocol.compatibility_tool_request
|
|
139
|
+
compatibility_tool_result_request = protocol.compatibility_tool_result_request
|
|
140
|
+
find_compat_tool_use = protocol.find_compat_tool_use
|
|
141
|
+
known_compatibility_tool_use_blocker = protocol.known_tool_use_blocker
|
|
142
|
+
normalize_thinking_for_non_anthropic_provider = protocol.normalize_thinking
|
|
143
|
+
normalize_tool_choice_for_provider = protocol.normalize_tool_choice
|
|
144
|
+
ollama_chat_request = protocol.ollama_chat_request
|
|
145
|
+
resolve_requested_model = protocol.resolve_requested_model
|
|
146
|
+
response_text_preview = protocol.response_text_preview
|
|
147
|
+
compatibility_runtime_lines = output.compatibility_runtime_lines
|
|
148
|
+
join_url = output.join_url
|
|
149
|
+
set_compatibility_cache = output.set_compatibility_cache
|
|
150
|
+
summarize_compat_response = output.summarize_compat_response
|
|
151
|
+
cfg = load_config()
|
|
152
|
+
provider, pcfg = get_current_provider(cfg)
|
|
153
|
+
test_mode = getattr(args, "mode", "auto") or "auto"
|
|
154
|
+
if test_mode not in ("auto", "quick", "smoke", "full"):
|
|
155
|
+
raise SystemExit("test mode must be auto, quick, smoke, or full")
|
|
156
|
+
effective_mode = "quick" if test_mode == "auto" and provider == "nvidia-hosted" else ("full" if test_mode == "auto" else test_mode)
|
|
157
|
+
lm_studio_preflight_lines: list[str] = []
|
|
158
|
+
if provider == "lm-studio":
|
|
159
|
+
try:
|
|
160
|
+
lm_studio_preflight_lines = ensure_lm_studio_model_loaded_for_context(pcfg, timeout=1.5)
|
|
161
|
+
save_config(cfg)
|
|
162
|
+
except Exception as exc:
|
|
163
|
+
print("Compatibility: FAIL")
|
|
164
|
+
print("Reason: CielRuntime could not automatically load the selected LM Studio model with the recommended context.")
|
|
165
|
+
print(f"Diagnosis: LM Studio load failed ({type(exc).__name__}: {exc}).")
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
selected, selection_lines = ensure_current_model_from_provider_list(provider, pcfg)
|
|
168
|
+
for line in selection_lines:
|
|
169
|
+
print(line)
|
|
170
|
+
if selection_lines:
|
|
171
|
+
save_config(cfg)
|
|
172
|
+
if not selected:
|
|
173
|
+
print("Compatibility: FAIL")
|
|
174
|
+
print("Reason: No concrete provider model is selected.")
|
|
175
|
+
print("Diagnosis: choose a model from the provider model list, then retry the compatibility test.")
|
|
176
|
+
set_compatibility_cache(cfg, provider, normalize_model_id(provider, str(pcfg.get("current_model") or "")) or "(unset)", False, None, "No concrete provider model is selected.", "Choose a model from the provider model list.")
|
|
177
|
+
raise SystemExit(1)
|
|
178
|
+
ollama_native = ollama_native_compat_enabled(provider, pcfg)
|
|
179
|
+
provider_native = provider_native_compat_enabled(provider, pcfg)
|
|
180
|
+
native = ollama_native or provider_native
|
|
181
|
+
model = current_upstream_model_id(provider, pcfg) if provider_native else (launch_model_id(provider, pcfg) if ollama_native else current_alias(cfg))
|
|
182
|
+
request_model = upstream_api_model_id(provider, model) if native else model
|
|
183
|
+
base = native_anthropic_base_url(provider, pcfg) if native else ROUTER_BASE
|
|
184
|
+
if not native:
|
|
185
|
+
# Compatibility tests must exercise the currently installed router.
|
|
186
|
+
# Older long-running routers can keep stale NVIDIA proxy code alive
|
|
187
|
+
# across npm upgrades, producing false nvd-claude-proxy failures.
|
|
188
|
+
stop_router_processes(quiet=True)
|
|
189
|
+
start_router_if_needed()
|
|
190
|
+
url = join_url(base, "/v1/messages")
|
|
191
|
+
headers = provider_headers(provider, pcfg)
|
|
192
|
+
headers[COMPATIBILITY_TEST_HEADER] = "1"
|
|
193
|
+
if ollama_native:
|
|
194
|
+
headers = {
|
|
195
|
+
"content-type": "application/json",
|
|
196
|
+
"anthropic-version": "2023-06-01",
|
|
197
|
+
"authorization": "Bearer ollama",
|
|
198
|
+
"x-api-key": "ollama",
|
|
199
|
+
COMPATIBILITY_TEST_HEADER: "1",
|
|
200
|
+
}
|
|
201
|
+
text_body = normalize_tool_choice_for_provider(
|
|
202
|
+
provider,
|
|
203
|
+
pcfg,
|
|
204
|
+
normalize_thinking_for_non_anthropic_provider(provider, pcfg, compatibility_text_request(request_model)),
|
|
205
|
+
)
|
|
206
|
+
tool_body = normalize_tool_choice_for_provider(
|
|
207
|
+
provider,
|
|
208
|
+
pcfg,
|
|
209
|
+
normalize_thinking_for_non_anthropic_provider(provider, pcfg, compatibility_tool_request(request_model)),
|
|
210
|
+
)
|
|
211
|
+
print(f"Testing provider: {provider}")
|
|
212
|
+
print(f"Test mode: {effective_mode}")
|
|
213
|
+
if ollama_native:
|
|
214
|
+
mode = "ollama-native"
|
|
215
|
+
elif vllm_native_compat_enabled(provider, pcfg):
|
|
216
|
+
mode = "vllm-native"
|
|
217
|
+
elif lm_studio_native_compat_enabled(provider, pcfg):
|
|
218
|
+
mode = "lm-studio-native"
|
|
219
|
+
elif nim_native_compat_enabled(provider, pcfg):
|
|
220
|
+
mode = "nim-native"
|
|
221
|
+
elif nvidia_hosted_native_compat_enabled(provider, pcfg):
|
|
222
|
+
mode = "nvidia-native"
|
|
223
|
+
else:
|
|
224
|
+
mode = "ciel-runtime-router"
|
|
225
|
+
print(f"Mode: {mode}")
|
|
226
|
+
print(f"Claude API URL: {url}")
|
|
227
|
+
if not native:
|
|
228
|
+
print(f"Upstream base URL: {pcfg.get('base_url')}")
|
|
229
|
+
for line in provider_ip_family_probe_lines(provider, pcfg):
|
|
230
|
+
print(line)
|
|
231
|
+
if provider in ("ollama", "ollama-cloud"):
|
|
232
|
+
req_preview = ollama_chat_request(resolve_requested_model(provider, pcfg, model), tool_body, pcfg, stream=False, provider=provider)
|
|
233
|
+
print(f"Ollama num_ctx: {req_preview.get('options', {}).get('num_ctx', 'default')}")
|
|
234
|
+
elif provider in OPENCODE_PROVIDER_NAMES:
|
|
235
|
+
for line in provider_ip_family_probe_lines(provider, pcfg):
|
|
236
|
+
print(line)
|
|
237
|
+
print(f"Model: {model}")
|
|
238
|
+
if request_model != model:
|
|
239
|
+
print(f"API model: {request_model}")
|
|
240
|
+
for line in lm_studio_preflight_lines:
|
|
241
|
+
print(line)
|
|
242
|
+
for line in compatibility_runtime_lines(provider, pcfg, native):
|
|
243
|
+
print(line)
|
|
244
|
+
for line in compatibility_endpoint_probe_lines(provider, pcfg, timeout=min(float(args.timeout or 1.5), 3.0)):
|
|
245
|
+
print(line)
|
|
246
|
+
if provider == "lm-studio":
|
|
247
|
+
info = upstream_model_runtime_info(provider, pcfg, timeout=1.5)
|
|
248
|
+
loaded = positive_int(info.get("loaded_context_len")) if info else None
|
|
249
|
+
state = str(info.get("state") or "") if info else ""
|
|
250
|
+
if loaded and loaded < LM_STUDIO_MIN_CLAUDE_CODE_CONTEXT:
|
|
251
|
+
print("Compatibility: FAIL")
|
|
252
|
+
print(
|
|
253
|
+
"Reason: LM Studio loaded context is "
|
|
254
|
+
f"{loaded:,} tokens; Claude Code needs at least {LM_STUDIO_MIN_CLAUDE_CODE_CONTEXT:,}."
|
|
255
|
+
)
|
|
256
|
+
print("Diagnosis: reload the model in LM Studio with a larger context length, then retry.")
|
|
257
|
+
sys.exit(1)
|
|
258
|
+
if state and state != "loaded":
|
|
259
|
+
print("Compatibility: FAIL")
|
|
260
|
+
print("Reason: the selected LM Studio model is not loaded, so the active context length cannot be verified.")
|
|
261
|
+
print(f"Diagnosis: load the model in LM Studio with at least {LM_STUDIO_MIN_CLAUDE_CODE_CONTEXT:,} context tokens, then retry.")
|
|
262
|
+
sys.exit(1)
|
|
263
|
+
if provider == "vllm":
|
|
264
|
+
hint = vllm_tool_parser_hint(model)
|
|
265
|
+
if hint:
|
|
266
|
+
print(hint)
|
|
267
|
+
|
|
268
|
+
def fail(message: str, code: int | None = None, diagnosis: str = "") -> None:
|
|
269
|
+
print("Compatibility: FAIL")
|
|
270
|
+
if code is not None:
|
|
271
|
+
print(f"HTTP: {code}")
|
|
272
|
+
print(f"Reason: {message[:1000]}")
|
|
273
|
+
if diagnosis:
|
|
274
|
+
print(diagnosis)
|
|
275
|
+
set_compatibility_cache(cfg, provider, model, False, code, message, diagnosis)
|
|
276
|
+
raise SystemExit(1)
|
|
277
|
+
|
|
278
|
+
def run_phase(label: str, request_body: dict[str, Any]) -> Any:
|
|
279
|
+
print(f"{label}: running")
|
|
280
|
+
try:
|
|
281
|
+
return post_json(
|
|
282
|
+
url,
|
|
283
|
+
request_body,
|
|
284
|
+
headers=headers,
|
|
285
|
+
timeout=args.timeout,
|
|
286
|
+
provider=provider if native else None,
|
|
287
|
+
pcfg=pcfg if native else None,
|
|
288
|
+
)
|
|
289
|
+
except urllib.error.HTTPError as exc:
|
|
290
|
+
msg = compatibility_http_error_message(exc)
|
|
291
|
+
diagnosis = compatibility_failure_diagnosis(provider, exc.code, msg)
|
|
292
|
+
fail(f"{label}: {msg}", exc.code, diagnosis or "")
|
|
293
|
+
except TimeoutError:
|
|
294
|
+
print("Compatibility: TIMEOUT")
|
|
295
|
+
print(f"Reason: {label} did not respond before the {args.timeout:g}s compatibility-test timeout.")
|
|
296
|
+
print("Diagnosis: this timeout was not saved as a model failure. Retry the test or choose another model if it repeats.")
|
|
297
|
+
sys.stdout.flush()
|
|
298
|
+
sys.exit(1)
|
|
299
|
+
except Exception as exc:
|
|
300
|
+
msg = f"{type(exc).__name__}: {exc}"
|
|
301
|
+
if "timed out" in msg.lower() or "timeout" in msg.lower():
|
|
302
|
+
print("Compatibility: TIMEOUT")
|
|
303
|
+
print(f"Reason: {label}: {msg}")
|
|
304
|
+
print("Diagnosis: this timeout was not saved as a model failure. Retry the test or choose another model if it repeats.")
|
|
305
|
+
sys.stdout.flush()
|
|
306
|
+
sys.exit(1)
|
|
307
|
+
fail(f"{label}: {msg}")
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
for line in run_compatibility_api_key_probes(provider, pcfg, model, text_body, args.timeout):
|
|
311
|
+
print(line)
|
|
312
|
+
except CompatibilityApiKeyProbeError as exc:
|
|
313
|
+
fail(f"API key check: {exc}", exc.code, exc.diagnosis)
|
|
314
|
+
|
|
315
|
+
text_data = run_phase("Text response", text_body)
|
|
316
|
+
for line in summarize_compat_response(text_data, "Text response"):
|
|
317
|
+
print(line)
|
|
318
|
+
|
|
319
|
+
if effective_mode == "quick":
|
|
320
|
+
set_compatibility_cache(cfg, provider, model, True, 200, "text quick OK", "")
|
|
321
|
+
print("Compatibility: OK")
|
|
322
|
+
print("Note: quick mode checked text only; run `ciel-runtime test 120 smoke` for tool_use or `ciel-runtime test 180 full` for tool_result.")
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
tool_blocker = known_compatibility_tool_use_blocker(provider, request_model)
|
|
326
|
+
if tool_blocker:
|
|
327
|
+
fail(
|
|
328
|
+
f"Tool use: {tool_blocker}",
|
|
329
|
+
diagnosis=(
|
|
330
|
+
"Diagnosis: the selected model is not suitable for Claude Code through the Anthropic "
|
|
331
|
+
"compatibility path because Claude Code depends on reliable tool_use responses."
|
|
332
|
+
),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
tool_data = run_phase("Tool use", tool_body)
|
|
336
|
+
tool_use, tool_error = find_compat_tool_use(tool_data)
|
|
337
|
+
if not tool_use:
|
|
338
|
+
diagnosis = (
|
|
339
|
+
"Diagnosis: the model/server did not return a valid Anthropic tool_use block. "
|
|
340
|
+
"Claude Code can fail with 'tool call could not be parsed' on this provider/model."
|
|
341
|
+
)
|
|
342
|
+
if provider == "vllm":
|
|
343
|
+
hint = vllm_tool_parser_hint(model)
|
|
344
|
+
if hint:
|
|
345
|
+
diagnosis = f"{diagnosis} {hint}"
|
|
346
|
+
fail(f"Tool use: {tool_error}", diagnosis=diagnosis)
|
|
347
|
+
for line in summarize_compat_response(tool_data, "Tool use"):
|
|
348
|
+
print(line)
|
|
349
|
+
|
|
350
|
+
if effective_mode == "smoke":
|
|
351
|
+
set_compatibility_cache(cfg, provider, model, True, 200, "text/tool_use smoke OK", "")
|
|
352
|
+
print("Compatibility: OK")
|
|
353
|
+
print("Note: smoke mode checked text and tool_use only; run `ciel-runtime test 180 full` for tool_result round trip.")
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
result_body = compatibility_tool_result_request(request_model, tool_use)
|
|
357
|
+
result_data = run_phase("Tool result", result_body)
|
|
358
|
+
result_preview = response_text_preview(result_data)
|
|
359
|
+
if not result_preview:
|
|
360
|
+
fail(
|
|
361
|
+
"Tool result: no final text response after tool_result.",
|
|
362
|
+
diagnosis="Diagnosis: the provider accepted tool_use but did not complete the tool_result round trip.",
|
|
363
|
+
)
|
|
364
|
+
for line in summarize_compat_response(result_data, "Tool result"):
|
|
365
|
+
print(line)
|
|
366
|
+
print(f"Tool result text: {result_preview[:120]}")
|
|
367
|
+
|
|
368
|
+
set_compatibility_cache(cfg, provider, model, True, 200, "text/tool_use/tool_result OK", "")
|
|
369
|
+
print("Compatibility: OK")
|
|
370
|
+
|