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.
- alloy_runtime_cli-0.1.0.dist-info/METADATA +61 -0
- alloy_runtime_cli-0.1.0.dist-info/RECORD +451 -0
- alloy_runtime_cli-0.1.0.dist-info/WHEEL +5 -0
- alloy_runtime_cli-0.1.0.dist-info/entry_points.txt +2 -0
- alloy_runtime_cli-0.1.0.dist-info/top_level.txt +1 -0
- cli/__init__.py +0 -0
- cli/commands/__init__.py +0 -0
- cli/commands/admin/__init__.py +0 -0
- cli/commands/admin/bootstrap_command.py +118 -0
- cli/commands/admin/credentials/__init__.py +0 -0
- cli/commands/admin/credentials/create/__init__.py +0 -0
- cli/commands/admin/credentials/create/command.py +148 -0
- cli/commands/admin/credentials/create/presenter.py +16 -0
- cli/commands/admin/credentials/grant/__init__.py +0 -0
- cli/commands/admin/credentials/grant/command.py +119 -0
- cli/commands/admin/credentials/grant/fields.py +33 -0
- cli/commands/admin/credentials/grant/presenter.py +23 -0
- cli/commands/agents/__init__.py +0 -0
- cli/commands/agents/create/__init__.py +0 -0
- cli/commands/agents/create/command.py +475 -0
- cli/commands/agents/create/fields.py +64 -0
- cli/commands/agents/create/presenter.py +68 -0
- cli/commands/agents/delete/__init__.py +0 -0
- cli/commands/agents/delete/command.py +47 -0
- cli/commands/agents/delete/presenter.py +16 -0
- cli/commands/agents/get/command.py +37 -0
- cli/commands/agents/get/presenter.py +32 -0
- cli/commands/agents/list/__init__.py +1 -0
- cli/commands/agents/list/command.py +54 -0
- cli/commands/agents/list/presenter.py +82 -0
- cli/commands/agents/update/__init__.py +0 -0
- cli/commands/agents/update/command.py +435 -0
- cli/commands/agents/update/fields.py +40 -0
- cli/commands/agents/update/presenter.py +68 -0
- cli/commands/audio/__init__.py +0 -0
- cli/commands/audio/transcribe/__init__.py +0 -0
- cli/commands/audio/transcribe/command.py +144 -0
- cli/commands/audio/transcribe/presenter.py +15 -0
- cli/commands/auth/__init__.py +0 -0
- cli/commands/auth/login/__init__.py +0 -0
- cli/commands/auth/login/command.py +80 -0
- cli/commands/auth/signup/__init__.py +0 -0
- cli/commands/auth/signup/command.py +115 -0
- cli/commands/billing/__init__.py +1 -0
- cli/commands/billing/costs/__init__.py +1 -0
- cli/commands/billing/costs/by_agent/__init__.py +1 -0
- cli/commands/billing/costs/by_agent/command.py +57 -0
- cli/commands/billing/costs/by_agent/presenter.py +81 -0
- cli/commands/billing/costs/by_model/__init__.py +1 -0
- cli/commands/billing/costs/by_model/command.py +57 -0
- cli/commands/billing/costs/by_model/presenter.py +80 -0
- cli/commands/billing/costs/daily/__init__.py +1 -0
- cli/commands/billing/costs/daily/command.py +55 -0
- cli/commands/billing/costs/daily/presenter.py +75 -0
- cli/commands/billing/costs/summary/__init__.py +1 -0
- cli/commands/billing/costs/summary/command.py +57 -0
- cli/commands/billing/costs/summary/presenter.py +42 -0
- cli/commands/billing/projects/__init__.py +1 -0
- cli/commands/billing/projects/create/__init__.py +1 -0
- cli/commands/billing/projects/create/command.py +60 -0
- cli/commands/billing/projects/create/presenter.py +26 -0
- cli/commands/billing/projects/get/__init__.py +1 -0
- cli/commands/billing/projects/get/command.py +33 -0
- cli/commands/billing/projects/get/presenter.py +32 -0
- cli/commands/billing/projects/list/__init__.py +1 -0
- cli/commands/billing/projects/list/command.py +40 -0
- cli/commands/billing/projects/list/presenter.py +57 -0
- cli/commands/content/__init__.py +1 -0
- cli/commands/content/delete/__init__.py +0 -0
- cli/commands/content/delete/command.py +49 -0
- cli/commands/content/delete/presenter.py +18 -0
- cli/commands/content/edit/__init__.py +1 -0
- cli/commands/content/edit/command.py +155 -0
- cli/commands/content/edit/editor.py +150 -0
- cli/commands/content/edit/presenter.py +146 -0
- cli/commands/content/get/__init__.py +1 -0
- cli/commands/content/get/command.py +39 -0
- cli/commands/content/get/presenter.py +176 -0
- cli/commands/content/list/__init__.py +1 -0
- cli/commands/content/list/command.py +347 -0
- cli/commands/content/list/export_formatters.py +409 -0
- cli/commands/content/list/export_handler.py +165 -0
- cli/commands/content/list/presenter.py +190 -0
- cli/commands/credentials/__init__.py +0 -0
- cli/commands/credentials/create/__init__.py +0 -0
- cli/commands/credentials/create/command.py +165 -0
- cli/commands/credentials/create/fields.py +38 -0
- cli/commands/credentials/create/presenter.py +20 -0
- cli/commands/credentials/update/__init__.py +0 -0
- cli/commands/credentials/update/command.py +53 -0
- cli/commands/credentials/update/fields.py +71 -0
- cli/commands/credentials/update/presenter.py +16 -0
- cli/commands/flag_utils.py +366 -0
- cli/commands/generate/__init__.py +0 -0
- cli/commands/generate/cancel/__init__.py +1 -0
- cli/commands/generate/cancel/command.py +44 -0
- cli/commands/generate/cancel/presenter.py +26 -0
- cli/commands/generate/status/__init__.py +1 -0
- cli/commands/generate/status/command.py +58 -0
- cli/commands/generate/status/presenter.py +78 -0
- cli/commands/generate/text/__init__.py +0 -0
- cli/commands/generate/text/command.py +1325 -0
- cli/commands/generate/text/concurrent_renderer.py +355 -0
- cli/commands/generate/text/presenter.py +287 -0
- cli/commands/generate/text/stream_renderer.py +129 -0
- cli/commands/knowledge/__init__.py +0 -0
- cli/commands/knowledge/collections/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster/command.py +64 -0
- cli/commands/knowledge/collections/cluster/presenter.py +74 -0
- cli/commands/knowledge/collections/cluster_status/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster_status/command.py +46 -0
- cli/commands/knowledge/collections/cluster_status/presenter.py +10 -0
- cli/commands/knowledge/collections/create/__init__.py +0 -0
- cli/commands/knowledge/collections/create/command.py +137 -0
- cli/commands/knowledge/collections/create/presenter.py +38 -0
- cli/commands/knowledge/collections/delete/__init__.py +1 -0
- cli/commands/knowledge/collections/delete/command.py +47 -0
- cli/commands/knowledge/collections/delete/presenter.py +20 -0
- cli/commands/knowledge/collections/get/__init__.py +1 -0
- cli/commands/knowledge/collections/get/command.py +30 -0
- cli/commands/knowledge/collections/get/presenter.py +44 -0
- cli/commands/knowledge/collections/list/__init__.py +1 -0
- cli/commands/knowledge/collections/list/command.py +41 -0
- cli/commands/knowledge/collections/list/presenter.py +68 -0
- cli/commands/knowledge/collections/update/__init__.py +0 -0
- cli/commands/knowledge/collections/update/command.py +97 -0
- cli/commands/knowledge/collections/update/presenter.py +42 -0
- cli/commands/knowledge/documents/__init__.py +0 -0
- cli/commands/knowledge/documents/bulk_metadata/__init__.py +0 -0
- cli/commands/knowledge/documents/bulk_metadata/command.py +119 -0
- cli/commands/knowledge/documents/bulk_metadata/presenter.py +36 -0
- cli/commands/knowledge/documents/delete/__init__.py +0 -0
- cli/commands/knowledge/documents/delete/command.py +47 -0
- cli/commands/knowledge/documents/delete/presenter.py +20 -0
- cli/commands/knowledge/documents/get/__init__.py +0 -0
- cli/commands/knowledge/documents/get/command.py +39 -0
- cli/commands/knowledge/documents/get/presenter.py +78 -0
- cli/commands/knowledge/documents/ingest/__init__.py +0 -0
- cli/commands/knowledge/documents/ingest/command.py +222 -0
- cli/commands/knowledge/documents/ingest/presenter.py +41 -0
- cli/commands/knowledge/documents/list/__init__.py +0 -0
- cli/commands/knowledge/documents/list/command.py +69 -0
- cli/commands/knowledge/documents/list/presenter.py +86 -0
- cli/commands/knowledge/documents/reingest/__init__.py +0 -0
- cli/commands/knowledge/documents/reingest/command.py +102 -0
- cli/commands/knowledge/documents/reingest/presenter.py +70 -0
- cli/commands/knowledge/documents/update/__init__.py +0 -0
- cli/commands/knowledge/documents/update/command.py +85 -0
- cli/commands/knowledge/documents/update/presenter.py +37 -0
- cli/commands/knowledge/recover/__init__.py +0 -0
- cli/commands/knowledge/recover/command.py +46 -0
- cli/commands/knowledge/recover/presenter.py +79 -0
- cli/commands/knowledge/search/__init__.py +0 -0
- cli/commands/knowledge/search/command.py +218 -0
- cli/commands/knowledge/search/presenter.py +111 -0
- cli/commands/knowledge/synthesis/__init__.py +0 -0
- cli/commands/knowledge/synthesis/create/__init__.py +0 -0
- cli/commands/knowledge/synthesis/create/command.py +127 -0
- cli/commands/knowledge/synthesis/create/presenter.py +33 -0
- cli/commands/knowledge/synthesis/delete/__init__.py +0 -0
- cli/commands/knowledge/synthesis/delete/command.py +53 -0
- cli/commands/knowledge/synthesis/delete/presenter.py +31 -0
- cli/commands/knowledge/synthesis/get/__init__.py +0 -0
- cli/commands/knowledge/synthesis/get/command.py +55 -0
- cli/commands/knowledge/synthesis/get/presenter.py +114 -0
- cli/commands/knowledge/synthesis/list/__init__.py +0 -0
- cli/commands/knowledge/synthesis/list/command.py +132 -0
- cli/commands/knowledge/synthesis/list/presenter.py +84 -0
- cli/commands/knowledge/synthesis/refresh/__init__.py +0 -0
- cli/commands/knowledge/synthesis/refresh/command.py +42 -0
- cli/commands/knowledge/synthesis/refresh/presenter.py +33 -0
- cli/commands/knowledge/synthesis/update/__init__.py +0 -0
- cli/commands/knowledge/synthesis/update/command.py +76 -0
- cli/commands/knowledge/synthesis/update/presenter.py +41 -0
- cli/commands/models/__init__.py +0 -0
- cli/commands/models/list/__init__.py +0 -0
- cli/commands/models/list/command.py +84 -0
- cli/commands/models/list/presenter.py +114 -0
- cli/commands/organizations/__init__.py +0 -0
- cli/commands/organizations/create/command.py +32 -0
- cli/commands/organizations/create/presenter.py +9 -0
- cli/commands/pipelines/__init__.py +1 -0
- cli/commands/pipelines/approvals/__init__.py +1 -0
- cli/commands/pipelines/approvals/decide_command.py +77 -0
- cli/commands/pipelines/approvals/get_command.py +44 -0
- cli/commands/pipelines/approvals/presenter.py +56 -0
- cli/commands/pipelines/costs/__init__.py +1 -0
- cli/commands/pipelines/costs/command.py +57 -0
- cli/commands/pipelines/costs/daily_command.py +54 -0
- cli/commands/pipelines/costs/daily_presenter.py +59 -0
- cli/commands/pipelines/costs/presenter.py +37 -0
- cli/commands/pipelines/create/__init__.py +1 -0
- cli/commands/pipelines/create/command.py +103 -0
- cli/commands/pipelines/create/presenter.py +22 -0
- cli/commands/pipelines/env_vars/__init__.py +1 -0
- cli/commands/pipelines/env_vars/command.py +51 -0
- cli/commands/pipelines/env_vars/presenter.py +16 -0
- cli/commands/pipelines/execute/__init__.py +1 -0
- cli/commands/pipelines/execute/command.py +142 -0
- cli/commands/pipelines/execute/presenter.py +47 -0
- cli/commands/pipelines/executions/__init__.py +1 -0
- cli/commands/pipelines/executions/costs/__init__.py +1 -0
- cli/commands/pipelines/executions/costs/command.py +48 -0
- cli/commands/pipelines/executions/costs/presenter.py +29 -0
- cli/commands/pipelines/executions/costs_by_model/__init__.py +1 -0
- cli/commands/pipelines/executions/costs_by_model/command.py +50 -0
- cli/commands/pipelines/executions/costs_by_model/presenter.py +78 -0
- cli/commands/pipelines/executions/costs_by_step/__init__.py +1 -0
- cli/commands/pipelines/executions/costs_by_step/command.py +50 -0
- cli/commands/pipelines/executions/costs_by_step/presenter.py +72 -0
- cli/commands/pipelines/executions/get_command.py +38 -0
- cli/commands/pipelines/executions/list_command.py +123 -0
- cli/commands/pipelines/executions/presenter.py +131 -0
- cli/commands/pipelines/executions/rerun_command.py +41 -0
- cli/commands/pipelines/executions/update/__init__.py +1 -0
- cli/commands/pipelines/executions/update/command.py +110 -0
- cli/commands/pipelines/executions/update/presenter.py +28 -0
- cli/commands/pipelines/get/__init__.py +1 -0
- cli/commands/pipelines/get/command.py +33 -0
- cli/commands/pipelines/get/presenter.py +48 -0
- cli/commands/pipelines/list/__init__.py +1 -0
- cli/commands/pipelines/list/command.py +53 -0
- cli/commands/pipelines/list/presenter.py +66 -0
- cli/commands/pipelines/schedules/__init__.py +1 -0
- cli/commands/pipelines/schedules/create_command.py +119 -0
- cli/commands/pipelines/schedules/create_presenter.py +35 -0
- cli/commands/pipelines/schedules/delete_command.py +52 -0
- cli/commands/pipelines/schedules/env_vars_command.py +59 -0
- cli/commands/pipelines/schedules/env_vars_presenter.py +16 -0
- cli/commands/pipelines/schedules/get_command.py +38 -0
- cli/commands/pipelines/schedules/list_command.py +33 -0
- cli/commands/pipelines/schedules/once_command.py +90 -0
- cli/commands/pipelines/schedules/once_presenter.py +30 -0
- cli/commands/pipelines/schedules/presenter.py +104 -0
- cli/commands/pipelines/schedules/update_command.py +139 -0
- cli/commands/pipelines/schedules/update_presenter.py +29 -0
- cli/commands/render/__init__.py +0 -0
- cli/commands/render/html_to_image/__init__.py +0 -0
- cli/commands/render/html_to_image/command.py +170 -0
- cli/commands/schemas/__init__.py +0 -0
- cli/commands/schemas/create/__init__.py +0 -0
- cli/commands/schemas/create/command.py +122 -0
- cli/commands/schemas/create/presenter.py +53 -0
- cli/commands/schemas/delete/command.py +45 -0
- cli/commands/schemas/delete/presenter.py +9 -0
- cli/commands/schemas/get/__init__.py +0 -0
- cli/commands/schemas/get/command.py +56 -0
- cli/commands/schemas/get/presenter.py +129 -0
- cli/commands/schemas/list/__init__.py +0 -0
- cli/commands/schemas/list/command.py +64 -0
- cli/commands/schemas/list/presenter.py +133 -0
- cli/commands/schemas/update/__init__.py +0 -0
- cli/commands/schemas/update/command.py +369 -0
- cli/commands/schemas/update/presenter.py +53 -0
- cli/commands/sessions/__init__.py +1 -0
- cli/commands/sessions/delete/__init__.py +1 -0
- cli/commands/sessions/delete/command.py +47 -0
- cli/commands/sessions/delete/presenter.py +10 -0
- cli/commands/sessions/get/__init__.py +1 -0
- cli/commands/sessions/get/command.py +42 -0
- cli/commands/sessions/get/presenter.py +59 -0
- cli/commands/sessions/list/__init__.py +1 -0
- cli/commands/sessions/list/command.py +61 -0
- cli/commands/sessions/list/presenter.py +68 -0
- cli/commands/sessions/messages/__init__.py +1 -0
- cli/commands/sessions/messages/command.py +78 -0
- cli/commands/sessions/messages/presenter.py +79 -0
- cli/commands/shared_flags.py +500 -0
- cli/commands/sync/__init__.py +0 -0
- cli/commands/sync/command.py +45 -0
- cli/commands/sync/presenter.py +49 -0
- cli/commands/tags/__init__.py +1 -0
- cli/commands/tags/create/__init__.py +1 -0
- cli/commands/tags/create/command.py +60 -0
- cli/commands/tags/delete/__init__.py +1 -0
- cli/commands/tags/delete/command.py +47 -0
- cli/commands/tags/delete/presenter.py +10 -0
- cli/commands/tags/get/command.py +31 -0
- cli/commands/tags/get/presenter.py +23 -0
- cli/commands/tags/list/__init__.py +1 -0
- cli/commands/tags/list/command.py +52 -0
- cli/commands/tags/list/presenter.py +49 -0
- cli/commands/tags/update/command.py +64 -0
- cli/commands/tags/update/presenter.py +9 -0
- cli/commands/templates/__init__.py +0 -0
- cli/commands/templates/create/__init__.py +0 -0
- cli/commands/templates/create/command.py +152 -0
- cli/commands/templates/create/presenter.py +86 -0
- cli/commands/templates/delete/__init__.py +0 -0
- cli/commands/templates/delete/command.py +47 -0
- cli/commands/templates/delete/presenter.py +16 -0
- cli/commands/templates/get/__init__.py +0 -0
- cli/commands/templates/get/command.py +52 -0
- cli/commands/templates/get/presenter.py +233 -0
- cli/commands/templates/get_by_version/command.py +32 -0
- cli/commands/templates/get_by_version/presenter.py +30 -0
- cli/commands/templates/list/__init__.py +1 -0
- cli/commands/templates/list/command.py +102 -0
- cli/commands/templates/list/presenter.py +93 -0
- cli/commands/templates/render/__init__.py +0 -0
- cli/commands/templates/render/command.py +115 -0
- cli/commands/templates/render/presenter.py +276 -0
- cli/commands/templates/update/__init__.py +0 -0
- cli/commands/templates/update/command.py +199 -0
- cli/commands/templates/update/presenter.py +94 -0
- cli/commands/templates/version/__init__.py +1 -0
- cli/commands/templates/version/command.py +116 -0
- cli/commands/templates/version/presenter.py +100 -0
- cli/commands/tool_configs/__init__.py +0 -0
- cli/commands/tool_configs/create/__init__.py +0 -0
- cli/commands/tool_configs/create/command.py +118 -0
- cli/commands/tool_configs/create/presenter.py +53 -0
- cli/commands/tool_configs/delete/__init__.py +0 -0
- cli/commands/tool_configs/delete/command.py +47 -0
- cli/commands/tool_configs/delete/presenter.py +18 -0
- cli/commands/tool_configs/get/__init__.py +0 -0
- cli/commands/tool_configs/get/command.py +31 -0
- cli/commands/tool_configs/get/presenter.py +62 -0
- cli/commands/tool_configs/list/__init__.py +0 -0
- cli/commands/tool_configs/list/command.py +59 -0
- cli/commands/tool_configs/list/presenter.py +60 -0
- cli/commands/tool_configs/update/__init__.py +0 -0
- cli/commands/tool_configs/update/command.py +128 -0
- cli/commands/tool_configs/update/presenter.py +53 -0
- cli/commands/tools/__init__.py +1 -0
- cli/commands/tools/get/__init__.py +1 -0
- cli/commands/tools/get/command.py +42 -0
- cli/commands/tools/get/presenter.py +45 -0
- cli/commands/tools/list/__init__.py +1 -0
- cli/commands/tools/list/command.py +56 -0
- cli/commands/tools/list/presenter.py +44 -0
- cli/commands/users/__init__.py +0 -0
- cli/commands/users/create/command.py +53 -0
- cli/commands/users/create/presenter.py +9 -0
- cli/commands/whoami/__init__.py +0 -0
- cli/commands/whoami/command.py +42 -0
- cli/infrastructure/__init__.py +0 -0
- cli/infrastructure/auth_storage.py +71 -0
- cli/infrastructure/client_factory.py +36 -0
- cli/infrastructure/command.py +75 -0
- cli/infrastructure/config.py +188 -0
- cli/infrastructure/console.py +27 -0
- cli/infrastructure/editor.py +138 -0
- cli/infrastructure/error_display.py +178 -0
- cli/infrastructure/field_extractor.py +360 -0
- cli/infrastructure/file_content.py +210 -0
- cli/infrastructure/filter_parser.py +256 -0
- cli/infrastructure/formatters/__init__.py +0 -0
- cli/infrastructure/formatters/base.py +99 -0
- cli/infrastructure/formatters/compact_formatter.py +245 -0
- cli/infrastructure/formatters/json_formatter.py +84 -0
- cli/infrastructure/formatters/lines_formatter.py +102 -0
- cli/infrastructure/formatting/__init__.py +0 -0
- cli/infrastructure/formatting/fields.py +193 -0
- cli/infrastructure/forms/__init__.py +0 -0
- cli/infrastructure/forms/agent_picker.py +123 -0
- cli/infrastructure/forms/agent_tool_editor.py +384 -0
- cli/infrastructure/forms/agent_tools_manager.py +212 -0
- cli/infrastructure/forms/base_picker.py +469 -0
- cli/infrastructure/forms/components.py +126 -0
- cli/infrastructure/forms/json_schema_builder.py +149 -0
- cli/infrastructure/forms/model_picker.py +134 -0
- cli/infrastructure/forms/parsers.py +173 -0
- cli/infrastructure/forms/resolution_modal.py +302 -0
- cli/infrastructure/forms/schema_picker.py +137 -0
- cli/infrastructure/forms/tag_management_modal.py +103 -0
- cli/infrastructure/forms/tag_picker.py +207 -0
- cli/infrastructure/forms/template_picker.py +131 -0
- cli/infrastructure/forms/tool_config_picker.py +130 -0
- cli/infrastructure/forms/tool_picker.py +103 -0
- cli/infrastructure/injection/__init__.py +0 -0
- cli/infrastructure/injection/parser.py +302 -0
- cli/infrastructure/injection/resolver.py +399 -0
- cli/infrastructure/kv_parser.py +130 -0
- cli/infrastructure/local_storage.py +227 -0
- cli/infrastructure/macro_parser.py +215 -0
- cli/infrastructure/output.py +192 -0
- cli/infrastructure/provider_setup.py +81 -0
- cli/infrastructure/renderers/__init__.py +0 -0
- cli/infrastructure/renderers/entity_renderer.py +77 -0
- cli/infrastructure/renderers/list_renderer.py +114 -0
- cli/infrastructure/scope_utils.py +47 -0
- cli/infrastructure/spinner.py +101 -0
- cli/infrastructure/tui/__init__.py +0 -0
- cli/infrastructure/tui/clipboard.py +41 -0
- cli/infrastructure/tui/formatters.py +105 -0
- cli/infrastructure/tui/preview.py +14 -0
- cli/infrastructure/tui/selectable.py +198 -0
- cli/infrastructure/validation/__init__.py +0 -0
- cli/infrastructure/validation/tag_validation.py +74 -0
- cli/main.py +759 -0
- cli/tui/__init__.py +0 -0
- cli/tui/app.py +199 -0
- cli/tui/app_store.py +73 -0
- cli/tui/chat/__init__.py +0 -0
- cli/tui/chat/commands/__init__.py +0 -0
- cli/tui/chat/commands/base.py +65 -0
- cli/tui/chat/commands/create_session.py +135 -0
- cli/tui/chat/commands/load_session.py +119 -0
- cli/tui/chat/commands/regenerate.py +120 -0
- cli/tui/chat/commands/reload_session.py +63 -0
- cli/tui/chat/commands/send_message.py +190 -0
- cli/tui/chat/commands/undo.py +66 -0
- cli/tui/chat/editor.py +71 -0
- cli/tui/chat/messages.py +223 -0
- cli/tui/chat/pane.py +141 -0
- cli/tui/chat/renderers/__init__.py +0 -0
- cli/tui/chat/renderers/base.py +72 -0
- cli/tui/chat/renderers/markdown.py +250 -0
- cli/tui/chat/renderers/plain.py +83 -0
- cli/tui/chat/screen.py +1155 -0
- cli/tui/chat/services/__init__.py +0 -0
- cli/tui/chat/services/injection.py +386 -0
- cli/tui/chat/services/name_generator.py +256 -0
- cli/tui/chat/slash_commands.py +424 -0
- cli/tui/chat/store.py +280 -0
- cli/tui/chat/types.py +220 -0
- cli/tui/chat/widgets/__init__.py +0 -0
- cli/tui/chat/widgets/chat_header.py +75 -0
- cli/tui/chat/widgets/chat_input.py +362 -0
- cli/tui/chat/widgets/injection_popup.py +161 -0
- cli/tui/chat/widgets/message_display.py +287 -0
- cli/tui/chat/widgets/session_sidebar.py +214 -0
- cli/tui/chat/widgets/welcome_screen.py +290 -0
- cli/tui/screens/__init__.py +0 -0
- cli/tui/screens/agents.py +344 -0
- cli/tui/screens/base.py +301 -0
- cli/tui/screens/content.py +508 -0
- cli/tui/screens/dashboard.py +89 -0
- cli/tui/screens/models.py +96 -0
- cli/tui/screens/nav_screen.py +186 -0
- cli/tui/screens/schemas.py +522 -0
- cli/tui/screens/templates.py +734 -0
- cli/tui/screens/tool_configs.py +335 -0
- cli/tui/styles/__init__.py +0 -0
- cli/tui/widgets/__init__.py +0 -0
- cli/tui/widgets/agent_create_modal.py +139 -0
- cli/tui/widgets/agent_form_modal.py +659 -0
- cli/tui/widgets/agent_update_modal.py +299 -0
- cli/tui/widgets/base_form_modal.py +77 -0
- cli/tui/widgets/confirm_modal.py +75 -0
- cli/tui/widgets/help_modal.py +145 -0
- cli/tui/widgets/new_session_modal.py +328 -0
- cli/tui/widgets/schema_create_modal.py +271 -0
- cli/tui/widgets/schema_update_modal.py +188 -0
- cli/tui/widgets/status_footer.py +147 -0
- cli/tui/widgets/template_create_modal.py +502 -0
- cli/tui/widgets/template_update_modal.py +308 -0
- cli/tui/widgets/tool_config_create_modal.py +216 -0
- 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)
|