@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,308 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Callable
|
|
5
|
+
|
|
6
|
+
from .architecture import ProviderContextPolicy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class ProviderContextServices:
|
|
11
|
+
positive_int: Callable[[Any], int | None]
|
|
12
|
+
model_context_hint: Callable[[str], int | None]
|
|
13
|
+
anthropic_context_hint: Callable[[str], int | None]
|
|
14
|
+
nvidia_context_default: Callable[[str], int | None]
|
|
15
|
+
upstream_context_limit: Callable[..., int | None]
|
|
16
|
+
ollama_context_limit: Callable[[dict[str, Any]], int | None]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class ContextPresetServices:
|
|
21
|
+
positive_int: Callable[[Any], int | None]
|
|
22
|
+
ollama_options: Callable[[dict[str, Any]], dict[str, Any]]
|
|
23
|
+
ollama_thinking_enabled: Callable[[str, dict[str, Any]], bool]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def resolve_context_capacity(
|
|
27
|
+
provider: str,
|
|
28
|
+
config: dict[str, Any],
|
|
29
|
+
policy: ProviderContextPolicy,
|
|
30
|
+
services: ProviderContextServices,
|
|
31
|
+
) -> int | None:
|
|
32
|
+
strategy = policy.capacity_strategy
|
|
33
|
+
model = str(config.get("current_model") or "")
|
|
34
|
+
positive_int = services.positive_int
|
|
35
|
+
|
|
36
|
+
def hint() -> int | None:
|
|
37
|
+
return services.model_context_hint(model)
|
|
38
|
+
|
|
39
|
+
def configured() -> int | None:
|
|
40
|
+
return positive_int(config.get("context_window"))
|
|
41
|
+
|
|
42
|
+
def maximum() -> int | None:
|
|
43
|
+
return positive_int(config.get("max_model_len"))
|
|
44
|
+
if strategy == "managed":
|
|
45
|
+
return None
|
|
46
|
+
if strategy == "nvidia":
|
|
47
|
+
return services.nvidia_context_default(model)
|
|
48
|
+
if strategy == "remote_first":
|
|
49
|
+
return (
|
|
50
|
+
services.upstream_context_limit(provider, config, timeout=1.0)
|
|
51
|
+
or maximum()
|
|
52
|
+
or hint()
|
|
53
|
+
or configured()
|
|
54
|
+
)
|
|
55
|
+
if strategy == "hint_first":
|
|
56
|
+
return hint() or maximum() or configured()
|
|
57
|
+
if strategy == "configured_first":
|
|
58
|
+
return maximum() or hint() or configured()
|
|
59
|
+
if strategy == "ollama":
|
|
60
|
+
return (
|
|
61
|
+
services.ollama_context_limit(config)
|
|
62
|
+
or hint()
|
|
63
|
+
or positive_int(config.get("num_ctx_max"))
|
|
64
|
+
or positive_int(config.get("num_ctx"))
|
|
65
|
+
)
|
|
66
|
+
if strategy == "anthropic_hint":
|
|
67
|
+
return configured() or maximum() or services.anthropic_context_hint(model)
|
|
68
|
+
return hint() or configured() or maximum()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def cap_context_settings(
|
|
72
|
+
config: dict[str, Any],
|
|
73
|
+
capacity: int | None,
|
|
74
|
+
policy: ProviderContextPolicy,
|
|
75
|
+
*,
|
|
76
|
+
positive_int: Callable[[Any], int | None],
|
|
77
|
+
) -> list[str]:
|
|
78
|
+
if not capacity:
|
|
79
|
+
return []
|
|
80
|
+
if policy.settings_strategy == "ollama":
|
|
81
|
+
maximum = positive_int(config.get("num_ctx_max"))
|
|
82
|
+
messages: list[str] = []
|
|
83
|
+
if maximum and maximum > capacity:
|
|
84
|
+
config["num_ctx_max"] = capacity
|
|
85
|
+
messages.append(f"Context max capped to selected model limit: {capacity:,} tokens.")
|
|
86
|
+
for key in ("num_ctx_min", "num_ctx"):
|
|
87
|
+
value = positive_int(config.get(key))
|
|
88
|
+
if value and value > capacity:
|
|
89
|
+
config[key] = capacity
|
|
90
|
+
return messages
|
|
91
|
+
if policy.settings_strategy == "standard":
|
|
92
|
+
context_window = positive_int(config.get("context_window"))
|
|
93
|
+
if context_window and context_window > capacity:
|
|
94
|
+
config["context_window"] = capacity
|
|
95
|
+
return [f"Context window capped to selected model limit: {capacity:,} tokens."]
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def small_context_output_token_cap(
|
|
100
|
+
context_window: int | None,
|
|
101
|
+
*,
|
|
102
|
+
positive_int: Callable[[Any], int | None],
|
|
103
|
+
) -> int | None:
|
|
104
|
+
context = positive_int(context_window)
|
|
105
|
+
if not context or context > 262144:
|
|
106
|
+
return None
|
|
107
|
+
divisor = 16 if context <= 131072 else 32
|
|
108
|
+
cap = max(1024, min(8192, context // divisor))
|
|
109
|
+
return max(1024, (cap // 1024) * 1024)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def cap_output_tokens(
|
|
113
|
+
configured: int | None,
|
|
114
|
+
policy: ProviderContextPolicy,
|
|
115
|
+
context_window: int | None,
|
|
116
|
+
*,
|
|
117
|
+
positive_int: Callable[[Any], int | None],
|
|
118
|
+
) -> int | None:
|
|
119
|
+
value = positive_int(configured)
|
|
120
|
+
if not value or policy.settings_strategy == "managed":
|
|
121
|
+
return value
|
|
122
|
+
cap = small_context_output_token_cap(
|
|
123
|
+
context_window,
|
|
124
|
+
positive_int=positive_int,
|
|
125
|
+
)
|
|
126
|
+
return min(value, cap) if cap else value
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def cap_output_settings(
|
|
130
|
+
config: dict[str, Any],
|
|
131
|
+
policy: ProviderContextPolicy,
|
|
132
|
+
context_window: int | None,
|
|
133
|
+
*,
|
|
134
|
+
positive_int: Callable[[Any], int | None],
|
|
135
|
+
format_context: Callable[[int | None], str],
|
|
136
|
+
) -> list[str]:
|
|
137
|
+
if policy.settings_strategy == "managed":
|
|
138
|
+
return []
|
|
139
|
+
cap = small_context_output_token_cap(
|
|
140
|
+
context_window,
|
|
141
|
+
positive_int=positive_int,
|
|
142
|
+
)
|
|
143
|
+
if not cap:
|
|
144
|
+
return []
|
|
145
|
+
if policy.settings_strategy == "ollama":
|
|
146
|
+
options = config.setdefault("ollama_options", {})
|
|
147
|
+
current = positive_int(options.get("num_predict")) or positive_int(
|
|
148
|
+
config.get("max_output_tokens")
|
|
149
|
+
)
|
|
150
|
+
if current and current > cap:
|
|
151
|
+
options["num_predict"] = cap
|
|
152
|
+
config["max_output_tokens"] = cap
|
|
153
|
+
return [_output_cap_message(cap, context_window, format_context)]
|
|
154
|
+
elif policy.settings_strategy == "standard":
|
|
155
|
+
current = positive_int(config.get("max_output_tokens"))
|
|
156
|
+
if current and current > cap:
|
|
157
|
+
config["max_output_tokens"] = cap
|
|
158
|
+
return [_output_cap_message(cap, context_window, format_context)]
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _output_cap_message(
|
|
163
|
+
cap: int,
|
|
164
|
+
context_window: int | None,
|
|
165
|
+
format_context: Callable[[int | None], str],
|
|
166
|
+
) -> str:
|
|
167
|
+
return (
|
|
168
|
+
f"Max output capped to {cap:,} tokens for context "
|
|
169
|
+
f"{format_context(context_window)}."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def infer_context_preset(
|
|
174
|
+
config: dict[str, Any],
|
|
175
|
+
policy: ProviderContextPolicy,
|
|
176
|
+
services: ContextPresetServices,
|
|
177
|
+
) -> str | None:
|
|
178
|
+
positive_int = services.positive_int
|
|
179
|
+
if policy.settings_strategy == "managed" and not policy.managed_preset_inference:
|
|
180
|
+
return None
|
|
181
|
+
if policy.settings_strategy == "ollama":
|
|
182
|
+
options = services.ollama_options(config)
|
|
183
|
+
output = positive_int(options.get("num_predict")) or 0
|
|
184
|
+
fixed_context = positive_int(config.get("num_ctx")) or 0
|
|
185
|
+
minimum = positive_int(config.get("num_ctx_min")) or 0
|
|
186
|
+
context = positive_int(config.get("num_ctx_max")) or 0
|
|
187
|
+
if services.ollama_thinking_enabled(str(config.get("current_model") or ""), config):
|
|
188
|
+
return "reasoning"
|
|
189
|
+
else:
|
|
190
|
+
output = positive_int(config.get("max_output_tokens")) or 0
|
|
191
|
+
fixed_context = 0
|
|
192
|
+
minimum = 0
|
|
193
|
+
context = positive_int(config.get("context_window")) or 0
|
|
194
|
+
if bool(config.get("think", False)):
|
|
195
|
+
return "reasoning"
|
|
196
|
+
if context >= 1_000_000:
|
|
197
|
+
return "million-context-1m"
|
|
198
|
+
if context >= 524288:
|
|
199
|
+
return "long-context-512k"
|
|
200
|
+
if context >= 307200:
|
|
201
|
+
return "long-context-300k"
|
|
202
|
+
if context >= 262144:
|
|
203
|
+
return "long-context-256k"
|
|
204
|
+
if context >= 131072 and output >= 8192:
|
|
205
|
+
return "long-context-128k"
|
|
206
|
+
if output >= 8192:
|
|
207
|
+
return "large-output"
|
|
208
|
+
if minimum >= 65536 or context >= 65536:
|
|
209
|
+
return "long-context-65k"
|
|
210
|
+
if fixed_context and fixed_context <= 32768 and output and output <= 2048:
|
|
211
|
+
return "fast"
|
|
212
|
+
if policy.settings_strategy != "ollama" and output and output <= 2048:
|
|
213
|
+
return "fast"
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def classify_model_family(
|
|
218
|
+
config: dict[str, Any],
|
|
219
|
+
policy: ProviderContextPolicy,
|
|
220
|
+
capacity: int | None,
|
|
221
|
+
services: ContextPresetServices,
|
|
222
|
+
) -> str:
|
|
223
|
+
model = str(config.get("current_model") or "").lower()
|
|
224
|
+
if any(marker in model for marker in ("coder", "codegemma", "starcoder", "devstral")):
|
|
225
|
+
return "coding"
|
|
226
|
+
if any(marker in model for marker in ("reason", "thinking", "r1", "qwq")):
|
|
227
|
+
return "reasoning"
|
|
228
|
+
if policy.context_family_before_size_markers:
|
|
229
|
+
return _context_family(capacity)
|
|
230
|
+
if any(marker in model for marker in ("1m", "v4-pro", "million")):
|
|
231
|
+
return "million-context"
|
|
232
|
+
if any(marker in model for marker in ("70b", "120b", "253b", "405b", "480b", "large", "ultra", "pro")):
|
|
233
|
+
return "large"
|
|
234
|
+
if policy.settings_strategy == "standard":
|
|
235
|
+
return _context_family(capacity)
|
|
236
|
+
if policy.settings_strategy == "ollama":
|
|
237
|
+
context = (
|
|
238
|
+
services.positive_int(config.get("num_ctx_max"))
|
|
239
|
+
or services.positive_int(config.get("num_ctx"))
|
|
240
|
+
)
|
|
241
|
+
return _context_family(context)
|
|
242
|
+
return "general"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def recommended_preset(family: str, capacity: int | None) -> str:
|
|
246
|
+
if family in {"reasoning", "coding"}:
|
|
247
|
+
return family
|
|
248
|
+
if family == "million-context":
|
|
249
|
+
return "million-context-1m"
|
|
250
|
+
if family == "long-context":
|
|
251
|
+
if capacity and capacity >= 524288:
|
|
252
|
+
return "long-context-512k"
|
|
253
|
+
if capacity and capacity >= 307200:
|
|
254
|
+
return "long-context-300k"
|
|
255
|
+
if capacity and capacity >= 262144:
|
|
256
|
+
return "long-context-256k"
|
|
257
|
+
if capacity and capacity >= 131072:
|
|
258
|
+
return "long-context-128k"
|
|
259
|
+
return "long-context-65k"
|
|
260
|
+
return "balanced"
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def required_context_for_preset(preset_id: str, policy: ProviderContextPolicy) -> int | None:
|
|
264
|
+
profile = policy.preset_context_profile
|
|
265
|
+
if preset_id == "million-context-1m":
|
|
266
|
+
return 1_000_000
|
|
267
|
+
if preset_id == "humanities-researcher":
|
|
268
|
+
return 524288 if profile == "ollama" else 262144
|
|
269
|
+
if preset_id in {"novelist", "mathematician", "product-architect"}:
|
|
270
|
+
return 262144
|
|
271
|
+
if preset_id == "teacher":
|
|
272
|
+
return 131072
|
|
273
|
+
if preset_id == "reasoning":
|
|
274
|
+
return 262144 if profile == "nvidia" else 131072
|
|
275
|
+
if preset_id == "large-output":
|
|
276
|
+
if profile == "nvidia":
|
|
277
|
+
return 262144
|
|
278
|
+
return 131072 if profile == "ollama" else 65536
|
|
279
|
+
return {
|
|
280
|
+
"long-context-512k": 524288,
|
|
281
|
+
"long-context-300k": 307200,
|
|
282
|
+
"long-context-256k": 262144,
|
|
283
|
+
"long-context-128k": 131072,
|
|
284
|
+
"long-context-65k": 131072 if profile == "nvidia" else 65536,
|
|
285
|
+
}.get(preset_id)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _context_family(capacity: int | None) -> str:
|
|
289
|
+
if capacity and capacity >= 1_000_000:
|
|
290
|
+
return "million-context"
|
|
291
|
+
if capacity and capacity >= 65536:
|
|
292
|
+
return "long-context"
|
|
293
|
+
return "general"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
__all__ = [
|
|
297
|
+
"ContextPresetServices",
|
|
298
|
+
"ProviderContextServices",
|
|
299
|
+
"cap_context_settings",
|
|
300
|
+
"cap_output_settings",
|
|
301
|
+
"cap_output_tokens",
|
|
302
|
+
"infer_context_preset",
|
|
303
|
+
"classify_model_family",
|
|
304
|
+
"recommended_preset",
|
|
305
|
+
"required_context_for_preset",
|
|
306
|
+
"resolve_context_capacity",
|
|
307
|
+
"small_context_output_token_cap",
|
|
308
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Provider-neutral projection of registered adapter contracts."""
|
|
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
|
+
from .architecture import (
|
|
10
|
+
ProviderAdapter,
|
|
11
|
+
ProviderConfig,
|
|
12
|
+
ProviderConfigurationPolicy,
|
|
13
|
+
ProviderContextPolicy,
|
|
14
|
+
ProviderModelCatalogPolicy,
|
|
15
|
+
ProviderRequestPolicy,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True)
|
|
20
|
+
class ProviderContractProjectionApi:
|
|
21
|
+
"""Project provider contracts without provider-name dispatch in the facade."""
|
|
22
|
+
|
|
23
|
+
adapter: Callable[[str, dict[str, Any]], ProviderAdapter]
|
|
24
|
+
contract: Callable[[str, dict[str, Any]], ProviderConfig]
|
|
25
|
+
request_base: Callable[[str, dict[str, Any]], str]
|
|
26
|
+
join_url: Callable[[str, str], str]
|
|
27
|
+
|
|
28
|
+
def _parts(self, provider: str, pcfg: dict[str, Any]) -> tuple[ProviderAdapter, ProviderConfig]:
|
|
29
|
+
return self.adapter(provider, pcfg), self.contract(provider, pcfg)
|
|
30
|
+
|
|
31
|
+
def endpoint(self, provider: str, pcfg: dict[str, Any], operation: str) -> str:
|
|
32
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
33
|
+
return self.join_url(self.request_base(provider, pcfg), adapter.resolve_endpoint(operation, contract))
|
|
34
|
+
|
|
35
|
+
def model_paths(self, provider: str, pcfg: dict[str, Any]) -> tuple[str, ...]:
|
|
36
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
37
|
+
return adapter.model_paths(contract)
|
|
38
|
+
|
|
39
|
+
def request_policy(self, provider: str, pcfg: dict[str, Any]) -> ProviderRequestPolicy:
|
|
40
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
41
|
+
return adapter.request_policy(contract)
|
|
42
|
+
|
|
43
|
+
def model_catalog_policy(self, provider: str, pcfg: dict[str, Any]) -> ProviderModelCatalogPolicy:
|
|
44
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
45
|
+
return adapter.model_catalog_policy(contract)
|
|
46
|
+
|
|
47
|
+
def preserves_anthropic_thinking_contract(self, provider: str, pcfg: dict[str, Any]) -> bool:
|
|
48
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
49
|
+
return adapter.preserves_anthropic_thinking(contract)
|
|
50
|
+
|
|
51
|
+
def context_compaction_available(self, provider: str, pcfg: dict[str, Any]) -> bool:
|
|
52
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
53
|
+
return adapter.context_compaction_available(contract)
|
|
54
|
+
|
|
55
|
+
def context_policy(self, provider: str, pcfg: dict[str, Any]) -> ProviderContextPolicy:
|
|
56
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
57
|
+
return adapter.context_policy(contract)
|
|
58
|
+
|
|
59
|
+
def configuration_policy(self, provider: str, pcfg: dict[str, Any]) -> ProviderConfigurationPolicy:
|
|
60
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
61
|
+
return adapter.configuration_policy(contract)
|
|
62
|
+
|
|
63
|
+
def model_panel_badge(self, provider: str, pcfg: dict[str, Any], model: str) -> str:
|
|
64
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
65
|
+
return adapter.model_panel_badge(contract, model)
|
|
66
|
+
|
|
67
|
+
def advisor_panel_notice(self, provider: str, pcfg: dict[str, Any]) -> tuple[tuple[str, ...], tuple[str, ...]] | None:
|
|
68
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
69
|
+
return adapter.advisor_panel_notice(contract)
|
|
70
|
+
|
|
71
|
+
def advisor_model_badge(self, provider: str, pcfg: dict[str, Any], model: str) -> str:
|
|
72
|
+
adapter, contract = self._parts(provider, pcfg)
|
|
73
|
+
return adapter.advisor_model_badge(contract, model)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Data-driven provider registration descriptors.
|
|
2
|
+
|
|
3
|
+
Descriptors contain discovery metadata and adapter construction only. Runtime
|
|
4
|
+
behavior remains in concrete adapters, so complex providers can use specialized
|
|
5
|
+
implementations without growing a central conditional router.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from ciel_runtime_support.architecture import ProviderAdapter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class ProviderDescriptor:
|
|
19
|
+
"""Stable provider identity and its concrete adapter factory type."""
|
|
20
|
+
|
|
21
|
+
name: str
|
|
22
|
+
label: str
|
|
23
|
+
adapter_type: Callable[..., ProviderAdapter]
|
|
24
|
+
aliases: tuple[str, ...] = ()
|
|
25
|
+
|
|
26
|
+
def __post_init__(self) -> None:
|
|
27
|
+
if not self.normalized_name:
|
|
28
|
+
raise ValueError("provider descriptor name cannot be empty")
|
|
29
|
+
if not self.label.strip():
|
|
30
|
+
raise ValueError(f"provider descriptor label cannot be empty: {self.name}")
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def normalized_name(self) -> str:
|
|
34
|
+
return normalize_provider_name(self.name)
|
|
35
|
+
|
|
36
|
+
def create(self, **kwargs: Any) -> ProviderAdapter:
|
|
37
|
+
base_url = str(kwargs.get("base_url") or "").strip()
|
|
38
|
+
return self.adapter_type(**({"base_url": base_url} if base_url else {}))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_provider_name(name: str) -> str:
|
|
42
|
+
return str(name or "").strip().lower().replace("_", "-")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ProviderDescriptorRegistry:
|
|
46
|
+
"""Read-only discovery index with normalized alias lookup."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, descriptors: tuple[ProviderDescriptor, ...]) -> None:
|
|
49
|
+
self._descriptors: dict[str, ProviderDescriptor] = {}
|
|
50
|
+
self._canonical: dict[str, ProviderDescriptor] = {}
|
|
51
|
+
for descriptor in descriptors:
|
|
52
|
+
name = descriptor.normalized_name
|
|
53
|
+
if name in self._canonical:
|
|
54
|
+
raise ValueError(f"provider descriptor already registered: {name}")
|
|
55
|
+
self._canonical[name] = descriptor
|
|
56
|
+
for candidate in (name, *descriptor.aliases):
|
|
57
|
+
key = normalize_provider_name(candidate)
|
|
58
|
+
if not key:
|
|
59
|
+
raise ValueError(f"provider alias cannot be empty: {name}")
|
|
60
|
+
if key in self._descriptors:
|
|
61
|
+
raise ValueError(f"provider descriptor alias already registered: {key}")
|
|
62
|
+
self._descriptors[key] = descriptor
|
|
63
|
+
|
|
64
|
+
def get(self, name: str) -> ProviderDescriptor:
|
|
65
|
+
try:
|
|
66
|
+
return self._descriptors[normalize_provider_name(name)]
|
|
67
|
+
except KeyError as exc:
|
|
68
|
+
raise KeyError(f"unknown provider descriptor: {name}") from exc
|
|
69
|
+
|
|
70
|
+
def descriptors(self) -> tuple[ProviderDescriptor, ...]:
|
|
71
|
+
return tuple(self._canonical[name] for name in sorted(self._canonical))
|
|
72
|
+
|
|
73
|
+
def names(self) -> tuple[str, ...]:
|
|
74
|
+
return tuple(sorted(self._canonical))
|
|
75
|
+
|
|
76
|
+
def aliases(self) -> dict[str, str]:
|
|
77
|
+
"""Project every registered lookup token to its canonical provider name."""
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
alias: descriptor.normalized_name
|
|
81
|
+
for alias, descriptor in self._descriptors.items()
|
|
82
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Provider protocol endpoint override and presentation policy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Mapping
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from ciel_runtime_support.runtime_constants import OPENCODE_ENDPOINT_ALIASES
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class ProviderEndpointPorts:
|
|
14
|
+
normalize_model_id: Callable[[str, str], str]
|
|
15
|
+
strip_context_suffix: Callable[[str], str]
|
|
16
|
+
alias_for: Callable[[str, str], str]
|
|
17
|
+
select_protocol: Callable[[str, dict[str, Any], str], str]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class ProviderEndpointPresentation:
|
|
22
|
+
aliases: Mapping[str, str]
|
|
23
|
+
labels: Mapping[str, str]
|
|
24
|
+
routed_protocols: frozenset[str]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def build_default_provider_endpoint_policy(
|
|
28
|
+
ports: ProviderEndpointPorts,
|
|
29
|
+
) -> ProviderEndpointPolicy:
|
|
30
|
+
"""Compose endpoint behavior with the runtime's standard presentation."""
|
|
31
|
+
return ProviderEndpointPolicy(
|
|
32
|
+
ports=ports,
|
|
33
|
+
presentation=ProviderEndpointPresentation(
|
|
34
|
+
aliases=OPENCODE_ENDPOINT_ALIASES,
|
|
35
|
+
labels={
|
|
36
|
+
"anthropic-messages": "messages",
|
|
37
|
+
"openai-chat": "chat",
|
|
38
|
+
"openai-responses": "responses",
|
|
39
|
+
"google-generative": "gemini",
|
|
40
|
+
},
|
|
41
|
+
routed_protocols=frozenset(
|
|
42
|
+
{"anthropic-messages", "openai-chat", "openai-responses"}
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class ProviderEndpointPolicy:
|
|
50
|
+
ports: ProviderEndpointPorts
|
|
51
|
+
presentation: ProviderEndpointPresentation
|
|
52
|
+
|
|
53
|
+
def normalize_endpoint_kind(self, value: Any) -> str | None:
|
|
54
|
+
key = str(value or "").strip().lower().replace("_", "-")
|
|
55
|
+
return self.presentation.aliases.get(key)
|
|
56
|
+
|
|
57
|
+
def endpoint_override(
|
|
58
|
+
self,
|
|
59
|
+
provider: str,
|
|
60
|
+
model_id: str,
|
|
61
|
+
provider_config: dict[str, Any] | None = None,
|
|
62
|
+
) -> str | None:
|
|
63
|
+
if not provider_config:
|
|
64
|
+
return None
|
|
65
|
+
overrides = provider_config.get("model_endpoints")
|
|
66
|
+
if not isinstance(overrides, dict):
|
|
67
|
+
return None
|
|
68
|
+
normalized = self.ports.normalize_model_id(provider, model_id)
|
|
69
|
+
candidates = (
|
|
70
|
+
model_id,
|
|
71
|
+
normalized,
|
|
72
|
+
self.ports.strip_context_suffix(model_id),
|
|
73
|
+
self.ports.alias_for(provider, normalized),
|
|
74
|
+
)
|
|
75
|
+
for candidate in candidates:
|
|
76
|
+
endpoint = self.normalize_endpoint_kind(overrides.get(candidate))
|
|
77
|
+
if endpoint:
|
|
78
|
+
return endpoint
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
def endpoint_kind(
|
|
82
|
+
self,
|
|
83
|
+
provider: str,
|
|
84
|
+
model_id: str,
|
|
85
|
+
provider_config: dict[str, Any] | None = None,
|
|
86
|
+
) -> str:
|
|
87
|
+
override = self.endpoint_override(provider, model_id, provider_config)
|
|
88
|
+
if override:
|
|
89
|
+
return override
|
|
90
|
+
config = provider_config or {}
|
|
91
|
+
protocol = self.ports.select_protocol(
|
|
92
|
+
provider,
|
|
93
|
+
config,
|
|
94
|
+
model_id,
|
|
95
|
+
)
|
|
96
|
+
return str(protocol).replace("_", "-")
|
|
97
|
+
|
|
98
|
+
def model_supported(
|
|
99
|
+
self,
|
|
100
|
+
provider: str,
|
|
101
|
+
model_id: str,
|
|
102
|
+
provider_config: dict[str, Any] | None = None,
|
|
103
|
+
) -> bool:
|
|
104
|
+
return (
|
|
105
|
+
self.endpoint_kind(provider, model_id, provider_config)
|
|
106
|
+
in self.presentation.routed_protocols
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def endpoint_display(
|
|
110
|
+
self,
|
|
111
|
+
provider: str,
|
|
112
|
+
model_id: str,
|
|
113
|
+
provider_config: dict[str, Any] | None = None,
|
|
114
|
+
) -> str:
|
|
115
|
+
endpoint = self.endpoint_kind(provider, model_id, provider_config)
|
|
116
|
+
text = self.presentation.labels.get(endpoint, endpoint)
|
|
117
|
+
if endpoint not in self.presentation.routed_protocols:
|
|
118
|
+
text += " unsupported"
|
|
119
|
+
if self.endpoint_override(provider, model_id, provider_config):
|
|
120
|
+
text += " override"
|
|
121
|
+
return text
|
|
122
|
+
|
|
123
|
+
def zen_endpoint_kind(self, model_id: str) -> str:
|
|
124
|
+
return self.endpoint_kind("opencode", model_id)
|
|
125
|
+
|
|
126
|
+
def zen_model_supported(self, model_id: str) -> bool:
|
|
127
|
+
return self.model_supported("opencode", model_id)
|
|
128
|
+
|
|
129
|
+
def go_endpoint_kind(self, model_id: str) -> str:
|
|
130
|
+
return self.endpoint_kind("opencode-go", model_id)
|