@assistant-ui/react-google-adk 0.0.5 → 0.0.7
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/AdkEventAccumulator.d.ts.map +1 -1
- package/dist/AdkEventAccumulator.js +48 -4
- package/dist/AdkEventAccumulator.js.map +1 -1
- package/dist/AdkSessionAdapter.js +2 -2
- package/dist/AdkSessionAdapter.js.map +1 -1
- package/dist/convertAdkMessages.d.ts +1 -1
- package/dist/convertAdkMessages.d.ts.map +1 -1
- package/dist/hooks.d.ts +4 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +18 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/useAdkMessages.d.ts +1 -0
- package/dist/useAdkMessages.d.ts.map +1 -1
- package/dist/useAdkMessages.js +13 -0
- package/dist/useAdkMessages.js.map +1 -1
- package/dist/useAdkRuntime.d.ts.map +1 -1
- package/dist/useAdkRuntime.js +46 -15
- package/dist/useAdkRuntime.js.map +1 -1
- package/package.json +8 -8
- package/src/AdkEventAccumulator.test.ts +521 -2
- package/src/AdkEventAccumulator.ts +49 -4
- package/src/AdkSessionAdapter.test.ts +1 -1
- package/src/AdkSessionAdapter.ts +3 -3
- package/src/convertAdkMessages.ts +1 -1
- package/src/hooks.ts +28 -1
- package/src/index.ts +1 -0
- package/src/useAdkMessages.ts +17 -0
- package/src/useAdkRuntime.test.ts +138 -0
- package/src/useAdkRuntime.ts +52 -18
|
@@ -166,7 +166,7 @@ export class AdkEventAccumulator {
|
|
|
166
166
|
branch?: string | undefined;
|
|
167
167
|
} = {};
|
|
168
168
|
private lastTransferToAgent: string | undefined;
|
|
169
|
-
private pendingLongRunningToolIds
|
|
169
|
+
private pendingLongRunningToolIds = new Set<string>();
|
|
170
170
|
private toolConfirmations: AdkToolConfirmation[] = [];
|
|
171
171
|
private authRequests: AdkAuthRequest[] = [];
|
|
172
172
|
private escalated = false;
|
|
@@ -202,9 +202,13 @@ export class AdkEventAccumulator {
|
|
|
202
202
|
this.lastTransferToAgent = event.actions.transferToAgent;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
// Track long-running tool IDs
|
|
205
|
+
// Track long-running tool IDs. Accumulate across events so that multiple
|
|
206
|
+
// HITL interrupts emitted in the same turn are all tracked — a
|
|
207
|
+
// single-event replacement would drop earlier ids.
|
|
206
208
|
if (event.longRunningToolIds?.length) {
|
|
207
|
-
|
|
209
|
+
for (const id of event.longRunningToolIds) {
|
|
210
|
+
this.pendingLongRunningToolIds.add(id);
|
|
211
|
+
}
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
// Track tool confirmations from actions
|
|
@@ -284,6 +288,41 @@ export class AdkEventAccumulator {
|
|
|
284
288
|
return this.getMessages();
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
// User-authored events → create human message, not AI.
|
|
292
|
+
// Without this, user events fall through to processPart →
|
|
293
|
+
// getOrCreateAiMessage, producing type:"ai" messages that
|
|
294
|
+
// convertAdkMessage maps to role:"assistant".
|
|
295
|
+
if (event.author === "user") {
|
|
296
|
+
this.finalizeCurrentMessage();
|
|
297
|
+
const humanParts: AdkMessageContentPart[] = [];
|
|
298
|
+
for (const part of parts) {
|
|
299
|
+
if (part.text != null && !part.thought) {
|
|
300
|
+
humanParts.push({ type: "text", text: part.text });
|
|
301
|
+
} else if (part.inlineData) {
|
|
302
|
+
humanParts.push({
|
|
303
|
+
type: "image",
|
|
304
|
+
mimeType: part.inlineData.mimeType,
|
|
305
|
+
data: part.inlineData.data,
|
|
306
|
+
});
|
|
307
|
+
} else if (part.fileData) {
|
|
308
|
+
humanParts.push({
|
|
309
|
+
type: "image_url",
|
|
310
|
+
url: part.fileData.fileUri,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (humanParts.length > 0) {
|
|
315
|
+
const id = event.id ?? uuidv4();
|
|
316
|
+
const first = humanParts[0];
|
|
317
|
+
const content: string | AdkMessageContentPart[] =
|
|
318
|
+
humanParts.length === 1 && first?.type === "text"
|
|
319
|
+
? first.text
|
|
320
|
+
: humanParts;
|
|
321
|
+
this.messagesMap.set(id, { id, type: "human", content });
|
|
322
|
+
}
|
|
323
|
+
return this.getMessages();
|
|
324
|
+
}
|
|
325
|
+
|
|
287
326
|
// If author changed, finalize previous message
|
|
288
327
|
if (this.currentMessageId && event.author && event.author !== "user") {
|
|
289
328
|
const current = this.messagesMap.get(this.currentMessageId);
|
|
@@ -302,7 +341,13 @@ export class AdkEventAccumulator {
|
|
|
302
341
|
// Check isFinalResponse (can be true even for partial events via skipSummarization/longRunningToolIds)
|
|
303
342
|
if (isFinalResponse(event) && this.currentMessageId) {
|
|
304
343
|
const msg = this.messagesMap.get(this.currentMessageId);
|
|
305
|
-
|
|
344
|
+
// Skip manual "complete" when longRunningToolIds is the sole reason for
|
|
345
|
+
// isFinalResponse — let auto-status apply requires-action for pending tool calls.
|
|
346
|
+
const isHitlOnly =
|
|
347
|
+
event.longRunningToolIds &&
|
|
348
|
+
event.longRunningToolIds.length > 0 &&
|
|
349
|
+
!event.actions?.skipSummarization;
|
|
350
|
+
if (msg && msg.type === "ai" && !msg.status && !isHitlOnly) {
|
|
306
351
|
const status = finishReasonToStatus(event.finishReason);
|
|
307
352
|
const updated: InProgressMessage = {
|
|
308
353
|
...msg,
|
|
@@ -126,7 +126,7 @@ describe("createAdkSessionAdapter - initialize", () => {
|
|
|
126
126
|
});
|
|
127
127
|
expect(JSON.parse(init?.body as string)).toEqual({});
|
|
128
128
|
expect(result).toMatchObject({
|
|
129
|
-
remoteId: "
|
|
129
|
+
remoteId: "new-session-1",
|
|
130
130
|
externalId: "new-session-1",
|
|
131
131
|
});
|
|
132
132
|
});
|
package/src/AdkSessionAdapter.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AssistantStream, AssistantStreamChunk } from "assistant-stream";
|
|
1
|
+
import type { AssistantStream, AssistantStreamChunk } from "assistant-stream";
|
|
2
2
|
import type {
|
|
3
3
|
RemoteThreadInitializeResponse,
|
|
4
4
|
RemoteThreadListAdapter,
|
|
@@ -112,7 +112,7 @@ export function createAdkSessionAdapter(
|
|
|
112
112
|
},
|
|
113
113
|
|
|
114
114
|
async initialize(
|
|
115
|
-
|
|
115
|
+
_threadId: string,
|
|
116
116
|
): Promise<RemoteThreadInitializeResponse> {
|
|
117
117
|
const headers = await getHeaders();
|
|
118
118
|
const res = await fetch(baseUrl, {
|
|
@@ -124,7 +124,7 @@ export function createAdkSessionAdapter(
|
|
|
124
124
|
throw new Error(`Failed to create session: ${res.status}`);
|
|
125
125
|
}
|
|
126
126
|
const session = (await res.json()) as { id: string };
|
|
127
|
-
return { remoteId:
|
|
127
|
+
return { remoteId: session.id, externalId: session.id };
|
|
128
128
|
},
|
|
129
129
|
|
|
130
130
|
async delete(remoteId: string): Promise<void> {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { ToolCallMessagePart } from "@assistant-ui/core";
|
|
4
|
-
import { useExternalMessageConverter } from "@assistant-ui/core/react";
|
|
4
|
+
import type { useExternalMessageConverter } from "@assistant-ui/core/react";
|
|
5
5
|
import type { AdkMessage, AdkMessageContentPart } from "./types";
|
|
6
6
|
|
|
7
7
|
type ContentPart =
|
package/src/hooks.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
5
6
|
import type {
|
|
6
7
|
AdkMessage,
|
|
7
8
|
AdkSendMessageConfig,
|
|
@@ -126,7 +127,11 @@ export const useAdkMessageMetadata = () => {
|
|
|
126
127
|
/** Returns a function to confirm or deny a pending tool confirmation. */
|
|
127
128
|
export const useAdkConfirmTool = () => {
|
|
128
129
|
const aui = useAui();
|
|
129
|
-
return (
|
|
130
|
+
return (
|
|
131
|
+
toolCallId: string,
|
|
132
|
+
confirmed: boolean,
|
|
133
|
+
payload?: ReadonlyJSONValue,
|
|
134
|
+
) => {
|
|
130
135
|
const extras = aui.thread().getState().extras;
|
|
131
136
|
const { send } = asAdkRuntimeExtras(extras);
|
|
132
137
|
return send(
|
|
@@ -170,6 +175,28 @@ export const useAdkSubmitAuth = () => {
|
|
|
170
175
|
};
|
|
171
176
|
};
|
|
172
177
|
|
|
178
|
+
/** Returns a function to submit the user's answer for a pending `adk_request_input` HITL interrupt. */
|
|
179
|
+
export const useAdkSubmitInput = () => {
|
|
180
|
+
const aui = useAui();
|
|
181
|
+
return (toolCallId: string, result: ReadonlyJSONValue) => {
|
|
182
|
+
const extras = aui.thread().getState().extras;
|
|
183
|
+
const { send } = asAdkRuntimeExtras(extras);
|
|
184
|
+
return send(
|
|
185
|
+
[
|
|
186
|
+
{
|
|
187
|
+
id: uuidv4(),
|
|
188
|
+
type: "tool",
|
|
189
|
+
tool_call_id: toolCallId,
|
|
190
|
+
name: "adk_request_input",
|
|
191
|
+
content: JSON.stringify({ result }),
|
|
192
|
+
status: "success",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
{},
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
|
|
173
200
|
// ── State prefix helpers ──
|
|
174
201
|
|
|
175
202
|
const APP_PREFIX = "app:";
|
package/src/index.ts
CHANGED
package/src/useAdkMessages.ts
CHANGED
|
@@ -64,6 +64,22 @@ export const useAdkMessages = ({
|
|
|
64
64
|
_setMessages(msgs);
|
|
65
65
|
}, []);
|
|
66
66
|
|
|
67
|
+
// Replace the message list AND reset derived per-turn HITL state.
|
|
68
|
+
// Used by truncation paths (edit, reload, load) so that stale interrupt
|
|
69
|
+
// markers and per-message metadata from the removed messages don't leak
|
|
70
|
+
// into the next turn.
|
|
71
|
+
const replaceMessages = useCallback(
|
|
72
|
+
(msgs: AdkMessage[]) => {
|
|
73
|
+
setMessagesImmediate(msgs);
|
|
74
|
+
setLongRunningToolIds([]);
|
|
75
|
+
setToolConfirmations([]);
|
|
76
|
+
setAuthRequests([]);
|
|
77
|
+
setEscalated(false);
|
|
78
|
+
setMessageMetadata(new Map());
|
|
79
|
+
},
|
|
80
|
+
[setMessagesImmediate],
|
|
81
|
+
);
|
|
82
|
+
|
|
67
83
|
const abortControllerRef = useRef<AbortController | null>(null);
|
|
68
84
|
|
|
69
85
|
const { onError, onCustomEvent, onAgentTransfer } = useMemo(
|
|
@@ -180,6 +196,7 @@ export const useAdkMessages = ({
|
|
|
180
196
|
sendMessage,
|
|
181
197
|
cancel,
|
|
182
198
|
setMessages: setMessagesImmediate,
|
|
199
|
+
replaceMessages,
|
|
183
200
|
};
|
|
184
201
|
};
|
|
185
202
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { getPendingCancellations, getPendingToolCalls } from "./useAdkRuntime";
|
|
3
|
+
import type { AdkMessage } from "./types";
|
|
4
|
+
|
|
5
|
+
const aiWithToolCalls = (
|
|
6
|
+
id: string,
|
|
7
|
+
toolCalls: Array<{ id: string; name: string }>,
|
|
8
|
+
): AdkMessage => ({
|
|
9
|
+
id,
|
|
10
|
+
type: "ai",
|
|
11
|
+
content: [],
|
|
12
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
13
|
+
id: tc.id,
|
|
14
|
+
name: tc.name,
|
|
15
|
+
args: {},
|
|
16
|
+
argsText: "{}",
|
|
17
|
+
})),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const toolResponse = (
|
|
21
|
+
id: string,
|
|
22
|
+
toolCallId: string,
|
|
23
|
+
name: string,
|
|
24
|
+
): AdkMessage => ({
|
|
25
|
+
id,
|
|
26
|
+
type: "tool",
|
|
27
|
+
tool_call_id: toolCallId,
|
|
28
|
+
name,
|
|
29
|
+
content: JSON.stringify({ ok: true }),
|
|
30
|
+
status: "success",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("getPendingToolCalls", () => {
|
|
34
|
+
it("returns tool calls without matching tool responses", () => {
|
|
35
|
+
const messages: AdkMessage[] = [
|
|
36
|
+
aiWithToolCalls("ai-1", [
|
|
37
|
+
{ id: "tc-1", name: "tool_a" },
|
|
38
|
+
{ id: "tc-2", name: "tool_b" },
|
|
39
|
+
]),
|
|
40
|
+
];
|
|
41
|
+
expect(getPendingToolCalls(messages)).toEqual([
|
|
42
|
+
{ id: "tc-1", name: "tool_a", args: {}, argsText: "{}" },
|
|
43
|
+
{ id: "tc-2", name: "tool_b", args: {}, argsText: "{}" },
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("excludes tool calls that have a matching tool response", () => {
|
|
48
|
+
const messages: AdkMessage[] = [
|
|
49
|
+
aiWithToolCalls("ai-1", [
|
|
50
|
+
{ id: "tc-1", name: "tool_a" },
|
|
51
|
+
{ id: "tc-2", name: "tool_b" },
|
|
52
|
+
]),
|
|
53
|
+
toolResponse("t-1", "tc-1", "tool_a"),
|
|
54
|
+
];
|
|
55
|
+
expect(getPendingToolCalls(messages)).toEqual([
|
|
56
|
+
{ id: "tc-2", name: "tool_b", args: {}, argsText: "{}" },
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns empty for threads with no ai messages", () => {
|
|
61
|
+
expect(getPendingToolCalls([])).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("getPendingCancellations", () => {
|
|
66
|
+
it("emits a {cancelled:true} tool message for every pending tool call", () => {
|
|
67
|
+
const messages: AdkMessage[] = [
|
|
68
|
+
aiWithToolCalls("ai-1", [{ id: "tc-1", name: "tool_a" }]),
|
|
69
|
+
];
|
|
70
|
+
const result = getPendingCancellations(messages, []);
|
|
71
|
+
expect(result).toHaveLength(1);
|
|
72
|
+
expect(result[0]).toMatchObject({
|
|
73
|
+
type: "tool",
|
|
74
|
+
name: "tool_a",
|
|
75
|
+
tool_call_id: "tc-1",
|
|
76
|
+
content: JSON.stringify({ cancelled: true }),
|
|
77
|
+
status: "error",
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("skips tool calls whose id is in longRunningToolIds", () => {
|
|
82
|
+
const messages: AdkMessage[] = [
|
|
83
|
+
aiWithToolCalls("ai-1", [
|
|
84
|
+
{ id: "lrt-1", name: "adk_request_input" },
|
|
85
|
+
{ id: "tc-2", name: "regular_tool" },
|
|
86
|
+
]),
|
|
87
|
+
];
|
|
88
|
+
const result = getPendingCancellations(messages, ["lrt-1"]);
|
|
89
|
+
expect(result).toHaveLength(1);
|
|
90
|
+
expect(result[0]).toMatchObject({
|
|
91
|
+
type: "tool",
|
|
92
|
+
name: "regular_tool",
|
|
93
|
+
tool_call_id: "tc-2",
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("skips all three HITL interrupt types", () => {
|
|
98
|
+
const messages: AdkMessage[] = [
|
|
99
|
+
aiWithToolCalls("ai-1", [
|
|
100
|
+
{ id: "lrt-1", name: "adk_request_input" },
|
|
101
|
+
{ id: "lrt-2", name: "adk_request_confirmation" },
|
|
102
|
+
{ id: "lrt-3", name: "adk_request_credential" },
|
|
103
|
+
]),
|
|
104
|
+
];
|
|
105
|
+
const result = getPendingCancellations(messages, [
|
|
106
|
+
"lrt-1",
|
|
107
|
+
"lrt-2",
|
|
108
|
+
"lrt-3",
|
|
109
|
+
]);
|
|
110
|
+
expect(result).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("returns empty when no tool calls are pending", () => {
|
|
114
|
+
const messages: AdkMessage[] = [
|
|
115
|
+
aiWithToolCalls("ai-1", [{ id: "tc-1", name: "tool_a" }]),
|
|
116
|
+
toolResponse("t-1", "tc-1", "tool_a"),
|
|
117
|
+
];
|
|
118
|
+
expect(getPendingCancellations(messages, [])).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("cancels regular tool calls even when HITL interrupts are resolved", () => {
|
|
122
|
+
const messages: AdkMessage[] = [
|
|
123
|
+
aiWithToolCalls("ai-1", [
|
|
124
|
+
{ id: "lrt-1", name: "adk_request_input" },
|
|
125
|
+
{ id: "tc-2", name: "regular_tool" },
|
|
126
|
+
]),
|
|
127
|
+
toolResponse("t-1", "lrt-1", "adk_request_input"),
|
|
128
|
+
];
|
|
129
|
+
// lrt-1 lingering in longRunningToolIds is harmless because
|
|
130
|
+
// getPendingToolCalls already excluded it via the tool response.
|
|
131
|
+
const result = getPendingCancellations(messages, ["lrt-1"]);
|
|
132
|
+
expect(result).toHaveLength(1);
|
|
133
|
+
expect(result[0]).toMatchObject({
|
|
134
|
+
tool_call_id: "tc-2",
|
|
135
|
+
name: "regular_tool",
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
package/src/useAdkRuntime.ts
CHANGED
|
@@ -53,12 +53,13 @@ const getMessageContent = (msg: AppendMessage) => {
|
|
|
53
53
|
case "tool-call":
|
|
54
54
|
throw new Error("Tool call appends are not supported.");
|
|
55
55
|
|
|
56
|
-
default:
|
|
56
|
+
default: {
|
|
57
57
|
const _exhaustiveCheck: "reasoning" | "source" | "audio" | "data" =
|
|
58
58
|
type;
|
|
59
59
|
throw new Error(
|
|
60
60
|
`Unsupported append message part type: ${_exhaustiveCheck}`,
|
|
61
61
|
);
|
|
62
|
+
}
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
65
|
|
|
@@ -69,7 +70,8 @@ const getMessageContent = (msg: AppendMessage) => {
|
|
|
69
70
|
return content;
|
|
70
71
|
};
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
/** @internal — exported for unit tests. */
|
|
74
|
+
export const getPendingToolCalls = (messages: AdkMessage[]) => {
|
|
73
75
|
const pending = new Map<string, { id: string; name: string }>();
|
|
74
76
|
for (const msg of messages) {
|
|
75
77
|
if (msg.type === "ai" && msg.tool_calls) {
|
|
@@ -84,6 +86,35 @@ const getPendingToolCalls = (messages: AdkMessage[]) => {
|
|
|
84
86
|
return [...pending.values()];
|
|
85
87
|
};
|
|
86
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @internal — exported for unit tests.
|
|
91
|
+
*
|
|
92
|
+
* Returns `{cancelled: true}` tool responses for pending tool calls when the
|
|
93
|
+
* user sends a new turn, EXCEPT for HITL interrupts marked via
|
|
94
|
+
* `long_running_tool_ids` (`adk_request_input`, `adk_request_confirmation`,
|
|
95
|
+
* `adk_request_credential`). Those must be answered through a dedicated tool
|
|
96
|
+
* UI + submit helper, not auto-cancelled.
|
|
97
|
+
*/
|
|
98
|
+
export const getPendingCancellations = (
|
|
99
|
+
messages: AdkMessage[],
|
|
100
|
+
longRunningToolIds: readonly string[],
|
|
101
|
+
): Array<AdkMessage & { type: "tool" }> => {
|
|
102
|
+
const longRunningSet = new Set(longRunningToolIds);
|
|
103
|
+
return getPendingToolCalls(messages)
|
|
104
|
+
.filter((t) => !longRunningSet.has(t.id))
|
|
105
|
+
.map(
|
|
106
|
+
(t) =>
|
|
107
|
+
({
|
|
108
|
+
id: uuidv4(),
|
|
109
|
+
type: "tool",
|
|
110
|
+
name: t.name,
|
|
111
|
+
tool_call_id: t.id,
|
|
112
|
+
content: JSON.stringify({ cancelled: true }),
|
|
113
|
+
status: "error",
|
|
114
|
+
}) satisfies AdkMessage & { type: "tool" },
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
87
118
|
const truncateAdkMessages = (
|
|
88
119
|
threadMessages: readonly ThreadMessage[],
|
|
89
120
|
parentId: string | null,
|
|
@@ -140,6 +171,7 @@ const useAdkRuntimeImpl = ({
|
|
|
140
171
|
getCheckpointId,
|
|
141
172
|
eventHandlers,
|
|
142
173
|
}: UseAdkRuntimeOptions) => {
|
|
174
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
143
175
|
const aui = useAui();
|
|
144
176
|
const {
|
|
145
177
|
messages,
|
|
@@ -153,13 +185,16 @@ const useAdkRuntimeImpl = ({
|
|
|
153
185
|
messageMetadata,
|
|
154
186
|
sendMessage,
|
|
155
187
|
cancel,
|
|
156
|
-
|
|
188
|
+
replaceMessages,
|
|
189
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
157
190
|
} = useAdkMessages({
|
|
158
191
|
stream,
|
|
159
192
|
...(eventHandlers && { eventHandlers }),
|
|
160
193
|
});
|
|
161
194
|
|
|
195
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
162
196
|
const [isRunning, setIsRunning] = useState(false);
|
|
197
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
163
198
|
const [toolStatuses, setToolStatuses] = useState<
|
|
164
199
|
Record<string, ToolExecutionStatus>
|
|
165
200
|
>({});
|
|
@@ -180,21 +215,25 @@ const useAdkRuntimeImpl = ({
|
|
|
180
215
|
}
|
|
181
216
|
};
|
|
182
217
|
|
|
218
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
183
219
|
const threadMessages = useExternalMessageConverter({
|
|
184
220
|
callback: convertAdkMessage,
|
|
185
221
|
messages,
|
|
186
222
|
isRunning: effectiveIsRunning,
|
|
187
223
|
});
|
|
188
224
|
|
|
225
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
189
226
|
const threadMessagesRef = useRef(threadMessages);
|
|
190
227
|
threadMessagesRef.current = threadMessages;
|
|
191
228
|
|
|
229
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
192
230
|
const [runtimeRef] = useState(() => ({
|
|
193
231
|
get current() {
|
|
194
232
|
return runtime;
|
|
195
233
|
},
|
|
196
234
|
}));
|
|
197
235
|
|
|
236
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
198
237
|
const toolInvocations = useToolInvocations({
|
|
199
238
|
state: { messages: threadMessages, isRunning: effectiveIsRunning },
|
|
200
239
|
getTools: () => runtimeRef.current.thread.getModelContext().tools,
|
|
@@ -218,6 +257,7 @@ const useAdkRuntimeImpl = ({
|
|
|
218
257
|
setToolStatuses,
|
|
219
258
|
});
|
|
220
259
|
|
|
260
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
221
261
|
const runtime = useExternalStoreRuntime({
|
|
222
262
|
isRunning: effectiveIsRunning,
|
|
223
263
|
messages: threadMessages,
|
|
@@ -239,17 +279,7 @@ const useAdkRuntimeImpl = ({
|
|
|
239
279
|
|
|
240
280
|
const cancellations =
|
|
241
281
|
autoCancelPendingToolCalls !== false
|
|
242
|
-
?
|
|
243
|
-
(t) =>
|
|
244
|
-
({
|
|
245
|
-
id: uuidv4(),
|
|
246
|
-
type: "tool",
|
|
247
|
-
name: t.name,
|
|
248
|
-
tool_call_id: t.id,
|
|
249
|
-
content: JSON.stringify({ cancelled: true }),
|
|
250
|
-
status: "error",
|
|
251
|
-
}) satisfies AdkMessage & { type: "tool" },
|
|
252
|
-
)
|
|
282
|
+
? getPendingCancellations(messages, longRunningToolIds)
|
|
253
283
|
: [];
|
|
254
284
|
|
|
255
285
|
return handleSendMessage(
|
|
@@ -271,7 +301,7 @@ const useAdkRuntimeImpl = ({
|
|
|
271
301
|
threadMessagesRef.current,
|
|
272
302
|
msg.parentId,
|
|
273
303
|
);
|
|
274
|
-
|
|
304
|
+
replaceMessages(truncated);
|
|
275
305
|
const externalId = aui.threadListItem().getState().externalId;
|
|
276
306
|
const checkpointId = externalId
|
|
277
307
|
? await getCheckpointId(externalId, truncated)
|
|
@@ -298,7 +328,7 @@ const useAdkRuntimeImpl = ({
|
|
|
298
328
|
threadMessagesRef.current,
|
|
299
329
|
parentId,
|
|
300
330
|
);
|
|
301
|
-
|
|
331
|
+
replaceMessages(truncated);
|
|
302
332
|
const externalId = aui.threadListItem().getState().externalId;
|
|
303
333
|
const checkpointId = externalId
|
|
304
334
|
? await getCheckpointId(externalId, truncated)
|
|
@@ -342,11 +372,14 @@ const useAdkRuntimeImpl = ({
|
|
|
342
372
|
});
|
|
343
373
|
|
|
344
374
|
{
|
|
375
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
345
376
|
const loadRef = useRef(load);
|
|
377
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
346
378
|
useEffect(() => {
|
|
347
379
|
loadRef.current = load;
|
|
348
380
|
});
|
|
349
381
|
|
|
382
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
350
383
|
useEffect(() => {
|
|
351
384
|
const loadFn = loadRef.current;
|
|
352
385
|
if (!loadFn) return;
|
|
@@ -356,13 +389,13 @@ const useAdkRuntimeImpl = ({
|
|
|
356
389
|
|
|
357
390
|
loadFn(externalId).then(
|
|
358
391
|
({ messages: msgs }) => {
|
|
359
|
-
|
|
392
|
+
replaceMessages(msgs);
|
|
360
393
|
},
|
|
361
394
|
(e) => {
|
|
362
395
|
console.warn("Failed to load ADK session:", e);
|
|
363
396
|
},
|
|
364
397
|
);
|
|
365
|
-
}, [aui,
|
|
398
|
+
}, [aui, replaceMessages]);
|
|
366
399
|
}
|
|
367
400
|
|
|
368
401
|
return runtime;
|
|
@@ -390,6 +423,7 @@ export const useAdkRuntime = ({
|
|
|
390
423
|
|
|
391
424
|
return useRemoteThreadListRuntime({
|
|
392
425
|
runtimeHook: function RuntimeHook() {
|
|
426
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
393
427
|
return useAdkRuntimeImpl(options);
|
|
394
428
|
},
|
|
395
429
|
adapter,
|