utim-cli 2.3.21__tar.gz → 2.3.22__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.
Files changed (171) hide show
  1. {utim_cli-2.3.21 → utim_cli-2.3.22}/CHANGELOG.md +15 -8
  2. {utim_cli-2.3.21/utim_cli.egg-info → utim_cli-2.3.22}/PKG-INFO +1 -1
  3. {utim_cli-2.3.21 → utim_cli-2.3.22}/pyproject.toml +1 -1
  4. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim/__init__.py +1 -1
  5. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/__init__.py +1 -1
  6. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/_version.py +1 -1
  7. utim_cli-2.3.22/utim_cli/constants.py +99 -0
  8. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/orchestrator_llm_transport.py +29 -1
  9. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/models.py +12 -0
  10. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/completion_routes.py +4 -52
  11. {utim_cli-2.3.21 → utim_cli-2.3.22/utim_cli.egg-info}/PKG-INFO +1 -1
  12. utim_cli-2.3.21/utim_cli/constants.py +0 -43
  13. {utim_cli-2.3.21 → utim_cli-2.3.22}/LICENSE +0 -0
  14. {utim_cli-2.3.21 → utim_cli-2.3.22}/MANIFEST.in +0 -0
  15. {utim_cli-2.3.21 → utim_cli-2.3.22}/README.md +0 -0
  16. {utim_cli-2.3.21 → utim_cli-2.3.22}/setup.cfg +0 -0
  17. {utim_cli-2.3.21 → utim_cli-2.3.22}/setup.py +0 -0
  18. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/_verify_repro.py +0 -0
  19. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/_verify_syntax.py +0 -0
  20. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/_wizard_smoke_test.py +0 -0
  21. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/agent.py +0 -0
  22. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/ask_helper.py +0 -0
  23. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/auth.py +0 -0
  24. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/backup.py +0 -0
  25. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/billing.py +0 -0
  26. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/blender_agent.py +0 -0
  27. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/bootstrap.py +0 -0
  28. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/brain.py +0 -0
  29. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/builtin/skills/utim-guide/SKILL.md +0 -0
  30. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/client_utils.py +0 -0
  31. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/config.py +0 -0
  32. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/context_pruner.py +0 -0
  33. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/crawler/__init__.py +0 -0
  34. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/crawler/engine.py +0 -0
  35. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/crawler/markdown_distiller.py +0 -0
  36. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/dependency_manager.py +0 -0
  37. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/doctor.py +0 -0
  38. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/evidence_registry.py +0 -0
  39. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/harbor.py +0 -0
  40. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/knowledge_graph.py +0 -0
  41. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/local_db.py +0 -0
  42. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/logger.py +0 -0
  43. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/mcp_clean_wrapper.py +0 -0
  44. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/mcp_client.py +0 -0
  45. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/mcp_registry.json +0 -0
  46. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/models.txt +0 -0
  47. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/orchestrator.py +0 -0
  48. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/orchestrator_helpers.py +0 -0
  49. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/orchestrator_tool_exec.py +0 -0
  50. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/orchestrator_turn_history.py +0 -0
  51. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/personalities.py +0 -0
  52. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/platform_installer.py +0 -0
  53. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/__init__.py +0 -0
  54. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/hooks.py +0 -0
  55. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/loader.py +0 -0
  56. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/manager.py +0 -0
  57. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/registry.py +0 -0
  58. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/runtime.py +0 -0
  59. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/schema.py +0 -0
  60. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/plugins/tool_loader.py +0 -0
  61. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/reflection.py +0 -0
  62. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/report.py +0 -0
  63. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/scrapy_search.py +0 -0
  64. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/__init__.py +0 -0
  65. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/config.py +0 -0
  66. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/context.py +0 -0
  67. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/events.py +0 -0
  68. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/handlers.py +0 -0
  69. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/session.py +0 -0
  70. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/sdk/sidecar.py +0 -0
  71. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/__init__.py +0 -0
  72. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/admin_auth.py +0 -0
  73. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/attribution.py +0 -0
  74. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/audit_log.py +0 -0
  75. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/auth.py +0 -0
  76. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/batch_processor.py +0 -0
  77. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/captcha.py +0 -0
  78. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/cli_auth.py +0 -0
  79. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/concurrency.py +0 -0
  80. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/db.py +0 -0
  81. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/SECRET_PROVISIONING.md +0 -0
  82. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/about.md +0 -0
  83. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/changelog.md +0 -0
  84. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/docs.md +0 -0
  85. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/features.md +0 -0
  86. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/license.md +0 -0
  87. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/pricing.md +0 -0
  88. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/privacy.md +0 -0
  89. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/refund.md +0 -0
  90. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/support.md +0 -0
  91. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/docs_md/terms.md +0 -0
  92. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/email_utils.py +0 -0
  93. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/exchange_rate.py +0 -0
  94. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/firebase.py +0 -0
  95. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/fix_duplicate_users.py +0 -0
  96. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/history.py +0 -0
  97. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/key_rotator.py +0 -0
  98. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/local_llm.py +0 -0
  99. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/logging_config.py +0 -0
  100. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/micro_batcher.py +0 -0
  101. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/model_agent.py +0 -0
  102. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/plan_integrity.py +0 -0
  103. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/pricing_updater.py +0 -0
  104. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/provision_build.py +0 -0
  105. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/rate_limit.py +0 -0
  106. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/rewards_engine.py +0 -0
  107. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/router.py +0 -0
  108. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/__init__.py +0 -0
  109. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/admin_bonus_routes.py +0 -0
  110. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/admin_db_routes.py +0 -0
  111. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/auth_routes.py +0 -0
  112. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/billing_routes.py +0 -0
  113. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/credit_routes.py +0 -0
  114. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/feedback_routes.py +0 -0
  115. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/marketplace_routes.py +0 -0
  116. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/quota_routes.py +0 -0
  117. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/quota_share_routes.py +0 -0
  118. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/referral_routes.py +0 -0
  119. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/rewards_routes.py +0 -0
  120. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/security_routes.py +0 -0
  121. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/session_routes.py +0 -0
  122. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/share_routes.py +0 -0
  123. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/routes/webhook_routes.py +0 -0
  124. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/security_alerts.py +0 -0
  125. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/server.py +0 -0
  126. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/storage_nodes.py +0 -0
  127. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/server/utils.py +0 -0
  128. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/share.py +0 -0
  129. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/share_tui.py +0 -0
  130. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/situational_scoring.py +0 -0
  131. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/state.py +0 -0
  132. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/subagent_manager.py +0 -0
  133. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/task_dispatcher.py +0 -0
  134. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/task_logger.py +0 -0
  135. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tools.py +0 -0
  136. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tools_grep.py +0 -0
  137. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/__init__.py +0 -0
  138. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/feedback_dialog.py +0 -0
  139. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/history_dialog.py +0 -0
  140. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/marketplace_app_state.py +0 -0
  141. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/marketplace_dialog.py +0 -0
  142. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/marketplace_layout_engine.py +0 -0
  143. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/mcp_dialog.py +0 -0
  144. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/miniagents_dialog.py +0 -0
  145. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/model_dialog.py +0 -0
  146. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/personality_dialog.py +0 -0
  147. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/plugins_dialog.py +0 -0
  148. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/publish_dialog.py +0 -0
  149. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/quota_dialog.py +0 -0
  150. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/quota_redeem_dialog.py +0 -0
  151. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/quota_share_dialog.py +0 -0
  152. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/resume_dialog.py +0 -0
  153. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/rewards_tui.py +0 -0
  154. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/skills_dialog.py +0 -0
  155. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/subagents_dialog.py +0 -0
  156. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/tasks_dialog.py +0 -0
  157. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/thinking_display.py +0 -0
  158. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/tools_dialog.py +0 -0
  159. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/tui/update_dialog.py +0 -0
  160. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/update_checker.py +0 -0
  161. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/utilities.py +0 -0
  162. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/utim.py +0 -0
  163. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/utimmodel.txt +0 -0
  164. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/vector_memory.py +0 -0
  165. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/wheel.py +0 -0
  166. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli/workspace.py +0 -0
  167. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli.egg-info/SOURCES.txt +0 -0
  168. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli.egg-info/dependency_links.txt +0 -0
  169. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli.egg-info/entry_points.txt +0 -0
  170. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli.egg-info/requires.txt +0 -0
  171. {utim_cli-2.3.21 → utim_cli-2.3.22}/utim_cli.egg-info/top_level.txt +0 -0
