agent-starter-pack 0.5.2__py3-none-any.whl → 0.6.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.
Files changed (75) hide show
  1. {agent_starter_pack-0.5.2.dist-info → agent_starter_pack-0.6.0.dist-info}/METADATA +2 -2
  2. {agent_starter_pack-0.5.2.dist-info → agent_starter_pack-0.6.0.dist-info}/RECORD +66 -40
  3. agents/adk_base/notebooks/adk_app_testing.ipynb +1 -1
  4. agents/adk_base/template/.templateconfig.yaml +1 -1
  5. agents/adk_gemini_fullstack/README.md +148 -0
  6. agents/adk_gemini_fullstack/app/agent.py +349 -0
  7. agents/adk_gemini_fullstack/app/config.py +11 -0
  8. agents/adk_gemini_fullstack/notebooks/adk_app_testing.ipynb +353 -0
  9. agents/adk_gemini_fullstack/notebooks/evaluating_adk_agent.ipynb +1528 -0
  10. agents/adk_gemini_fullstack/template/.templateconfig.yaml +37 -0
  11. agents/adk_gemini_fullstack/tests/integration/test_agent.py +58 -0
  12. agents/agentic_rag/notebooks/adk_app_testing.ipynb +1 -1
  13. agents/agentic_rag/template/.templateconfig.yaml +1 -1
  14. agents/crewai_coding_crew/template/.templateconfig.yaml +1 -1
  15. agents/langgraph_base_react/template/.templateconfig.yaml +1 -1
  16. src/base_template/Makefile +21 -2
  17. src/base_template/README.md +8 -3
  18. src/base_template/pyproject.toml +1 -4
  19. src/cli/commands/create.py +17 -10
  20. src/cli/utils/template.py +13 -10
  21. src/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
  22. src/deployment_targets/agent_engine/app/agent_engine_app.py +17 -5
  23. src/deployment_targets/cloud_run/app/server.py +17 -2
  24. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +1 -1
  25. src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
  26. src/frontends/adk_gemini_fullstack/frontend/components.json +21 -0
  27. src/frontends/adk_gemini_fullstack/frontend/eslint.config.js +28 -0
  28. src/frontends/adk_gemini_fullstack/frontend/index.html +12 -0
  29. src/frontends/adk_gemini_fullstack/frontend/package-lock.json +5829 -0
  30. src/frontends/adk_gemini_fullstack/frontend/package.json +46 -0
  31. src/frontends/adk_gemini_fullstack/frontend/public/vite.svg +1 -0
  32. src/frontends/adk_gemini_fullstack/frontend/src/App.tsx +565 -0
  33. src/frontends/adk_gemini_fullstack/frontend/src/components/ActivityTimeline.tsx +244 -0
  34. src/frontends/adk_gemini_fullstack/frontend/src/components/ChatMessagesView.tsx +419 -0
  35. src/frontends/adk_gemini_fullstack/frontend/src/components/InputForm.tsx +60 -0
  36. src/frontends/adk_gemini_fullstack/frontend/src/components/WelcomeScreen.tsx +56 -0
  37. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/badge.tsx +46 -0
  38. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/button.tsx +59 -0
  39. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/card.tsx +92 -0
  40. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/input.tsx +21 -0
  41. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/scroll-area.tsx +56 -0
  42. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/select.tsx +183 -0
  43. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/tabs.tsx +64 -0
  44. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/textarea.tsx +18 -0
  45. src/frontends/adk_gemini_fullstack/frontend/src/global.css +154 -0
  46. src/frontends/adk_gemini_fullstack/frontend/src/main.tsx +13 -0
  47. src/frontends/adk_gemini_fullstack/frontend/src/utils.ts +7 -0
  48. src/frontends/adk_gemini_fullstack/frontend/src/vite-env.d.ts +1 -0
  49. src/frontends/adk_gemini_fullstack/frontend/tsconfig.json +28 -0
  50. src/frontends/adk_gemini_fullstack/frontend/tsconfig.node.json +24 -0
  51. src/frontends/adk_gemini_fullstack/frontend/vite.config.ts +26 -0
  52. src/resources/locks/uv-adk_base-agent_engine.lock +303 -251
  53. src/resources/locks/uv-adk_base-cloud_run.lock +367 -306
  54. src/resources/locks/uv-adk_gemini_fullstack-agent_engine.lock +3217 -0
  55. src/resources/locks/uv-adk_gemini_fullstack-cloud_run.lock +3513 -0
  56. src/resources/locks/uv-agentic_rag-agent_engine.lock +621 -565
  57. src/resources/locks/uv-agentic_rag-cloud_run.lock +854 -782
  58. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +860 -723
  59. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +1139 -960
  60. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +718 -587
  61. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +977 -815
  62. src/resources/locks/uv-live_api-cloud_run.lock +784 -709
  63. src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
  64. src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
  65. src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
  66. src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
  67. src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
  68. src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
  69. src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
  70. src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
  71. src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
  72. {agent_starter_pack-0.5.2.dist-info → agent_starter_pack-0.6.0.dist-info}/WHEEL +0 -0
  73. {agent_starter_pack-0.5.2.dist-info → agent_starter_pack-0.6.0.dist-info}/entry_points.txt +0 -0
  74. {agent_starter_pack-0.5.2.dist-info → agent_starter_pack-0.6.0.dist-info}/licenses/LICENSE +0 -0
  75. /src/{deployment_targets/agent_engine → base_template}/app/utils/gcs.py +0 -0
