agent-starter-pack 0.3.3__py3-none-any.whl → 0.21.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.
- agent_starter_pack/agents/README.md +7 -0
- agents/langgraph_base_react/template/.templateconfig.yaml → agent_starter_pack/agents/adk_a2a_base/.template/templateconfig.yaml +5 -10
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- src/deployment_targets/cloud_run/Dockerfile → agent_starter_pack/agents/adk_a2a_base/app/__init__.py +2 -14
- agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
- agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
- agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → agent_starter_pack/agents/adk_a2a_base/notebooks/evaluating_adk_agent.ipynb +163 -199
- {agents/adk_base → agent_starter_pack/agents/adk_a2a_base}/tests/integration/test_agent.py +2 -2
- agents/adk_base/template/.templateconfig.yaml → agent_starter_pack/agents/adk_base/.template/templateconfig.yaml +4 -1
- {agents → agent_starter_pack/agents}/adk_base/README.md +1 -1
- agent_starter_pack/agents/adk_base/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/adk_base/app/agent.py +5 -2
- {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +128 -82
- agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb → agent_starter_pack/agents/adk_base/notebooks/evaluating_adk_agent.ipynb +181 -207
- agent_starter_pack/agents/adk_base/tests/integration/test_agent.py +58 -0
- agents/crewai_coding_crew/template/.templateconfig.yaml → agent_starter_pack/agents/adk_live/.template/templateconfig.yaml +5 -9
- agent_starter_pack/agents/adk_live/README.md +32 -0
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +51 -0
- agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
- agents/agentic_rag/template/.templateconfig.yaml → agent_starter_pack/agents/agentic_rag/.template/templateconfig.yaml +7 -3
- {agents → agent_starter_pack/agents}/agentic_rag/README.md +1 -1
- agent_starter_pack/agents/agentic_rag/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +8 -4
- {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +128 -82
- agent_starter_pack/agents/agentic_rag/notebooks/evaluating_adk_agent.ipynb +1535 -0
- {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +3 -3
- agent_starter_pack/agents/langgraph_base/.template/templateconfig.yaml +31 -0
- agent_starter_pack/agents/langgraph_base/README.md +30 -0
- agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
- agent_starter_pack/agents/langgraph_base/app/agent.py +34 -0
- {agents/crewai_coding_crew → agent_starter_pack/agents/langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +30 -17
- {agents/langgraph_base_react → agent_starter_pack/agents/langgraph_base}/tests/integration/test_agent.py +2 -2
- {src → agent_starter_pack}/base_template/.gitignore +5 -3
- agent_starter_pack/base_template/GEMINI.md +5 -0
- agent_starter_pack/base_template/Makefile +339 -0
- agent_starter_pack/base_template/README.md +267 -0
- agent_starter_pack/base_template/deployment/README.md +11 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +2 -2
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +6 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +12 -11
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +5 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +10 -10
- agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
- agent_starter_pack/base_template/deployment/terraform/github.tf +337 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +20 -41
- {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +10 -3
- {src/resources/setup_cicd → agent_starter_pack/base_template/deployment/terraform}/providers.tf +8 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +7 -8
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +7 -16
- {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +61 -28
- {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +6 -1
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
- src/base_template/deployment/terraform/build_triggers.tf → agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +55 -38
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- {src → agent_starter_pack}/base_template/pyproject.toml +24 -37
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +132 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +259 -0
- src/base_template/deployment/cd/deploy-to-prod.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +38 -30
- src/base_template/deployment/ci/pr_checks.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +5 -5
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +322 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- {src/base_template/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/typing.py +7 -9
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py +25 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/part_converter.py +138 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/__init__.py +13 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/a2a_agent_executor.py +265 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/task_result_aggregator.py +152 -0
- agent_starter_pack/cli/commands/create.py +1256 -0
- agent_starter_pack/cli/commands/enhance.py +611 -0
- agent_starter_pack/cli/commands/list.py +203 -0
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/commands/setup_cicd.py +862 -0
- {src → agent_starter_pack}/cli/main.py +10 -2
- {src → agent_starter_pack}/cli/utils/cicd.py +139 -48
- agent_starter_pack/cli/utils/gcp.py +263 -0
- agent_starter_pack/cli/utils/logging.py +103 -0
- agent_starter_pack/cli/utils/remote_template.py +677 -0
- agent_starter_pack/cli/utils/template.py +1466 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +20 -6
- {src → agent_starter_pack}/data_ingestion/pyproject.toml +1 -0
- {src → agent_starter_pack}/data_ingestion/uv.lock +602 -494
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +484 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +424 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +263 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +519 -0
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +51 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +243 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +417 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +705 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +165 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +329 -0
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/fast_api_app.py +556 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +79 -1044
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
- agent_starter_pack/frontends/adk_live_react/frontend/src/App.tsx +65 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/SidePanel.tsx +516 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/side-panel.scss +563 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +40 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +210 -24
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
- agent_starter_pack/resources/docs/adk-cheatsheet.md +1628 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
- agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +4946 -0
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +4991 -0
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4963 -0
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +5006 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +5487 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +5532 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-agent_engine.lock +5788 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-cloud_run.lock +5811 -0
- {src → agent_starter_pack}/utils/generate_locks.py +15 -12
- {src → agent_starter_pack}/utils/lock_utils.py +4 -7
- {src → agent_starter_pack}/utils/watch_and_rebuild.py +2 -2
- agent_starter_pack-0.21.0.dist-info/METADATA +182 -0
- agent_starter_pack-0.21.0.dist-info/RECORD +171 -0
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +362 -0
- agent_starter_pack-0.3.3.dist-info/METADATA +0 -164
- agent_starter_pack-0.3.3.dist-info/RECORD +0 -176
- agent_starter_pack-0.3.3.dist-info/entry_points.txt +0 -2
- agents/crewai_coding_crew/README.md +0 -34
- agents/crewai_coding_crew/app/agent.py +0 -86
- agents/crewai_coding_crew/app/crew/config/agents.yaml +0 -39
- agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agents/langgraph_base_react/README.md +0 -9
- agents/langgraph_base_react/app/agent.py +0 -73
- agents/live_api/README.md +0 -37
- agents/live_api/app/agent.py +0 -78
- agents/live_api/app/server.py +0 -196
- agents/live_api/app/templates.py +0 -51
- agents/live_api/app/vector_store.py +0 -55
- agents/live_api/template/.templateconfig.yaml +0 -29
- agents/live_api/tests/integration/test_server_e2e.py +0 -254
- agents/live_api/tests/load_test/load_test.py +0 -40
- agents/live_api/tests/unit/test_server.py +0 -143
- src/base_template/Makefile +0 -72
- src/base_template/README.md +0 -208
- src/base_template/app/__init__.py +0 -3
- src/base_template/app/utils/tracing.py +0 -155
- src/base_template/deployment/README.md +0 -126
- src/base_template/deployment/cd/staging.yaml +0 -216
- src/base_template/deployment/terraform/dev/log_sinks.tf +0 -63
- src/base_template/deployment/terraform/log_sinks.tf +0 -70
- src/base_template/deployment/terraform/providers.tf +0 -37
- src/cli/commands/create.py +0 -664
- src/cli/commands/setup_cicd.py +0 -829
- src/cli/utils/gcp.py +0 -117
- src/cli/utils/logging.py +0 -51
- src/cli/utils/template.py +0 -737
- src/deployment_targets/agent_engine/app/agent_engine_app.py +0 -336
- src/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -183
- src/deployment_targets/agent_engine/tests/load_test/README.md +0 -42
- src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -107
- src/deployment_targets/cloud_run/app/server.py +0 -154
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +0 -249
- src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
- src/deployment_targets/cloud_run/tests/load_test/README.md +0 -83
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -118
- src/deployment_targets/cloud_run/uv.lock +0 -6952
- src/frontends/live_api_react/frontend/src/App.tsx +0 -205
- src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +0 -217
- src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +0 -201
- src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +0 -161
- src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +0 -285
- src/frontends/streamlit/frontend/side_bar.py +0 -214
- src/frontends/streamlit/frontend/streamlit_app.py +0 -265
- src/frontends/streamlit/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- src/frontends/streamlit/frontend/utils/local_chat_history.py +0 -125
- src/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- src/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- src/frontends/streamlit/frontend/utils/stream_handler.py +0 -301
- src/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
- src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
- src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
- src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
- src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
- src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
- src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
- src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
- src/resources/containers/data_processing/Dockerfile +0 -27
- src/resources/containers/e2e-tests/Dockerfile +0 -19
- src/resources/idx/.idx/dev.nix +0 -57
- src/resources/idx/idx-template.json +0 -21
- src/resources/idx/idx-template.nix +0 -26
- src/resources/locks/uv-adk_base-agent_engine.lock +0 -5338
- src/resources/locks/uv-adk_base-cloud_run.lock +0 -5930
- src/resources/locks/uv-agentic_rag-agent_engine.lock +0 -5528
- src/resources/locks/uv-agentic_rag-cloud_run.lock +0 -6120
- src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6231
- src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -6839
- src/resources/locks/uv-langgraph_base_react-agent_engine.lock +0 -5233
- src/resources/locks/uv-langgraph_base_react-cloud_run.lock +0 -5862
- src/resources/locks/uv-live_api-cloud_run.lock +0 -5832
- src/resources/setup_cicd/cicd_variables.tf +0 -41
- src/resources/setup_cicd/github.tf +0 -87
- {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
- {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
- {src/deployment_targets/agent_engine/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/gcs.py +0 -0
- {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
- {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
- {src → agent_starter_pack}/cli/utils/version.py +0 -0
- {src → agent_starter_pack}/data_ingestion/README.md +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Converters between A2A Parts and LangChain message content."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from a2a.types import FilePart, FileWithBytes, FileWithUri, Part, TextPart
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
LangChainContent = str | list[str | dict[str, Any]]
|
|
28
|
+
LangChainContentDict = dict[str, Any]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def convert_a2a_part_to_langchain_content(part: Part) -> LangChainContentDict | str:
|
|
32
|
+
"""Convert an A2A Part to LangChain message content format."""
|
|
33
|
+
|
|
34
|
+
root = part.root
|
|
35
|
+
|
|
36
|
+
if isinstance(root, TextPart):
|
|
37
|
+
return {"type": "text", "text": root.text}
|
|
38
|
+
|
|
39
|
+
elif isinstance(root, FilePart):
|
|
40
|
+
file_data = root.file
|
|
41
|
+
mime_type = file_data.mime_type if hasattr(file_data, "mime_type") else None
|
|
42
|
+
|
|
43
|
+
# Determine media type from mime_type
|
|
44
|
+
media_type = "image" # default
|
|
45
|
+
if mime_type:
|
|
46
|
+
if mime_type.startswith("audio/"):
|
|
47
|
+
media_type = "audio"
|
|
48
|
+
elif mime_type.startswith("video/"):
|
|
49
|
+
media_type = "video"
|
|
50
|
+
|
|
51
|
+
if isinstance(file_data, FileWithUri):
|
|
52
|
+
return {"type": media_type, "url": file_data.uri}
|
|
53
|
+
else:
|
|
54
|
+
# Base64 data should already be encoded
|
|
55
|
+
return {
|
|
56
|
+
"type": media_type,
|
|
57
|
+
"base64": file_data.bytes,
|
|
58
|
+
"mime_type": mime_type or "application/octet-stream",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
else:
|
|
62
|
+
import json
|
|
63
|
+
|
|
64
|
+
data_str = json.dumps(root.data, indent=2)
|
|
65
|
+
return {"type": "text", "text": f"[Structured Data]\n{data_str}"}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def convert_langchain_content_to_a2a_part(content: Any) -> Part:
|
|
69
|
+
"""Convert LangChain message content to an A2A Part."""
|
|
70
|
+
|
|
71
|
+
if isinstance(content, str):
|
|
72
|
+
return Part(root=TextPart(text=content))
|
|
73
|
+
|
|
74
|
+
if isinstance(content, dict):
|
|
75
|
+
content_type = content.get("type")
|
|
76
|
+
|
|
77
|
+
if content_type == "text":
|
|
78
|
+
text = content.get("text", "")
|
|
79
|
+
return Part(root=TextPart(text=text))
|
|
80
|
+
|
|
81
|
+
elif content_type in ("image", "audio", "video"):
|
|
82
|
+
# Handle URL-based media
|
|
83
|
+
if "url" in content:
|
|
84
|
+
return Part(root=FilePart(file=FileWithUri(uri=content["url"])))
|
|
85
|
+
|
|
86
|
+
# Handle base64-encoded media
|
|
87
|
+
elif "base64" in content:
|
|
88
|
+
mime_type = content.get("mime_type")
|
|
89
|
+
return Part(
|
|
90
|
+
root=FilePart(
|
|
91
|
+
file=FileWithBytes(bytes=content["base64"], mime_type=mime_type)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Handle file_id-based media
|
|
96
|
+
elif "file_id" in content:
|
|
97
|
+
return Part(
|
|
98
|
+
root=FilePart(file=FileWithUri(uri=f"file://{content['file_id']}"))
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
import json
|
|
103
|
+
|
|
104
|
+
text = json.dumps(content)
|
|
105
|
+
logger.warning(f"Unknown content type '{content_type}', converting to text")
|
|
106
|
+
return Part(root=TextPart(text=text))
|
|
107
|
+
|
|
108
|
+
logger.warning(f"Unknown content type: {type(content)}, converting to text")
|
|
109
|
+
return Part(root=TextPart(text=str(content)))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def convert_a2a_parts_to_langchain_content(parts: list[Part]) -> LangChainContent:
|
|
113
|
+
"""Convert a list of A2A Parts to LangChain message content."""
|
|
114
|
+
|
|
115
|
+
if not parts:
|
|
116
|
+
return ""
|
|
117
|
+
|
|
118
|
+
converted: list[str | dict[str, Any]] = []
|
|
119
|
+
for part in parts:
|
|
120
|
+
result = convert_a2a_part_to_langchain_content(part)
|
|
121
|
+
converted.append(result)
|
|
122
|
+
|
|
123
|
+
if len(converted) == 1 and isinstance(converted[0], str):
|
|
124
|
+
return converted[0]
|
|
125
|
+
|
|
126
|
+
return converted
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def convert_langchain_content_to_a2a_parts(content: LangChainContent) -> list[Part]:
|
|
130
|
+
"""Convert LangChain message content to a list of A2A Parts."""
|
|
131
|
+
|
|
132
|
+
if isinstance(content, str):
|
|
133
|
+
return [Part(root=TextPart(text=content))]
|
|
134
|
+
|
|
135
|
+
result: list[Part] = []
|
|
136
|
+
for item in content:
|
|
137
|
+
result.append(convert_langchain_content_to_a2a_part(item))
|
|
138
|
+
return result
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import uuid
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
|
|
21
|
+
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
22
|
+
from a2a.server.events import EventQueue
|
|
23
|
+
from a2a.types import (
|
|
24
|
+
Artifact,
|
|
25
|
+
Message,
|
|
26
|
+
Part,
|
|
27
|
+
Role,
|
|
28
|
+
TaskArtifactUpdateEvent,
|
|
29
|
+
TaskState,
|
|
30
|
+
TaskStatus,
|
|
31
|
+
TaskStatusUpdateEvent,
|
|
32
|
+
TextPart,
|
|
33
|
+
UnsupportedOperationError,
|
|
34
|
+
)
|
|
35
|
+
from a2a.utils.errors import ServerError
|
|
36
|
+
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
|
37
|
+
from langgraph.graph.state import CompiledStateGraph
|
|
38
|
+
from pydantic import BaseModel
|
|
39
|
+
from typing_extensions import override
|
|
40
|
+
|
|
41
|
+
from ..converters import (
|
|
42
|
+
convert_a2a_parts_to_langchain_content,
|
|
43
|
+
convert_langchain_content_to_a2a_parts,
|
|
44
|
+
)
|
|
45
|
+
from .task_result_aggregator import LangGraphTaskResultAggregator
|
|
46
|
+
|
|
47
|
+
logging.basicConfig(level=logging.INFO)
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LangGraphAgentExecutorConfig(BaseModel):
|
|
52
|
+
"""Configuration for the LangGraphAgentExecutor."""
|
|
53
|
+
|
|
54
|
+
enable_streaming: bool = True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class LangGraphAgentExecutor(AgentExecutor):
|
|
58
|
+
"""An AgentExecutor that runs a LangGraph agent against an A2A request and
|
|
59
|
+
publishes updates to an event queue."""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
*,
|
|
64
|
+
graph: CompiledStateGraph,
|
|
65
|
+
config: LangGraphAgentExecutorConfig | None = None,
|
|
66
|
+
):
|
|
67
|
+
super().__init__()
|
|
68
|
+
self._graph = graph
|
|
69
|
+
self._config = config or LangGraphAgentExecutorConfig()
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
73
|
+
"""Cancel the execution."""
|
|
74
|
+
# TODO: Implement proper cancellation logic if needed
|
|
75
|
+
raise ServerError(error=UnsupportedOperationError())
|
|
76
|
+
|
|
77
|
+
@override
|
|
78
|
+
async def execute(
|
|
79
|
+
self,
|
|
80
|
+
context: RequestContext,
|
|
81
|
+
event_queue: EventQueue,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Executes an A2A request and publishes updates to the event queue."""
|
|
84
|
+
|
|
85
|
+
if not context.message:
|
|
86
|
+
raise ValueError("A2A request must have a message")
|
|
87
|
+
|
|
88
|
+
if not context.task_id:
|
|
89
|
+
raise ValueError("task_id is required")
|
|
90
|
+
if not context.context_id:
|
|
91
|
+
raise ValueError("context_id is required")
|
|
92
|
+
|
|
93
|
+
task_id = context.task_id
|
|
94
|
+
context_id = context.context_id
|
|
95
|
+
|
|
96
|
+
if not context.current_task:
|
|
97
|
+
await event_queue.enqueue_event(
|
|
98
|
+
TaskStatusUpdateEvent(
|
|
99
|
+
task_id=task_id,
|
|
100
|
+
status=TaskStatus(
|
|
101
|
+
state=TaskState.submitted,
|
|
102
|
+
message=context.message,
|
|
103
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
104
|
+
),
|
|
105
|
+
context_id=context_id,
|
|
106
|
+
final=False,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
await self._handle_request(context, event_queue)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.error("Error handling A2A request: %s", e, exc_info=True)
|
|
114
|
+
try:
|
|
115
|
+
await event_queue.enqueue_event(
|
|
116
|
+
TaskStatusUpdateEvent(
|
|
117
|
+
task_id=task_id,
|
|
118
|
+
status=TaskStatus(
|
|
119
|
+
state=TaskState.failed,
|
|
120
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
121
|
+
message=Message(
|
|
122
|
+
message_id=str(uuid.uuid4()),
|
|
123
|
+
role=Role.agent,
|
|
124
|
+
parts=[Part(root=TextPart(text=str(e)))],
|
|
125
|
+
),
|
|
126
|
+
),
|
|
127
|
+
context_id=context_id,
|
|
128
|
+
final=True,
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
except Exception as enqueue_error:
|
|
132
|
+
logger.error(
|
|
133
|
+
"Failed to publish failure event: %s", enqueue_error, exc_info=True
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def _handle_request(
|
|
137
|
+
self,
|
|
138
|
+
context: RequestContext,
|
|
139
|
+
event_queue: EventQueue,
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Handle the A2A request and publish events."""
|
|
142
|
+
|
|
143
|
+
graph = self._graph
|
|
144
|
+
|
|
145
|
+
if not context.task_id:
|
|
146
|
+
raise ValueError("task_id is required")
|
|
147
|
+
if not context.context_id:
|
|
148
|
+
raise ValueError("context_id is required")
|
|
149
|
+
|
|
150
|
+
task_id = context.task_id
|
|
151
|
+
context_id = context.context_id
|
|
152
|
+
|
|
153
|
+
await event_queue.enqueue_event(
|
|
154
|
+
TaskStatusUpdateEvent(
|
|
155
|
+
task_id=task_id,
|
|
156
|
+
status=TaskStatus(
|
|
157
|
+
state=TaskState.working,
|
|
158
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
159
|
+
),
|
|
160
|
+
context_id=context_id,
|
|
161
|
+
final=False,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Convert A2A message parts to LangChain content
|
|
166
|
+
message_content = (
|
|
167
|
+
convert_a2a_parts_to_langchain_content(context.message.parts)
|
|
168
|
+
if context.message
|
|
169
|
+
else ""
|
|
170
|
+
)
|
|
171
|
+
messages = [HumanMessage(content=message_content)]
|
|
172
|
+
input_dict = {"messages": messages}
|
|
173
|
+
|
|
174
|
+
task_result_aggregator = LangGraphTaskResultAggregator()
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
if self._config.enable_streaming:
|
|
178
|
+
async for chunk in graph.astream(input_dict, stream_mode="messages"):
|
|
179
|
+
if isinstance(chunk, tuple) and len(chunk) > 0:
|
|
180
|
+
message = chunk[0]
|
|
181
|
+
|
|
182
|
+
# Process AIMessage chunks
|
|
183
|
+
if isinstance(message, AIMessage) and message.content:
|
|
184
|
+
task_result_aggregator.process_message(message)
|
|
185
|
+
|
|
186
|
+
parts = convert_langchain_content_to_a2a_parts(
|
|
187
|
+
message.content
|
|
188
|
+
)
|
|
189
|
+
await event_queue.enqueue_event(
|
|
190
|
+
TaskStatusUpdateEvent(
|
|
191
|
+
task_id=task_id,
|
|
192
|
+
status=TaskStatus(
|
|
193
|
+
state=TaskState.working,
|
|
194
|
+
timestamp=datetime.now(
|
|
195
|
+
timezone.utc
|
|
196
|
+
).isoformat(),
|
|
197
|
+
message=Message(
|
|
198
|
+
message_id=str(uuid.uuid4()),
|
|
199
|
+
role=Role.agent,
|
|
200
|
+
parts=parts,
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
context_id=context_id,
|
|
204
|
+
final=False,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Process ToolMessage chunks (for multimodal content)
|
|
209
|
+
elif isinstance(message, ToolMessage):
|
|
210
|
+
task_result_aggregator.process_message(message)
|
|
211
|
+
else:
|
|
212
|
+
result = await graph.ainvoke(input_dict)
|
|
213
|
+
if "messages" in result:
|
|
214
|
+
for msg in result["messages"]:
|
|
215
|
+
if isinstance(msg, (AIMessage, ToolMessage)) and msg.content:
|
|
216
|
+
task_result_aggregator.process_message(msg)
|
|
217
|
+
if (
|
|
218
|
+
task_result_aggregator.task_state == TaskState.working
|
|
219
|
+
and task_result_aggregator.task_status_message is not None
|
|
220
|
+
and task_result_aggregator.task_status_message.parts
|
|
221
|
+
):
|
|
222
|
+
# Publish the artifact update event as the final result
|
|
223
|
+
await event_queue.enqueue_event(
|
|
224
|
+
TaskArtifactUpdateEvent(
|
|
225
|
+
task_id=task_id,
|
|
226
|
+
last_chunk=True,
|
|
227
|
+
context_id=context_id,
|
|
228
|
+
artifact=Artifact(
|
|
229
|
+
artifact_id=str(uuid.uuid4()),
|
|
230
|
+
parts=task_result_aggregator.get_final_parts(),
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
# Publish the final status update event
|
|
235
|
+
await event_queue.enqueue_event(
|
|
236
|
+
TaskStatusUpdateEvent(
|
|
237
|
+
task_id=task_id,
|
|
238
|
+
status=TaskStatus(
|
|
239
|
+
state=TaskState.completed,
|
|
240
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
241
|
+
),
|
|
242
|
+
context_id=context_id,
|
|
243
|
+
final=True,
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
else:
|
|
247
|
+
# Publish final status with current task_state and message
|
|
248
|
+
await event_queue.enqueue_event(
|
|
249
|
+
TaskStatusUpdateEvent(
|
|
250
|
+
task_id=task_id,
|
|
251
|
+
status=TaskStatus(
|
|
252
|
+
state=task_result_aggregator.task_state,
|
|
253
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
254
|
+
message=task_result_aggregator.task_status_message,
|
|
255
|
+
),
|
|
256
|
+
context_id=context_id,
|
|
257
|
+
final=True,
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
logger.error("Error during graph execution: %s", e, exc_info=True)
|
|
263
|
+
# Update task state to failed using aggregator
|
|
264
|
+
task_result_aggregator.set_failed(str(e))
|
|
265
|
+
raise
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from a2a.types import (
|
|
18
|
+
FilePart,
|
|
19
|
+
FileWithBytes,
|
|
20
|
+
FileWithUri,
|
|
21
|
+
Message,
|
|
22
|
+
Part,
|
|
23
|
+
Role,
|
|
24
|
+
TaskState,
|
|
25
|
+
TextPart,
|
|
26
|
+
)
|
|
27
|
+
from langchain_core.messages import AIMessage, ToolMessage
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LangGraphTaskResultAggregator:
|
|
31
|
+
"""Aggregates streaming LangGraph messages into a final consolidated result."""
|
|
32
|
+
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
self._task_state = TaskState.working
|
|
35
|
+
self._accumulated_content = "" # Accumulate text content across chunks
|
|
36
|
+
self._task_status_message: Message | None = None
|
|
37
|
+
self._media_parts: list[Part] = [] # Track media parts from tool responses
|
|
38
|
+
|
|
39
|
+
def process_message(self, message: AIMessage | ToolMessage) -> None:
|
|
40
|
+
"""Process a streaming message chunk from LangGraph."""
|
|
41
|
+
|
|
42
|
+
# Handle tool responses to extract media
|
|
43
|
+
if isinstance(message, ToolMessage):
|
|
44
|
+
self._extract_media_from_tool_response(message)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
if not message.content:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if isinstance(message.content, str):
|
|
51
|
+
self._accumulated_content += message.content
|
|
52
|
+
|
|
53
|
+
elif isinstance(message.content, list):
|
|
54
|
+
for item in message.content:
|
|
55
|
+
if isinstance(item, str):
|
|
56
|
+
self._accumulated_content += item
|
|
57
|
+
elif isinstance(item, dict) and item.get("type") == "text":
|
|
58
|
+
self._accumulated_content += item.get("text", "")
|
|
59
|
+
|
|
60
|
+
# Update the task status message with current accumulated content
|
|
61
|
+
if self._accumulated_content or self._media_parts:
|
|
62
|
+
parts = []
|
|
63
|
+
if self._accumulated_content:
|
|
64
|
+
parts.append(Part(root=TextPart(text=self._accumulated_content)))
|
|
65
|
+
parts.extend(self._media_parts)
|
|
66
|
+
|
|
67
|
+
self._task_status_message = Message(
|
|
68
|
+
message_id="aggregated",
|
|
69
|
+
role=Role.agent,
|
|
70
|
+
parts=parts,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def _extract_media_from_tool_response(self, message: ToolMessage) -> None:
|
|
74
|
+
"""Extract media parts from a ToolMessage."""
|
|
75
|
+
|
|
76
|
+
if not message.content:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
if isinstance(message.content, list):
|
|
80
|
+
for item in message.content:
|
|
81
|
+
if isinstance(item, dict):
|
|
82
|
+
content_type = item.get("type")
|
|
83
|
+
if content_type == "image":
|
|
84
|
+
self._media_parts.append(
|
|
85
|
+
self._convert_media_to_a2a_part(item, "image")
|
|
86
|
+
)
|
|
87
|
+
elif content_type == "audio":
|
|
88
|
+
self._media_parts.append(
|
|
89
|
+
self._convert_media_to_a2a_part(item, "audio")
|
|
90
|
+
)
|
|
91
|
+
elif content_type == "video":
|
|
92
|
+
self._media_parts.append(
|
|
93
|
+
self._convert_media_to_a2a_part(item, "video")
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def _convert_media_to_a2a_part(
|
|
97
|
+
self, content: dict[str, str], media_type: str
|
|
98
|
+
) -> Part:
|
|
99
|
+
"""Convert a media content block to an A2A Part."""
|
|
100
|
+
|
|
101
|
+
mime_type = content.get("mime_type")
|
|
102
|
+
|
|
103
|
+
if "url" in content:
|
|
104
|
+
return Part(
|
|
105
|
+
root=FilePart(file=FileWithUri(uri=content["url"], mime_type=mime_type))
|
|
106
|
+
)
|
|
107
|
+
elif "base64" in content:
|
|
108
|
+
return Part(
|
|
109
|
+
root=FilePart(
|
|
110
|
+
file=FileWithBytes(bytes=content["base64"], mime_type=mime_type)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
elif "file_id" in content:
|
|
114
|
+
# For now, store file_id as a URI
|
|
115
|
+
return Part(
|
|
116
|
+
root=FilePart(
|
|
117
|
+
file=FileWithUri(
|
|
118
|
+
uri=f"file://{content['file_id']}", mime_type=mime_type
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Fallback to empty text part
|
|
124
|
+
return Part(root=TextPart(text=f"[{media_type} content]"))
|
|
125
|
+
|
|
126
|
+
def get_final_parts(self) -> list[Part]:
|
|
127
|
+
"""Get the final consolidated parts for the artifact."""
|
|
128
|
+
|
|
129
|
+
parts = []
|
|
130
|
+
if self._accumulated_content:
|
|
131
|
+
parts.append(Part(root=TextPart(text=self._accumulated_content)))
|
|
132
|
+
parts.extend(self._media_parts)
|
|
133
|
+
return parts if parts else []
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def task_state(self) -> TaskState:
|
|
137
|
+
"""Get the current task state."""
|
|
138
|
+
return self._task_state
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def task_status_message(self) -> Message | None:
|
|
142
|
+
"""Get the current task status message with accumulated content."""
|
|
143
|
+
return self._task_status_message
|
|
144
|
+
|
|
145
|
+
def set_failed(self, error_message: str) -> None:
|
|
146
|
+
"""Set the task state to failed."""
|
|
147
|
+
self._task_state = TaskState.failed
|
|
148
|
+
self._task_status_message = Message(
|
|
149
|
+
message_id="error",
|
|
150
|
+
role=Role.agent,
|
|
151
|
+
parts=[Part(root=TextPart(text=error_message))],
|
|
152
|
+
)
|