agent-starter-pack 0.18.2__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/{langgraph_base_react → adk_a2a_base}/.template/templateconfig.yaml +5 -12
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- agent_starter_pack/{frontends/streamlit/frontend/style/app_markdown.py → agents/adk_a2a_base/app/__init__.py} +3 -23
- 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
- agent_starter_pack/agents/{crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → adk_a2a_base/notebooks/evaluating_adk_agent.ipynb} +163 -199
- agent_starter_pack/agents/adk_a2a_base/tests/integration/test_agent.py +58 -0
- agent_starter_pack/agents/adk_base/app/__init__.py +2 -2
- agent_starter_pack/agents/adk_base/app/agent.py +3 -0
- agent_starter_pack/agents/adk_base/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/app/__init__.py +2 -2
- agent_starter_pack/agents/agentic_rag/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/.template/templateconfig.yaml +12 -9
- 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_react → langgraph_base}/app/agent.py +4 -4
- agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/tests/integration/test_agent.py +1 -1
- agent_starter_pack/base_template/.gitignore +4 -2
- agent_starter_pack/base_template/Makefile +110 -16
- agent_starter_pack/base_template/README.md +97 -12
- agent_starter_pack/base_template/deployment/terraform/dev/apis.tf +4 -6
- agent_starter_pack/base_template/deployment/terraform/dev/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/dev/variables.tf +5 -3
- 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 +16 -9
- agent_starter_pack/base_template/deployment/terraform/locals.tf +7 -7
- agent_starter_pack/base_template/deployment/terraform/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- agent_starter_pack/base_template/deployment/terraform/storage.tf +0 -9
- agent_starter_pack/base_template/deployment/terraform/variables.tf +15 -19
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +20 -22
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- agent_starter_pack/base_template/pyproject.toml +5 -17
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +19 -4
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +36 -11
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +24 -5
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +44 -9
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/typing.py +4 -6
- agent_starter_pack/{agents/crewai_coding_crew/app/crew/config/agents.yaml → base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py } +9 -23
- 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 +40 -4
- agent_starter_pack/cli/commands/enhance.py +1 -1
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/main.py +2 -0
- agent_starter_pack/cli/utils/cicd.py +20 -4
- agent_starter_pack/cli/utils/template.py +257 -25
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +113 -16
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +2 -2
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +178 -9
- 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 +193 -307
- 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}}/{utils → app_utils}/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +13 -14
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +4 -1
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +85 -86
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +139 -107
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +228 -12
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +4 -4
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +92 -12
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/{server.py → fast_api_app.py} +194 -121
- agent_starter_pack/frontends/adk_live_react/frontend/package-lock.json +18 -18
- agent_starter_pack/frontends/adk_live_react/frontend/src/multimodal-live-types.ts +5 -3
- agent_starter_pack/resources/docs/adk-cheatsheet.md +198 -41
- 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 +1443 -709
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +1443 -709
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +1568 -749
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +1123 -929
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-agent_engine.lock → uv-langgraph_base-agent_engine.lock} +1714 -1689
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-cloud_run.lock → uv-langgraph_base-cloud_run.lock} +1285 -2374
- agent_starter_pack/utils/watch_and_rebuild.py +1 -1
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/METADATA +3 -6
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/RECORD +89 -93
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +4 -5
- agent_starter_pack/agents/crewai_coding_crew/README.md +0 -34
- agent_starter_pack/agents/crewai_coding_crew/app/agent.py +0 -47
- agent_starter_pack/agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agent_starter_pack/agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agent_starter_pack/agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agent_starter_pack/agents/langgraph_base_react/README.md +0 -9
- agent_starter_pack/agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -1574
- agent_starter_pack/base_template/deployment/terraform/dev/log_sinks.tf +0 -69
- agent_starter_pack/base_template/deployment/terraform/log_sinks.tf +0 -79
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +0 -155
- agent_starter_pack/cli/utils/register_gemini_enterprise.py +0 -406
- agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +0 -82
- agent_starter_pack/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +0 -99
- agent_starter_pack/frontends/streamlit/frontend/side_bar.py +0 -214
- agent_starter_pack/frontends/streamlit/frontend/streamlit_app.py +0 -265
- agent_starter_pack/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- agent_starter_pack/frontends/streamlit/frontend/utils/local_chat_history.py +0 -127
- agent_starter_pack/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- agent_starter_pack/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- agent_starter_pack/frontends/streamlit/frontend/utils/stream_handler.py +0 -310
- agent_starter_pack/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6650
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -7825
- agent_starter_pack-0.18.2.dist-info/entry_points.txt +0 -3
- /agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +0 -0
- /agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/gcs.py +0 -0
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,310 +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="unreachable"
|
|
16
|
-
import importlib
|
|
17
|
-
import json
|
|
18
|
-
import uuid
|
|
19
|
-
from collections.abc import Generator
|
|
20
|
-
from typing import Any
|
|
21
|
-
from urllib.parse import urljoin
|
|
22
|
-
|
|
23
|
-
import google.auth
|
|
24
|
-
import google.auth.transport.requests
|
|
25
|
-
import google.oauth2.id_token
|
|
26
|
-
import requests
|
|
27
|
-
import streamlit as st
|
|
28
|
-
import vertexai
|
|
29
|
-
from google.auth.exceptions import DefaultCredentialsError
|
|
30
|
-
from langchain_core.messages import AIMessage, ToolMessage
|
|
31
|
-
from vertexai import agent_engines
|
|
32
|
-
|
|
33
|
-
from frontend.utils.multimodal_utils import format_content
|
|
34
|
-
|
|
35
|
-
st.cache_resource.clear()
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@st.cache_resource
|
|
39
|
-
def get_remote_agent(remote_agent_engine_id: str) -> Any:
|
|
40
|
-
"""Get cached remote agent instance."""
|
|
41
|
-
# Extract location and engine ID from the full resource ID.
|
|
42
|
-
parts = remote_agent_engine_id.split("/")
|
|
43
|
-
project_id = parts[1]
|
|
44
|
-
location = parts[3]
|
|
45
|
-
vertexai.init(project=project_id, location=location)
|
|
46
|
-
return agent_engines.AgentEngine(remote_agent_engine_id)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@st.cache_resource
|
|
50
|
-
def get_remote_url_config(url: str, authenticate_request: bool) -> dict[str, Any]:
|
|
51
|
-
"""Get cached remote URL agent configuration."""
|
|
52
|
-
stream_url = urljoin(url, "stream_messages")
|
|
53
|
-
creds, _ = google.auth.default()
|
|
54
|
-
id_token = None
|
|
55
|
-
if authenticate_request:
|
|
56
|
-
auth_req = google.auth.transport.requests.Request()
|
|
57
|
-
try:
|
|
58
|
-
id_token = google.oauth2.id_token.fetch_id_token(auth_req, stream_url)
|
|
59
|
-
except DefaultCredentialsError:
|
|
60
|
-
creds.refresh(auth_req)
|
|
61
|
-
id_token = creds.id_token
|
|
62
|
-
return {
|
|
63
|
-
"url": stream_url,
|
|
64
|
-
"authenticate_request": authenticate_request,
|
|
65
|
-
"creds": creds,
|
|
66
|
-
"id_token": id_token,
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@st.cache_resource()
|
|
71
|
-
def get_local_agent(agent_callable_path: str) -> Any:
|
|
72
|
-
"""Get cached local agent instance."""
|
|
73
|
-
module_path, class_name = agent_callable_path.rsplit(".", 1)
|
|
74
|
-
module = importlib.import_module(module_path)
|
|
75
|
-
agent = getattr(module, class_name)()
|
|
76
|
-
agent.set_up()
|
|
77
|
-
return agent
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class Client:
|
|
81
|
-
"""A client for streaming events from a server."""
|
|
82
|
-
|
|
83
|
-
def __init__(
|
|
84
|
-
self,
|
|
85
|
-
agent_callable_path: str | None = None,
|
|
86
|
-
remote_agent_engine_id: str | None = None,
|
|
87
|
-
url: str | None = None,
|
|
88
|
-
authenticate_request: bool = False,
|
|
89
|
-
) -> None:
|
|
90
|
-
"""Initialize the Client with appropriate configuration.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
agent_callable_path: Path to local agent class
|
|
94
|
-
remote_agent_engine_id: ID of remote Agent engine
|
|
95
|
-
url: URL for remote service
|
|
96
|
-
authenticate_request: Whether to authenticate requests to remote URL
|
|
97
|
-
"""
|
|
98
|
-
if url:
|
|
99
|
-
remote_config = get_remote_url_config(url, authenticate_request)
|
|
100
|
-
self.url = remote_config["url"]
|
|
101
|
-
self.authenticate_request = remote_config["authenticate_request"]
|
|
102
|
-
self.creds = remote_config["creds"]
|
|
103
|
-
self.id_token = remote_config["id_token"]
|
|
104
|
-
self.agent = None
|
|
105
|
-
elif remote_agent_engine_id:
|
|
106
|
-
self.agent = get_remote_agent(remote_agent_engine_id)
|
|
107
|
-
self.url = None
|
|
108
|
-
else:
|
|
109
|
-
self.url = None
|
|
110
|
-
if agent_callable_path is None:
|
|
111
|
-
raise ValueError("agent_callable_path cannot be None")
|
|
112
|
-
self.agent = get_local_agent(agent_callable_path)
|
|
113
|
-
|
|
114
|
-
def log_feedback(self, feedback_dict: dict[str, Any], run_id: str) -> None:
|
|
115
|
-
"""Log user feedback for a specific run."""
|
|
116
|
-
score = feedback_dict["score"]
|
|
117
|
-
if score == "😞":
|
|
118
|
-
score = 0.0
|
|
119
|
-
elif score == "🙁":
|
|
120
|
-
score = 0.25
|
|
121
|
-
elif score == "😐":
|
|
122
|
-
score = 0.5
|
|
123
|
-
elif score == "🙂":
|
|
124
|
-
score = 0.75
|
|
125
|
-
elif score == "😀":
|
|
126
|
-
score = 1.0
|
|
127
|
-
feedback_dict["score"] = score
|
|
128
|
-
feedback_dict["run_id"] = run_id
|
|
129
|
-
feedback_dict["log_type"] = "feedback"
|
|
130
|
-
feedback_dict.pop("type")
|
|
131
|
-
url = urljoin(self.url, "feedback")
|
|
132
|
-
headers = {
|
|
133
|
-
"Content-Type": "application/json",
|
|
134
|
-
}
|
|
135
|
-
if self.url:
|
|
136
|
-
url = urljoin(self.url, "feedback")
|
|
137
|
-
headers = {
|
|
138
|
-
"Content-Type": "application/json",
|
|
139
|
-
}
|
|
140
|
-
if self.authenticate_request:
|
|
141
|
-
headers["Authorization"] = f"Bearer {self.id_token}"
|
|
142
|
-
requests.post(
|
|
143
|
-
url, data=json.dumps(feedback_dict), headers=headers, timeout=10
|
|
144
|
-
)
|
|
145
|
-
elif self.agent is not None:
|
|
146
|
-
self.agent.register_feedback(feedback=feedback_dict)
|
|
147
|
-
else:
|
|
148
|
-
raise ValueError("No agent or URL configured for feedback logging")
|
|
149
|
-
|
|
150
|
-
def stream_messages(
|
|
151
|
-
self, data: dict[str, Any]
|
|
152
|
-
) -> Generator[dict[str, Any], None, None]:
|
|
153
|
-
"""Stream events from the server, yielding parsed event data."""
|
|
154
|
-
if self.url:
|
|
155
|
-
headers = {
|
|
156
|
-
"Content-Type": "application/json",
|
|
157
|
-
"Accept": "text/event-stream",
|
|
158
|
-
}
|
|
159
|
-
if self.authenticate_request:
|
|
160
|
-
headers["Authorization"] = f"Bearer {self.id_token}"
|
|
161
|
-
with requests.post(
|
|
162
|
-
self.url, json=data, headers=headers, stream=True, timeout=60
|
|
163
|
-
) as response:
|
|
164
|
-
for line in response.iter_lines():
|
|
165
|
-
if line:
|
|
166
|
-
try:
|
|
167
|
-
event = json.loads(line.decode("utf-8"))
|
|
168
|
-
yield event
|
|
169
|
-
except json.JSONDecodeError:
|
|
170
|
-
print(f"Failed to parse event: {line.decode('utf-8')}")
|
|
171
|
-
elif self.agent is not None:
|
|
172
|
-
yield from self.agent.stream_query(**data)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
class StreamHandler:
|
|
176
|
-
"""Handles streaming updates to a Streamlit interface."""
|
|
177
|
-
|
|
178
|
-
def __init__(self, st: Any, initial_text: str = "") -> None:
|
|
179
|
-
"""Initialize the StreamHandler with Streamlit context and initial text."""
|
|
180
|
-
self.st = st
|
|
181
|
-
self.tool_expander = st.expander("Tool Calls:", expanded=False)
|
|
182
|
-
self.container = st.empty()
|
|
183
|
-
self.text = initial_text
|
|
184
|
-
self.tools_logs = initial_text
|
|
185
|
-
|
|
186
|
-
def new_token(self, token: str) -> None:
|
|
187
|
-
"""Add a new token to the main text display."""
|
|
188
|
-
self.text += token
|
|
189
|
-
self.container.markdown(format_content(self.text), unsafe_allow_html=True)
|
|
190
|
-
|
|
191
|
-
def new_status(self, status_update: str) -> None:
|
|
192
|
-
"""Add a new status update to the tool calls expander."""
|
|
193
|
-
self.tools_logs += status_update
|
|
194
|
-
self.tool_expander.markdown(status_update)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
class EventProcessor:
|
|
198
|
-
"""Processes events from the stream and updates the UI accordingly."""
|
|
199
|
-
|
|
200
|
-
def __init__(self, st: Any, client: Client, stream_handler: StreamHandler) -> None:
|
|
201
|
-
"""Initialize the EventProcessor with Streamlit context, client, and stream handler."""
|
|
202
|
-
self.st = st
|
|
203
|
-
self.client = client
|
|
204
|
-
self.stream_handler = stream_handler
|
|
205
|
-
self.final_content = ""
|
|
206
|
-
self.tool_calls: list[dict[str, Any]] = []
|
|
207
|
-
self.current_run_id: str | None = None
|
|
208
|
-
self.additional_kwargs: dict[str, Any] = {}
|
|
209
|
-
|
|
210
|
-
def process_events(self) -> None:
|
|
211
|
-
"""Process events from the stream, handling each event type appropriately."""
|
|
212
|
-
messages = self.st.session_state.user_chats[
|
|
213
|
-
self.st.session_state["session_id"]
|
|
214
|
-
]["messages"]
|
|
215
|
-
self.current_run_id = str(uuid.uuid4())
|
|
216
|
-
# Set run_id in session state at start of processing
|
|
217
|
-
self.st.session_state["run_id"] = self.current_run_id
|
|
218
|
-
stream = self.client.stream_messages(
|
|
219
|
-
data={
|
|
220
|
-
"input": {"messages": messages},
|
|
221
|
-
"config": {
|
|
222
|
-
"run_id": self.current_run_id,
|
|
223
|
-
"metadata": {
|
|
224
|
-
"user_id": self.st.session_state["user_id"],
|
|
225
|
-
"session_id": self.st.session_state["session_id"],
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
}
|
|
229
|
-
)
|
|
230
|
-
# Each event is a tuple message, metadata. https://langchain-ai.github.io/langgraph/how-tos/streaming/#messages
|
|
231
|
-
for message, _ in stream:
|
|
232
|
-
if isinstance(message, dict):
|
|
233
|
-
if message.get("type") == "constructor":
|
|
234
|
-
message = message["kwargs"]
|
|
235
|
-
|
|
236
|
-
# Handle tool calls
|
|
237
|
-
if message.get("tool_calls"):
|
|
238
|
-
tool_calls = message["tool_calls"]
|
|
239
|
-
ai_message = AIMessage(content="", tool_calls=tool_calls)
|
|
240
|
-
self.tool_calls.append(ai_message.model_dump())
|
|
241
|
-
for tool_call in tool_calls:
|
|
242
|
-
msg = f"\n\nCalling tool: `{tool_call['name']}` with args: `{tool_call['args']}`"
|
|
243
|
-
self.stream_handler.new_status(msg)
|
|
244
|
-
|
|
245
|
-
# Handle tool responses
|
|
246
|
-
elif message.get("tool_call_id"):
|
|
247
|
-
content = message["content"]
|
|
248
|
-
tool_call_id = message["tool_call_id"]
|
|
249
|
-
tool_message = ToolMessage(
|
|
250
|
-
content=content, type="tool", tool_call_id=tool_call_id
|
|
251
|
-
).model_dump()
|
|
252
|
-
self.tool_calls.append(tool_message)
|
|
253
|
-
msg = f"\n\nTool response: `{content}`"
|
|
254
|
-
self.stream_handler.new_status(msg)
|
|
255
|
-
|
|
256
|
-
# Handle incremental AI response chunks
|
|
257
|
-
# These are partial content pieces that need to be accumulated
|
|
258
|
-
elif (
|
|
259
|
-
message.get("content")
|
|
260
|
-
and message.get("type") == "AIMessageChunk"
|
|
261
|
-
):
|
|
262
|
-
self.final_content += message.get("content")
|
|
263
|
-
self.stream_handler.new_token(message.get("content"))
|
|
264
|
-
|
|
265
|
-
# Handle complete AI responses
|
|
266
|
-
# This is used when receiving a full message rather than chunks
|
|
267
|
-
elif message.get("content") and message.get("type") == "ai":
|
|
268
|
-
self.final_content = message.get("content")
|
|
269
|
-
|
|
270
|
-
# Handle end of stream
|
|
271
|
-
if self.final_content:
|
|
272
|
-
final_message = AIMessage(
|
|
273
|
-
content=self.final_content,
|
|
274
|
-
id=self.current_run_id,
|
|
275
|
-
additional_kwargs=self.additional_kwargs,
|
|
276
|
-
).model_dump()
|
|
277
|
-
session = self.st.session_state["session_id"]
|
|
278
|
-
self.st.session_state.user_chats[session]["messages"] = (
|
|
279
|
-
self.st.session_state.user_chats[session]["messages"] + self.tool_calls
|
|
280
|
-
)
|
|
281
|
-
self.st.session_state.user_chats[session]["messages"].append(final_message)
|
|
282
|
-
self.st.session_state.run_id = self.current_run_id
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def get_chain_response(st: Any, client: Client, stream_handler: StreamHandler) -> None:
|
|
286
|
-
"""Process the chain response update the Streamlit UI.
|
|
287
|
-
|
|
288
|
-
This function initiates the event processing for a chain of operations,
|
|
289
|
-
involving an AI model's response generation and potential tool calls.
|
|
290
|
-
It creates an EventProcessor instance and starts the event processing loop.
|
|
291
|
-
|
|
292
|
-
Args:
|
|
293
|
-
st (Any): The Streamlit app instance, used for accessing session state
|
|
294
|
-
and updating the UI.
|
|
295
|
-
client (Client): An instance of the Client class used to stream events
|
|
296
|
-
from the server.
|
|
297
|
-
stream_handler (StreamHandler): An instance of the StreamHandler class
|
|
298
|
-
used to update the Streamlit UI with
|
|
299
|
-
streaming content.
|
|
300
|
-
|
|
301
|
-
Returns:
|
|
302
|
-
None
|
|
303
|
-
|
|
304
|
-
Side effects:
|
|
305
|
-
- Updates the Streamlit UI with streaming tokens and tool call information.
|
|
306
|
-
- Modifies the session state to include the final AI message and run ID.
|
|
307
|
-
- Handles various events like chain starts/ends, tool calls, and model outputs.
|
|
308
|
-
"""
|
|
309
|
-
processor = EventProcessor(st, client, stream_handler)
|
|
310
|
-
processor.process_events()
|
|
@@ -1,94 +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="assignment"
|
|
16
|
-
import os
|
|
17
|
-
from typing import Any
|
|
18
|
-
|
|
19
|
-
import google.auth
|
|
20
|
-
from langchain_core.messages import AIMessage
|
|
21
|
-
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
22
|
-
from langchain_google_vertexai import ChatVertexAI
|
|
23
|
-
|
|
24
|
-
title_template = ChatPromptTemplate.from_messages(
|
|
25
|
-
[
|
|
26
|
-
(
|
|
27
|
-
"system",
|
|
28
|
-
"""Given a list of messages between a human and AI, come up with a short and relevant title for the conversation. Use up to 10 words. The title needs to be concise.
|
|
29
|
-
Examples:
|
|
30
|
-
**Input:**
|
|
31
|
-
```
|
|
32
|
-
Human: hi, what is the best italian dish?
|
|
33
|
-
AI: That's a tough one! Italy has so many amazing dishes, it's hard to pick just one "best." To help me give you a great suggestion, tell me a little more about what you're looking for.
|
|
34
|
-
```
|
|
35
|
-
**Output:** Best italian dish
|
|
36
|
-
|
|
37
|
-
**Input:**
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
Human: How to fix a broken laptop screen?
|
|
41
|
-
AI: Fixing a broken laptop screen can be tricky and often requires professional help. However, there are a few things you can try at home before resorting to a repair shop.
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**Output:** Fixing a broken laptop screen
|
|
45
|
-
|
|
46
|
-
**Input:**
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
Human: Can you write me a poem about the beach?
|
|
50
|
-
AI: As the sun dips down below the horizon
|
|
51
|
-
And the waves gently kiss the shore,
|
|
52
|
-
I sit here and watch the ocean
|
|
53
|
-
And feel its power evermore.
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
**Output:** Poem about the beach
|
|
57
|
-
|
|
58
|
-
**Input:**
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
Human: What's the best way to learn to code?
|
|
62
|
-
AI: There are many ways to learn to code, and the best method for you will depend on your learning style and goals.
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
**Output:** How to learn to code
|
|
66
|
-
|
|
67
|
-
If there's not enough context in the conversation to create a meaningful title, create a generic title like "New Conversation", or "A simple greeting".
|
|
68
|
-
|
|
69
|
-
""",
|
|
70
|
-
),
|
|
71
|
-
MessagesPlaceholder(variable_name="messages"),
|
|
72
|
-
]
|
|
73
|
-
)
|
|
74
|
-
try:
|
|
75
|
-
# Initialize Vertex AI with default project credentials
|
|
76
|
-
_, project_id = google.auth.default()
|
|
77
|
-
|
|
78
|
-
llm = ChatVertexAI(
|
|
79
|
-
model_name="gemini-2.0-flash-001",
|
|
80
|
-
temperature=0,
|
|
81
|
-
project=project_id,
|
|
82
|
-
location=os.getenv("LOCATION", "us-central1"),
|
|
83
|
-
)
|
|
84
|
-
chain_title = title_template | llm
|
|
85
|
-
|
|
86
|
-
except Exception:
|
|
87
|
-
# Fallback to a simple title generator when Vertex AI is unavailable
|
|
88
|
-
print("WARNING: Failed to initialize Vertex AI. Using dummy LLM instead.")
|
|
89
|
-
|
|
90
|
-
class DummyChain:
|
|
91
|
-
def invoke(*args: Any, **kwargs: Any) -> AIMessage:
|
|
92
|
-
return AIMessage(content="conversation")
|
|
93
|
-
|
|
94
|
-
chain_title = DummyChain()
|