alloy-runtime-cli 0.1.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 (451) hide show
  1. alloy_runtime_cli-0.1.0.dist-info/METADATA +61 -0
  2. alloy_runtime_cli-0.1.0.dist-info/RECORD +451 -0
  3. alloy_runtime_cli-0.1.0.dist-info/WHEEL +5 -0
  4. alloy_runtime_cli-0.1.0.dist-info/entry_points.txt +2 -0
  5. alloy_runtime_cli-0.1.0.dist-info/top_level.txt +1 -0
  6. cli/__init__.py +0 -0
  7. cli/commands/__init__.py +0 -0
  8. cli/commands/admin/__init__.py +0 -0
  9. cli/commands/admin/bootstrap_command.py +118 -0
  10. cli/commands/admin/credentials/__init__.py +0 -0
  11. cli/commands/admin/credentials/create/__init__.py +0 -0
  12. cli/commands/admin/credentials/create/command.py +148 -0
  13. cli/commands/admin/credentials/create/presenter.py +16 -0
  14. cli/commands/admin/credentials/grant/__init__.py +0 -0
  15. cli/commands/admin/credentials/grant/command.py +119 -0
  16. cli/commands/admin/credentials/grant/fields.py +33 -0
  17. cli/commands/admin/credentials/grant/presenter.py +23 -0
  18. cli/commands/agents/__init__.py +0 -0
  19. cli/commands/agents/create/__init__.py +0 -0
  20. cli/commands/agents/create/command.py +475 -0
  21. cli/commands/agents/create/fields.py +64 -0
  22. cli/commands/agents/create/presenter.py +68 -0
  23. cli/commands/agents/delete/__init__.py +0 -0
  24. cli/commands/agents/delete/command.py +47 -0
  25. cli/commands/agents/delete/presenter.py +16 -0
  26. cli/commands/agents/get/command.py +37 -0
  27. cli/commands/agents/get/presenter.py +32 -0
  28. cli/commands/agents/list/__init__.py +1 -0
  29. cli/commands/agents/list/command.py +54 -0
  30. cli/commands/agents/list/presenter.py +82 -0
  31. cli/commands/agents/update/__init__.py +0 -0
  32. cli/commands/agents/update/command.py +435 -0
  33. cli/commands/agents/update/fields.py +40 -0
  34. cli/commands/agents/update/presenter.py +68 -0
  35. cli/commands/audio/__init__.py +0 -0
  36. cli/commands/audio/transcribe/__init__.py +0 -0
  37. cli/commands/audio/transcribe/command.py +144 -0
  38. cli/commands/audio/transcribe/presenter.py +15 -0
  39. cli/commands/auth/__init__.py +0 -0
  40. cli/commands/auth/login/__init__.py +0 -0
  41. cli/commands/auth/login/command.py +80 -0
  42. cli/commands/auth/signup/__init__.py +0 -0
  43. cli/commands/auth/signup/command.py +115 -0
  44. cli/commands/billing/__init__.py +1 -0
  45. cli/commands/billing/costs/__init__.py +1 -0
  46. cli/commands/billing/costs/by_agent/__init__.py +1 -0
  47. cli/commands/billing/costs/by_agent/command.py +57 -0
  48. cli/commands/billing/costs/by_agent/presenter.py +81 -0
  49. cli/commands/billing/costs/by_model/__init__.py +1 -0
  50. cli/commands/billing/costs/by_model/command.py +57 -0
  51. cli/commands/billing/costs/by_model/presenter.py +80 -0
  52. cli/commands/billing/costs/daily/__init__.py +1 -0
  53. cli/commands/billing/costs/daily/command.py +55 -0
  54. cli/commands/billing/costs/daily/presenter.py +75 -0
  55. cli/commands/billing/costs/summary/__init__.py +1 -0
  56. cli/commands/billing/costs/summary/command.py +57 -0
  57. cli/commands/billing/costs/summary/presenter.py +42 -0
  58. cli/commands/billing/projects/__init__.py +1 -0
  59. cli/commands/billing/projects/create/__init__.py +1 -0
  60. cli/commands/billing/projects/create/command.py +60 -0
  61. cli/commands/billing/projects/create/presenter.py +26 -0
  62. cli/commands/billing/projects/get/__init__.py +1 -0
  63. cli/commands/billing/projects/get/command.py +33 -0
  64. cli/commands/billing/projects/get/presenter.py +32 -0
  65. cli/commands/billing/projects/list/__init__.py +1 -0
  66. cli/commands/billing/projects/list/command.py +40 -0
  67. cli/commands/billing/projects/list/presenter.py +57 -0
  68. cli/commands/content/__init__.py +1 -0
  69. cli/commands/content/delete/__init__.py +0 -0
  70. cli/commands/content/delete/command.py +49 -0
  71. cli/commands/content/delete/presenter.py +18 -0
  72. cli/commands/content/edit/__init__.py +1 -0
  73. cli/commands/content/edit/command.py +155 -0
  74. cli/commands/content/edit/editor.py +150 -0
  75. cli/commands/content/edit/presenter.py +146 -0
  76. cli/commands/content/get/__init__.py +1 -0
  77. cli/commands/content/get/command.py +39 -0
  78. cli/commands/content/get/presenter.py +176 -0
  79. cli/commands/content/list/__init__.py +1 -0
  80. cli/commands/content/list/command.py +347 -0
  81. cli/commands/content/list/export_formatters.py +409 -0
  82. cli/commands/content/list/export_handler.py +165 -0
  83. cli/commands/content/list/presenter.py +190 -0
  84. cli/commands/credentials/__init__.py +0 -0
  85. cli/commands/credentials/create/__init__.py +0 -0
  86. cli/commands/credentials/create/command.py +165 -0
  87. cli/commands/credentials/create/fields.py +38 -0
  88. cli/commands/credentials/create/presenter.py +20 -0
  89. cli/commands/credentials/update/__init__.py +0 -0
  90. cli/commands/credentials/update/command.py +53 -0
  91. cli/commands/credentials/update/fields.py +71 -0
  92. cli/commands/credentials/update/presenter.py +16 -0
  93. cli/commands/flag_utils.py +366 -0
  94. cli/commands/generate/__init__.py +0 -0
  95. cli/commands/generate/cancel/__init__.py +1 -0
  96. cli/commands/generate/cancel/command.py +44 -0
  97. cli/commands/generate/cancel/presenter.py +26 -0
  98. cli/commands/generate/status/__init__.py +1 -0
  99. cli/commands/generate/status/command.py +58 -0
  100. cli/commands/generate/status/presenter.py +78 -0
  101. cli/commands/generate/text/__init__.py +0 -0
  102. cli/commands/generate/text/command.py +1325 -0
  103. cli/commands/generate/text/concurrent_renderer.py +355 -0
  104. cli/commands/generate/text/presenter.py +287 -0
  105. cli/commands/generate/text/stream_renderer.py +129 -0
  106. cli/commands/knowledge/__init__.py +0 -0
  107. cli/commands/knowledge/collections/__init__.py +0 -0
  108. cli/commands/knowledge/collections/cluster/__init__.py +0 -0
  109. cli/commands/knowledge/collections/cluster/command.py +64 -0
  110. cli/commands/knowledge/collections/cluster/presenter.py +74 -0
  111. cli/commands/knowledge/collections/cluster_status/__init__.py +0 -0
  112. cli/commands/knowledge/collections/cluster_status/command.py +46 -0
  113. cli/commands/knowledge/collections/cluster_status/presenter.py +10 -0
  114. cli/commands/knowledge/collections/create/__init__.py +0 -0
  115. cli/commands/knowledge/collections/create/command.py +137 -0
  116. cli/commands/knowledge/collections/create/presenter.py +38 -0
  117. cli/commands/knowledge/collections/delete/__init__.py +1 -0
  118. cli/commands/knowledge/collections/delete/command.py +47 -0
  119. cli/commands/knowledge/collections/delete/presenter.py +20 -0
  120. cli/commands/knowledge/collections/get/__init__.py +1 -0
  121. cli/commands/knowledge/collections/get/command.py +30 -0
  122. cli/commands/knowledge/collections/get/presenter.py +44 -0
  123. cli/commands/knowledge/collections/list/__init__.py +1 -0
  124. cli/commands/knowledge/collections/list/command.py +41 -0
  125. cli/commands/knowledge/collections/list/presenter.py +68 -0
  126. cli/commands/knowledge/collections/update/__init__.py +0 -0
  127. cli/commands/knowledge/collections/update/command.py +97 -0
  128. cli/commands/knowledge/collections/update/presenter.py +42 -0
  129. cli/commands/knowledge/documents/__init__.py +0 -0
  130. cli/commands/knowledge/documents/bulk_metadata/__init__.py +0 -0
  131. cli/commands/knowledge/documents/bulk_metadata/command.py +119 -0
  132. cli/commands/knowledge/documents/bulk_metadata/presenter.py +36 -0
  133. cli/commands/knowledge/documents/delete/__init__.py +0 -0
  134. cli/commands/knowledge/documents/delete/command.py +47 -0
  135. cli/commands/knowledge/documents/delete/presenter.py +20 -0
  136. cli/commands/knowledge/documents/get/__init__.py +0 -0
  137. cli/commands/knowledge/documents/get/command.py +39 -0
  138. cli/commands/knowledge/documents/get/presenter.py +78 -0
  139. cli/commands/knowledge/documents/ingest/__init__.py +0 -0
  140. cli/commands/knowledge/documents/ingest/command.py +222 -0
  141. cli/commands/knowledge/documents/ingest/presenter.py +41 -0
  142. cli/commands/knowledge/documents/list/__init__.py +0 -0
  143. cli/commands/knowledge/documents/list/command.py +69 -0
  144. cli/commands/knowledge/documents/list/presenter.py +86 -0
  145. cli/commands/knowledge/documents/reingest/__init__.py +0 -0
  146. cli/commands/knowledge/documents/reingest/command.py +102 -0
  147. cli/commands/knowledge/documents/reingest/presenter.py +70 -0
  148. cli/commands/knowledge/documents/update/__init__.py +0 -0
  149. cli/commands/knowledge/documents/update/command.py +85 -0
  150. cli/commands/knowledge/documents/update/presenter.py +37 -0
  151. cli/commands/knowledge/recover/__init__.py +0 -0
  152. cli/commands/knowledge/recover/command.py +46 -0
  153. cli/commands/knowledge/recover/presenter.py +79 -0
  154. cli/commands/knowledge/search/__init__.py +0 -0
  155. cli/commands/knowledge/search/command.py +218 -0
  156. cli/commands/knowledge/search/presenter.py +111 -0
  157. cli/commands/knowledge/synthesis/__init__.py +0 -0
  158. cli/commands/knowledge/synthesis/create/__init__.py +0 -0
  159. cli/commands/knowledge/synthesis/create/command.py +127 -0
  160. cli/commands/knowledge/synthesis/create/presenter.py +33 -0
  161. cli/commands/knowledge/synthesis/delete/__init__.py +0 -0
  162. cli/commands/knowledge/synthesis/delete/command.py +53 -0
  163. cli/commands/knowledge/synthesis/delete/presenter.py +31 -0
  164. cli/commands/knowledge/synthesis/get/__init__.py +0 -0
  165. cli/commands/knowledge/synthesis/get/command.py +55 -0
  166. cli/commands/knowledge/synthesis/get/presenter.py +114 -0
  167. cli/commands/knowledge/synthesis/list/__init__.py +0 -0
  168. cli/commands/knowledge/synthesis/list/command.py +132 -0
  169. cli/commands/knowledge/synthesis/list/presenter.py +84 -0
  170. cli/commands/knowledge/synthesis/refresh/__init__.py +0 -0
  171. cli/commands/knowledge/synthesis/refresh/command.py +42 -0
  172. cli/commands/knowledge/synthesis/refresh/presenter.py +33 -0
  173. cli/commands/knowledge/synthesis/update/__init__.py +0 -0
  174. cli/commands/knowledge/synthesis/update/command.py +76 -0
  175. cli/commands/knowledge/synthesis/update/presenter.py +41 -0
  176. cli/commands/models/__init__.py +0 -0
  177. cli/commands/models/list/__init__.py +0 -0
  178. cli/commands/models/list/command.py +84 -0
  179. cli/commands/models/list/presenter.py +114 -0
  180. cli/commands/organizations/__init__.py +0 -0
  181. cli/commands/organizations/create/command.py +32 -0
  182. cli/commands/organizations/create/presenter.py +9 -0
  183. cli/commands/pipelines/__init__.py +1 -0
  184. cli/commands/pipelines/approvals/__init__.py +1 -0
  185. cli/commands/pipelines/approvals/decide_command.py +77 -0
  186. cli/commands/pipelines/approvals/get_command.py +44 -0
  187. cli/commands/pipelines/approvals/presenter.py +56 -0
  188. cli/commands/pipelines/costs/__init__.py +1 -0
  189. cli/commands/pipelines/costs/command.py +57 -0
  190. cli/commands/pipelines/costs/daily_command.py +54 -0
  191. cli/commands/pipelines/costs/daily_presenter.py +59 -0
  192. cli/commands/pipelines/costs/presenter.py +37 -0
  193. cli/commands/pipelines/create/__init__.py +1 -0
  194. cli/commands/pipelines/create/command.py +103 -0
  195. cli/commands/pipelines/create/presenter.py +22 -0
  196. cli/commands/pipelines/env_vars/__init__.py +1 -0
  197. cli/commands/pipelines/env_vars/command.py +51 -0
  198. cli/commands/pipelines/env_vars/presenter.py +16 -0
  199. cli/commands/pipelines/execute/__init__.py +1 -0
  200. cli/commands/pipelines/execute/command.py +142 -0
  201. cli/commands/pipelines/execute/presenter.py +47 -0
  202. cli/commands/pipelines/executions/__init__.py +1 -0
  203. cli/commands/pipelines/executions/costs/__init__.py +1 -0
  204. cli/commands/pipelines/executions/costs/command.py +48 -0
  205. cli/commands/pipelines/executions/costs/presenter.py +29 -0
  206. cli/commands/pipelines/executions/costs_by_model/__init__.py +1 -0
  207. cli/commands/pipelines/executions/costs_by_model/command.py +50 -0
  208. cli/commands/pipelines/executions/costs_by_model/presenter.py +78 -0
  209. cli/commands/pipelines/executions/costs_by_step/__init__.py +1 -0
  210. cli/commands/pipelines/executions/costs_by_step/command.py +50 -0
  211. cli/commands/pipelines/executions/costs_by_step/presenter.py +72 -0
  212. cli/commands/pipelines/executions/get_command.py +38 -0
  213. cli/commands/pipelines/executions/list_command.py +123 -0
  214. cli/commands/pipelines/executions/presenter.py +131 -0
  215. cli/commands/pipelines/executions/rerun_command.py +41 -0
  216. cli/commands/pipelines/executions/update/__init__.py +1 -0
  217. cli/commands/pipelines/executions/update/command.py +110 -0
  218. cli/commands/pipelines/executions/update/presenter.py +28 -0
  219. cli/commands/pipelines/get/__init__.py +1 -0
  220. cli/commands/pipelines/get/command.py +33 -0
  221. cli/commands/pipelines/get/presenter.py +48 -0
  222. cli/commands/pipelines/list/__init__.py +1 -0
  223. cli/commands/pipelines/list/command.py +53 -0
  224. cli/commands/pipelines/list/presenter.py +66 -0
  225. cli/commands/pipelines/schedules/__init__.py +1 -0
  226. cli/commands/pipelines/schedules/create_command.py +119 -0
  227. cli/commands/pipelines/schedules/create_presenter.py +35 -0
  228. cli/commands/pipelines/schedules/delete_command.py +52 -0
  229. cli/commands/pipelines/schedules/env_vars_command.py +59 -0
  230. cli/commands/pipelines/schedules/env_vars_presenter.py +16 -0
  231. cli/commands/pipelines/schedules/get_command.py +38 -0
  232. cli/commands/pipelines/schedules/list_command.py +33 -0
  233. cli/commands/pipelines/schedules/once_command.py +90 -0
  234. cli/commands/pipelines/schedules/once_presenter.py +30 -0
  235. cli/commands/pipelines/schedules/presenter.py +104 -0
  236. cli/commands/pipelines/schedules/update_command.py +139 -0
  237. cli/commands/pipelines/schedules/update_presenter.py +29 -0
  238. cli/commands/render/__init__.py +0 -0
  239. cli/commands/render/html_to_image/__init__.py +0 -0
  240. cli/commands/render/html_to_image/command.py +170 -0
  241. cli/commands/schemas/__init__.py +0 -0
  242. cli/commands/schemas/create/__init__.py +0 -0
  243. cli/commands/schemas/create/command.py +122 -0
  244. cli/commands/schemas/create/presenter.py +53 -0
  245. cli/commands/schemas/delete/command.py +45 -0
  246. cli/commands/schemas/delete/presenter.py +9 -0
  247. cli/commands/schemas/get/__init__.py +0 -0
  248. cli/commands/schemas/get/command.py +56 -0
  249. cli/commands/schemas/get/presenter.py +129 -0
  250. cli/commands/schemas/list/__init__.py +0 -0
  251. cli/commands/schemas/list/command.py +64 -0
  252. cli/commands/schemas/list/presenter.py +133 -0
  253. cli/commands/schemas/update/__init__.py +0 -0
  254. cli/commands/schemas/update/command.py +369 -0
  255. cli/commands/schemas/update/presenter.py +53 -0
  256. cli/commands/sessions/__init__.py +1 -0
  257. cli/commands/sessions/delete/__init__.py +1 -0
  258. cli/commands/sessions/delete/command.py +47 -0
  259. cli/commands/sessions/delete/presenter.py +10 -0
  260. cli/commands/sessions/get/__init__.py +1 -0
  261. cli/commands/sessions/get/command.py +42 -0
  262. cli/commands/sessions/get/presenter.py +59 -0
  263. cli/commands/sessions/list/__init__.py +1 -0
  264. cli/commands/sessions/list/command.py +61 -0
  265. cli/commands/sessions/list/presenter.py +68 -0
  266. cli/commands/sessions/messages/__init__.py +1 -0
  267. cli/commands/sessions/messages/command.py +78 -0
  268. cli/commands/sessions/messages/presenter.py +79 -0
  269. cli/commands/shared_flags.py +500 -0
  270. cli/commands/sync/__init__.py +0 -0
  271. cli/commands/sync/command.py +45 -0
  272. cli/commands/sync/presenter.py +49 -0
  273. cli/commands/tags/__init__.py +1 -0
  274. cli/commands/tags/create/__init__.py +1 -0
  275. cli/commands/tags/create/command.py +60 -0
  276. cli/commands/tags/delete/__init__.py +1 -0
  277. cli/commands/tags/delete/command.py +47 -0
  278. cli/commands/tags/delete/presenter.py +10 -0
  279. cli/commands/tags/get/command.py +31 -0
  280. cli/commands/tags/get/presenter.py +23 -0
  281. cli/commands/tags/list/__init__.py +1 -0
  282. cli/commands/tags/list/command.py +52 -0
  283. cli/commands/tags/list/presenter.py +49 -0
  284. cli/commands/tags/update/command.py +64 -0
  285. cli/commands/tags/update/presenter.py +9 -0
  286. cli/commands/templates/__init__.py +0 -0
  287. cli/commands/templates/create/__init__.py +0 -0
  288. cli/commands/templates/create/command.py +152 -0
  289. cli/commands/templates/create/presenter.py +86 -0
  290. cli/commands/templates/delete/__init__.py +0 -0
  291. cli/commands/templates/delete/command.py +47 -0
  292. cli/commands/templates/delete/presenter.py +16 -0
  293. cli/commands/templates/get/__init__.py +0 -0
  294. cli/commands/templates/get/command.py +52 -0
  295. cli/commands/templates/get/presenter.py +233 -0
  296. cli/commands/templates/get_by_version/command.py +32 -0
  297. cli/commands/templates/get_by_version/presenter.py +30 -0
  298. cli/commands/templates/list/__init__.py +1 -0
  299. cli/commands/templates/list/command.py +102 -0
  300. cli/commands/templates/list/presenter.py +93 -0
  301. cli/commands/templates/render/__init__.py +0 -0
  302. cli/commands/templates/render/command.py +115 -0
  303. cli/commands/templates/render/presenter.py +276 -0
  304. cli/commands/templates/update/__init__.py +0 -0
  305. cli/commands/templates/update/command.py +199 -0
  306. cli/commands/templates/update/presenter.py +94 -0
  307. cli/commands/templates/version/__init__.py +1 -0
  308. cli/commands/templates/version/command.py +116 -0
  309. cli/commands/templates/version/presenter.py +100 -0
  310. cli/commands/tool_configs/__init__.py +0 -0
  311. cli/commands/tool_configs/create/__init__.py +0 -0
  312. cli/commands/tool_configs/create/command.py +118 -0
  313. cli/commands/tool_configs/create/presenter.py +53 -0
  314. cli/commands/tool_configs/delete/__init__.py +0 -0
  315. cli/commands/tool_configs/delete/command.py +47 -0
  316. cli/commands/tool_configs/delete/presenter.py +18 -0
  317. cli/commands/tool_configs/get/__init__.py +0 -0
  318. cli/commands/tool_configs/get/command.py +31 -0
  319. cli/commands/tool_configs/get/presenter.py +62 -0
  320. cli/commands/tool_configs/list/__init__.py +0 -0
  321. cli/commands/tool_configs/list/command.py +59 -0
  322. cli/commands/tool_configs/list/presenter.py +60 -0
  323. cli/commands/tool_configs/update/__init__.py +0 -0
  324. cli/commands/tool_configs/update/command.py +128 -0
  325. cli/commands/tool_configs/update/presenter.py +53 -0
  326. cli/commands/tools/__init__.py +1 -0
  327. cli/commands/tools/get/__init__.py +1 -0
  328. cli/commands/tools/get/command.py +42 -0
  329. cli/commands/tools/get/presenter.py +45 -0
  330. cli/commands/tools/list/__init__.py +1 -0
  331. cli/commands/tools/list/command.py +56 -0
  332. cli/commands/tools/list/presenter.py +44 -0
  333. cli/commands/users/__init__.py +0 -0
  334. cli/commands/users/create/command.py +53 -0
  335. cli/commands/users/create/presenter.py +9 -0
  336. cli/commands/whoami/__init__.py +0 -0
  337. cli/commands/whoami/command.py +42 -0
  338. cli/infrastructure/__init__.py +0 -0
  339. cli/infrastructure/auth_storage.py +71 -0
  340. cli/infrastructure/client_factory.py +36 -0
  341. cli/infrastructure/command.py +75 -0
  342. cli/infrastructure/config.py +188 -0
  343. cli/infrastructure/console.py +27 -0
  344. cli/infrastructure/editor.py +138 -0
  345. cli/infrastructure/error_display.py +178 -0
  346. cli/infrastructure/field_extractor.py +360 -0
  347. cli/infrastructure/file_content.py +210 -0
  348. cli/infrastructure/filter_parser.py +256 -0
  349. cli/infrastructure/formatters/__init__.py +0 -0
  350. cli/infrastructure/formatters/base.py +99 -0
  351. cli/infrastructure/formatters/compact_formatter.py +245 -0
  352. cli/infrastructure/formatters/json_formatter.py +84 -0
  353. cli/infrastructure/formatters/lines_formatter.py +102 -0
  354. cli/infrastructure/formatting/__init__.py +0 -0
  355. cli/infrastructure/formatting/fields.py +193 -0
  356. cli/infrastructure/forms/__init__.py +0 -0
  357. cli/infrastructure/forms/agent_picker.py +123 -0
  358. cli/infrastructure/forms/agent_tool_editor.py +384 -0
  359. cli/infrastructure/forms/agent_tools_manager.py +212 -0
  360. cli/infrastructure/forms/base_picker.py +469 -0
  361. cli/infrastructure/forms/components.py +126 -0
  362. cli/infrastructure/forms/json_schema_builder.py +149 -0
  363. cli/infrastructure/forms/model_picker.py +134 -0
  364. cli/infrastructure/forms/parsers.py +173 -0
  365. cli/infrastructure/forms/resolution_modal.py +302 -0
  366. cli/infrastructure/forms/schema_picker.py +137 -0
  367. cli/infrastructure/forms/tag_management_modal.py +103 -0
  368. cli/infrastructure/forms/tag_picker.py +207 -0
  369. cli/infrastructure/forms/template_picker.py +131 -0
  370. cli/infrastructure/forms/tool_config_picker.py +130 -0
  371. cli/infrastructure/forms/tool_picker.py +103 -0
  372. cli/infrastructure/injection/__init__.py +0 -0
  373. cli/infrastructure/injection/parser.py +302 -0
  374. cli/infrastructure/injection/resolver.py +399 -0
  375. cli/infrastructure/kv_parser.py +130 -0
  376. cli/infrastructure/local_storage.py +227 -0
  377. cli/infrastructure/macro_parser.py +215 -0
  378. cli/infrastructure/output.py +192 -0
  379. cli/infrastructure/provider_setup.py +81 -0
  380. cli/infrastructure/renderers/__init__.py +0 -0
  381. cli/infrastructure/renderers/entity_renderer.py +77 -0
  382. cli/infrastructure/renderers/list_renderer.py +114 -0
  383. cli/infrastructure/scope_utils.py +47 -0
  384. cli/infrastructure/spinner.py +101 -0
  385. cli/infrastructure/tui/__init__.py +0 -0
  386. cli/infrastructure/tui/clipboard.py +41 -0
  387. cli/infrastructure/tui/formatters.py +105 -0
  388. cli/infrastructure/tui/preview.py +14 -0
  389. cli/infrastructure/tui/selectable.py +198 -0
  390. cli/infrastructure/validation/__init__.py +0 -0
  391. cli/infrastructure/validation/tag_validation.py +74 -0
  392. cli/main.py +759 -0
  393. cli/tui/__init__.py +0 -0
  394. cli/tui/app.py +199 -0
  395. cli/tui/app_store.py +73 -0
  396. cli/tui/chat/__init__.py +0 -0
  397. cli/tui/chat/commands/__init__.py +0 -0
  398. cli/tui/chat/commands/base.py +65 -0
  399. cli/tui/chat/commands/create_session.py +135 -0
  400. cli/tui/chat/commands/load_session.py +119 -0
  401. cli/tui/chat/commands/regenerate.py +120 -0
  402. cli/tui/chat/commands/reload_session.py +63 -0
  403. cli/tui/chat/commands/send_message.py +190 -0
  404. cli/tui/chat/commands/undo.py +66 -0
  405. cli/tui/chat/editor.py +71 -0
  406. cli/tui/chat/messages.py +223 -0
  407. cli/tui/chat/pane.py +141 -0
  408. cli/tui/chat/renderers/__init__.py +0 -0
  409. cli/tui/chat/renderers/base.py +72 -0
  410. cli/tui/chat/renderers/markdown.py +250 -0
  411. cli/tui/chat/renderers/plain.py +83 -0
  412. cli/tui/chat/screen.py +1155 -0
  413. cli/tui/chat/services/__init__.py +0 -0
  414. cli/tui/chat/services/injection.py +386 -0
  415. cli/tui/chat/services/name_generator.py +256 -0
  416. cli/tui/chat/slash_commands.py +424 -0
  417. cli/tui/chat/store.py +280 -0
  418. cli/tui/chat/types.py +220 -0
  419. cli/tui/chat/widgets/__init__.py +0 -0
  420. cli/tui/chat/widgets/chat_header.py +75 -0
  421. cli/tui/chat/widgets/chat_input.py +362 -0
  422. cli/tui/chat/widgets/injection_popup.py +161 -0
  423. cli/tui/chat/widgets/message_display.py +287 -0
  424. cli/tui/chat/widgets/session_sidebar.py +214 -0
  425. cli/tui/chat/widgets/welcome_screen.py +290 -0
  426. cli/tui/screens/__init__.py +0 -0
  427. cli/tui/screens/agents.py +344 -0
  428. cli/tui/screens/base.py +301 -0
  429. cli/tui/screens/content.py +508 -0
  430. cli/tui/screens/dashboard.py +89 -0
  431. cli/tui/screens/models.py +96 -0
  432. cli/tui/screens/nav_screen.py +186 -0
  433. cli/tui/screens/schemas.py +522 -0
  434. cli/tui/screens/templates.py +734 -0
  435. cli/tui/screens/tool_configs.py +335 -0
  436. cli/tui/styles/__init__.py +0 -0
  437. cli/tui/widgets/__init__.py +0 -0
  438. cli/tui/widgets/agent_create_modal.py +139 -0
  439. cli/tui/widgets/agent_form_modal.py +659 -0
  440. cli/tui/widgets/agent_update_modal.py +299 -0
  441. cli/tui/widgets/base_form_modal.py +77 -0
  442. cli/tui/widgets/confirm_modal.py +75 -0
  443. cli/tui/widgets/help_modal.py +145 -0
  444. cli/tui/widgets/new_session_modal.py +328 -0
  445. cli/tui/widgets/schema_create_modal.py +271 -0
  446. cli/tui/widgets/schema_update_modal.py +188 -0
  447. cli/tui/widgets/status_footer.py +147 -0
  448. cli/tui/widgets/template_create_modal.py +502 -0
  449. cli/tui/widgets/template_update_modal.py +308 -0
  450. cli/tui/widgets/tool_config_create_modal.py +216 -0
  451. cli/tui/widgets/tool_config_update_modal.py +208 -0
