superqode 0.1.5__py3-none-any.whl

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 (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1016 @@
1
+ """
2
+ SuperQode Model Database - Model pricing, features, and metadata.
3
+
4
+ Provides detailed information about LLM models including:
5
+ - Pricing (input/output per 1M tokens)
6
+ - Context window size
7
+ - Feature support (tools, vision, etc.)
8
+ - Recommendations
9
+
10
+ Usage:
11
+ from superqode.providers.models import get_model_info, MODELS
12
+
13
+ info = get_model_info("anthropic", "claude-sonnet-4")
14
+ print(f"Price: ${info.input_price}/${info.output_price} per 1M tokens")
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass, field
20
+ from enum import Enum, auto
21
+ from typing import Dict, List, Optional, Tuple
22
+
23
+
24
+ # ============================================================================
25
+ # MODEL INFO
26
+ # ============================================================================
27
+
28
+
29
+ class ModelCapability(Enum):
30
+ """Model capabilities."""
31
+
32
+ TOOLS = auto() # Function calling / tools
33
+ VISION = auto() # Image input
34
+ STREAMING = auto() # Streaming output
35
+ JSON_MODE = auto() # Structured JSON output
36
+ REASONING = auto() # Extended thinking / reasoning
37
+ CODE = auto() # Optimized for code
38
+ LONG_CONTEXT = auto() # > 100K context
39
+
40
+
41
+ @dataclass
42
+ class ModelInfo:
43
+ """Detailed model information."""
44
+
45
+ id: str # Model identifier
46
+ name: str # Human-readable name
47
+ provider: str # Provider ID
48
+
49
+ # Pricing (per 1M tokens, USD)
50
+ input_price: float = 0.0
51
+ output_price: float = 0.0
52
+
53
+ # Context
54
+ context_window: int = 128000 # Max tokens
55
+ max_output: int = 4096 # Max output tokens
56
+
57
+ # Capabilities
58
+ capabilities: List[ModelCapability] = field(default_factory=list)
59
+
60
+ # Metadata
61
+ description: str = ""
62
+ recommended_for: List[str] = field(default_factory=list)
63
+ released: str = "" # Release date
64
+
65
+ @property
66
+ def supports_tools(self) -> bool:
67
+ return ModelCapability.TOOLS in self.capabilities
68
+
69
+ @property
70
+ def supports_vision(self) -> bool:
71
+ return ModelCapability.VISION in self.capabilities
72
+
73
+ @property
74
+ def supports_reasoning(self) -> bool:
75
+ return ModelCapability.REASONING in self.capabilities
76
+
77
+ @property
78
+ def is_code_optimized(self) -> bool:
79
+ return ModelCapability.CODE in self.capabilities
80
+
81
+ @property
82
+ def price_display(self) -> str:
83
+ """Display-friendly pricing."""
84
+ if self.input_price == 0 and self.output_price == 0:
85
+ return "Free"
86
+ return f"${self.input_price:.2f}/${self.output_price:.2f}"
87
+
88
+ @property
89
+ def context_display(self) -> str:
90
+ """Display-friendly context window."""
91
+ if self.context_window >= 1000000:
92
+ return f"{self.context_window // 1000000}M"
93
+ elif self.context_window >= 1000:
94
+ return f"{self.context_window // 1000}K"
95
+ return str(self.context_window)
96
+
97
+ def estimate_cost(self, input_tokens: int, output_tokens: int) -> float:
98
+ """Estimate cost for given token counts."""
99
+ input_cost = (input_tokens / 1_000_000) * self.input_price
100
+ output_cost = (output_tokens / 1_000_000) * self.output_price
101
+ return input_cost + output_cost
102
+
103
+
104
+ # ============================================================================
105
+ # MODEL DATABASE
106
+ # ============================================================================
107
+
108
+ MODELS: Dict[str, Dict[str, ModelInfo]] = {
109
+ # =========================================================================
110
+ # ANTHROPIC
111
+ # =========================================================================
112
+ "anthropic": {
113
+ "claude-opus-4-5-20251101": ModelInfo(
114
+ id="claude-opus-4-5-20251101",
115
+ name="Claude Opus 4.5",
116
+ provider="anthropic",
117
+ input_price=15.0,
118
+ output_price=75.0,
119
+ context_window=200000,
120
+ max_output=8192,
121
+ capabilities=[
122
+ ModelCapability.TOOLS,
123
+ ModelCapability.VISION,
124
+ ModelCapability.STREAMING,
125
+ ModelCapability.JSON_MODE,
126
+ ModelCapability.REASONING,
127
+ ModelCapability.CODE,
128
+ ModelCapability.LONG_CONTEXT,
129
+ ],
130
+ description="Most capable Claude model - latest flagship",
131
+ recommended_for=["complex reasoning", "research", "difficult coding"],
132
+ released="2025-11",
133
+ ),
134
+ "claude-sonnet-4-5-20250929": ModelInfo(
135
+ id="claude-sonnet-4-5-20250929",
136
+ name="Claude Sonnet 4.5",
137
+ provider="anthropic",
138
+ input_price=3.0,
139
+ output_price=15.0,
140
+ context_window=200000,
141
+ max_output=8192,
142
+ capabilities=[
143
+ ModelCapability.TOOLS,
144
+ ModelCapability.VISION,
145
+ ModelCapability.STREAMING,
146
+ ModelCapability.JSON_MODE,
147
+ ModelCapability.CODE,
148
+ ModelCapability.LONG_CONTEXT,
149
+ ],
150
+ description="Best balance of intelligence and speed",
151
+ recommended_for=["coding", "analysis", "general"],
152
+ released="2025-09",
153
+ ),
154
+ "claude-haiku-4-5-20251001": ModelInfo(
155
+ id="claude-haiku-4-5-20251001",
156
+ name="Claude Haiku 4.5",
157
+ provider="anthropic",
158
+ input_price=0.25,
159
+ output_price=1.25,
160
+ context_window=200000,
161
+ max_output=8192,
162
+ capabilities=[
163
+ ModelCapability.TOOLS,
164
+ ModelCapability.VISION,
165
+ ModelCapability.STREAMING,
166
+ ModelCapability.JSON_MODE,
167
+ ModelCapability.LONG_CONTEXT,
168
+ ],
169
+ description="Fastest and most cost-effective",
170
+ recommended_for=["quick tasks", "high volume"],
171
+ released="2025-10",
172
+ ),
173
+ "claude-sonnet-4-20250514": ModelInfo(
174
+ id="claude-sonnet-4-20250514",
175
+ name="Claude Sonnet 4",
176
+ provider="anthropic",
177
+ input_price=3.0,
178
+ output_price=15.0,
179
+ context_window=200000,
180
+ max_output=8192,
181
+ capabilities=[
182
+ ModelCapability.TOOLS,
183
+ ModelCapability.VISION,
184
+ ModelCapability.STREAMING,
185
+ ModelCapability.JSON_MODE,
186
+ ModelCapability.CODE,
187
+ ModelCapability.LONG_CONTEXT,
188
+ ],
189
+ description="Previous Sonnet generation",
190
+ recommended_for=["coding", "analysis", "general"],
191
+ released="2025-05",
192
+ ),
193
+ "claude-opus-4-20250514": ModelInfo(
194
+ id="claude-opus-4-20250514",
195
+ name="Claude Opus 4",
196
+ provider="anthropic",
197
+ input_price=15.0,
198
+ output_price=75.0,
199
+ context_window=200000,
200
+ max_output=8192,
201
+ capabilities=[
202
+ ModelCapability.TOOLS,
203
+ ModelCapability.VISION,
204
+ ModelCapability.STREAMING,
205
+ ModelCapability.JSON_MODE,
206
+ ModelCapability.REASONING,
207
+ ModelCapability.CODE,
208
+ ModelCapability.LONG_CONTEXT,
209
+ ],
210
+ description="Previous Opus generation",
211
+ recommended_for=["complex reasoning", "research", "difficult coding"],
212
+ released="2025-05",
213
+ ),
214
+ "claude-haiku-4-20250514": ModelInfo(
215
+ id="claude-haiku-4-20250514",
216
+ name="Claude Haiku 4",
217
+ provider="anthropic",
218
+ input_price=0.25,
219
+ output_price=1.25,
220
+ context_window=200000,
221
+ max_output=8192,
222
+ capabilities=[
223
+ ModelCapability.TOOLS,
224
+ ModelCapability.VISION,
225
+ ModelCapability.STREAMING,
226
+ ModelCapability.JSON_MODE,
227
+ ModelCapability.LONG_CONTEXT,
228
+ ],
229
+ description="Previous Haiku generation",
230
+ recommended_for=["quick tasks", "high volume"],
231
+ released="2025-05",
232
+ ),
233
+ },
234
+ # =========================================================================
235
+ # OPENAI
236
+ # =========================================================================
237
+ "openai": {
238
+ "gpt-5.2": ModelInfo(
239
+ id="gpt-5.2",
240
+ name="GPT-5.2",
241
+ provider="openai",
242
+ input_price=5.0,
243
+ output_price=20.0,
244
+ context_window=256000,
245
+ max_output=32768,
246
+ capabilities=[
247
+ ModelCapability.TOOLS,
248
+ ModelCapability.VISION,
249
+ ModelCapability.STREAMING,
250
+ ModelCapability.JSON_MODE,
251
+ ModelCapability.REASONING,
252
+ ModelCapability.CODE,
253
+ ModelCapability.LONG_CONTEXT,
254
+ ],
255
+ description="Latest GPT-5 flagship with reasoning",
256
+ recommended_for=["complex reasoning", "coding", "research"],
257
+ released="2025-12",
258
+ ),
259
+ "gpt-5.2-pro": ModelInfo(
260
+ id="gpt-5.2-pro",
261
+ name="GPT-5.2 Pro",
262
+ provider="openai",
263
+ input_price=6.0,
264
+ output_price=24.0,
265
+ context_window=256000,
266
+ max_output=32768,
267
+ capabilities=[
268
+ ModelCapability.TOOLS,
269
+ ModelCapability.VISION,
270
+ ModelCapability.STREAMING,
271
+ ModelCapability.JSON_MODE,
272
+ ModelCapability.REASONING,
273
+ ModelCapability.CODE,
274
+ ModelCapability.LONG_CONTEXT,
275
+ ],
276
+ description="GPT-5.2 Pro variant - enhanced capabilities",
277
+ recommended_for=["complex reasoning", "coding", "research"],
278
+ released="2025-12",
279
+ ),
280
+ "gpt-5.2-codex": ModelInfo(
281
+ id="gpt-5.2-codex",
282
+ name="GPT-5.2 Codex",
283
+ provider="openai",
284
+ input_price=5.5,
285
+ output_price=22.0,
286
+ context_window=256000,
287
+ max_output=32768,
288
+ capabilities=[
289
+ ModelCapability.TOOLS,
290
+ ModelCapability.VISION,
291
+ ModelCapability.STREAMING,
292
+ ModelCapability.JSON_MODE,
293
+ ModelCapability.REASONING,
294
+ ModelCapability.CODE,
295
+ ModelCapability.LONG_CONTEXT,
296
+ ],
297
+ description="GPT-5.2 Codex variant - optimized for code",
298
+ recommended_for=["coding", "code generation", "code review"],
299
+ released="2025-12",
300
+ ),
301
+ "gpt-5.1": ModelInfo(
302
+ id="gpt-5.1",
303
+ name="GPT-5.1",
304
+ provider="openai",
305
+ input_price=4.0,
306
+ output_price=16.0,
307
+ context_window=200000,
308
+ max_output=32768,
309
+ capabilities=[
310
+ ModelCapability.TOOLS,
311
+ ModelCapability.VISION,
312
+ ModelCapability.STREAMING,
313
+ ModelCapability.JSON_MODE,
314
+ ModelCapability.REASONING,
315
+ ModelCapability.CODE,
316
+ ModelCapability.LONG_CONTEXT,
317
+ ],
318
+ description="GPT-5 series - highly capable",
319
+ recommended_for=["general", "coding", "analysis"],
320
+ released="2025-11",
321
+ ),
322
+ "gpt-5.1-codex": ModelInfo(
323
+ id="gpt-5.1-codex",
324
+ name="GPT-5.1 Codex",
325
+ provider="openai",
326
+ input_price=4.5,
327
+ output_price=18.0,
328
+ context_window=200000,
329
+ max_output=32768,
330
+ capabilities=[
331
+ ModelCapability.TOOLS,
332
+ ModelCapability.VISION,
333
+ ModelCapability.STREAMING,
334
+ ModelCapability.JSON_MODE,
335
+ ModelCapability.REASONING,
336
+ ModelCapability.CODE,
337
+ ModelCapability.LONG_CONTEXT,
338
+ ],
339
+ description="GPT-5.1 Codex variant - optimized for code",
340
+ recommended_for=["coding", "code generation"],
341
+ released="2025-11",
342
+ ),
343
+ "gpt-5.1-codex-mini": ModelInfo(
344
+ id="gpt-5.1-codex-mini",
345
+ name="GPT-5.1 Codex Mini",
346
+ provider="openai",
347
+ input_price=2.0,
348
+ output_price=8.0,
349
+ context_window=200000,
350
+ max_output=16384,
351
+ capabilities=[
352
+ ModelCapability.TOOLS,
353
+ ModelCapability.VISION,
354
+ ModelCapability.STREAMING,
355
+ ModelCapability.JSON_MODE,
356
+ ModelCapability.CODE,
357
+ ModelCapability.LONG_CONTEXT,
358
+ ],
359
+ description="GPT-5.1 Codex Mini - fast and efficient for code",
360
+ recommended_for=["quick coding", "code completion"],
361
+ released="2025-11",
362
+ ),
363
+ "gpt-4o-2024-11-20": ModelInfo(
364
+ id="gpt-4o-2024-11-20",
365
+ name="GPT-4o (Nov 2024)",
366
+ provider="openai",
367
+ input_price=2.50,
368
+ output_price=10.0,
369
+ context_window=128000,
370
+ max_output=16384,
371
+ capabilities=[
372
+ ModelCapability.TOOLS,
373
+ ModelCapability.VISION,
374
+ ModelCapability.STREAMING,
375
+ ModelCapability.JSON_MODE,
376
+ ModelCapability.CODE,
377
+ ModelCapability.LONG_CONTEXT,
378
+ ],
379
+ description="GPT-4o latest stable version",
380
+ recommended_for=["general", "coding", "vision"],
381
+ released="2024-11",
382
+ ),
383
+ "gpt-4o": ModelInfo(
384
+ id="gpt-4o",
385
+ name="GPT-4o",
386
+ provider="openai",
387
+ input_price=2.50,
388
+ output_price=10.0,
389
+ context_window=128000,
390
+ max_output=16384,
391
+ capabilities=[
392
+ ModelCapability.TOOLS,
393
+ ModelCapability.VISION,
394
+ ModelCapability.STREAMING,
395
+ ModelCapability.JSON_MODE,
396
+ ModelCapability.CODE,
397
+ ModelCapability.LONG_CONTEXT,
398
+ ],
399
+ description="Most capable GPT-4 variant",
400
+ recommended_for=["general", "coding", "vision"],
401
+ released="2024-05",
402
+ ),
403
+ "gpt-4o-mini": ModelInfo(
404
+ id="gpt-4o-mini",
405
+ name="GPT-4o Mini",
406
+ provider="openai",
407
+ input_price=0.15,
408
+ output_price=0.60,
409
+ context_window=128000,
410
+ max_output=16384,
411
+ capabilities=[
412
+ ModelCapability.TOOLS,
413
+ ModelCapability.VISION,
414
+ ModelCapability.STREAMING,
415
+ ModelCapability.JSON_MODE,
416
+ ModelCapability.LONG_CONTEXT,
417
+ ],
418
+ description="Fast and cost-effective",
419
+ recommended_for=["quick tasks", "high volume"],
420
+ released="2024-07",
421
+ ),
422
+ "o1": ModelInfo(
423
+ id="o1",
424
+ name="o1",
425
+ provider="openai",
426
+ input_price=15.0,
427
+ output_price=60.0,
428
+ context_window=200000,
429
+ max_output=100000,
430
+ capabilities=[
431
+ ModelCapability.REASONING,
432
+ ModelCapability.CODE,
433
+ ModelCapability.LONG_CONTEXT,
434
+ ],
435
+ description="Advanced reasoning model",
436
+ recommended_for=["complex reasoning", "math", "science"],
437
+ released="2024-09",
438
+ ),
439
+ "o1-mini": ModelInfo(
440
+ id="o1-mini",
441
+ name="o1-mini",
442
+ provider="openai",
443
+ input_price=3.0,
444
+ output_price=12.0,
445
+ context_window=128000,
446
+ max_output=65536,
447
+ capabilities=[
448
+ ModelCapability.REASONING,
449
+ ModelCapability.CODE,
450
+ ModelCapability.LONG_CONTEXT,
451
+ ],
452
+ description="Smaller reasoning model",
453
+ recommended_for=["coding", "math"],
454
+ released="2024-09",
455
+ ),
456
+ },
457
+ # =========================================================================
458
+ # GOOGLE
459
+ # =========================================================================
460
+ "google": {
461
+ "gemini-3-pro-preview": ModelInfo(
462
+ id="gemini-3-pro-preview",
463
+ name="Gemini 3 Pro Preview",
464
+ provider="google",
465
+ input_price=2.0,
466
+ output_price=8.0,
467
+ context_window=2000000,
468
+ max_output=16384,
469
+ capabilities=[
470
+ ModelCapability.TOOLS,
471
+ ModelCapability.VISION,
472
+ ModelCapability.STREAMING,
473
+ ModelCapability.JSON_MODE,
474
+ ModelCapability.REASONING,
475
+ ModelCapability.CODE,
476
+ ModelCapability.LONG_CONTEXT,
477
+ ],
478
+ description="Latest Gemini 3 flagship - most capable (2M context)",
479
+ recommended_for=["complex reasoning", "large codebases", "research"],
480
+ released="2025-12",
481
+ ),
482
+ "gemini-3-flash-preview": ModelInfo(
483
+ id="gemini-3-flash-preview",
484
+ name="Gemini 3 Flash Preview",
485
+ provider="google",
486
+ input_price=0.15,
487
+ output_price=0.60,
488
+ context_window=1000000,
489
+ max_output=8192,
490
+ capabilities=[
491
+ ModelCapability.TOOLS,
492
+ ModelCapability.VISION,
493
+ ModelCapability.STREAMING,
494
+ ModelCapability.JSON_MODE,
495
+ ModelCapability.CODE,
496
+ ModelCapability.LONG_CONTEXT,
497
+ ],
498
+ description="Gemini 3 fast model - 1M context (Latest)",
499
+ recommended_for=["quick tasks", "high volume", "coding"],
500
+ released="2025-12",
501
+ ),
502
+ "gemini-2.5-pro": ModelInfo(
503
+ id="gemini-2.5-pro",
504
+ name="Gemini 2.5 Pro",
505
+ provider="google",
506
+ input_price=1.25,
507
+ output_price=5.0,
508
+ context_window=2000000,
509
+ max_output=8192,
510
+ capabilities=[
511
+ ModelCapability.TOOLS,
512
+ ModelCapability.VISION,
513
+ ModelCapability.STREAMING,
514
+ ModelCapability.JSON_MODE,
515
+ ModelCapability.REASONING,
516
+ ModelCapability.CODE,
517
+ ModelCapability.LONG_CONTEXT,
518
+ ],
519
+ description="Gemini 2.5 Pro with 2M context",
520
+ recommended_for=["large codebases", "long documents"],
521
+ released="2025-01",
522
+ ),
523
+ "gemini-2.5-flash": ModelInfo(
524
+ id="gemini-2.5-flash",
525
+ name="Gemini 2.5 Flash",
526
+ provider="google",
527
+ input_price=0.075,
528
+ output_price=0.30,
529
+ context_window=1000000,
530
+ max_output=8192,
531
+ capabilities=[
532
+ ModelCapability.TOOLS,
533
+ ModelCapability.VISION,
534
+ ModelCapability.STREAMING,
535
+ ModelCapability.JSON_MODE,
536
+ ModelCapability.CODE,
537
+ ModelCapability.LONG_CONTEXT,
538
+ ],
539
+ description="Fast and efficient with 1M context",
540
+ recommended_for=["quick tasks", "high volume"],
541
+ released="2025-01",
542
+ ),
543
+ "gemini-2.0-flash": ModelInfo(
544
+ id="gemini-2.0-flash",
545
+ name="Gemini 2.0 Flash",
546
+ provider="google",
547
+ input_price=0.10,
548
+ output_price=0.40,
549
+ context_window=1000000,
550
+ max_output=8192,
551
+ capabilities=[
552
+ ModelCapability.TOOLS,
553
+ ModelCapability.VISION,
554
+ ModelCapability.STREAMING,
555
+ ModelCapability.JSON_MODE,
556
+ ModelCapability.CODE,
557
+ ModelCapability.LONG_CONTEXT,
558
+ ],
559
+ description="Previous Flash generation",
560
+ recommended_for=["general tasks"],
561
+ released="2024-12",
562
+ ),
563
+ },
564
+ # =========================================================================
565
+ # DEEPSEEK
566
+ # =========================================================================
567
+ "deepseek": {
568
+ "deepseek-ai/DeepSeek-V3.2": ModelInfo(
569
+ id="deepseek-ai/DeepSeek-V3.2",
570
+ name="DeepSeek V3.2",
571
+ provider="deepseek",
572
+ input_price=0.27,
573
+ output_price=1.10,
574
+ context_window=128000,
575
+ max_output=16384,
576
+ capabilities=[
577
+ ModelCapability.TOOLS,
578
+ ModelCapability.STREAMING,
579
+ ModelCapability.JSON_MODE,
580
+ ModelCapability.REASONING,
581
+ ModelCapability.CODE,
582
+ ModelCapability.LONG_CONTEXT,
583
+ ],
584
+ description="Latest DeepSeek V3.2 - most capable",
585
+ recommended_for=["complex reasoning", "coding", "research"],
586
+ released="2025-12",
587
+ ),
588
+ "deepseek-ai/DeepSeek-R1": ModelInfo(
589
+ id="deepseek-ai/DeepSeek-R1",
590
+ name="DeepSeek R1",
591
+ provider="deepseek",
592
+ input_price=0.55,
593
+ output_price=2.19,
594
+ context_window=64000,
595
+ max_output=8192,
596
+ capabilities=[
597
+ ModelCapability.STREAMING,
598
+ ModelCapability.REASONING,
599
+ ModelCapability.CODE,
600
+ ],
601
+ description="Advanced reasoning model - R1 series",
602
+ recommended_for=["complex reasoning", "math", "coding"],
603
+ released="2025-01",
604
+ ),
605
+ "deepseek-chat": ModelInfo(
606
+ id="deepseek-chat",
607
+ name="DeepSeek Chat (V3)",
608
+ provider="deepseek",
609
+ input_price=0.14,
610
+ output_price=0.28,
611
+ context_window=64000,
612
+ max_output=8192,
613
+ capabilities=[
614
+ ModelCapability.TOOLS,
615
+ ModelCapability.STREAMING,
616
+ ModelCapability.JSON_MODE,
617
+ ModelCapability.CODE,
618
+ ],
619
+ description="Very cost-effective general model",
620
+ recommended_for=["general", "budget-conscious"],
621
+ released="2024-12",
622
+ ),
623
+ "deepseek-coder": ModelInfo(
624
+ id="deepseek-coder",
625
+ name="DeepSeek Coder",
626
+ provider="deepseek",
627
+ input_price=0.14,
628
+ output_price=0.28,
629
+ context_window=64000,
630
+ max_output=8192,
631
+ capabilities=[
632
+ ModelCapability.TOOLS,
633
+ ModelCapability.STREAMING,
634
+ ModelCapability.JSON_MODE,
635
+ ModelCapability.CODE,
636
+ ],
637
+ description="Specialized for coding tasks",
638
+ recommended_for=["coding", "code review"],
639
+ released="2024-12",
640
+ ),
641
+ "deepseek-reasoner": ModelInfo(
642
+ id="deepseek-reasoner",
643
+ name="DeepSeek Reasoner",
644
+ provider="deepseek",
645
+ input_price=0.55,
646
+ output_price=2.19,
647
+ context_window=64000,
648
+ max_output=8192,
649
+ capabilities=[
650
+ ModelCapability.STREAMING,
651
+ ModelCapability.REASONING,
652
+ ModelCapability.CODE,
653
+ ],
654
+ description="Advanced reasoning model",
655
+ recommended_for=["complex reasoning", "math"],
656
+ released="2025-01",
657
+ ),
658
+ },
659
+ # =========================================================================
660
+ # GROQ
661
+ # =========================================================================
662
+ "groq": {
663
+ "llama-3.3-70b-versatile": ModelInfo(
664
+ id="llama-3.3-70b-versatile",
665
+ name="Llama 3.3 70B",
666
+ provider="groq",
667
+ input_price=0.59,
668
+ output_price=0.79,
669
+ context_window=128000,
670
+ max_output=32768,
671
+ capabilities=[
672
+ ModelCapability.TOOLS,
673
+ ModelCapability.STREAMING,
674
+ ModelCapability.JSON_MODE,
675
+ ModelCapability.CODE,
676
+ ModelCapability.LONG_CONTEXT,
677
+ ],
678
+ description="Very fast inference via Groq LPU",
679
+ recommended_for=["speed-critical", "coding"],
680
+ released="2024-12",
681
+ ),
682
+ "llama-3.1-8b-instant": ModelInfo(
683
+ id="llama-3.1-8b-instant",
684
+ name="Llama 3.1 8B Instant",
685
+ provider="groq",
686
+ input_price=0.05,
687
+ output_price=0.08,
688
+ context_window=128000,
689
+ max_output=8192,
690
+ capabilities=[
691
+ ModelCapability.TOOLS,
692
+ ModelCapability.STREAMING,
693
+ ModelCapability.LONG_CONTEXT,
694
+ ],
695
+ description="Ultra-fast small model",
696
+ recommended_for=["quick tasks", "prototyping"],
697
+ released="2024-07",
698
+ ),
699
+ },
700
+ # =========================================================================
701
+ # OPENROUTER
702
+ # =========================================================================
703
+ "openrouter": {
704
+ "anthropic/claude-sonnet-4": ModelInfo(
705
+ id="anthropic/claude-sonnet-4",
706
+ name="Claude Sonnet 4 (via OpenRouter)",
707
+ provider="openrouter",
708
+ input_price=3.0,
709
+ output_price=15.0,
710
+ context_window=200000,
711
+ max_output=8192,
712
+ capabilities=[
713
+ ModelCapability.TOOLS,
714
+ ModelCapability.VISION,
715
+ ModelCapability.STREAMING,
716
+ ModelCapability.CODE,
717
+ ModelCapability.LONG_CONTEXT,
718
+ ],
719
+ description="Claude via OpenRouter",
720
+ recommended_for=["coding", "general"],
721
+ ),
722
+ "openai/gpt-4o": ModelInfo(
723
+ id="openai/gpt-4o",
724
+ name="GPT-4o (via OpenRouter)",
725
+ provider="openrouter",
726
+ input_price=2.50,
727
+ output_price=10.0,
728
+ context_window=128000,
729
+ max_output=16384,
730
+ capabilities=[
731
+ ModelCapability.TOOLS,
732
+ ModelCapability.VISION,
733
+ ModelCapability.STREAMING,
734
+ ModelCapability.CODE,
735
+ ModelCapability.LONG_CONTEXT,
736
+ ],
737
+ description="GPT-4o via OpenRouter",
738
+ recommended_for=["general", "coding"],
739
+ ),
740
+ "google/gemini-2.0-flash": ModelInfo(
741
+ id="google/gemini-2.0-flash",
742
+ name="Gemini 2.0 Flash (via OpenRouter)",
743
+ provider="openrouter",
744
+ input_price=0.10,
745
+ output_price=0.40,
746
+ context_window=1000000,
747
+ max_output=8192,
748
+ capabilities=[
749
+ ModelCapability.TOOLS,
750
+ ModelCapability.VISION,
751
+ ModelCapability.STREAMING,
752
+ ModelCapability.LONG_CONTEXT,
753
+ ],
754
+ description="Gemini via OpenRouter",
755
+ recommended_for=["long context"],
756
+ ),
757
+ },
758
+ # =========================================================================
759
+ # OLLAMA (Local - Free)
760
+ # =========================================================================
761
+ "ollama": {
762
+ "llama3.2:3b": ModelInfo(
763
+ id="llama3.2:3b",
764
+ name="Llama 3.2 3B",
765
+ provider="ollama",
766
+ input_price=0.0,
767
+ output_price=0.0,
768
+ context_window=128000,
769
+ max_output=4096,
770
+ capabilities=[
771
+ ModelCapability.STREAMING,
772
+ ],
773
+ description="Small local model",
774
+ recommended_for=["quick local tasks"],
775
+ ),
776
+ "qwen2.5-coder:7b": ModelInfo(
777
+ id="qwen2.5-coder:7b",
778
+ name="Qwen 2.5 Coder 7B",
779
+ provider="ollama",
780
+ input_price=0.0,
781
+ output_price=0.0,
782
+ context_window=32000,
783
+ max_output=4096,
784
+ capabilities=[
785
+ ModelCapability.STREAMING,
786
+ ModelCapability.CODE,
787
+ ],
788
+ description="Local coding model",
789
+ recommended_for=["local coding"],
790
+ ),
791
+ "qwen2.5-coder:32b": ModelInfo(
792
+ id="qwen2.5-coder:32b",
793
+ name="Qwen 2.5 Coder 32B",
794
+ provider="ollama",
795
+ input_price=0.0,
796
+ output_price=0.0,
797
+ context_window=32000,
798
+ max_output=4096,
799
+ capabilities=[
800
+ ModelCapability.STREAMING,
801
+ ModelCapability.CODE,
802
+ ],
803
+ description="Larger local coding model",
804
+ recommended_for=["local coding", "complex tasks"],
805
+ ),
806
+ },
807
+ }
808
+
809
+
810
+ # ============================================================================
811
+ # LIVE DATA INTEGRATION (models.dev)
812
+ # ============================================================================
813
+
814
+ # Flag to track if live data is available
815
+ _live_models: Optional[Dict[str, Dict[str, ModelInfo]]] = None
816
+ _use_live_data: bool = False
817
+
818
+
819
+ def set_live_models(models: Dict[str, Dict[str, ModelInfo]]) -> None:
820
+ """
821
+ Set live model data from models.dev.
822
+
823
+ Called by the models_dev module after fetching fresh data.
824
+ """
825
+ global _live_models, _use_live_data
826
+ _live_models = models
827
+ _use_live_data = True
828
+
829
+
830
+ def get_effective_models() -> Dict[str, Dict[str, ModelInfo]]:
831
+ """
832
+ Get the effective models database.
833
+
834
+ Returns live data if available, otherwise falls back to hardcoded MODELS.
835
+ """
836
+ if _use_live_data and _live_models:
837
+ # Merge: live data takes precedence, but keep hardcoded for missing providers
838
+ merged = MODELS.copy()
839
+ for provider_id, models in _live_models.items():
840
+ if provider_id in merged:
841
+ # Merge models within provider (live takes precedence)
842
+ merged[provider_id] = {**merged[provider_id], **models}
843
+ else:
844
+ merged[provider_id] = models
845
+ return merged
846
+ return MODELS
847
+
848
+
849
+ # ============================================================================
850
+ # HELPER FUNCTIONS
851
+ # ============================================================================
852
+
853
+
854
+ def get_model_info(provider_id: str, model_id: str) -> Optional[ModelInfo]:
855
+ """Get model info by provider and model ID."""
856
+ models = get_effective_models()
857
+ provider_models = models.get(provider_id, {})
858
+
859
+ # Try exact match first
860
+ if model_id in provider_models:
861
+ return provider_models[model_id]
862
+
863
+ # Try fuzzy match (partial ID match)
864
+ for mid, info in provider_models.items():
865
+ if model_id in mid or mid in model_id:
866
+ return info
867
+
868
+ return None
869
+
870
+
871
+ def get_models_for_provider(provider_id: str) -> Dict[str, ModelInfo]:
872
+ """Get all models for a provider, filtered to ensure they belong to that provider."""
873
+ models = get_effective_models().get(provider_id, {})
874
+
875
+ # Filter models to ensure they actually belong to this provider
876
+ # This prevents models from other providers (e.g., GPT OSS) from appearing in Google's list
877
+ filtered = {}
878
+ for model_id, model_info in models.items():
879
+ # Ensure the model's provider field matches the requested provider
880
+ if model_info.provider == provider_id:
881
+ filtered[model_id] = model_info
882
+ # Special case for Google: only include Gemini models
883
+ elif provider_id == "google":
884
+ model_id_lower = model_id.lower()
885
+ model_name_lower = model_info.name.lower() if model_info.name else ""
886
+ # Only include if it's clearly a Gemini model
887
+ if (
888
+ ("gemini" in model_id_lower or "gemini" in model_name_lower)
889
+ and "gpt" not in model_id_lower
890
+ and "gpt" not in model_name_lower
891
+ ):
892
+ filtered[model_id] = model_info
893
+
894
+ return filtered
895
+
896
+
897
+ def get_all_models() -> List[ModelInfo]:
898
+ """Get all models across all providers."""
899
+ all_models = []
900
+ for provider_models in get_effective_models().values():
901
+ all_models.extend(provider_models.values())
902
+ return all_models
903
+
904
+
905
+ def get_all_providers() -> List[str]:
906
+ """Get all available provider IDs."""
907
+ return list(get_effective_models().keys())
908
+
909
+
910
+ def get_cheapest_models(limit: int = 5) -> List[ModelInfo]:
911
+ """Get the cheapest models by input price."""
912
+ all_models = get_all_models()
913
+ sorted_models = sorted(all_models, key=lambda m: m.input_price)
914
+ return sorted_models[:limit]
915
+
916
+
917
+ def get_models_with_capability(capability: ModelCapability) -> List[ModelInfo]:
918
+ """Get models that have a specific capability."""
919
+ return [m for m in get_all_models() if capability in m.capabilities]
920
+
921
+
922
+ def get_recommended_for_coding() -> List[ModelInfo]:
923
+ """Get models recommended for coding."""
924
+ return [m for m in get_all_models() if "coding" in m.recommended_for or m.is_code_optimized]
925
+
926
+
927
+ def search_models(query: str, limit: int = 20) -> List[ModelInfo]:
928
+ """
929
+ Search models by name, ID, or provider.
930
+
931
+ Args:
932
+ query: Search string (case-insensitive)
933
+ limit: Maximum results to return
934
+ """
935
+ query_lower = query.lower()
936
+ results = []
937
+
938
+ for model in get_all_models():
939
+ score = 0
940
+ # Exact ID match
941
+ if query_lower == model.id.lower():
942
+ score = 100
943
+ # ID contains query
944
+ elif query_lower in model.id.lower():
945
+ score = 80
946
+ # Name contains query
947
+ elif query_lower in model.name.lower():
948
+ score = 60
949
+ # Provider contains query
950
+ elif query_lower in model.provider.lower():
951
+ score = 40
952
+ # Description contains query
953
+ elif model.description and query_lower in model.description.lower():
954
+ score = 20
955
+
956
+ if score > 0:
957
+ results.append((score, model))
958
+
959
+ # Sort by score descending, then by name
960
+ results.sort(key=lambda x: (-x[0], x[1].name))
961
+ return [model for _, model in results[:limit]]
962
+
963
+
964
+ def estimate_session_cost(
965
+ provider_id: str,
966
+ model_id: str,
967
+ input_tokens: int,
968
+ output_tokens: int,
969
+ ) -> Tuple[float, str]:
970
+ """
971
+ Estimate cost for a session.
972
+
973
+ Returns:
974
+ (cost, formatted_string)
975
+ """
976
+ info = get_model_info(provider_id, model_id)
977
+ if info:
978
+ cost = info.estimate_cost(input_tokens, output_tokens)
979
+ return cost, f"${cost:.4f}"
980
+ return 0.0, "Unknown"
981
+
982
+
983
+ def is_using_live_data() -> bool:
984
+ """Check if live models.dev data is being used."""
985
+ return _use_live_data and _live_models is not None
986
+
987
+
988
+ def get_data_source() -> str:
989
+ """Get a description of the current data source."""
990
+ if is_using_live_data():
991
+ return "models.dev (live)"
992
+ return "built-in (offline)"
993
+
994
+
995
+ # ============================================================================
996
+ # EXPORTS
997
+ # ============================================================================
998
+
999
+ __all__ = [
1000
+ "ModelInfo",
1001
+ "ModelCapability",
1002
+ "MODELS",
1003
+ "get_model_info",
1004
+ "get_models_for_provider",
1005
+ "get_all_models",
1006
+ "get_all_providers",
1007
+ "get_cheapest_models",
1008
+ "get_models_with_capability",
1009
+ "get_recommended_for_coding",
1010
+ "search_models",
1011
+ "estimate_session_cost",
1012
+ "set_live_models",
1013
+ "get_effective_models",
1014
+ "is_using_live_data",
1015
+ "get_data_source",
1016
+ ]