@assistant-ui/react-google-adk 0.0.30 → 0.0.31
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/dist/AdkClient.d.ts +2 -3
- package/dist/AdkClient.d.ts.map +1 -1
- package/dist/AdkEventAccumulator.d.ts +3 -5
- package/dist/AdkEventAccumulator.d.ts.map +1 -1
- package/dist/AdkEventAccumulator.js +63 -99
- package/dist/AdkEventAccumulator.js.map +1 -1
- package/dist/AdkSessionAdapter.d.ts +7 -4
- package/dist/AdkSessionAdapter.d.ts.map +1 -1
- package/dist/AdkSessionAdapter.js +7 -4
- package/dist/AdkSessionAdapter.js.map +1 -1
- package/dist/adkExtras.d.ts +1 -2
- package/dist/adkExtras.d.ts.map +1 -1
- package/dist/adkToolApproval.d.ts +6 -7
- package/dist/adkToolApproval.d.ts.map +1 -1
- package/dist/adkToolApproval.js +1 -3
- package/dist/adkToolApproval.js.map +1 -1
- package/dist/contentToParts.d.ts +1 -2
- package/dist/contentToParts.d.ts.map +1 -1
- package/dist/convertAdkMessages.d.ts +2 -3
- package/dist/convertAdkMessages.d.ts.map +1 -1
- package/dist/convertToAdkMessages.d.ts +5 -6
- package/dist/convertToAdkMessages.d.ts.map +1 -1
- package/dist/hooks.d.ts +15 -16
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +1 -1
- package/dist/normalizeAdkPart.d.ts +4 -0
- package/dist/normalizeAdkPart.d.ts.map +1 -0
- package/dist/normalizeAdkPart.js +31 -0
- package/dist/normalizeAdkPart.js.map +1 -0
- package/dist/parseAdkEvent.d.ts +1 -2
- package/dist/parseAdkEvent.d.ts.map +1 -1
- package/dist/sdkIdentity.d.ts +1 -2
- package/dist/sdkIdentity.d.ts.map +1 -1
- package/dist/sdkIdentity.js +1 -1
- package/dist/server/adkEventStream.d.ts +2 -3
- package/dist/server/adkEventStream.d.ts.map +1 -1
- package/dist/server/createAdkApiRoute.d.ts +2 -3
- package/dist/server/createAdkApiRoute.d.ts.map +1 -1
- package/dist/server/parseAdkRequest.d.ts +2 -3
- package/dist/server/parseAdkRequest.d.ts.map +1 -1
- package/dist/structuredEvents.d.ts +1 -2
- package/dist/structuredEvents.d.ts.map +1 -1
- package/dist/structuredEvents.js +1 -1
- package/dist/structuredEvents.js.map +1 -1
- package/dist/toAdkFunctionResponse.d.ts +2 -3
- package/dist/toAdkFunctionResponse.d.ts.map +1 -1
- package/dist/trimTrailingSlashes.d.ts +1 -2
- package/dist/trimTrailingSlashes.d.ts.map +1 -1
- package/dist/types.d.ts +22 -23
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/useAdkMessages.d.ts +2 -3
- package/dist/useAdkMessages.d.ts.map +1 -1
- package/dist/useAdkMessages.js +2 -0
- package/dist/useAdkMessages.js.map +1 -1
- package/dist/useAdkRuntime.d.ts +2 -3
- package/dist/useAdkRuntime.d.ts.map +1 -1
- package/dist/useAdkRuntime.js +1 -1
- package/package.json +10 -9
- package/src/AdkEventAccumulator.test.ts +312 -11
- package/src/AdkEventAccumulator.ts +68 -142
- package/src/AdkSessionAdapter.test.ts +267 -1
- package/src/AdkSessionAdapter.ts +24 -5
- package/src/adkToolApproval.test.ts +0 -4
- package/src/adkToolApproval.ts +1 -3
- package/src/normalizeAdkPart.ts +35 -0
- package/src/structuredEvents.test.ts +69 -0
- package/src/structuredEvents.ts +4 -1
- package/src/types.ts +1 -1
- package/src/useAdkMessages.test.ts +102 -3
- package/src/useAdkMessages.ts +2 -0
- package/src/useAdkRuntime.replacement.test.tsx +11 -7
|
@@ -246,6 +246,78 @@ describe("AdkEventAccumulator - function calls", () => {
|
|
|
246
246
|
);
|
|
247
247
|
expect(acc.getToolConfirmations()).toHaveLength(0);
|
|
248
248
|
});
|
|
249
|
+
|
|
250
|
+
const requestEvent = (name: string) =>
|
|
251
|
+
makeEvent({
|
|
252
|
+
author: "agent",
|
|
253
|
+
content: {
|
|
254
|
+
role: "model",
|
|
255
|
+
parts: [{ functionCall: { name, id: "rc-1" } }, { text: "still here" }],
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("keeps an args-less confirmation call answerable", () => {
|
|
260
|
+
const acc = new AdkEventAccumulator();
|
|
261
|
+
const msgs = acc.processEvent(requestEvent("adk_request_confirmation"));
|
|
262
|
+
|
|
263
|
+
expect(acc.getToolConfirmations()).toEqual([
|
|
264
|
+
{
|
|
265
|
+
toolCallId: "rc-1",
|
|
266
|
+
toolName: "",
|
|
267
|
+
args: {},
|
|
268
|
+
hint: "",
|
|
269
|
+
confirmed: false,
|
|
270
|
+
},
|
|
271
|
+
]);
|
|
272
|
+
expect((msgs[0] as AdkMessage & { type: "ai" }).tool_calls).toEqual([
|
|
273
|
+
{
|
|
274
|
+
id: "rc-1",
|
|
275
|
+
name: "adk_request_confirmation",
|
|
276
|
+
args: {},
|
|
277
|
+
argsText: "{}",
|
|
278
|
+
},
|
|
279
|
+
]);
|
|
280
|
+
expect(msgs[0]).toMatchObject({
|
|
281
|
+
content: [{ type: "text", text: "still here" }],
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("keeps an args-less credential call answerable", () => {
|
|
286
|
+
const acc = new AdkEventAccumulator();
|
|
287
|
+
const msgs = acc.processEvent(requestEvent("adk_request_credential"));
|
|
288
|
+
|
|
289
|
+
expect(acc.getAuthRequests()).toEqual([
|
|
290
|
+
{ toolCallId: "rc-1", authConfig: undefined },
|
|
291
|
+
]);
|
|
292
|
+
expect((msgs[0] as AdkMessage & { type: "ai" }).tool_calls).toEqual([
|
|
293
|
+
{
|
|
294
|
+
id: "rc-1",
|
|
295
|
+
name: "adk_request_credential",
|
|
296
|
+
args: {},
|
|
297
|
+
argsText: "{}",
|
|
298
|
+
},
|
|
299
|
+
]);
|
|
300
|
+
expect(msgs[0]).toMatchObject({
|
|
301
|
+
content: [{ type: "text", text: "still here" }],
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("defaults an ordinary call without args to empty args", () => {
|
|
306
|
+
const acc = new AdkEventAccumulator();
|
|
307
|
+
const msgs = acc.processEvent(
|
|
308
|
+
makeEvent({
|
|
309
|
+
author: "agent",
|
|
310
|
+
content: {
|
|
311
|
+
role: "model",
|
|
312
|
+
parts: [{ functionCall: { name: "search", id: "tc-1" } }],
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
expect((msgs[0] as AdkMessage & { type: "ai" }).tool_calls).toEqual([
|
|
318
|
+
{ id: "tc-1", name: "search", args: {}, argsText: "{}" },
|
|
319
|
+
]);
|
|
320
|
+
});
|
|
249
321
|
});
|
|
250
322
|
|
|
251
323
|
describe("AdkEventAccumulator - function responses", () => {
|
|
@@ -1102,21 +1174,176 @@ describe("AdkEventAccumulator - special function calls", () => {
|
|
|
1102
1174
|
});
|
|
1103
1175
|
});
|
|
1104
1176
|
|
|
1105
|
-
it(
|
|
1177
|
+
it.each([
|
|
1178
|
+
[
|
|
1179
|
+
"snake_case",
|
|
1180
|
+
{ function_call_id: "tc-original", auth_config: { type: "oauth2" } },
|
|
1181
|
+
],
|
|
1182
|
+
[
|
|
1183
|
+
"camelCase",
|
|
1184
|
+
{ functionCallId: "tc-original", authConfig: { type: "oauth2" } },
|
|
1185
|
+
],
|
|
1186
|
+
])(
|
|
1187
|
+
"records an auth request under its adk_request_credential call id (%s args)",
|
|
1188
|
+
(_, args) => {
|
|
1189
|
+
const acc = new AdkEventAccumulator();
|
|
1190
|
+
acc.processEvent(
|
|
1191
|
+
makeEvent({
|
|
1192
|
+
author: "agent",
|
|
1193
|
+
content: {
|
|
1194
|
+
role: "model",
|
|
1195
|
+
parts: [
|
|
1196
|
+
{
|
|
1197
|
+
functionCall: {
|
|
1198
|
+
name: "adk_request_credential",
|
|
1199
|
+
id: "cred-1",
|
|
1200
|
+
args,
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
],
|
|
1204
|
+
},
|
|
1205
|
+
}),
|
|
1206
|
+
);
|
|
1207
|
+
expect(acc.getAuthRequests()).toEqual([
|
|
1208
|
+
{ toolCallId: "cred-1", authConfig: { type: "oauth2" } },
|
|
1209
|
+
]);
|
|
1210
|
+
},
|
|
1211
|
+
);
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
describe.each([
|
|
1215
|
+
{
|
|
1216
|
+
name: "adk_request_confirmation",
|
|
1217
|
+
args: {
|
|
1218
|
+
originalFunctionCall: { id: "gated", name: "transfer", args: {} },
|
|
1219
|
+
toolConfirmation: { hint: "Transfer?" },
|
|
1220
|
+
},
|
|
1221
|
+
actions: {
|
|
1222
|
+
requestedToolConfirmations: { gated: { hint: "Transfer?" } },
|
|
1223
|
+
},
|
|
1224
|
+
reply: { confirmed: true },
|
|
1225
|
+
pending: (acc: AdkEventAccumulator) =>
|
|
1226
|
+
acc.getToolConfirmations().map((c) => c.toolCallId),
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
name: "adk_request_credential",
|
|
1230
|
+
args: { function_call_id: "gated", auth_config: { credentialKey: "k" } },
|
|
1231
|
+
actions: {
|
|
1232
|
+
requestedAuthConfigs: {
|
|
1233
|
+
gated: { credentialKey: "k" },
|
|
1234
|
+
"gated-2": { credentialKey: "k" },
|
|
1235
|
+
},
|
|
1236
|
+
},
|
|
1237
|
+
reply: { exchangedAuthCredential: { authType: "apiKey", apiKey: "key" } },
|
|
1238
|
+
pending: (acc: AdkEventAccumulator) =>
|
|
1239
|
+
acc.getAuthRequests().map((r) => r.toolCallId),
|
|
1240
|
+
},
|
|
1241
|
+
])("AdkEventAccumulator - pending $name requests", (request) => {
|
|
1242
|
+
const requestEvents: AdkEvent[] = [
|
|
1243
|
+
makeEvent({
|
|
1244
|
+
id: "call",
|
|
1245
|
+
author: "agent",
|
|
1246
|
+
content: {
|
|
1247
|
+
role: "model",
|
|
1248
|
+
parts: [
|
|
1249
|
+
{ functionCall: { name: "transfer", id: "gated", args: {} } },
|
|
1250
|
+
{ functionCall: { name: "transfer", id: "gated-2", args: {} } },
|
|
1251
|
+
],
|
|
1252
|
+
},
|
|
1253
|
+
}),
|
|
1254
|
+
makeEvent({
|
|
1255
|
+
id: "request",
|
|
1256
|
+
author: "agent",
|
|
1257
|
+
longRunningToolIds: ["req"],
|
|
1258
|
+
actions: request.actions,
|
|
1259
|
+
content: {
|
|
1260
|
+
role: "user",
|
|
1261
|
+
parts: [
|
|
1262
|
+
{
|
|
1263
|
+
functionCall: { name: request.name, id: "req", args: request.args },
|
|
1264
|
+
},
|
|
1265
|
+
],
|
|
1266
|
+
},
|
|
1267
|
+
}),
|
|
1268
|
+
makeEvent({
|
|
1269
|
+
id: "interim",
|
|
1270
|
+
author: "agent",
|
|
1271
|
+
actions: request.actions,
|
|
1272
|
+
content: {
|
|
1273
|
+
role: "user",
|
|
1274
|
+
parts: [
|
|
1275
|
+
{
|
|
1276
|
+
functionResponse: {
|
|
1277
|
+
id: "gated",
|
|
1278
|
+
name: "transfer",
|
|
1279
|
+
response: { status: "pending" },
|
|
1280
|
+
},
|
|
1281
|
+
},
|
|
1282
|
+
],
|
|
1283
|
+
},
|
|
1284
|
+
}),
|
|
1285
|
+
];
|
|
1286
|
+
const replyPart = {
|
|
1287
|
+
functionResponse: {
|
|
1288
|
+
id: "req",
|
|
1289
|
+
name: request.name,
|
|
1290
|
+
response: request.reply,
|
|
1291
|
+
},
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
it("lists the request once, under its call, until the user replies to that call", () => {
|
|
1295
|
+
const acc = new AdkEventAccumulator();
|
|
1296
|
+
for (const event of requestEvents) acc.processEvent(event);
|
|
1297
|
+
expect(request.pending(acc)).toEqual(["req"]);
|
|
1298
|
+
|
|
1299
|
+
acc.processEvent(
|
|
1300
|
+
makeEvent({
|
|
1301
|
+
id: "reply",
|
|
1302
|
+
author: "user",
|
|
1303
|
+
content: { role: "user", parts: [replyPart] },
|
|
1304
|
+
}),
|
|
1305
|
+
);
|
|
1306
|
+
expect(request.pending(acc)).toEqual([]);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
it("carries the request into an accumulator seeded with the thread, which an optimistic reply settles", () => {
|
|
1310
|
+
const replayed = new AdkEventAccumulator();
|
|
1311
|
+
let messages: AdkMessage[] = [];
|
|
1312
|
+
for (const event of requestEvents) messages = replayed.processEvent(event);
|
|
1313
|
+
|
|
1314
|
+
const acc = new AdkEventAccumulator(messages);
|
|
1315
|
+
expect(request.pending(acc)).toEqual(["req"]);
|
|
1316
|
+
|
|
1317
|
+
acc.processEvent(
|
|
1318
|
+
makeEvent({
|
|
1319
|
+
id: "optimistic",
|
|
1320
|
+
content: { role: "user", parts: [replyPart] },
|
|
1321
|
+
}),
|
|
1322
|
+
);
|
|
1323
|
+
expect(request.pending(acc)).toEqual([]);
|
|
1324
|
+
});
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
describe("AdkEventAccumulator - confirmation replies", () => {
|
|
1328
|
+
it.each([
|
|
1329
|
+
[["req"], "an unreadable reply", { response: "not json" }],
|
|
1330
|
+
[[], "a denial", { confirmed: false }],
|
|
1331
|
+
])("lists %j after %s", (pending, _, response) => {
|
|
1106
1332
|
const acc = new AdkEventAccumulator();
|
|
1107
1333
|
acc.processEvent(
|
|
1108
1334
|
makeEvent({
|
|
1335
|
+
id: "request",
|
|
1109
1336
|
author: "agent",
|
|
1337
|
+
longRunningToolIds: ["req"],
|
|
1110
1338
|
content: {
|
|
1111
1339
|
role: "model",
|
|
1112
1340
|
parts: [
|
|
1113
1341
|
{
|
|
1114
1342
|
functionCall: {
|
|
1115
|
-
name: "
|
|
1116
|
-
id: "
|
|
1343
|
+
name: "adk_request_confirmation",
|
|
1344
|
+
id: "req",
|
|
1117
1345
|
args: {
|
|
1118
|
-
|
|
1119
|
-
auth_config: { type: "oauth2" },
|
|
1346
|
+
originalFunctionCall: { id: "gated", name: "transfer" },
|
|
1120
1347
|
},
|
|
1121
1348
|
},
|
|
1122
1349
|
},
|
|
@@ -1124,12 +1351,27 @@ describe("AdkEventAccumulator - special function calls", () => {
|
|
|
1124
1351
|
},
|
|
1125
1352
|
}),
|
|
1126
1353
|
);
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1354
|
+
acc.processEvent(
|
|
1355
|
+
makeEvent({
|
|
1356
|
+
id: "reply",
|
|
1357
|
+
author: "user",
|
|
1358
|
+
content: {
|
|
1359
|
+
role: "user",
|
|
1360
|
+
parts: [
|
|
1361
|
+
{
|
|
1362
|
+
functionResponse: {
|
|
1363
|
+
id: "req",
|
|
1364
|
+
name: "adk_request_confirmation",
|
|
1365
|
+
response,
|
|
1366
|
+
},
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
1369
|
+
},
|
|
1370
|
+
}),
|
|
1371
|
+
);
|
|
1372
|
+
expect(acc.getToolConfirmations().map((c) => c.toolCallId)).toEqual(
|
|
1373
|
+
pending,
|
|
1374
|
+
);
|
|
1133
1375
|
});
|
|
1134
1376
|
});
|
|
1135
1377
|
|
|
@@ -1691,6 +1933,65 @@ describe("AdkEventAccumulator - user message handling", () => {
|
|
|
1691
1933
|
});
|
|
1692
1934
|
});
|
|
1693
1935
|
|
|
1936
|
+
it.each([
|
|
1937
|
+
{
|
|
1938
|
+
name: "inlineData without mimeType",
|
|
1939
|
+
part: { inlineData: { data: "abc123" } },
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
name: "inlineData without data",
|
|
1943
|
+
part: { inlineData: { mimeType: "image/png" } },
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
name: "fileData without fileUri",
|
|
1947
|
+
part: { fileData: { mimeType: "image/png" } },
|
|
1948
|
+
},
|
|
1949
|
+
])("skips malformed $name while preserving valid content", ({ part }) => {
|
|
1950
|
+
const acc = new AdkEventAccumulator();
|
|
1951
|
+
const msgs = acc.processEvent(
|
|
1952
|
+
makeEvent({
|
|
1953
|
+
author: "agent",
|
|
1954
|
+
content: {
|
|
1955
|
+
role: "model",
|
|
1956
|
+
parts: [part as any, { text: "still here" }],
|
|
1957
|
+
},
|
|
1958
|
+
}),
|
|
1959
|
+
);
|
|
1960
|
+
|
|
1961
|
+
expect(msgs).toMatchObject([
|
|
1962
|
+
{ type: "ai", content: [{ type: "text", text: "still here" }] },
|
|
1963
|
+
]);
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
it("skips malformed user media without creating an empty human message", () => {
|
|
1967
|
+
const acc = new AdkEventAccumulator();
|
|
1968
|
+
const malformedMedia = [
|
|
1969
|
+
{ inlineData: { data: "abc123" } },
|
|
1970
|
+
{ fileData: { mimeType: "application/pdf" } },
|
|
1971
|
+
] as any[];
|
|
1972
|
+
|
|
1973
|
+
expect(
|
|
1974
|
+
acc.processEvent(
|
|
1975
|
+
makeEvent({
|
|
1976
|
+
author: "user",
|
|
1977
|
+
content: { role: "user", parts: malformedMedia },
|
|
1978
|
+
}),
|
|
1979
|
+
),
|
|
1980
|
+
).toEqual([]);
|
|
1981
|
+
|
|
1982
|
+
const msgs = acc.processEvent(
|
|
1983
|
+
makeEvent({
|
|
1984
|
+
author: "user",
|
|
1985
|
+
content: {
|
|
1986
|
+
role: "user",
|
|
1987
|
+
parts: [...malformedMedia, { text: "still here" }],
|
|
1988
|
+
},
|
|
1989
|
+
}),
|
|
1990
|
+
);
|
|
1991
|
+
|
|
1992
|
+
expect(msgs).toMatchObject([{ type: "human", content: "still here" }]);
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1694
1995
|
it("tool result events (no author, role:'user') still create tool messages", () => {
|
|
1695
1996
|
// Regression: the user-author check must not hijack tool events.
|
|
1696
1997
|
// messageToEvent for `type:'tool'` emits events without `author`,
|
|
@@ -12,6 +12,8 @@ import type {
|
|
|
12
12
|
AdkMessageMetadata,
|
|
13
13
|
} from "./types";
|
|
14
14
|
import type { ReadonlyJSONObject } from "assistant-stream/utils";
|
|
15
|
+
import { normalizeAdkPart } from "./normalizeAdkPart";
|
|
16
|
+
import { projectAdkToolApprovals } from "./adkToolApproval";
|
|
15
17
|
import { isAdkFunctionError } from "./toAdkFunctionResponse";
|
|
16
18
|
|
|
17
19
|
type InProgressMessage = AdkMessage & { type: "ai" };
|
|
@@ -99,56 +101,27 @@ const finishReasonToStatus = (
|
|
|
99
101
|
return { type: "complete", reason: "stop" };
|
|
100
102
|
};
|
|
101
103
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
): AdkMessageContentPart =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
mimeType
|
|
104
|
+
const mediaToContentPart = ({
|
|
105
|
+
inlineData,
|
|
106
|
+
fileData,
|
|
107
|
+
}: Record<string, unknown>): AdkMessageContentPart | undefined => {
|
|
108
|
+
if (isRecord(inlineData)) {
|
|
109
|
+
const { mimeType, data } = inlineData;
|
|
110
|
+
if (typeof mimeType !== "string" || typeof data !== "string") return;
|
|
111
|
+
return mimeType.startsWith("image/")
|
|
112
|
+
? { type: "image", mimeType, data }
|
|
113
|
+
: { type: "file", mimeType, data };
|
|
114
|
+
}
|
|
115
|
+
if (!isRecord(fileData)) return;
|
|
116
|
+
const { fileUri, mimeType } = fileData;
|
|
117
|
+
if (typeof fileUri !== "string") return;
|
|
118
|
+
return typeof mimeType !== "string" || mimeType.startsWith("image/")
|
|
115
119
|
? { type: "image_url", url: fileUri }
|
|
116
120
|
: { type: "file_url", url: fileUri, mimeType };
|
|
121
|
+
};
|
|
117
122
|
|
|
118
123
|
// ── Snake_case normalization ──
|
|
119
124
|
|
|
120
|
-
const normalizeEventPart = (part: AdkEventPart): AdkEventPart => {
|
|
121
|
-
const p = part as Record<string, unknown>;
|
|
122
|
-
const result: Record<string, unknown> = { ...p };
|
|
123
|
-
if ("function_call" in p && !("functionCall" in p))
|
|
124
|
-
result.functionCall = p.function_call;
|
|
125
|
-
if ("function_response" in p && !("functionResponse" in p))
|
|
126
|
-
result.functionResponse = p.function_response;
|
|
127
|
-
if ("inline_data" in p && !("inlineData" in p))
|
|
128
|
-
result.inlineData = p.inline_data;
|
|
129
|
-
if ("file_data" in p && !("fileData" in p)) result.fileData = p.file_data;
|
|
130
|
-
if (isRecord(result.inlineData)) {
|
|
131
|
-
const data = result.inlineData;
|
|
132
|
-
if ("mime_type" in data && !("mimeType" in data))
|
|
133
|
-
result.inlineData = { ...data, mimeType: data.mime_type };
|
|
134
|
-
}
|
|
135
|
-
if (isRecord(result.fileData)) {
|
|
136
|
-
const data = result.fileData;
|
|
137
|
-
result.fileData = {
|
|
138
|
-
...data,
|
|
139
|
-
...("mime_type" in data &&
|
|
140
|
-
!("mimeType" in data) && { mimeType: data.mime_type }),
|
|
141
|
-
...("file_uri" in data &&
|
|
142
|
-
!("fileUri" in data) && { fileUri: data.file_uri }),
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
if ("executable_code" in p && !("executableCode" in p))
|
|
146
|
-
result.executableCode = p.executable_code;
|
|
147
|
-
if ("code_execution_result" in p && !("codeExecutionResult" in p))
|
|
148
|
-
result.codeExecutionResult = p.code_execution_result;
|
|
149
|
-
return result as AdkEventPart;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
125
|
const normalizeEvent = (event: AdkEvent): AdkEvent => {
|
|
153
126
|
const e = event as Record<string, unknown>;
|
|
154
127
|
const result: Record<string, unknown> = { ...e };
|
|
@@ -195,8 +168,8 @@ const normalizeEvent = (event: AdkEvent): AdkEvent => {
|
|
|
195
168
|
|
|
196
169
|
if (result.content && (result.content as Record<string, unknown>).parts) {
|
|
197
170
|
const content = result.content as Record<string, unknown>;
|
|
198
|
-
const parts = content.parts as
|
|
199
|
-
result.content = { ...content, parts: parts.map(
|
|
171
|
+
const parts = content.parts as Record<string, unknown>[];
|
|
172
|
+
result.content = { ...content, parts: parts.map(normalizeAdkPart) };
|
|
200
173
|
}
|
|
201
174
|
|
|
202
175
|
return result as AdkEvent;
|
|
@@ -220,8 +193,6 @@ export class AdkEventAccumulator {
|
|
|
220
193
|
} = {};
|
|
221
194
|
private lastTransferToAgent: string | undefined;
|
|
222
195
|
private pendingLongRunningToolIds = new Set<string>();
|
|
223
|
-
private toolConfirmations: AdkToolConfirmation[] = [];
|
|
224
|
-
private authRequests: AdkAuthRequest[] = [];
|
|
225
196
|
private escalated = false;
|
|
226
197
|
private messageMetadataMap = new Map<string, AdkMessageMetadata>();
|
|
227
198
|
// How many assistant messages each event has opened, so a replay of that
|
|
@@ -273,32 +244,6 @@ export class AdkEventAccumulator {
|
|
|
273
244
|
}
|
|
274
245
|
}
|
|
275
246
|
|
|
276
|
-
// Track tool confirmations from actions
|
|
277
|
-
if (event.actions?.requestedToolConfirmations) {
|
|
278
|
-
for (const [tcId, conf] of Object.entries(
|
|
279
|
-
event.actions.requestedToolConfirmations,
|
|
280
|
-
)) {
|
|
281
|
-
const c = conf as Record<string, unknown>;
|
|
282
|
-
this.toolConfirmations.push({
|
|
283
|
-
toolCallId: tcId,
|
|
284
|
-
toolName: "",
|
|
285
|
-
args: {},
|
|
286
|
-
hint: (c.hint as string) ?? "",
|
|
287
|
-
confirmed: false,
|
|
288
|
-
payload: c.payload,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Track auth requests from actions
|
|
294
|
-
if (event.actions?.requestedAuthConfigs) {
|
|
295
|
-
for (const [tcId, authConf] of Object.entries(
|
|
296
|
-
event.actions.requestedAuthConfigs,
|
|
297
|
-
)) {
|
|
298
|
-
this.authRequests.push({ toolCallId: tcId, authConfig: authConf });
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
247
|
// Track agent info
|
|
303
248
|
if (event.author && event.author !== "user") {
|
|
304
249
|
this.lastAgentInfo = {
|
|
@@ -361,14 +306,9 @@ export class AdkEventAccumulator {
|
|
|
361
306
|
for (const [index, part] of parts.entries()) {
|
|
362
307
|
if (part.text != null && !part.thought) {
|
|
363
308
|
humanParts.push({ type: "text", text: part.text });
|
|
364
|
-
} else if (part.inlineData) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
);
|
|
368
|
-
} else if (part.fileData) {
|
|
369
|
-
humanParts.push(
|
|
370
|
-
fileDataToPart(part.fileData.fileUri, part.fileData.mimeType),
|
|
371
|
-
);
|
|
309
|
+
} else if (part.inlineData || part.fileData) {
|
|
310
|
+
const mediaPart = mediaToContentPart(part);
|
|
311
|
+
if (mediaPart) humanParts.push(mediaPart);
|
|
372
312
|
} else if (part.functionResponse?.id) {
|
|
373
313
|
// ADK records tool confirmation and other client-supplied tool
|
|
374
314
|
// results as user-authored function responses, and its request
|
|
@@ -462,45 +402,6 @@ export class AdkEventAccumulator {
|
|
|
462
402
|
event: AdkEvent,
|
|
463
403
|
partIndex: number,
|
|
464
404
|
): void {
|
|
465
|
-
// Detect special ADK function calls
|
|
466
|
-
if (part.functionCall && !event.partial) {
|
|
467
|
-
const name = part.functionCall.name;
|
|
468
|
-
|
|
469
|
-
// Tool confirmation request
|
|
470
|
-
if (name === ADK_REQUEST_CONFIRMATION) {
|
|
471
|
-
const callArgs = part.functionCall.args;
|
|
472
|
-
// ADK JS: args keys are "originalFunctionCall" and "toolConfirmation"
|
|
473
|
-
// ADK Python: args keys are "original_function_call" and "tool_confirmation"
|
|
474
|
-
const original =
|
|
475
|
-
(callArgs.originalFunctionCall as Record<string, unknown>) ??
|
|
476
|
-
(callArgs.original_function_call as Record<string, unknown>);
|
|
477
|
-
const conf =
|
|
478
|
-
(callArgs.toolConfirmation as Record<string, unknown>) ??
|
|
479
|
-
(callArgs.tool_confirmation as Record<string, unknown>);
|
|
480
|
-
this.toolConfirmations.push({
|
|
481
|
-
toolCallId: part.functionCall.id ?? "",
|
|
482
|
-
toolName: (original?.name as string) ?? "",
|
|
483
|
-
args: (original?.args as Record<string, unknown>) ?? {},
|
|
484
|
-
hint: (conf?.hint as string) ?? "",
|
|
485
|
-
confirmed: false,
|
|
486
|
-
payload: conf?.payload,
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Auth credential request
|
|
491
|
-
if (name === ADK_REQUEST_CREDENTIAL) {
|
|
492
|
-
const credArgs = part.functionCall.args;
|
|
493
|
-
// ADK JS: args keys are "function_call_id" and "auth_config"
|
|
494
|
-
const originalToolCallId =
|
|
495
|
-
(credArgs.function_call_id as string) ?? part.functionCall.id ?? "";
|
|
496
|
-
const authConfig = credArgs.auth_config ?? credArgs;
|
|
497
|
-
this.authRequests.push({
|
|
498
|
-
toolCallId: originalToolCallId,
|
|
499
|
-
authConfig,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
405
|
// Text with thought=true → reasoning (accumulated)
|
|
505
406
|
if (part.text != null && part.thought) {
|
|
506
407
|
const msg = this.getOrCreateAiMessage(event);
|
|
@@ -540,8 +441,8 @@ export class AdkEventAccumulator {
|
|
|
540
441
|
const toolCall: AdkToolCall = {
|
|
541
442
|
id: part.functionCall.id ?? generateId(),
|
|
542
443
|
name: part.functionCall.name,
|
|
543
|
-
args: part.functionCall.args as ReadonlyJSONObject,
|
|
544
|
-
argsText: JSON.stringify(part.functionCall.args),
|
|
444
|
+
args: (part.functionCall.args ?? {}) as ReadonlyJSONObject,
|
|
445
|
+
argsText: JSON.stringify(part.functionCall.args ?? {}),
|
|
545
446
|
};
|
|
546
447
|
const existing = [...(msg.tool_calls ?? [])];
|
|
547
448
|
const idx = existing.findIndex((tc) => tc.id === toolCall.id);
|
|
@@ -598,22 +499,10 @@ export class AdkEventAccumulator {
|
|
|
598
499
|
return;
|
|
599
500
|
}
|
|
600
501
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
inlineDataToPart(part.inlineData.mimeType, part.inlineData.data),
|
|
606
|
-
);
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if (part.fileData) {
|
|
611
|
-
const msg = this.getOrCreateAiMessage(event);
|
|
612
|
-
this.appendContent(
|
|
613
|
-
msg,
|
|
614
|
-
fileDataToPart(part.fileData.fileUri, part.fileData.mimeType),
|
|
615
|
-
);
|
|
616
|
-
}
|
|
502
|
+
const mediaPart = mediaToContentPart(part);
|
|
503
|
+
if (!mediaPart) return;
|
|
504
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
505
|
+
this.appendContent(msg, mediaPart);
|
|
617
506
|
}
|
|
618
507
|
|
|
619
508
|
private trackMessageMetadata(event: AdkEvent): void {
|
|
@@ -708,6 +597,18 @@ export class AdkEventAccumulator {
|
|
|
708
597
|
this.currentMessageId = null;
|
|
709
598
|
}
|
|
710
599
|
|
|
600
|
+
// ADK resumes a confirmation or credential request only on a reply that quotes its synthetic call's id, so those calls are the whole record; requestedToolConfirmations and requestedAuthConfigs key the gated call instead, which no reply answers.
|
|
601
|
+
private getRequestCalls(name: string): AdkToolCall[] {
|
|
602
|
+
const calls = new Map<string, AdkToolCall>();
|
|
603
|
+
for (const msg of this.messagesMap.values()) {
|
|
604
|
+
if (msg.type !== "ai") continue;
|
|
605
|
+
for (const call of msg.tool_calls ?? []) {
|
|
606
|
+
if (call.name === name && !calls.has(call.id)) calls.set(call.id, call);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return [...calls.values()];
|
|
610
|
+
}
|
|
611
|
+
|
|
711
612
|
getMessages(): AdkMessage[] {
|
|
712
613
|
return [...this.messagesMap.values()];
|
|
713
614
|
}
|
|
@@ -733,11 +634,36 @@ export class AdkEventAccumulator {
|
|
|
733
634
|
}
|
|
734
635
|
|
|
735
636
|
getToolConfirmations(): AdkToolConfirmation[] {
|
|
736
|
-
|
|
637
|
+
const { approvals } = projectAdkToolApprovals(this.getMessages());
|
|
638
|
+
return this.getRequestCalls(ADK_REQUEST_CONFIRMATION)
|
|
639
|
+
.filter(({ id }) => approvals.get(id)?.approved === undefined)
|
|
640
|
+
.map(({ id, args = {} }) => {
|
|
641
|
+
const original = (args.originalFunctionCall ??
|
|
642
|
+
args.original_function_call) as Record<string, unknown> | undefined;
|
|
643
|
+
const confirmation = (args.toolConfirmation ??
|
|
644
|
+
args.tool_confirmation) as Record<string, unknown> | undefined;
|
|
645
|
+
return {
|
|
646
|
+
toolCallId: id,
|
|
647
|
+
toolName: (original?.name as string) ?? "",
|
|
648
|
+
args: (original?.args as Record<string, unknown>) ?? {},
|
|
649
|
+
hint: (confirmation?.hint as string) ?? "",
|
|
650
|
+
confirmed: false,
|
|
651
|
+
payload: confirmation?.payload,
|
|
652
|
+
};
|
|
653
|
+
});
|
|
737
654
|
}
|
|
738
655
|
|
|
739
656
|
getAuthRequests(): AdkAuthRequest[] {
|
|
740
|
-
|
|
657
|
+
const answered = new Set<string>();
|
|
658
|
+
for (const msg of this.messagesMap.values()) {
|
|
659
|
+
if (msg.type === "tool") answered.add(msg.tool_call_id);
|
|
660
|
+
}
|
|
661
|
+
return this.getRequestCalls(ADK_REQUEST_CREDENTIAL)
|
|
662
|
+
.filter(({ id }) => !answered.has(id))
|
|
663
|
+
.map(({ id, args = {} }) => ({
|
|
664
|
+
toolCallId: id,
|
|
665
|
+
authConfig: args.auth_config ?? args.authConfig,
|
|
666
|
+
}));
|
|
741
667
|
}
|
|
742
668
|
|
|
743
669
|
isEscalated(): boolean {
|