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,1325 @@
|
|
|
1
|
+
"""Generate text command implementation."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated, Any, Optional, cast
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from cli.commands.generate.text.concurrent_renderer import ConcurrentRenderer
|
|
14
|
+
from cli.commands.generate.text.presenter import GenerateTextPresenter
|
|
15
|
+
from cli.commands.generate.text.stream_renderer import StreamRenderer
|
|
16
|
+
from cli.commands.shared_flags import (
|
|
17
|
+
AGENT,
|
|
18
|
+
MAX_TOKENS,
|
|
19
|
+
MESSAGE,
|
|
20
|
+
OUTPUT_FILE,
|
|
21
|
+
OUTPUT_MODE,
|
|
22
|
+
REASONING_EFFORT,
|
|
23
|
+
SCHEMA_DEF,
|
|
24
|
+
SCHEMA_FORMAT,
|
|
25
|
+
SCHEMA_REF,
|
|
26
|
+
SHOW_THINKING,
|
|
27
|
+
STREAM,
|
|
28
|
+
TAGS,
|
|
29
|
+
TEMPERATURE,
|
|
30
|
+
)
|
|
31
|
+
from cli.infrastructure.command import async_command, authenticated_client
|
|
32
|
+
from cli.infrastructure.console import get_console
|
|
33
|
+
from cli.infrastructure.editor import EditorError, open_editor
|
|
34
|
+
from cli.infrastructure.file_content import FileContentError, resolve_content
|
|
35
|
+
from cli.infrastructure.output import OutputService
|
|
36
|
+
from cli.infrastructure.spinner import spinner
|
|
37
|
+
from alloy_runtime_types.dtos.generation import (
|
|
38
|
+
AgentSource,
|
|
39
|
+
DirectModelSource,
|
|
40
|
+
GenerateTextRequest,
|
|
41
|
+
GenerateTextResponse,
|
|
42
|
+
GenerationParameters,
|
|
43
|
+
InlineOutputSchema,
|
|
44
|
+
SchemaReference,
|
|
45
|
+
)
|
|
46
|
+
from alloy_runtime_types.enums.provider import Provider
|
|
47
|
+
from alloy_runtime_types.enums.schema_enums import SchemaFormat as SchemaFormatEnum
|
|
48
|
+
|
|
49
|
+
# Type alias for repeatable --header option
|
|
50
|
+
REQUEST_HEADERS = Annotated[
|
|
51
|
+
Optional[list[str]],
|
|
52
|
+
typer.Option(
|
|
53
|
+
"-H",
|
|
54
|
+
"--header",
|
|
55
|
+
help="Request header (key:value). Repeatable. E.g., -H 'anthropic-beta:context-1m-2025-08-07'",
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def generate_text_command(
|
|
61
|
+
# Message (required - one of message or message-template)
|
|
62
|
+
message: Optional[str] = MESSAGE,
|
|
63
|
+
message_template: Optional[str] = typer.Option(
|
|
64
|
+
None,
|
|
65
|
+
"--message-template",
|
|
66
|
+
help="Template name/UUID for user message",
|
|
67
|
+
),
|
|
68
|
+
message_vars: Annotated[
|
|
69
|
+
Optional[list[str]],
|
|
70
|
+
typer.Option(
|
|
71
|
+
"-v",
|
|
72
|
+
"--var",
|
|
73
|
+
help="Variable (key=value or key:=json). Repeatable.",
|
|
74
|
+
),
|
|
75
|
+
] = None,
|
|
76
|
+
edit: bool = typer.Option(
|
|
77
|
+
False,
|
|
78
|
+
"-e",
|
|
79
|
+
"--edit",
|
|
80
|
+
help="Open $EDITOR to compose message",
|
|
81
|
+
),
|
|
82
|
+
# Model Selection (mutually exclusive with agent)
|
|
83
|
+
agent: Optional[str] = AGENT,
|
|
84
|
+
# Multi-agent comparison (mutually exclusive with agent and provider/model)
|
|
85
|
+
agents: Annotated[
|
|
86
|
+
Optional[list[str]],
|
|
87
|
+
typer.Option(
|
|
88
|
+
"-A",
|
|
89
|
+
"--agents",
|
|
90
|
+
help="Compare multiple agents. Repeatable.",
|
|
91
|
+
),
|
|
92
|
+
] = None,
|
|
93
|
+
provider: Optional[str] = typer.Option(
|
|
94
|
+
None,
|
|
95
|
+
"-p",
|
|
96
|
+
"--provider",
|
|
97
|
+
help="Provider (requires -M/--model)",
|
|
98
|
+
),
|
|
99
|
+
model: Optional[str] = typer.Option(
|
|
100
|
+
None,
|
|
101
|
+
"-M",
|
|
102
|
+
"--model",
|
|
103
|
+
help="Model name (requires -p/--provider)",
|
|
104
|
+
),
|
|
105
|
+
# System Instruction (optional)
|
|
106
|
+
system_instruction: Optional[str] = typer.Option(
|
|
107
|
+
None,
|
|
108
|
+
"--system",
|
|
109
|
+
help="System instruction (or @file)",
|
|
110
|
+
),
|
|
111
|
+
system_template: Optional[str] = typer.Option(
|
|
112
|
+
None,
|
|
113
|
+
"--system-template",
|
|
114
|
+
help="Template name/UUID for system instruction",
|
|
115
|
+
),
|
|
116
|
+
system_vars: Annotated[
|
|
117
|
+
Optional[list[str]],
|
|
118
|
+
typer.Option(
|
|
119
|
+
"--system-var",
|
|
120
|
+
help="System template var (key=value). Repeatable.",
|
|
121
|
+
),
|
|
122
|
+
] = None,
|
|
123
|
+
# Generation Parameters
|
|
124
|
+
temperature: Optional[float] = TEMPERATURE,
|
|
125
|
+
max_tokens: Optional[int] = MAX_TOKENS,
|
|
126
|
+
reasoning_effort: Optional[str] = REASONING_EFFORT,
|
|
127
|
+
# Additional Generation Parameters
|
|
128
|
+
top_p: Optional[float] = typer.Option(
|
|
129
|
+
None,
|
|
130
|
+
"--top-p",
|
|
131
|
+
min=0.0,
|
|
132
|
+
max=1.0,
|
|
133
|
+
help="Nucleus sampling parameter (0.0-1.0)",
|
|
134
|
+
),
|
|
135
|
+
top_k: Optional[int] = typer.Option(
|
|
136
|
+
None,
|
|
137
|
+
"--top-k",
|
|
138
|
+
min=1,
|
|
139
|
+
help="Top-k sampling parameter",
|
|
140
|
+
),
|
|
141
|
+
frequency_penalty: Optional[float] = typer.Option(
|
|
142
|
+
None,
|
|
143
|
+
"--frequency-penalty",
|
|
144
|
+
min=-2.0,
|
|
145
|
+
max=2.0,
|
|
146
|
+
help="Frequency penalty (-2.0 to 2.0)",
|
|
147
|
+
),
|
|
148
|
+
presence_penalty: Optional[float] = typer.Option(
|
|
149
|
+
None,
|
|
150
|
+
"--presence-penalty",
|
|
151
|
+
min=-2.0,
|
|
152
|
+
max=2.0,
|
|
153
|
+
help="Presence penalty (-2.0 to 2.0)",
|
|
154
|
+
),
|
|
155
|
+
seed: Optional[int] = typer.Option(
|
|
156
|
+
None,
|
|
157
|
+
"--seed",
|
|
158
|
+
help="Random seed for reproducible outputs",
|
|
159
|
+
),
|
|
160
|
+
stop: Optional[str] = typer.Option(
|
|
161
|
+
None,
|
|
162
|
+
"--stop",
|
|
163
|
+
help="Stop sequence(s) - comma-separated for multiple",
|
|
164
|
+
),
|
|
165
|
+
logprobs: Optional[bool] = typer.Option(
|
|
166
|
+
None,
|
|
167
|
+
"--logprobs/--no-logprobs",
|
|
168
|
+
help="Include log probabilities in response",
|
|
169
|
+
),
|
|
170
|
+
top_logprobs: Optional[int] = typer.Option(
|
|
171
|
+
None,
|
|
172
|
+
"--top-logprobs",
|
|
173
|
+
min=1,
|
|
174
|
+
max=20,
|
|
175
|
+
help="Number of top log probabilities to return (1-20)",
|
|
176
|
+
),
|
|
177
|
+
user: Optional[str] = typer.Option(
|
|
178
|
+
None,
|
|
179
|
+
"--user",
|
|
180
|
+
help="End-user ID for tracking and abuse monitoring",
|
|
181
|
+
),
|
|
182
|
+
# Session and Context
|
|
183
|
+
session_id: Optional[UUID] = typer.Option(
|
|
184
|
+
None,
|
|
185
|
+
"--session",
|
|
186
|
+
help="Session ID for multi-turn",
|
|
187
|
+
),
|
|
188
|
+
tags: Optional[str] = TAGS,
|
|
189
|
+
# Display Options
|
|
190
|
+
stream: bool = STREAM,
|
|
191
|
+
show_thinking: bool = SHOW_THINKING,
|
|
192
|
+
# Concurrent Execution
|
|
193
|
+
count: int = typer.Option(
|
|
194
|
+
1,
|
|
195
|
+
"--count",
|
|
196
|
+
min=1,
|
|
197
|
+
max=10,
|
|
198
|
+
help="Concurrent executions (1-10)",
|
|
199
|
+
),
|
|
200
|
+
# Output Schema Options
|
|
201
|
+
schema: Optional[str] = SCHEMA_REF,
|
|
202
|
+
schema_format: Optional[str] = SCHEMA_FORMAT,
|
|
203
|
+
schema_def: Optional[str] = SCHEMA_DEF,
|
|
204
|
+
# Output Options
|
|
205
|
+
output: str = OUTPUT_MODE,
|
|
206
|
+
output_file: Optional[str] = OUTPUT_FILE,
|
|
207
|
+
# Request Headers (for direct model mode)
|
|
208
|
+
headers: REQUEST_HEADERS = None,
|
|
209
|
+
# Async Execution Options
|
|
210
|
+
run_async: bool = typer.Option(
|
|
211
|
+
False,
|
|
212
|
+
"--async",
|
|
213
|
+
help="Submit for background processing. Returns execution_id for polling.",
|
|
214
|
+
),
|
|
215
|
+
external_id: Optional[str] = typer.Option(
|
|
216
|
+
None,
|
|
217
|
+
"--external-id",
|
|
218
|
+
help="External identifier for idempotency and tracking",
|
|
219
|
+
),
|
|
220
|
+
billing_project_id: Optional[UUID] = typer.Option(
|
|
221
|
+
None,
|
|
222
|
+
"--billing-project",
|
|
223
|
+
help="Billing project ID for cost tracking",
|
|
224
|
+
),
|
|
225
|
+
parent_execution_id: Optional[UUID] = typer.Option(
|
|
226
|
+
None,
|
|
227
|
+
"--parent-execution",
|
|
228
|
+
help="Parent execution ID for lineage tracking",
|
|
229
|
+
),
|
|
230
|
+
execution_purpose: Optional[str] = typer.Option(
|
|
231
|
+
None,
|
|
232
|
+
"--execution-purpose",
|
|
233
|
+
help="Label for why this execution exists (e.g., 'batch_export', 'testing')",
|
|
234
|
+
),
|
|
235
|
+
max_history_messages: Optional[int] = typer.Option(
|
|
236
|
+
None,
|
|
237
|
+
"--max-history",
|
|
238
|
+
min=1,
|
|
239
|
+
max=1000,
|
|
240
|
+
help="Limit chat history to N most recent messages",
|
|
241
|
+
),
|
|
242
|
+
regenerate: bool = typer.Option(
|
|
243
|
+
False,
|
|
244
|
+
"--regenerate",
|
|
245
|
+
help="Regenerate response to the last user message in the session",
|
|
246
|
+
),
|
|
247
|
+
chat_history: Optional[str] = typer.Option(
|
|
248
|
+
None,
|
|
249
|
+
"--chat-history",
|
|
250
|
+
help="Chat history as JSON or @file.json",
|
|
251
|
+
),
|
|
252
|
+
user_metadata: Optional[str] = typer.Option(
|
|
253
|
+
None,
|
|
254
|
+
"--user-metadata",
|
|
255
|
+
help="User metadata as JSON or @file.json",
|
|
256
|
+
),
|
|
257
|
+
webhook_url: Optional[str] = typer.Option(
|
|
258
|
+
None,
|
|
259
|
+
"--webhook-url",
|
|
260
|
+
help="Webhook URL for completion notification",
|
|
261
|
+
),
|
|
262
|
+
tools: Optional[str] = typer.Option(
|
|
263
|
+
None,
|
|
264
|
+
"--tools",
|
|
265
|
+
help="Tool definitions as JSON or @file.json (direct model mode)",
|
|
266
|
+
),
|
|
267
|
+
) -> None:
|
|
268
|
+
"""Generate text using AI models.
|
|
269
|
+
|
|
270
|
+
Use a pre-configured agent or specify provider/model directly.
|
|
271
|
+
Supports streaming, concurrent execution, and multi-agent comparison.
|
|
272
|
+
|
|
273
|
+
Examples:
|
|
274
|
+
|
|
275
|
+
# With agent
|
|
276
|
+
ai generate text -a my-agent -m "Write a haiku"
|
|
277
|
+
|
|
278
|
+
# With provider/model
|
|
279
|
+
ai generate text -p anthropic -M claude-3-sonnet -m "Explain decorators"
|
|
280
|
+
|
|
281
|
+
# Concurrent executions
|
|
282
|
+
ai generate text -a my-agent -m "Random fact" --count 3
|
|
283
|
+
|
|
284
|
+
# Multi-agent comparison
|
|
285
|
+
ai generate text -A agent1 -A agent2 -m "Explain AI"
|
|
286
|
+
|
|
287
|
+
# With template and variables
|
|
288
|
+
ai generate text -a my-agent --message-template tpl -v topic=AI
|
|
289
|
+
|
|
290
|
+
# Compose in editor
|
|
291
|
+
ai generate text -a my-agent -e
|
|
292
|
+
|
|
293
|
+
# Output to clipboard or file
|
|
294
|
+
ai generate text -a my-agent -m "Story" -o clipboard
|
|
295
|
+
ai generate text -a my-agent -m "Story" -o file -O out.txt
|
|
296
|
+
|
|
297
|
+
# Structured output with schema
|
|
298
|
+
ai generate text -p openai -M gpt-4o -m "List colors" --schema my-schema
|
|
299
|
+
|
|
300
|
+
# With custom headers (e.g., Anthropic 1M context beta)
|
|
301
|
+
ai generate text -p anthropic -M claude-sonnet-4-5 -m @big-file.txt -H 'anthropic-beta:context-1m-2025-08-07'
|
|
302
|
+
"""
|
|
303
|
+
# Resolve @file references for message, system_instruction, and schema_def
|
|
304
|
+
try:
|
|
305
|
+
message = resolve_content(message)
|
|
306
|
+
system_instruction = resolve_content(system_instruction)
|
|
307
|
+
schema_def = resolve_content(schema_def)
|
|
308
|
+
except FileContentError as e:
|
|
309
|
+
raise typer.BadParameter(str(e))
|
|
310
|
+
|
|
311
|
+
# Handle --edit flag: open editor to compose message
|
|
312
|
+
if edit:
|
|
313
|
+
if message_template:
|
|
314
|
+
raise typer.BadParameter(
|
|
315
|
+
"Cannot use --edit with --message-template. "
|
|
316
|
+
"Use --edit with --message or alone."
|
|
317
|
+
)
|
|
318
|
+
try:
|
|
319
|
+
edited_message = open_editor(initial_content=message or "")
|
|
320
|
+
if edited_message is None:
|
|
321
|
+
raise typer.Exit(code=1)
|
|
322
|
+
# Strip whitespace and check for empty content
|
|
323
|
+
edited_message = edited_message.strip()
|
|
324
|
+
if not edited_message:
|
|
325
|
+
console = get_console()
|
|
326
|
+
console.print("[yellow]No message entered. Aborting.[/yellow]")
|
|
327
|
+
raise typer.Exit(code=1)
|
|
328
|
+
message = edited_message
|
|
329
|
+
except EditorError as e:
|
|
330
|
+
console = get_console()
|
|
331
|
+
console.print(f"[red]Editor error:[/red] {e}")
|
|
332
|
+
raise typer.Exit(code=1)
|
|
333
|
+
|
|
334
|
+
# Validate output options
|
|
335
|
+
if output not in ("console", "clipboard", "file"):
|
|
336
|
+
raise typer.BadParameter(
|
|
337
|
+
f"Invalid output '{output}'. Valid options: console, clipboard, file"
|
|
338
|
+
)
|
|
339
|
+
if output == "file" and not output_file:
|
|
340
|
+
raise typer.BadParameter("--output-file is required when using --output=file")
|
|
341
|
+
|
|
342
|
+
# Validate output schema options
|
|
343
|
+
if schema and (schema_format or schema_def):
|
|
344
|
+
raise typer.BadParameter(
|
|
345
|
+
"--schema cannot be combined with --schema-format/--schema-def. "
|
|
346
|
+
"Use --schema for existing schemas, or --schema-format + --schema-def for inline."
|
|
347
|
+
)
|
|
348
|
+
if (schema_format and not schema_def) or (schema_def and not schema_format):
|
|
349
|
+
raise typer.BadParameter(
|
|
350
|
+
"--schema-format and --schema-def must be used together"
|
|
351
|
+
)
|
|
352
|
+
if schema_format and schema_format not in ("json_schema", "regex"):
|
|
353
|
+
valid = ", ".join(f.value for f in SchemaFormatEnum)
|
|
354
|
+
raise typer.BadParameter(
|
|
355
|
+
f"Invalid schema format '{schema_format}'. Valid: {valid}"
|
|
356
|
+
)
|
|
357
|
+
# Output schema cannot be used with agents (agents have their own)
|
|
358
|
+
if (schema or schema_format) and (agent or agents):
|
|
359
|
+
raise typer.BadParameter(
|
|
360
|
+
"--schema/--schema-format cannot be used with --agent or --agents. "
|
|
361
|
+
"Agents have their own output schema configuration."
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Headers can only be used with direct model mode (not agents)
|
|
365
|
+
if headers and (agent or agents):
|
|
366
|
+
raise typer.BadParameter(
|
|
367
|
+
"--header cannot be used with --agent or --agents. "
|
|
368
|
+
"For agents, configure request_headers in the agent's settings."
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# Validate --async and --stream are mutually exclusive
|
|
372
|
+
if run_async and stream:
|
|
373
|
+
raise typer.BadParameter(
|
|
374
|
+
"--async cannot be used with --stream. "
|
|
375
|
+
"Use --async for background processing or --stream for real-time responses."
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Validate --async incompatible with concurrent execution
|
|
379
|
+
if run_async and count > 1:
|
|
380
|
+
raise typer.BadParameter(
|
|
381
|
+
"--async cannot be used with --count. "
|
|
382
|
+
"Async execution does not support concurrent runs."
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Validate --async incompatible with multi-agent mode
|
|
386
|
+
if run_async and agents:
|
|
387
|
+
raise typer.BadParameter(
|
|
388
|
+
"--async cannot be used with --agents. "
|
|
389
|
+
"Async execution does not support multi-agent comparison."
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# Validate --tools can only be used with direct model mode (not agents)
|
|
393
|
+
if tools and (agent or agents):
|
|
394
|
+
raise typer.BadParameter(
|
|
395
|
+
"--tools cannot be used with --agent or --agents. "
|
|
396
|
+
"For agents, configure tools in the agent's settings."
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Validate mutually exclusive options
|
|
400
|
+
_validate_arguments(
|
|
401
|
+
message=message,
|
|
402
|
+
message_template=message_template,
|
|
403
|
+
agent=agent,
|
|
404
|
+
agents=agents,
|
|
405
|
+
provider=provider,
|
|
406
|
+
model=model,
|
|
407
|
+
count=count,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Execute the command
|
|
411
|
+
# Parse variables from key=value format to dict
|
|
412
|
+
from cli.infrastructure.kv_parser import parse_kv_pairs
|
|
413
|
+
|
|
414
|
+
parsed_message_vars = parse_kv_pairs(message_vars)
|
|
415
|
+
parsed_system_vars = parse_kv_pairs(system_vars)
|
|
416
|
+
|
|
417
|
+
# Parse headers from key:value format to dict
|
|
418
|
+
parsed_headers: dict[str, str] | None = None
|
|
419
|
+
if headers:
|
|
420
|
+
parsed_headers = {}
|
|
421
|
+
for h in headers:
|
|
422
|
+
if ":" not in h:
|
|
423
|
+
raise typer.BadParameter(
|
|
424
|
+
f"Invalid header format '{h}'. Use 'key:value' format."
|
|
425
|
+
)
|
|
426
|
+
key, value = h.split(":", 1)
|
|
427
|
+
parsed_headers[key.strip()] = value.strip()
|
|
428
|
+
|
|
429
|
+
# Helper function to parse JSON or @file content
|
|
430
|
+
def parse_json_or_file(value: str | None, param_name: str) -> Any | None:
|
|
431
|
+
if value is None:
|
|
432
|
+
return None
|
|
433
|
+
try:
|
|
434
|
+
resolved = resolve_content(value)
|
|
435
|
+
except FileContentError as e:
|
|
436
|
+
raise typer.BadParameter(str(e))
|
|
437
|
+
if resolved is None:
|
|
438
|
+
return None
|
|
439
|
+
try:
|
|
440
|
+
return json.loads(resolved)
|
|
441
|
+
except json.JSONDecodeError as e:
|
|
442
|
+
raise typer.BadParameter(f"Invalid JSON for {param_name}: {e}")
|
|
443
|
+
|
|
444
|
+
# Parse JSON options
|
|
445
|
+
parsed_chat_history = parse_json_or_file(chat_history, "--chat-history")
|
|
446
|
+
parsed_user_metadata = parse_json_or_file(user_metadata, "--user-metadata")
|
|
447
|
+
parsed_tools = parse_json_or_file(tools, "--tools")
|
|
448
|
+
|
|
449
|
+
# Parse stop sequences (can be single string or comma-separated)
|
|
450
|
+
parsed_stop: str | list[str] | None = None
|
|
451
|
+
if stop:
|
|
452
|
+
if "," in stop:
|
|
453
|
+
parsed_stop = [s.strip() for s in stop.split(",") if s.strip()]
|
|
454
|
+
else:
|
|
455
|
+
parsed_stop = stop
|
|
456
|
+
|
|
457
|
+
_execute_generate_text(
|
|
458
|
+
message=message,
|
|
459
|
+
message_template=message_template,
|
|
460
|
+
message_vars=parsed_message_vars,
|
|
461
|
+
agent=agent,
|
|
462
|
+
agents=agents,
|
|
463
|
+
provider=provider,
|
|
464
|
+
model=model,
|
|
465
|
+
system_instruction=system_instruction,
|
|
466
|
+
system_template=system_template,
|
|
467
|
+
system_vars=parsed_system_vars,
|
|
468
|
+
temperature=temperature,
|
|
469
|
+
max_tokens=max_tokens,
|
|
470
|
+
reasoning_effort=reasoning_effort,
|
|
471
|
+
top_p=top_p,
|
|
472
|
+
top_k=top_k,
|
|
473
|
+
frequency_penalty=frequency_penalty,
|
|
474
|
+
presence_penalty=presence_penalty,
|
|
475
|
+
seed=seed,
|
|
476
|
+
stop=parsed_stop,
|
|
477
|
+
logprobs=logprobs,
|
|
478
|
+
top_logprobs=top_logprobs,
|
|
479
|
+
user=user,
|
|
480
|
+
session_id=session_id,
|
|
481
|
+
tags=tags,
|
|
482
|
+
stream=stream,
|
|
483
|
+
show_thinking=show_thinking,
|
|
484
|
+
count=count,
|
|
485
|
+
output_mode=output,
|
|
486
|
+
output_file=output_file,
|
|
487
|
+
schema=schema,
|
|
488
|
+
schema_format=schema_format,
|
|
489
|
+
schema_def=schema_def,
|
|
490
|
+
request_headers=parsed_headers,
|
|
491
|
+
run_async=run_async,
|
|
492
|
+
external_id=external_id,
|
|
493
|
+
billing_project_id=billing_project_id,
|
|
494
|
+
max_history_messages=max_history_messages,
|
|
495
|
+
regenerate=regenerate,
|
|
496
|
+
chat_history=parsed_chat_history,
|
|
497
|
+
user_metadata=parsed_user_metadata,
|
|
498
|
+
webhook_url=webhook_url,
|
|
499
|
+
tools=parsed_tools,
|
|
500
|
+
parent_execution_id=parent_execution_id,
|
|
501
|
+
execution_purpose=execution_purpose,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _validate_arguments(
|
|
506
|
+
message: str | None,
|
|
507
|
+
message_template: str | None,
|
|
508
|
+
agent: str | None,
|
|
509
|
+
agents: list[str] | None,
|
|
510
|
+
provider: str | None,
|
|
511
|
+
model: str | None,
|
|
512
|
+
count: int,
|
|
513
|
+
) -> None:
|
|
514
|
+
"""Validate command arguments.
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
message: User message text
|
|
518
|
+
message_template: User message template name or UUID
|
|
519
|
+
agent: Agent name or UUID
|
|
520
|
+
agents: List of agent names/UUIDs for multi-agent comparison
|
|
521
|
+
provider: Provider key
|
|
522
|
+
model: Model name
|
|
523
|
+
count: Concurrent execution count
|
|
524
|
+
|
|
525
|
+
Raises:
|
|
526
|
+
typer.BadParameter: If validation fails
|
|
527
|
+
"""
|
|
528
|
+
# Require message OR message_template
|
|
529
|
+
if not message and not message_template:
|
|
530
|
+
raise typer.BadParameter(
|
|
531
|
+
"Either --message (-m) or --message-template is required"
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
if message and message_template:
|
|
535
|
+
raise typer.BadParameter("Cannot specify both --message and --message-template")
|
|
536
|
+
|
|
537
|
+
# Multi-agent mode validation
|
|
538
|
+
if agents:
|
|
539
|
+
if agent:
|
|
540
|
+
raise typer.BadParameter(
|
|
541
|
+
"Cannot specify both --agent (-a) and --agents (-A)"
|
|
542
|
+
)
|
|
543
|
+
if provider or model:
|
|
544
|
+
raise typer.BadParameter(
|
|
545
|
+
"Cannot specify both --agents (-A) and --provider/--model"
|
|
546
|
+
)
|
|
547
|
+
if len(agents) < 2:
|
|
548
|
+
raise typer.BadParameter(
|
|
549
|
+
"--agents (-A) requires at least 2 agents for comparison"
|
|
550
|
+
)
|
|
551
|
+
if len(agents) > 10:
|
|
552
|
+
raise typer.BadParameter("--agents (-A) supports a maximum of 10 agents")
|
|
553
|
+
if count > 1:
|
|
554
|
+
raise typer.BadParameter(
|
|
555
|
+
"Cannot use --count with --agents. Each agent runs once."
|
|
556
|
+
)
|
|
557
|
+
return # Skip other validations in multi-agent mode
|
|
558
|
+
|
|
559
|
+
# Require agent OR (provider AND model)
|
|
560
|
+
if not agent and not (provider and model):
|
|
561
|
+
raise typer.BadParameter(
|
|
562
|
+
"Either --agent (-a) or both --provider (-p) and --model are required"
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
if agent and (provider or model):
|
|
566
|
+
raise typer.BadParameter("Cannot specify both --agent and --provider/--model")
|
|
567
|
+
|
|
568
|
+
# Validate count range
|
|
569
|
+
if count < 1 or count > 10:
|
|
570
|
+
raise typer.BadParameter("--count must be between 1 and 10")
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def _copy_to_clipboard(content: str, console: Console) -> bool:
|
|
574
|
+
"""Copy generated content to clipboard.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
content: Generated text content
|
|
578
|
+
console: Rich console for status messages
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
True if copy succeeded, False otherwise
|
|
582
|
+
"""
|
|
583
|
+
try:
|
|
584
|
+
import pyperclip
|
|
585
|
+
|
|
586
|
+
pyperclip.copy(content)
|
|
587
|
+
console.print(
|
|
588
|
+
f"[green]Copied to clipboard[/green] ({len(content):,} characters)"
|
|
589
|
+
)
|
|
590
|
+
return True
|
|
591
|
+
except ImportError:
|
|
592
|
+
console.print(
|
|
593
|
+
"[yellow]pyperclip not installed. Install with: pip install pyperclip[/yellow]"
|
|
594
|
+
)
|
|
595
|
+
console.print("[dim]Falling back to console output...[/dim]")
|
|
596
|
+
console.print(content)
|
|
597
|
+
return False
|
|
598
|
+
except Exception as e:
|
|
599
|
+
console.print(f"[red]Failed to copy to clipboard:[/red] {e}")
|
|
600
|
+
console.print("[dim]Falling back to console output...[/dim]")
|
|
601
|
+
console.print(content)
|
|
602
|
+
return False
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _write_output_file(output_file: str, content: str, console: Console) -> None:
|
|
606
|
+
"""Write generated content to a file.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
output_file: File path to write to
|
|
610
|
+
content: Generated text content
|
|
611
|
+
console: Rich console for status messages
|
|
612
|
+
"""
|
|
613
|
+
try:
|
|
614
|
+
path = Path(output_file)
|
|
615
|
+
# Create parent directories if they don't exist
|
|
616
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
617
|
+
path.write_text(content, encoding="utf-8")
|
|
618
|
+
console.print(f"[green]Output written to:[/green] {output_file}")
|
|
619
|
+
except OSError as e:
|
|
620
|
+
console.print(f"[red]Failed to write output file:[/red] {e}")
|
|
621
|
+
raise typer.Exit(code=1)
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def _get_numbered_filepath(base_path: str, index: int) -> str:
|
|
625
|
+
"""Generate a numbered filepath for concurrent outputs.
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
base_path: Base file path (e.g., "output.txt")
|
|
629
|
+
index: Execution index (1-based)
|
|
630
|
+
|
|
631
|
+
Returns:
|
|
632
|
+
Numbered filepath (e.g., "output_1.txt")
|
|
633
|
+
"""
|
|
634
|
+
path = Path(base_path)
|
|
635
|
+
stem = path.stem
|
|
636
|
+
suffix = path.suffix
|
|
637
|
+
return str(path.parent / f"{stem}_{index}{suffix}")
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _write_concurrent_outputs(
|
|
641
|
+
output_file: str,
|
|
642
|
+
results: list[tuple[str, str, str | None, str | None]],
|
|
643
|
+
console: Console,
|
|
644
|
+
) -> None:
|
|
645
|
+
"""Write concurrent streaming results to numbered files.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
output_file: Base file path
|
|
649
|
+
results: List of (content, thinking, error, session_id) tuples
|
|
650
|
+
console: Rich console for status messages
|
|
651
|
+
"""
|
|
652
|
+
for i, (content, _, error, _) in enumerate(results, 1):
|
|
653
|
+
if error is None and content:
|
|
654
|
+
filepath = _get_numbered_filepath(output_file, i)
|
|
655
|
+
_write_output_file(filepath, content, console)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def _write_concurrent_responses(
|
|
659
|
+
output_file: str,
|
|
660
|
+
responses: list[GenerateTextResponse],
|
|
661
|
+
console: Console,
|
|
662
|
+
) -> None:
|
|
663
|
+
"""Write concurrent non-streaming responses to numbered files.
|
|
664
|
+
|
|
665
|
+
Args:
|
|
666
|
+
output_file: Base file path
|
|
667
|
+
responses: List of GenerateTextResponse objects
|
|
668
|
+
console: Rich console for status messages
|
|
669
|
+
"""
|
|
670
|
+
for i, response in enumerate(responses, 1):
|
|
671
|
+
filepath = _get_numbered_filepath(output_file, i)
|
|
672
|
+
_write_output_file(filepath, response.output_text, console)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def _get_agent_filepath(base_path: str, agent_id: str) -> str:
|
|
676
|
+
"""Generate a filepath with agent identifier for multi-agent outputs.
|
|
677
|
+
|
|
678
|
+
Args:
|
|
679
|
+
base_path: Base file path (e.g., "output.txt")
|
|
680
|
+
agent_id: Agent name or UUID
|
|
681
|
+
|
|
682
|
+
Returns:
|
|
683
|
+
Filepath with agent identifier (e.g., "output_my-agent.txt")
|
|
684
|
+
"""
|
|
685
|
+
path = Path(base_path)
|
|
686
|
+
stem = path.stem
|
|
687
|
+
suffix = path.suffix
|
|
688
|
+
# Sanitize agent_id for use in filename (replace problematic chars)
|
|
689
|
+
safe_agent_id = agent_id.replace("/", "_").replace("\\", "_").replace(":", "_")
|
|
690
|
+
return str(path.parent / f"{stem}_{safe_agent_id}{suffix}")
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def _write_multi_agent_outputs(
|
|
694
|
+
output_file: str,
|
|
695
|
+
agents: list[str],
|
|
696
|
+
results: list[tuple[str, str, str | None, str | None]],
|
|
697
|
+
console: Console,
|
|
698
|
+
) -> None:
|
|
699
|
+
"""Write multi-agent streaming results to agent-named files.
|
|
700
|
+
|
|
701
|
+
Args:
|
|
702
|
+
output_file: Base file path
|
|
703
|
+
agents: List of agent identifiers
|
|
704
|
+
results: List of (content, thinking, error, session_id) tuples
|
|
705
|
+
console: Rich console for status messages
|
|
706
|
+
"""
|
|
707
|
+
for i, (content, _, error, _) in enumerate(results):
|
|
708
|
+
if error is None and content:
|
|
709
|
+
filepath = _get_agent_filepath(output_file, agents[i])
|
|
710
|
+
_write_output_file(filepath, content, console)
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _write_multi_agent_responses(
|
|
714
|
+
output_file: str,
|
|
715
|
+
successful_responses: list[tuple[str, GenerateTextResponse]],
|
|
716
|
+
console: Console,
|
|
717
|
+
) -> None:
|
|
718
|
+
"""Write multi-agent non-streaming responses to agent-named files.
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
output_file: Base file path
|
|
722
|
+
successful_responses: List of (agent_id, response) tuples
|
|
723
|
+
console: Rich console for status messages
|
|
724
|
+
"""
|
|
725
|
+
for agent_id, response in successful_responses:
|
|
726
|
+
filepath = _get_agent_filepath(output_file, agent_id)
|
|
727
|
+
_write_output_file(filepath, response.output_text, console)
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
@async_command
|
|
731
|
+
async def _execute_generate_text(
|
|
732
|
+
message: str | None,
|
|
733
|
+
message_template: str | None,
|
|
734
|
+
message_vars: dict[str, Any],
|
|
735
|
+
agent: str | None,
|
|
736
|
+
agents: list[str] | None,
|
|
737
|
+
provider: str | None,
|
|
738
|
+
model: str | None,
|
|
739
|
+
system_instruction: str | None,
|
|
740
|
+
system_template: str | None,
|
|
741
|
+
system_vars: dict[str, Any],
|
|
742
|
+
temperature: float | None,
|
|
743
|
+
max_tokens: int | None,
|
|
744
|
+
reasoning_effort: str | None,
|
|
745
|
+
top_p: float | None,
|
|
746
|
+
top_k: int | None,
|
|
747
|
+
frequency_penalty: float | None,
|
|
748
|
+
presence_penalty: float | None,
|
|
749
|
+
seed: int | None,
|
|
750
|
+
stop: str | list[str] | None,
|
|
751
|
+
logprobs: bool | None,
|
|
752
|
+
top_logprobs: int | None,
|
|
753
|
+
user: str | None,
|
|
754
|
+
session_id: UUID | None,
|
|
755
|
+
tags: str | None,
|
|
756
|
+
stream: bool,
|
|
757
|
+
show_thinking: bool,
|
|
758
|
+
count: int,
|
|
759
|
+
output_mode: str,
|
|
760
|
+
output_file: str | None,
|
|
761
|
+
schema: str | None,
|
|
762
|
+
schema_format: str | None,
|
|
763
|
+
schema_def: str | None,
|
|
764
|
+
request_headers: dict[str, str] | None = None,
|
|
765
|
+
run_async: bool = False,
|
|
766
|
+
external_id: str | None = None,
|
|
767
|
+
billing_project_id: UUID | None = None,
|
|
768
|
+
max_history_messages: int | None = None,
|
|
769
|
+
regenerate: bool = False,
|
|
770
|
+
chat_history: list[dict[str, str]] | None = None,
|
|
771
|
+
user_metadata: dict[str, Any] | None = None,
|
|
772
|
+
webhook_url: str | None = None,
|
|
773
|
+
tools: list[Any] | None = None,
|
|
774
|
+
parent_execution_id: UUID | None = None,
|
|
775
|
+
execution_purpose: str | None = None,
|
|
776
|
+
) -> None:
|
|
777
|
+
"""Execute the generate text command.
|
|
778
|
+
|
|
779
|
+
Args:
|
|
780
|
+
message: User message text
|
|
781
|
+
message_template: User message template name or UUID
|
|
782
|
+
message_vars: User message template variables (parsed from key=value)
|
|
783
|
+
agent: Agent name or UUID
|
|
784
|
+
agents: List of agent names/UUIDs for multi-agent comparison
|
|
785
|
+
provider: Provider key
|
|
786
|
+
model: Model name
|
|
787
|
+
system_instruction: System instruction text
|
|
788
|
+
system_template: System instruction template name or UUID
|
|
789
|
+
system_vars: System instruction template variables (parsed from key=value)
|
|
790
|
+
temperature: Temperature parameter
|
|
791
|
+
max_tokens: Max tokens parameter
|
|
792
|
+
reasoning_effort: Reasoning effort level
|
|
793
|
+
session_id: Chat session ID
|
|
794
|
+
tags: Comma-separated tags
|
|
795
|
+
stream: Whether to stream
|
|
796
|
+
show_thinking: Whether to show thinking blocks
|
|
797
|
+
count: Number of concurrent executions
|
|
798
|
+
output_mode: Output destination - 'console', 'clipboard', or 'file'
|
|
799
|
+
output_file: File path for 'file' output mode
|
|
800
|
+
schema: Schema reference (name or UUID) for structured output
|
|
801
|
+
schema_format: Inline schema format ('json_schema' or 'regex')
|
|
802
|
+
schema_def: Inline schema definition string
|
|
803
|
+
request_headers: Custom HTTP headers for direct model mode
|
|
804
|
+
"""
|
|
805
|
+
console = get_console()
|
|
806
|
+
output = OutputService.get()
|
|
807
|
+
|
|
808
|
+
# Parse provider enum if specified
|
|
809
|
+
provider_enum = None
|
|
810
|
+
if provider:
|
|
811
|
+
try:
|
|
812
|
+
provider_enum = Provider(provider)
|
|
813
|
+
except ValueError:
|
|
814
|
+
valid = ", ".join(
|
|
815
|
+
p.value for p in Provider if not p.name.startswith("TESTING")
|
|
816
|
+
)
|
|
817
|
+
raise typer.BadParameter(
|
|
818
|
+
f"Invalid provider '{provider}'. Valid providers: {valid[:200]}..."
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
# Get template variables (already parsed by callback, convert empty dict to None)
|
|
822
|
+
message_variables = message_vars if message_vars else None
|
|
823
|
+
system_variables = system_vars if system_vars else None
|
|
824
|
+
|
|
825
|
+
# Parse tags
|
|
826
|
+
tag_list = [t.strip() for t in tags.split(",")] if tags else []
|
|
827
|
+
|
|
828
|
+
# Build generation parameters
|
|
829
|
+
gen_params = None
|
|
830
|
+
if any(
|
|
831
|
+
[
|
|
832
|
+
temperature is not None,
|
|
833
|
+
max_tokens is not None,
|
|
834
|
+
reasoning_effort is not None,
|
|
835
|
+
top_p is not None,
|
|
836
|
+
top_k is not None,
|
|
837
|
+
frequency_penalty is not None,
|
|
838
|
+
presence_penalty is not None,
|
|
839
|
+
seed is not None,
|
|
840
|
+
stop is not None,
|
|
841
|
+
logprobs is not None,
|
|
842
|
+
top_logprobs is not None,
|
|
843
|
+
user is not None,
|
|
844
|
+
]
|
|
845
|
+
):
|
|
846
|
+
gen_params = GenerationParameters(
|
|
847
|
+
temperature=temperature,
|
|
848
|
+
max_tokens=max_tokens,
|
|
849
|
+
reasoning_effort=reasoning_effort, # type: ignore[arg-type]
|
|
850
|
+
top_p=top_p,
|
|
851
|
+
top_k=top_k,
|
|
852
|
+
frequency_penalty=frequency_penalty,
|
|
853
|
+
presence_penalty=presence_penalty,
|
|
854
|
+
seed=seed,
|
|
855
|
+
stop=stop,
|
|
856
|
+
logprobs=logprobs,
|
|
857
|
+
top_logprobs=top_logprobs,
|
|
858
|
+
user=user,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# Build output schema (already validated in command function)
|
|
862
|
+
output_schema: InlineOutputSchema | SchemaReference | None = None
|
|
863
|
+
if schema:
|
|
864
|
+
output_schema = SchemaReference(schema_id=schema)
|
|
865
|
+
elif schema_format and schema_def:
|
|
866
|
+
output_schema = InlineOutputSchema(
|
|
867
|
+
schema_format=SchemaFormatEnum(schema_format),
|
|
868
|
+
schema_definition=schema_def,
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
# Multi-agent comparison mode
|
|
872
|
+
if agents:
|
|
873
|
+
await _execute_multi_agent(
|
|
874
|
+
agents=agents,
|
|
875
|
+
message=message,
|
|
876
|
+
message_template=message_template,
|
|
877
|
+
message_variables=message_variables,
|
|
878
|
+
system_instruction=system_instruction,
|
|
879
|
+
system_template=system_template,
|
|
880
|
+
system_variables=system_variables,
|
|
881
|
+
gen_params=gen_params,
|
|
882
|
+
tag_list=tag_list,
|
|
883
|
+
stream=stream,
|
|
884
|
+
show_thinking=show_thinking,
|
|
885
|
+
console=console,
|
|
886
|
+
_output=output,
|
|
887
|
+
output_mode=output_mode,
|
|
888
|
+
output_file=output_file,
|
|
889
|
+
)
|
|
890
|
+
return
|
|
891
|
+
|
|
892
|
+
# Build model_source based on user input
|
|
893
|
+
if agent:
|
|
894
|
+
model_source = AgentSource(agent=agent, generation_params=gen_params)
|
|
895
|
+
else:
|
|
896
|
+
# Must be provider + model (validated earlier)
|
|
897
|
+
assert provider_enum is not None and model is not None
|
|
898
|
+
model_source = DirectModelSource(
|
|
899
|
+
provider_key=provider_enum,
|
|
900
|
+
provider_model_name=model,
|
|
901
|
+
generation_params=gen_params,
|
|
902
|
+
request_headers=request_headers,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
# Build request
|
|
906
|
+
request = GenerateTextRequest(
|
|
907
|
+
model_source=model_source,
|
|
908
|
+
user_message=message,
|
|
909
|
+
user_message_template=message_template,
|
|
910
|
+
user_message_variables=message_variables,
|
|
911
|
+
system_instruction=system_instruction,
|
|
912
|
+
system_instruction_template=system_template,
|
|
913
|
+
system_instruction_variables=system_variables,
|
|
914
|
+
chat_session_id=session_id,
|
|
915
|
+
tags=tag_list,
|
|
916
|
+
stream=stream,
|
|
917
|
+
output_schema=output_schema,
|
|
918
|
+
external_id=external_id,
|
|
919
|
+
billing_project_id=billing_project_id,
|
|
920
|
+
max_history_messages=max_history_messages,
|
|
921
|
+
regenerate=regenerate,
|
|
922
|
+
chat_history=chat_history,
|
|
923
|
+
webhook_url=webhook_url,
|
|
924
|
+
tools=tools,
|
|
925
|
+
parent_execution_id=parent_execution_id,
|
|
926
|
+
execution_purpose=execution_purpose,
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
# Handle async execution mode
|
|
930
|
+
if run_async:
|
|
931
|
+
await _execute_async(
|
|
932
|
+
request=request,
|
|
933
|
+
user_metadata=user_metadata,
|
|
934
|
+
webhook_url=webhook_url,
|
|
935
|
+
console=console,
|
|
936
|
+
)
|
|
937
|
+
return
|
|
938
|
+
|
|
939
|
+
# Execute based on count and streaming mode
|
|
940
|
+
request = GenerateTextRequest(
|
|
941
|
+
model_source=model_source,
|
|
942
|
+
user_message=message,
|
|
943
|
+
user_message_template=message_template,
|
|
944
|
+
user_message_variables=message_variables,
|
|
945
|
+
system_instruction=system_instruction,
|
|
946
|
+
system_instruction_template=system_template,
|
|
947
|
+
system_instruction_variables=system_variables,
|
|
948
|
+
chat_session_id=session_id,
|
|
949
|
+
tags=tag_list,
|
|
950
|
+
stream=stream,
|
|
951
|
+
output_schema=output_schema,
|
|
952
|
+
parent_execution_id=parent_execution_id,
|
|
953
|
+
execution_purpose=execution_purpose,
|
|
954
|
+
)
|
|
955
|
+
|
|
956
|
+
# Execute based on count and streaming mode
|
|
957
|
+
if count == 1:
|
|
958
|
+
await _execute_single(
|
|
959
|
+
request, stream, show_thinking, console, output_mode, output_file
|
|
960
|
+
)
|
|
961
|
+
else:
|
|
962
|
+
await _execute_concurrent(
|
|
963
|
+
request,
|
|
964
|
+
stream,
|
|
965
|
+
show_thinking,
|
|
966
|
+
count,
|
|
967
|
+
console,
|
|
968
|
+
output,
|
|
969
|
+
output_mode,
|
|
970
|
+
output_file,
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
async def _execute_async(
|
|
975
|
+
request: GenerateTextRequest,
|
|
976
|
+
user_metadata: dict[str, Any] | None,
|
|
977
|
+
webhook_url: str | None,
|
|
978
|
+
console: Console,
|
|
979
|
+
) -> None:
|
|
980
|
+
"""Execute a text generation asynchronously (background processing).
|
|
981
|
+
|
|
982
|
+
Submits the generation request for background processing and returns immediately
|
|
983
|
+
with an execution ID for polling.
|
|
984
|
+
|
|
985
|
+
Args:
|
|
986
|
+
request: Generation request (stream must be False)
|
|
987
|
+
user_metadata: Optional metadata attached to the execution
|
|
988
|
+
webhook_url: Optional webhook URL for completion notification
|
|
989
|
+
console: Rich console for output
|
|
990
|
+
"""
|
|
991
|
+
async with authenticated_client() as (_config, client):
|
|
992
|
+
async with spinner("Submitting async generation..."):
|
|
993
|
+
response = await client.generate_text_async(
|
|
994
|
+
request,
|
|
995
|
+
webhook_url=webhook_url,
|
|
996
|
+
user_metadata=user_metadata,
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
# Display submission result
|
|
1000
|
+
console.print()
|
|
1001
|
+
console.print("[green]Generation queued for background processing[/green]")
|
|
1002
|
+
console.print()
|
|
1003
|
+
console.print(f"[dim]Execution ID:[/dim] {response.execution_id}")
|
|
1004
|
+
if response.external_id:
|
|
1005
|
+
console.print(f"[dim]External ID:[/dim] {response.external_id}")
|
|
1006
|
+
console.print()
|
|
1007
|
+
console.print("[dim]Check status with:[/dim] ai generate status ")
|
|
1008
|
+
console.print()
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
async def _execute_single(
|
|
1012
|
+
request: GenerateTextRequest,
|
|
1013
|
+
stream: bool,
|
|
1014
|
+
show_thinking: bool,
|
|
1015
|
+
console: Console,
|
|
1016
|
+
output_mode: str = "console",
|
|
1017
|
+
output_file: str | None = None,
|
|
1018
|
+
) -> None:
|
|
1019
|
+
"""Execute a single text generation.
|
|
1020
|
+
|
|
1021
|
+
Args:
|
|
1022
|
+
request: Generation request
|
|
1023
|
+
stream: Whether to stream
|
|
1024
|
+
show_thinking: Whether to show thinking blocks
|
|
1025
|
+
console: Rich console
|
|
1026
|
+
output_mode: Output destination - 'console', 'clipboard', or 'file'
|
|
1027
|
+
output_file: File path for 'file' output mode
|
|
1028
|
+
"""
|
|
1029
|
+
async with authenticated_client() as (_config, client):
|
|
1030
|
+
if stream:
|
|
1031
|
+
# Streaming mode
|
|
1032
|
+
renderer = StreamRenderer(console)
|
|
1033
|
+
stream_gen = client.generate_text_stream(request)
|
|
1034
|
+
content, thinking, session_id = await renderer.render_stream(
|
|
1035
|
+
stream_gen, show_thinking
|
|
1036
|
+
)
|
|
1037
|
+
|
|
1038
|
+
# Handle output based on mode
|
|
1039
|
+
if output_mode == "clipboard":
|
|
1040
|
+
_copy_to_clipboard(content, console)
|
|
1041
|
+
elif output_mode == "file" and output_file:
|
|
1042
|
+
_write_output_file(output_file, content, console)
|
|
1043
|
+
|
|
1044
|
+
# Display completion message with session_id
|
|
1045
|
+
console.print()
|
|
1046
|
+
if thinking and show_thinking:
|
|
1047
|
+
console.print(
|
|
1048
|
+
f"[dim]Completed with {len(thinking)} chars of thinking[/dim]"
|
|
1049
|
+
)
|
|
1050
|
+
if session_id:
|
|
1051
|
+
console.print(f"[dim]Session ID:[/dim] {session_id}")
|
|
1052
|
+
console.print()
|
|
1053
|
+
|
|
1054
|
+
else:
|
|
1055
|
+
# Non-streaming mode
|
|
1056
|
+
async with spinner("Generating text..."):
|
|
1057
|
+
response = await client.generate_text(request)
|
|
1058
|
+
|
|
1059
|
+
# Handle output based on mode
|
|
1060
|
+
if output_mode == "clipboard":
|
|
1061
|
+
_copy_to_clipboard(response.output_text, console)
|
|
1062
|
+
elif output_mode == "file" and output_file:
|
|
1063
|
+
_write_output_file(output_file, response.output_text, console)
|
|
1064
|
+
else:
|
|
1065
|
+
presenter = GenerateTextPresenter(console)
|
|
1066
|
+
presenter.present_result(response, show_metadata=True)
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
async def _execute_concurrent(
|
|
1070
|
+
request: GenerateTextRequest,
|
|
1071
|
+
stream: bool,
|
|
1072
|
+
show_thinking: bool,
|
|
1073
|
+
count: int,
|
|
1074
|
+
console: Console,
|
|
1075
|
+
_output: OutputService,
|
|
1076
|
+
output_mode: str = "console",
|
|
1077
|
+
output_file: str | None = None,
|
|
1078
|
+
) -> None:
|
|
1079
|
+
"""Execute multiple concurrent text generations.
|
|
1080
|
+
|
|
1081
|
+
Args:
|
|
1082
|
+
request: Base generation request (reused for each execution)
|
|
1083
|
+
stream: Whether to stream
|
|
1084
|
+
show_thinking: Whether to show thinking blocks
|
|
1085
|
+
count: Number of concurrent executions
|
|
1086
|
+
console: Rich console
|
|
1087
|
+
output: Output service (used for status messages)
|
|
1088
|
+
output_mode: Output destination - 'console', 'clipboard', or 'file'
|
|
1089
|
+
output_file: File path for 'file' output mode
|
|
1090
|
+
"""
|
|
1091
|
+
async with authenticated_client() as (_config, client):
|
|
1092
|
+
if stream:
|
|
1093
|
+
# Concurrent streaming mode
|
|
1094
|
+
renderer = ConcurrentRenderer(console, count)
|
|
1095
|
+
|
|
1096
|
+
# Create stream generators
|
|
1097
|
+
streams = [client.generate_text_stream(request) for _ in range(count)]
|
|
1098
|
+
|
|
1099
|
+
# Render concurrently
|
|
1100
|
+
results = await renderer.render_streams(streams, show_thinking)
|
|
1101
|
+
|
|
1102
|
+
# Handle output based on mode
|
|
1103
|
+
if output_mode == "clipboard":
|
|
1104
|
+
# Combine all successful outputs for clipboard
|
|
1105
|
+
combined = "\n\n---\n\n".join(
|
|
1106
|
+
content
|
|
1107
|
+
for content, _, error, _ in results
|
|
1108
|
+
if error is None and content
|
|
1109
|
+
)
|
|
1110
|
+
if combined:
|
|
1111
|
+
_copy_to_clipboard(combined, console)
|
|
1112
|
+
elif output_mode == "file" and output_file:
|
|
1113
|
+
_write_concurrent_outputs(output_file, results, console)
|
|
1114
|
+
|
|
1115
|
+
# Display completion summary with session IDs
|
|
1116
|
+
console.print()
|
|
1117
|
+
successful = sum(1 for _, _, error, _ in results if error is None)
|
|
1118
|
+
failed = count - successful
|
|
1119
|
+
|
|
1120
|
+
if failed > 0:
|
|
1121
|
+
console.print(
|
|
1122
|
+
f"[yellow]Completed: {successful} successful, {failed} failed[/yellow]"
|
|
1123
|
+
)
|
|
1124
|
+
else:
|
|
1125
|
+
console.print(
|
|
1126
|
+
f"[green]All {count} executions completed successfully[/green]"
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
# Display session IDs for each execution
|
|
1130
|
+
console.print()
|
|
1131
|
+
for i, (_, _, error, session_id) in enumerate(results, 1):
|
|
1132
|
+
if error is None and session_id:
|
|
1133
|
+
console.print(f"[dim]Execution {i} Session ID:[/dim] {session_id}")
|
|
1134
|
+
console.print()
|
|
1135
|
+
|
|
1136
|
+
else:
|
|
1137
|
+
# Concurrent non-streaming mode
|
|
1138
|
+
async with spinner(f"Generating {count} concurrent executions..."):
|
|
1139
|
+
# Create tasks
|
|
1140
|
+
tasks = [client.generate_text(request) for _ in range(count)]
|
|
1141
|
+
|
|
1142
|
+
# Execute concurrently
|
|
1143
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
1144
|
+
|
|
1145
|
+
# Separate successful responses from errors
|
|
1146
|
+
successful_responses: list[GenerateTextResponse] = []
|
|
1147
|
+
errors: list[tuple[int, str]] = []
|
|
1148
|
+
for i, response in enumerate(responses):
|
|
1149
|
+
if isinstance(response, Exception):
|
|
1150
|
+
errors.append((i + 1, str(response)))
|
|
1151
|
+
else:
|
|
1152
|
+
successful_responses.append(cast(GenerateTextResponse, response))
|
|
1153
|
+
|
|
1154
|
+
# Handle output based on mode
|
|
1155
|
+
if output_mode == "clipboard" and successful_responses:
|
|
1156
|
+
# Combine all outputs for clipboard
|
|
1157
|
+
combined = "\n\n---\n\n".join(
|
|
1158
|
+
r.output_text for r in successful_responses
|
|
1159
|
+
)
|
|
1160
|
+
_copy_to_clipboard(combined, console)
|
|
1161
|
+
elif output_mode == "file" and output_file and successful_responses:
|
|
1162
|
+
_write_concurrent_responses(output_file, successful_responses, console)
|
|
1163
|
+
elif successful_responses:
|
|
1164
|
+
# Display results only if console mode
|
|
1165
|
+
presenter = GenerateTextPresenter(console)
|
|
1166
|
+
presenter.present_multiple_results(
|
|
1167
|
+
successful_responses, show_metadata=True
|
|
1168
|
+
)
|
|
1169
|
+
|
|
1170
|
+
# Display errors if any
|
|
1171
|
+
if errors:
|
|
1172
|
+
console.print()
|
|
1173
|
+
console.print("[red]Failed executions:[/red]")
|
|
1174
|
+
for idx, error in errors:
|
|
1175
|
+
console.print(f" [red]Execution {idx}:[/red] {error}")
|
|
1176
|
+
console.print()
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
async def _execute_multi_agent(
|
|
1180
|
+
agents: list[str],
|
|
1181
|
+
message: str | None,
|
|
1182
|
+
message_template: str | None,
|
|
1183
|
+
message_variables: dict[str, Any] | None,
|
|
1184
|
+
system_instruction: str | None,
|
|
1185
|
+
system_template: str | None,
|
|
1186
|
+
system_variables: dict[str, Any] | None,
|
|
1187
|
+
gen_params: GenerationParameters | None,
|
|
1188
|
+
tag_list: list[str],
|
|
1189
|
+
stream: bool,
|
|
1190
|
+
show_thinking: bool,
|
|
1191
|
+
console: Console,
|
|
1192
|
+
_output: OutputService,
|
|
1193
|
+
output_mode: str = "console",
|
|
1194
|
+
output_file: str | None = None,
|
|
1195
|
+
) -> None:
|
|
1196
|
+
"""Execute text generation across multiple agents for comparison.
|
|
1197
|
+
|
|
1198
|
+
Sends the same message to each specified agent concurrently and displays
|
|
1199
|
+
the results side-by-side for easy comparison.
|
|
1200
|
+
|
|
1201
|
+
Args:
|
|
1202
|
+
agents: List of agent names or UUIDs to compare
|
|
1203
|
+
message: User message text
|
|
1204
|
+
message_template: User message template name or UUID
|
|
1205
|
+
message_variables: Template variables for user message
|
|
1206
|
+
system_instruction: System instruction text
|
|
1207
|
+
system_template: System instruction template name or UUID
|
|
1208
|
+
system_variables: Template variables for system instruction
|
|
1209
|
+
gen_params: Generation parameters
|
|
1210
|
+
tag_list: Tags for generated content
|
|
1211
|
+
stream: Whether to stream responses
|
|
1212
|
+
show_thinking: Whether to show thinking blocks
|
|
1213
|
+
console: Rich console for output
|
|
1214
|
+
output: Output service for status messages
|
|
1215
|
+
output_mode: Output destination - 'console', 'clipboard', or 'file'
|
|
1216
|
+
output_file: File path for 'file' output mode
|
|
1217
|
+
"""
|
|
1218
|
+
async with authenticated_client() as (_config, client):
|
|
1219
|
+
# Build a request for each agent
|
|
1220
|
+
requests = [
|
|
1221
|
+
GenerateTextRequest(
|
|
1222
|
+
model_source=AgentSource(agent=agent_id, generation_params=gen_params),
|
|
1223
|
+
user_message=message,
|
|
1224
|
+
user_message_template=message_template,
|
|
1225
|
+
user_message_variables=message_variables,
|
|
1226
|
+
system_instruction=system_instruction,
|
|
1227
|
+
system_instruction_template=system_template,
|
|
1228
|
+
system_instruction_variables=system_variables,
|
|
1229
|
+
tags=tag_list,
|
|
1230
|
+
stream=stream,
|
|
1231
|
+
)
|
|
1232
|
+
for agent_id in agents
|
|
1233
|
+
]
|
|
1234
|
+
|
|
1235
|
+
if stream:
|
|
1236
|
+
# Concurrent streaming mode with agent identifiers
|
|
1237
|
+
renderer = ConcurrentRenderer(
|
|
1238
|
+
console, len(agents), agent_identifiers=agents
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
# Create stream generators for each agent's request
|
|
1242
|
+
streams = [client.generate_text_stream(req) for req in requests]
|
|
1243
|
+
|
|
1244
|
+
# Render concurrently
|
|
1245
|
+
results = await renderer.render_streams(streams, show_thinking)
|
|
1246
|
+
|
|
1247
|
+
# Handle output based on mode
|
|
1248
|
+
if output_mode == "clipboard":
|
|
1249
|
+
# Combine all successful outputs with agent labels for clipboard
|
|
1250
|
+
parts: list[str] = []
|
|
1251
|
+
for i, (content, _, error, _) in enumerate(results):
|
|
1252
|
+
if error is None and content:
|
|
1253
|
+
parts.append(f"## {agents[i]}\n\n{content}")
|
|
1254
|
+
if parts:
|
|
1255
|
+
combined = "\n\n---\n\n".join(parts)
|
|
1256
|
+
_copy_to_clipboard(combined, console)
|
|
1257
|
+
elif output_mode == "file" and output_file:
|
|
1258
|
+
_write_multi_agent_outputs(output_file, agents, results, console)
|
|
1259
|
+
|
|
1260
|
+
# Display completion summary
|
|
1261
|
+
console.print()
|
|
1262
|
+
successful = sum(1 for _, _, error, _ in results if error is None)
|
|
1263
|
+
failed = len(agents) - successful
|
|
1264
|
+
|
|
1265
|
+
if failed > 0:
|
|
1266
|
+
console.print(
|
|
1267
|
+
f"[yellow]Completed: {successful} successful, {failed} failed[/yellow]"
|
|
1268
|
+
)
|
|
1269
|
+
else:
|
|
1270
|
+
console.print(
|
|
1271
|
+
f"[green]All {len(agents)} agents completed successfully[/green]"
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1274
|
+
# Display session IDs for each agent
|
|
1275
|
+
console.print()
|
|
1276
|
+
for i, (_, _, error, session_id) in enumerate(results):
|
|
1277
|
+
if error is None and session_id:
|
|
1278
|
+
console.print(f"[dim]{agents[i]} Session ID:[/dim] {session_id}")
|
|
1279
|
+
console.print()
|
|
1280
|
+
|
|
1281
|
+
else:
|
|
1282
|
+
# Concurrent non-streaming mode
|
|
1283
|
+
async with spinner(f"Generating responses from {len(agents)} agents..."):
|
|
1284
|
+
# Create tasks for each agent
|
|
1285
|
+
tasks = [client.generate_text(req) for req in requests]
|
|
1286
|
+
|
|
1287
|
+
# Execute concurrently
|
|
1288
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
1289
|
+
|
|
1290
|
+
# Separate successful responses from errors
|
|
1291
|
+
successful_responses: list[tuple[str, GenerateTextResponse]] = []
|
|
1292
|
+
errors: list[tuple[str, str]] = []
|
|
1293
|
+
for i, response in enumerate(responses):
|
|
1294
|
+
agent_id = agents[i]
|
|
1295
|
+
if isinstance(response, Exception):
|
|
1296
|
+
errors.append((agent_id, str(response)))
|
|
1297
|
+
else:
|
|
1298
|
+
successful_responses.append(
|
|
1299
|
+
(agent_id, cast(GenerateTextResponse, response))
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
# Handle output based on mode
|
|
1303
|
+
if output_mode == "clipboard" and successful_responses:
|
|
1304
|
+
# Combine all outputs with agent labels for clipboard
|
|
1305
|
+
parts = [
|
|
1306
|
+
f"## {agent_id}\n\n{response.output_text}"
|
|
1307
|
+
for agent_id, response in successful_responses
|
|
1308
|
+
]
|
|
1309
|
+
combined = "\n\n---\n\n".join(parts)
|
|
1310
|
+
_copy_to_clipboard(combined, console)
|
|
1311
|
+
elif output_mode == "file" and output_file and successful_responses:
|
|
1312
|
+
_write_multi_agent_responses(output_file, successful_responses, console)
|
|
1313
|
+
elif successful_responses:
|
|
1314
|
+
presenter = GenerateTextPresenter(console)
|
|
1315
|
+
for agent_id, response in successful_responses:
|
|
1316
|
+
console.print(f"\n[bold cyan]Agent: {agent_id}[/bold cyan]")
|
|
1317
|
+
presenter.present_result(response, show_metadata=True)
|
|
1318
|
+
|
|
1319
|
+
# Display errors if any
|
|
1320
|
+
if errors:
|
|
1321
|
+
console.print()
|
|
1322
|
+
console.print("[red]Failed agents:[/red]")
|
|
1323
|
+
for agent_id, error in errors:
|
|
1324
|
+
console.print(f" [red]{agent_id}:[/red] {error}")
|
|
1325
|
+
console.print()
|