@@ -0,0 +1,659 @@
1
+ """Base modal class for agent create/update forms.
2
+
3
+ Provides a comprehensive modal screen with all agent fields that can be
4
+ subclassed for create and update operations.
5
+ """
6
+
7
+ import json
8
+ from abc import abstractmethod
9
+ from dataclasses import dataclass
10
+ from typing import Any, cast
11
+ from uuid import UUID
12
+
13
+ from textual import on, work
14
+ from textual.app import ComposeResult
15
+ from textual.containers import Horizontal, VerticalScroll
16
+ from textual.widgets import (
17
+ Button,
18
+ Input,
19
+ Label,
20
+ RadioButton,
21
+ RadioSet,
22
+ Static,
23
+ TextArea,
24
+ )
25
+
26
+ from cli.infrastructure.scope_utils import get_scope_from_radioset
27
+ from cli.tui.widgets.base_form_modal import BaseFormModal
28
+
29
+ from alloy_runtime_sdk.api_client.client import ApiClient
30
+ from alloy_runtime_types.dtos.agents import (
31
+ AgentModelConfig,
32
+ AgentToolConfig,
33
+ AgentToolResponse,
34
+ )
35
+ from alloy_runtime_types.dtos.generation import GenerationParameters
36
+ from alloy_runtime_types.dtos.models import ModelListItemResponse
37
+ from alloy_runtime_types.enums.ownership_scope import OwnershipScope
38
+ from alloy_runtime_types.enums.provider import Provider
39
+ from alloy_runtime_types.enums.template_enums import TemplateContentType
40
+
41
+ from cli.infrastructure.forms.agent_picker import AgentPicker
42
+ from cli.infrastructure.forms.agent_tools_manager import AgentToolsManager
43
+ from cli.infrastructure.forms.json_schema_builder import JsonSchemaBuilder
44
+ from cli.infrastructure.forms.model_picker import ModelPicker
45
+ from cli.infrastructure.forms.schema_picker import SchemaPicker
46
+ from cli.infrastructure.forms.tag_picker import TagPicker
47
+ from cli.infrastructure.forms.template_picker import TemplatePicker
48
+
49
+
50
+ @dataclass
51
+ class AgentFormResult:
52
+ """Result of successful agent form submission."""
53
+
54
+ agent_id: str
55
+ agent_name: str
56
+ is_update: bool = False
57
+
58
+
59
+ class AgentFormModal(BaseFormModal[AgentFormResult]):
60
+ """Base modal for agent create/update forms.
61
+
62
+ Provides a comprehensive form with:
63
+ - Required fields: name, models, tags
64
+ - Basic info: description, scope, base agent
65
+ - System instruction: template picker
66
+ - Schemas: input/output schema pickers or inline JSON schema builder
67
+ - Generation parameters: temperature, max_tokens, etc.
68
+ - Advanced: default headers with quick-add buttons
69
+
70
+ Subclasses must implement:
71
+ - _get_modal_title(): Return the modal title
72
+ - _get_submit_button_text(): Return the submit button text
73
+ - _finalize_submission(): Perform the create/update API call
74
+ - _is_scope_editable(): Whether the scope can be changed
75
+
76
+ Optionally override:
77
+ - on_mount(): To pre-populate fields with existing data
78
+ """
79
+
80
+ def __init__(
81
+ self,
82
+ client: ApiClient,
83
+ **kwargs: Any,
84
+ ) -> None:
85
+ """Initialize the agent form modal.
86
+
87
+ Args:
88
+ client: ApiClient instance for API calls
89
+ """
90
+ super().__init__(**kwargs)
91
+ self._client = client
92
+
93
+ @abstractmethod
94
+ def _get_modal_title(self) -> str:
95
+ """Return the modal title."""
96
+ ...
97
+
98
+ @abstractmethod
99
+ def _get_submit_button_text(self) -> str:
100
+ """Return the submit button text."""
101
+ ...
102
+
103
+ @abstractmethod
104
+ def _is_scope_editable(self) -> bool:
105
+ """Return whether the scope can be changed."""
106
+ ...
107
+
108
+ @abstractmethod
109
+ async def _submit_form(self) -> AgentFormResult:
110
+ """Submit the form and return the result.
111
+
112
+ This should call the appropriate API (create or update) and return
113
+ the result. Errors should be raised as exceptions.
114
+ """
115
+ ...
116
+
117
+ def compose(self) -> ComposeResult:
118
+ with VerticalScroll(id="agent-create-modal"):
119
+ yield Label(self._get_modal_title(), classes="modal-title")
120
+
121
+ # ===== REQUIRED FIELDS =====
122
+ yield Static("[bold]Required[/]", classes="form-section")
123
+
124
+ yield Static("Name *", classes="field-label")
125
+ yield Input(
126
+ placeholder="Unique agent name (e.g., my-writer-agent)",
127
+ id="name-input",
128
+ )
129
+
130
+ yield Static(
131
+ "Models * (first = primary, rest = fallbacks)", classes="field-label"
132
+ )
133
+ yield ModelPicker(
134
+ client=self._client,
135
+ id="model-picker",
136
+ )
137
+
138
+ yield Static(
139
+ "Tags * (required for user/org scoped agents)", classes="field-label"
140
+ )
141
+ yield TagPicker(
142
+ client=self._client,
143
+ placeholder="Search tags...",
144
+ empty_message="No tags selected (required)",
145
+ id="tag-picker",
146
+ )
147
+
148
+ # ===== BASIC INFO =====
149
+ yield Static("[bold]Basic Info[/]", classes="form-section")
150
+
151
+ yield Static("Description", classes="field-label")
152
+ yield Input(
153
+ placeholder="Brief description of what this agent does",
154
+ id="description-input",
155
+ )
156
+
157
+ yield Static("Scope", classes="field-label")
158
+ with RadioSet(id="scope-radios", disabled=not self._is_scope_editable()):
159
+ yield RadioButton("User (personal)", id="scope-user", value=True)
160
+ yield RadioButton("Organization", id="scope-org")
161
+ yield RadioButton("Global", id="scope-global")
162
+
163
+ yield Static("Base Agent (inherit configuration)", classes="field-label")
164
+ yield AgentPicker(
165
+ client=self._client,
166
+ placeholder="Search agents to inherit from...",
167
+ empty_message="No base agent selected (optional)",
168
+ id="base-agent-picker",
169
+ )
170
+
171
+ # ===== SYSTEM INSTRUCTION =====
172
+ yield Static("[bold]System Instruction[/]", classes="form-section")
173
+
174
+ yield Static("System Prompt Template", classes="field-label")
175
+ yield TemplatePicker(
176
+ client=self._client,
177
+ content_type=TemplateContentType.SYSTEM_INSTRUCTION,
178
+ placeholder="Search system instruction templates...",
179
+ empty_message="No template selected (optional)",
180
+ id="system-template-picker",
181
+ )
182
+
183
+ # ===== SCHEMAS =====
184
+ yield Static("[bold]Output Schema[/]", classes="form-section")
185
+
186
+ yield Static("Output Schema (search or leave empty)", classes="field-label")
187
+ yield SchemaPicker(
188
+ client=self._client,
189
+ schema_format="json_schema",
190
+ placeholder="Search output schemas...",
191
+ empty_message="No output schema selected (optional)",
192
+ id="output-schema-picker",
193
+ )
194
+
195
+ yield Static(
196
+ "Or: Output Schema from JSON example (generates schema)",
197
+ classes="field-label",
198
+ )
199
+ yield JsonSchemaBuilder(id="output-schema-builder")
200
+
201
+ # ===== TOOLS =====
202
+ yield Static("[bold]Tools[/]", classes="form-section")
203
+
204
+ yield Static(
205
+ "Configure agent tools (search, RAG, etc.)",
206
+ classes="field-label",
207
+ )
208
+ yield AgentToolsManager(
209
+ client=self._client,
210
+ id="tools-manager",
211
+ )
212
+
213
+ # ===== GENERATION PARAMETERS =====
214
+ yield Static("[bold]Generation Parameters[/]", classes="form-section")
215
+
216
+ with Horizontal(classes="param-row"):
217
+ with VerticalScroll(classes="param-field"):
218
+ yield Static("Temperature", classes="field-label")
219
+ yield Input(
220
+ placeholder="0.0-2.0 (higher = more creative)",
221
+ id="temperature-input",
222
+ )
223
+ with VerticalScroll(classes="param-field"):
224
+ yield Static("Max Tokens", classes="field-label")
225
+ yield Input(
226
+ placeholder="Max output length (e.g., 4096)",
227
+ id="max-tokens-input",
228
+ )
229
+
230
+ with Horizontal(classes="param-row"):
231
+ with VerticalScroll(classes="param-field"):
232
+ yield Static("Top P", classes="field-label")
233
+ yield Input(
234
+ placeholder="0.0-1.0 (nucleus sampling)",
235
+ id="top-p-input",
236
+ )
237
+ with VerticalScroll(classes="param-field"):
238
+ yield Static("Top K", classes="field-label")
239
+ yield Input(
240
+ placeholder="Limit token choices (e.g., 40)",
241
+ id="top-k-input",
242
+ )
243
+
244
+ with Horizontal(classes="param-row"):
245
+ with VerticalScroll(classes="param-field"):
246
+ yield Static("Frequency Penalty", classes="field-label")
247
+ yield Input(
248
+ placeholder="-2.0 to 2.0",
249
+ id="frequency-penalty-input",
250
+ )
251
+ with VerticalScroll(classes="param-field"):
252
+ yield Static("Presence Penalty", classes="field-label")
253
+ yield Input(
254
+ placeholder="-2.0 to 2.0",
255
+ id="presence-penalty-input",
256
+ )
257
+
258
+ yield Static(
259
+ "Reasoning Effort (Claude extended thinking)", classes="field-label"
260
+ )
261
+ with RadioSet(id="reasoning-radios"):
262
+ yield RadioButton("None", id="reasoning-none", value=True)
263
+ yield RadioButton("Low", id="reasoning-low")
264
+ yield RadioButton("Medium", id="reasoning-medium")
265
+ yield RadioButton("High", id="reasoning-high")
266
+
267
+ yield Static("Seed", classes="field-label")
268
+ yield Input(
269
+ placeholder="Integer for reproducible outputs",
270
+ id="seed-input",
271
+ )
272
+
273
+ # ===== ADVANCED =====
274
+ yield Static("[bold]Advanced[/]", classes="form-section")
275
+
276
+ yield Static("Default Headers (JSON)", classes="field-label")
277
+ yield TextArea(
278
+ id="default-headers-textarea",
279
+ )
280
+ with Horizontal(classes="quick-add-buttons"):
281
+ yield Button(
282
+ "+ Anthropic 1M Context",
283
+ id="add-anthropic-1m-btn",
284
+ variant="default",
285
+ )
286
+
287
+ # Error display
288
+ yield Static("", id="error-display", classes="error-display")
289
+
290
+ with Horizontal(classes="button-row"):
291
+ yield Button(
292
+ self._get_submit_button_text(), id="submit-btn", variant="success"
293
+ )
294
+ yield Button("Cancel", id="cancel-btn", variant="default")
295
+
296
+ def on_mount(self) -> None:
297
+ """Focus the name input on mount."""
298
+ self.query_one("#name-input", Input).focus()
299
+
300
+ @on(Button.Pressed, "#submit-btn")
301
+ def on_submit_pressed(self, event: Button.Pressed) -> None:
302
+ """Handle submit button press."""
303
+ self.action_submit()
304
+
305
+ @on(Button.Pressed, "#cancel-btn")
306
+ def on_cancel_pressed(self, event: Button.Pressed) -> None:
307
+ """Handle cancel button press."""
308
+ self.dismiss(None)
309
+
310
+ @on(Button.Pressed, "#add-anthropic-1m-btn")
311
+ def on_add_anthropic_1m_pressed(self, event: Button.Pressed) -> None:
312
+ """Add Anthropic 1M context header."""
313
+ self._add_header("anthropic-beta", "context-1m-2025-08-07")
314
+
315
+ def action_submit(self) -> None:
316
+ """Handle form submission."""
317
+ try:
318
+ self._validate_fields()
319
+ self._hide_error()
320
+ self._finalize_submission()
321
+ except ValueError as e:
322
+ self._show_error(str(e))
323
+
324
+ def _validate_fields(self) -> None:
325
+ """Validate all required fields."""
326
+ name = self.query_one("#name-input", Input).value.strip()
327
+ if not name:
328
+ raise ValueError("Name is required")
329
+
330
+ model_picker = self.query_one("#model-picker", ModelPicker)
331
+ if not model_picker.selected_models:
332
+ raise ValueError("At least one model is required")
333
+
334
+ # Tags are required for user and org scope
335
+ scope = self._get_scope()
336
+ tag_picker = self.query_one("#tag-picker", TagPicker)
337
+ if scope != OwnershipScope.GLOBAL and not tag_picker.selected_tags:
338
+ raise ValueError("At least one tag is required for user/org scoped agents")
339
+
340
+ # Validate output schema exclusivity
341
+ output_schema_picker = self.query_one("#output-schema-picker", SchemaPicker)
342
+ output_schema_builder = self.query_one(
343
+ "#output-schema-builder", JsonSchemaBuilder
344
+ )
345
+ if output_schema_picker.selected_id and output_schema_builder.generated_schema:
346
+ raise ValueError(
347
+ "Specify either an existing output schema OR paste a JSON example, not both"
348
+ )
349
+
350
+ # Validate JSON builder state
351
+ if (
352
+ output_schema_builder.json_input
353
+ and not output_schema_builder.generated_schema
354
+ ):
355
+ raise ValueError(
356
+ "Output Schema JSON has errors. Fix them or clear the field."
357
+ )
358
+
359
+ # Validate tools
360
+ tools_manager = self.query_one("#tools-manager", AgentToolsManager)
361
+ tools_error = tools_manager.validate_all()
362
+ if tools_error:
363
+ raise ValueError(tools_error)
364
+
365
+ def _get_scope(self) -> OwnershipScope:
366
+ """Get scope from RadioSet."""
367
+ return get_scope_from_radioset(self.query_one("#scope-radios", RadioSet))
368
+
369
+ def _get_reasoning_effort(self) -> str | None:
370
+ """Get reasoning effort from RadioSet."""
371
+ radios = self.query_one("#reasoning-radios", RadioSet)
372
+ pressed = radios.pressed_button
373
+ if pressed is None or pressed.id == "reasoning-none":
374
+ return None
375
+ elif pressed.id == "reasoning-low":
376
+ return "low"
377
+ elif pressed.id == "reasoning-medium":
378
+ return "medium"
379
+ else:
380
+ return "high"
381
+
382
+ def _parse_float(
383
+ self, value: str, field_name: str, min_val: float, max_val: float
384
+ ) -> float | None:
385
+ """Parse a float value with validation."""
386
+ if not value.strip():
387
+ return None
388
+ try:
389
+ result = float(value)
390
+ if result < min_val or result > max_val:
391
+ raise ValueError(
392
+ f"{field_name} must be between {min_val} and {max_val}"
393
+ )
394
+ return result
395
+ except ValueError as e:
396
+ if "must be between" in str(e):
397
+ raise
398
+ raise ValueError(f"{field_name} must be a valid number") from e
399
+
400
+ def _parse_int(
401
+ self, value: str, field_name: str, min_val: int | None = None
402
+ ) -> int | None:
403
+ """Parse an integer value with validation."""
404
+ if not value.strip():
405
+ return None
406
+ try:
407
+ result = int(value)
408
+ if min_val is not None and result < min_val:
409
+ raise ValueError(f"{field_name} must be at least {min_val}")
410
+ return result
411
+ except ValueError as e:
412
+ if "must be" in str(e):
413
+ raise
414
+ raise ValueError(f"{field_name} must be a valid integer") from e
415
+
416
+ def _parse_uuid(self, value: str | None, field_name: str) -> UUID | None:
417
+ """Parse a UUID value."""
418
+ if not value:
419
+ return None
420
+ try:
421
+ return UUID(value)
422
+ except ValueError:
423
+ raise ValueError(f"{field_name} has an invalid ID format")
424
+
425
+ def _parse_json(self, value: str, field_name: str) -> dict[str, Any] | None:
426
+ """Parse JSON value for headers."""
427
+ if not value.strip():
428
+ return None
429
+ try:
430
+ result = json.loads(value)
431
+ if not isinstance(result, dict):
432
+ raise ValueError(f"{field_name} must be a JSON object")
433
+ return cast(dict[str, Any], result)
434
+ except json.JSONDecodeError as e:
435
+ raise ValueError(f"{field_name} has invalid JSON: {e.msg}") from e
436
+
437
+ def _add_header(self, key: str, value: str) -> None:
438
+ """Add a header to the default headers JSON field."""
439
+ headers_textarea = self.query_one("#default-headers-textarea", TextArea)
440
+ current_text = headers_textarea.text.strip()
441
+
442
+ if current_text:
443
+ try:
444
+ headers = json.loads(current_text)
445
+ if not isinstance(headers, dict):
446
+ headers = {}
447
+ except json.JSONDecodeError:
448
+ self._show_error(
449
+ "Cannot add header: Default Headers contains invalid JSON"
450
+ )
451
+ return
452
+ else:
453
+ headers = {}
454
+
455
+ headers[key] = value
456
+ headers_textarea.text = json.dumps(headers, indent=2)
457
+
458
+ def _build_model_configs(self) -> list[AgentModelConfig]:
459
+ """Build model configs from the model picker."""
460
+ model_picker = self.query_one("#model-picker", ModelPicker)
461
+ return [
462
+ AgentModelConfig(
463
+ provider_key=Provider(m.provider_key),
464
+ provider_model_name=m.provider_model_name,
465
+ preference_order=i,
466
+ )
467
+ for i, m in enumerate(model_picker.selected_models)
468
+ ]
469
+
470
+ def _build_generation_params(self) -> GenerationParameters | None:
471
+ """Build GenerationParameters from form fields."""
472
+ temperature = self._parse_float(
473
+ self.query_one("#temperature-input", Input).value,
474
+ "Temperature",
475
+ 0.0,
476
+ 2.0,
477
+ )
478
+ max_tokens = self._parse_int(
479
+ self.query_one("#max-tokens-input", Input).value,
480
+ "Max Tokens",
481
+ 1,
482
+ )
483
+ top_p = self._parse_float(
484
+ self.query_one("#top-p-input", Input).value,
485
+ "Top P",
486
+ 0.0,
487
+ 1.0,
488
+ )
489
+ top_k = self._parse_int(
490
+ self.query_one("#top-k-input", Input).value,
491
+ "Top K",
492
+ 1,
493
+ )
494
+ frequency_penalty = self._parse_float(
495
+ self.query_one("#frequency-penalty-input", Input).value,
496
+ "Frequency Penalty",
497
+ -2.0,
498
+ 2.0,
499
+ )
500
+ presence_penalty = self._parse_float(
501
+ self.query_one("#presence-penalty-input", Input).value,
502
+ "Presence Penalty",
503
+ -2.0,
504
+ 2.0,
505
+ )
506
+ reasoning_effort = self._get_reasoning_effort()
507
+ seed = self._parse_int(
508
+ self.query_one("#seed-input", Input).value,
509
+ "Seed",
510
+ )
511
+
512
+ # Only return if at least one param is set
513
+ if any(
514
+ [
515
+ temperature,
516
+ max_tokens,
517
+ top_p,
518
+ top_k,
519
+ frequency_penalty,
520
+ presence_penalty,
521
+ reasoning_effort,
522
+ seed,
523
+ ]
524
+ ):
525
+ return GenerationParameters(
526
+ temperature=temperature,
527
+ max_tokens=max_tokens,
528
+ top_p=top_p,
529
+ top_k=top_k,
530
+ frequency_penalty=frequency_penalty,
531
+ presence_penalty=presence_penalty,
532
+ reasoning_effort=reasoning_effort, # type: ignore[arg-type]
533
+ seed=seed,
534
+ )
535
+
536
+ return None
537
+
538
+ def _get_output_schema(self) -> dict[str, Any] | None:
539
+ """Get inline output schema from JSON builder if present."""
540
+ output_schema_builder = self.query_one(
541
+ "#output-schema-builder", JsonSchemaBuilder
542
+ )
543
+ if output_schema_builder.generated_schema:
544
+ return {
545
+ "type": "json_schema",
546
+ "schema_def": output_schema_builder.generated_schema,
547
+ }
548
+ return None
549
+
550
+ @work(exclusive=True)
551
+ async def _finalize_submission(self) -> None:
552
+ """Build request and submit to API."""
553
+ try:
554
+ result = await self._submit_form()
555
+ self.dismiss(result)
556
+ except Exception as e:
557
+ self._show_error(f"Failed to save agent: {e}")
558
+
559
+ # ========== Helper methods for pre-populating fields ==========
560
+
561
+ def _set_name(self, name: str) -> None:
562
+ """Set the name input value."""
563
+ self.query_one("#name-input", Input).value = name
564
+
565
+ def _set_description(self, description: str | None) -> None:
566
+ """Set the description input value."""
567
+ self.query_one("#description-input", Input).value = description or ""
568
+
569
+ def _set_scope(self, scope: OwnershipScope) -> None:
570
+ """Set the scope radio selection."""
571
+ scope_map = {
572
+ OwnershipScope.USER: "scope-user",
573
+ OwnershipScope.ORGANIZATION: "scope-org",
574
+ OwnershipScope.GLOBAL: "scope-global",
575
+ }
576
+ radio_id = scope_map.get(scope, "scope-user")
577
+ radio = self.query_one(f"#{radio_id}", RadioButton)
578
+ radio.value = True
579
+
580
+ def _set_models(self, models: list[ModelListItemResponse]) -> None:
581
+ """Set the model picker selections."""
582
+ model_picker = self.query_one("#model-picker", ModelPicker)
583
+ model_picker.set_initial_models(models)
584
+
585
+ def _set_tags(self, tags: list[dict[str, Any]]) -> None:
586
+ """Set the tag picker selections."""
587
+ tag_picker = self.query_one("#tag-picker", TagPicker)
588
+ tag_picker.set_initial_selection(tags)
589
+
590
+ def _set_base_agent(self, agent_id: str, name: str) -> None:
591
+ """Set the base agent picker selection."""
592
+ agent_picker = self.query_one("#base-agent-picker", AgentPicker)
593
+ agent_picker.set_initial_agent_by_id(agent_id, name)
594
+
595
+ def _set_system_template(
596
+ self, version_id: str, name: str = "System Template"
597
+ ) -> None:
598
+ """Set the system template picker selection."""
599
+ template_picker = self.query_one("#system-template-picker", TemplatePicker)
600
+ template_picker.set_initial_template_by_version_id(version_id, name)
601
+
602
+ def _set_output_schema(self, schema_id: str, name: str = "Output Schema") -> None:
603
+ """Set the output schema picker selection."""
604
+ schema_picker = self.query_one("#output-schema-picker", SchemaPicker)
605
+ schema_picker.set_initial_schema_by_id(schema_id, name)
606
+
607
+ def _set_generation_params(self, params: GenerationParameters | None) -> None:
608
+ """Set the generation parameter input values."""
609
+ if params is None:
610
+ return
611
+
612
+ if params.temperature is not None:
613
+ self.query_one("#temperature-input", Input).value = str(params.temperature)
614
+ if params.max_tokens is not None:
615
+ self.query_one("#max-tokens-input", Input).value = str(params.max_tokens)
616
+ if params.top_p is not None:
617
+ self.query_one("#top-p-input", Input).value = str(params.top_p)
618
+ if params.top_k is not None:
619
+ self.query_one("#top-k-input", Input).value = str(params.top_k)
620
+ if params.frequency_penalty is not None:
621
+ self.query_one("#frequency-penalty-input", Input).value = str(
622
+ params.frequency_penalty
623
+ )
624
+ if params.presence_penalty is not None:
625
+ self.query_one("#presence-penalty-input", Input).value = str(
626
+ params.presence_penalty
627
+ )
628
+ if params.seed is not None:
629
+ self.query_one("#seed-input", Input).value = str(params.seed)
630
+
631
+ # Set reasoning effort radio
632
+ if params.reasoning_effort:
633
+ effort_map = {
634
+ "low": "reasoning-low",
635
+ "medium": "reasoning-medium",
636
+ "high": "reasoning-high",
637
+ }
638
+ radio_id = effort_map.get(params.reasoning_effort)
639
+ if radio_id:
640
+ radio = self.query_one(f"#{radio_id}", RadioButton)
641
+ radio.value = True
642
+
643
+ def _set_default_headers(self, headers: dict[str, str] | None) -> None:
644
+ """Set the default headers textarea value."""
645
+ if headers:
646
+ self.query_one("#default-headers-textarea", TextArea).text = json.dumps(
647
+ headers, indent=2
648
+ )
649
+
650
+ def _build_tools(self) -> list[AgentToolConfig] | None:
651
+ """Build tool configs from the tools manager."""
652
+ tools_manager = self.query_one("#tools-manager", AgentToolsManager)
653
+ tools = tools_manager.configured_tools
654
+ return tools if tools else None
655
+
656
+ def _set_tools(self, tools: list[AgentToolResponse]) -> None:
657
+ """Pre-populate tools manager with existing tools."""
658
+ tools_manager = self.query_one("#tools-manager", AgentToolsManager)
659
+ tools_manager.set_initial_tools(tools)