agent-starter-pack 0.15.7__py3-none-any.whl → 0.17.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.
Potentially problematic release.
This version of agent-starter-pack might be problematic. Click here for more details.
- {agents → agent_starter_pack/agents}/adk_base/.template/templateconfig.yaml +1 -1
- {agents/live_api → agent_starter_pack/agents/adk_live}/.template/templateconfig.yaml +5 -7
- agent_starter_pack/agents/adk_live/README.md +32 -0
- agent_starter_pack/agents/adk_live/app/agent.py +48 -0
- agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
- {agents → agent_starter_pack/agents}/agentic_rag/.template/templateconfig.yaml +1 -1
- agent_starter_pack/agents/crewai_coding_crew/app/agent.py +47 -0
- agent_starter_pack/agents/langgraph_base_react/app/agent.py +34 -0
- {src → agent_starter_pack}/base_template/GEMINI.md +1 -1
- {src → agent_starter_pack}/base_template/Makefile +130 -61
- {src → agent_starter_pack}/base_template/README.md +6 -6
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/log_sinks.tf +31 -25
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/github.tf +14 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/log_sinks.tf +37 -28
- {src → agent_starter_pack}/base_template/deployment/terraform/providers.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +4 -2
- {src → agent_starter_pack}/base_template/pyproject.toml +22 -21
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +5 -5
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +3 -3
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +74 -11
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +6 -6
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +4 -4
- {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +95 -13
- {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +1 -1
- {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/typing.py +4 -4
- {src → agent_starter_pack}/cli/commands/setup_cicd.py +1 -1
- {src → agent_starter_pack}/cli/main.py +2 -2
- {src → agent_starter_pack}/cli/utils/gcp.py +1 -1
- {src → agent_starter_pack}/cli/utils/remote_template.py +12 -9
- {src → agent_starter_pack}/cli/utils/template.py +19 -15
- agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +82 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +387 -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 +255 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +40 -14
- {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +13 -4
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +520 -0
- {src → agent_starter_pack}/deployment_targets/cloud_run/Dockerfile +3 -3
- {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -4
- {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/service.tf +7 -7
- {src → agent_starter_pack}/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +207 -5
- {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/README.md +82 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +249 -0
- {src → agent_starter_pack}/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/server.py +190 -146
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +39 -1007
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.tsx +1 -1
- {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
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/SidePanel.tsx +11 -5
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/side-panel.scss +146 -115
- 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 +38 -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 +204 -23
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/local_chat_history.py +2 -0
- {src → agent_starter_pack}/resources/docs/adk-cheatsheet.md +5 -5
- agent_starter_pack/resources/idx/.idx/dev.nix +64 -0
- agent_starter_pack/resources/idx/idx-template.json +6 -0
- {src → agent_starter_pack}/resources/idx/idx-template.nix +2 -3
- {src → agent_starter_pack}/resources/locks/uv-adk_base-agent_engine.lock +1079 -954
- {src → agent_starter_pack}/resources/locks/uv-adk_base-cloud_run.lock +1441 -1309
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4229 -0
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +4822 -0
- {src → agent_starter_pack}/resources/locks/uv-agentic_rag-agent_engine.lock +1107 -997
- {src → agent_starter_pack}/resources/locks/uv-agentic_rag-cloud_run.lock +1485 -1368
- {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-agent_engine.lock +1294 -1297
- {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-cloud_run.lock +2028 -1807
- {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-agent_engine.lock +1176 -1197
- {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-cloud_run.lock +1947 -1679
- {src → agent_starter_pack}/utils/generate_locks.py +12 -7
- {src → agent_starter_pack}/utils/lock_utils.py +2 -2
- {src → agent_starter_pack}/utils/watch_and_rebuild.py +1 -1
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/METADATA +17 -18
- agent_starter_pack-0.17.0.dist-info/RECORD +179 -0
- agent_starter_pack-0.17.0.dist-info/entry_points.txt +2 -0
- llm.txt +1 -1
- agent_starter_pack-0.15.7.dist-info/RECORD +0 -176
- agent_starter_pack-0.15.7.dist-info/entry_points.txt +0 -2
- agents/crewai_coding_crew/app/agent.py +0 -86
- agents/langgraph_base_react/app/agent.py +0 -73
- agents/live_api/README.md +0 -37
- agents/live_api/app/agent.py +0 -72
- agents/live_api/tests/integration/test_server_e2e.py +0 -260
- agents/live_api/tests/load_test/load_test.py +0 -40
- agents/live_api/tests/unit/test_server.py +0 -144
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -186
- src/deployment_targets/agent_engine/tests/load_test/README.md +0 -37
- src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -126
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -122
- src/resources/idx/.idx/dev.nix +0 -50
- src/resources/idx/idx-template.json +0 -21
- src/resources/locks/uv-live_api-cloud_run.lock +0 -6118
- {agents → agent_starter_pack/agents}/README.md +0 -0
- {agents → agent_starter_pack/agents}/adk_base/README.md +0 -0
- {agents → agent_starter_pack/agents}/adk_base/app/__init__.py +0 -0
- {agents → agent_starter_pack/agents}/adk_base/app/agent.py +0 -0
- {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +0 -0
- {agents → agent_starter_pack/agents}/adk_base/notebooks/evaluating_adk_agent.ipynb +0 -0
- {agents → agent_starter_pack/agents}/adk_base/tests/integration/test_agent.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/README.md +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/__init__.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/notebooks/evaluating_adk_agent.ipynb +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/.template/templateconfig.yaml +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/README.md +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/agents.yaml +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/tasks.yaml +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/crew.py +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +0 -0
- {agents → agent_starter_pack/agents}/crewai_coding_crew/tests/integration/test_agent.py +0 -0
- {agents → agent_starter_pack/agents}/langgraph_base_react/.template/templateconfig.yaml +0 -0
- {agents → agent_starter_pack/agents}/langgraph_base_react/README.md +0 -0
- {agents → agent_starter_pack/agents}/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -0
- {agents → agent_starter_pack/agents}/langgraph_base_react/tests/integration/test_agent.py +0 -0
- {src → agent_starter_pack}/base_template/.gitignore +0 -0
- {src → agent_starter_pack}/base_template/deployment/README.md +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +0 -0
- {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
- {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/gcs.py +0 -0
- {src → agent_starter_pack}/cli/commands/create.py +0 -0
- {src → agent_starter_pack}/cli/commands/enhance.py +0 -0
- {src → agent_starter_pack}/cli/commands/list.py +0 -0
- {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
- {src → agent_starter_pack}/cli/utils/cicd.py +0 -0
- {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
- {src → agent_starter_pack}/cli/utils/logging.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/components/process_data.py +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +0 -0
- {src → agent_starter_pack}/data_ingestion/pyproject.toml +0 -0
- {src → agent_starter_pack}/data_ingestion/uv.lock +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
- {src → agent_starter_pack}/deployment_targets/cloud_run/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
- {src → agent_starter_pack}/frontends/streamlit/frontend/side_bar.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/streamlit_app.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/style/app_markdown.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/chat_utils.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/message_editing.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/stream_handler.py +0 -0
- {src → agent_starter_pack}/frontends/streamlit/frontend/utils/title_summary.py +0 -0
- {src → agent_starter_pack}/resources/containers/data_processing/Dockerfile +0 -0
- {src → agent_starter_pack}/resources/containers/e2e-tests/Dockerfile +0 -0
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,86 +0,0 @@
|
|
|
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
|
-
# mypy: disable-error-code="union-attr"
|
|
16
|
-
from langchain_core.messages import BaseMessage
|
|
17
|
-
from langchain_core.runnables import RunnableConfig
|
|
18
|
-
from langchain_core.tools import tool
|
|
19
|
-
from langchain_google_vertexai import ChatVertexAI
|
|
20
|
-
from langgraph.graph import END, MessagesState, StateGraph
|
|
21
|
-
from langgraph.prebuilt import ToolNode
|
|
22
|
-
|
|
23
|
-
from .crew.crew import DevCrew
|
|
24
|
-
|
|
25
|
-
LOCATION = "global"
|
|
26
|
-
LLM = "gemini-2.5-flash"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@tool
|
|
30
|
-
def coding_tool(code_instructions: str) -> str:
|
|
31
|
-
"""Use this tool to write a python program given a set of requirements and or instructions."""
|
|
32
|
-
inputs = {"code_instructions": code_instructions}
|
|
33
|
-
return DevCrew().crew().kickoff(inputs=inputs)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
tools = [coding_tool]
|
|
37
|
-
|
|
38
|
-
# 2. Set up the language model
|
|
39
|
-
llm = ChatVertexAI(
|
|
40
|
-
model=LLM, location=LOCATION, temperature=0, max_tokens=4096, streaming=True
|
|
41
|
-
).bind_tools(tools)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# 3. Define workflow components
|
|
45
|
-
def should_continue(state: MessagesState) -> str:
|
|
46
|
-
"""Determines whether to use the crew or end the conversation."""
|
|
47
|
-
last_message = state["messages"][-1]
|
|
48
|
-
return "dev_crew" if last_message.tool_calls else END
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def call_model(state: MessagesState, config: RunnableConfig) -> dict[str, BaseMessage]:
|
|
52
|
-
"""Calls the language model and returns the response."""
|
|
53
|
-
system_message = (
|
|
54
|
-
"You are an expert Lead Software Engineer Manager.\n"
|
|
55
|
-
"Your role is to speak to a user and understand what kind of code they need to "
|
|
56
|
-
"build.\n"
|
|
57
|
-
"Part of your task is therefore to gather requirements and clarifying ambiguity "
|
|
58
|
-
"by asking followup questions. Don't ask all the questions together as the user "
|
|
59
|
-
"has a low attention span, rather ask a question at the time.\n"
|
|
60
|
-
"Once the problem to solve is clear, you will call your tool for writing the "
|
|
61
|
-
"solution.\n"
|
|
62
|
-
"Remember, you are an expert in understanding requirements but you cannot code, "
|
|
63
|
-
"use your coding tool to generate a solution. Keep the test cases if any, they "
|
|
64
|
-
"are useful for the user."
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
messages_with_system = [{"type": "system", "content": system_message}] + state[
|
|
68
|
-
"messages"
|
|
69
|
-
]
|
|
70
|
-
# Forward the RunnableConfig object to ensure the agent is capable of streaming the response.
|
|
71
|
-
response = llm.invoke(messages_with_system, config)
|
|
72
|
-
return {"messages": response}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# 4. Create the workflow graph
|
|
76
|
-
workflow = StateGraph(MessagesState)
|
|
77
|
-
workflow.add_node("agent", call_model)
|
|
78
|
-
workflow.add_node("dev_crew", ToolNode(tools))
|
|
79
|
-
workflow.set_entry_point("agent")
|
|
80
|
-
|
|
81
|
-
# 5. Define graph edges
|
|
82
|
-
workflow.add_conditional_edges("agent", should_continue)
|
|
83
|
-
workflow.add_edge("dev_crew", "agent")
|
|
84
|
-
|
|
85
|
-
# 6. Compile the workflow
|
|
86
|
-
agent = workflow.compile()
|
|
@@ -1,73 +0,0 @@
|
|
|
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
|
-
# mypy: disable-error-code="union-attr"
|
|
16
|
-
from langchain_core.messages import BaseMessage
|
|
17
|
-
from langchain_core.runnables import RunnableConfig
|
|
18
|
-
from langchain_core.tools import tool
|
|
19
|
-
from langchain_google_vertexai import ChatVertexAI
|
|
20
|
-
from langgraph.graph import END, MessagesState, StateGraph
|
|
21
|
-
from langgraph.prebuilt import ToolNode
|
|
22
|
-
|
|
23
|
-
LOCATION = "global"
|
|
24
|
-
LLM = "gemini-2.5-flash"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# 1. Define tools
|
|
28
|
-
@tool
|
|
29
|
-
def search(query: str) -> str:
|
|
30
|
-
"""Simulates a web search. Use it get information on weather"""
|
|
31
|
-
if "sf" in query.lower() or "san francisco" in query.lower():
|
|
32
|
-
return "It's 60 degrees and foggy."
|
|
33
|
-
return "It's 90 degrees and sunny."
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
tools = [search]
|
|
37
|
-
|
|
38
|
-
# 2. Set up the language model
|
|
39
|
-
llm = ChatVertexAI(
|
|
40
|
-
model=LLM, location=LOCATION, temperature=0, max_tokens=1024, streaming=True
|
|
41
|
-
).bind_tools(tools)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# 3. Define workflow components
|
|
45
|
-
def should_continue(state: MessagesState) -> str:
|
|
46
|
-
"""Determines whether to use tools or end the conversation."""
|
|
47
|
-
last_message = state["messages"][-1]
|
|
48
|
-
return "tools" if last_message.tool_calls else END
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def call_model(state: MessagesState, config: RunnableConfig) -> dict[str, BaseMessage]:
|
|
52
|
-
"""Calls the language model and returns the response."""
|
|
53
|
-
system_message = "You are a helpful AI assistant."
|
|
54
|
-
messages_with_system = [{"type": "system", "content": system_message}] + state[
|
|
55
|
-
"messages"
|
|
56
|
-
]
|
|
57
|
-
# Forward the RunnableConfig object to ensure the agent is capable of streaming the response.
|
|
58
|
-
response = llm.invoke(messages_with_system, config)
|
|
59
|
-
return {"messages": response}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# 4. Create the workflow graph
|
|
63
|
-
workflow = StateGraph(MessagesState)
|
|
64
|
-
workflow.add_node("agent", call_model)
|
|
65
|
-
workflow.add_node("tools", ToolNode(tools))
|
|
66
|
-
workflow.set_entry_point("agent")
|
|
67
|
-
|
|
68
|
-
# 5. Define graph edges
|
|
69
|
-
workflow.add_conditional_edges("agent", should_continue)
|
|
70
|
-
workflow.add_edge("tools", "agent")
|
|
71
|
-
|
|
72
|
-
# 6. Compile the workflow
|
|
73
|
-
agent = workflow.compile()
|
agents/live_api/README.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Multimodal Live Agent
|
|
2
|
-
|
|
3
|
-
This pattern showcases a real-time conversational agent powered by Google Gemini. The agent handles audio, video, and text interactions while leveraging tool calling capabilities for enhanced responses.
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
**Key components:**
|
|
8
|
-
|
|
9
|
-
- **Python Backend** (in `app/` folder): A production-ready server built with [FastAPI](https://fastapi.tiangolo.com/) and [google-genai](https://googleapis.github.io/python-genai/) that features:
|
|
10
|
-
|
|
11
|
-
- **Real-time bidirectional communication** via WebSockets between the frontend and Gemini model
|
|
12
|
-
- **Integrated tool calling** with a weather information tool for demonstrating external data retrieval
|
|
13
|
-
- **Production-grade reliability** with retry logic and automatic reconnection capabilities
|
|
14
|
-
- **Deployment flexibility** supporting both AI Studio and Vertex AI endpoints
|
|
15
|
-
- **Feedback logging endpoint** for collecting user interactions
|
|
16
|
-
|
|
17
|
-
- **React Frontend** (in `frontend/` folder): Extends the [Multimodal live API Web Console](https://github.com/google-gemini/multimodal-live-api-web-console), with added features like **custom URLs** and **feedback collection**.
|
|
18
|
-
|
|
19
|
-

|
|
20
|
-
|
|
21
|
-
Once both the backend and frontend are running, click the play button in the frontend UI to establish a connection with the backend. You can now interact with the Multimodal Live Agent! You can try asking questions such as "What's the weather like in San Francisco?" to see the agent use its weather information tool.
|
|
22
|
-
|
|
23
|
-
## Additional Resources for Multimodal Live API
|
|
24
|
-
|
|
25
|
-
Explore these resources to learn more about the Multimodal Live API and see examples of its usage:
|
|
26
|
-
|
|
27
|
-
- [Project Pastra](https://github.com/heiko-hotz/gemini-multimodal-live-dev-guide/tree/main): a comprehensive developer guide for the Gemini Multimodal Live API.
|
|
28
|
-
- [Google Cloud Multimodal Live API demos and samples](https://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/multimodal-live-api): Collection of code samples and demo applications leveraging multimodal live API in Vertex AI
|
|
29
|
-
- [Gemini 2 Cookbook](https://github.com/google-gemini/cookbook/tree/main/gemini-2): Practical examples and tutorials for working with Gemini 2
|
|
30
|
-
- [Multimodal Live API Web Console](https://github.com/google-gemini/multimodal-live-api-web-console): Interactive React-based web interface for testing and experimenting with Gemini Multimodal Live API.
|
|
31
|
-
|
|
32
|
-
## Current Status & Future Work
|
|
33
|
-
|
|
34
|
-
This pattern is under active development. Key areas planned for future enhancement include:
|
|
35
|
-
|
|
36
|
-
* **Observability:** Implementing comprehensive monitoring and tracing features.
|
|
37
|
-
* **Load Testing:** Integrating load testing capabilities.
|
agents/live_api/app/agent.py
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
import os
|
|
16
|
-
|
|
17
|
-
import google.auth
|
|
18
|
-
import vertexai
|
|
19
|
-
from google import genai
|
|
20
|
-
from google.genai import types
|
|
21
|
-
|
|
22
|
-
# Constants
|
|
23
|
-
VERTEXAI = os.getenv("VERTEXAI", "true").lower() == "true"
|
|
24
|
-
LOCATION = "us-central1"
|
|
25
|
-
MODEL_ID = "gemini-live-2.5-flash-preview-native-audio"
|
|
26
|
-
|
|
27
|
-
# Initialize Google Cloud clients
|
|
28
|
-
credentials, project_id = google.auth.default()
|
|
29
|
-
vertexai.init(project=project_id, location=LOCATION)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if VERTEXAI:
|
|
33
|
-
genai_client = genai.Client(project=project_id, location=LOCATION, vertexai=True)
|
|
34
|
-
else:
|
|
35
|
-
# API key should be set using GOOGLE_API_KEY environment variable
|
|
36
|
-
genai_client = genai.Client(http_options={"api_version": "v1alpha"})
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get_weather(query: str) -> dict:
|
|
40
|
-
"""Simulates a web search. Use it get information on weather.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
query: A string containing the location to get weather information for.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
A string with the simulated weather information for the queried location.
|
|
47
|
-
"""
|
|
48
|
-
if "sf" in query.lower() or "san francisco" in query.lower():
|
|
49
|
-
return {"output": "It's 60 degrees and foggy."}
|
|
50
|
-
return {"output": "It's 90 degrees and sunny."}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# Configure tools available to the agent and live connection
|
|
54
|
-
tool_functions = {"get_weather": get_weather}
|
|
55
|
-
|
|
56
|
-
live_connect_config = types.LiveConnectConfig(
|
|
57
|
-
response_modalities=[types.Modality.AUDIO],
|
|
58
|
-
tools=list(tool_functions.values()),
|
|
59
|
-
system_instruction=types.Content(
|
|
60
|
-
parts=[
|
|
61
|
-
types.Part(
|
|
62
|
-
text="""You are a helpful AI assistant designed to provide accurate and useful information. You are able to accommodate different languages and tones of voice."""
|
|
63
|
-
)
|
|
64
|
-
]
|
|
65
|
-
),
|
|
66
|
-
speech_config=types.SpeechConfig(
|
|
67
|
-
voice_config=types.VoiceConfig(
|
|
68
|
-
prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Kore")
|
|
69
|
-
)
|
|
70
|
-
),
|
|
71
|
-
enable_affective_dialog=True,
|
|
72
|
-
)
|
|
@@ -1,260 +0,0 @@
|
|
|
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
|
-
import asyncio
|
|
16
|
-
import json
|
|
17
|
-
import logging
|
|
18
|
-
import os
|
|
19
|
-
import subprocess
|
|
20
|
-
import sys
|
|
21
|
-
import threading
|
|
22
|
-
import time
|
|
23
|
-
import uuid
|
|
24
|
-
from collections.abc import Iterator
|
|
25
|
-
from typing import Any
|
|
26
|
-
|
|
27
|
-
import pytest
|
|
28
|
-
import requests
|
|
29
|
-
import websockets.client
|
|
30
|
-
|
|
31
|
-
# Configure logging
|
|
32
|
-
logging.basicConfig(level=logging.DEBUG)
|
|
33
|
-
logger = logging.getLogger(__name__)
|
|
34
|
-
|
|
35
|
-
BASE_URL = "ws://127.0.0.1:8000/"
|
|
36
|
-
WS_URL = BASE_URL + "ws"
|
|
37
|
-
|
|
38
|
-
FEEDBACK_URL = "http://127.0.0.1:8000/feedback"
|
|
39
|
-
HEADERS = {"Content-Type": "application/json"}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def log_output(pipe: Any, log_func: Any) -> None:
|
|
43
|
-
"""Log the output from the given pipe."""
|
|
44
|
-
for line in iter(pipe.readline, ""):
|
|
45
|
-
log_func(line.strip())
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def start_server() -> subprocess.Popen[str]:
|
|
49
|
-
"""Start the FastAPI server using subprocess and log its output."""
|
|
50
|
-
command = [
|
|
51
|
-
sys.executable,
|
|
52
|
-
"-m",
|
|
53
|
-
"uvicorn",
|
|
54
|
-
"app.server:app",
|
|
55
|
-
"--host",
|
|
56
|
-
"0.0.0.0",
|
|
57
|
-
"--port",
|
|
58
|
-
"8000",
|
|
59
|
-
]
|
|
60
|
-
env = os.environ.copy()
|
|
61
|
-
env["INTEGRATION_TEST"] = "TRUE"
|
|
62
|
-
process = subprocess.Popen(
|
|
63
|
-
command,
|
|
64
|
-
stdout=subprocess.PIPE,
|
|
65
|
-
stderr=subprocess.PIPE,
|
|
66
|
-
text=True,
|
|
67
|
-
bufsize=1,
|
|
68
|
-
env=env,
|
|
69
|
-
encoding="utf-8",
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
# Start threads to log stdout and stderr in real-time
|
|
73
|
-
threading.Thread(
|
|
74
|
-
target=log_output, args=(process.stdout, logger.info), daemon=True
|
|
75
|
-
).start()
|
|
76
|
-
threading.Thread(
|
|
77
|
-
target=log_output, args=(process.stderr, logger.error), daemon=True
|
|
78
|
-
).start()
|
|
79
|
-
|
|
80
|
-
return process
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def wait_for_server(timeout: int = 60, interval: int = 1) -> bool:
|
|
84
|
-
"""Wait for the server to be ready."""
|
|
85
|
-
start_time = time.time()
|
|
86
|
-
while time.time() - start_time < timeout:
|
|
87
|
-
try:
|
|
88
|
-
response = requests.get("http://127.0.0.1:8000/docs", timeout=10)
|
|
89
|
-
if response.status_code == 200:
|
|
90
|
-
logger.info("Server is ready")
|
|
91
|
-
return True
|
|
92
|
-
except Exception:
|
|
93
|
-
pass
|
|
94
|
-
time.sleep(interval)
|
|
95
|
-
logger.error(f"Server did not become ready within {timeout} seconds")
|
|
96
|
-
return False
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@pytest.fixture(scope="session")
|
|
100
|
-
def server_fixture(request: Any) -> Iterator[subprocess.Popen[str]]:
|
|
101
|
-
"""Pytest fixture to start and stop the server for testing."""
|
|
102
|
-
logger.info("Starting server process")
|
|
103
|
-
server_process = start_server()
|
|
104
|
-
if not wait_for_server():
|
|
105
|
-
pytest.fail("Server failed to start")
|
|
106
|
-
logger.info("Server process started")
|
|
107
|
-
|
|
108
|
-
def stop_server() -> None:
|
|
109
|
-
logger.info("Stopping server process")
|
|
110
|
-
server_process.terminate()
|
|
111
|
-
try:
|
|
112
|
-
server_process.wait(timeout=5)
|
|
113
|
-
except subprocess.TimeoutExpired:
|
|
114
|
-
logger.warning("Server process did not terminate, killing it")
|
|
115
|
-
server_process.kill()
|
|
116
|
-
server_process.wait()
|
|
117
|
-
logger.info("Server process stopped")
|
|
118
|
-
|
|
119
|
-
request.addfinalizer(stop_server)
|
|
120
|
-
yield server_process
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@pytest.mark.asyncio
|
|
124
|
-
async def test_websocket_connection(server_fixture: subprocess.Popen[str]) -> None:
|
|
125
|
-
"""Test the websocket connection and message exchange."""
|
|
126
|
-
|
|
127
|
-
async def send_message(websocket: Any, message: dict[str, Any]) -> None:
|
|
128
|
-
"""Helper function to send messages and log them."""
|
|
129
|
-
await websocket.send(json.dumps(message))
|
|
130
|
-
|
|
131
|
-
async def receive_message(websocket: Any, timeout: float = 5.0) -> dict[str, Any]:
|
|
132
|
-
"""Helper function to receive messages with timeout."""
|
|
133
|
-
try:
|
|
134
|
-
response = await asyncio.wait_for(websocket.recv(), timeout=timeout)
|
|
135
|
-
if isinstance(response, bytes):
|
|
136
|
-
return json.loads(response.decode())
|
|
137
|
-
if isinstance(response, str):
|
|
138
|
-
return json.loads(response)
|
|
139
|
-
return response
|
|
140
|
-
except asyncio.TimeoutError as exc:
|
|
141
|
-
raise TimeoutError(
|
|
142
|
-
f"No response received within {timeout} seconds"
|
|
143
|
-
) from exc
|
|
144
|
-
|
|
145
|
-
try:
|
|
146
|
-
await asyncio.sleep(2)
|
|
147
|
-
|
|
148
|
-
async with websockets.connect(
|
|
149
|
-
WS_URL, ping_timeout=10, close_timeout=10
|
|
150
|
-
) as websocket:
|
|
151
|
-
try:
|
|
152
|
-
# Wait for initial ready message
|
|
153
|
-
initial_response = None
|
|
154
|
-
for _ in range(10):
|
|
155
|
-
try:
|
|
156
|
-
initial_response = await receive_message(websocket, timeout=5.0)
|
|
157
|
-
if (
|
|
158
|
-
initial_response is not None
|
|
159
|
-
and initial_response.get("status")
|
|
160
|
-
== "Backend is ready for conversation"
|
|
161
|
-
):
|
|
162
|
-
break
|
|
163
|
-
except TimeoutError:
|
|
164
|
-
if _ == 9:
|
|
165
|
-
raise
|
|
166
|
-
continue
|
|
167
|
-
|
|
168
|
-
assert (
|
|
169
|
-
initial_response is not None
|
|
170
|
-
and initial_response.get("status")
|
|
171
|
-
== "Backend is ready for conversation"
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
# Send messages
|
|
175
|
-
setup_msg = {"setup": {"run_id": "test-run", "user_id": "test-user"}}
|
|
176
|
-
await send_message(websocket, setup_msg)
|
|
177
|
-
|
|
178
|
-
dummy_audio = bytes([0] * 1024)
|
|
179
|
-
audio_msg = {
|
|
180
|
-
"realtimeInput": {
|
|
181
|
-
"mediaChunks": [
|
|
182
|
-
{
|
|
183
|
-
"mimeType": "audio/pcm;rate=16000",
|
|
184
|
-
"data": dummy_audio.hex(),
|
|
185
|
-
}
|
|
186
|
-
]
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
await send_message(websocket, audio_msg)
|
|
190
|
-
|
|
191
|
-
text_msg = {
|
|
192
|
-
"clientContent": {
|
|
193
|
-
"turns": [
|
|
194
|
-
{"role": "user", "parts": [{"text": "Hello, how are you?"}]}
|
|
195
|
-
],
|
|
196
|
-
"turnComplete": True,
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
await send_message(websocket, text_msg)
|
|
200
|
-
|
|
201
|
-
# Collect responses with timeout
|
|
202
|
-
responses = []
|
|
203
|
-
try:
|
|
204
|
-
while True:
|
|
205
|
-
try:
|
|
206
|
-
response = await receive_message(websocket, timeout=10.0)
|
|
207
|
-
responses.append(response)
|
|
208
|
-
if (
|
|
209
|
-
len(responses) >= 3
|
|
210
|
-
): # Exit after receiving enough responses
|
|
211
|
-
break
|
|
212
|
-
except TimeoutError:
|
|
213
|
-
break
|
|
214
|
-
except asyncio.TimeoutError:
|
|
215
|
-
logger.info("Response collection timed out")
|
|
216
|
-
|
|
217
|
-
# Verify responses
|
|
218
|
-
assert len(responses) > 0, "No responses received from server"
|
|
219
|
-
assert any(
|
|
220
|
-
isinstance(r, dict) and "serverContent" in r for r in responses
|
|
221
|
-
)
|
|
222
|
-
logger.info(
|
|
223
|
-
f"Test completed successfully. Received {len(responses)} responses"
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
finally:
|
|
227
|
-
# Ensure websocket is closed properly
|
|
228
|
-
await websocket.close()
|
|
229
|
-
|
|
230
|
-
except Exception as e:
|
|
231
|
-
logger.error(f"Test failed with error: {e!s}")
|
|
232
|
-
raise
|
|
233
|
-
|
|
234
|
-
finally:
|
|
235
|
-
# Clean up any remaining tasks
|
|
236
|
-
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
|
|
237
|
-
for task in tasks:
|
|
238
|
-
task.cancel()
|
|
239
|
-
if tasks:
|
|
240
|
-
await asyncio.gather(*tasks, return_exceptions=True)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def test_collect_feedback(server_fixture: subprocess.Popen[str]) -> None:
|
|
244
|
-
"""
|
|
245
|
-
Test the feedback collection endpoint (/feedback) to ensure it properly
|
|
246
|
-
logs the received feedback.
|
|
247
|
-
"""
|
|
248
|
-
# Create sample feedback data
|
|
249
|
-
feedback_data = {
|
|
250
|
-
"score": 4,
|
|
251
|
-
"text": "Great response!",
|
|
252
|
-
"run_id": str(uuid.uuid4()),
|
|
253
|
-
"user_id": "user1",
|
|
254
|
-
"log_type": "feedback",
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
response = requests.post(
|
|
258
|
-
FEEDBACK_URL, json=feedback_data, headers=HEADERS, timeout=10
|
|
259
|
-
)
|
|
260
|
-
assert response.status_code == 200
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
import time
|
|
16
|
-
|
|
17
|
-
from locust import HttpUser, between, task
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class DummyUser(HttpUser):
|
|
21
|
-
"""Simulates a user for testing purposes."""
|
|
22
|
-
|
|
23
|
-
wait_time = between(1, 3) # Wait 1-3 seconds between tasks
|
|
24
|
-
|
|
25
|
-
@task
|
|
26
|
-
def dummy_task(self) -> None:
|
|
27
|
-
"""A dummy task that simulates work without making actual requests."""
|
|
28
|
-
# Simulate some processing time
|
|
29
|
-
time.sleep(0.1)
|
|
30
|
-
|
|
31
|
-
# Record a successful dummy request
|
|
32
|
-
self.environment.events.request.fire(
|
|
33
|
-
request_type="POST",
|
|
34
|
-
name="dummy_endpoint",
|
|
35
|
-
response_time=100,
|
|
36
|
-
response_length=1024,
|
|
37
|
-
response=None,
|
|
38
|
-
context={},
|
|
39
|
-
exception=None,
|
|
40
|
-
)
|