agentpool 2.1.9__py3-none-any.whl → 2.5.0__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 (311) hide show
  1. acp/__init__.py +13 -4
  2. acp/acp_requests.py +20 -77
  3. acp/agent/connection.py +8 -0
  4. acp/agent/implementations/debug_server/debug_server.py +6 -2
  5. acp/agent/protocol.py +6 -0
  6. acp/bridge/README.md +15 -2
  7. acp/bridge/__init__.py +3 -2
  8. acp/bridge/__main__.py +60 -19
  9. acp/bridge/ws_server.py +173 -0
  10. acp/bridge/ws_server_cli.py +89 -0
  11. acp/client/connection.py +38 -29
  12. acp/client/implementations/default_client.py +3 -2
  13. acp/client/implementations/headless_client.py +2 -2
  14. acp/connection.py +2 -2
  15. acp/notifications.py +20 -50
  16. acp/schema/__init__.py +2 -0
  17. acp/schema/agent_responses.py +21 -0
  18. acp/schema/client_requests.py +3 -3
  19. acp/schema/session_state.py +63 -29
  20. acp/stdio.py +39 -9
  21. acp/task/supervisor.py +2 -2
  22. acp/transports.py +362 -2
  23. acp/utils.py +17 -4
  24. agentpool/__init__.py +6 -1
  25. agentpool/agents/__init__.py +2 -0
  26. agentpool/agents/acp_agent/acp_agent.py +407 -277
  27. agentpool/agents/acp_agent/acp_converters.py +196 -38
  28. agentpool/agents/acp_agent/client_handler.py +191 -26
  29. agentpool/agents/acp_agent/session_state.py +17 -6
  30. agentpool/agents/agent.py +607 -572
  31. agentpool/agents/agui_agent/__init__.py +0 -2
  32. agentpool/agents/agui_agent/agui_agent.py +176 -110
  33. agentpool/agents/agui_agent/agui_converters.py +0 -131
  34. agentpool/agents/agui_agent/helpers.py +3 -4
  35. agentpool/agents/base_agent.py +632 -17
  36. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  37. agentpool/agents/claude_code_agent/__init__.py +13 -1
  38. agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
  39. agentpool/agents/claude_code_agent/converters.py +74 -143
  40. agentpool/agents/claude_code_agent/history.py +474 -0
  41. agentpool/agents/claude_code_agent/models.py +77 -0
  42. agentpool/agents/claude_code_agent/static_info.py +100 -0
  43. agentpool/agents/claude_code_agent/usage.py +242 -0
  44. agentpool/agents/context.py +40 -0
  45. agentpool/agents/events/__init__.py +24 -0
  46. agentpool/agents/events/builtin_handlers.py +67 -1
  47. agentpool/agents/events/event_emitter.py +32 -2
  48. agentpool/agents/events/events.py +104 -3
  49. agentpool/agents/events/infer_info.py +145 -0
  50. agentpool/agents/events/processors.py +254 -0
  51. agentpool/agents/interactions.py +41 -6
  52. agentpool/agents/modes.py +67 -0
  53. agentpool/agents/slashed_agent.py +5 -4
  54. agentpool/agents/tool_call_accumulator.py +213 -0
  55. agentpool/agents/tool_wrapping.py +18 -6
  56. agentpool/common_types.py +56 -21
  57. agentpool/config_resources/__init__.py +38 -1
  58. agentpool/config_resources/acp_assistant.yml +2 -2
  59. agentpool/config_resources/agents.yml +3 -0
  60. agentpool/config_resources/agents_template.yml +1 -0
  61. agentpool/config_resources/claude_code_agent.yml +10 -6
  62. agentpool/config_resources/external_acp_agents.yml +2 -1
  63. agentpool/delegation/base_team.py +4 -30
  64. agentpool/delegation/pool.py +136 -289
  65. agentpool/delegation/team.py +58 -57
  66. agentpool/delegation/teamrun.py +51 -55
  67. agentpool/diagnostics/__init__.py +53 -0
  68. agentpool/diagnostics/lsp_manager.py +1593 -0
  69. agentpool/diagnostics/lsp_proxy.py +41 -0
  70. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  71. agentpool/diagnostics/models.py +398 -0
  72. agentpool/functional/run.py +10 -4
  73. agentpool/mcp_server/__init__.py +0 -2
  74. agentpool/mcp_server/client.py +76 -32
  75. agentpool/mcp_server/conversions.py +54 -13
  76. agentpool/mcp_server/manager.py +34 -54
  77. agentpool/mcp_server/registries/official_registry_client.py +35 -1
  78. agentpool/mcp_server/tool_bridge.py +186 -139
  79. agentpool/messaging/__init__.py +0 -2
  80. agentpool/messaging/compaction.py +72 -197
  81. agentpool/messaging/connection_manager.py +11 -10
  82. agentpool/messaging/event_manager.py +5 -5
  83. agentpool/messaging/message_container.py +6 -30
  84. agentpool/messaging/message_history.py +99 -8
  85. agentpool/messaging/messagenode.py +52 -14
  86. agentpool/messaging/messages.py +54 -35
  87. agentpool/messaging/processing.py +12 -22
  88. agentpool/models/__init__.py +1 -1
  89. agentpool/models/acp_agents/base.py +6 -24
  90. agentpool/models/acp_agents/mcp_capable.py +126 -157
  91. agentpool/models/acp_agents/non_mcp.py +129 -95
  92. agentpool/models/agents.py +98 -76
  93. agentpool/models/agui_agents.py +1 -1
  94. agentpool/models/claude_code_agents.py +144 -19
  95. agentpool/models/file_parsing.py +0 -1
  96. agentpool/models/manifest.py +113 -50
  97. agentpool/prompts/conversion_manager.py +1 -1
  98. agentpool/prompts/prompts.py +5 -2
  99. agentpool/repomap.py +1 -1
  100. agentpool/resource_providers/__init__.py +11 -1
  101. agentpool/resource_providers/aggregating.py +56 -5
  102. agentpool/resource_providers/base.py +70 -4
  103. agentpool/resource_providers/codemode/code_executor.py +72 -5
  104. agentpool/resource_providers/codemode/helpers.py +2 -2
  105. agentpool/resource_providers/codemode/provider.py +64 -12
  106. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  107. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  108. agentpool/resource_providers/filtering.py +3 -1
  109. agentpool/resource_providers/mcp_provider.py +89 -12
  110. agentpool/resource_providers/plan_provider.py +228 -46
  111. agentpool/resource_providers/pool.py +7 -3
  112. agentpool/resource_providers/resource_info.py +111 -0
  113. agentpool/resource_providers/static.py +4 -2
  114. agentpool/sessions/__init__.py +4 -1
  115. agentpool/sessions/manager.py +33 -5
  116. agentpool/sessions/models.py +59 -6
  117. agentpool/sessions/protocol.py +28 -0
  118. agentpool/sessions/session.py +11 -55
  119. agentpool/skills/registry.py +13 -8
  120. agentpool/storage/manager.py +572 -49
  121. agentpool/talk/registry.py +4 -4
  122. agentpool/talk/talk.py +9 -10
  123. agentpool/testing.py +538 -20
  124. agentpool/tool_impls/__init__.py +6 -0
  125. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  126. agentpool/tool_impls/agent_cli/tool.py +95 -0
  127. agentpool/tool_impls/bash/__init__.py +64 -0
  128. agentpool/tool_impls/bash/helpers.py +35 -0
  129. agentpool/tool_impls/bash/tool.py +171 -0
  130. agentpool/tool_impls/delete_path/__init__.py +70 -0
  131. agentpool/tool_impls/delete_path/tool.py +142 -0
  132. agentpool/tool_impls/download_file/__init__.py +80 -0
  133. agentpool/tool_impls/download_file/tool.py +183 -0
  134. agentpool/tool_impls/execute_code/__init__.py +55 -0
  135. agentpool/tool_impls/execute_code/tool.py +163 -0
  136. agentpool/tool_impls/grep/__init__.py +80 -0
  137. agentpool/tool_impls/grep/tool.py +200 -0
  138. agentpool/tool_impls/list_directory/__init__.py +73 -0
  139. agentpool/tool_impls/list_directory/tool.py +197 -0
  140. agentpool/tool_impls/question/__init__.py +42 -0
  141. agentpool/tool_impls/question/tool.py +127 -0
  142. agentpool/tool_impls/read/__init__.py +104 -0
  143. agentpool/tool_impls/read/tool.py +305 -0
  144. agentpool/tools/__init__.py +2 -1
  145. agentpool/tools/base.py +114 -34
  146. agentpool/tools/manager.py +57 -1
  147. agentpool/ui/base.py +2 -2
  148. agentpool/ui/mock_provider.py +2 -2
  149. agentpool/ui/stdlib_provider.py +2 -2
  150. agentpool/utils/file_watcher.py +269 -0
  151. agentpool/utils/identifiers.py +121 -0
  152. agentpool/utils/pydantic_ai_helpers.py +46 -0
  153. agentpool/utils/streams.py +616 -2
  154. agentpool/utils/subprocess_utils.py +155 -0
  155. agentpool/utils/token_breakdown.py +461 -0
  156. agentpool/vfs_registry.py +7 -2
  157. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
  158. agentpool-2.5.0.dist-info/RECORD +579 -0
  159. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  160. agentpool_cli/__main__.py +24 -0
  161. agentpool_cli/create.py +1 -1
  162. agentpool_cli/serve_acp.py +100 -21
  163. agentpool_cli/serve_agui.py +87 -0
  164. agentpool_cli/serve_opencode.py +119 -0
  165. agentpool_cli/ui.py +557 -0
  166. agentpool_commands/__init__.py +42 -5
  167. agentpool_commands/agents.py +75 -2
  168. agentpool_commands/history.py +62 -0
  169. agentpool_commands/mcp.py +176 -0
  170. agentpool_commands/models.py +56 -3
  171. agentpool_commands/pool.py +260 -0
  172. agentpool_commands/session.py +1 -1
  173. agentpool_commands/text_sharing/__init__.py +119 -0
  174. agentpool_commands/text_sharing/base.py +123 -0
  175. agentpool_commands/text_sharing/github_gist.py +80 -0
  176. agentpool_commands/text_sharing/opencode.py +462 -0
  177. agentpool_commands/text_sharing/paste_rs.py +59 -0
  178. agentpool_commands/text_sharing/pastebin.py +116 -0
  179. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  180. agentpool_commands/tools.py +57 -0
  181. agentpool_commands/utils.py +80 -30
  182. agentpool_config/__init__.py +30 -2
  183. agentpool_config/agentpool_tools.py +498 -0
  184. agentpool_config/builtin_tools.py +77 -22
  185. agentpool_config/commands.py +24 -1
  186. agentpool_config/compaction.py +258 -0
  187. agentpool_config/converters.py +1 -1
  188. agentpool_config/event_handlers.py +42 -0
  189. agentpool_config/events.py +1 -1
  190. agentpool_config/forward_targets.py +1 -4
  191. agentpool_config/jinja.py +3 -3
  192. agentpool_config/mcp_server.py +132 -6
  193. agentpool_config/nodes.py +1 -1
  194. agentpool_config/observability.py +44 -0
  195. agentpool_config/session.py +0 -3
  196. agentpool_config/storage.py +82 -38
  197. agentpool_config/task.py +3 -3
  198. agentpool_config/tools.py +11 -22
  199. agentpool_config/toolsets.py +109 -233
  200. agentpool_server/a2a_server/agent_worker.py +307 -0
  201. agentpool_server/a2a_server/server.py +23 -18
  202. agentpool_server/acp_server/acp_agent.py +234 -181
  203. agentpool_server/acp_server/commands/acp_commands.py +151 -156
  204. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
  205. agentpool_server/acp_server/event_converter.py +651 -0
  206. agentpool_server/acp_server/input_provider.py +53 -10
  207. agentpool_server/acp_server/server.py +24 -90
  208. agentpool_server/acp_server/session.py +173 -331
  209. agentpool_server/acp_server/session_manager.py +8 -34
  210. agentpool_server/agui_server/server.py +3 -1
  211. agentpool_server/mcp_server/server.py +5 -2
  212. agentpool_server/opencode_server/.rules +95 -0
  213. agentpool_server/opencode_server/ENDPOINTS.md +401 -0
  214. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  215. agentpool_server/opencode_server/__init__.py +19 -0
  216. agentpool_server/opencode_server/command_validation.py +172 -0
  217. agentpool_server/opencode_server/converters.py +975 -0
  218. agentpool_server/opencode_server/dependencies.py +24 -0
  219. agentpool_server/opencode_server/input_provider.py +421 -0
  220. agentpool_server/opencode_server/models/__init__.py +250 -0
  221. agentpool_server/opencode_server/models/agent.py +53 -0
  222. agentpool_server/opencode_server/models/app.py +72 -0
  223. agentpool_server/opencode_server/models/base.py +26 -0
  224. agentpool_server/opencode_server/models/common.py +23 -0
  225. agentpool_server/opencode_server/models/config.py +37 -0
  226. agentpool_server/opencode_server/models/events.py +821 -0
  227. agentpool_server/opencode_server/models/file.py +88 -0
  228. agentpool_server/opencode_server/models/mcp.py +44 -0
  229. agentpool_server/opencode_server/models/message.py +179 -0
  230. agentpool_server/opencode_server/models/parts.py +323 -0
  231. agentpool_server/opencode_server/models/provider.py +81 -0
  232. agentpool_server/opencode_server/models/pty.py +43 -0
  233. agentpool_server/opencode_server/models/question.py +56 -0
  234. agentpool_server/opencode_server/models/session.py +111 -0
  235. agentpool_server/opencode_server/routes/__init__.py +29 -0
  236. agentpool_server/opencode_server/routes/agent_routes.py +473 -0
  237. agentpool_server/opencode_server/routes/app_routes.py +202 -0
  238. agentpool_server/opencode_server/routes/config_routes.py +302 -0
  239. agentpool_server/opencode_server/routes/file_routes.py +571 -0
  240. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  241. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  242. agentpool_server/opencode_server/routes/message_routes.py +761 -0
  243. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  244. agentpool_server/opencode_server/routes/pty_routes.py +300 -0
  245. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  246. agentpool_server/opencode_server/routes/session_routes.py +1276 -0
  247. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  248. agentpool_server/opencode_server/server.py +475 -0
  249. agentpool_server/opencode_server/state.py +151 -0
  250. agentpool_server/opencode_server/time_utils.py +8 -0
  251. agentpool_storage/__init__.py +12 -0
  252. agentpool_storage/base.py +184 -2
  253. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  254. agentpool_storage/claude_provider/__init__.py +42 -0
  255. agentpool_storage/claude_provider/provider.py +1089 -0
  256. agentpool_storage/file_provider.py +278 -15
  257. agentpool_storage/memory_provider.py +193 -12
  258. agentpool_storage/models.py +3 -0
  259. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  260. agentpool_storage/opencode_provider/__init__.py +16 -0
  261. agentpool_storage/opencode_provider/helpers.py +414 -0
  262. agentpool_storage/opencode_provider/provider.py +895 -0
  263. agentpool_storage/project_store.py +325 -0
  264. agentpool_storage/session_store.py +26 -6
  265. agentpool_storage/sql_provider/__init__.py +4 -2
  266. agentpool_storage/sql_provider/models.py +48 -0
  267. agentpool_storage/sql_provider/sql_provider.py +269 -3
  268. agentpool_storage/sql_provider/utils.py +12 -13
  269. agentpool_storage/zed_provider/__init__.py +16 -0
  270. agentpool_storage/zed_provider/helpers.py +281 -0
  271. agentpool_storage/zed_provider/models.py +130 -0
  272. agentpool_storage/zed_provider/provider.py +442 -0
  273. agentpool_storage/zed_provider.py +803 -0
  274. agentpool_toolsets/__init__.py +0 -2
  275. agentpool_toolsets/builtin/__init__.py +2 -12
  276. agentpool_toolsets/builtin/code.py +96 -57
  277. agentpool_toolsets/builtin/debug.py +118 -48
  278. agentpool_toolsets/builtin/execution_environment.py +115 -230
  279. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  280. agentpool_toolsets/builtin/skills.py +9 -4
  281. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  282. agentpool_toolsets/builtin/workers.py +4 -2
  283. agentpool_toolsets/composio_toolset.py +2 -2
  284. agentpool_toolsets/entry_points.py +3 -1
  285. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  286. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  287. agentpool_toolsets/fsspec_toolset/grep.py +99 -7
  288. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  289. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  290. agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
  291. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  292. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  293. agentpool_toolsets/mcp_discovery/toolset.py +511 -0
  294. agentpool_toolsets/mcp_run_toolset.py +87 -12
  295. agentpool_toolsets/notifications.py +33 -33
  296. agentpool_toolsets/openapi.py +3 -1
  297. agentpool_toolsets/search_toolset.py +3 -1
  298. agentpool-2.1.9.dist-info/RECORD +0 -474
  299. agentpool_config/resources.py +0 -33
  300. agentpool_server/acp_server/acp_tools.py +0 -43
  301. agentpool_server/acp_server/commands/spawn.py +0 -210
  302. agentpool_storage/text_log_provider.py +0 -275
  303. agentpool_toolsets/builtin/agent_management.py +0 -239
  304. agentpool_toolsets/builtin/chain.py +0 -288
  305. agentpool_toolsets/builtin/history.py +0 -36
  306. agentpool_toolsets/builtin/integration.py +0 -85
  307. agentpool_toolsets/builtin/tool_management.py +0 -90
  308. agentpool_toolsets/builtin/user_interaction.py +0 -52
  309. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  310. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  311. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,302 @@