@@ -1,19 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.3.22] - 2026-09-06
4
+
5
+ ### 🛡️ Seamless Rate-Limit Failover to Paid Slugs
6
+ - **Client-Side Auto-Shift**: In `orchestrator_llm_transport.py`, when a free model (e.g. `poolside/laguna-s-2.1:free`) encounters an HTTP 429 or streaming rate-limit event, the client immediately switches to its paid slug (`poolside/laguna-s-2.1`) without terminating the conversation turn or showing an interrupting rate-limit error.
7
+ - **Single Source of Truth**: Centralized `get_paid_slug()` in `utim_cli/constants.py` for uniform mapping across client transport and server completion routing.
8
+ - **Laguna Paid Slug Registration**: Added `poolside/laguna-s-2.1` to the server model registry with complete tool, chat, and reasoning capabilities.
9
+ - **Cost-Protected MiniMax Failover**: Ensured rate-limited MiniMax free models shift to the Laguna S 2.1 paid slug rather than expensive MiniMax paid tiers.
10
+ - **Credit Rate Preservation**: Retained fixed free model pricing ($0.30 input / $0.50 output per 1M tokens) on server fallback turns.
11
+
3
12
  ## [2.3.21] - 2026-09-06
4
13
 
5
14
  ### 💰 Pricing Modifications
