utim-cli 2.3.24__tar.gz → 2.3.25__tar.gz
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.
- {utim_cli-2.3.24 → utim_cli-2.3.25}/CHANGELOG.md +5 -0
- {utim_cli-2.3.24/utim_cli.egg-info → utim_cli-2.3.25}/PKG-INFO +1 -1
- {utim_cli-2.3.24 → utim_cli-2.3.25}/pyproject.toml +1 -1
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim/__init__.py +1 -1
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/__init__.py +1 -1
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_version.py +1 -1
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_llm_transport.py +1 -44
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_tool_exec.py +7 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/state.py +1 -0
- utim_cli-2.3.25/utim_cli/tui/compression_dialog.py +188 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utim.py +151 -166
- {utim_cli-2.3.24 → utim_cli-2.3.25/utim_cli.egg-info}/PKG-INFO +1 -1
- utim_cli-2.3.24/utim_cli/tui/compression_dialog.py +0 -230
- {utim_cli-2.3.24 → utim_cli-2.3.25}/LICENSE +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/MANIFEST.in +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/README.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/setup.cfg +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/setup.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_chip_test.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_verify_repro.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_verify_syntax.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/_wizard_smoke_test.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/agent.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/ask_helper.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/auth.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/backup.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/billing.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/blender_agent.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/bootstrap.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/brain.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/builtin/skills/utim-guide/SKILL.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/client_utils.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/config.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/constants.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/context_pruner.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/engine.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/crawler/markdown_distiller.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/dependency_manager.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/doctor.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/evidence_registry.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/harbor.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/knowledge_graph.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/local_db.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/logger.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_clean_wrapper.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_client.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/mcp_registry.json +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/models.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_helpers.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/orchestrator_turn_history.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/personalities.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/platform_installer.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/hooks.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/loader.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/manager.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/registry.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/runtime.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/schema.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/plugins/tool_loader.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/reflection.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/report.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/scrapy_search.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/config.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/context.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/events.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/handlers.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/session.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/sdk/sidecar.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/admin_auth.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/attribution.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/audit_log.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/auth.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/batch_processor.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/captcha.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/cli_auth.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/concurrency.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/db.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/about.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/changelog.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/docs.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/features.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/license.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/pricing.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/privacy.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/refund.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/support.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/docs_md/terms.md +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/email_utils.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/exchange_rate.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/firebase.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/fix_duplicate_users.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/history.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/key_rotator.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/local_llm.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/logging_config.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/micro_batcher.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/model_agent.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/models.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/plan_integrity.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/pricing_updater.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/provision_build.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/rate_limit.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/rewards_engine.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/router.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/admin_bonus_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/admin_db_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/auth_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/billing_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/completion_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/credit_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/feedback_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/marketplace_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/quota_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/quota_share_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/referral_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/rewards_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/security_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/session_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/share_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/routes/webhook_routes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/security_alerts.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/server.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/storage_nodes.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/server/utils.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/share.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/share_tui.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/situational_scoring.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/subagent_manager.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/task_dispatcher.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/task_logger.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tools.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tools_grep.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/__init__.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/feedback_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/history_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_app_state.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/marketplace_layout_engine.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/mcp_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/miniagents_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/model_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/personality_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/plugins_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/publish_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_redeem_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/quota_share_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/resume_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/rewards_tui.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/skills_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/subagents_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/tasks_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/thinking_display.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/tools_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/tui/update_dialog.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/update_checker.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utilities.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/utimmodel.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/vector_memory.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/wheel.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli/workspace.py +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/SOURCES.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/dependency_links.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/entry_points.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/requires.txt +0 -0
- {utim_cli-2.3.24 → utim_cli-2.3.25}/utim_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: utim-cli
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.25
|
|
4
4
|
Summary: UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal.
|
|
5
5
|
License: Emend AI Proprietary EULA
|
|
6
6
|
Project-URL: Homepage, https://utim.dev
|
|
@@ -14,7 +14,7 @@ Quickstart:
|
|
|
14
14
|
try:
|
|
15
15
|
from utim_cli._version import VERSION as __version__
|
|
16
16
|
except ImportError:
|
|
17
|
-
__version__ = "2.3.
|
|
17
|
+
__version__ = "2.3.25"
|
|
18
18
|
|
|
19
19
|
# Developer-friendly core classes (similar to `import genai` / `import openai`)
|
|
20
20
|
from utim_cli.sdk.session import UtimSession as Session
|
|
@@ -268,11 +268,6 @@ class LLMTransportMixin:
|
|
|
268
268
|
if not race_candidates:
|
|
269
269
|
return None
|
|
270
270
|
|
|
271
|
-
if not silent:
|
|
272
|
-
self.console.print(
|
|
273
|
-
f"\n[dim yellow]↪ Primary model unavailable — racing {len(race_candidates)} fallback models for lowest latency response…[/dim yellow]\n"
|
|
274
|
-
)
|
|
275
|
-
|
|
276
271
|
winner_lock = threading.Lock()
|
|
277
272
|
winner_model = [None]
|
|
278
273
|
winner_result = [None]
|
|
@@ -1135,10 +1130,6 @@ class LLMTransportMixin:
|
|
|
1135
1130
|
_exhausted_key = _api_key
|
|
1136
1131
|
|
|
1137
1132
|
if _exhausted_key:
|
|
1138
|
-
remaining = config.get_exhaustion_countdown(_exhausted_key)
|
|
1139
|
-
remaining_str = f"{remaining // 3600}h {remaining % 3600 // 60}m" if remaining else "24h"
|
|
1140
|
-
if not silent:
|
|
1141
|
-
self.console.print(f"[dim]Skipping exhausted API key {_exhausted_key[:12]}... (cooldown: {remaining_str})[/dim]")
|
|
1142
1133
|
raise _ServerUnavailableError(f"API key exhausted: {_exhausted_key[:12]}...")
|
|
1143
1134
|
|
|
1144
1135
|
# Build auth headers or query params depending on provider
|
|
@@ -1610,17 +1601,8 @@ class LLMTransportMixin:
|
|
|
1610
1601
|
_paid_slug = current_model.rsplit(":free", 1)[0]
|
|
1611
1602
|
_remaining = models_to_try[model_idx + 1:]
|
|
1612
1603
|
if _paid_slug not in _remaining:
|
|
1613
|
-
if not silent:
|
|
1614
|
-
self.console.print(
|
|
1615
|
-
f"\n[dim yellow]↪ Free model rate-limited — trying paid version '{_paid_slug}'…[/dim yellow]"
|
|
1616
|
-
)
|
|
1617
1604
|
# Insert paid slug as the very next model, then break
|
|
1618
1605
|
models_to_try[model_idx + 1:model_idx + 1] = [_paid_slug]
|
|
1619
|
-
else:
|
|
1620
|
-
if not silent:
|
|
1621
|
-
self.console.print(
|
|
1622
|
-
f"\n[dim yellow]↪ Free model rate-limited — paid version already in queue, skipping…[/dim yellow]"
|
|
1623
|
-
)
|
|
1624
1606
|
break # inner attempt loop → outer loop picks up injected paid slug
|
|
1625
1607
|
# Non-free model: nothing to fall back to — show message and return
|
|
1626
1608
|
rate_limit_display = (
|
|
@@ -1641,21 +1623,9 @@ class LLMTransportMixin:
|
|
|
1641
1623
|
"tool_calls": None,
|
|
1642
1624
|
}, False
|
|
1643
1625
|
elif _is_provider_upstream:
|
|
1644
|
-
if not silent:
|
|
1645
|
-
self.console.print(
|
|
1646
|
-
f"\n[dim yellow]↪ {_friendly_name}'s provider is having trouble — "
|
|
1647
|
-
f"switching to the next available model…[/dim yellow]"
|
|
1648
|
-
)
|
|
1649
1626
|
raise RuntimeError(f"__PROVIDER_ERROR__:{current_model}")
|
|
1650
1627
|
else:
|
|
1651
|
-
|
|
1652
|
-
_short = err_msg.split("{'error'")[0].strip() or err_msg[:120]
|
|
1653
|
-
if not silent:
|
|
1654
|
-
self.console.print(
|
|
1655
|
-
f"\n[bold red]Model error on {_friendly_name}: {_short}[/bold red]\n"
|
|
1656
|
-
f"[dim]Trying the next available model…[/dim]"
|
|
1657
|
-
)
|
|
1658
|
-
raise RuntimeError(f"Server completion error ({current_model}): {err_msg}")
|
|
1628
|
+
raise RuntimeError(f"Server completion error ({current_model}): {err_msg}")
|
|
1659
1629
|
if "finish_reason" in chunk and chunk["finish_reason"]:
|
|
1660
1630
|
_stream_finish_reason = chunk["finish_reason"]
|
|
1661
1631
|
elif not _stream_finish_reason:
|
|
@@ -2188,24 +2158,11 @@ class LLMTransportMixin:
|
|
|
2188
2158
|
_paid_slug = current_model.rsplit(":free", 1)[0]
|
|
2189
2159
|
_remaining = models_to_try[model_idx + 1:]
|
|
2190
2160
|
if _paid_slug not in _remaining:
|
|
2191
|
-
if not silent:
|
|
2192
|
-
self.console.print(
|
|
2193
|
-
f"\n[dim yellow]↪ Free model rate-limited — trying paid version '{_paid_slug}'…[/dim yellow]"
|
|
2194
|
-
)
|
|
2195
2161
|
# Insert paid slug as the very next model, then break
|
|
2196
2162
|
models_to_try[model_idx + 1:model_idx + 1] = [_paid_slug]
|
|
2197
|
-
else:
|
|
2198
|
-
if not silent:
|
|
2199
|
-
self.console.print(
|
|
2200
|
-
f"\n[dim yellow]↪ Free model rate-limited — paid version already in queue, skipping…[/dim yellow]"
|
|
2201
|
-
)
|
|
2202
2163
|
break # inner attempt loop → outer loop picks up injected paid slug
|
|
2203
2164
|
# If primary model is rate-limited and fallback models exist, race them
|
|
2204
2165
|
if current_is_primary and fallback_list:
|
|
2205
|
-
if not silent:
|
|
2206
|
-
self.console.print(
|
|
2207
|
-
f"\n[dim yellow]⏳ Model '{current_model}' is temporarily rate-limited by the provider — racing fallback models…[/dim yellow]\n"
|
|
2208
|
-
)
|
|
2209
2166
|
raise _ServerUnavailableError(f"Rate-limited on model '{current_model}'")
|
|
2210
2167
|
|
|
2211
2168
|
# Non-free model with no fallback: show message and return
|
|
@@ -659,6 +659,13 @@ class ToolExecutionMixin:
|
|
|
659
659
|
self.console.print(bl)
|
|
660
660
|
rendered_text = capture.get()
|
|
661
661
|
if print_to_console and getattr(self, "_subagent_depth", 0) == 0:
|
|
662
|
+
try:
|
|
663
|
+
from utim_cli.state import STATE
|
|
664
|
+
if STATE.get("dialog_active"):
|
|
665
|
+
self.console.print(rendered_text, end="", markup=False, highlight=False)
|
|
666
|
+
return rendered_text
|
|
667
|
+
except Exception:
|
|
668
|
+
pass
|
|
662
669
|
if getattr(self.console, "file", None) is not sys.stdout and getattr(self.console, "file", None) is not sys.__stdout__:
|
|
663
670
|
# IMPORTANT: rendered_text is a *plain string* that was already
|
|
664
671
|
# produced by Rich (via capture.get()). It can legitimately
|
|
@@ -13,4 +13,5 @@ STATE = {
|
|
|
13
13
|
"tools_expanded": False, # Toggle global tool results inline expand/collapse
|
|
14
14
|
"thinking_topic": "", # Dynamic topic for the spinner
|
|
15
15
|
"is_verified": False, # True if user has entered the secret code in this session
|
|
16
|
+
"dialog_active": False, # True when any modal/dialog is full screen or running in terminal
|
|
16
17
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
/compression -- Interactive context compression level picker.
|
|
3
|
+
|
|
4
|
+
Five levels from Very Low (almost verbatim, costs more) to Very High
|
|
5
|
+
(aggressive summarisation, saves tokens but risks forgetting old context).
|
|
6
|
+
The chosen level is persisted in config and immediately applied to
|
|
7
|
+
context_pruner.build_compacted_history_message on the next turn.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from typing import Tuple
|
|
12
|
+
|
|
13
|
+
# -- Level definitions ---------------------------------------------------------
|
|
14
|
+
# Each entry: (id, label, oldest_keep_ratio, newest_keep_ratio, merge_count, warning)
|
|
15
|
+
COMPRESSION_LEVELS = [
|
|
16
|
+
(
|
|
17
|
+
"very_low",
|
|
18
|
+
"Very Low",
|
|
19
|
+
0.50, 0.95,
|
|
20
|
+
0,
|
|
21
|
+
"Can cost more because most of the context is kept; model has very low risk of forgetting.",
|
|
22
|
+
"yellow",
|
|
23
|
+
),
|
|
24
|
+
(
|
|
25
|
+
"low",
|
|
26
|
+
"Low",
|
|
27
|
+
0.25, 0.85,
|
|
28
|
+
2,
|
|
29
|
+
"Mild compression on older turns; keeps context intact with minimal forgetting risk.",
|
|
30
|
+
"dim",
|
|
31
|
+
),
|
|
32
|
+
(
|
|
33
|
+
"medium",
|
|
34
|
+
"Medium [default]",
|
|
35
|
+
0.07, 0.70,
|
|
36
|
+
5,
|
|
37
|
+
"Balanced progressive compression (oldest ~7% to newest ~70% kept); merges turns 1-5.",
|
|
38
|
+
"dim",
|
|
39
|
+
),
|
|
40
|
+
(
|
|
41
|
+
"high",
|
|
42
|
+
"High",
|
|
43
|
+
0.03, 0.50,
|
|
44
|
+
8,
|
|
45
|
+
"Aggressive compression; saves more tokens but might lose details from earlier turns.",
|
|
46
|
+
"yellow",
|
|
47
|
+
),
|
|
48
|
+
(
|
|
49
|
+
"very_high",
|
|
50
|
+
"Very High",
|
|
51
|
+
0.01, 0.30,
|
|
52
|
+
12,
|
|
53
|
+
"Maximum compression; saves tokens, but might lose bit of context from previous turns when session is running long.",
|
|
54
|
+
"red",
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
LEVEL_IDS = [l[0] for l in COMPRESSION_LEVELS]
|
|
59
|
+
DEFAULT_LEVEL = "medium"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_compression_level() -> str:
|
|
63
|
+
"""Return the currently persisted compression level id (defaults to 'medium')."""
|
|
64
|
+
try:
|
|
65
|
+
from utim_cli.config import config
|
|
66
|
+
val = config.get("compression_level") or DEFAULT_LEVEL
|
|
67
|
+
return val if val in LEVEL_IDS else DEFAULT_LEVEL
|
|
68
|
+
except Exception:
|
|
69
|
+
return DEFAULT_LEVEL
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def set_compression_level(level_id: str) -> None:
|
|
73
|
+
"""Persist the chosen compression level and notify context_pruner."""
|
|
74
|
+
try:
|
|
75
|
+
from utim_cli.config import config
|
|
76
|
+
config.set("compression_level", level_id)
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
try:
|
|
80
|
+
from utim_cli.context_pruner import apply_compression_level
|
|
81
|
+
apply_compression_level(level_id)
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_level_params(level_id: str) -> Tuple[float, float, int]:
|
|
87
|
+
"""Return (oldest_ratio, newest_ratio, merge_count) for a level id."""
|
|
88
|
+
for lid, _, oldest, newest, merge, _, _ in COMPRESSION_LEVELS:
|
|
89
|
+
if lid == level_id:
|
|
90
|
+
return oldest, newest, merge
|
|
91
|
+
return 0.07, 0.70, 5 # medium fallback
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# -- TUI dialog ----------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
def _dialog_compression(orchestrator=None) -> None:
|
|
97
|
+
"""Open the interactive compression level picker using UTIM's full-screen list dialog."""
|
|
98
|
+
from utim_cli.utim import console, _run_list_dialog
|
|
99
|
+
|
|
100
|
+
current_level = get_compression_level()
|
|
101
|
+
|
|
102
|
+
# Build options list from COMPRESSION_LEVELS
|
|
103
|
+
options = []
|
|
104
|
+
for lid, label, oldest, newest, merge, warning, warn_color in COMPRESSION_LEVELS:
|
|
105
|
+
options.append({
|
|
106
|
+
"id": lid,
|
|
107
|
+
"label": label,
|
|
108
|
+
"oldest": oldest,
|
|
109
|
+
"newest": newest,
|
|
110
|
+
"merge": merge,
|
|
111
|
+
"warning": warning,
|
|
112
|
+
"warn_color": warn_color,
|
|
113
|
+
})
|
|
114
|
+
options.append({
|
|
115
|
+
"id": "cancel",
|
|
116
|
+
"label": "↩ Cancel",
|
|
117
|
+
"warning": "Exit without changing compression level",
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
def _render_row(i: int, row: dict, sel: bool):
|
|
121
|
+
bg = "bg:#313244 fg:#ffffff bold" if sel else ""
|
|
122
|
+
if row.get("id") == "cancel":
|
|
123
|
+
return [
|
|
124
|
+
(bg or "class:dim", " ➔ " if sel else " "),
|
|
125
|
+
(bg or "bold #f38ba8", f"{row['label']}\n"),
|
|
126
|
+
(bg or "class:dim", f" {row['warning']}\n"),
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
is_current = (row["id"] == current_level)
|
|
130
|
+
badge = "● ACTIVE " if is_current else "○ "
|
|
131
|
+
badge_style = bg or ("bold #a6e3a1" if is_current else "class:dim")
|
|
132
|
+
|
|
133
|
+
oldest_pct = int(row["oldest"] * 100)
|
|
134
|
+
newest_pct = int(row["newest"] * 100)
|
|
135
|
+
ratio_str = f"oldest {oldest_pct}% → newest {newest_pct}% kept"
|
|
136
|
+
|
|
137
|
+
warn_color = row.get("warn_color", "dim")
|
|
138
|
+
if warn_color == "red":
|
|
139
|
+
warn_style = bg or "bold #ff5555"
|
|
140
|
+
elif warn_color == "yellow":
|
|
141
|
+
warn_style = bg or "#ffb86c"
|
|
142
|
+
else:
|
|
143
|
+
warn_style = bg or "class:dim"
|
|
144
|
+
icon = "⚠ " if warn_color in ("red", "yellow") else "ℹ "
|
|
145
|
+
|
|
146
|
+
if row.get("merge", 0) > 0:
|
|
147
|
+
merge_txt = f"Merges oldest {row['merge']} turns into one block"
|
|
148
|
+
else:
|
|
149
|
+
merge_txt = "No turn merging (all turns kept individually)"
|
|
150
|
+
|
|
151
|
+
fragments = [
|
|
152
|
+
(badge_style, f" {badge}"),
|
|
153
|
+
(bg or "bold #42bcf5", f"{row['label']:<20}"),
|
|
154
|
+
(bg or "class:dim", f" [{ratio_str}]\n"),
|
|
155
|
+
(bg or "#a6adc8", f" • {merge_txt}\n"),
|
|
156
|
+
(warn_style, f" {icon}{row['warning']}\n"),
|
|
157
|
+
]
|
|
158
|
+
return fragments
|
|
159
|
+
|
|
160
|
+
action, idx = _run_list_dialog(
|
|
161
|
+
options,
|
|
162
|
+
_render_row,
|
|
163
|
+
title="❯ Context Compression Level Picker",
|
|
164
|
+
legend=(
|
|
165
|
+
"↑↓ Navigate • Enter Apply • Esc / q Cancel • "
|
|
166
|
+
"Currently active level marked [bold #a6e3a1]● ACTIVE[/bold #a6e3a1]"
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if action != "select" or idx is None:
|
|
171
|
+
try:
|
|
172
|
+
console.print("\n [dim]✗ Cancelled. No changes made.[/dim]\n")
|
|
173
|
+
except Exception:
|
|
174
|
+
pass
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
chosen = options[idx]
|
|
178
|
+
if chosen["id"] == "cancel":
|
|
179
|
+
console.print("\n [dim]✗ Cancelled. No changes made.[/dim]\n")
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
set_compression_level(chosen["id"])
|
|
183
|
+
|
|
184
|
+
console.print(
|
|
185
|
+
f"\n [bold #42bcf5]✓ Compression level set to[/bold #42bcf5] "
|
|
186
|
+
f"[bold white]{chosen['label']}[/bold white] "
|
|
187
|
+
f"[dim](takes effect on next context window build)[/dim]\n"
|
|
188
|
+
)
|