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,355 @@
|
|
|
1
|
+
"""Concurrent execution renderer with side-by-side display.
|
|
2
|
+
|
|
3
|
+
Manages multiple simultaneous text generations with a clean
|
|
4
|
+
layout that adapts based on the number of concurrent executions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from collections.abc import AsyncGenerator
|
|
9
|
+
|
|
10
|
+
from rich.console import Console, Group, RenderableType
|
|
11
|
+
from rich.layout import Layout
|
|
12
|
+
from rich.live import Live
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
from rich.text import Text
|
|
15
|
+
|
|
16
|
+
from alloy_runtime_types.dtos.generation import GenerateTextStreamChunk
|
|
17
|
+
|
|
18
|
+
# Display configuration
|
|
19
|
+
CONCURRENT_REFRESH_RATE = 8 # Frames per second
|
|
20
|
+
CHUNK_UPDATE_INTERVAL = 5 # Update display every N chunks
|
|
21
|
+
THINKING_HEIGHT_RATIO = 0.25 # Thinking panel gets 25% of space
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ExecutionState:
|
|
25
|
+
"""Tracks state for a single concurrent execution."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, index: int, agent_identifier: str | None = None):
|
|
28
|
+
"""Initialize execution state.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
index: Execution index (0-based)
|
|
32
|
+
agent_identifier: Agent name or UUID for display (optional)
|
|
33
|
+
"""
|
|
34
|
+
self.index = index
|
|
35
|
+
self.agent_identifier = agent_identifier
|
|
36
|
+
self.thinking_text = ""
|
|
37
|
+
self.content_text = ""
|
|
38
|
+
self.execution_id: str | None = None
|
|
39
|
+
self.session_id: str | None = None
|
|
40
|
+
self.is_complete = False
|
|
41
|
+
self.has_error = False
|
|
42
|
+
self.error_message: str | None = None
|
|
43
|
+
|
|
44
|
+
def process_chunk(self, chunk: GenerateTextStreamChunk) -> None:
|
|
45
|
+
"""Process a stream chunk and update state.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
chunk: Stream chunk to process
|
|
49
|
+
"""
|
|
50
|
+
if self.execution_id is None:
|
|
51
|
+
self.execution_id = str(chunk.execution_id)
|
|
52
|
+
|
|
53
|
+
if chunk.chunk_type == "thinking":
|
|
54
|
+
if chunk.reasoning_content:
|
|
55
|
+
self.thinking_text += chunk.reasoning_content
|
|
56
|
+
elif chunk.chunk_type == "metadata":
|
|
57
|
+
# Metadata chunks - capture session_id from final metadata
|
|
58
|
+
if chunk.metadata and "chat_session_id" in chunk.metadata:
|
|
59
|
+
self.session_id = chunk.metadata["chat_session_id"]
|
|
60
|
+
else:
|
|
61
|
+
if chunk.content:
|
|
62
|
+
self.content_text += chunk.content
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ConcurrentRenderer:
|
|
66
|
+
"""Renders multiple concurrent text generations.
|
|
67
|
+
|
|
68
|
+
Features:
|
|
69
|
+
- Side-by-side layout for 2-3 concurrent executions
|
|
70
|
+
- Stacked layout for 4+ executions
|
|
71
|
+
- Individual thinking and content panels per execution
|
|
72
|
+
- Graceful error handling per execution
|
|
73
|
+
- Agent name display for multi-agent comparison mode
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
console: Console,
|
|
79
|
+
execution_count: int,
|
|
80
|
+
agent_identifiers: list[str] | None = None,
|
|
81
|
+
):
|
|
82
|
+
"""Initialize concurrent renderer.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
console: Rich console for output
|
|
86
|
+
execution_count: Number of concurrent executions (1-10)
|
|
87
|
+
agent_identifiers: Optional list of agent names/UUIDs for display
|
|
88
|
+
"""
|
|
89
|
+
self.console = console
|
|
90
|
+
self.execution_count = min(max(execution_count, 1), 10) # Clamp 1-10
|
|
91
|
+
self._agent_identifiers = agent_identifiers
|
|
92
|
+
|
|
93
|
+
# Create execution states with optional agent identifiers
|
|
94
|
+
self._states: list[ExecutionState] = []
|
|
95
|
+
for i in range(self.execution_count):
|
|
96
|
+
agent_id = (
|
|
97
|
+
agent_identifiers[i]
|
|
98
|
+
if agent_identifiers and i < len(agent_identifiers)
|
|
99
|
+
else None
|
|
100
|
+
)
|
|
101
|
+
self._states.append(ExecutionState(i, agent_identifier=agent_id))
|
|
102
|
+
|
|
103
|
+
self._use_side_by_side = execution_count <= 3
|
|
104
|
+
|
|
105
|
+
# Calculate dimensions based on terminal size
|
|
106
|
+
terminal_height = console.size.height or 24
|
|
107
|
+
terminal_width = console.size.width or 80
|
|
108
|
+
|
|
109
|
+
# Calculate panel width (account for side-by-side splitting and borders)
|
|
110
|
+
if self._use_side_by_side:
|
|
111
|
+
# Each panel gets equal share, minus borders (2 chars each side)
|
|
112
|
+
self._panel_width = max((terminal_width // execution_count) - 4, 20)
|
|
113
|
+
else:
|
|
114
|
+
self._panel_width = max(terminal_width - 4, 20)
|
|
115
|
+
|
|
116
|
+
# Calculate max lines per panel
|
|
117
|
+
reserved_lines = 6 # Status bar, panel borders, spacing
|
|
118
|
+
available_height = max(terminal_height - reserved_lines, 10)
|
|
119
|
+
self._max_panel_lines = available_height
|
|
120
|
+
|
|
121
|
+
async def render_streams(
|
|
122
|
+
self,
|
|
123
|
+
streams: list[AsyncGenerator[GenerateTextStreamChunk, None]],
|
|
124
|
+
show_thinking: bool = True,
|
|
125
|
+
) -> list[tuple[str, str, str | None, str | None]]:
|
|
126
|
+
"""Render multiple concurrent streams.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
streams: List of async generators for each execution
|
|
130
|
+
show_thinking: Whether to display thinking blocks
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
List of (content_text, thinking_text, error, session_id) tuples for each execution
|
|
134
|
+
"""
|
|
135
|
+
if len(streams) != self.execution_count:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"Expected {self.execution_count} streams, got {len(streams)}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
with Live(
|
|
141
|
+
self._build_display(),
|
|
142
|
+
console=self.console,
|
|
143
|
+
refresh_per_second=CONCURRENT_REFRESH_RATE,
|
|
144
|
+
transient=False,
|
|
145
|
+
) as live:
|
|
146
|
+
# Create tasks for each stream, passing live for updates
|
|
147
|
+
tasks = [
|
|
148
|
+
self._consume_stream(i, stream, show_thinking, live)
|
|
149
|
+
for i, stream in enumerate(streams)
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
# Run all streams concurrently
|
|
153
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
154
|
+
|
|
155
|
+
# Final update
|
|
156
|
+
live.update(self._build_display())
|
|
157
|
+
|
|
158
|
+
# Return results
|
|
159
|
+
return [
|
|
160
|
+
(
|
|
161
|
+
state.content_text,
|
|
162
|
+
state.thinking_text,
|
|
163
|
+
state.error_message,
|
|
164
|
+
state.session_id,
|
|
165
|
+
)
|
|
166
|
+
for state in self._states
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
async def _consume_stream(
|
|
170
|
+
self,
|
|
171
|
+
index: int,
|
|
172
|
+
stream: AsyncGenerator[GenerateTextStreamChunk, None],
|
|
173
|
+
show_thinking: bool,
|
|
174
|
+
live: Live,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Consume a single stream and update its state.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
index: Execution index
|
|
180
|
+
stream: Async generator of chunks
|
|
181
|
+
show_thinking: Whether to process thinking chunks
|
|
182
|
+
live: Rich Live display to update
|
|
183
|
+
"""
|
|
184
|
+
state = self._states[index]
|
|
185
|
+
chunk_count = 0
|
|
186
|
+
try:
|
|
187
|
+
async for chunk in stream:
|
|
188
|
+
if show_thinking or chunk.chunk_type != "thinking":
|
|
189
|
+
state.process_chunk(chunk)
|
|
190
|
+
chunk_count += 1
|
|
191
|
+
# Update display every N chunks to reduce flicker
|
|
192
|
+
if chunk_count % CHUNK_UPDATE_INTERVAL == 0:
|
|
193
|
+
live.update(self._build_display())
|
|
194
|
+
state.is_complete = True
|
|
195
|
+
# Final update when complete
|
|
196
|
+
live.update(self._build_display())
|
|
197
|
+
except Exception as e:
|
|
198
|
+
state.has_error = True
|
|
199
|
+
state.error_message = str(e)
|
|
200
|
+
state.is_complete = True
|
|
201
|
+
# Update display to show error
|
|
202
|
+
live.update(self._build_display())
|
|
203
|
+
|
|
204
|
+
def _build_display(self) -> Layout | Group:
|
|
205
|
+
"""Build the display layout based on execution count.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Layout for side-by-side or Group for stacked
|
|
209
|
+
"""
|
|
210
|
+
if self._use_side_by_side:
|
|
211
|
+
return self._build_side_by_side_layout()
|
|
212
|
+
else:
|
|
213
|
+
return self._build_stacked_layout()
|
|
214
|
+
|
|
215
|
+
def _build_side_by_side_layout(self) -> Layout:
|
|
216
|
+
"""Build side-by-side layout for 2-3 executions.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Layout with horizontal split
|
|
220
|
+
"""
|
|
221
|
+
layout = Layout()
|
|
222
|
+
|
|
223
|
+
# Create columns
|
|
224
|
+
columns = [
|
|
225
|
+
Layout(self._render_execution_panel(state), name=f"exec_{state.index}")
|
|
226
|
+
for state in self._states
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
layout.split_row(*columns)
|
|
230
|
+
return layout
|
|
231
|
+
|
|
232
|
+
def _build_stacked_layout(self) -> Group:
|
|
233
|
+
"""Build stacked layout for 4+ executions.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Group of panels
|
|
237
|
+
"""
|
|
238
|
+
panels = [self._render_execution_panel(state) for state in self._states]
|
|
239
|
+
return Group(*panels)
|
|
240
|
+
|
|
241
|
+
def _tail_text(self, text: str, max_visual_lines: int, width: int) -> str:
|
|
242
|
+
"""Get the last N visual lines of text (tail behavior with wrapping).
|
|
243
|
+
|
|
244
|
+
Accounts for line wrapping based on panel width to ensure content
|
|
245
|
+
fits within the visible area.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
text: Full text content
|
|
249
|
+
max_visual_lines: Maximum number of visual lines to return
|
|
250
|
+
width: Panel content width for wrap calculation
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Last max_visual_lines worth of text
|
|
254
|
+
"""
|
|
255
|
+
if not text or max_visual_lines <= 0 or width <= 0:
|
|
256
|
+
return text
|
|
257
|
+
|
|
258
|
+
lines = text.split("\n")
|
|
259
|
+
result_lines: list[str] = []
|
|
260
|
+
visual_line_count = 0
|
|
261
|
+
|
|
262
|
+
# Work backwards through lines
|
|
263
|
+
for line in reversed(lines):
|
|
264
|
+
# Calculate how many visual lines this text line takes
|
|
265
|
+
line_visual_lines = max(1, (len(line) + width - 1) // width) if line else 1
|
|
266
|
+
|
|
267
|
+
if visual_line_count + line_visual_lines > max_visual_lines:
|
|
268
|
+
# This line would exceed our budget
|
|
269
|
+
# If we have nothing yet, take a truncated version of this line
|
|
270
|
+
if not result_lines:
|
|
271
|
+
chars_available = max_visual_lines * width
|
|
272
|
+
result_lines.append(
|
|
273
|
+
line[-chars_available:] if len(line) > chars_available else line
|
|
274
|
+
)
|
|
275
|
+
break
|
|
276
|
+
|
|
277
|
+
result_lines.append(line)
|
|
278
|
+
visual_line_count += line_visual_lines
|
|
279
|
+
|
|
280
|
+
result_lines.reverse()
|
|
281
|
+
return "\n".join(result_lines)
|
|
282
|
+
|
|
283
|
+
def _render_execution_panel(self, state: ExecutionState) -> Panel:
|
|
284
|
+
"""Render a single execution as a panel.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
state: Execution state to render
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Panel with execution content
|
|
291
|
+
"""
|
|
292
|
+
content_parts: list[RenderableType] = []
|
|
293
|
+
|
|
294
|
+
# Calculate line budget - split between thinking and content
|
|
295
|
+
thinking_max_lines = int(self._max_panel_lines * THINKING_HEIGHT_RATIO)
|
|
296
|
+
content_max_lines = (
|
|
297
|
+
self._max_panel_lines - thinking_max_lines - 4
|
|
298
|
+
) # 4 for borders
|
|
299
|
+
|
|
300
|
+
# Content width accounts for panel padding (2 chars each side)
|
|
301
|
+
content_width = max(self._panel_width - 4, 10)
|
|
302
|
+
|
|
303
|
+
# Add thinking section if present (show tail/latest)
|
|
304
|
+
if state.thinking_text:
|
|
305
|
+
thinking_display = self._tail_text(
|
|
306
|
+
state.thinking_text, thinking_max_lines, content_width
|
|
307
|
+
)
|
|
308
|
+
thinking_text = Text(thinking_display, style="dim italic")
|
|
309
|
+
thinking_panel = Panel(
|
|
310
|
+
thinking_text,
|
|
311
|
+
title="[red]Thinking[/red]",
|
|
312
|
+
title_align="left",
|
|
313
|
+
border_style="red",
|
|
314
|
+
padding=(0, 1),
|
|
315
|
+
)
|
|
316
|
+
content_parts.append(thinking_panel)
|
|
317
|
+
|
|
318
|
+
# Add content (show tail/latest)
|
|
319
|
+
if state.content_text:
|
|
320
|
+
content_display = self._tail_text(
|
|
321
|
+
state.content_text, content_max_lines, content_width
|
|
322
|
+
)
|
|
323
|
+
content_parts.append(Text(content_display))
|
|
324
|
+
elif state.has_error:
|
|
325
|
+
content_parts.append(Text(f"Error: {state.error_message}", style="red"))
|
|
326
|
+
elif not state.is_complete:
|
|
327
|
+
content_parts.append(Text("Generating...", style="dim"))
|
|
328
|
+
else:
|
|
329
|
+
content_parts.append(Text("(empty response)", style="dim"))
|
|
330
|
+
|
|
331
|
+
# Determine panel styling
|
|
332
|
+
# Use agent identifier if available, otherwise fall back to execution number
|
|
333
|
+
if state.agent_identifier:
|
|
334
|
+
title = f"Agent: {state.agent_identifier}"
|
|
335
|
+
else:
|
|
336
|
+
title = f"Execution {state.index + 1}"
|
|
337
|
+
|
|
338
|
+
if state.is_complete:
|
|
339
|
+
if state.has_error:
|
|
340
|
+
border_style = "red"
|
|
341
|
+
title = f"{title} [red](error)[/red]"
|
|
342
|
+
else:
|
|
343
|
+
border_style = "green"
|
|
344
|
+
title = f"{title} [green](complete)[/green]"
|
|
345
|
+
else:
|
|
346
|
+
border_style = "blue"
|
|
347
|
+
title = f"{title} [blue](running)[/blue]"
|
|
348
|
+
|
|
349
|
+
return Panel(
|
|
350
|
+
Group(*content_parts) if content_parts else Text(""),
|
|
351
|
+
title=title,
|
|
352
|
+
title_align="left",
|
|
353
|
+
border_style=border_style,
|
|
354
|
+
padding=(0, 1),
|
|
355
|
+
)
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Non-streaming result presenter for text generation.
|
|
2
|
+
|
|
3
|
+
Displays final generation results with usage summary and costs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from alloy_runtime_types.dtos.generation import GenerateTextResponse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GenerateTextPresenter:
|
|
17
|
+
"""Presents completed text generation results.
|
|
18
|
+
|
|
19
|
+
Features:
|
|
20
|
+
- Final text output with optional thinking block
|
|
21
|
+
- Usage statistics (tokens) table
|
|
22
|
+
- Cost breakdown table
|
|
23
|
+
- Execution metadata
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, console: Console):
|
|
27
|
+
"""Initialize presenter.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
console: Rich console for output
|
|
31
|
+
"""
|
|
32
|
+
self.console = console
|
|
33
|
+
|
|
34
|
+
def present_result(
|
|
35
|
+
self, response: GenerateTextResponse, show_metadata: bool = True
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Present a non-streaming generation result.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
response: Complete generation response
|
|
41
|
+
show_metadata: Whether to show usage/cost tables (default True)
|
|
42
|
+
"""
|
|
43
|
+
# Display main output text
|
|
44
|
+
self.console.print()
|
|
45
|
+
self.console.print(
|
|
46
|
+
Panel(
|
|
47
|
+
Text(response.output_text),
|
|
48
|
+
title="Generated Text",
|
|
49
|
+
title_align="left",
|
|
50
|
+
border_style="green",
|
|
51
|
+
padding=(1, 2),
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
self.console.print()
|
|
55
|
+
|
|
56
|
+
# Display metadata if requested
|
|
57
|
+
if show_metadata:
|
|
58
|
+
self._display_usage_table(response)
|
|
59
|
+
self._display_cost_table(response)
|
|
60
|
+
self._display_execution_info(response)
|
|
61
|
+
|
|
62
|
+
def present_multiple_results(
|
|
63
|
+
self, responses: list[GenerateTextResponse], show_metadata: bool = True
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Present multiple concurrent generation results.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
responses: List of generation responses
|
|
69
|
+
show_metadata: Whether to show usage/cost summaries
|
|
70
|
+
"""
|
|
71
|
+
self.console.print()
|
|
72
|
+
|
|
73
|
+
for i, response in enumerate(responses, 1):
|
|
74
|
+
# Display each result with index
|
|
75
|
+
self.console.print(
|
|
76
|
+
Panel(
|
|
77
|
+
Text(response.output_text),
|
|
78
|
+
title=f"Generated Text {i}/{len(responses)}",
|
|
79
|
+
title_align="left",
|
|
80
|
+
border_style="green",
|
|
81
|
+
padding=(1, 2),
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
self.console.print()
|
|
85
|
+
|
|
86
|
+
# Display aggregated metadata if requested
|
|
87
|
+
if show_metadata and responses:
|
|
88
|
+
self._display_aggregated_usage(responses)
|
|
89
|
+
self._display_aggregated_costs(responses)
|
|
90
|
+
|
|
91
|
+
# Display session IDs for each execution
|
|
92
|
+
self.console.print()
|
|
93
|
+
for i, response in enumerate(responses, 1):
|
|
94
|
+
self.console.print(
|
|
95
|
+
f"[dim]Execution {i} Session ID:[/dim] {response.chat_session_id}"
|
|
96
|
+
)
|
|
97
|
+
self.console.print()
|
|
98
|
+
|
|
99
|
+
def _display_usage_table(self, response: GenerateTextResponse) -> None:
|
|
100
|
+
"""Display token usage table.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
response: Generation response
|
|
104
|
+
"""
|
|
105
|
+
table = Table(title="Token Usage", show_header=True, header_style="bold cyan")
|
|
106
|
+
table.add_column("Metric", style="dim")
|
|
107
|
+
table.add_column("Count", justify="right")
|
|
108
|
+
|
|
109
|
+
usage = response.usage
|
|
110
|
+
table.add_row("Input Tokens", str(usage.input_tokens))
|
|
111
|
+
table.add_row("Output Tokens", str(usage.output_tokens))
|
|
112
|
+
|
|
113
|
+
if usage.cache_creation_tokens:
|
|
114
|
+
table.add_row(
|
|
115
|
+
"Cache Creation Tokens", str(usage.cache_creation_tokens), style="dim"
|
|
116
|
+
)
|
|
117
|
+
if usage.cache_read_tokens:
|
|
118
|
+
table.add_row(
|
|
119
|
+
"Cache Read Tokens", str(usage.cache_read_tokens), style="dim"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
total = usage.input_tokens + usage.output_tokens
|
|
123
|
+
table.add_row("Total Tokens", str(total), style="bold")
|
|
124
|
+
|
|
125
|
+
self.console.print(table)
|
|
126
|
+
self.console.print()
|
|
127
|
+
|
|
128
|
+
def _display_cost_table(self, response: GenerateTextResponse) -> None:
|
|
129
|
+
"""Display cost breakdown table.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
response: Generation response
|
|
133
|
+
"""
|
|
134
|
+
table = Table(
|
|
135
|
+
title="Cost Breakdown", show_header=True, header_style="bold cyan"
|
|
136
|
+
)
|
|
137
|
+
table.add_column("Component", style="dim")
|
|
138
|
+
table.add_column("USD", justify="right")
|
|
139
|
+
|
|
140
|
+
cost = response.cost
|
|
141
|
+
table.add_row("Input Cost", self._format_cost(cost.input_cost_usd))
|
|
142
|
+
table.add_row("Output Cost", self._format_cost(cost.output_cost_usd))
|
|
143
|
+
|
|
144
|
+
if cost.cache_cost_usd:
|
|
145
|
+
table.add_row(
|
|
146
|
+
"Cache Cost", self._format_cost(cost.cache_cost_usd), style="dim"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
table.add_row(
|
|
150
|
+
"Total Cost", self._format_cost(cost.total_cost_usd), style="bold green"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self.console.print(table)
|
|
154
|
+
self.console.print()
|
|
155
|
+
|
|
156
|
+
def _display_execution_info(self, response: GenerateTextResponse) -> None:
|
|
157
|
+
"""Display execution metadata.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
response: Generation response
|
|
161
|
+
"""
|
|
162
|
+
info_parts = [
|
|
163
|
+
f"[dim]Session ID:[/dim] {response.chat_session_id}",
|
|
164
|
+
f"[dim]Execution ID:[/dim] {response.execution_id}",
|
|
165
|
+
f"[dim]Model:[/dim] {response.provider_key}/{response.provider_model_name}",
|
|
166
|
+
f"[dim]Finish Reason:[/dim] {response.finish_reason}",
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
if response.tags:
|
|
170
|
+
info_parts.append(f"[dim]Tags:[/dim] {', '.join(response.tags)}")
|
|
171
|
+
|
|
172
|
+
for part in info_parts:
|
|
173
|
+
self.console.print(part)
|
|
174
|
+
|
|
175
|
+
self.console.print()
|
|
176
|
+
|
|
177
|
+
def _display_aggregated_usage(self, responses: list[GenerateTextResponse]) -> None:
|
|
178
|
+
"""Display aggregated token usage across multiple executions.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
responses: List of generation responses
|
|
182
|
+
"""
|
|
183
|
+
table = Table(
|
|
184
|
+
title=f"Aggregated Token Usage ({len(responses)} executions)",
|
|
185
|
+
show_header=True,
|
|
186
|
+
header_style="bold cyan",
|
|
187
|
+
)
|
|
188
|
+
table.add_column("Metric", style="dim")
|
|
189
|
+
table.add_column("Total", justify="right")
|
|
190
|
+
table.add_column("Average", justify="right")
|
|
191
|
+
|
|
192
|
+
total_input = sum(r.usage.input_tokens for r in responses)
|
|
193
|
+
total_output = sum(r.usage.output_tokens for r in responses)
|
|
194
|
+
total_cache_creation = sum(
|
|
195
|
+
r.usage.cache_creation_tokens or 0 for r in responses
|
|
196
|
+
)
|
|
197
|
+
total_cache_read = sum(r.usage.cache_read_tokens or 0 for r in responses)
|
|
198
|
+
|
|
199
|
+
count = len(responses)
|
|
200
|
+
table.add_row("Input Tokens", str(total_input), str(total_input // count))
|
|
201
|
+
table.add_row("Output Tokens", str(total_output), str(total_output // count))
|
|
202
|
+
|
|
203
|
+
if total_cache_creation > 0:
|
|
204
|
+
table.add_row(
|
|
205
|
+
"Cache Creation",
|
|
206
|
+
str(total_cache_creation),
|
|
207
|
+
str(total_cache_creation // count),
|
|
208
|
+
style="dim",
|
|
209
|
+
)
|
|
210
|
+
if total_cache_read > 0:
|
|
211
|
+
table.add_row(
|
|
212
|
+
"Cache Read",
|
|
213
|
+
str(total_cache_read),
|
|
214
|
+
str(total_cache_read // count),
|
|
215
|
+
style="dim",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
total_tokens = total_input + total_output
|
|
219
|
+
table.add_row(
|
|
220
|
+
"Total Tokens", str(total_tokens), str(total_tokens // count), style="bold"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
self.console.print(table)
|
|
224
|
+
self.console.print()
|
|
225
|
+
|
|
226
|
+
def _display_aggregated_costs(self, responses: list[GenerateTextResponse]) -> None:
|
|
227
|
+
"""Display aggregated costs across multiple executions.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
responses: List of generation responses
|
|
231
|
+
"""
|
|
232
|
+
table = Table(
|
|
233
|
+
title=f"Aggregated Costs ({len(responses)} executions)",
|
|
234
|
+
show_header=True,
|
|
235
|
+
header_style="bold cyan",
|
|
236
|
+
)
|
|
237
|
+
table.add_column("Component", style="dim")
|
|
238
|
+
table.add_column("Total USD", justify="right")
|
|
239
|
+
table.add_column("Average USD", justify="right")
|
|
240
|
+
|
|
241
|
+
total_input_cost = Decimal(sum(r.cost.input_cost_usd for r in responses))
|
|
242
|
+
total_output_cost = Decimal(sum(r.cost.output_cost_usd for r in responses))
|
|
243
|
+
total_cache_cost = Decimal(
|
|
244
|
+
sum(r.cost.cache_cost_usd or Decimal(0) for r in responses)
|
|
245
|
+
)
|
|
246
|
+
total_cost = Decimal(sum(r.cost.total_cost_usd for r in responses))
|
|
247
|
+
|
|
248
|
+
count = Decimal(len(responses))
|
|
249
|
+
table.add_row(
|
|
250
|
+
"Input Cost",
|
|
251
|
+
self._format_cost(total_input_cost),
|
|
252
|
+
self._format_cost(total_input_cost / count),
|
|
253
|
+
)
|
|
254
|
+
table.add_row(
|
|
255
|
+
"Output Cost",
|
|
256
|
+
self._format_cost(total_output_cost),
|
|
257
|
+
self._format_cost(total_output_cost / count),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if total_cache_cost > 0:
|
|
261
|
+
table.add_row(
|
|
262
|
+
"Cache Cost",
|
|
263
|
+
self._format_cost(total_cache_cost),
|
|
264
|
+
self._format_cost(total_cache_cost / count),
|
|
265
|
+
style="dim",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
table.add_row(
|
|
269
|
+
"Total Cost",
|
|
270
|
+
self._format_cost(total_cost),
|
|
271
|
+
self._format_cost(total_cost / count),
|
|
272
|
+
style="bold green",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
self.console.print(table)
|
|
276
|
+
self.console.print()
|
|
277
|
+
|
|
278
|
+
def _format_cost(self, cost: Decimal) -> str:
|
|
279
|
+
"""Format cost value as currency string.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
cost: Cost in USD
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Formatted cost string with dollar sign
|
|
286
|
+
"""
|
|
287
|
+
return f"${cost:.6f}"
|