6
- - **OpenRouter Tier Markup**: Standardized 30% markup across OpenRouter models in the server `model_registry` table.
7
- - **Free Model Fallback Credit Rate Preservation**: Preserved fixed consumption rates ($0.30 input / $0.50 output per 1M tokens) when transitioning from exhausted APINEX keys to fallback paid slugs, ensuring consistent credit accounting.
15
+ - OpenRouter tier markup standardization and free model fallback credit preservation
8
16
 
9
- ### 📦 Models Payload Fixes
10
- - **Upstream Provider Normalization**: Normalized vendor provider resolution for multi-provider models (MiniMax, Cohere, Dots-Studio, InclusionAI) to route cleanly via OpenRouter upstream.
11
- - **Image Generation Model Filtering**: Excluded image-generation models from the interactive chat model catalog to avoid completion payload incompatibilities.
12
- - **APINEX Model Mapping Support**: Added model registry definitions and key rotation support for `qwen/3.8-max` and `free/qwen-3.8-max`.
17
+ ### 📦 Models Payload Fixes
18
+ - Upstream provider normalization and APINEX model mapping support
13
19
 
14
20
  ### 🛡️ Rate Limit & Vision Tooling Fixes
15
- - **MiniMax Rate-Limit Fallback to Laguna**: Redirected exhausted MiniMax free models (`minimax/minimax-m2.7:free`, `minimax/minimax-m3:free`) to fall back to `poolside/laguna-s-2.1` paid slug rather than expensive MiniMax paid slugs.
16
- - **APINEX Transparent Image Analysis**: Updated `read_file` on image files under APINEX models to automatically execute `analyze_image` in the background and return only the text summary, bypassing vision parameter constraints.
21
+ - MiniMax rate-limit fallback to Laguna and APINEX transparent image analysis
22
+
23
+ *See [docs](docs.md) for detailed APINEX migration information and implementation notes.*
17
24
 
18
25
  ## [2.3.20] - 2026-09-04
19
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.3.21
3
+ Version: 2.3.22
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "utim-cli"
7
- version = "2.3.21"
7
+ version = "2.3.22"
8
8
 
9
9
  description = "UTIM – Universal Terminal Intelligence Manager. A powerful agentic AI coding assistant for your terminal."