1
+ """Config and provider routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import defaultdict
6
+ from datetime import timedelta
7
+ from typing import TYPE_CHECKING
8
+
9
+ from fastapi import APIRouter
10
+
11
+ from agentpool_server.opencode_server.dependencies import StateDep
12
+ from agentpool_server.opencode_server.models import (
13
+ Config,
14
+ Mode,
15
+ Model,
16
+ ModelCost,
17
+ ModelLimit,
18
+ Provider,
19
+ ProviderListResponse,
20
+ ProvidersResponse,
21
+ )
22
+
23
+
24
+ if TYPE_CHECKING:
25
+ from tokonomics.model_discovery.model_info import ModelInfo as TokoModelInfo
26
+
27
+
28
+ router = APIRouter(tags=["config"])
29
+
30
+ # Provider display names and environment variable mappings
31
+ PROVIDER_INFO: dict[str, tuple[str, list[str]]] = {
32
+ "anthropic": ("Anthropic", ["ANTHROPIC_API_KEY"]),
33
+ "openai": ("OpenAI", ["OPENAI_API_KEY"]),
34
+ "google": ("Google", ["GOOGLE_API_KEY", "GEMINI_API_KEY"]),
35
+ "mistral": ("Mistral", ["MISTRAL_API_KEY"]),
36
+ "groq": ("Groq", ["GROQ_API_KEY"]),
37
+ "deepseek": ("DeepSeek", ["DEEPSEEK_API_KEY"]),
38
+ "xai": ("xAI", ["XAI_API_KEY"]),
39
+ "together": ("Together AI", ["TOGETHER_API_KEY"]),
40
+ "perplexity": ("Perplexity", ["PERPLEXITY_API_KEY"]),
41
+ "cohere": ("Cohere", ["COHERE_API_KEY"]),
42
+ "fireworks": ("Fireworks AI", ["FIREWORKS_API_KEY"]),
43
+ "openrouter": ("OpenRouter", ["OPENROUTER_API_KEY"]),
44
+ "bedrock": ("AWS Bedrock", ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]),
45
+ "azure": ("Azure OpenAI", ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"]),
46
+ "vertex": ("Google Vertex AI", ["GOOGLE_APPLICATION_CREDENTIALS"]),
47
+ }
48
+
49
+
50
+ def _convert_toko_model_to_opencode(model: TokoModelInfo) -> Model:
51
+ """Convert a tokonomics ModelInfo to an OpenCode Model."""
52
+ # Convert pricing (tokonomics uses per-token, OpenCode uses per-million-token)
53
+ input_cost = 0.0
54
+ output_cost = 0.0
55
+ cache_read = None
56
+ cache_write = None
57
+
58
+ if model.pricing:
59
+ # tokonomics pricing is per-token, convert to per-million-tokens
60
+ if model.pricing.prompt is not None:
61
+ input_cost = model.pricing.prompt * 1_000_000
62
+ if model.pricing.completion is not None:
63
+ output_cost = model.pricing.completion * 1_000_000
64
+ if model.pricing.input_cache_read is not None:
65
+ cache_read = model.pricing.input_cache_read * 1_000_000
66
+ if model.pricing.input_cache_write is not None:
67
+ cache_write = model.pricing.input_cache_write * 1_000_000
68
+
69
+ cost = ModelCost(
70
+ input=input_cost,
71
+ output=output_cost,
72
+ cache_read=cache_read,
73
+ cache_write=cache_write,
74
+ )
75
+
76
+ # Convert limits
77
+ context = float(model.context_window) if model.context_window else 128000.0
78
+ output = float(model.max_output_tokens) if model.max_output_tokens else 4096.0
79
+ limit = ModelLimit(context=context, output=output)
80
+
81
+ # Determine capabilities from modalities and metadata
82
+ has_vision = "image" in model.input_modalities
83
+ has_reasoning = "reasoning" in model.output_modalities or "thinking" in model.name.lower()
84
+
85
+ # Format release date if available
86
+ release_date = ""
87
+ if model.created_at:
88
+ release_date = model.created_at.strftime("%Y-%m-%d")
89
+
90
+ return Model(
91
+ id=model.id,
92
+ name=model.name,
93
+ attachment=has_vision,
94
+ cost=cost,
95
+ limit=limit,
96
+ reasoning=has_reasoning,
97
+ release_date=release_date,
98
+ temperature=True,
99
+ tool_call=True, # Assume most models support tool calling
100
+ )
101
+
102
+
103
+ def _group_models_by_provider(
104
+ models: list[TokoModelInfo],
105
+ ) -> dict[str, list[TokoModelInfo]]:
106
+ """Group models by their provider."""
107
+ grouped: dict[str, list[TokoModelInfo]] = defaultdict(list)
108
+ for model in models:
109
+ # Skip embedding models - OpenCode is for chat/agent models
110
+ if model.is_embedding:
111
+ continue
112
+ grouped[model.provider].append(model)
113
+ return grouped
114
+
115
+
116
+ def _build_providers(models: list[TokoModelInfo]) -> list[Provider]:
117
+ """Build Provider list from tokonomics models."""
118
+ grouped = _group_models_by_provider(models)
119
+ providers: list[Provider] = []
120
+
121
+ for provider_id, provider_models in sorted(grouped.items()):
122
+ # Get provider display info
123
+ display_name, env_vars = PROVIDER_INFO.get(
124
+ provider_id, (provider_id.title(), [f"{provider_id.upper()}_API_KEY"])
125
+ )
126
+
127
+ # Convert models to OpenCode format
128
+ models_dict: dict[str, Model] = {}
129
+ for toko_model in provider_models:
130
+ opencode_model = _convert_toko_model_to_opencode(toko_model)
131
+ models_dict[toko_model.id] = opencode_model
132
+
133
+ provider = Provider(
134
+ id=provider_id,
135
+ name=display_name,
136
+ env=env_vars,
137
+ models=models_dict,
138
+ )
139
+ providers.append(provider)
140
+
141
+ return providers
142
+
143
+
144
+ async def _get_available_models() -> list[TokoModelInfo]:
145
+ """Fetch available models using tokonomics."""
146
+ from tokonomics.model_discovery import get_all_models
147
+
148
+ max_age = timedelta(days=7) # Cache for a week
149
+ return await get_all_models(max_age=max_age)
150
+
151
+
152
+ @router.get("/config")
153
+ async def get_config(state: StateDep) -> Config:
154
+ """Get server configuration."""
155
+ import os
156
+
157
+ # Initialize config if not yet set
158
+ if state.config is None:
159
+ state.config = Config()
160
+
161
+ # Set a default model if not already configured
162
+ if state.config.model is None:
163
+ try:
164
+ # Get available models
165
+ toko_models = await state.agent.get_available_models()
166
+ if toko_models:
167
+ providers = _build_providers(toko_models)
168
+
169
+ # Find first connected provider and use its first model
170
+ for provider in providers:
171
+ if any(os.environ.get(env) for env in provider.env) and provider.models:
172
+ first_model = next(iter(provider.models.keys()))
173
+ state.config.model = f"{provider.id}/{first_model}"
174
+ break
175
+ except Exception: # noqa: BLE001
176
+ pass # If we can't set a default, that's okay
177
+
178
+ return state.config
179
+
180
+
181
+ @router.patch("/config")
182
+ async def update_config(state: StateDep, config_update: Config) -> Config:
183
+ """Update server configuration.
184
+
185
+ Only updates fields that are provided (non-None).
186
+ Returns the complete updated config.
187
+ """
188
+ # Initialize config if not yet set
189
+ if state.config is None:
190
+ state.config = Config()
191
+
192
+ # Update only the fields that were provided
193
+ update_data = config_update.model_dump(exclude_unset=True)
194
+ for field_name, value in update_data.items():
195
+ setattr(state.config, field_name, value)
196
+
197
+ return state.config
198
+
199
+
200
+ def _get_dummy_providers() -> list[Provider]:
201
+ """Return a single dummy provider for testing."""
202
+ dummy_model = Model(
203
+ id="gpt-4o",
204
+ name="GPT-4o",
205
+ attachment=True,
206
+ cost=ModelCost(input=5.0, output=15.0),
207
+ limit=ModelLimit(context=128000.0, output=4096.0),
208
+ reasoning=False,
209
+ release_date="2024-05-13",
210
+ temperature=True,
211
+ tool_call=True,
212
+ )
213
+ dummy_provider = Provider(
214
+ id="openai",
215
+ name="OpenAI",
216
+ env=["OPENAI_API_KEY"],
217
+ models={"gpt-4o": dummy_model},
218
+ )
219
+ return [dummy_provider]
220
+
221
+
222
+ @router.get("/config/providers")
223
+ async def get_providers(state: StateDep) -> ProvidersResponse:
224
+ """Get available providers and models from agent."""
225
+ import os
226
+
227
+ providers: list[Provider] = []
228
+
229
+ # Try to get models from the agent
230
+ try:
231
+ toko_models = await state.agent.get_available_models()
232
+ if toko_models:
233
+ providers = _build_providers(toko_models)
234
+ except Exception: # noqa: BLE001
235
+ pass # Fall through to dummy providers
236
+
237
+ # Fall back to dummy providers if no models available
238
+ if not providers:
239
+ providers = _get_dummy_providers()
240
+
241
+ # Build default models map: use first model for each connected provider
242
+ default_models: dict[str, str] = {}
243
+ connected_providers = [
244
+ provider.id for provider in providers if any(os.environ.get(env) for env in provider.env)
245
+ ]
246
+
247
+ for provider in providers:
248
+ if provider.id in connected_providers and provider.models:
249
+ # Simply use the first available model
250
+ default_models[provider.id] = next(iter(provider.models.keys()))
251
+
252
+ return ProvidersResponse(providers=providers, default=default_models)
253
+
254
+
255
+ @router.get("/provider")
256
+ async def list_providers(state: StateDep) -> ProviderListResponse:
257
+ """List all providers."""
258
+ import os
259
+
260
+ providers: list[Provider] = []
261
+
262
+ # Try to get models from the agent
263
+ try:
264
+ toko_models = await state.agent.get_available_models()
265
+ if toko_models:
266
+ providers = _build_providers(toko_models)
267
+ except Exception: # noqa: BLE001
268
+ pass # Fall through to dummy providers
269
+
270
+ # Fall back to dummy providers if no models available
271
+ if not providers:
272
+ providers = _get_dummy_providers()
273
+
274
+ # Determine which providers are "connected" based on env vars
275
+ connected = [
276
+ provider.id for provider in providers if any(os.environ.get(env) for env in provider.env)
277
+ ]
278
+
279
+ # Build default models map: use first model for each connected provider
280
+ default_models: dict[str, str] = {}
281
+ for provider in providers:
282
+ if provider.id in connected and provider.models:
283
+ # Simply use the first available model
284
+ default_models[provider.id] = next(iter(provider.models.keys()))
285
+
286
+ return ProviderListResponse(
287
+ all=providers,
288
+ default=default_models,
289
+ connected=connected,
290
+ )
291
+
292
+
293
+ @router.get("/mode")
294
+ async def list_modes(state: StateDep) -> list[Mode]:
295
+ """List available modes."""
296
+ _ = state # unused for now
297
+ return [
298
+ Mode(
299
+ name="default",
300
+ tools={},
301
+ )
302
+ ]