@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
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import type { MessageStatus } from "@assistant-ui/core";
|
|
3
|
+
import type {
|
|
4
|
+
AdkEvent,
|
|
5
|
+
AdkEventPart,
|
|
6
|
+
AdkMessage,
|
|
7
|
+
AdkMessageContentPart,
|
|
8
|
+
AdkToolCall,
|
|
9
|
+
AdkToolConfirmation,
|
|
10
|
+
AdkAuthRequest,
|
|
11
|
+
AdkMessageMetadata,
|
|
12
|
+
} from "./types";
|
|
13
|
+
import type { ReadonlyJSONObject } from "assistant-stream/utils";
|
|
14
|
+
|
|
15
|
+
type InProgressMessage = AdkMessage & { type: "ai" };
|
|
16
|
+
|
|
17
|
+
const ADK_REQUEST_CONFIRMATION = "adk_request_confirmation";
|
|
18
|
+
const ADK_REQUEST_CREDENTIAL = "adk_request_credential";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if an event is a final response using the same logic as ADK's
|
|
22
|
+
* `isFinalResponse()`.
|
|
23
|
+
*/
|
|
24
|
+
export const isFinalResponse = (event: AdkEvent): boolean => {
|
|
25
|
+
if (
|
|
26
|
+
event.actions?.skipSummarization ||
|
|
27
|
+
(event.longRunningToolIds && event.longRunningToolIds.length > 0)
|
|
28
|
+
) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parts = event.content?.parts;
|
|
33
|
+
return (
|
|
34
|
+
!event.partial &&
|
|
35
|
+
(parts?.length ?? 0) > 0 &&
|
|
36
|
+
!parts!.some((p) => p.functionCall) &&
|
|
37
|
+
!parts!.some((p) => p.functionResponse) &&
|
|
38
|
+
!parts![parts!.length - 1]?.codeExecutionResult
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const CONTENT_FILTER_REASONS = new Set([
|
|
43
|
+
"SAFETY",
|
|
44
|
+
"RECITATION",
|
|
45
|
+
"BLOCKLIST",
|
|
46
|
+
"PROHIBITED_CONTENT",
|
|
47
|
+
"SPII",
|
|
48
|
+
"LANGUAGE",
|
|
49
|
+
"IMAGE_SAFETY",
|
|
50
|
+
"IMAGE_RECITATION",
|
|
51
|
+
"IMAGE_OTHER",
|
|
52
|
+
"IMAGE_PROHIBITED_CONTENT",
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const ERROR_REASONS = new Set([
|
|
56
|
+
"MALFORMED_FUNCTION_CALL",
|
|
57
|
+
"UNEXPECTED_TOOL_CALL",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const finishReasonToStatus = (
|
|
61
|
+
finishReason: string | undefined,
|
|
62
|
+
): MessageStatus => {
|
|
63
|
+
if (finishReason === "MAX_TOKENS") {
|
|
64
|
+
return { type: "incomplete", reason: "length" };
|
|
65
|
+
}
|
|
66
|
+
if (finishReason && CONTENT_FILTER_REASONS.has(finishReason)) {
|
|
67
|
+
return {
|
|
68
|
+
type: "incomplete",
|
|
69
|
+
reason: "content-filter",
|
|
70
|
+
error: `Content filtered: ${finishReason}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (finishReason && ERROR_REASONS.has(finishReason)) {
|
|
74
|
+
return {
|
|
75
|
+
type: "incomplete",
|
|
76
|
+
reason: "error",
|
|
77
|
+
error: `LLM error: ${finishReason}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return { type: "complete", reason: "stop" };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ── Snake_case normalization ──
|
|
84
|
+
|
|
85
|
+
const normalizeEventPart = (part: AdkEventPart): AdkEventPart => {
|
|
86
|
+
const p = part as Record<string, unknown>;
|
|
87
|
+
const result: Record<string, unknown> = { ...p };
|
|
88
|
+
if ("function_call" in p && !("functionCall" in p))
|
|
89
|
+
result.functionCall = p.function_call;
|
|
90
|
+
if ("function_response" in p && !("functionResponse" in p))
|
|
91
|
+
result.functionResponse = p.function_response;
|
|
92
|
+
if ("inline_data" in p && !("inlineData" in p))
|
|
93
|
+
result.inlineData = p.inline_data;
|
|
94
|
+
if ("file_data" in p && !("fileData" in p)) result.fileData = p.file_data;
|
|
95
|
+
if ("executable_code" in p && !("executableCode" in p))
|
|
96
|
+
result.executableCode = p.executable_code;
|
|
97
|
+
if ("code_execution_result" in p && !("codeExecutionResult" in p))
|
|
98
|
+
result.codeExecutionResult = p.code_execution_result;
|
|
99
|
+
return result as AdkEventPart;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const normalizeEvent = (event: AdkEvent): AdkEvent => {
|
|
103
|
+
const e = event as Record<string, unknown>;
|
|
104
|
+
const result: Record<string, unknown> = { ...e };
|
|
105
|
+
if ("error_code" in e && !("errorCode" in e)) result.errorCode = e.error_code;
|
|
106
|
+
if ("error_message" in e && !("errorMessage" in e))
|
|
107
|
+
result.errorMessage = e.error_message;
|
|
108
|
+
if ("long_running_tool_ids" in e && !("longRunningToolIds" in e))
|
|
109
|
+
result.longRunningToolIds = e.long_running_tool_ids;
|
|
110
|
+
if ("turn_complete" in e && !("turnComplete" in e))
|
|
111
|
+
result.turnComplete = e.turn_complete;
|
|
112
|
+
if ("finish_reason" in e && !("finishReason" in e))
|
|
113
|
+
result.finishReason = e.finish_reason;
|
|
114
|
+
if ("invocation_id" in e && !("invocationId" in e))
|
|
115
|
+
result.invocationId = e.invocation_id;
|
|
116
|
+
if ("custom_metadata" in e && !("customMetadata" in e))
|
|
117
|
+
result.customMetadata = e.custom_metadata;
|
|
118
|
+
if ("grounding_metadata" in e && !("groundingMetadata" in e))
|
|
119
|
+
result.groundingMetadata = e.grounding_metadata;
|
|
120
|
+
if ("citation_metadata" in e && !("citationMetadata" in e))
|
|
121
|
+
result.citationMetadata = e.citation_metadata;
|
|
122
|
+
if ("usage_metadata" in e && !("usageMetadata" in e))
|
|
123
|
+
result.usageMetadata = e.usage_metadata;
|
|
124
|
+
|
|
125
|
+
if (result.actions) {
|
|
126
|
+
const a = result.actions as Record<string, unknown>;
|
|
127
|
+
const na: Record<string, unknown> = { ...a };
|
|
128
|
+
if ("state_delta" in a && !("stateDelta" in a))
|
|
129
|
+
na.stateDelta = a.state_delta;
|
|
130
|
+
if ("artifact_delta" in a && !("artifactDelta" in a))
|
|
131
|
+
na.artifactDelta = a.artifact_delta;
|
|
132
|
+
if ("transfer_to_agent" in a && !("transferToAgent" in a))
|
|
133
|
+
na.transferToAgent = a.transfer_to_agent;
|
|
134
|
+
if ("skip_summarization" in a && !("skipSummarization" in a))
|
|
135
|
+
na.skipSummarization = a.skip_summarization;
|
|
136
|
+
if ("requested_auth_configs" in a && !("requestedAuthConfigs" in a))
|
|
137
|
+
na.requestedAuthConfigs = a.requested_auth_configs;
|
|
138
|
+
if (
|
|
139
|
+
"requested_tool_confirmations" in a &&
|
|
140
|
+
!("requestedToolConfirmations" in a)
|
|
141
|
+
)
|
|
142
|
+
na.requestedToolConfirmations = a.requested_tool_confirmations;
|
|
143
|
+
result.actions = na;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (result.content && (result.content as Record<string, unknown>).parts) {
|
|
147
|
+
const content = result.content as Record<string, unknown>;
|
|
148
|
+
const parts = content.parts as AdkEventPart[];
|
|
149
|
+
result.content = { ...content, parts: parts.map(normalizeEventPart) };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return result as AdkEvent;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// ── Accumulator ──
|
|
156
|
+
|
|
157
|
+
export class AdkEventAccumulator {
|
|
158
|
+
private messagesMap = new Map<string, AdkMessage>();
|
|
159
|
+
private currentMessageId: string | null = null;
|
|
160
|
+
private partialTextBuffer = "";
|
|
161
|
+
private partialReasoningBuffer = "";
|
|
162
|
+
private accumulatedStateDelta: Record<string, unknown> = {};
|
|
163
|
+
private accumulatedArtifactDelta: Record<string, number> = {};
|
|
164
|
+
private lastAgentInfo: {
|
|
165
|
+
name?: string | undefined;
|
|
166
|
+
branch?: string | undefined;
|
|
167
|
+
} = {};
|
|
168
|
+
private lastTransferToAgent: string | undefined;
|
|
169
|
+
private pendingLongRunningToolIds: string[] = [];
|
|
170
|
+
private toolConfirmations: AdkToolConfirmation[] = [];
|
|
171
|
+
private authRequests: AdkAuthRequest[] = [];
|
|
172
|
+
private escalated = false;
|
|
173
|
+
private messageMetadataMap = new Map<string, AdkMessageMetadata>();
|
|
174
|
+
constructor(initialMessages?: AdkMessage[]) {
|
|
175
|
+
if (initialMessages) {
|
|
176
|
+
for (const msg of initialMessages) {
|
|
177
|
+
this.messagesMap.set(msg.id, msg);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
processEvent(rawEvent: AdkEvent): AdkMessage[] {
|
|
183
|
+
const event = normalizeEvent(rawEvent);
|
|
184
|
+
|
|
185
|
+
// Accumulate state delta
|
|
186
|
+
if (event.actions?.stateDelta) {
|
|
187
|
+
Object.assign(this.accumulatedStateDelta, event.actions.stateDelta);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Accumulate artifact delta
|
|
191
|
+
if (event.actions?.artifactDelta) {
|
|
192
|
+
Object.assign(this.accumulatedArtifactDelta, event.actions.artifactDelta);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Track escalation
|
|
196
|
+
if (event.actions?.escalate) {
|
|
197
|
+
this.escalated = true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Track agent transfer
|
|
201
|
+
if (event.actions?.transferToAgent) {
|
|
202
|
+
this.lastTransferToAgent = event.actions.transferToAgent;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Track long-running tool IDs
|
|
206
|
+
if (event.longRunningToolIds?.length) {
|
|
207
|
+
this.pendingLongRunningToolIds = event.longRunningToolIds;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Track tool confirmations from actions
|
|
211
|
+
if (event.actions?.requestedToolConfirmations) {
|
|
212
|
+
for (const [tcId, conf] of Object.entries(
|
|
213
|
+
event.actions.requestedToolConfirmations,
|
|
214
|
+
)) {
|
|
215
|
+
const c = conf as Record<string, unknown>;
|
|
216
|
+
this.toolConfirmations.push({
|
|
217
|
+
toolCallId: tcId,
|
|
218
|
+
toolName: "",
|
|
219
|
+
args: {},
|
|
220
|
+
hint: (c.hint as string) ?? "",
|
|
221
|
+
confirmed: false,
|
|
222
|
+
payload: c.payload,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Track auth requests from actions
|
|
228
|
+
if (event.actions?.requestedAuthConfigs) {
|
|
229
|
+
for (const [tcId, authConf] of Object.entries(
|
|
230
|
+
event.actions.requestedAuthConfigs,
|
|
231
|
+
)) {
|
|
232
|
+
this.authRequests.push({ toolCallId: tcId, authConfig: authConf });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Track agent info
|
|
237
|
+
if (event.author && event.author !== "user") {
|
|
238
|
+
this.lastAgentInfo = {
|
|
239
|
+
name: event.author ?? undefined,
|
|
240
|
+
branch: event.branch ?? undefined,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Handle interrupted events
|
|
245
|
+
if (event.interrupted) {
|
|
246
|
+
if (this.currentMessageId) {
|
|
247
|
+
const msg = this.messagesMap.get(this.currentMessageId);
|
|
248
|
+
if (msg && msg.type === "ai" && !msg.status) {
|
|
249
|
+
const updated: InProgressMessage = {
|
|
250
|
+
...msg,
|
|
251
|
+
content: [...this.getContentArray(msg)],
|
|
252
|
+
status: { type: "incomplete", reason: "cancelled" },
|
|
253
|
+
};
|
|
254
|
+
this.messagesMap.set(updated.id, updated);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
this.finalizeCurrentMessage();
|
|
258
|
+
return this.getMessages();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Handle error events
|
|
262
|
+
if (event.errorCode || event.errorMessage) {
|
|
263
|
+
this.finalizeCurrentMessage();
|
|
264
|
+
const errorMsg = this.getOrCreateAiMessage(event);
|
|
265
|
+
const errorText =
|
|
266
|
+
event.errorMessage ?? event.errorCode ?? "Unknown error";
|
|
267
|
+
const updated: InProgressMessage = {
|
|
268
|
+
...errorMsg,
|
|
269
|
+
content: [
|
|
270
|
+
...this.getContentArray(errorMsg),
|
|
271
|
+
{ type: "text", text: errorText },
|
|
272
|
+
],
|
|
273
|
+
status: { type: "incomplete", reason: "error", error: errorText },
|
|
274
|
+
};
|
|
275
|
+
this.messagesMap.set(updated.id, updated);
|
|
276
|
+
this.currentMessageId = null;
|
|
277
|
+
return this.getMessages();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const parts = event.content?.parts;
|
|
281
|
+
if (!parts?.length) {
|
|
282
|
+
// Track metadata even for content-less events (e.g. turnComplete)
|
|
283
|
+
this.trackMessageMetadata(event);
|
|
284
|
+
return this.getMessages();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// If author changed, finalize previous message
|
|
288
|
+
if (this.currentMessageId && event.author && event.author !== "user") {
|
|
289
|
+
const current = this.messagesMap.get(this.currentMessageId);
|
|
290
|
+
if (current && current.type === "ai" && current.author !== event.author) {
|
|
291
|
+
this.finalizeCurrentMessage();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (const part of parts) {
|
|
296
|
+
this.processPart(part, event);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Track per-message metadata (grounding, citation, usage)
|
|
300
|
+
this.trackMessageMetadata(event);
|
|
301
|
+
|
|
302
|
+
// Check isFinalResponse (can be true even for partial events via skipSummarization/longRunningToolIds)
|
|
303
|
+
if (isFinalResponse(event) && this.currentMessageId) {
|
|
304
|
+
const msg = this.messagesMap.get(this.currentMessageId);
|
|
305
|
+
if (msg && msg.type === "ai" && !msg.status) {
|
|
306
|
+
const status = finishReasonToStatus(event.finishReason);
|
|
307
|
+
const updated: InProgressMessage = {
|
|
308
|
+
...msg,
|
|
309
|
+
content: [...this.getContentArray(msg)],
|
|
310
|
+
status,
|
|
311
|
+
};
|
|
312
|
+
this.messagesMap.set(updated.id, updated);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Non-partial event finalizes the current message
|
|
317
|
+
if (!event.partial || isFinalResponse(event)) {
|
|
318
|
+
this.finalizeCurrentMessage();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return this.getMessages();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private processPart(part: AdkEventPart, event: AdkEvent): void {
|
|
325
|
+
// Detect special ADK function calls
|
|
326
|
+
if (part.functionCall) {
|
|
327
|
+
const name = part.functionCall.name;
|
|
328
|
+
|
|
329
|
+
// Tool confirmation request
|
|
330
|
+
if (name === ADK_REQUEST_CONFIRMATION) {
|
|
331
|
+
const callArgs = part.functionCall.args;
|
|
332
|
+
// ADK JS: args keys are "originalFunctionCall" and "toolConfirmation"
|
|
333
|
+
// ADK Python: args keys are "original_function_call" and "tool_confirmation"
|
|
334
|
+
const original =
|
|
335
|
+
(callArgs.originalFunctionCall as Record<string, unknown>) ??
|
|
336
|
+
(callArgs.original_function_call as Record<string, unknown>);
|
|
337
|
+
const conf =
|
|
338
|
+
(callArgs.toolConfirmation as Record<string, unknown>) ??
|
|
339
|
+
(callArgs.tool_confirmation as Record<string, unknown>);
|
|
340
|
+
this.toolConfirmations.push({
|
|
341
|
+
toolCallId: part.functionCall.id ?? "",
|
|
342
|
+
toolName: (original?.name as string) ?? "",
|
|
343
|
+
args: (original?.args as Record<string, unknown>) ?? {},
|
|
344
|
+
hint: (conf?.hint as string) ?? "",
|
|
345
|
+
confirmed: false,
|
|
346
|
+
payload: conf?.payload,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Auth credential request
|
|
351
|
+
if (name === ADK_REQUEST_CREDENTIAL) {
|
|
352
|
+
const credArgs = part.functionCall.args;
|
|
353
|
+
// ADK JS: args keys are "function_call_id" and "auth_config"
|
|
354
|
+
const originalToolCallId =
|
|
355
|
+
(credArgs.function_call_id as string) ?? part.functionCall.id ?? "";
|
|
356
|
+
const authConfig = credArgs.auth_config ?? credArgs;
|
|
357
|
+
this.authRequests.push({
|
|
358
|
+
toolCallId: originalToolCallId,
|
|
359
|
+
authConfig,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Text with thought=true → reasoning (accumulated)
|
|
365
|
+
if (part.text != null && part.thought) {
|
|
366
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
367
|
+
if (event.partial) {
|
|
368
|
+
this.partialReasoningBuffer += part.text;
|
|
369
|
+
this.replaceLastReasoningContent(msg, this.partialReasoningBuffer);
|
|
370
|
+
} else {
|
|
371
|
+
this.partialReasoningBuffer = "";
|
|
372
|
+
this.replaceLastReasoningContent(msg, part.text);
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Regular text
|
|
378
|
+
if (part.text != null) {
|
|
379
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
380
|
+
if (event.partial) {
|
|
381
|
+
this.partialTextBuffer += part.text;
|
|
382
|
+
this.replaceLastTextContent(msg, this.partialTextBuffer);
|
|
383
|
+
} else {
|
|
384
|
+
this.partialTextBuffer = "";
|
|
385
|
+
this.replaceLastTextContent(msg, part.text);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Function call (including special ones above — still create tool-call parts)
|
|
391
|
+
if (part.functionCall) {
|
|
392
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
393
|
+
const toolCall: AdkToolCall = {
|
|
394
|
+
id: part.functionCall.id ?? uuidv4(),
|
|
395
|
+
name: part.functionCall.name,
|
|
396
|
+
args: part.functionCall.args as ReadonlyJSONObject,
|
|
397
|
+
argsText: JSON.stringify(part.functionCall.args),
|
|
398
|
+
};
|
|
399
|
+
const existing = [...(msg.tool_calls ?? [])];
|
|
400
|
+
const idx = existing.findIndex((tc) => tc.id === toolCall.id);
|
|
401
|
+
if (idx >= 0) {
|
|
402
|
+
existing[idx] = toolCall;
|
|
403
|
+
} else {
|
|
404
|
+
existing.push(toolCall);
|
|
405
|
+
}
|
|
406
|
+
const updated: InProgressMessage = {
|
|
407
|
+
...msg,
|
|
408
|
+
content: [...this.getContentArray(msg)],
|
|
409
|
+
tool_calls: existing,
|
|
410
|
+
};
|
|
411
|
+
this.messagesMap.set(updated.id, updated);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Function response → tool message
|
|
416
|
+
if (part.functionResponse) {
|
|
417
|
+
this.finalizeCurrentMessage();
|
|
418
|
+
const toolMsg: AdkMessage = {
|
|
419
|
+
id: uuidv4(),
|
|
420
|
+
type: "tool",
|
|
421
|
+
tool_call_id: part.functionResponse.id ?? "",
|
|
422
|
+
name: part.functionResponse.name,
|
|
423
|
+
content: JSON.stringify(part.functionResponse.response),
|
|
424
|
+
status: "success",
|
|
425
|
+
};
|
|
426
|
+
this.messagesMap.set(toolMsg.id, toolMsg);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Executable code
|
|
431
|
+
if (part.executableCode) {
|
|
432
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
433
|
+
this.appendContent(msg, {
|
|
434
|
+
type: "code",
|
|
435
|
+
code: part.executableCode.code,
|
|
436
|
+
language: part.executableCode.language ?? "python",
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Code execution result
|
|
442
|
+
if (part.codeExecutionResult) {
|
|
443
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
444
|
+
this.appendContent(msg, {
|
|
445
|
+
type: "code_result",
|
|
446
|
+
output: part.codeExecutionResult.output,
|
|
447
|
+
outcome: part.codeExecutionResult.outcome ?? "OUTCOME_OK",
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Inline data (images etc)
|
|
453
|
+
if (part.inlineData) {
|
|
454
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
455
|
+
this.appendContent(msg, {
|
|
456
|
+
type: "image",
|
|
457
|
+
mimeType: part.inlineData.mimeType,
|
|
458
|
+
data: part.inlineData.data,
|
|
459
|
+
});
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// File data (URI reference)
|
|
464
|
+
if (part.fileData) {
|
|
465
|
+
const msg = this.getOrCreateAiMessage(event);
|
|
466
|
+
this.appendContent(msg, {
|
|
467
|
+
type: "image_url",
|
|
468
|
+
url: part.fileData.fileUri,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
private trackMessageMetadata(event: AdkEvent): void {
|
|
474
|
+
if (
|
|
475
|
+
!this.currentMessageId ||
|
|
476
|
+
(!event.groundingMetadata &&
|
|
477
|
+
!event.citationMetadata &&
|
|
478
|
+
!event.usageMetadata)
|
|
479
|
+
)
|
|
480
|
+
return;
|
|
481
|
+
|
|
482
|
+
const existing = this.messageMetadataMap.get(this.currentMessageId) ?? {};
|
|
483
|
+
const updated: AdkMessageMetadata = { ...existing };
|
|
484
|
+
if (event.groundingMetadata)
|
|
485
|
+
updated.groundingMetadata = event.groundingMetadata;
|
|
486
|
+
if (event.citationMetadata)
|
|
487
|
+
updated.citationMetadata = event.citationMetadata;
|
|
488
|
+
if (event.usageMetadata) updated.usageMetadata = event.usageMetadata;
|
|
489
|
+
this.messageMetadataMap.set(this.currentMessageId, updated);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private getContentArray(msg: AdkMessage): AdkMessageContentPart[] {
|
|
493
|
+
return Array.isArray(msg.content)
|
|
494
|
+
? (msg.content as AdkMessageContentPart[])
|
|
495
|
+
: [];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private getOrCreateAiMessage(event: AdkEvent): InProgressMessage {
|
|
499
|
+
if (this.currentMessageId) {
|
|
500
|
+
const existing = this.messagesMap.get(this.currentMessageId);
|
|
501
|
+
if (existing && existing.type === "ai") {
|
|
502
|
+
return existing;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const id = uuidv4();
|
|
507
|
+
const msg: InProgressMessage = {
|
|
508
|
+
id,
|
|
509
|
+
type: "ai",
|
|
510
|
+
content: [] as AdkMessageContentPart[],
|
|
511
|
+
...(event.author != null && { author: event.author }),
|
|
512
|
+
...(event.branch != null && { branch: event.branch }),
|
|
513
|
+
};
|
|
514
|
+
this.messagesMap.set(id, msg);
|
|
515
|
+
this.currentMessageId = id;
|
|
516
|
+
this.partialTextBuffer = "";
|
|
517
|
+
this.partialReasoningBuffer = "";
|
|
518
|
+
return msg;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private appendContent(
|
|
522
|
+
msg: InProgressMessage,
|
|
523
|
+
part: AdkMessageContentPart,
|
|
524
|
+
): void {
|
|
525
|
+
const content = [...this.getContentArray(msg), part];
|
|
526
|
+
const updated: InProgressMessage = { ...msg, content };
|
|
527
|
+
this.messagesMap.set(updated.id, updated);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private replaceLastTextContent(msg: InProgressMessage, text: string): void {
|
|
531
|
+
const content = [...this.getContentArray(msg)];
|
|
532
|
+
const lastTextIdx = content.findLastIndex((p) => p.type === "text");
|
|
533
|
+
if (lastTextIdx >= 0) {
|
|
534
|
+
content[lastTextIdx] = { type: "text", text };
|
|
535
|
+
} else {
|
|
536
|
+
content.push({ type: "text", text });
|
|
537
|
+
}
|
|
538
|
+
const updated: InProgressMessage = { ...msg, content };
|
|
539
|
+
this.messagesMap.set(updated.id, updated);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
private replaceLastReasoningContent(
|
|
543
|
+
msg: InProgressMessage,
|
|
544
|
+
text: string,
|
|
545
|
+
): void {
|
|
546
|
+
const content = [...this.getContentArray(msg)];
|
|
547
|
+
const lastIdx = content.findLastIndex((p) => p.type === "reasoning");
|
|
548
|
+
if (lastIdx >= 0) {
|
|
549
|
+
content[lastIdx] = { type: "reasoning", text };
|
|
550
|
+
} else {
|
|
551
|
+
content.push({ type: "reasoning", text });
|
|
552
|
+
}
|
|
553
|
+
const updated: InProgressMessage = { ...msg, content };
|
|
554
|
+
this.messagesMap.set(updated.id, updated);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private finalizeCurrentMessage(): void {
|
|
558
|
+
this.partialTextBuffer = "";
|
|
559
|
+
this.partialReasoningBuffer = "";
|
|
560
|
+
this.currentMessageId = null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
getMessages(): AdkMessage[] {
|
|
564
|
+
return [...this.messagesMap.values()];
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
getStateDelta(): Record<string, unknown> {
|
|
568
|
+
return { ...this.accumulatedStateDelta };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
getArtifactDelta(): Record<string, number> {
|
|
572
|
+
return { ...this.accumulatedArtifactDelta };
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
getAgentInfo(): { name?: string | undefined; branch?: string | undefined } {
|
|
576
|
+
return { ...this.lastAgentInfo };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
getLastTransferToAgent(): string | undefined {
|
|
580
|
+
return this.lastTransferToAgent;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
getLongRunningToolIds(): string[] {
|
|
584
|
+
return [...this.pendingLongRunningToolIds];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
getToolConfirmations(): AdkToolConfirmation[] {
|
|
588
|
+
return [...this.toolConfirmations];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
getAuthRequests(): AdkAuthRequest[] {
|
|
592
|
+
return [...this.authRequests];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
isEscalated(): boolean {
|
|
596
|
+
return this.escalated;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
getMessageMetadata(): Map<string, AdkMessageMetadata> {
|
|
600
|
+
return new Map(this.messageMetadataMap);
|
|
601
|
+
}
|
|
602
|
+
}
|