10
10
  readme = "README.md"
@@ -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.21"
17
+ __version__ = "2.3.22"
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
@@ -5,7 +5,7 @@ try:
5
5
  from utim_cli._version import VERSION as __version__
6
6
  except ImportError:
7
7
  # Fallback during first-install / editable-install edge cases
8
- __version__ = "2.3.21"
8
+ __version__ = "2.3.22"
9
9
 
10
10
 
11
11
  import builtins, os, pathlib, urllib.parse, re
@@ -11,6 +11,6 @@
11
11
  # from here so every surface stays in sync automatically.
12
12
  # ─────────────────────────────────────────────────────────────────────────────
13
13
 
14
- VERSION = "2.3.21"
14
+ VERSION = "2.3.22"
15
15
 
16
16
 
@@ -0,0 +1,99 @@
1
+ import os
2
+
3
+ DEFAULT_MODEL = "poolside/laguna-s-2.1:free"
4
+ SERVER_URL = os.getenv("UTIM_SERVER_URL", "https://api.utim.dev")
5
+ OPENROUTER_BASE_URL = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
6
+
7
+ SUBAGENT_DEFAULTS = {
8
+ "web_search": DEFAULT_MODEL,
9
+ "plan_project": DEFAULT_MODEL,
10
+ "generate_image": "qwen/qwen-image-3",
11
+ "analyze_image": "google/gemma-4-31b-it:free",
12
+ }
13
+
14
+ APINEX_MODEL_IDS = {
15
+ "claude/opus-5",
16
+ "claude/sonnet-5",
17
+ "deepseek/v4-flash-0731",
18
+ "deepseek/v4-pro-0813",
19
+ "free/deepseek-v4-flash-0731",
20
+ "free/deepseek-v4-pro-0813",
21
+ "free/glm-5.3-flash",
22
+ "free/gemini-3.1-pro",
23
+ "free/gemini-3.7-flash",
24
+ "gemini/3.1-pro",
25
+ "gemini/3.7-flash",
26
+ "kimi/k3",
27
+ "gpt/5.6-luna",
28
+ "gpt/5.6-sol",
29
+ "gpt/5.6-terra",
30
+ "glm/5.3",
31
+ "glm/5.3-flash",
32
+ "grok/4.6",
33
+ "qwen/3.8-max",
34
+ "free/qwen-3.8-max",
35
+ }
36
+
37
+ APINEX_TO_OPENROUTER_PAID: dict[str, str] = {
38
+ "gemini/3.7-flash": "google/gemini-2.5-flash",
39
+ "free/gemini-3.7-flash": "google/gemini-2.5-flash",
40
+ "gemini/3.1-pro": "google/gemini-2.5-pro",
41
+ "free/gemini-3.1-pro": "google/gemini-2.5-pro",
42
+ "deepseek/v4-flash-0731": "deepseek/deepseek-chat",
43
+ "free/deepseek-v4-flash-0731": "deepseek/deepseek-chat",
44
+ "deepseek/v4-pro-0813": "deepseek/deepseek-r1",
45
+ "free/deepseek-v4-pro-0813": "deepseek/deepseek-r1",
46
+ "claude/sonnet-5": "anthropic/claude-3.5-sonnet",
47
+ "claude/opus-5": "anthropic/claude-3-opus",
48
+ "gpt/5.6-luna": "openai/gpt-4o-mini",
49
+ "gpt/5.6-sol": "openai/gpt-4o",
50
+ "gpt/5.6-terra": "openai/gpt-4o",
51
+ "glm/5.3": "z-ai/glm-4",
52
+ "glm/5.3-flash": "z-ai/glm-4",
53
+ "free/glm-5.3-flash": "z-ai/glm-4",
54
+ "kimi/k3": "moonshotai/kimi-k2",
55
+ "grok/4.6": "x-ai/grok-2-1212",
56
+ "qwen/3.8-max": "qwen/qwen-2.5-72b-instruct",
57
+ "free/qwen-3.8-max": "qwen/qwen-2.5-72b-instruct",
58
+ }
59
+
60
+
61
+ def get_paid_slug(model_id: str) -> str:
62
+ """Return the paid equivalent slug for a free or gateway model.
63
+ - MiniMax models (e.g. minimax/minimax-m2.7:free, minimax/minimax-m3:free) are pricy,
64
+ so when hitting rate limits, the server falls back to Laguna S 2.1 paid slug
65
+ ('poolside/laguna-s-2.1') instead of expensive MiniMax paid slugs.
66
+ - If model ends with ':free', strip ':free' (provider/model:free -> provider/model).
67
+ - If APINEX model, map to corresponding OpenRouter paid model.
68
+ - If model starts with 'free/', check APINEX mapping or strip 'free/'.
69
+ - If 'openrouter/free', return standard high-capacity default ('google/gemini-2.5-flash').
70
+ """
71
+ clean_id = (model_id or "").strip()
72
+ clean_lower = clean_id.lower()
73
+
74
+ # MiniMax free models are pricy on their paid tier; route fallback to Laguna S 2.1 paid slug
75
+ if "minimax" in clean_lower:
76
+ return "poolside/laguna-s-2.1"
77
+
78
+ if clean_id.endswith(":free"):
79
+ return clean_id[:-5]
80
+ if clean_id in APINEX_TO_OPENROUTER_PAID:
81
+ return APINEX_TO_OPENROUTER_PAID[clean_id]
82
+ if clean_id.startswith("free/"):
83
+ unprefixed = clean_id[5:]
84
+ if unprefixed in APINEX_TO_OPENROUTER_PAID:
85
+ return APINEX_TO_OPENROUTER_PAID[unprefixed]
86
+ if unprefixed.endswith(":free"):
87
+ return unprefixed[:-5]
88
+ return unprefixed
89
+ if clean_id == "openrouter/free":
90
+ return "google/gemini-2.5-flash"
91
+ return clean_id
92
+
93
+
94
+ import sys
95
+ _IS_LEGACY_WIN = (sys.platform == "win32" and "WT_SESSION" not in os.environ)
96
+ PROMPT_SYMBOL = ">" if _IS_LEGACY_WIN else "❯"
97
+ ARROW_SYMBOL = "->" if _IS_LEGACY_WIN else "➔"
98
+
99
+
@@ -30,7 +30,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
30
30
  from typing import Any, Dict, List, Optional, Tuple
