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.

Files changed (195) hide show
  1. {agents → agent_starter_pack/agents}/adk_base/.template/templateconfig.yaml +1 -1
  2. {agents/live_api → agent_starter_pack/agents/adk_live}/.template/templateconfig.yaml +5 -7
  3. agent_starter_pack/agents/adk_live/README.md +32 -0
  4. agent_starter_pack/agents/adk_live/app/agent.py +48 -0
  5. agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
  6. {agents → agent_starter_pack/agents}/agentic_rag/.template/templateconfig.yaml +1 -1
  7. agent_starter_pack/agents/crewai_coding_crew/app/agent.py +47 -0
  8. agent_starter_pack/agents/langgraph_base_react/app/agent.py +34 -0
  9. {src → agent_starter_pack}/base_template/GEMINI.md +1 -1
  10. {src → agent_starter_pack}/base_template/Makefile +130 -61
  11. {src → agent_starter_pack}/base_template/README.md +6 -6
  12. {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +1 -1
  13. {src → agent_starter_pack}/base_template/deployment/terraform/dev/log_sinks.tf +31 -25
  14. {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +1 -1
  15. {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +1 -1
  16. {src → agent_starter_pack}/base_template/deployment/terraform/github.tf +14 -0
  17. {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +1 -1
  18. {src → agent_starter_pack}/base_template/deployment/terraform/log_sinks.tf +37 -28
  19. {src → agent_starter_pack}/base_template/deployment/terraform/providers.tf +1 -1
  20. {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +1 -1
  21. {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
  22. {src → agent_starter_pack}/base_template/pyproject.toml +22 -21
  23. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +5 -5
  24. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +3 -3
  25. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +74 -11
  26. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +6 -6
  27. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +4 -4
  28. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +95 -13
  29. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +1 -1
  30. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/typing.py +4 -4
  31. {src → agent_starter_pack}/cli/commands/setup_cicd.py +1 -1
  32. {src → agent_starter_pack}/cli/main.py +2 -2
  33. {src → agent_starter_pack}/cli/utils/gcp.py +1 -1
  34. {src → agent_starter_pack}/cli/utils/remote_template.py +12 -9
  35. {src → agent_starter_pack}/cli/utils/template.py +19 -15
  36. agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +82 -0
  37. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +387 -0
  38. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
  39. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +255 -0
  40. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +40 -14
  41. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +13 -4
  42. 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
  43. {src → agent_starter_pack}/deployment_targets/cloud_run/Dockerfile +3 -3
  44. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -4
  45. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/service.tf +7 -7
  46. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +207 -5
  47. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/README.md +82 -0
  48. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +249 -0
  49. {src → agent_starter_pack}/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/server.py +190 -146
  50. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +39 -1007
  51. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
  52. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.tsx +1 -1
  53. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
  54. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
  55. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/SidePanel.tsx +11 -5
  56. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/side-panel.scss +146 -115
  57. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
  58. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
  59. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
  60. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +38 -2
  61. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
  62. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
  63. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +204 -23
  64. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
  65. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/local_chat_history.py +2 -0
  66. {src → agent_starter_pack}/resources/docs/adk-cheatsheet.md +5 -5
  67. agent_starter_pack/resources/idx/.idx/dev.nix +64 -0
  68. agent_starter_pack/resources/idx/idx-template.json +6 -0
  69. {src → agent_starter_pack}/resources/idx/idx-template.nix +2 -3
  70. {src → agent_starter_pack}/resources/locks/uv-adk_base-agent_engine.lock +1079 -954
  71. {src → agent_starter_pack}/resources/locks/uv-adk_base-cloud_run.lock +1441 -1309
  72. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4229 -0
  73. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +4822 -0
  74. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-agent_engine.lock +1107 -997
  75. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-cloud_run.lock +1485 -1368
  76. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-agent_engine.lock +1294 -1297
  77. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-cloud_run.lock +2028 -1807
  78. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-agent_engine.lock +1176 -1197
  79. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-cloud_run.lock +1947 -1679
  80. {src → agent_starter_pack}/utils/generate_locks.py +12 -7
  81. {src → agent_starter_pack}/utils/lock_utils.py +2 -2
  82. {src → agent_starter_pack}/utils/watch_and_rebuild.py +1 -1
  83. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/METADATA +17 -18
  84. agent_starter_pack-0.17.0.dist-info/RECORD +179 -0
  85. agent_starter_pack-0.17.0.dist-info/entry_points.txt +2 -0
  86. llm.txt +1 -1
  87. agent_starter_pack-0.15.7.dist-info/RECORD +0 -176
  88. agent_starter_pack-0.15.7.dist-info/entry_points.txt +0 -2
  89. agents/crewai_coding_crew/app/agent.py +0 -86
  90. agents/langgraph_base_react/app/agent.py +0 -73
  91. agents/live_api/README.md +0 -37
  92. agents/live_api/app/agent.py +0 -72
  93. agents/live_api/tests/integration/test_server_e2e.py +0 -260
  94. agents/live_api/tests/load_test/load_test.py +0 -40
  95. agents/live_api/tests/unit/test_server.py +0 -144
  96. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -186
  97. src/deployment_targets/agent_engine/tests/load_test/README.md +0 -37
  98. src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -126
  99. src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -122
  100. src/resources/idx/.idx/dev.nix +0 -50
  101. src/resources/idx/idx-template.json +0 -21
  102. src/resources/locks/uv-live_api-cloud_run.lock +0 -6118
  103. {agents → agent_starter_pack/agents}/README.md +0 -0
  104. {agents → agent_starter_pack/agents}/adk_base/README.md +0 -0
  105. {agents → agent_starter_pack/agents}/adk_base/app/__init__.py +0 -0
  106. {agents → agent_starter_pack/agents}/adk_base/app/agent.py +0 -0
  107. {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +0 -0
  108. {agents → agent_starter_pack/agents}/adk_base/notebooks/evaluating_adk_agent.ipynb +0 -0
  109. {agents → agent_starter_pack/agents}/adk_base/tests/integration/test_agent.py +0 -0
  110. {agents → agent_starter_pack/agents}/agentic_rag/README.md +0 -0
  111. {agents → agent_starter_pack/agents}/agentic_rag/app/__init__.py +0 -0
  112. {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +0 -0
  113. {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
  114. {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
  115. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +0 -0
  116. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/evaluating_adk_agent.ipynb +0 -0
  117. {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +0 -0
  118. {agents → agent_starter_pack/agents}/crewai_coding_crew/.template/templateconfig.yaml +0 -0
  119. {agents → agent_starter_pack/agents}/crewai_coding_crew/README.md +0 -0
  120. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/agents.yaml +0 -0
  121. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/tasks.yaml +0 -0
  122. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/crew.py +0 -0
  123. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +0 -0
  124. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  125. {agents → agent_starter_pack/agents}/crewai_coding_crew/tests/integration/test_agent.py +0 -0
  126. {agents → agent_starter_pack/agents}/langgraph_base_react/.template/templateconfig.yaml +0 -0
  127. {agents → agent_starter_pack/agents}/langgraph_base_react/README.md +0 -0
  128. {agents → agent_starter_pack/agents}/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  129. {agents → agent_starter_pack/agents}/langgraph_base_react/tests/integration/test_agent.py +0 -0
  130. {src → agent_starter_pack}/base_template/.gitignore +0 -0
  131. {src → agent_starter_pack}/base_template/deployment/README.md +0 -0
  132. {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +0 -0
  133. {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +0 -0
  134. {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +0 -0
  135. {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
  136. {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +0 -0
  137. {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +0 -0
  138. {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +0 -0
  139. {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +0 -0
  140. {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +0 -0
  141. {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
  142. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/gcs.py +0 -0
  143. {src → agent_starter_pack}/cli/commands/create.py +0 -0
  144. {src → agent_starter_pack}/cli/commands/enhance.py +0 -0
  145. {src → agent_starter_pack}/cli/commands/list.py +0 -0
  146. {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
  147. {src → agent_starter_pack}/cli/utils/cicd.py +0 -0
  148. {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
  149. {src → agent_starter_pack}/cli/utils/logging.py +0 -0
  150. {src → agent_starter_pack}/cli/utils/version.py +0 -0
  151. {src → agent_starter_pack}/data_ingestion/README.md +0 -0
  152. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
  153. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +0 -0
  154. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
  155. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +0 -0
  156. {src → agent_starter_pack}/data_ingestion/pyproject.toml +0 -0
  157. {src → agent_starter_pack}/data_ingestion/uv.lock +0 -0
  158. {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
  159. {src → agent_starter_pack}/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -0
  160. {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  161. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  162. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
  163. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
  164. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
  165. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
  166. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
  167. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
  168. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
  169. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
  170. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
  171. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
  172. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
  173. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
  174. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
  175. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
  176. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
  177. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
  178. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
  179. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
  180. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
  181. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
  182. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
  183. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
  184. {src → agent_starter_pack}/frontends/streamlit/frontend/side_bar.py +0 -0
  185. {src → agent_starter_pack}/frontends/streamlit/frontend/streamlit_app.py +0 -0
  186. {src → agent_starter_pack}/frontends/streamlit/frontend/style/app_markdown.py +0 -0
  187. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/chat_utils.py +0 -0
  188. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/message_editing.py +0 -0
  189. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -0
  190. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/stream_handler.py +0 -0
  191. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/title_summary.py +0 -0
  192. {src → agent_starter_pack}/resources/containers/data_processing/Dockerfile +0 -0
  193. {src → agent_starter_pack}/resources/containers/e2e-tests/Dockerfile +0 -0
  194. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/WHEEL +0 -0
  195. {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
- ![live_api_diagram](https://storage.googleapis.com/github-repo/generative-ai/sample-apps/e2e-gen-ai-app-starter-pack/live_api_diagram.png)
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
- ![live api demo](https://storage.googleapis.com/github-repo/generative-ai/sample-apps/e2e-gen-ai-app-starter-pack/live_api_pattern_demo.gif)
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.
@@ -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
- )