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,328 @@
|
|
|
1
|
+
"""New session modal for creating chat sessions.
|
|
2
|
+
|
|
3
|
+
Provides a modal screen that allows users to create a new chat session,
|
|
4
|
+
selecting either an agent or a direct model to use.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from textual import on
|
|
12
|
+
from textual.app import ComposeResult
|
|
13
|
+
from textual.binding import Binding
|
|
14
|
+
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
15
|
+
from textual.screen import ModalScreen
|
|
16
|
+
from textual.widgets import (
|
|
17
|
+
Button,
|
|
18
|
+
Input,
|
|
19
|
+
Label,
|
|
20
|
+
Static,
|
|
21
|
+
TabbedContent,
|
|
22
|
+
TabPane,
|
|
23
|
+
TextArea,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
27
|
+
from alloy_runtime_types.dtos.models import ModelListItemResponse
|
|
28
|
+
|
|
29
|
+
from cli.infrastructure.forms.agent_picker import AgentPicker
|
|
30
|
+
from cli.infrastructure.forms.base_picker import SingleSelectPicker
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class NewSessionConfig:
|
|
35
|
+
"""Configuration for a new chat session.
|
|
36
|
+
|
|
37
|
+
Supports two modes:
|
|
38
|
+
- Agent mode: Use agent_id/agent_name (model comes from agent config)
|
|
39
|
+
- Model mode: Use provider_key/provider_model_name directly
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
name: str | None
|
|
43
|
+
# Agent mode fields
|
|
44
|
+
agent_id: UUID | None = None
|
|
45
|
+
agent_name: str | None = None
|
|
46
|
+
# Model mode fields
|
|
47
|
+
provider_key: str | None = None
|
|
48
|
+
provider_model_name: str | None = None
|
|
49
|
+
system_instruction: str | None = None
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def is_model_mode(self) -> bool:
|
|
53
|
+
"""Check if this config uses direct model mode."""
|
|
54
|
+
return self.provider_key is not None and self.provider_model_name is not None
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_agent_mode(self) -> bool:
|
|
58
|
+
"""Check if this config uses agent mode."""
|
|
59
|
+
return self.agent_id is not None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class SingleModelPicker(SingleSelectPicker[ModelListItemResponse]):
|
|
63
|
+
"""Single-select model picker for the new session modal.
|
|
64
|
+
|
|
65
|
+
Unlike ModelPicker which allows multi-select for fallback models,
|
|
66
|
+
this picker only allows selecting one model for direct model chat.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
client: ApiClient,
|
|
72
|
+
placeholder: str = "Search models (e.g., gpt-4, claude, sonnet)...",
|
|
73
|
+
**kwargs: Any,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Initialize the model picker.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
client: ApiClient instance for API calls
|
|
79
|
+
placeholder: Placeholder text for search input
|
|
80
|
+
"""
|
|
81
|
+
super().__init__(
|
|
82
|
+
client=client,
|
|
83
|
+
placeholder=placeholder,
|
|
84
|
+
empty_message="No model selected",
|
|
85
|
+
**kwargs,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def selected_model(self) -> ModelListItemResponse | None:
|
|
90
|
+
"""Get the selected model."""
|
|
91
|
+
return self._selected_item
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def selected_provider_key(self) -> str | None:
|
|
95
|
+
"""Get the provider key of the selected model."""
|
|
96
|
+
if self._selected_item is None:
|
|
97
|
+
return None
|
|
98
|
+
return self._selected_item.provider_key
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def selected_model_name(self) -> str | None:
|
|
102
|
+
"""Get the model name of the selected model."""
|
|
103
|
+
if self._selected_item is None:
|
|
104
|
+
return None
|
|
105
|
+
return self._selected_item.provider_model_name
|
|
106
|
+
|
|
107
|
+
async def _fetch_items(self, query: str) -> list[ModelListItemResponse]:
|
|
108
|
+
"""Fetch models matching query from the API."""
|
|
109
|
+
response = await self._client.list_models(
|
|
110
|
+
search=query,
|
|
111
|
+
configured_only=True,
|
|
112
|
+
limit=50,
|
|
113
|
+
)
|
|
114
|
+
return list(response.models)
|
|
115
|
+
|
|
116
|
+
def _format_item(self, item: ModelListItemResponse) -> str:
|
|
117
|
+
"""Format model for display in option list."""
|
|
118
|
+
return f"{item.provider_key}:{item.provider_model_name}"
|
|
119
|
+
|
|
120
|
+
def _get_item_id(self, item: ModelListItemResponse) -> str:
|
|
121
|
+
"""Get unique identifier for a model."""
|
|
122
|
+
return f"{item.provider_key}:{item.provider_model_name}"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class NewSessionModal(ModalScreen[NewSessionConfig | None]):
|
|
126
|
+
"""Modal for creating a new chat session.
|
|
127
|
+
|
|
128
|
+
Allows users to optionally name the session and select either:
|
|
129
|
+
- An agent (uses agent's system instruction and models)
|
|
130
|
+
- A model directly (optionally with a custom system instruction)
|
|
131
|
+
|
|
132
|
+
Returns NewSessionConfig on create, or None if cancelled.
|
|
133
|
+
|
|
134
|
+
Usage:
|
|
135
|
+
def on_session_created(result: NewSessionConfig | None):
|
|
136
|
+
if result is not None:
|
|
137
|
+
# Create session with result
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
self.app.push_screen(
|
|
141
|
+
NewSessionModal(client=server_client),
|
|
142
|
+
on_session_created
|
|
143
|
+
)
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
BINDINGS = [
|
|
147
|
+
Binding("escape", "cancel", "Cancel", show=True),
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
DEFAULT_CSS = """
|
|
151
|
+
NewSessionModal {
|
|
152
|
+
align: center middle;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
NewSessionModal > Vertical {
|
|
156
|
+
width: 80;
|
|
157
|
+
max-height: 40;
|
|
158
|
+
background: $surface;
|
|
159
|
+
border: thick $primary;
|
|
160
|
+
padding: 1 2;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
NewSessionModal .modal-title {
|
|
164
|
+
text-align: center;
|
|
165
|
+
text-style: bold;
|
|
166
|
+
margin-bottom: 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
NewSessionModal .section-label {
|
|
170
|
+
margin-top: 1;
|
|
171
|
+
text-style: bold;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
NewSessionModal .help-text {
|
|
175
|
+
color: $text-muted;
|
|
176
|
+
margin-bottom: 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
NewSessionModal .button-row {
|
|
180
|
+
margin-top: 1;
|
|
181
|
+
height: auto;
|
|
182
|
+
align: center middle;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
NewSessionModal .button-row Button {
|
|
186
|
+
margin: 0 1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
NewSessionModal #session-name {
|
|
190
|
+
margin-bottom: 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
NewSessionModal TabbedContent {
|
|
194
|
+
height: auto;
|
|
195
|
+
max-height: 25;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
NewSessionModal TabPane {
|
|
199
|
+
height: auto;
|
|
200
|
+
padding: 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
NewSessionModal #system-instruction {
|
|
204
|
+
height: 5;
|
|
205
|
+
margin-top: 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
NewSessionModal #model-tab-scroll {
|
|
209
|
+
height: 100%;
|
|
210
|
+
max-height: 20;
|
|
211
|
+
}
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
client: ApiClient,
|
|
217
|
+
**kwargs: Any,
|
|
218
|
+
) -> None:
|
|
219
|
+
"""Initialize the new session modal.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
client: ApiClient instance for API calls
|
|
223
|
+
"""
|
|
224
|
+
super().__init__(**kwargs)
|
|
225
|
+
self._client = client
|
|
226
|
+
self._active_tab: str = "agent"
|
|
227
|
+
|
|
228
|
+
def compose(self) -> ComposeResult:
|
|
229
|
+
with Vertical():
|
|
230
|
+
yield Label("New Chat Session", classes="modal-title")
|
|
231
|
+
|
|
232
|
+
yield Static("Session Name (optional):", classes="section-label")
|
|
233
|
+
yield Input(
|
|
234
|
+
placeholder="Enter a name for this chat...",
|
|
235
|
+
id="session-name",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
with TabbedContent(id="mode-tabs"):
|
|
239
|
+
with TabPane("Agent", id="agent-tab"):
|
|
240
|
+
yield Static(
|
|
241
|
+
"Choose an agent to chat with. The agent's system instruction "
|
|
242
|
+
"and configured models will be used.",
|
|
243
|
+
classes="help-text",
|
|
244
|
+
)
|
|
245
|
+
yield AgentPicker(
|
|
246
|
+
client=self._client,
|
|
247
|
+
placeholder="Search for an agent...",
|
|
248
|
+
id="agent-picker",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
with TabPane("Model", id="model-tab"):
|
|
252
|
+
with VerticalScroll(id="model-tab-scroll"):
|
|
253
|
+
yield Static(
|
|
254
|
+
"Chat directly with a model. Optionally provide a system instruction.",
|
|
255
|
+
classes="help-text",
|
|
256
|
+
)
|
|
257
|
+
yield SingleModelPicker(
|
|
258
|
+
client=self._client,
|
|
259
|
+
placeholder="Search for a model...",
|
|
260
|
+
id="model-picker",
|
|
261
|
+
)
|
|
262
|
+
yield Static(
|
|
263
|
+
"System Instruction (optional):", classes="section-label"
|
|
264
|
+
)
|
|
265
|
+
yield TextArea(
|
|
266
|
+
id="system-instruction",
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
with Horizontal(classes="button-row"):
|
|
270
|
+
yield Button("Create", id="create-btn", variant="success")
|
|
271
|
+
yield Button("Cancel", id="cancel-btn", variant="default")
|
|
272
|
+
|
|
273
|
+
def on_mount(self) -> None:
|
|
274
|
+
"""Focus the session name input on mount."""
|
|
275
|
+
self.query_one("#session-name", Input).focus()
|
|
276
|
+
|
|
277
|
+
@on(TabbedContent.TabActivated)
|
|
278
|
+
def on_tab_activated(self, event: TabbedContent.TabActivated) -> None:
|
|
279
|
+
"""Track which tab is active."""
|
|
280
|
+
if event.pane.id == "agent-tab":
|
|
281
|
+
self._active_tab = "agent"
|
|
282
|
+
elif event.pane.id == "model-tab":
|
|
283
|
+
self._active_tab = "model"
|
|
284
|
+
|
|
285
|
+
@on(Button.Pressed, "#create-btn")
|
|
286
|
+
def on_create_pressed(self, event: Button.Pressed) -> None:
|
|
287
|
+
"""Create session and dismiss with config."""
|
|
288
|
+
name_input = self.query_one("#session-name", Input)
|
|
289
|
+
name = name_input.value.strip() or None
|
|
290
|
+
|
|
291
|
+
if self._active_tab == "agent":
|
|
292
|
+
# Agent mode
|
|
293
|
+
agent_picker = self.query_one("#agent-picker", AgentPicker)
|
|
294
|
+
agent_id_str = agent_picker.selected_agent_id
|
|
295
|
+
agent_id = UUID(agent_id_str) if agent_id_str else None
|
|
296
|
+
agent_name = agent_picker.selected_agent_name
|
|
297
|
+
|
|
298
|
+
config = NewSessionConfig(
|
|
299
|
+
name=name,
|
|
300
|
+
agent_id=agent_id,
|
|
301
|
+
agent_name=agent_name,
|
|
302
|
+
)
|
|
303
|
+
else:
|
|
304
|
+
# Model mode
|
|
305
|
+
model_picker = self.query_one("#model-picker", SingleModelPicker)
|
|
306
|
+
system_input = self.query_one("#system-instruction", TextArea)
|
|
307
|
+
|
|
308
|
+
provider_key = model_picker.selected_provider_key
|
|
309
|
+
model_name = model_picker.selected_model_name
|
|
310
|
+
system_instruction = system_input.text.strip() or None
|
|
311
|
+
|
|
312
|
+
config = NewSessionConfig(
|
|
313
|
+
name=name,
|
|
314
|
+
provider_key=provider_key,
|
|
315
|
+
provider_model_name=model_name,
|
|
316
|
+
system_instruction=system_instruction,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
self.dismiss(config)
|
|
320
|
+
|
|
321
|
+
@on(Button.Pressed, "#cancel-btn")
|
|
322
|
+
def on_cancel_pressed(self, event: Button.Pressed) -> None:
|
|
323
|
+
"""Cancel and dismiss with None."""
|
|
324
|
+
self.dismiss(None)
|
|
325
|
+
|
|
326
|
+
def action_cancel(self) -> None:
|
|
327
|
+
"""Cancel via escape key."""
|
|
328
|
+
self.dismiss(None)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Schema creation modal for creating new validation schemas.
|
|
2
|
+
|
|
3
|
+
Provides a comprehensive modal screen that allows users to create a new schema
|
|
4
|
+
with support for JSON Schema or Regex formats, including JSON example generation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
from textual import on, work
|
|
13
|
+
from textual.app import ComposeResult
|
|
14
|
+
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
15
|
+
from textual.widgets import (
|
|
16
|
+
Button,
|
|
17
|
+
Input,
|
|
18
|
+
Label,
|
|
19
|
+
RadioButton,
|
|
20
|
+
RadioSet,
|
|
21
|
+
Static,
|
|
22
|
+
TextArea,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from cli.infrastructure.scope_utils import get_scope_from_radioset
|
|
26
|
+
from cli.tui.widgets.base_form_modal import BaseFormModal
|
|
27
|
+
|
|
28
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
29
|
+
from alloy_runtime_types.dtos.schemas import CreateSchemaRequest
|
|
30
|
+
from alloy_runtime_types.enums.ownership_scope import OwnershipScope
|
|
31
|
+
from alloy_runtime_types.enums.schema_enums import SchemaFormat
|
|
32
|
+
|
|
33
|
+
from cli.infrastructure.forms.json_schema_builder import JsonSchemaBuilder
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from cli.tui.app import AlloyRuntimeApp
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class SchemaCreateResult:
|
|
41
|
+
"""Result of successful schema creation."""
|
|
42
|
+
|
|
43
|
+
schema_id: str
|
|
44
|
+
schema_name: str
|
|
45
|
+
version: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SchemaCreateModal(BaseFormModal[SchemaCreateResult]):
|
|
49
|
+
"""Modal for creating a new schema.
|
|
50
|
+
|
|
51
|
+
Provides a comprehensive form with:
|
|
52
|
+
- Required fields: name, schema format, definition
|
|
53
|
+
- Schema definition via TextArea (paste JSON schema or regex)
|
|
54
|
+
- JSON Schema generation from JSON example
|
|
55
|
+
- Optional fields: description
|
|
56
|
+
- Scope selection (user/organization/global)
|
|
57
|
+
|
|
58
|
+
Usage:
|
|
59
|
+
def on_schema_created(result: SchemaCreateResult | None):
|
|
60
|
+
if result is not None:
|
|
61
|
+
# Schema was created successfully
|
|
62
|
+
self.notify(f"Created schema: {result.schema_name}")
|
|
63
|
+
self._do_search("") # Refresh list
|
|
64
|
+
|
|
65
|
+
client = await self.app.store.get_client()
|
|
66
|
+
self.app.push_screen(
|
|
67
|
+
SchemaCreateModal(client=client),
|
|
68
|
+
on_schema_created
|
|
69
|
+
)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
app: "AlloyRuntimeApp"
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
client: ApiClient,
|
|
77
|
+
**kwargs: Any,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Initialize the schema creation modal.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
client: ApiClient instance for API calls
|
|
83
|
+
"""
|
|
84
|
+
super().__init__(**kwargs)
|
|
85
|
+
self._client = client
|
|
86
|
+
|
|
87
|
+
def compose(self) -> ComposeResult:
|
|
88
|
+
with VerticalScroll(id="schema-create-modal"):
|
|
89
|
+
yield Label("Create New Schema", classes="modal-title")
|
|
90
|
+
|
|
91
|
+
# ===== REQUIRED FIELDS =====
|
|
92
|
+
yield Static("[bold]Required[/]", classes="form-section")
|
|
93
|
+
|
|
94
|
+
yield Static(
|
|
95
|
+
"Name (lowercase, dots/dashes/underscores)", classes="field-label"
|
|
96
|
+
)
|
|
97
|
+
yield Input(
|
|
98
|
+
placeholder="e.g., user.profile, api.response-format",
|
|
99
|
+
id="name-input",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
yield Static("Schema Format", classes="field-label")
|
|
103
|
+
with RadioSet(id="format-radios"):
|
|
104
|
+
yield RadioButton("JSON Schema", id="format-json-schema", value=True)
|
|
105
|
+
yield RadioButton("Regex", id="format-regex")
|
|
106
|
+
|
|
107
|
+
yield Static("Scope", classes="field-label")
|
|
108
|
+
with RadioSet(id="scope-radios"):
|
|
109
|
+
yield RadioButton("User (personal)", id="scope-user", value=True)
|
|
110
|
+
yield RadioButton("Organization", id="scope-org")
|
|
111
|
+
yield RadioButton("Global", id="scope-global")
|
|
112
|
+
|
|
113
|
+
# ===== SCHEMA DEFINITION =====
|
|
114
|
+
yield Static("[bold]Schema Definition[/]", classes="form-section")
|
|
115
|
+
|
|
116
|
+
yield Static(
|
|
117
|
+
"Definition (paste JSON Schema or Regex pattern)", classes="field-label"
|
|
118
|
+
)
|
|
119
|
+
yield TextArea(
|
|
120
|
+
id="definition-textarea",
|
|
121
|
+
classes="definition-input",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# JSON Schema builder section (only visible for JSON Schema format)
|
|
125
|
+
with Vertical(id="json-builder-section"):
|
|
126
|
+
yield Static(
|
|
127
|
+
"Or: Generate from JSON example (JSON Schema only)",
|
|
128
|
+
classes="field-label",
|
|
129
|
+
)
|
|
130
|
+
yield JsonSchemaBuilder(id="schema-builder")
|
|
131
|
+
yield Button(
|
|
132
|
+
"Use Generated Schema",
|
|
133
|
+
id="use-generated-btn",
|
|
134
|
+
variant="primary",
|
|
135
|
+
disabled=True,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# ===== OPTIONAL FIELDS =====
|
|
139
|
+
yield Static("[bold]Optional[/]", classes="form-section")
|
|
140
|
+
|
|
141
|
+
yield Static("Description", classes="field-label")
|
|
142
|
+
yield Input(
|
|
143
|
+
placeholder="Brief description of this schema's purpose",
|
|
144
|
+
id="description-input",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Error display
|
|
148
|
+
yield Static("", id="error-display", classes="error-display")
|
|
149
|
+
|
|
150
|
+
with Horizontal(classes="button-row"):
|
|
151
|
+
yield Button("Create Schema", id="create-btn", variant="success")
|
|
152
|
+
yield Button("Cancel", id="cancel-btn", variant="default")
|
|
153
|
+
|
|
154
|
+
def on_mount(self) -> None:
|
|
155
|
+
"""Focus the name input on mount."""
|
|
156
|
+
self.query_one("#name-input", Input).focus()
|
|
157
|
+
|
|
158
|
+
@on(RadioSet.Changed, "#format-radios")
|
|
159
|
+
def on_format_changed(self, event: RadioSet.Changed) -> None:
|
|
160
|
+
"""Handle schema format change - show/hide JSON builder."""
|
|
161
|
+
json_builder_section = self.query_one("#json-builder-section", Vertical)
|
|
162
|
+
if event.pressed.id == "format-json-schema":
|
|
163
|
+
json_builder_section.display = True
|
|
164
|
+
else:
|
|
165
|
+
json_builder_section.display = False
|
|
166
|
+
# Clear the builder when switching to regex
|
|
167
|
+
self.query_one("#schema-builder", JsonSchemaBuilder).clear()
|
|
168
|
+
|
|
169
|
+
@on(JsonSchemaBuilder.SchemaChanged)
|
|
170
|
+
def on_schema_builder_changed(self, event: JsonSchemaBuilder.SchemaChanged) -> None:
|
|
171
|
+
"""Enable/disable the 'Use Generated Schema' button based on builder state."""
|
|
172
|
+
use_btn = self.query_one("#use-generated-btn", Button)
|
|
173
|
+
use_btn.disabled = event.schema is None
|
|
174
|
+
|
|
175
|
+
@on(Button.Pressed, "#use-generated-btn")
|
|
176
|
+
def on_use_generated_pressed(self, event: Button.Pressed) -> None:
|
|
177
|
+
"""Copy the generated schema to the definition textarea."""
|
|
178
|
+
builder = self.query_one("#schema-builder", JsonSchemaBuilder)
|
|
179
|
+
if builder.generated_schema:
|
|
180
|
+
schema_str = json.dumps(builder.generated_schema, indent=2)
|
|
181
|
+
self.query_one("#definition-textarea", TextArea).text = schema_str
|
|
182
|
+
self.app.notify(
|
|
183
|
+
"Generated schema copied to definition", severity="information"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@on(Button.Pressed, "#create-btn")
|
|
187
|
+
def on_create_pressed(self, event: Button.Pressed) -> None:
|
|
188
|
+
"""Handle create button press."""
|
|
189
|
+
self.action_submit()
|
|
190
|
+
|
|
191
|
+
@on(Button.Pressed, "#cancel-btn")
|
|
192
|
+
def on_cancel_pressed(self, event: Button.Pressed) -> None:
|
|
193
|
+
"""Handle cancel button press."""
|
|
194
|
+
self.dismiss(None)
|
|
195
|
+
|
|
196
|
+
def action_submit(self) -> None:
|
|
197
|
+
"""Handle form submission."""
|
|
198
|
+
try:
|
|
199
|
+
self._validate_fields()
|
|
200
|
+
self._hide_error()
|
|
201
|
+
self._finalize_submission()
|
|
202
|
+
except ValueError as e:
|
|
203
|
+
self._show_error(str(e))
|
|
204
|
+
|
|
205
|
+
def _validate_fields(self) -> None:
|
|
206
|
+
"""Validate all required fields."""
|
|
207
|
+
name = self.query_one("#name-input", Input).value.strip()
|
|
208
|
+
if not name:
|
|
209
|
+
raise ValueError("Name is required")
|
|
210
|
+
|
|
211
|
+
if not re.match(r"^[a-z0-9._-]+$", name):
|
|
212
|
+
raise ValueError(
|
|
213
|
+
"Name must be lowercase alphanumeric with dots, dashes, or underscores only"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
definition = self.query_one("#definition-textarea", TextArea).text.strip()
|
|
217
|
+
if not definition:
|
|
218
|
+
raise ValueError(
|
|
219
|
+
"Schema definition is required. Paste a JSON Schema or regex pattern."
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Validate JSON schema format
|
|
223
|
+
schema_format = self._get_schema_format()
|
|
224
|
+
if schema_format == SchemaFormat.JSON_SCHEMA:
|
|
225
|
+
try:
|
|
226
|
+
json.loads(definition)
|
|
227
|
+
except json.JSONDecodeError as e:
|
|
228
|
+
raise ValueError(f"Invalid JSON in schema definition: {e.msg}")
|
|
229
|
+
|
|
230
|
+
def _get_schema_format(self) -> SchemaFormat:
|
|
231
|
+
"""Get schema format from RadioSet."""
|
|
232
|
+
radios = self.query_one("#format-radios", RadioSet)
|
|
233
|
+
pressed = radios.pressed_button
|
|
234
|
+
if pressed is None or pressed.id == "format-json-schema":
|
|
235
|
+
return SchemaFormat.JSON_SCHEMA
|
|
236
|
+
return SchemaFormat.REGEX
|
|
237
|
+
|
|
238
|
+
def _get_scope(self) -> OwnershipScope:
|
|
239
|
+
"""Get scope from RadioSet."""
|
|
240
|
+
return get_scope_from_radioset(self.query_one("#scope-radios", RadioSet))
|
|
241
|
+
|
|
242
|
+
def _build_request(self) -> CreateSchemaRequest:
|
|
243
|
+
"""Build the API request."""
|
|
244
|
+
name = self.query_one("#name-input", Input).value.strip()
|
|
245
|
+
definition = self.query_one("#definition-textarea", TextArea).text.strip()
|
|
246
|
+
description = self.query_one("#description-input", Input).value.strip() or None
|
|
247
|
+
|
|
248
|
+
return CreateSchemaRequest(
|
|
249
|
+
name=name,
|
|
250
|
+
schema_format=self._get_schema_format(),
|
|
251
|
+
schema_definition=definition,
|
|
252
|
+
description=description,
|
|
253
|
+
scope=self._get_scope(),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
@work(exclusive=True)
|
|
257
|
+
async def _finalize_submission(self) -> None:
|
|
258
|
+
"""Submit to API."""
|
|
259
|
+
try:
|
|
260
|
+
request = self._build_request()
|
|
261
|
+
response = await self._client.create_schema(request)
|
|
262
|
+
|
|
263
|
+
result = SchemaCreateResult(
|
|
264
|
+
schema_id=str(response.id),
|
|
265
|
+
schema_name=response.schema_name,
|
|
266
|
+
version=response.version,
|
|
267
|
+
)
|
|
268
|
+
self.dismiss(result)
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
self._show_error(f"Failed to create schema: {e}")
|