@@ -1,110 +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
- from datetime import datetime
17
-
18
- import yaml
19
-
20
- from frontend.utils.title_summary import DummySummarizer, TitleGenerator
21
-
22
-
23
- class LocalChatMessageHistory:
24
- """Manages local storage and retrieval of chat message history."""
25
-
26
- def __init__(
27
- self,
28
- user_id: str,
29
- session_id: str = "default",
30
- base_dir: str = ".streamlit_chats",
31
- ) -> None:
32
- self.user_id = user_id
33
- self.session_id = session_id
34
- self.base_dir = base_dir
35
- self.user_dir = os.path.join(self.base_dir, self.user_id)
36
- self.session_file = os.path.join(self.user_dir, f"{session_id}.yaml")
37
- try:
38
- self.title_generator = TitleGenerator()
39
- except: # noqa: E722
40
- self.title_generator: TitleGenerator = DummySummarizer() # type: ignore
41
- os.makedirs(self.user_dir, exist_ok=True)
42
-
43
- def get_session(self, session_id: str) -> None:
44
- """Updates the session ID and file path for the current session."""
45
- self.session_id = session_id
46
- self.session_file = os.path.join(self.user_dir, f"{session_id}.yaml")
47
-
48
- def get_all_conversations(self) -> dict[str, dict]:
49
- """Retrieves all conversations for the current user."""
50
- conversations = {}
51
- for filename in os.listdir(self.user_dir):
52
- if filename.endswith(".yaml"):
53
- file_path = os.path.join(self.user_dir, filename)
54
- with open(file_path) as f:
55
- conversation = yaml.safe_load(f)
56
- if not isinstance(conversation, list) or len(conversation) > 1:
57
- raise ValueError(
58
- f"""Invalid format in {file_path}.
59
- YAML file can only contain one conversation with the following
60
- structure.
61
- - messages:
62
- - content: [message text]
63
- - type: (human or ai)"""
64
- )
65
- conversation = conversation[0]
66
- if "title" not in conversation:
67
- conversation["title"] = filename
68
- conversations[filename[:-5]] = conversation
69
- return dict(
70
- sorted(conversations.items(), key=lambda x: x[1].get("update_time", ""))
71
- )
72
-
73
- def upsert_session(self, session: dict) -> None:
74
- """Updates or inserts a session into the local storage."""
75
- session["update_time"] = datetime.now().isoformat()
76
- with open(self.session_file, "w") as f:
77
- yaml.dump(
78
- [session],
79
- f,
80
- allow_unicode=True,
81
- default_flow_style=False,
82
- encoding="utf-8",
83
- )
84
-
85
- def set_title(self, session: dict) -> None:
86
- """
87
- Set the title for the given session.
88
-
89
- This method generates a title for the session based on its messages.
90
- If the session has messages, it appends a special message to prompt
91
- for title creation, generates the title using a title chain, and
92
- updates the session with the new title.
93
-
94
- Args:
95
- session (dict): A dictionary containing session information,
96
- including messages.
97
-
98
- Returns:
99
- None
100
- """
101
- if session["messages"]:
102
- session["title"] = self.title_generator.summarize(
103
- events=session["messages"]
104
- )
105
- self.upsert_session(session)
106
-
107
- def clear(self) -> None:
108
- """Removes the current session file if it exists."""
109
- if os.path.exists(self.session_file):
110
- os.remove(self.session_file)
@@ -1,61 +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
- # fmt: off
16
-
17
- from typing import Any
18
-
19
- from google.adk.events.event import Event
20
-
21
-
22
- class MessageEditing:
23
- """Provides methods for editing, refreshing, and deleting chat messages."""
24
-
25
- @staticmethod
26
- def edit_message(st: Any, button_idx: int, message_type: str) -> None:
27
- """Edit a message in the chat history."""
28
- button_id = f"edit_box_{button_idx}"
29
- # Handle Event type messages
30
- message = st.session_state.user_chats[st.session_state["session_id"]]["messages"][button_idx]
31
- # Convert to Event if it's not already
32
- event = message if isinstance(message, Event) else Event.model_validate(message)
33
- # Update the text content in the event
34
- if hasattr(event.content, 'parts'):
35
- for part in event.content.parts:
36
- if hasattr(part, 'text'):
37
- part.text = st.session_state[button_id]
38
- break
39
- # Update the message in the session state
40
- st.session_state.user_chats[st.session_state["session_id"]]["messages"][button_idx] = event.model_dump()
41
-
42
- @staticmethod
43
- def refresh_message(st: Any, button_idx: int, content: str) -> None:
44
- """Refresh a message in the chat history."""
45
- messages = st.session_state.user_chats[st.session_state["session_id"]][
46
- "messages"
47
- ]
48
- st.session_state.user_chats[st.session_state["session_id"]][
49
- "messages"
50
- ] = messages[:button_idx]
51
- st.session_state.modified_prompt = content
52
-
53
- @staticmethod
54
- def delete_message(st: Any, button_idx: int) -> None:
55
- """Delete a message from the chat history."""
56
- messages = st.session_state.user_chats[st.session_state["session_id"]][
57
- "messages"
58
- ]
59
- st.session_state.user_chats[st.session_state["session_id"]][
60
- "messages"
61
- ] = messages[:button_idx]
@@ -1,223 +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 base64
16
- from typing import Any
17
- from urllib.parse import quote
18
-
19
- import google.cloud.storage as storage
20
- from google.genai import types
21
-
22
- HELP_MESSAGE_MULTIMODALITY = (
23
- "For Gemini models to access the URIs you provide, store them in "
24
- "Google Cloud Storage buckets within the same project used by Gemini."
25
- )
26
-
27
- HELP_GCS_CHECKBOX = (
28
- "Enabling GCS upload will increase the app observability by avoiding"
29
- " forwarding and logging large byte strings within the app."
30
- )
31
-
32
-
33
- def format_content(parts: list[types.Part] | str) -> str:
34
- """Formats content as a string, handling both text and multimedia inputs."""
35
- # Check if parts is empty
36
- if not parts:
37
- return ""
38
-
39
- # Handle case where parts might be a string
40
- if isinstance(parts, str):
41
- return parts
42
-
43
- markdown = ""
44
- text = ""
45
- for part in parts:
46
- # Convert to Part object if it's a dictionary
47
- if isinstance(part, dict):
48
- part = types.Part.model_validate(part)
49
- if part.text:
50
- text = part.text
51
- # Handle inline data (images and other media)
52
- elif part.inline_data:
53
- if "image" in part.inline_data.mime_type:
54
- # For images, create an HTML img tag
55
- image_data = base64.b64encode(part.inline_data.data).decode("utf-8")
56
- image_markdown = f'<img src="data:{part.inline_data.mime_type};base64,{image_data}" width="100">'
57
- markdown = (
58
- markdown
59
- + f"""
60
- - {image_markdown}
61
- """
62
- )
63
- else:
64
- # For other media types
65
- markdown = markdown + f"- Local media: {part.inline_data.mime_type}\n"
66
- # Handle file data (GCS files)
67
- elif part.file_data:
68
- if part.file_data.mime_type and "image" in part.file_data.mime_type:
69
- # GCS images
70
- image_url = gs_uri_to_https_url(part.file_data.file_uri)
71
- image_markdown = f'<img src="{image_url}" width="100">'
72
- markdown = (
73
- markdown
74
- + f"""
75
- - {image_markdown}
76
- """
77
- )
78
- else:
79
- # GCS other media
80
- image_url = gs_uri_to_https_url(part.file_data.file_uri)
81
- markdown = (
82
- markdown + f"- Remote media: "
83
- f"[{part.file_data.file_uri}]({image_url})\n"
84
- )
85
- markdown = (
86
- markdown
87
- + f"""
88
-
89
- {text}"""
90
- )
91
- return markdown
92
-
93
-
94
- def get_gcs_blob_mime_type(gcs_uri: str) -> str | None:
95
- """Fetches the MIME type (content type) of a Google Cloud Storage blob.
96
-
97
- Args:
98
- gcs_uri (str): The GCS URI of the blob in the format "gs://bucket-name/object-name".
99
-
100
- Returns:
101
- str: The MIME type of the blob (e.g., "image/jpeg", "text/plain") if found,
102
- or None if the blob does not exist or an error occurs.
103
- """
104
- storage_client = storage.Client()
105
-
106
- try:
107
- bucket_name, object_name = gcs_uri.replace("gs://", "").split("/", 1)
108
-
109
- bucket = storage_client.bucket(bucket_name)
110
- blob = bucket.blob(object_name)
111
- blob.reload()
112
- return blob.content_type
113
- except Exception as e:
114
- print(f"Error retrieving MIME type for {gcs_uri}: {e}")
115
- return None # Indicate failure
116
-
117
-
118
- def get_parts_from_files(
119
- upload_gcs_checkbox: bool, uploaded_files: list[Any], gcs_uris: str
120
- ) -> list[types.Part]:
121
- """Processes uploaded files and GCS URIs to create a list of content parts."""
122
- parts = []
123
- # read from local directly
124
- if not upload_gcs_checkbox:
125
- for uploaded_file in uploaded_files:
126
- im_bytes = uploaded_file.read()
127
- if "image" in uploaded_file.type:
128
- # Create inline_data Part for images
129
- blob = types.Blob(
130
- mime_type=uploaded_file.type,
131
- data=base64.b64encode(im_bytes).decode("utf-8"),
132
- )
133
- parts.append(types.Part(inline_data=blob))
134
- else:
135
- # Create inline_data Part for other file types
136
- blob = types.Blob(
137
- mime_type=uploaded_file.type,
138
- data=base64.b64encode(im_bytes).decode("utf-8"),
139
- )
140
- parts.append(types.Part(inline_data=blob))
141
-
142
- # Process GCS URIs
143
- if gcs_uris != "":
144
- for uri in gcs_uris.split(","):
145
- mime_type = get_gcs_blob_mime_type(uri)
146
- if mime_type:
147
- # Create file_data Part for GCS URIs
148
- file_data = types.FileData(file_uri=uri, mime_type=mime_type)
149
- parts.append(types.Part(file_data=file_data))
150
-
151
- return parts
152
-
153
-
154
- def upload_bytes_to_gcs(
155
- bucket_name: str,
156
- blob_name: str,
157
- file_bytes: bytes,
158
- content_type: str | None = None,
159
- ) -> str:
160
- """Uploads a bytes object to Google Cloud Storage and returns the GCS URI.
161
-
162
- Args:
163
- bucket_name: The name of the GCS bucket.
164
- blob_name: The desired name for the uploaded file in GCS.
165
- file_bytes: The file's content as a bytes object.
166
- content_type (optional): The MIME type of the file (e.g., "image/png").
167
- If not provided, GCS will try to infer it.
168
-
169
- Returns:
170
- str: The GCS URI (gs://bucket_name/blob_name) of the uploaded file.
171
-
172
- Raises:
173
- GoogleCloudError: If there's an issue with the GCS operation.
174
- """
175
- storage_client = storage.Client()
176
- bucket = storage_client.bucket(bucket_name)
177
- blob = bucket.blob(blob_name)
178
- blob.upload_from_string(data=file_bytes, content_type=content_type)
179
- # Construct and return the GCS URI
180
- gcs_uri = f"gs://{bucket_name}/{blob_name}"
181
- return gcs_uri
182
-
183
-
184
- def gs_uri_to_https_url(gs_uri: str) -> str:
185
- """Converts a GS URI to an HTTPS URL without authentication.
186
-
187
- Args:
188
- gs_uri: The GS URI in the format gs://<bucket>/<object>.
189
-
190
- Returns:
191
- The corresponding HTTPS URL, or None if the GS URI is invalid.
192
- """
193
-
194
- if not gs_uri.startswith("gs://"):
195
- raise ValueError("Invalid GS URI format")
196
-
197
- gs_uri = gs_uri[5:]
198
-
199
- # Extract bucket and object names, then URL encode the object name
200
- bucket_name, object_name = gs_uri.split("/", 1)
201
- object_name = quote(object_name)
202
-
203
- # Construct the HTTPS URL
204
- https_url = f"https://storage.mtls.cloud.google.com/{bucket_name}/{object_name}"
205
- return https_url
206
-
207
-
208
- def upload_files_to_gcs(st: Any, bucket_name: str, files_to_upload: list[Any]) -> None:
209
- """Upload multiple files to Google Cloud Storage and store URIs in session state."""
210
- bucket_name = bucket_name.replace("gs://", "")
211
- uploaded_uris = []
212
- for file in files_to_upload:
213
- if file:
214
- file_bytes = file.read()
215
- gcs_uri = upload_bytes_to_gcs(
216
- bucket_name=bucket_name,
217
- blob_name=file.name,
218
- file_bytes=file_bytes,
219
- content_type=file.type,
220
- )
221
- uploaded_uris.append(gcs_uri)
222
- st.session_state.uploader_key += 1
223
- st.session_state["gcs_uris_to_be_sent"] = ",".join(uploaded_uris)