31
31
 
32
32
  import requests
33
- from utim_cli.constants import DEFAULT_MODEL
33
+ from utim_cli.constants import DEFAULT_MODEL, get_paid_slug
34
34
 
35
35
  # openai SDK removed — we call OpenRouter directly via requests (no Rust/jiter needed)
36
36
  from rich.console import Console
@@ -1558,6 +1558,20 @@ class LLMTransportMixin:
1558
1558
  "tool_calls": None,
1559
1559
  }, False
1560
1560
  elif _is_rate_limit:
1561
+ _paid_slug = get_paid_slug(current_model)
1562
+ if _paid_slug != current_model:
1563
+ if not silent:
1564
+ self.console.print(
1565
+ f"\n[bold yellow]⏳ Model '{current_model}' is temporarily rate-limited. Shifting seamlessly to paid slug [bold]{_paid_slug}[/bold]...[/bold yellow]\n"
1566
+ )
1567
+ if _paid_slug not in models_to_try:
1568
+ models_to_try.insert(model_idx + 1, _paid_slug)
1569
+ if not override_model and model_idx == 0:
1570
+ self.model_id = _paid_slug
1571
+ self.model_source = "utim"
1572
+ config.set("main_model_source", "utim")
1573
+ raise RuntimeError(f"__RATE_LIMIT_SHIFT__:{current_model}:{_paid_slug}")
1574
+
1561
1575
  rate_limit_display = (
1562
1576
  f"⏳ **Model Rate Limited**: `{current_model}` is currently experiencing high demand and is temporarily rate-limited by the provider.\n\n"
1563
1577
  f"- **What to do:** Please wait a moment before retrying, or switch to a different model with `/model` or `Ctrl+M`.\n"
@@ -2115,6 +2129,20 @@ class LLMTransportMixin:
2115
2129
  "tool_calls": None,
2116
2130
  }, False
