agent-starter-pack 0.0.1b0__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 (162) hide show
  1. agent_starter_pack-0.0.1b0.dist-info/METADATA +143 -0
  2. agent_starter_pack-0.0.1b0.dist-info/RECORD +162 -0
  3. agent_starter_pack-0.0.1b0.dist-info/WHEEL +4 -0
  4. agent_starter_pack-0.0.1b0.dist-info/entry_points.txt +2 -0
  5. agent_starter_pack-0.0.1b0.dist-info/licenses/LICENSE +201 -0
  6. agents/agentic_rag_vertexai_search/README.md +22 -0
  7. agents/agentic_rag_vertexai_search/app/agent.py +145 -0
  8. agents/agentic_rag_vertexai_search/app/retrievers.py +79 -0
  9. agents/agentic_rag_vertexai_search/app/templates.py +53 -0
  10. agents/agentic_rag_vertexai_search/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  11. agents/agentic_rag_vertexai_search/template/.templateconfig.yaml +14 -0
  12. agents/agentic_rag_vertexai_search/tests/integration/test_agent.py +57 -0
  13. agents/crewai_coding_crew/README.md +34 -0
  14. agents/crewai_coding_crew/app/agent.py +86 -0
  15. agents/crewai_coding_crew/app/crew/config/agents.yaml +39 -0
  16. agents/crewai_coding_crew/app/crew/config/tasks.yaml +37 -0
  17. agents/crewai_coding_crew/app/crew/crew.py +71 -0
  18. agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +1571 -0
  19. agents/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  20. agents/crewai_coding_crew/template/.templateconfig.yaml +12 -0
  21. agents/crewai_coding_crew/tests/integration/test_agent.py +47 -0
  22. agents/langgraph_base_react/README.md +9 -0
  23. agents/langgraph_base_react/app/agent.py +73 -0
  24. agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  25. agents/langgraph_base_react/template/.templateconfig.yaml +13 -0
  26. agents/langgraph_base_react/tests/integration/test_agent.py +48 -0
  27. agents/multimodal_live_api/README.md +50 -0
  28. agents/multimodal_live_api/app/agent.py +86 -0
  29. agents/multimodal_live_api/app/server.py +193 -0
  30. agents/multimodal_live_api/app/templates.py +51 -0
  31. agents/multimodal_live_api/app/vector_store.py +55 -0
  32. agents/multimodal_live_api/template/.templateconfig.yaml +15 -0
  33. agents/multimodal_live_api/tests/integration/test_server_e2e.py +254 -0
  34. agents/multimodal_live_api/tests/load_test/load_test.py +40 -0
  35. agents/multimodal_live_api/tests/unit/test_server.py +143 -0
  36. src/base_template/.gitignore +197 -0
  37. src/base_template/Makefile +37 -0
  38. src/base_template/README.md +91 -0
  39. src/base_template/app/utils/tracing.py +143 -0
  40. src/base_template/app/utils/typing.py +115 -0
  41. src/base_template/deployment/README.md +123 -0
  42. src/base_template/deployment/cd/deploy-to-prod.yaml +98 -0
  43. src/base_template/deployment/cd/staging.yaml +215 -0
  44. src/base_template/deployment/ci/pr_checks.yaml +51 -0
  45. src/base_template/deployment/terraform/apis.tf +34 -0
  46. src/base_template/deployment/terraform/build_triggers.tf +122 -0
  47. src/base_template/deployment/terraform/dev/apis.tf +42 -0
  48. src/base_template/deployment/terraform/dev/iam.tf +90 -0
  49. src/base_template/deployment/terraform/dev/log_sinks.tf +66 -0
  50. src/base_template/deployment/terraform/dev/providers.tf +29 -0
  51. src/base_template/deployment/terraform/dev/storage.tf +76 -0
  52. src/base_template/deployment/terraform/dev/variables.tf +126 -0
  53. src/base_template/deployment/terraform/dev/vars/env.tfvars +21 -0
  54. src/base_template/deployment/terraform/iam.tf +130 -0
  55. src/base_template/deployment/terraform/locals.tf +50 -0
  56. src/base_template/deployment/terraform/log_sinks.tf +72 -0
  57. src/base_template/deployment/terraform/providers.tf +35 -0
  58. src/base_template/deployment/terraform/service_accounts.tf +42 -0
  59. src/base_template/deployment/terraform/storage.tf +100 -0
  60. src/base_template/deployment/terraform/variables.tf +202 -0
  61. src/base_template/deployment/terraform/vars/env.tfvars +43 -0
  62. src/base_template/pyproject.toml +113 -0
  63. src/base_template/tests/unit/test_utils/test_tracing_exporter.py +140 -0
  64. src/cli/commands/create.py +534 -0
  65. src/cli/commands/setup_cicd.py +730 -0
  66. src/cli/main.py +35 -0
  67. src/cli/utils/__init__.py +35 -0
  68. src/cli/utils/cicd.py +662 -0
  69. src/cli/utils/gcp.py +120 -0
  70. src/cli/utils/logging.py +51 -0
  71. src/cli/utils/template.py +644 -0
  72. src/data_ingestion/README.md +79 -0
  73. src/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +175 -0
  74. src/data_ingestion/data_ingestion_pipeline/components/process_data.py +321 -0
  75. src/data_ingestion/data_ingestion_pipeline/pipeline.py +58 -0
  76. src/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +184 -0
  77. src/data_ingestion/pyproject.toml +17 -0
  78. src/data_ingestion/uv.lock +999 -0
  79. src/deployment_targets/agent_engine/app/agent_engine_app.py +238 -0
  80. src/deployment_targets/agent_engine/app/utils/gcs.py +42 -0
  81. src/deployment_targets/agent_engine/deployment_metadata.json +4 -0
  82. src/deployment_targets/agent_engine/notebooks/intro_reasoning_engine.ipynb +869 -0
  83. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +120 -0
  84. src/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  85. src/deployment_targets/agent_engine/tests/load_test/.results/report.html +264 -0
  86. src/deployment_targets/agent_engine/tests/load_test/.results/results_exceptions.csv +1 -0
  87. src/deployment_targets/agent_engine/tests/load_test/.results/results_failures.csv +1 -0
  88. src/deployment_targets/agent_engine/tests/load_test/.results/results_stats.csv +3 -0
  89. src/deployment_targets/agent_engine/tests/load_test/.results/results_stats_history.csv +22 -0
  90. src/deployment_targets/agent_engine/tests/load_test/README.md +42 -0
  91. src/deployment_targets/agent_engine/tests/load_test/load_test.py +100 -0
  92. src/deployment_targets/agent_engine/tests/unit/test_dummy.py +22 -0
  93. src/deployment_targets/cloud_run/Dockerfile +29 -0
  94. src/deployment_targets/cloud_run/app/server.py +128 -0
  95. src/deployment_targets/cloud_run/deployment/terraform/artifact_registry.tf +22 -0
  96. src/deployment_targets/cloud_run/deployment/terraform/dev/service_accounts.tf +20 -0
  97. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +192 -0
  98. src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  99. src/deployment_targets/cloud_run/tests/load_test/README.md +79 -0
  100. src/deployment_targets/cloud_run/tests/load_test/load_test.py +85 -0
  101. src/deployment_targets/cloud_run/tests/unit/test_server.py +142 -0
  102. src/deployment_targets/cloud_run/uv.lock +6952 -0
  103. src/frontends/live_api_react/frontend/package-lock.json +19405 -0
  104. src/frontends/live_api_react/frontend/package.json +56 -0
  105. src/frontends/live_api_react/frontend/public/favicon.ico +0 -0
  106. src/frontends/live_api_react/frontend/public/index.html +62 -0
  107. src/frontends/live_api_react/frontend/public/robots.txt +3 -0
  108. src/frontends/live_api_react/frontend/src/App.scss +189 -0
  109. src/frontends/live_api_react/frontend/src/App.test.tsx +25 -0
  110. src/frontends/live_api_react/frontend/src/App.tsx +205 -0
  111. src/frontends/live_api_react/frontend/src/components/audio-pulse/AudioPulse.tsx +64 -0
  112. src/frontends/live_api_react/frontend/src/components/audio-pulse/audio-pulse.scss +68 -0
  113. src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +217 -0
  114. src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +201 -0
  115. src/frontends/live_api_react/frontend/src/components/logger/Logger.tsx +241 -0
  116. src/frontends/live_api_react/frontend/src/components/logger/logger.scss +133 -0
  117. src/frontends/live_api_react/frontend/src/components/logger/mock-logs.ts +151 -0
  118. src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +161 -0
  119. src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +285 -0
  120. src/frontends/live_api_react/frontend/src/contexts/LiveAPIContext.tsx +48 -0
  121. src/frontends/live_api_react/frontend/src/hooks/use-live-api.ts +115 -0
  122. src/frontends/live_api_react/frontend/src/hooks/use-media-stream-mux.ts +23 -0
  123. src/frontends/live_api_react/frontend/src/hooks/use-screen-capture.ts +72 -0
  124. src/frontends/live_api_react/frontend/src/hooks/use-webcam.ts +69 -0
  125. src/frontends/live_api_react/frontend/src/index.css +28 -0
  126. src/frontends/live_api_react/frontend/src/index.tsx +35 -0
  127. src/frontends/live_api_react/frontend/src/multimodal-live-types.ts +242 -0
  128. src/frontends/live_api_react/frontend/src/react-app-env.d.ts +17 -0
  129. src/frontends/live_api_react/frontend/src/reportWebVitals.ts +31 -0
  130. src/frontends/live_api_react/frontend/src/setupTests.ts +21 -0
  131. src/frontends/live_api_react/frontend/src/utils/audio-recorder.ts +111 -0
  132. src/frontends/live_api_react/frontend/src/utils/audio-streamer.ts +270 -0
  133. src/frontends/live_api_react/frontend/src/utils/audioworklet-registry.ts +43 -0
  134. src/frontends/live_api_react/frontend/src/utils/multimodal-live-client.ts +329 -0
  135. src/frontends/live_api_react/frontend/src/utils/store-logger.ts +64 -0
  136. src/frontends/live_api_react/frontend/src/utils/utils.ts +86 -0
  137. src/frontends/live_api_react/frontend/src/utils/worklets/audio-processing.ts +73 -0
  138. src/frontends/live_api_react/frontend/src/utils/worklets/vol-meter.ts +65 -0
  139. src/frontends/live_api_react/frontend/tsconfig.json +25 -0
  140. src/frontends/streamlit/frontend/side_bar.py +213 -0
  141. src/frontends/streamlit/frontend/streamlit_app.py +263 -0
  142. src/frontends/streamlit/frontend/style/app_markdown.py +37 -0
  143. src/frontends/streamlit/frontend/utils/chat_utils.py +67 -0
  144. src/frontends/streamlit/frontend/utils/local_chat_history.py +125 -0
  145. src/frontends/streamlit/frontend/utils/message_editing.py +59 -0
  146. src/frontends/streamlit/frontend/utils/multimodal_utils.py +217 -0
  147. src/frontends/streamlit/frontend/utils/stream_handler.py +282 -0
  148. src/frontends/streamlit/frontend/utils/title_summary.py +77 -0
  149. src/resources/containers/data_processing/Dockerfile +25 -0
  150. src/resources/locks/uv-agentic_rag_vertexai_search-agent_engine.lock +4684 -0
  151. src/resources/locks/uv-agentic_rag_vertexai_search-cloud_run.lock +5799 -0
  152. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +5509 -0
  153. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +6688 -0
  154. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +4595 -0
  155. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +5710 -0
  156. src/resources/locks/uv-multimodal_live_api-cloud_run.lock +5665 -0
  157. src/resources/setup_cicd/cicd_variables.tf +36 -0
  158. src/resources/setup_cicd/github.tf +85 -0
  159. src/resources/setup_cicd/providers.tf +39 -0
  160. src/utils/generate_locks.py +135 -0
  161. src/utils/lock_utils.py +82 -0
  162. src/utils/watch_and_rebuild.py +190 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Copyright 2024 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import cn from "classnames";
18
+
19
+ import { memo, ReactNode, RefObject, useEffect, useRef, useState } from "react";
20
+ import { useLiveAPIContext } from "../../contexts/LiveAPIContext";
21
+ import { UseMediaStreamResult } from "../../hooks/use-media-stream-mux";
22
+ import { useScreenCapture } from "../../hooks/use-screen-capture";
23
+ import { useWebcam } from "../../hooks/use-webcam";
24
+ import { AudioRecorder } from "../../utils/audio-recorder";
25
+ import AudioPulse from "../audio-pulse/AudioPulse";
26
+ import "./control-tray.scss";
27
+
28
+ export type ControlTrayProps = {
29
+ videoRef: RefObject<HTMLVideoElement>;
30
+ children?: ReactNode;
31
+ supportsVideo: boolean;
32
+ onVideoStreamChange?: (stream: MediaStream | null) => void;
33
+ };
34
+
35
+ type MediaStreamButtonProps = {
36
+ isStreaming: boolean;
37
+ onIcon: string;
38
+ offIcon: string;
39
+ start: () => Promise<any>;
40
+ stop: () => any;
41
+ };
42
+
43
+ /**
44
+ * button used for triggering webcam or screen-capture
45
+ */
46
+ const MediaStreamButton = memo(
47
+ ({ isStreaming, onIcon, offIcon, start, stop }: MediaStreamButtonProps) =>
48
+ isStreaming ? (
49
+ <button className="action-button" onClick={stop}>
50
+ <span className="material-symbols-outlined">{onIcon}</span>
51
+ </button>
52
+ ) : (
53
+ <button className="action-button" onClick={start}>
54
+ <span className="material-symbols-outlined">{offIcon}</span>
55
+ </button>
56
+ ),
57
+ );
58
+
59
+ function ControlTray({
60
+ videoRef,
61
+ children,
62
+ onVideoStreamChange = () => {},
63
+ supportsVideo,
64
+ }: ControlTrayProps) {
65
+ const videoStreams = [useWebcam(), useScreenCapture()];
66
+ const [activeVideoStream, setActiveVideoStream] =
67
+ useState<MediaStream | null>(null);
68
+ const [webcam, screenCapture] = videoStreams;
69
+ const [inVolume, setInVolume] = useState(0);
70
+ const [audioRecorder] = useState(() => new AudioRecorder());
71
+ const [muted, setMuted] = useState(false);
72
+ const renderCanvasRef = useRef<HTMLCanvasElement>(null);
73
+ const connectButtonRef = useRef<HTMLButtonElement>(null);
74
+
75
+ const { client, connected, connect, disconnect, volume } =
76
+ useLiveAPIContext();
77
+
78
+ useEffect(() => {
79
+ if (!connected && connectButtonRef.current) {
80
+ connectButtonRef.current.focus();
81
+ }
82
+ }, [connected]);
83
+ useEffect(() => {
84
+ document.documentElement.style.setProperty(
85
+ "--volume",
86
+ `${Math.max(5, Math.min(inVolume * 200, 8))}px`,
87
+ );
88
+ }, [inVolume]);
89
+
90
+ useEffect(() => {
91
+ const onData = (base64: string) => {
92
+ client.sendRealtimeInput([
93
+ {
94
+ mimeType: "audio/pcm;rate=16000",
95
+ data: base64,
96
+ },
97
+ ]);
98
+ };
99
+ if (connected && !muted && audioRecorder) {
100
+ audioRecorder.on("data", onData).on("volume", setInVolume).start();
101
+ } else {
102
+ audioRecorder.stop();
103
+ }
104
+ return () => {
105
+ audioRecorder.off("data", onData).off("volume", setInVolume);
106
+ };
107
+ }, [connected, client, muted, audioRecorder]);
108
+
109
+ useEffect(() => {
110
+ if (videoRef.current) {
111
+ videoRef.current.srcObject = activeVideoStream;
112
+ }
113
+
114
+ let timeoutId = -1;
115
+
116
+ function sendVideoFrame() {
117
+ const video = videoRef.current;
118
+ const canvas = renderCanvasRef.current;
119
+
120
+ if (!video || !canvas) {
121
+ return;
122
+ }
123
+
124
+ const ctx = canvas.getContext("2d")!;
125
+ canvas.width = video.videoWidth * 0.25;
126
+ canvas.height = video.videoHeight * 0.25;
127
+ if (canvas.width + canvas.height > 0) {
128
+ ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);
129
+ const base64 = canvas.toDataURL("image/jpeg", 1.0);
130
+ const data = base64.slice(base64.indexOf(",") + 1, Infinity);
131
+ client.sendRealtimeInput([{ mimeType: "image/jpeg", data }]);
132
+ }
133
+ if (connected) {
134
+ timeoutId = window.setTimeout(sendVideoFrame, 1000 / 0.5);
135
+ }
136
+ }
137
+ if (connected && activeVideoStream !== null) {
138
+ requestAnimationFrame(sendVideoFrame);
139
+ }
140
+ return () => {
141
+ clearTimeout(timeoutId);
142
+ };
143
+ }, [connected, activeVideoStream, client, videoRef]);
144
+
145
+ //handler for swapping from one video-stream to the next
146
+ const changeStreams = (next?: UseMediaStreamResult) => async () => {
147
+ if (next) {
148
+ const mediaStream = await next.start();
149
+ setActiveVideoStream(mediaStream);
150
+ onVideoStreamChange(mediaStream);
151
+ } else {
152
+ setActiveVideoStream(null);
153
+ onVideoStreamChange(null);
154
+ }
155
+
156
+ videoStreams.filter((msr) => msr !== next).forEach((msr) => msr.stop());
157
+ };
158
+
159
+ return (
160
+ <section className="control-tray">
161
+ <canvas style={{ display: "none" }} ref={renderCanvasRef} />
162
+ <nav className={cn("actions-nav", { disabled: !connected })}>
163
+ <button
164
+ className={cn("action-button mic-button")}
165
+ onClick={() => setMuted(!muted)}
166
+ >
167
+ {!muted ? (
168
+ <span className="material-symbols-outlined filled">mic</span>
169
+ ) : (
170
+ <span className="material-symbols-outlined filled">mic_off</span>
171
+ )}
172
+ </button>
173
+
174
+ <div className="action-button no-action outlined">
175
+ <AudioPulse volume={volume} active={connected} hover={false} />
176
+ </div>
177
+
178
+ {supportsVideo && (
179
+ <>
180
+ <MediaStreamButton
181
+ isStreaming={screenCapture.isStreaming}
182
+ start={changeStreams(screenCapture)}
183
+ stop={changeStreams()}
184
+ onIcon="cancel_presentation"
185
+ offIcon="present_to_all"
186
+ />
187
+ <MediaStreamButton
188
+ isStreaming={webcam.isStreaming}
189
+ start={changeStreams(webcam)}
190
+ stop={changeStreams()}
191
+ onIcon="videocam_off"
192
+ offIcon="videocam"
193
+ />
194
+ </>
195
+ )}
196
+ {children}
197
+ </nav>
198
+
199
+ <div className={cn("connection-container", { connected })}>
200
+ <div className="connection-button-container">
201
+ <button
202
+ ref={connectButtonRef}
203
+ className={cn("action-button connect-toggle", { connected })}
204
+ onClick={connected ? disconnect : connect}
205
+ >
206
+ <span className="material-symbols-outlined filled">
207
+ {connected ? "pause" : "play_arrow"}
208
+ </span>
209
+ </button>
210
+ </div>
211
+ <span className="text-indicator">Streaming</span>
212
+ </div>
213
+ </section>
214
+ );
215
+ }
216
+
217
+ export default memo(ControlTray);
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Copyright 2025 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /* stylelint-disable */
18
+ .action-button {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ background: var(--Neutral-20);
23
+ color: var(--Neutral-60);
24
+ font-size: 1.25rem;
25
+ line-height: 1.75rem;
26
+ text-transform: lowercase;
27
+ cursor: pointer;
28
+ animation: opacity-pulse 3s ease-in infinite;
29
+ transition: all 0.2s ease-in-out;
30
+ width: 48px;
31
+ height: 48px;
32
+ border-radius: 18px;
33
+ border: 1px solid rgba(0, 0, 0, 0);
34
+ user-select: none;
35
+ cursor: pointer;
36
+
37
+ &:focus {
38
+ border: 2px solid var(--Neutral-20);
39
+ outline: 2px solid var(--Neutral-80);
40
+ }
41
+
42
+ &.outlined {
43
+ background: var(--Neutral-2);
44
+ border: 1px solid var(--Neutral-20);
45
+ }
46
+
47
+ .no-action {
48
+ pointer-events: none;
49
+ }
50
+
51
+ &:hover {
52
+ background: rgba(0, 0, 0, 0);
53
+ border: 1px solid var(--Neutral-20);
54
+ }
55
+
56
+ &.connected {
57
+ background: var(--Blue-800);
58
+ color: var(--Blue-500);
59
+
60
+ &:hover {
61
+ border: 1px solid var(--Blue-500);
62
+ }
63
+ }
64
+ }
65
+
66
+ @property --volume {
67
+ syntax: "length";
68
+ inherit: false;
69
+ initial-value: 0px;
70
+ }
71
+
72
+ .disabled .mic-button,
73
+ .mic-button.disabled {
74
+ &:before {
75
+ background: rgba(0, 0, 0, 0);
76
+ }
77
+ }
78
+
79
+ .mic-button {
80
+ position: relative;
81
+ background-color: var(--accent-red);
82
+ z-index: 1;
83
+ color: black;
84
+ transition: all 0.2s ease-in;
85
+
86
+ &:focus {
87
+ border: 2px solid var(--Neutral-20);
88
+ outline: 2px solid var(--Red-500);
89
+ }
90
+
91
+ &:hover {
92
+ background-color: var(--Red-400);
93
+ }
94
+
95
+ &:before {
96
+ position: absolute;
97
+ z-index: -1;
98
+ top: calc(var(--volume) * -1);
99
+ left: calc(var(--volume) * -1);
100
+ display: block;
101
+ content: "";
102
+ opacity: 0.35;
103
+ background-color: var(--Red-500);
104
+ width: calc(100% + var(--volume) * 2);
105
+ height: calc(100% + var(--volume) * 2);
106
+ border-radius: 24px;
107
+ transition: all 0.02s ease-in-out;
108
+ }
109
+ }
110
+
111
+ .connect-toggle {
112
+ &:focus {
113
+ border: 2px solid var(--Neutral-20);
114
+ outline: 2px solid var(--Neutral-80);
115
+ }
116
+
117
+ &:not(.connected) {
118
+ background-color: var(--Blue-500);
119
+ color: var(--Neutral-5);
120
+ }
121
+ }
122
+
123
+ .control-tray {
124
+ position: absolute;
125
+ bottom: 0;
126
+ left: 50%;
127
+ transform: translate(-50%, 0);
128
+ display: inline-flex;
129
+ justify-content: center;
130
+ align-items: flex-start;
131
+ gap: 8px;
132
+ padding-bottom: 18px;
133
+
134
+ .disabled .action-button,
135
+ .action-button.disabled {
136
+ background: rgba(0, 0, 0, 0);
137
+ border: 1px solid var(--Neutral-30, #404547);
138
+ color: var(--Neutral-30);
139
+ }
140
+
141
+ .connection-container {
142
+ display: flex;
143
+ flex-direction: column;
144
+ justify-content: center;
145
+ align-items: center;
146
+ gap: 4px;
147
+
148
+ .connection-button-container {
149
+ border-radius: 27px;
150
+ border: 1px solid var(--Neutral-30);
151
+ background: var(--Neutral-5);
152
+ padding: 10px;
153
+ }
154
+
155
+ .text-indicator {
156
+ font-size: 11px;
157
+ color: var(--Blue-500);
158
+ user-select: none;
159
+ }
160
+
161
+ &:not(.connected) {
162
+ .text-indicator {
163
+ opacity: 0;
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ .actions-nav {
170
+ background: var(--Neutral-5);
171
+ border: 1px solid var(--Neutral-30);
172
+ border-radius: 27px;
173
+ display: inline-flex;
174
+ gap: 12px;
175
+ align-items: center;
176
+ overflow: clip;
177
+ padding: 10px;
178
+
179
+ transition: all 0.6s ease-in;
180
+
181
+ & > * {
182
+ display: flex;
183
+ align-items: center;
184
+ flex-direction: column;
185
+ gap: 1rem;
186
+ }
187
+ }
188
+
189
+ @keyframes opacity-pulse {
190
+ 0% {
191
+ opacity: 0.9;
192
+ }
193
+
194
+ 50% {
195
+ opacity: 1;
196
+ }
197
+
198
+ 100% {
199
+ opacity: 0.9;
200
+ }
201
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Copyright 2024 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import "./logger.scss";
18
+
19
+ import { Part } from "@google/generative-ai";
20
+ import cn from "classnames";
21
+ import { ReactNode } from "react";
22
+ import { useLoggerStore } from "../../utils/store-logger";
23
+ import {
24
+ ClientContentMessage,
25
+ isClientContentMessage,
26
+ isInterrupted,
27
+ isModelTurn,
28
+ isServerContenteMessage,
29
+ isToolCallCancellationMessage,
30
+ isToolCallMessage,
31
+ isToolResponseMessage,
32
+ isTurnComplete,
33
+ ModelTurn,
34
+ ServerContentMessage,
35
+ StreamingLog,
36
+ ToolCallCancellationMessage,
37
+ ToolCallMessage,
38
+ ToolResponseMessage,
39
+ } from "../../multimodal-live-types";
40
+
41
+ const formatTime = (d: Date) => d.toLocaleTimeString().slice(0, -3);
42
+
43
+ const LogEntry = ({
44
+ log,
45
+ MessageComponent,
46
+ }: {
47
+ log: StreamingLog;
48
+ MessageComponent: ({
49
+ message,
50
+ }: {
51
+ message: StreamingLog["message"];
52
+ }) => ReactNode;
53
+ }): JSX.Element => (
54
+ <li
55
+ className={cn(
56
+ `plain-log`,
57
+ `source-${log.type.slice(0, log.type.indexOf("."))}`,
58
+ {
59
+ receive: log.type.includes("receive"),
60
+ send: log.type.includes("send"),
61
+ },
62
+ )}
63
+ >
64
+ <span className="timestamp">{formatTime(log.date)}</span>
65
+ <span className="source">{log.type}</span>
66
+ <span className="message">
67
+ <MessageComponent message={log.message} />
68
+ </span>
69
+ {log.count && <span className="count">{log.count}</span>}
70
+ </li>
71
+ );
72
+
73
+ const PlainTextMessage = ({
74
+ message,
75
+ }: {
76
+ message: StreamingLog["message"];
77
+ }) => <span>{message as string}</span>;
78
+
79
+ type Message = { message: StreamingLog["message"] };
80
+
81
+ const AnyMessage = ({ message }: Message) => (
82
+ <pre>{JSON.stringify(message, null, " ")}</pre>
83
+ );
84
+
85
+ const RenderPart = ({ part }: { part: Part }) =>
86
+ part.text && part.text.length ? (
87
+ <p className="part part-text">{part.text}</p>
88
+ ) : (
89
+ <div className="part part-inlinedata">
90
+ <h5>Inline Data: {part.inlineData?.mimeType}</h5>
91
+ </div>
92
+ );
93
+
94
+ const ClientContentLog = ({ message }: Message) => {
95
+ const { turns, turnComplete } = (message as ClientContentMessage)
96
+ .clientContent;
97
+ return (
98
+ <div className="rich-log client-content user">
99
+ <h4 className="roler-user">User</h4>
100
+ {turns.map((turn, i) => (
101
+ <div key={`message-turn-${i}`}>
102
+ {turn.parts
103
+ .filter((part) => !(part.text && part.text === "\n"))
104
+ .map((part, j) => (
105
+ <RenderPart part={part} key={`message-turh-${i}-part-${j}`} />
106
+ ))}
107
+ </div>
108
+ ))}
109
+ {!turnComplete ? <span>turnComplete: false</span> : ""}
110
+ </div>
111
+ );
112
+ };
113
+
114
+ const ToolCallLog = ({ message }: Message) => {
115
+ const { toolCall } = message as ToolCallMessage;
116
+ return (
117
+ <div className={cn("rich-log tool-call")}>
118
+ {toolCall.functionCalls.map((fc, i) => (
119
+ <div key={fc.id} className="part part-functioncall">
120
+ <h5>Function call: {fc.name}</h5>
121
+ <pre>{JSON.stringify(fc, null, " ")}</pre>
122
+ </div>
123
+ ))}
124
+ </div>
125
+ );
126
+ };
127
+
128
+ const ToolCallCancellationLog = ({ message }: Message): JSX.Element => (
129
+ <div className={cn("rich-log tool-call-cancellation")}>
130
+ <span>
131
+ {" "}
132
+ ids:{" "}
133
+ {(message as ToolCallCancellationMessage).toolCallCancellation.ids.map(
134
+ (id) => (
135
+ <span className="inline-code" key={`cancel-${id}`}>
136
+ "{id}"
137
+ </span>
138
+ ),
139
+ )}
140
+ </span>
141
+ </div>
142
+ );
143
+
144
+ const ToolResponseLog = ({ message }: Message): JSX.Element => (
145
+ <div className={cn("rich-log tool-response")}>
146
+ {(message as ToolResponseMessage).toolResponse.functionResponses.map(
147
+ (fc) => (
148
+ <div key={`tool-response-${fc.id}`} className="part">
149
+ <h5>Function Response: {fc.id}</h5>
150
+ <pre>{JSON.stringify(fc.response, null, " ")}</pre>
151
+ </div>
152
+ ),
153
+ )}
154
+ </div>
155
+ );
156
+
157
+ const ModelTurnLog = ({ message }: Message): JSX.Element => {
158
+ const serverContent = (message as ServerContentMessage).serverContent;
159
+ const { modelTurn } = serverContent as ModelTurn;
160
+ const { parts } = modelTurn;
161
+
162
+ return (
163
+ <div className="rich-log model-turn model">
164
+ <h4 className="role-model">Model</h4>
165
+ {parts
166
+ .filter((part) => !(part.text && part.text === "\n"))
167
+ .map((part, j) => (
168
+ <RenderPart part={part} key={`model-turn-part-${j}`} />
169
+ ))}
170
+ </div>
171
+ );
172
+ };
173
+
174
+ const CustomPlainTextLog = (msg: string) => () => (
175
+ <PlainTextMessage message={msg} />
176
+ );
177
+
178
+ export type LoggerFilterType = "conversations" | "tools" | "none";
179
+
180
+ export type LoggerProps = {
181
+ filter: LoggerFilterType;
182
+ };
183
+
184
+ const filters: Record<LoggerFilterType, (log: StreamingLog) => boolean> = {
185
+ tools: (log: StreamingLog) =>
186
+ isToolCallMessage(log.message) ||
187
+ isToolResponseMessage(log.message) ||
188
+ isToolCallCancellationMessage(log.message),
189
+ conversations: (log: StreamingLog) =>
190
+ isClientContentMessage(log.message) || isServerContenteMessage(log.message),
191
+ none: () => true,
192
+ };
193
+
194
+ const component = (log: StreamingLog) => {
195
+ if (typeof log.message === "string") {
196
+ return PlainTextMessage;
197
+ }
198
+ if (isClientContentMessage(log.message)) {
199
+ return ClientContentLog;
200
+ }
201
+ if (isToolCallMessage(log.message)) {
202
+ return ToolCallLog;
203
+ }
204
+ if (isToolCallCancellationMessage(log.message)) {
205
+ return ToolCallCancellationLog;
206
+ }
207
+ if (isToolResponseMessage(log.message)) {
208
+ return ToolResponseLog;
209
+ }
210
+ if (isServerContenteMessage(log.message)) {
211
+ const { serverContent } = log.message;
212
+ if (isInterrupted(serverContent)) {
213
+ return CustomPlainTextLog("interrupted");
214
+ }
215
+ if (isTurnComplete(serverContent)) {
216
+ return CustomPlainTextLog("turnComplete");
217
+ }
218
+ if (isModelTurn(serverContent)) {
219
+ return ModelTurnLog;
220
+ }
221
+ }
222
+ return AnyMessage;
223
+ };
224
+
225
+ export default function Logger({ filter = "none" }: LoggerProps) {
226
+ const { logs } = useLoggerStore();
227
+
228
+ const filterFn = filters[filter];
229
+
230
+ return (
231
+ <div className="logger">
232
+ <ul className="logger-list">
233
+ {logs.filter(filterFn).map((log, key) => {
234
+ return (
235
+ <LogEntry MessageComponent={component(log)} log={log} key={key} />
236
+ );
237
+ })}
238
+ </ul>
239
+ </div>
240
+ );
241
+ }