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,207 @@
|
|
|
1
|
+
"""Tag picker widget for selecting multiple tags.
|
|
2
|
+
|
|
3
|
+
A multi-select search picker that uses the ApiClient to fetch tags
|
|
4
|
+
and allows selecting multiple tags by their paths. Supports creating
|
|
5
|
+
new tags on-the-fly when the searched tag doesn't exist.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from textual.message import Message
|
|
11
|
+
from textual.widgets import OptionList
|
|
12
|
+
from textual.widgets.option_list import Option
|
|
13
|
+
|
|
14
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
15
|
+
|
|
16
|
+
from cli.infrastructure.forms.base_picker import MultiSelectPicker
|
|
17
|
+
from cli.infrastructure.validation.tag_validation import validate_tag_path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TagPicker(MultiSelectPicker[dict[str, Any]]):
|
|
21
|
+
"""Multi-select tag picker with live API search and on-the-fly tag creation.
|
|
22
|
+
|
|
23
|
+
Uses the ApiClient to fetch tags on each keystroke. Results appear in a
|
|
24
|
+
list below the search input. User can select multiple tags.
|
|
25
|
+
|
|
26
|
+
When searching for a tag that doesn't exist, the picker shows an option
|
|
27
|
+
to create the new tag. The tag will be auto-created on the server when
|
|
28
|
+
the content is saved.
|
|
29
|
+
|
|
30
|
+
Usage:
|
|
31
|
+
yield TagPicker(
|
|
32
|
+
client=server_client,
|
|
33
|
+
placeholder="Search tags...",
|
|
34
|
+
id="tags",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Get selected tag paths
|
|
38
|
+
picker = self.query_one("#tags", TagPicker)
|
|
39
|
+
tag_paths = picker.selected_tags # list[str]
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
class TagsChanged(Message):
|
|
43
|
+
"""Posted when selected tags change."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, selected_tags: list[str]) -> None:
|
|
46
|
+
self.selected_tags = selected_tags
|
|
47
|
+
super().__init__()
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
client: ApiClient,
|
|
52
|
+
placeholder: str = "Search tags...",
|
|
53
|
+
empty_message: str = "No tags selected (required)",
|
|
54
|
+
**kwargs: Any,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Initialize the tag picker.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
client: ApiClient instance for API calls
|
|
60
|
+
placeholder: Placeholder text for search input
|
|
61
|
+
empty_message: Message shown when nothing is selected
|
|
62
|
+
"""
|
|
63
|
+
super().__init__(
|
|
64
|
+
client=client,
|
|
65
|
+
placeholder=placeholder,
|
|
66
|
+
empty_message=empty_message,
|
|
67
|
+
**kwargs,
|
|
68
|
+
)
|
|
69
|
+
self._last_search_query: str = ""
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def selected_tags(self) -> list[str]:
|
|
73
|
+
"""Get the list of selected tag paths."""
|
|
74
|
+
return list(self._selected_items.keys())
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def selected_tag_items(self) -> list[dict[str, Any]]:
|
|
78
|
+
"""Get the full selected tag dicts."""
|
|
79
|
+
return list(self._selected_items.values())
|
|
80
|
+
|
|
81
|
+
async def _fetch_items(self, query: str) -> list[dict[str, Any]]:
|
|
82
|
+
"""Fetch tags matching query from the API."""
|
|
83
|
+
self._last_search_query = query
|
|
84
|
+
response = await self._client.list_tags(search=query, limit=50)
|
|
85
|
+
results = [tag.model_dump() for tag in response.tags]
|
|
86
|
+
|
|
87
|
+
# Check if query exactly matches any existing tag
|
|
88
|
+
exact_match_exists = any(tag.get("tag_path", "") == query for tag in results)
|
|
89
|
+
|
|
90
|
+
# If no exact match, check if we can offer to create the tag
|
|
91
|
+
if not exact_match_exists:
|
|
92
|
+
is_valid, _ = validate_tag_path(query)
|
|
93
|
+
if is_valid:
|
|
94
|
+
# Add synthetic "create new tag" option at the top
|
|
95
|
+
create_option = {
|
|
96
|
+
"tag_path": query,
|
|
97
|
+
"display_name": query,
|
|
98
|
+
"_is_new": True,
|
|
99
|
+
}
|
|
100
|
+
results.insert(0, create_option)
|
|
101
|
+
|
|
102
|
+
return results
|
|
103
|
+
|
|
104
|
+
def _format_item(self, item: dict[str, Any]) -> str:
|
|
105
|
+
"""Format tag for display in option list."""
|
|
106
|
+
tag_path = item.get("tag_path", "")
|
|
107
|
+
is_new = item.get("_is_new", False)
|
|
108
|
+
|
|
109
|
+
if is_new:
|
|
110
|
+
return f"[cyan][+] Create:[/] {tag_path}"
|
|
111
|
+
else:
|
|
112
|
+
display_name = item.get("display_name", tag_path)
|
|
113
|
+
if display_name and display_name != tag_path:
|
|
114
|
+
return f"{tag_path} ({display_name})"
|
|
115
|
+
return tag_path
|
|
116
|
+
|
|
117
|
+
def _get_item_id(self, item: dict[str, Any]) -> str:
|
|
118
|
+
"""Get unique identifier for a tag (its path)."""
|
|
119
|
+
return item.get("tag_path", "")
|
|
120
|
+
|
|
121
|
+
def _get_result_count_message(self, count: int) -> str:
|
|
122
|
+
"""Get status message for result count."""
|
|
123
|
+
# Count existing vs new tags
|
|
124
|
+
existing_count = sum(
|
|
125
|
+
1 for t in self._search_results if not t.get("_is_new", False)
|
|
126
|
+
)
|
|
127
|
+
has_create_option = any(t.get("_is_new", False) for t in self._search_results)
|
|
128
|
+
|
|
129
|
+
if existing_count == 0 and not has_create_option:
|
|
130
|
+
# No results and query is invalid
|
|
131
|
+
is_valid, error = validate_tag_path(self._last_search_query)
|
|
132
|
+
if not is_valid:
|
|
133
|
+
return f"[yellow]{error}[/]"
|
|
134
|
+
return "No tags found"
|
|
135
|
+
elif has_create_option and existing_count == 0:
|
|
136
|
+
return "No existing tags - Enter to create new tag"
|
|
137
|
+
elif has_create_option:
|
|
138
|
+
return f"{existing_count} tag{'s' if existing_count != 1 else ''} found - or create new"
|
|
139
|
+
else:
|
|
140
|
+
return f"{existing_count} tag{'s' if existing_count != 1 else ''} - Enter to toggle"
|
|
141
|
+
|
|
142
|
+
def _update_options(self) -> None:
|
|
143
|
+
"""Update the option list with current search results (custom styling for tags)."""
|
|
144
|
+
options = self.query_one("#search-options", OptionList)
|
|
145
|
+
options.clear_options()
|
|
146
|
+
|
|
147
|
+
for i, tag in enumerate(self._search_results):
|
|
148
|
+
tag_path = tag.get("tag_path", "")
|
|
149
|
+
is_new = tag.get("_is_new", False)
|
|
150
|
+
|
|
151
|
+
if is_new:
|
|
152
|
+
# Style "create new tag" option distinctly
|
|
153
|
+
if tag_path in self._selected_items:
|
|
154
|
+
display_text = f"[green]✓[/] [cyan][+] Create:[/] {tag_path}"
|
|
155
|
+
else:
|
|
156
|
+
display_text = f" [cyan][+] Create:[/] {tag_path}"
|
|
157
|
+
else:
|
|
158
|
+
# Existing tag with display name
|
|
159
|
+
display_name = tag.get("display_name", tag_path)
|
|
160
|
+
if display_name and display_name != tag_path:
|
|
161
|
+
display_text = f"{tag_path} ({display_name})"
|
|
162
|
+
else:
|
|
163
|
+
display_text = tag_path
|
|
164
|
+
|
|
165
|
+
# Mark selected tags with checkmark
|
|
166
|
+
if tag_path in self._selected_items:
|
|
167
|
+
display_text = f"[green]✓[/] {display_text}"
|
|
168
|
+
else:
|
|
169
|
+
display_text = f" {display_text}"
|
|
170
|
+
|
|
171
|
+
options.add_option(Option(display_text, id=f"opt-{i}"))
|
|
172
|
+
|
|
173
|
+
if self._search_results:
|
|
174
|
+
options.highlighted = 0
|
|
175
|
+
|
|
176
|
+
def _format_selection_display(self) -> str | None:
|
|
177
|
+
"""Format selected tags for display."""
|
|
178
|
+
if not self._selected_items:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
# Format tags, marking new ones with (new)
|
|
182
|
+
formatted_tags: list[str] = []
|
|
183
|
+
for tag_path, tag_data in self._selected_items.items():
|
|
184
|
+
if tag_data.get("_is_new", False):
|
|
185
|
+
formatted_tags.append(f"[cyan]{tag_path}[/] [dim](new)[/]")
|
|
186
|
+
else:
|
|
187
|
+
formatted_tags.append(tag_path)
|
|
188
|
+
|
|
189
|
+
paths = ", ".join(formatted_tags)
|
|
190
|
+
return f"[bold green]Selected ({len(self._selected_items)}):[/] {paths}"
|
|
191
|
+
|
|
192
|
+
def _post_selection_changed(self) -> None:
|
|
193
|
+
"""Post TagsChanged message with tag paths."""
|
|
194
|
+
self.post_message(self.TagsChanged(self.selected_tags))
|
|
195
|
+
|
|
196
|
+
def set_initial_selection(self, tags: list[dict[str, Any]]) -> None:
|
|
197
|
+
"""Pre-populate the picker with existing tag selections.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
tags: List of tag dicts with at least 'tag_path' key
|
|
201
|
+
"""
|
|
202
|
+
self._selected_items.clear()
|
|
203
|
+
for tag in tags:
|
|
204
|
+
tag_path = tag.get("tag_path", "")
|
|
205
|
+
if tag_path:
|
|
206
|
+
self._selected_items[tag_path] = tag
|
|
207
|
+
self._update_selected_display()
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Template picker component for selecting system instruction templates.
|
|
2
|
+
|
|
3
|
+
A specialized search picker that uses the ApiClient to fetch templates
|
|
4
|
+
and returns the selected template's latest version ID.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
10
|
+
from alloy_runtime_types.enums.template_enums import TemplateContentType
|
|
11
|
+
|
|
12
|
+
from cli.infrastructure.forms.base_picker import SingleSelectPicker
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _format_template(template: dict[str, Any]) -> str:
|
|
16
|
+
"""Format a template for display in the picker."""
|
|
17
|
+
name = template.get("name", "Unknown")
|
|
18
|
+
version = template.get("latest_version_number", "?")
|
|
19
|
+
visibility = template.get("visibility_id", "private")
|
|
20
|
+
content_type = template.get("content_type_id", "")
|
|
21
|
+
|
|
22
|
+
# Short visibility label
|
|
23
|
+
vis_label = {"private": "Private", "organization": "Org", "public": "Public"}.get(
|
|
24
|
+
visibility, visibility
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Include content type if not system_instruction (since that's the default filter)
|
|
28
|
+
type_label = ""
|
|
29
|
+
if content_type and content_type != "system_instruction":
|
|
30
|
+
type_label = f" [{content_type}]"
|
|
31
|
+
|
|
32
|
+
return f"{name} (v{version}) - {vis_label}{type_label}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TemplatePicker(SingleSelectPicker[dict[str, Any]]):
|
|
36
|
+
"""Template picker for selecting system instruction templates.
|
|
37
|
+
|
|
38
|
+
Searches templates via the ApiClient and returns the selected template's
|
|
39
|
+
latest version ID. User sees friendly template names, not UUIDs.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
yield TemplatePicker(
|
|
43
|
+
client=server_client,
|
|
44
|
+
id="system-template",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Get selected version ID
|
|
48
|
+
picker = self.query_one("#system-template", TemplatePicker)
|
|
49
|
+
version_id = picker.selected_id # UUID string or None
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
client: ApiClient,
|
|
55
|
+
content_type: TemplateContentType
|
|
56
|
+
| None = TemplateContentType.SYSTEM_INSTRUCTION,
|
|
57
|
+
placeholder: str = "Search templates (e.g., system prompt default)...",
|
|
58
|
+
empty_message: str = "No template selected (optional)",
|
|
59
|
+
**kwargs: Any,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Initialize the template picker.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
client: ApiClient instance for API calls
|
|
65
|
+
content_type: Filter by template content type (default: system_instruction)
|
|
66
|
+
placeholder: Placeholder text for search input
|
|
67
|
+
empty_message: Message shown when nothing is selected
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(
|
|
70
|
+
client=client,
|
|
71
|
+
placeholder=placeholder,
|
|
72
|
+
empty_message=empty_message,
|
|
73
|
+
**kwargs,
|
|
74
|
+
)
|
|
75
|
+
self._content_type = content_type
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def selected_version_id(self) -> str | None:
|
|
79
|
+
"""Alias for selected_id - returns the template version UUID."""
|
|
80
|
+
return self.selected_id
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def selected_template_name(self) -> str | None:
|
|
84
|
+
"""Get the name of the selected template."""
|
|
85
|
+
if self._selected_item is None:
|
|
86
|
+
return None
|
|
87
|
+
return self._selected_item.get("name")
|
|
88
|
+
|
|
89
|
+
async def _fetch_items(self, query: str) -> list[dict[str, Any]]:
|
|
90
|
+
"""Fetch templates matching query from the API."""
|
|
91
|
+
response = await self._client.list_templates(
|
|
92
|
+
search=query,
|
|
93
|
+
content_type=self._content_type,
|
|
94
|
+
limit=50,
|
|
95
|
+
)
|
|
96
|
+
return [template.model_dump() for template in response.templates]
|
|
97
|
+
|
|
98
|
+
def _format_item(self, item: dict[str, Any]) -> str:
|
|
99
|
+
"""Format template for display in option list."""
|
|
100
|
+
return _format_template(item)
|
|
101
|
+
|
|
102
|
+
def _get_item_id(self, item: dict[str, Any]) -> str:
|
|
103
|
+
"""Get unique identifier for a template (latest_version_id)."""
|
|
104
|
+
return str(item.get("latest_version_id", ""))
|
|
105
|
+
|
|
106
|
+
def _is_item_selected(self, item: dict[str, Any]) -> bool:
|
|
107
|
+
"""Check if an item is currently selected (by latest_version_id)."""
|
|
108
|
+
if self._selected_item is None:
|
|
109
|
+
return False
|
|
110
|
+
return self._get_item_id(item) == self._get_item_id(self._selected_item)
|
|
111
|
+
|
|
112
|
+
def set_initial_template(self, template: dict[str, Any]) -> None:
|
|
113
|
+
"""Pre-populate the picker with an existing template selection.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
template: Template dict with at least 'latest_version_id' and 'name' keys
|
|
117
|
+
"""
|
|
118
|
+
self.set_initial_selection(template)
|
|
119
|
+
|
|
120
|
+
def set_initial_template_by_version_id(
|
|
121
|
+
self, version_id: str, name: str = "Selected Template"
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Pre-populate the picker with a template by its version ID.
|
|
124
|
+
|
|
125
|
+
Use this when you only have the version ID and not the full template data.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
version_id: The template version UUID
|
|
129
|
+
name: Display name for the template
|
|
130
|
+
"""
|
|
131
|
+
self.set_initial_selection({"latest_version_id": version_id, "name": name})
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Tool config picker widget with search and single-select.
|
|
2
|
+
|
|
3
|
+
A search input that uses the ApiClient to fetch tool configs on each keystroke,
|
|
4
|
+
displaying results in a static list below. Filters by tool_id to only show
|
|
5
|
+
configs for a specific tool.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
11
|
+
from alloy_runtime_types.dtos.tool_configs import ToolConfigSummary
|
|
12
|
+
|
|
13
|
+
from cli.infrastructure.forms.base_picker import SingleSelectPicker
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolConfigPicker(SingleSelectPicker[ToolConfigSummary]):
|
|
17
|
+
"""Single-select tool config picker with live search.
|
|
18
|
+
|
|
19
|
+
Filters tool configs by tool_id to only show configs for the selected tool.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
yield ToolConfigPicker(
|
|
23
|
+
client=server_client,
|
|
24
|
+
tool_id="rag_search",
|
|
25
|
+
id="tool-config-picker"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Get selected config
|
|
29
|
+
picker = self.query_one("#tool-config-picker", ToolConfigPicker)
|
|
30
|
+
config = picker.selected_config # ToolConfigSummary | None
|
|
31
|
+
|
|
32
|
+
Keybindings:
|
|
33
|
+
- Type to search (fetches from API)
|
|
34
|
+
- Up/Down to navigate results
|
|
35
|
+
- Enter to select/deselect
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
client: ApiClient,
|
|
41
|
+
tool_id: str | None = None,
|
|
42
|
+
placeholder: str = "Type to search saved configs...",
|
|
43
|
+
**kwargs: Any,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Initialize the tool config picker.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
client: ApiClient instance for API calls
|
|
49
|
+
tool_id: Filter by tool ID (e.g., 'rag_search')
|
|
50
|
+
placeholder: Placeholder text for search input
|
|
51
|
+
"""
|
|
52
|
+
super().__init__(
|
|
53
|
+
client=client,
|
|
54
|
+
placeholder=placeholder,
|
|
55
|
+
empty_message="No saved config selected",
|
|
56
|
+
fetch_on_mount=True, # Load configs initially
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
59
|
+
self._tool_id = tool_id
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def tool_id(self) -> str | None:
|
|
63
|
+
"""Get the tool ID filter."""
|
|
64
|
+
return self._tool_id
|
|
65
|
+
|
|
66
|
+
@tool_id.setter
|
|
67
|
+
def tool_id(self, value: str | None) -> None:
|
|
68
|
+
"""Set the tool ID filter and refresh results."""
|
|
69
|
+
self._tool_id = value
|
|
70
|
+
# Clear current selection if tool changed
|
|
71
|
+
self.clear()
|
|
72
|
+
# Trigger a refresh
|
|
73
|
+
if self._fetch_on_mount:
|
|
74
|
+
self._fetch_initial()
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def selected_config(self) -> ToolConfigSummary | None:
|
|
78
|
+
"""Get the selected tool config."""
|
|
79
|
+
return self.selected_item
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def selected_config_id(self) -> str | None:
|
|
83
|
+
"""Get the ID of the selected tool config as string."""
|
|
84
|
+
if self.selected_item:
|
|
85
|
+
return str(self.selected_item.id)
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
async def _fetch_items(self, query: str) -> list[ToolConfigSummary]:
|
|
89
|
+
"""Fetch tool configs matching query from the API."""
|
|
90
|
+
if not self._tool_id:
|
|
91
|
+
# No tool selected yet, return empty list
|
|
92
|
+
return []
|
|
93
|
+
|
|
94
|
+
response = await self._client.list_tool_configs(
|
|
95
|
+
search=query if query else None,
|
|
96
|
+
tool_id=self._tool_id,
|
|
97
|
+
limit=50,
|
|
98
|
+
)
|
|
99
|
+
return list(response.tool_configs)
|
|
100
|
+
|
|
101
|
+
def _format_item(self, item: ToolConfigSummary) -> str:
|
|
102
|
+
"""Format tool config for display in option list."""
|
|
103
|
+
desc = ""
|
|
104
|
+
if item.description:
|
|
105
|
+
desc = (
|
|
106
|
+
f" - {item.description[:30]}..."
|
|
107
|
+
if len(item.description) > 30
|
|
108
|
+
else f" - {item.description}"
|
|
109
|
+
)
|
|
110
|
+
return f"{item.name}{desc}"
|
|
111
|
+
|
|
112
|
+
def _get_item_id(self, item: ToolConfigSummary) -> str:
|
|
113
|
+
"""Get unique identifier for a tool config."""
|
|
114
|
+
return str(item.id)
|
|
115
|
+
|
|
116
|
+
def _get_result_count_message(self, count: int) -> str:
|
|
117
|
+
"""Get status message for result count."""
|
|
118
|
+
if count == 0:
|
|
119
|
+
if not self._tool_id:
|
|
120
|
+
return "Select a tool first"
|
|
121
|
+
return "No saved configs found for this tool"
|
|
122
|
+
return f"{count} config{'s' if count != 1 else ''} found - Enter to select"
|
|
123
|
+
|
|
124
|
+
def set_initial_config(self, config: ToolConfigSummary) -> None:
|
|
125
|
+
"""Pre-populate the picker with an existing config selection.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
config: ToolConfigSummary object to pre-select
|
|
129
|
+
"""
|
|
130
|
+
self.set_initial_selection(config)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Tool picker widget with search and single-select.
|
|
2
|
+
|
|
3
|
+
A search input that uses the ApiClient to fetch available tools on each keystroke,
|
|
4
|
+
displaying results in a static list below. Users can select a single tool.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from alloy_runtime_sdk.api_client.client import ApiClient
|
|
10
|
+
from alloy_runtime_types.dtos.tools import ToolResponse
|
|
11
|
+
|
|
12
|
+
from cli.infrastructure.forms.base_picker import SingleSelectPicker
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ToolPicker(SingleSelectPicker[ToolResponse]):
|
|
16
|
+
"""Single-select tool picker with live search.
|
|
17
|
+
|
|
18
|
+
Uses the ApiClient to fetch tools on each keystroke. Results appear in a
|
|
19
|
+
static box below the search input.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
yield ToolPicker(
|
|
23
|
+
client=server_client,
|
|
24
|
+
id="tool-picker"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Get selected tool
|
|
28
|
+
picker = self.query_one("#tool-picker", ToolPicker)
|
|
29
|
+
tool = picker.selected_tool # ToolResponse | None
|
|
30
|
+
|
|
31
|
+
Keybindings:
|
|
32
|
+
- Type to search (fetches from API)
|
|
33
|
+
- Up/Down to navigate results
|
|
34
|
+
- Enter to select/deselect
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
client: ApiClient,
|
|
40
|
+
placeholder: str = "Type to search tools (e.g., rag, web, search)...",
|
|
41
|
+
**kwargs: Any,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Initialize the tool picker.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
client: ApiClient instance for API calls
|
|
47
|
+
placeholder: Placeholder text for search input
|
|
48
|
+
"""
|
|
49
|
+
super().__init__(
|
|
50
|
+
client=client,
|
|
51
|
+
placeholder=placeholder,
|
|
52
|
+
empty_message="No tool selected",
|
|
53
|
+
fetch_on_mount=True, # Load all tools initially
|
|
54
|
+
**kwargs,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def selected_tool(self) -> ToolResponse | None:
|
|
59
|
+
"""Get the selected tool."""
|
|
60
|
+
return self.selected_item
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def selected_tool_id(self) -> str | None:
|
|
64
|
+
"""Get the ID of the selected tool."""
|
|
65
|
+
return self.selected_id
|
|
66
|
+
|
|
67
|
+
async def _fetch_items(self, query: str) -> list[ToolResponse]:
|
|
68
|
+
"""Fetch tools matching query from the API."""
|
|
69
|
+
response = await self._client.list_tools(
|
|
70
|
+
search=query if query else None,
|
|
71
|
+
limit=50,
|
|
72
|
+
)
|
|
73
|
+
return list(response.tools)
|
|
74
|
+
|
|
75
|
+
def _format_item(self, item: ToolResponse) -> str:
|
|
76
|
+
"""Format tool for display in option list."""
|
|
77
|
+
has_config = item.parameter_schema is not None
|
|
78
|
+
config_indicator = " [dim][configurable][/]" if has_config else ""
|
|
79
|
+
# Truncate description if too long
|
|
80
|
+
desc = (
|
|
81
|
+
item.description[:50] + "..."
|
|
82
|
+
if len(item.description) > 50
|
|
83
|
+
else item.description
|
|
84
|
+
)
|
|
85
|
+
return f"{item.id}{config_indicator} - {desc}"
|
|
86
|
+
|
|
87
|
+
def _get_item_id(self, item: ToolResponse) -> str:
|
|
88
|
+
"""Get unique identifier for a tool."""
|
|
89
|
+
return item.id
|
|
90
|
+
|
|
91
|
+
def _get_result_count_message(self, count: int) -> str:
|
|
92
|
+
"""Get status message for result count."""
|
|
93
|
+
if count == 0:
|
|
94
|
+
return "No tools found"
|
|
95
|
+
return f"{count} tool{'s' if count != 1 else ''} found - Enter to select"
|
|
96
|
+
|
|
97
|
+
def set_initial_tool(self, tool: ToolResponse) -> None:
|
|
98
|
+
"""Pre-populate the picker with an existing tool selection.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
tool: ToolResponse object to pre-select
|
|
102
|
+
"""
|
|
103
|
+
self.set_initial_selection(tool)
|
|
File without changes
|