2117
2131
 
2132
+ _paid_slug = get_paid_slug(current_model)
2133
+ if _paid_slug != current_model:
2134
+ if not silent:
2135
+ self.console.print(
2136
+ f"\n[bold yellow]⏳ Model '{current_model}' is temporarily rate-limited. Shifting seamlessly to paid slug [bold]{_paid_slug}[/bold]...[/bold yellow]\n"
2137
+ )
2138
+ if _paid_slug not in models_to_try:
2139
+ models_to_try.insert(model_idx + 1, _paid_slug)
2140
+ if not override_model and model_idx == 0:
2141
+ self.model_id = _paid_slug
2142
+ self.model_source = "utim"
2143
+ config.set("main_model_source", "utim")
2144
+ break # attempt loop → outer loop immediately tries _paid_slug next!
2145
+
2118
2146
  rate_limit_display = (
2119
2147
  f"⏳ **Model Rate Limited**: `{current_model}` is currently experiencing high demand and is temporarily rate-limited by the provider.\n\n"
2120
2148
  f"- **What to do:** Please wait a moment before retrying, or switch to a different model with `/model` or `Ctrl+M`.\n"
@@ -1082,6 +1082,18 @@ MODEL_REGISTRY: dict[MODEL_ID, ModelEntry] = {
1082
1082
  capabilities=["code", "chat", "tool_use"],
1083
1083
  tags=["free"],
1084
1084
  ),
1085
+ "poolside/laguna-s-2.1": ModelEntry(
1086
+ model_id="poolside/laguna-s-2.1",
1087
+ provider="openrouter",
1088
+ cost_input_per_1k=0.000117,
1089
+ cost_output_per_1k=0.000234,
1090
+ cost_input_per_1m=0.117,
1091
+ cost_output_per_1m=0.234,
1092
+ context_window=1_048_576,
1093
+ capabilities=["code", "chat", "tool_use"],
1094
+ tags=["premium", "code"],
1095
+ max_output_tokens=131_072,
1096
+ ),
1085
1097
  "krea/krea-2-medium-turbo": ModelEntry(
1086
1098
  model_id="krea/krea-2-medium-turbo",
1087
1099
  provider="openrouter",
@@ -556,59 +556,11 @@ def get_openrouter_free_model(model_id: str, db: Optional[Session] = None) -> st
556
556
  return free_candidates[0]["model_id"]
557
557
 
558
558
 
559
- APINEX_TO_OPENROUTER_PAID: Dict[str, str] = {
560
- "gemini/3.7-flash": "google/gemini-2.5-flash",
561
- "free/gemini-3.7-flash": "google/gemini-2.5-flash",
562
- "gemini/3.1-pro": "google/gemini-2.5-pro",
563
- "free/gemini-3.1-pro": "google/gemini-2.5-pro",
564
- "deepseek/v4-flash-0731": "deepseek/deepseek-chat",
565
- "free/deepseek-v4-flash-0731": "deepseek/deepseek-chat",
566
- "deepseek/v4-pro-0813": "deepseek/deepseek-r1",
567
- "free/deepseek-v4-pro-0813": "deepseek/deepseek-r1",
568
- "claude/sonnet-5": "anthropic/claude-3.5-sonnet",
569
- "claude/opus-5": "anthropic/claude-3-opus",
570
- "gpt/5.6-luna": "openai/gpt-4o-mini",
571
- "gpt/5.6-sol": "openai/gpt-4o",
572
- "gpt/5.6-terra": "openai/gpt-4o",
573
- "glm/5.3": "z-ai/glm-4",
574
- "glm/5.3-flash": "z-ai/glm-4",
575
- "free/glm-5.3-flash": "z-ai/glm-4",
576
- "kimi/k3": "moonshotai/kimi-k2",
577
- "grok/4.6": "x-ai/grok-2-1212",
578
- }
579
-
559
+ from utim_cli.constants import (
560
+ APINEX_TO_OPENROUTER_PAID,
561
+ get_paid_slug,
562
+ )
580
563
 
581
- def get_paid_slug(model_id: str) -> str:
582
- """Return the paid equivalent slug for a free or gateway model.
583
- - MiniMax models (e.g. minimax/minimax-m2.7:free, minimax/minimax-m3:free) are pricy,
584
- so when hitting rate limits, the server falls back to Laguna S 2.1 paid slug
585
- ('poolside/laguna-s-2.1') instead of expensive MiniMax paid slugs.
586
- - If model ends with ':free', strip ':free' (provider/model:free -> provider/model).
587
- - If APINEX model, map to corresponding OpenRouter paid model.
588
- - If model starts with 'free/', check APINEX mapping or strip 'free/'.
589
- - If 'openrouter/free', return standard high-capacity default ('google/gemini-2.5-flash').
590
- """
591
- clean_id = model_id.strip()
592
- clean_lower = clean_id.lower()
593
-
594
- # MiniMax free models are pricy on their paid tier; route fallback to Laguna S 2.1 paid slug
595
- if "minimax" in clean_lower:
596
- return "poolside/laguna-s-2.1"
597
-
598
- if clean_id.endswith(":free"):
599
- return clean_id[:-5]
600
- if clean_id in APINEX_TO_OPENROUTER_PAID:
601
- return APINEX_TO_OPENROUTER_PAID[clean_id]
602
- if clean_id.startswith("free/"):
603
- unprefixed = clean_id[5:]
604
- if unprefixed in APINEX_TO_OPENROUTER_PAID:
605
- return APINEX_TO_OPENROUTER_PAID[unprefixed]
606
- if unprefixed.endswith(":free"):
607
- return unprefixed[:-5]
608
- return unprefixed
609
- if clean_id == "openrouter/free":
610
- return "google/gemini-2.5-flash"
611
- return clean_id
612
564
 
613
565
 
614
566
  def is_free_or_gateway_model(model_id: str) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: utim-cli
3
- Version: 2.3.21
3
+ Version: 2.3.22
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
@@ -1,43 +0,0 @@
1
- import os
2
-
3
- DEFAULT_MODEL = "poolside/laguna-s-2.1:free"
4
- SERVER_URL = os.getenv("UTIM_SERVER_URL", "https://api.utim.dev")
5
- OPENROUTER_BASE_URL = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
6
-
7
- SUBAGENT_DEFAULTS = {
8
- "web_search": DEFAULT_MODEL,
9
- "plan_project": DEFAULT_MODEL,
10
- "generate_image": "qwen/qwen-image-3",
11
- "analyze_image": "google/gemma-4-31b-it:free",
12
- }
13
-
14
- APINEX_MODEL_IDS = {
15
- "claude/opus-5",
16
- "claude/sonnet-5",
17
- "deepseek/v4-flash-0731",
18
- "deepseek/v4-pro-0813",
19
- "free/deepseek-v4-flash-0731",
20
- "free/deepseek-v4-pro-0813",
21
- "free/glm-5.3-flash",
22
- "free/gemini-3.1-pro",
23
- "free/gemini-3.7-flash",
24
- "gemini/3.1-pro",
25
- "gemini/3.7-flash",
26
- "kimi/k3",
27
- "gpt/5.6-luna",
28
- "gpt/5.6-sol",
29
- "gpt/5.6-terra",
30
- "glm/5.3",
31
- "glm/5.3-flash",
32
- "grok/4.6",
33
- "qwen/3.8-max",
34
- "free/qwen-3.8-max",
35
- }
36
-
37
-
38
- import sys
39
- _IS_LEGACY_WIN = (sys.platform == "win32" and "WT_SESSION" not in os.environ)
40
- PROMPT_SYMBOL = ">" if _IS_LEGACY_WIN else "❯"
41
- ARROW_SYMBOL = "->" if _IS_LEGACY_WIN else "➔"
42
-
43
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes