@assistant-ui/react-google-adk 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/AdkClient.d.ts +45 -0
- package/dist/AdkClient.d.ts.map +1 -0
- package/dist/AdkClient.js +204 -0
- package/dist/AdkClient.js.map +1 -0
- package/dist/AdkEventAccumulator.d.ts +45 -0
- package/dist/AdkEventAccumulator.d.ts.map +1 -0
- package/dist/AdkEventAccumulator.js +508 -0
- package/dist/AdkEventAccumulator.js.map +1 -0
- package/dist/AdkSessionAdapter.d.ts +61 -0
- package/dist/AdkSessionAdapter.d.ts.map +1 -0
- package/dist/AdkSessionAdapter.js +159 -0
- package/dist/AdkSessionAdapter.js.map +1 -0
- package/dist/convertAdkMessages.d.ts +4 -0
- package/dist/convertAdkMessages.d.ts.map +1 -0
- package/dist/convertAdkMessages.js +75 -0
- package/dist/convertAdkMessages.js.map +1 -0
- package/dist/hooks.d.ts +50 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +173 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/server/adkEventStream.d.ts +42 -0
- package/dist/server/adkEventStream.d.ts.map +1 -0
- package/dist/server/adkEventStream.js +135 -0
- package/dist/server/adkEventStream.js.map +1 -0
- package/dist/server/createAdkApiRoute.d.ts +47 -0
- package/dist/server/createAdkApiRoute.d.ts.map +1 -0
- package/dist/server/createAdkApiRoute.js +41 -0
- package/dist/server/createAdkApiRoute.js.map +1 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +4 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/parseAdkRequest.d.ts +56 -0
- package/dist/server/parseAdkRequest.d.ts.map +1 -0
- package/dist/server/parseAdkRequest.js +93 -0
- package/dist/server/parseAdkRequest.js.map +1 -0
- package/dist/structuredEvents.d.ts +7 -0
- package/dist/structuredEvents.d.ts.map +1 -0
- package/dist/structuredEvents.js +79 -0
- package/dist/structuredEvents.js.map +1 -0
- package/dist/types.d.ts +253 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/dist/useAdkMessages.d.ts +28 -0
- package/dist/useAdkMessages.d.ts.map +1 -0
- package/dist/useAdkMessages.js +198 -0
- package/dist/useAdkMessages.js.map +1 -0
- package/dist/useAdkRuntime.d.ts +36 -0
- package/dist/useAdkRuntime.d.ts.map +1 -0
- package/dist/useAdkRuntime.js +252 -0
- package/dist/useAdkRuntime.js.map +1 -0
- package/package.json +83 -0
- package/server/package.json +4 -0
- package/src/AdkClient.test.ts +662 -0
- package/src/AdkClient.ts +274 -0
- package/src/AdkEventAccumulator.test.ts +591 -0
- package/src/AdkEventAccumulator.ts +602 -0
- package/src/AdkSessionAdapter.test.ts +362 -0
- package/src/AdkSessionAdapter.ts +245 -0
- package/src/convertAdkMessages.test.ts +209 -0
- package/src/convertAdkMessages.ts +93 -0
- package/src/hooks.ts +217 -0
- package/src/index.ts +66 -0
- package/src/server/adkEventStream.test.ts +78 -0
- package/src/server/adkEventStream.ts +161 -0
- package/src/server/createAdkApiRoute.test.ts +370 -0
- package/src/server/createAdkApiRoute.ts +86 -0
- package/src/server/index.ts +6 -0
- package/src/server/parseAdkRequest.test.ts +152 -0
- package/src/server/parseAdkRequest.ts +122 -0
- package/src/structuredEvents.ts +81 -0
- package/src/types.ts +265 -0
- package/src/useAdkMessages.ts +259 -0
- package/src/useAdkRuntime.ts +398 -0
package/src/AdkClient.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AdkEvent,
|
|
3
|
+
AdkMessage,
|
|
4
|
+
AdkMessageContentPart,
|
|
5
|
+
AdkStreamCallback,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
export type CreateAdkStreamOptions = {
|
|
9
|
+
/**
|
|
10
|
+
* URL to POST to. Either a proxy route (e.g. "/api/adk") or
|
|
11
|
+
* an ADK server base URL (e.g. "http://localhost:8000").
|
|
12
|
+
*
|
|
13
|
+
* When `appName` and `userId` are provided, POSTs to `${api}/run_sse`
|
|
14
|
+
* in ADK-native format. Otherwise POSTs directly to `api` in proxy format
|
|
15
|
+
* (compatible with `parseAdkRequest`).
|
|
16
|
+
*/
|
|
17
|
+
api: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* ADK application name. When provided along with `userId`,
|
|
21
|
+
* enables direct connection to an ADK server.
|
|
22
|
+
*/
|
|
23
|
+
appName?: string | undefined;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ADK user ID. Required when `appName` is provided.
|
|
27
|
+
*/
|
|
28
|
+
userId?: string | undefined;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extra headers to send with every request.
|
|
32
|
+
* Can be a static object or an async function for dynamic auth tokens.
|
|
33
|
+
*/
|
|
34
|
+
headers?:
|
|
35
|
+
| Record<string, string>
|
|
36
|
+
| (() => Record<string, string> | Promise<Record<string, string>>)
|
|
37
|
+
| undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates an `AdkStreamCallback` that connects to an ADK endpoint.
|
|
42
|
+
*
|
|
43
|
+
* @example Proxy mode (with a Next.js API route)
|
|
44
|
+
* ```ts
|
|
45
|
+
* const stream = createAdkStream({ api: "/api/adk" });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example Direct mode (connecting to ADK server)
|
|
49
|
+
* ```ts
|
|
50
|
+
* const stream = createAdkStream({
|
|
51
|
+
* api: "http://localhost:8000",
|
|
52
|
+
* appName: "my-app",
|
|
53
|
+
* userId: "user-1",
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function createAdkStream(
|
|
58
|
+
options: CreateAdkStreamOptions,
|
|
59
|
+
): AdkStreamCallback {
|
|
60
|
+
const isDirect = options.appName != null;
|
|
61
|
+
|
|
62
|
+
return async function* (messages, config) {
|
|
63
|
+
const headers = await resolveHeaders(options.headers);
|
|
64
|
+
|
|
65
|
+
let url: string;
|
|
66
|
+
let body: unknown;
|
|
67
|
+
|
|
68
|
+
if (isDirect) {
|
|
69
|
+
// Direct mode: POST to ADK server's /run_sse
|
|
70
|
+
url = `${options.api}/run_sse`;
|
|
71
|
+
const { externalId } = await config.initialize();
|
|
72
|
+
body = {
|
|
73
|
+
appName: options.appName,
|
|
74
|
+
userId: options.userId,
|
|
75
|
+
sessionId: externalId,
|
|
76
|
+
newMessage: messagesToContent(messages),
|
|
77
|
+
streaming: true,
|
|
78
|
+
...(config.stateDelta != null && { stateDelta: config.stateDelta }),
|
|
79
|
+
};
|
|
80
|
+
} else {
|
|
81
|
+
// Proxy mode: POST in parseAdkRequest-compatible format
|
|
82
|
+
url = options.api;
|
|
83
|
+
body = messagesToProxyBody(messages, config);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const response = await fetch(url, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
89
|
+
body: JSON.stringify(body),
|
|
90
|
+
signal: config.abortSignal,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`ADK request failed: ${response.status} ${response.statusText}`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
yield* parseSSEResponse(response);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── Internal helpers ──
|
|
104
|
+
|
|
105
|
+
async function resolveHeaders(
|
|
106
|
+
headers:
|
|
107
|
+
| Record<string, string>
|
|
108
|
+
| (() => Record<string, string> | Promise<Record<string, string>>)
|
|
109
|
+
| undefined,
|
|
110
|
+
): Promise<Record<string, string>> {
|
|
111
|
+
if (!headers) return {};
|
|
112
|
+
if (typeof headers === "function") return await headers();
|
|
113
|
+
return headers;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Converts AdkMessage[] (new messages) into ADK Content format
|
|
118
|
+
* for the direct `/run_sse` endpoint.
|
|
119
|
+
*/
|
|
120
|
+
function messagesToContent(messages: AdkMessage[]): {
|
|
121
|
+
role: string;
|
|
122
|
+
parts: Array<Record<string, unknown>>;
|
|
123
|
+
} {
|
|
124
|
+
const parts: Array<Record<string, unknown>> = [];
|
|
125
|
+
|
|
126
|
+
for (const msg of messages) {
|
|
127
|
+
if (msg.type === "human") {
|
|
128
|
+
for (const part of contentToParts(msg.content)) {
|
|
129
|
+
parts.push(part);
|
|
130
|
+
}
|
|
131
|
+
} else if (msg.type === "tool") {
|
|
132
|
+
let response: unknown;
|
|
133
|
+
try {
|
|
134
|
+
response = JSON.parse(msg.content);
|
|
135
|
+
} catch {
|
|
136
|
+
response = msg.content;
|
|
137
|
+
}
|
|
138
|
+
parts.push({
|
|
139
|
+
functionResponse: {
|
|
140
|
+
name: msg.name,
|
|
141
|
+
id: msg.tool_call_id,
|
|
142
|
+
response,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (parts.length === 0) {
|
|
149
|
+
parts.push({ text: "" });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { role: "user", parts };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Converts AdkMessage[] into the proxy request body format
|
|
157
|
+
* (compatible with `parseAdkRequest`).
|
|
158
|
+
*/
|
|
159
|
+
function messagesToProxyBody(
|
|
160
|
+
messages: AdkMessage[],
|
|
161
|
+
config: {
|
|
162
|
+
runConfig?: unknown;
|
|
163
|
+
checkpointId?: string | undefined;
|
|
164
|
+
stateDelta?: Record<string, unknown> | undefined;
|
|
165
|
+
},
|
|
166
|
+
): Record<string, unknown> {
|
|
167
|
+
const body: Record<string, unknown> = {};
|
|
168
|
+
|
|
169
|
+
if (config.runConfig != null) body.runConfig = config.runConfig;
|
|
170
|
+
if (config.checkpointId != null) body.checkpointId = config.checkpointId;
|
|
171
|
+
if (config.stateDelta != null) body.stateDelta = config.stateDelta;
|
|
172
|
+
|
|
173
|
+
// Check if there's a tool result
|
|
174
|
+
const toolMsg = messages.find((m) => m.type === "tool");
|
|
175
|
+
if (toolMsg && toolMsg.type === "tool") {
|
|
176
|
+
// If there are also other messages (e.g. cancellations), send as parts
|
|
177
|
+
if (messages.length > 1) {
|
|
178
|
+
body.parts = messagesToContent(messages).parts;
|
|
179
|
+
return body;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let result: unknown;
|
|
183
|
+
try {
|
|
184
|
+
result = JSON.parse(toolMsg.content);
|
|
185
|
+
} catch {
|
|
186
|
+
result = toolMsg.content;
|
|
187
|
+
}
|
|
188
|
+
body.type = "tool-result";
|
|
189
|
+
body.toolCallId = toolMsg.tool_call_id;
|
|
190
|
+
body.toolName = toolMsg.name;
|
|
191
|
+
body.result = result;
|
|
192
|
+
body.isError = toolMsg.status === "error";
|
|
193
|
+
return body;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Human message(s) - possibly with cancellation tool results prepended
|
|
197
|
+
if (messages.length === 1 && messages[0]!.type === "human") {
|
|
198
|
+
const msg = messages[0]!;
|
|
199
|
+
if (typeof msg.content === "string") {
|
|
200
|
+
body.message = msg.content;
|
|
201
|
+
} else {
|
|
202
|
+
body.parts = contentToParts(msg.content);
|
|
203
|
+
}
|
|
204
|
+
return body;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Multiple messages (e.g. cancellations + human): send as parts array
|
|
208
|
+
body.parts = messagesToContent(messages).parts;
|
|
209
|
+
return body;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function contentToParts(
|
|
213
|
+
content: string | AdkMessageContentPart[],
|
|
214
|
+
): Array<Record<string, unknown>> {
|
|
215
|
+
if (typeof content === "string") return [{ text: content }];
|
|
216
|
+
return content.map((part) => {
|
|
217
|
+
switch (part.type) {
|
|
218
|
+
case "text":
|
|
219
|
+
return { text: part.text };
|
|
220
|
+
case "reasoning":
|
|
221
|
+
return { text: part.text, thought: true };
|
|
222
|
+
case "image":
|
|
223
|
+
return { inlineData: { mimeType: part.mimeType, data: part.data } };
|
|
224
|
+
case "image_url":
|
|
225
|
+
return { fileData: { fileUri: part.url } };
|
|
226
|
+
case "code":
|
|
227
|
+
return {
|
|
228
|
+
executableCode: { code: part.code, language: part.language },
|
|
229
|
+
};
|
|
230
|
+
case "code_result":
|
|
231
|
+
return {
|
|
232
|
+
codeExecutionResult: { output: part.output, outcome: part.outcome },
|
|
233
|
+
};
|
|
234
|
+
default:
|
|
235
|
+
return { text: "" };
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function* parseSSEResponse(response: Response): AsyncGenerator<AdkEvent> {
|
|
241
|
+
const reader = response.body!.getReader();
|
|
242
|
+
const decoder = new TextDecoder();
|
|
243
|
+
let buffer = "";
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
while (true) {
|
|
247
|
+
const { done, value } = await reader.read();
|
|
248
|
+
if (done) break;
|
|
249
|
+
|
|
250
|
+
buffer += decoder.decode(value, { stream: true });
|
|
251
|
+
const parts = buffer.split("\n\n");
|
|
252
|
+
buffer = parts.pop() ?? "";
|
|
253
|
+
|
|
254
|
+
for (const part of parts) {
|
|
255
|
+
for (const line of part.split("\n")) {
|
|
256
|
+
if (line.startsWith("data: ")) {
|
|
257
|
+
yield JSON.parse(line.slice(6)) as AdkEvent;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Handle remaining buffer
|
|
264
|
+
if (buffer.trim()) {
|
|
265
|
+
for (const line of buffer.split("\n")) {
|
|
266
|
+
if (line.startsWith("data: ")) {
|
|
267
|
+
yield JSON.parse(line.slice(6)) as AdkEvent;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} finally {
|
|
272
|
+
reader.releaseLock();
|
|
273
|
+
}
|
|
274
|
+
}
|