@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,208 @@
|
|
|
1
|
+
"""Provider-neutral context setup projection and mutation service."""
|
|
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 ProviderContextPolicy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ContextMode = tuple[int, int, int]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True, slots=True)
|
|
16
|
+
class ContextSetupPorts:
|
|
17
|
+
context_capacity: Callable[[str, dict[str, Any]], int | None]
|
|
18
|
+
context_policy: Callable[[str, dict[str, Any]], ProviderContextPolicy]
|
|
19
|
+
positive_int: Callable[[Any], int | None]
|
|
20
|
+
format_context: Callable[[int | None], str]
|
|
21
|
+
ui_text: Callable[[str, str], str]
|
|
22
|
+
pad_cells: Callable[[str, int], str]
|
|
23
|
+
cap_context: Callable[[str, dict[str, Any]], list[str]]
|
|
24
|
+
cap_output: Callable[[str, dict[str, Any]], list[str]]
|
|
25
|
+
apply_timeout: Callable[[str, dict[str, Any]], list[str]]
|
|
26
|
+
context_status: Callable[[str, dict[str, Any]], str]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_ORDERED_MODES = (
|
|
30
|
+
"context-compact",
|
|
31
|
+
"context-balanced",
|
|
32
|
+
"context-project",
|
|
33
|
+
"context-full",
|
|
34
|
+
)
|
|
35
|
+
_TEXT = {
|
|
36
|
+
"en": {
|
|
37
|
+
"context-compact": ("Compact / fast", "small context, faster and cheaper"),
|
|
38
|
+
"context-balanced": (
|
|
39
|
+
"Balanced",
|
|
40
|
+
"good default for normal coding sessions",
|
|
41
|
+
),
|
|
42
|
+
"context-project": (
|
|
43
|
+
"Large project",
|
|
44
|
+
"more files/history, slower but safer for big work",
|
|
45
|
+
),
|
|
46
|
+
"context-full": (
|
|
47
|
+
"Full model window",
|
|
48
|
+
"use the selected model's maximum context",
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
"ko": {
|
|
52
|
+
"context-compact": ("컴팩트/빠름", "작은 컨텍스트, 빠르고 가벼움"),
|
|
53
|
+
"context-balanced": ("균형형", "일반 코딩 세션의 권장 기본값"),
|
|
54
|
+
"context-project": (
|
|
55
|
+
"대형 프로젝트",
|
|
56
|
+
"파일/히스토리를 더 많이 사용, 큰 작업에 안정적",
|
|
57
|
+
),
|
|
58
|
+
"context-full": (
|
|
59
|
+
"모델 최대 컨텍스트",
|
|
60
|
+
"선택한 모델의 최대 컨텍스트 사용",
|
|
61
|
+
),
|
|
62
|
+
},
|
|
63
|
+
"ja": {
|
|
64
|
+
"context-compact": (
|
|
65
|
+
"コンパクト/高速",
|
|
66
|
+
"小さなコンテキストで高速かつ軽量",
|
|
67
|
+
),
|
|
68
|
+
"context-balanced": (
|
|
69
|
+
"バランス",
|
|
70
|
+
"通常のコーディングセッション向けの既定値",
|
|
71
|
+
),
|
|
72
|
+
"context-project": (
|
|
73
|
+
"大規模プロジェクト",
|
|
74
|
+
"より多くのファイル/履歴を使う大型作業向け",
|
|
75
|
+
),
|
|
76
|
+
"context-full": (
|
|
77
|
+
"モデル最大コンテキスト",
|
|
78
|
+
"選択モデルの最大コンテキストを使用",
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
"zh": {
|
|
82
|
+
"context-compact": ("紧凑/快速", "较小上下文,更快更轻"),
|
|
83
|
+
"context-balanced": ("均衡", "普通编码会话的推荐默认值"),
|
|
84
|
+
"context-project": ("大型项目", "使用更多文件/历史,适合大任务"),
|
|
85
|
+
"context-full": ("模型最大上下文", "使用所选模型的最大上下文"),
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ContextSetupService:
|
|
91
|
+
def __init__(self, ports: ContextSetupPorts) -> None:
|
|
92
|
+
self.ports = ports
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def mode_values(capacity: int | None) -> dict[str, ContextMode]:
|
|
96
|
+
cap = capacity or 131072
|
|
97
|
+
|
|
98
|
+
def clamp(value: int) -> int:
|
|
99
|
+
return max(8192, min(cap, value))
|
|
100
|
+
|
|
101
|
+
compact = clamp(32768)
|
|
102
|
+
balanced = clamp(65536 if cap <= 131072 else 131072)
|
|
103
|
+
project = clamp(262144 if cap >= 262144 else cap)
|
|
104
|
+
full = clamp(cap)
|
|
105
|
+
return {
|
|
106
|
+
"context-compact": (
|
|
107
|
+
compact,
|
|
108
|
+
min(2048, max(1024, compact // 16)),
|
|
109
|
+
4096,
|
|
110
|
+
),
|
|
111
|
+
"context-balanced": (
|
|
112
|
+
balanced,
|
|
113
|
+
min(4096, max(2048, balanced // 16)),
|
|
114
|
+
4096,
|
|
115
|
+
),
|
|
116
|
+
"context-project": (
|
|
117
|
+
project,
|
|
118
|
+
min(8192, max(4096, project // 16)),
|
|
119
|
+
8192,
|
|
120
|
+
),
|
|
121
|
+
"context-full": (
|
|
122
|
+
full,
|
|
123
|
+
min(16384, max(4096, full // 16)),
|
|
124
|
+
8192,
|
|
125
|
+
),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def text(key: str, language: str) -> tuple[str, str]:
|
|
130
|
+
entries = _TEXT.get(language, _TEXT["en"])
|
|
131
|
+
return entries.get(key, _TEXT["en"][key])
|
|
132
|
+
|
|
133
|
+
def panel_rows(
|
|
134
|
+
self,
|
|
135
|
+
provider: str,
|
|
136
|
+
config: dict[str, Any],
|
|
137
|
+
language: str,
|
|
138
|
+
) -> tuple[list[str], list[str]]:
|
|
139
|
+
capacity = self.ports.context_capacity(provider, config)
|
|
140
|
+
rows = [f"Model context capacity: {self.ports.format_context(capacity)}"]
|
|
141
|
+
values = ["__info__"]
|
|
142
|
+
strategy = self.ports.context_policy(provider, config).settings_strategy
|
|
143
|
+
if strategy == "managed":
|
|
144
|
+
rows.append("Claude Code manages Anthropic context automatically.")
|
|
145
|
+
values.append("__info__")
|
|
146
|
+
rows.append(self.ports.ui_text("back", language))
|
|
147
|
+
values.append("back")
|
|
148
|
+
return rows, values
|
|
149
|
+
current_window = self.ports.positive_int(
|
|
150
|
+
config.get("num_ctx_max" if strategy == "ollama" else "context_window")
|
|
151
|
+
)
|
|
152
|
+
choices = self.mode_values(capacity)
|
|
153
|
+
visible_modes: list[str] = []
|
|
154
|
+
seen_windows: set[int] = set()
|
|
155
|
+
for key in reversed(_ORDERED_MODES):
|
|
156
|
+
window = choices[key][0]
|
|
157
|
+
if window not in seen_windows:
|
|
158
|
+
seen_windows.add(window)
|
|
159
|
+
visible_modes.append(key)
|
|
160
|
+
for key in reversed(visible_modes):
|
|
161
|
+
window, _reserve, output = choices[key]
|
|
162
|
+
label, description = self.text(key, language)
|
|
163
|
+
mark = "*" if current_window == window else " "
|
|
164
|
+
rows.append(
|
|
165
|
+
f"{mark} {self.ports.pad_cells(label, 22)} "
|
|
166
|
+
f"{self.ports.format_context(window):>6} "
|
|
167
|
+
f"out {self.ports.format_context(output):>5} {description}"
|
|
168
|
+
)
|
|
169
|
+
values.append(key)
|
|
170
|
+
rows.append(self.ports.ui_text("back", language))
|
|
171
|
+
values.append("back")
|
|
172
|
+
return rows, values
|
|
173
|
+
|
|
174
|
+
def apply(
|
|
175
|
+
self,
|
|
176
|
+
provider: str,
|
|
177
|
+
config: dict[str, Any],
|
|
178
|
+
mode: str,
|
|
179
|
+
language: str,
|
|
180
|
+
) -> list[str]:
|
|
181
|
+
choices = self.mode_values(self.ports.context_capacity(provider, config))
|
|
182
|
+
if mode not in choices:
|
|
183
|
+
raise SystemExit(f"Unknown context mode: {mode}")
|
|
184
|
+
window, reserve, output = choices[mode]
|
|
185
|
+
label = self.text(mode, language)[0]
|
|
186
|
+
strategy = self.ports.context_policy(provider, config).settings_strategy
|
|
187
|
+
if strategy == "ollama":
|
|
188
|
+
config["num_ctx"] = "auto"
|
|
189
|
+
config["num_ctx_max"] = window
|
|
190
|
+
config["num_ctx_min"] = min(
|
|
191
|
+
window,
|
|
192
|
+
32768 if window <= 65536 else 65536,
|
|
193
|
+
)
|
|
194
|
+
config.setdefault("ollama_options", {})["num_predict"] = output
|
|
195
|
+
elif strategy == "standard":
|
|
196
|
+
config["context_window"] = window
|
|
197
|
+
config["context_reserve_tokens"] = reserve
|
|
198
|
+
config["max_output_tokens"] = output
|
|
199
|
+
else:
|
|
200
|
+
return ["Context setup is managed by Claude Code for this provider."]
|
|
201
|
+
messages = self.ports.cap_context(provider, config)
|
|
202
|
+
messages.extend(self.ports.cap_output(provider, config))
|
|
203
|
+
messages.extend(self.ports.apply_timeout(provider, config))
|
|
204
|
+
return [
|
|
205
|
+
f"{self.ports.ui_text('context_setup', language)}: {label}",
|
|
206
|
+
f"Applied context: {self.ports.context_status(provider, config)}",
|
|
207
|
+
*messages,
|
|
208
|
+
]
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Pure message projection and chunking policy for context compaction."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Callable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PROMPT_TOOL_INPUT_FIELD_LIMIT = 1200
|
|
11
|
+
PROMPT_MESSAGE_TEXT_LIMIT = 20000
|
|
12
|
+
CLAUDE_CODE_PERSISTED_OUTPUT_MARKER = "<persisted-output>"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True, slots=True)
|
|
16
|
+
class ContextSummaryPolicy:
|
|
17
|
+
estimate_tokens: Callable[[Any], int]
|
|
18
|
+
positive_int: Callable[[Any], int | None]
|
|
19
|
+
content_to_text: Callable[[Any], str]
|
|
20
|
+
compact_json: Callable[..., str]
|
|
21
|
+
latest_user_text: Callable[[dict[str, Any]], str]
|
|
22
|
+
|
|
23
|
+
def is_compact_request(self, body: dict[str, Any]) -> bool:
|
|
24
|
+
if not isinstance(body, dict):
|
|
25
|
+
return False
|
|
26
|
+
text = self.latest_user_text(body).lower()
|
|
27
|
+
return bool(
|
|
28
|
+
text
|
|
29
|
+
and (
|
|
30
|
+
"<command-name>/compact</command-name>" in text
|
|
31
|
+
or ("<command-message>compact</command-message>" in text and "<command-name>" in text)
|
|
32
|
+
or ("create a detailed summary of the conversation" in text and "compact" in text)
|
|
33
|
+
or ("summarize the conversation so far" in text and "compact" in text)
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def text_only_body(
|
|
38
|
+
self,
|
|
39
|
+
body: dict[str, Any],
|
|
40
|
+
system_prompt: str,
|
|
41
|
+
append_system: Callable[[Any, list[str]], Any],
|
|
42
|
+
log: Callable[[str, str], None],
|
|
43
|
+
) -> dict[str, Any]:
|
|
44
|
+
if not self.is_compact_request(body):
|
|
45
|
+
return body
|
|
46
|
+
result = dict(body)
|
|
47
|
+
removed_tools = bool(result.pop("tools", None))
|
|
48
|
+
removed_choice = bool(result.pop("tool_choice", None))
|
|
49
|
+
result.pop("parallel_tool_calls", None)
|
|
50
|
+
result["system"] = append_system(result.get("system"), [system_prompt])
|
|
51
|
+
if removed_tools or removed_choice:
|
|
52
|
+
log(
|
|
53
|
+
"INFO",
|
|
54
|
+
"compact_request_text_only removed_tools=%s removed_tool_choice=%s"
|
|
55
|
+
% (str(removed_tools).lower(), str(removed_choice).lower()),
|
|
56
|
+
)
|
|
57
|
+
return result
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def truncate(text: str, limit: int) -> str:
|
|
61
|
+
if len(text) <= limit:
|
|
62
|
+
return text
|
|
63
|
+
return text[:limit] + f"\n...[truncated {len(text) - limit} chars]..."
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def is_persisted_output(text: str) -> bool:
|
|
67
|
+
return CLAUDE_CODE_PERSISTED_OUTPUT_MARKER in str(text or "")
|
|
68
|
+
|
|
69
|
+
def compact_tool_value(self, value: Any, limit: int = PROMPT_TOOL_INPUT_FIELD_LIMIT) -> Any:
|
|
70
|
+
if isinstance(value, str):
|
|
71
|
+
return self.truncate(value, limit)
|
|
72
|
+
if isinstance(value, list):
|
|
73
|
+
return [self.compact_tool_value(item, limit) for item in value[:20]]
|
|
74
|
+
if isinstance(value, dict):
|
|
75
|
+
return {
|
|
76
|
+
key: self.truncate(item, limit)
|
|
77
|
+
if key in {"content", "old_string", "new_string", "command"}
|
|
78
|
+
and isinstance(item, str)
|
|
79
|
+
else self.compact_tool_value(item, limit)
|
|
80
|
+
for key, item in value.items()
|
|
81
|
+
}
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
def tool_input(self, tool_input: Any) -> str:
|
|
85
|
+
if not tool_input:
|
|
86
|
+
return "{}"
|
|
87
|
+
return json.dumps(self.compact_tool_value(tool_input), ensure_ascii=False, sort_keys=True)
|
|
88
|
+
|
|
89
|
+
def message_text(self, text: str) -> str:
|
|
90
|
+
return self.truncate(text, PROMPT_MESSAGE_TEXT_LIMIT)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def tool_markers(message: dict[str, Any]) -> list[str]:
|
|
94
|
+
content = message.get("content")
|
|
95
|
+
if not isinstance(content, list):
|
|
96
|
+
return []
|
|
97
|
+
markers: list[str] = []
|
|
98
|
+
for block in content:
|
|
99
|
+
if not isinstance(block, dict):
|
|
100
|
+
continue
|
|
101
|
+
if block.get("type") == "tool_use":
|
|
102
|
+
name = str(block.get("name") or "tool")
|
|
103
|
+
tool_id = str(block.get("id") or "")
|
|
104
|
+
markers.append(f"tool_use:{name}{('/' + tool_id) if tool_id else ''}")
|
|
105
|
+
elif block.get("type") == "tool_result":
|
|
106
|
+
markers.append(f"tool_result:{str(block.get('tool_use_id') or 'tool')}")
|
|
107
|
+
return markers
|
|
108
|
+
|
|
109
|
+
def summary_line(self, index: int, message: dict[str, Any], text_limit: int = 700) -> str:
|
|
110
|
+
role = str(message.get("role") or "unknown")
|
|
111
|
+
text = " ".join(self.content_to_text(message.get("content")).split())
|
|
112
|
+
parts = [f"message {index}", f"role={role}"]
|
|
113
|
+
if markers := self.tool_markers(message):
|
|
114
|
+
parts.append("markers=" + ",".join(markers[:6]))
|
|
115
|
+
if text:
|
|
116
|
+
parts.append("text=" + self.truncate(text, text_limit))
|
|
117
|
+
return "- " + " | ".join(parts)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def chunk_ranges(count: int, chunks: int) -> list[tuple[int, int]]:
|
|
121
|
+
chunks = max(1, min(chunks, count))
|
|
122
|
+
return [
|
|
123
|
+
(start, end)
|
|
124
|
+
for index in range(chunks)
|
|
125
|
+
if (start := (index * count) // chunks) < (end := ((index + 1) * count) // chunks)
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
def guard_chunk_count(
|
|
129
|
+
self,
|
|
130
|
+
omitted_messages: list[dict[str, Any]],
|
|
131
|
+
budget_tokens: int | None = None,
|
|
132
|
+
) -> int:
|
|
133
|
+
if not omitted_messages:
|
|
134
|
+
return 0
|
|
135
|
+
omitted_tokens = sum(self.estimate_tokens(message) for message in omitted_messages)
|
|
136
|
+
target = 32768
|
|
137
|
+
if budget := self.positive_int(budget_tokens):
|
|
138
|
+
target = max(target, min(262144, max(1, budget // 4)))
|
|
139
|
+
return max(1, min(12, (omitted_tokens + target - 1) // target))
|
|
140
|
+
|
|
141
|
+
def guard_summary(
|
|
142
|
+
self,
|
|
143
|
+
omitted_messages: list[dict[str, Any]],
|
|
144
|
+
budget_tokens: int,
|
|
145
|
+
start_index: int = 0,
|
|
146
|
+
) -> str:
|
|
147
|
+
count = len(omitted_messages)
|
|
148
|
+
tokens = sum(self.estimate_tokens(message) for message in omitted_messages)
|
|
149
|
+
if count <= 0:
|
|
150
|
+
return (
|
|
151
|
+
"[ciel-runtime context guard: older conversation history was compacted because "
|
|
152
|
+
f"the provider context budget is {budget_tokens} tokens.]"
|
|
153
|
+
)
|
|
154
|
+
max_tokens = max(1024, min(24576, max(1, budget_tokens) // 10))
|
|
155
|
+
max_chars = max_tokens * 4
|
|
156
|
+
chunks = self.guard_chunk_count(omitted_messages, budget_tokens)
|
|
157
|
+
lines = [
|
|
158
|
+
f"[ciel-runtime context guard: compacted {count} older messages, approx {tokens} tokens, because the provider context budget is {budget_tokens} tokens.]",
|
|
159
|
+
"The recent tail is preserved verbatim. Older history is represented below as deterministic chunk summaries; use file reads or MCP queries if exact old content is needed.",
|
|
160
|
+
]
|
|
161
|
+
for number, (start, end) in enumerate(self.chunk_ranges(count, chunks), start=1):
|
|
162
|
+
chunk = omitted_messages[start:end]
|
|
163
|
+
chunk_tokens = sum(self.estimate_tokens(message) for message in chunk)
|
|
164
|
+
lines.append(
|
|
165
|
+
f"Chunk {number}/{chunks}: messages {start_index + start}-{start_index + end - 1}, approx {chunk_tokens} tokens."
|
|
166
|
+
)
|
|
167
|
+
offsets = list(range(len(chunk))) if len(chunk) <= 4 else [0, 1, len(chunk) - 2, len(chunk) - 1]
|
|
168
|
+
for offset in dict.fromkeys(offsets):
|
|
169
|
+
lines.append(self.summary_line(start_index + start + offset, chunk[offset]))
|
|
170
|
+
if len("\n".join(lines)) > max_chars:
|
|
171
|
+
lines.append(f"...[context guard summary truncated to {max_tokens} tokens]...")
|
|
172
|
+
break
|
|
173
|
+
summary = "\n".join(lines)
|
|
174
|
+
return self.truncate(summary, max_chars) if len(summary) > max_chars else summary
|
|
175
|
+
|
|
176
|
+
def compact_message(self, message: dict[str, Any], index: int) -> str:
|
|
177
|
+
role = str(message.get("role") or "unknown")
|
|
178
|
+
parts = [f"Message {index} role={role}"]
|
|
179
|
+
if name := message.get("name") or message.get("tool_name"):
|
|
180
|
+
parts.append(f"name={name}")
|
|
181
|
+
if message.get("tool_call_id"):
|
|
182
|
+
parts.append(f"tool_call_id={message.get('tool_call_id')}")
|
|
183
|
+
content = self.content_to_text(message.get("content"))
|
|
184
|
+
if message.get("tool_calls"):
|
|
185
|
+
tool_calls = "tool_calls=" + self.compact_json(message.get("tool_calls"), max_chars=6000)
|
|
186
|
+
content = f"{content}\n\n{tool_calls}" if content else tool_calls
|
|
187
|
+
return f"{' '.join(parts)}\n{self.message_text(content)}"
|
|
188
|
+
|
|
189
|
+
def instruction_index(self, messages: list[dict[str, Any]]) -> int | None:
|
|
190
|
+
fallback: int | None = None
|
|
191
|
+
for index, message in enumerate(messages):
|
|
192
|
+
if str(message.get("role") or "") != "user":
|
|
193
|
+
continue
|
|
194
|
+
text = self.content_to_text(message.get("content")).lower()
|
|
195
|
+
if text:
|
|
196
|
+
fallback = index
|
|
197
|
+
if (
|
|
198
|
+
"<command-name>/compact</command-name>" in text
|
|
199
|
+
or ("<command-message>compact</command-message>" in text and "<command-name>" in text)
|
|
200
|
+
or ("create a detailed summary of the conversation" in text and "compact" in text)
|
|
201
|
+
or ("summarize the conversation so far" in text and "compact" in text)
|
|
202
|
+
):
|
|
203
|
+
return index
|
|
204
|
+
return fallback
|
|
205
|
+
|
|
206
|
+
def chunk_target_tokens(self, config: dict[str, Any] | None, budget_tokens: int) -> int:
|
|
207
|
+
configured = self.positive_int((config or {}).get("context_compact_chunk_tokens"))
|
|
208
|
+
return max(8192, configured) if configured else max(8192, min(65536, max(1, budget_tokens) // 4))
|
|
209
|
+
|
|
210
|
+
def summary_output_tokens(self, config: dict[str, Any] | None, budget_tokens: int) -> int:
|
|
211
|
+
configured = self.positive_int((config or {}).get("context_compact_summary_tokens"))
|
|
212
|
+
return max(512, configured) if configured else max(1024, min(8192, max(1, budget_tokens) // 64))
|
|
213
|
+
|
|
214
|
+
def split_messages(
|
|
215
|
+
self,
|
|
216
|
+
messages: list[dict[str, Any]],
|
|
217
|
+
target_tokens: int,
|
|
218
|
+
) -> list[tuple[int, list[dict[str, Any]]]]:
|
|
219
|
+
chunks: list[tuple[int, list[dict[str, Any]]]] = []
|
|
220
|
+
current: list[dict[str, Any]] = []
|
|
221
|
+
current_start = 0
|
|
222
|
+
current_tokens = 0
|
|
223
|
+
for index, message in enumerate(messages):
|
|
224
|
+
tokens = max(1, self.estimate_tokens(message))
|
|
225
|
+
if current and current_tokens + tokens > target_tokens:
|
|
226
|
+
chunks.append((current_start, current))
|
|
227
|
+
current, current_tokens, current_start = [], 0, index
|
|
228
|
+
if not current:
|
|
229
|
+
current_start = index
|
|
230
|
+
current.append(message)
|
|
231
|
+
current_tokens += tokens
|
|
232
|
+
if current:
|
|
233
|
+
chunks.append((current_start, current))
|
|
234
|
+
return chunks
|
|
235
|
+
|
|
236
|
+
def chunk_prompt(
|
|
237
|
+
self,
|
|
238
|
+
chunk: list[dict[str, Any]],
|
|
239
|
+
start_index: int,
|
|
240
|
+
chunk_number: int,
|
|
241
|
+
chunk_total: int,
|
|
242
|
+
) -> str:
|
|
243
|
+
parts = [
|
|
244
|
+
f"Segment {chunk_number}/{chunk_total}. Summarize messages {start_index}-{start_index + len(chunk) - 1}.",
|
|
245
|
+
"Return only the segment summary.",
|
|
246
|
+
]
|
|
247
|
+
parts.extend(
|
|
248
|
+
self.compact_message(message, start_index + offset)
|
|
249
|
+
for offset, message in enumerate(chunk)
|
|
250
|
+
)
|
|
251
|
+
return "\n\n".join(parts)
|
|
252
|
+
|
|
253
|
+
def extract_response_text(self, data: Any, wire: str) -> str:
|
|
254
|
+
if not isinstance(data, dict):
|
|
255
|
+
return ""
|
|
256
|
+
if wire == "ollama":
|
|
257
|
+
message = data.get("message") if isinstance(data.get("message"), dict) else {}
|
|
258
|
+
return str(message.get("content") or data.get("response") or "").strip()
|
|
259
|
+
if wire == "openai":
|
|
260
|
+
choices = data.get("choices")
|
|
261
|
+
if isinstance(choices, list) and choices:
|
|
262
|
+
choice = choices[0] if isinstance(choices[0], dict) else {}
|
|
263
|
+
message = choice.get("message") if isinstance(choice.get("message"), dict) else {}
|
|
264
|
+
return str(message.get("content") or "").strip()
|
|
265
|
+
return ""
|
|
266
|
+
if wire == "anthropic":
|
|
267
|
+
return self.content_to_text(data.get("content")).strip()
|
|
268
|
+
return ""
|
|
269
|
+
|
|
270
|
+
def reduce_prompt(
|
|
271
|
+
self,
|
|
272
|
+
summaries: list[str],
|
|
273
|
+
compact_instruction: str,
|
|
274
|
+
budget_tokens: int,
|
|
275
|
+
source_message_count: int,
|
|
276
|
+
) -> str:
|
|
277
|
+
parts = [
|
|
278
|
+
"[ciel-runtime segmented compact]",
|
|
279
|
+
f"The previous conversation was too large for a single compact request. It was summarized in {len(summaries)} segment(s) from {source_message_count} message(s).",
|
|
280
|
+
"Segment summaries:",
|
|
281
|
+
]
|
|
282
|
+
parts.extend(
|
|
283
|
+
f"## Segment {index}\n{summary.strip()}"
|
|
284
|
+
for index, summary in enumerate(summaries, start=1)
|
|
285
|
+
)
|
|
286
|
+
parts.extend(
|
|
287
|
+
(
|
|
288
|
+
"Claude Code compact instruction:",
|
|
289
|
+
self.message_text(compact_instruction),
|
|
290
|
+
"Using the segment summaries above, return only the final compact summary text requested by Claude Code.",
|
|
291
|
+
)
|
|
292
|
+
)
|
|
293
|
+
text = "\n\n".join(parts)
|
|
294
|
+
max_chars = max(8192, max(1, budget_tokens) * 3)
|
|
295
|
+
return self.truncate(text, max_chars) if len(text) > max_chars else text
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@dataclass(frozen=True, slots=True)
|
|
299
|
+
class ContextSummaryCompatibilityApi:
|
|
300
|
+
"""Typed facade adapter for context-summary projections."""
|
|
301
|
+
|
|
302
|
+
policy_factory: Callable[[], ContextSummaryPolicy]
|
|
303
|
+
compact_system_prompt: str
|
|
304
|
+
append_system: Callable[[Any, list[str]], Any]
|
|
305
|
+
log: Callable[[str, str], None]
|
|
306
|
+
|
|
307
|
+
def is_compact_request(self, body: dict[str, Any]) -> bool:
|
|
308
|
+
return self.policy_factory().is_compact_request(body)
|
|
309
|
+
|
|
310
|
+
def text_only_body(self, body: dict[str, Any]) -> dict[str, Any]:
|
|
311
|
+
return self.policy_factory().text_only_body(
|
|
312
|
+
body, self.compact_system_prompt, self.append_system, self.log
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def compact_tool_value(self, value: Any, limit: int = PROMPT_TOOL_INPUT_FIELD_LIMIT) -> Any:
|
|
316
|
+
return self.policy_factory().compact_tool_value(value, limit)
|
|
317
|
+
|
|
318
|
+
def tool_input(self, tool_input: Any) -> str:
|
|
319
|
+
return self.policy_factory().tool_input(tool_input)
|
|
320
|
+
|
|
321
|
+
def message_text(self, text: str) -> str:
|
|
322
|
+
return self.policy_factory().message_text(text)
|
|
323
|
+
|
|
324
|
+
def summary_line(
|
|
325
|
+
self, index: int, message: dict[str, Any], *, text_limit: int = 700
|
|
326
|
+
) -> str:
|
|
327
|
+
return self.policy_factory().summary_line(index, message, text_limit)
|
|
328
|
+
|
|
329
|
+
def guard_chunk_count(
|
|
330
|
+
self,
|
|
331
|
+
omitted_messages: list[dict[str, Any]],
|
|
332
|
+
budget_tokens: int | None = None,
|
|
333
|
+
) -> int:
|
|
334
|
+
return self.policy_factory().guard_chunk_count(omitted_messages, budget_tokens)
|
|
335
|
+
|
|
336
|
+
def guard_summary(
|
|
337
|
+
self,
|
|
338
|
+
omitted_messages: list[dict[str, Any]],
|
|
339
|
+
budget_tokens: int,
|
|
340
|
+
*,
|
|
341
|
+
start_index: int = 0,
|
|
342
|
+
) -> str:
|
|
343
|
+
return self.policy_factory().guard_summary(
|
|
344
|
+
omitted_messages, budget_tokens, start_index
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def compact_message(self, message: dict[str, Any], index: int) -> str:
|
|
348
|
+
return self.policy_factory().compact_message(message, index)
|
|
349
|
+
|
|
350
|
+
def instruction_index(self, messages: list[dict[str, Any]]) -> int | None:
|
|
351
|
+
return self.policy_factory().instruction_index(messages)
|
|
352
|
+
|
|
353
|
+
def chunk_target_tokens(
|
|
354
|
+
self, config: dict[str, Any] | None, budget_tokens: int
|
|
355
|
+
) -> int:
|
|
356
|
+
return self.policy_factory().chunk_target_tokens(config, budget_tokens)
|
|
357
|
+
|
|
358
|
+
def summary_output_tokens(
|
|
359
|
+
self, config: dict[str, Any] | None, budget_tokens: int
|
|
360
|
+
) -> int:
|
|
361
|
+
return self.policy_factory().summary_output_tokens(config, budget_tokens)
|
|
362
|
+
|
|
363
|
+
def split_messages(
|
|
364
|
+
self, messages: list[dict[str, Any]], target_tokens: int
|
|
365
|
+
) -> list[tuple[int, list[dict[str, Any]]]]:
|
|
366
|
+
return self.policy_factory().split_messages(messages, target_tokens)
|
|
367
|
+
|
|
368
|
+
def chunk_prompt(
|
|
369
|
+
self,
|
|
370
|
+
chunk: list[dict[str, Any]],
|
|
371
|
+
start_index: int,
|
|
372
|
+
chunk_no: int,
|
|
373
|
+
chunk_total: int,
|
|
374
|
+
) -> str:
|
|
375
|
+
return self.policy_factory().chunk_prompt(
|
|
376
|
+
chunk, start_index, chunk_no, chunk_total
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
def extract_response_text(self, data: Any, wire: str) -> str:
|
|
380
|
+
return self.policy_factory().extract_response_text(data, wire)
|
|
381
|
+
|
|
382
|
+
def reduce_prompt(
|
|
383
|
+
self,
|
|
384
|
+
summaries: list[str],
|
|
385
|
+
compact_instruction: str,
|
|
386
|
+
*,
|
|
387
|
+
budget_tokens: int,
|
|
388
|
+
source_message_count: int,
|
|
389
|
+
) -> str:
|
|
390
|
+
return self.policy_factory().reduce_prompt(
|
|
391
|
+
summaries, compact_instruction, budget_tokens, source_message_count
|
|
392
|
+
)
|