lemma-sdk 0.2.45 → 0.2.46
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/assistant-events.js +12 -2
- package/dist/browser/lemma-client.js +56 -84
- package/dist/client.js +1 -1
- package/dist/hey_client/types.gen.d.ts +2 -14
- package/dist/namespaces/conversations.d.ts +2 -2
- package/dist/namespaces/integrations.d.ts +2 -4
- package/dist/namespaces/integrations.js +2 -2
- package/dist/namespaces/records.d.ts +3 -9
- package/dist/namespaces/records.js +4 -33
- package/dist/openapi_client/index.d.ts +3 -5
- package/dist/openapi_client/index.js +2 -0
- package/dist/openapi_client/models/AgentToolset.d.ts +2 -1
- package/dist/openapi_client/models/AgentToolset.js +1 -0
- package/dist/openapi_client/models/AppTriggerResponseSchema.d.ts +2 -0
- package/dist/openapi_client/models/ApprovalDecisionResponse.d.ts +6 -0
- package/dist/openapi_client/models/AuthProvider.d.ts +4 -0
- package/dist/openapi_client/models/AuthProvider.js +9 -0
- package/dist/openapi_client/models/FlowRunStatus.d.ts +3 -2
- package/dist/openapi_client/models/FlowRunStatus.js +3 -2
- package/dist/openapi_client/models/MessageKind.d.ts +14 -0
- package/dist/openapi_client/models/MessageKind.js +19 -0
- package/dist/openapi_client/models/MessageResponse.d.ts +5 -6
- package/dist/openapi_client/models/WorkflowRunResponse.d.ts +2 -2
- package/dist/openapi_client/services/AgentConversationsService.d.ts +5 -5
- package/dist/openapi_client/services/AgentConversationsService.js +3 -3
- package/dist/openapi_client/services/ApplicationsService.d.ts +19 -19
- package/dist/openapi_client/services/ApplicationsService.js +48 -44
- package/dist/openapi_client/services/RecordsService.d.ts +1 -3
- package/dist/openapi_client/services/RecordsService.js +1 -5
- package/dist/react/assistant-output.d.ts +6 -0
- package/dist/react/assistant-output.js +16 -0
- package/dist/react/useAssistantController.d.ts +8 -1
- package/dist/react/useAssistantController.js +87 -250
- package/dist/react/useAssistantRuntime.js +6 -17
- package/dist/react/useAssistantSession.d.ts +4 -2
- package/dist/react/useAssistantSession.js +6 -7
- package/dist/react/useConversationMessages.d.ts +2 -2
- package/dist/react/useConversationMessages.js +3 -5
- package/dist/react/useRecords.d.ts +1 -1
- package/dist/react/useRecords.js +2 -13
- package/dist/react/useReferencingRecords.d.ts +4 -6
- package/dist/react/useReferencingRecords.js +5 -5
- package/dist/react/useReverseRelatedRecords.d.ts +3 -4
- package/dist/react/useReverseRelatedRecords.js +5 -5
- package/dist/types.d.ts +12 -6
- package/package.json +1 -1
- package/dist/openapi_client/models/NotificationContent.d.ts +0 -4
- package/dist/openapi_client/models/TextContent.d.ts +0 -4
- package/dist/openapi_client/models/TextContent.js +0 -1
- package/dist/openapi_client/models/ThinkingContent.d.ts +0 -4
- package/dist/openapi_client/models/ThinkingContent.js +0 -1
- package/dist/openapi_client/models/ToolCallContent.d.ts +0 -6
- package/dist/openapi_client/models/ToolCallContent.js +0 -1
- package/dist/openapi_client/models/ToolReturnContent.d.ts +0 -6
- package/dist/openapi_client/models/ToolReturnContent.js +0 -1
- /package/dist/openapi_client/models/{NotificationContent.js → ApprovalDecisionResponse.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { useAssistantRuntime } from "./useAssistantRuntime.js";
|
|
3
3
|
import { useAssistantSession } from "./useAssistantSession.js";
|
|
4
|
+
const CONVERSATIONS_PAGE_SIZE = 30;
|
|
4
5
|
const EMPTY_SCOPE_KEY = JSON.stringify({
|
|
5
6
|
podId: null,
|
|
6
7
|
assistantName: null,
|
|
@@ -10,43 +11,6 @@ const EMPTY_SCOPE_KEY = JSON.stringify({
|
|
|
10
11
|
function isRecord(value) {
|
|
11
12
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
12
13
|
}
|
|
13
|
-
function stringifyContent(value) {
|
|
14
|
-
if (typeof value === "string")
|
|
15
|
-
return value;
|
|
16
|
-
if (Array.isArray(value)) {
|
|
17
|
-
const text = value
|
|
18
|
-
.map((entry) => extractTextFromStructuredContentEntry(entry))
|
|
19
|
-
.filter((entry) => entry.length > 0)
|
|
20
|
-
.join("\n\n")
|
|
21
|
-
.trim();
|
|
22
|
-
if (text.length > 0)
|
|
23
|
-
return text;
|
|
24
|
-
return "";
|
|
25
|
-
}
|
|
26
|
-
if (!isRecord(value))
|
|
27
|
-
return "";
|
|
28
|
-
const direct = value.content;
|
|
29
|
-
if (typeof direct === "string")
|
|
30
|
-
return direct;
|
|
31
|
-
if (Array.isArray(direct)) {
|
|
32
|
-
const text = direct
|
|
33
|
-
.map((entry) => extractTextFromStructuredContentEntry(entry))
|
|
34
|
-
.filter((entry) => entry.length > 0)
|
|
35
|
-
.join("\n\n")
|
|
36
|
-
.trim();
|
|
37
|
-
if (text.length > 0)
|
|
38
|
-
return text;
|
|
39
|
-
}
|
|
40
|
-
const text = value.text;
|
|
41
|
-
if (typeof text === "string")
|
|
42
|
-
return text;
|
|
43
|
-
try {
|
|
44
|
-
return JSON.stringify(value, null, 2);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
14
|
function parseMaybeJsonObject(value) {
|
|
51
15
|
if (isRecord(value))
|
|
52
16
|
return value;
|
|
@@ -71,37 +35,6 @@ function parseMaybeJsonValue(value) {
|
|
|
71
35
|
return value;
|
|
72
36
|
}
|
|
73
37
|
}
|
|
74
|
-
function extractTextFromStructuredContentEntry(entry) {
|
|
75
|
-
if (typeof entry === "string")
|
|
76
|
-
return entry.trim();
|
|
77
|
-
if (!isRecord(entry))
|
|
78
|
-
return "";
|
|
79
|
-
if (typeof entry.text === "string")
|
|
80
|
-
return entry.text.trim();
|
|
81
|
-
if (typeof entry.content === "string")
|
|
82
|
-
return entry.content.trim();
|
|
83
|
-
if (typeof entry.value === "string")
|
|
84
|
-
return entry.value.trim();
|
|
85
|
-
if (Array.isArray(entry.content)) {
|
|
86
|
-
const nested = entry.content
|
|
87
|
-
.map((child) => extractTextFromStructuredContentEntry(child))
|
|
88
|
-
.filter((text) => text.length > 0)
|
|
89
|
-
.join("\n")
|
|
90
|
-
.trim();
|
|
91
|
-
if (nested.length > 0)
|
|
92
|
-
return nested;
|
|
93
|
-
}
|
|
94
|
-
if (Array.isArray(entry.summary)) {
|
|
95
|
-
const summary = entry.summary
|
|
96
|
-
.map((child) => extractTextFromStructuredContentEntry(child))
|
|
97
|
-
.filter((text) => text.length > 0)
|
|
98
|
-
.join("\n")
|
|
99
|
-
.trim();
|
|
100
|
-
if (summary.length > 0)
|
|
101
|
-
return summary;
|
|
102
|
-
}
|
|
103
|
-
return "";
|
|
104
|
-
}
|
|
105
38
|
function parseTimestampMs(value) {
|
|
106
39
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
107
40
|
return value;
|
|
@@ -137,27 +70,19 @@ function parseThinkingDurationFromRecord(record) {
|
|
|
137
70
|
?? parseDurationMs(record.thought_duration_ms)
|
|
138
71
|
?? parseDurationMs(record.thoughtDurationMs);
|
|
139
72
|
}
|
|
140
|
-
function
|
|
141
|
-
if (
|
|
142
|
-
return null;
|
|
143
|
-
const rawType = typeof content.type === "string" ? content.type.toLowerCase() : "";
|
|
144
|
-
if (rawType !== "thinking" && rawType !== "reasoning") {
|
|
73
|
+
function extractThinkingPart(msg) {
|
|
74
|
+
if (msg.kind !== "thinking")
|
|
145
75
|
return null;
|
|
146
|
-
|
|
147
|
-
const text = extractTextFromStructuredContentEntry(content.content ?? content.text ?? content.value ?? content.summary);
|
|
76
|
+
const text = typeof msg.text === "string" ? msg.text.trim() : "";
|
|
148
77
|
if (!text)
|
|
149
78
|
return null;
|
|
150
|
-
const
|
|
151
|
-
.find((value) => typeof value === "string");
|
|
152
|
-
const normalizedState = stateValue?.toLowerCase() || "";
|
|
153
|
-
const isStreaming = normalizedState.includes("stream")
|
|
154
|
-
|| normalizedState.includes("progress")
|
|
155
|
-
|| normalizedState.includes("running")
|
|
156
|
-
|| normalizedState.includes("thinking");
|
|
79
|
+
const metadata = getMessageMetadata(msg);
|
|
157
80
|
return {
|
|
158
81
|
text,
|
|
159
|
-
state:
|
|
160
|
-
durationMs:
|
|
82
|
+
state: "done",
|
|
83
|
+
durationMs: metadata
|
|
84
|
+
? parseThinkingDurationFromRecord(metadata)
|
|
85
|
+
: undefined,
|
|
161
86
|
};
|
|
162
87
|
}
|
|
163
88
|
function normalizeToolResult(value) {
|
|
@@ -172,143 +97,33 @@ function normalizeToolResult(value) {
|
|
|
172
97
|
function getMessageMetadata(msg) {
|
|
173
98
|
return (msg.message_metadata || msg.metadata || undefined);
|
|
174
99
|
}
|
|
175
|
-
function getNativeToolPayload(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const toolCallId = typeof content.tool_call_id === "string" ? content.tool_call_id : null;
|
|
179
|
-
if (!toolCallId)
|
|
180
|
-
return null;
|
|
181
|
-
const toolName = typeof content.tool_name === "string" ? content.tool_name : undefined;
|
|
182
|
-
if ("tool_output" in content) {
|
|
100
|
+
function getNativeToolPayload(msg) {
|
|
101
|
+
const toolName = typeof msg.tool_name === "string" ? msg.tool_name : undefined;
|
|
102
|
+
if (msg.kind === "tool_call") {
|
|
183
103
|
return {
|
|
184
|
-
kind: "
|
|
185
|
-
toolCallId
|
|
104
|
+
kind: "call",
|
|
105
|
+
toolCallId: (typeof msg.tool_call_id === "string" && msg.tool_call_id) || `${msg.id}-tool-call`,
|
|
186
106
|
toolName,
|
|
187
|
-
|
|
107
|
+
args: parseMaybeJsonObject(parseMaybeJsonValue(msg.tool_args)),
|
|
188
108
|
};
|
|
189
109
|
}
|
|
190
|
-
if (
|
|
110
|
+
if (msg.kind === "tool_return") {
|
|
191
111
|
return {
|
|
192
|
-
kind: "
|
|
193
|
-
toolCallId
|
|
112
|
+
kind: "result",
|
|
113
|
+
toolCallId: (typeof msg.tool_call_id === "string" && msg.tool_call_id) || `${msg.id}-tool-result`,
|
|
194
114
|
toolName,
|
|
195
|
-
|
|
115
|
+
result: normalizeToolResult(msg.tool_result),
|
|
196
116
|
};
|
|
197
117
|
}
|
|
198
118
|
return null;
|
|
199
119
|
}
|
|
200
|
-
function hasNativeToolPayloadContent(content) {
|
|
201
|
-
return getNativeToolPayload(content) !== null;
|
|
202
|
-
}
|
|
203
120
|
function toolInvocationKey(tool) {
|
|
204
121
|
return `${tool.toolCallId}:${tool.state}`;
|
|
205
122
|
}
|
|
206
|
-
function toolInvocationFromStructuredContentEntry(entry, fallbackId) {
|
|
207
|
-
const type = typeof entry.type === "string" ? entry.type.toLowerCase() : "";
|
|
208
|
-
const functionObj = isRecord(entry.function) ? entry.function : {};
|
|
209
|
-
const hasToolShape = type.includes("tool")
|
|
210
|
-
|| type.includes("function_call")
|
|
211
|
-
|| type.includes("function_result")
|
|
212
|
-
|| typeof entry.tool_call_id === "string"
|
|
213
|
-
|| typeof entry.call_id === "string"
|
|
214
|
-
|| typeof entry.tool_name === "string"
|
|
215
|
-
|| typeof functionObj.name === "string"
|
|
216
|
-
|| "tool_output" in entry
|
|
217
|
-
|| ("result" in entry && typeof entry.call_id === "string");
|
|
218
|
-
if (!hasToolShape)
|
|
219
|
-
return null;
|
|
220
|
-
const rawResult = entry.tool_output ?? entry.output ?? entry.result;
|
|
221
|
-
const isResultLike = type.includes("result")
|
|
222
|
-
|| type.includes("output")
|
|
223
|
-
|| type.includes("return")
|
|
224
|
-
|| typeof rawResult !== "undefined";
|
|
225
|
-
const toolCallId = ((typeof entry.tool_call_id === "string" && entry.tool_call_id)
|
|
226
|
-
|| (typeof entry.toolCallId === "string" && entry.toolCallId)
|
|
227
|
-
|| (typeof entry.call_id === "string" && entry.call_id)
|
|
228
|
-
|| (typeof entry.id === "string" && entry.id)
|
|
229
|
-
|| fallbackId);
|
|
230
|
-
const toolName = ((typeof entry.tool_name === "string" && entry.tool_name)
|
|
231
|
-
|| (typeof entry.toolName === "string" && entry.toolName)
|
|
232
|
-
|| (typeof functionObj.name === "string" && functionObj.name)
|
|
233
|
-
|| (typeof entry.name === "string" && entry.name)
|
|
234
|
-
|| "tool");
|
|
235
|
-
const argsRaw = functionObj.arguments
|
|
236
|
-
?? entry.tool_input
|
|
237
|
-
?? entry.input
|
|
238
|
-
?? entry.args
|
|
239
|
-
?? entry.arguments;
|
|
240
|
-
const state = isResultLike ? "result" : "call";
|
|
241
|
-
return {
|
|
242
|
-
toolCallId,
|
|
243
|
-
toolName,
|
|
244
|
-
args: parseMaybeJsonObject(parseMaybeJsonValue(argsRaw)),
|
|
245
|
-
state,
|
|
246
|
-
...(isResultLike ? { result: normalizeToolResult(rawResult) } : {}),
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
function parseStructuredAssistantParts(content) {
|
|
250
|
-
if (!Array.isArray(content))
|
|
251
|
-
return null;
|
|
252
|
-
const parts = [];
|
|
253
|
-
const textChunks = [];
|
|
254
|
-
const representedToolKeys = new Set();
|
|
255
|
-
content.forEach((rawPart, index) => {
|
|
256
|
-
if (!isRecord(rawPart))
|
|
257
|
-
return;
|
|
258
|
-
const partType = typeof rawPart.type === "string" ? rawPart.type.toLowerCase() : "";
|
|
259
|
-
const partId = (typeof rawPart.id === "string" && rawPart.id) || `content-part-${index}`;
|
|
260
|
-
const toolInvocation = toolInvocationFromStructuredContentEntry(rawPart, `${partId}-tool`);
|
|
261
|
-
if (toolInvocation) {
|
|
262
|
-
representedToolKeys.add(toolInvocationKey(toolInvocation));
|
|
263
|
-
parts.push({
|
|
264
|
-
id: `${partId}-tool`,
|
|
265
|
-
type: "tool",
|
|
266
|
-
toolInvocation,
|
|
267
|
-
});
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const text = extractTextFromStructuredContentEntry(rawPart);
|
|
271
|
-
if (!text)
|
|
272
|
-
return;
|
|
273
|
-
if (partType.includes("reasoning") || partType.includes("thinking")) {
|
|
274
|
-
const stateValue = [rawPart.state, rawPart.status, rawPart.phase]
|
|
275
|
-
.find((value) => typeof value === "string");
|
|
276
|
-
const normalizedState = stateValue?.toLowerCase() || partType;
|
|
277
|
-
const isStreaming = normalizedState.includes("stream")
|
|
278
|
-
|| normalizedState.includes("progress")
|
|
279
|
-
|| normalizedState.includes("running")
|
|
280
|
-
|| normalizedState.includes("thinking");
|
|
281
|
-
parts.push({
|
|
282
|
-
id: `${partId}-reasoning`,
|
|
283
|
-
type: "reasoning",
|
|
284
|
-
text,
|
|
285
|
-
state: isStreaming ? "streaming" : "done",
|
|
286
|
-
durationMs: parseThinkingDurationFromRecord(rawPart),
|
|
287
|
-
startedAtMs: parseTimestampMs(rawPart.started_at)
|
|
288
|
-
?? parseTimestampMs(rawPart.startedAt)
|
|
289
|
-
?? parseTimestampMs(rawPart.created_at)
|
|
290
|
-
?? parseTimestampMs(rawPart.createdAt)
|
|
291
|
-
?? undefined,
|
|
292
|
-
});
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
textChunks.push(text);
|
|
296
|
-
parts.push({
|
|
297
|
-
id: `${partId}-text`,
|
|
298
|
-
type: "text",
|
|
299
|
-
text,
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
return {
|
|
303
|
-
parts,
|
|
304
|
-
textContent: textChunks.join("\n\n").trim(),
|
|
305
|
-
representedToolKeys,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
123
|
function mapToolInvocations(msg) {
|
|
309
124
|
const invocations = [];
|
|
310
125
|
const metadata = getMessageMetadata(msg);
|
|
311
|
-
const nativeToolPayload = getNativeToolPayload(msg
|
|
126
|
+
const nativeToolPayload = getNativeToolPayload(msg);
|
|
312
127
|
if (metadata?.message_type === "tool_call") {
|
|
313
128
|
invocations.push({
|
|
314
129
|
toolCallId: metadata.tool_call_id || `${msg.id}-tool-call`,
|
|
@@ -362,20 +177,6 @@ function mapToolInvocations(msg) {
|
|
|
362
177
|
result: nativeToolPayload.result || {},
|
|
363
178
|
});
|
|
364
179
|
}
|
|
365
|
-
const contentObj = isRecord(msg.content) ? msg.content : null;
|
|
366
|
-
if (contentObj && nativeToolPayload === null && "tool_output" in contentObj) {
|
|
367
|
-
invocations.push({
|
|
368
|
-
toolCallId: (typeof contentObj.tool_call_id === "string" && contentObj.tool_call_id)
|
|
369
|
-
|| metadata?.tool_call_id
|
|
370
|
-
|| `${msg.id}-tool-output`,
|
|
371
|
-
toolName: (typeof contentObj.tool_name === "string" && contentObj.tool_name)
|
|
372
|
-
|| metadata?.tool_name
|
|
373
|
-
|| "tool",
|
|
374
|
-
args: metadata?.args || {},
|
|
375
|
-
state: "result",
|
|
376
|
-
result: normalizeToolResult(contentObj.tool_output),
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
180
|
const seen = new Set();
|
|
380
181
|
return invocations.filter((invocation) => {
|
|
381
182
|
const key = toolInvocationKey(invocation);
|
|
@@ -387,43 +188,34 @@ function mapToolInvocations(msg) {
|
|
|
387
188
|
}
|
|
388
189
|
function mapConversationMessage(msg, options) {
|
|
389
190
|
const toolInvocations = mapToolInvocations(msg);
|
|
390
|
-
const structured = parseStructuredAssistantParts(msg.content);
|
|
391
|
-
const explicitThinkingPart = extractThinkingPartFromContent(msg.content);
|
|
392
191
|
const createdAtMs = parseTimestampMs(msg.created_at) ?? undefined;
|
|
393
|
-
const parts =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
: [];
|
|
401
|
-
const representedToolKeys = structured?.representedToolKeys || new Set();
|
|
402
|
-
let content = structured
|
|
403
|
-
? structured.textContent
|
|
404
|
-
: (hasNativeToolPayloadContent(msg.content) ? "" : stringifyContent(msg.content));
|
|
405
|
-
if (explicitThinkingPart) {
|
|
406
|
-
content = "";
|
|
192
|
+
const parts = [];
|
|
193
|
+
let content = "";
|
|
194
|
+
// Flat shape: a message is exactly one kind. Thinking renders as a reasoning
|
|
195
|
+
// part; text/notification render as a text part; tool_call/tool_return render
|
|
196
|
+
// via toolInvocations below.
|
|
197
|
+
const thinkingPart = extractThinkingPart(msg);
|
|
198
|
+
if (thinkingPart) {
|
|
407
199
|
parts.push({
|
|
408
200
|
id: `${msg.id}-reasoning`,
|
|
409
201
|
type: "reasoning",
|
|
410
|
-
text:
|
|
411
|
-
state:
|
|
412
|
-
durationMs:
|
|
202
|
+
text: thinkingPart.text,
|
|
203
|
+
state: thinkingPart.state,
|
|
204
|
+
durationMs: thinkingPart.durationMs ?? options?.thinkingDurationMs,
|
|
413
205
|
startedAtMs: createdAtMs,
|
|
414
206
|
});
|
|
415
207
|
}
|
|
416
|
-
else if (
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
208
|
+
else if (msg.kind === "text" || msg.kind === "notification") {
|
|
209
|
+
content = typeof msg.text === "string" ? msg.text.trim() : "";
|
|
210
|
+
if (content) {
|
|
211
|
+
parts.push({
|
|
212
|
+
id: `${msg.id}-text`,
|
|
213
|
+
type: "text",
|
|
214
|
+
text: content,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
422
217
|
}
|
|
423
218
|
toolInvocations.forEach((toolInvocation, index) => {
|
|
424
|
-
const key = toolInvocationKey(toolInvocation);
|
|
425
|
-
if (representedToolKeys.has(key))
|
|
426
|
-
return;
|
|
427
219
|
parts.push({
|
|
428
220
|
id: `${msg.id}-tool-${index}`,
|
|
429
221
|
type: "tool",
|
|
@@ -442,8 +234,11 @@ function mapConversationMessage(msg, options) {
|
|
|
442
234
|
agent_run_id: msg.agent_run_id,
|
|
443
235
|
metadata: msg.metadata ?? null,
|
|
444
236
|
message_metadata: msg.message_metadata ?? null,
|
|
237
|
+
kind: msg.kind,
|
|
445
238
|
tool_call_id: msg.tool_call_id ?? null,
|
|
446
239
|
tool_name: msg.tool_name ?? null,
|
|
240
|
+
tool_args: msg.tool_args ?? null,
|
|
241
|
+
tool_result: msg.tool_result ?? null,
|
|
447
242
|
};
|
|
448
243
|
}
|
|
449
244
|
function mapConversationMessages(messages) {
|
|
@@ -451,7 +246,7 @@ function mapConversationMessages(messages) {
|
|
|
451
246
|
const pendingToolCalls = new Map();
|
|
452
247
|
const estimateThinkingDurationMs = (index) => {
|
|
453
248
|
const message = messages[index];
|
|
454
|
-
if (!message || !
|
|
249
|
+
if (!message || !extractThinkingPart(message))
|
|
455
250
|
return undefined;
|
|
456
251
|
const startedAtMs = parseTimestampMs(message.created_at);
|
|
457
252
|
if (!startedAtMs)
|
|
@@ -477,7 +272,7 @@ function mapConversationMessages(messages) {
|
|
|
477
272
|
pendingToolCalls.set(invocation.toolCallId, invocation);
|
|
478
273
|
}
|
|
479
274
|
});
|
|
480
|
-
const nativePayload = getNativeToolPayload(rawMessage
|
|
275
|
+
const nativePayload = getNativeToolPayload(rawMessage);
|
|
481
276
|
const isToolRole = rawMessage.role === "tool";
|
|
482
277
|
if (isToolRole && nativePayload?.kind === "result" && mappedMessage.toolInvocations && mappedMessage.toolInvocations.length > 0) {
|
|
483
278
|
let mergedIntoPriorCall = false;
|
|
@@ -625,6 +420,8 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
625
420
|
const [conversationRuntime, setConversationRuntimeState] = useState(null);
|
|
626
421
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
627
422
|
const [isLoadingConversations, setIsLoadingConversations] = useState(false);
|
|
423
|
+
const [isLoadingMoreConversations, setIsLoadingMoreConversations] = useState(false);
|
|
424
|
+
const [conversationsCursor, setConversationsCursor] = useState(null);
|
|
628
425
|
const [isLoadingMessages, setIsLoadingMessages] = useState(false);
|
|
629
426
|
const [isLoadingOlderMessages, setIsLoadingOlderMessages] = useState(false);
|
|
630
427
|
const [isUploadingFiles, setIsUploadingFiles] = useState(false);
|
|
@@ -739,9 +536,10 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
739
536
|
const loadConversations = useCallback(async () => {
|
|
740
537
|
setIsLoadingConversations(true);
|
|
741
538
|
try {
|
|
742
|
-
const response = await sessionListConversations({ scope });
|
|
539
|
+
const response = await sessionListConversations({ scope, limit: CONVERSATIONS_PAGE_SIZE });
|
|
743
540
|
const nextConversations = sortConversationsByUpdatedAt(response.items || []);
|
|
744
541
|
setConversations(nextConversations);
|
|
542
|
+
setConversationsCursor(response.next_page_token ?? null);
|
|
745
543
|
setActiveConversationId((current) => {
|
|
746
544
|
if (current && nextConversations.some((conversation) => conversation.id === current)) {
|
|
747
545
|
return current;
|
|
@@ -759,6 +557,36 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
759
557
|
setIsLoadingConversations(false);
|
|
760
558
|
}
|
|
761
559
|
}, [scope, sessionListConversations]);
|
|
560
|
+
const loadMoreConversations = useCallback(async () => {
|
|
561
|
+
if (!conversationsCursor || isLoadingConversations || isLoadingMoreConversations) {
|
|
562
|
+
return [];
|
|
563
|
+
}
|
|
564
|
+
setIsLoadingMoreConversations(true);
|
|
565
|
+
try {
|
|
566
|
+
const response = await sessionListConversations({
|
|
567
|
+
scope,
|
|
568
|
+
limit: CONVERSATIONS_PAGE_SIZE,
|
|
569
|
+
pageToken: conversationsCursor,
|
|
570
|
+
});
|
|
571
|
+
const moreConversations = response.items || [];
|
|
572
|
+
setConversations((prev) => {
|
|
573
|
+
const byId = new Map(prev.map((conversation) => [conversation.id, conversation]));
|
|
574
|
+
for (const conversation of moreConversations) {
|
|
575
|
+
byId.set(conversation.id, conversation);
|
|
576
|
+
}
|
|
577
|
+
return sortConversationsByUpdatedAt(Array.from(byId.values()));
|
|
578
|
+
});
|
|
579
|
+
setConversationsCursor(response.next_page_token ?? null);
|
|
580
|
+
return moreConversations;
|
|
581
|
+
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
setLocalError((prev) => prev || (err instanceof Error ? err.message : "Failed to load more conversations"));
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
finally {
|
|
587
|
+
setIsLoadingMoreConversations(false);
|
|
588
|
+
}
|
|
589
|
+
}, [conversationsCursor, isLoadingConversations, isLoadingMoreConversations, scope, sessionListConversations]);
|
|
762
590
|
const loadAvailableModels = useCallback(async () => {
|
|
763
591
|
try {
|
|
764
592
|
const response = await client.conversations.listModels({
|
|
@@ -909,9 +737,11 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
909
737
|
setConversationModelState(null);
|
|
910
738
|
setConversationRuntimeState(null);
|
|
911
739
|
setConversations([]);
|
|
740
|
+
setConversationsCursor(null);
|
|
912
741
|
setLocalError(null);
|
|
913
742
|
setOlderMessagesCursor(null);
|
|
914
743
|
setIsLoadingConversations(false);
|
|
744
|
+
setIsLoadingMoreConversations(false);
|
|
915
745
|
setIsLoadingMessages(false);
|
|
916
746
|
setIsLoadingOlderMessages(false);
|
|
917
747
|
return;
|
|
@@ -925,6 +755,7 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
925
755
|
setConversationModelState(null);
|
|
926
756
|
setConversationRuntimeState(null);
|
|
927
757
|
setConversations([]);
|
|
758
|
+
setConversationsCursor(null);
|
|
928
759
|
setLocalError(null);
|
|
929
760
|
clearRuntimeMessages();
|
|
930
761
|
setOlderMessagesCursor(null);
|
|
@@ -1286,6 +1117,8 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
1286
1117
|
isActiveConversationRunning,
|
|
1287
1118
|
isLoading,
|
|
1288
1119
|
isLoadingConversations,
|
|
1120
|
+
isLoadingMoreConversations,
|
|
1121
|
+
hasMoreConversations: !!conversationsCursor,
|
|
1289
1122
|
isLoadingMessages,
|
|
1290
1123
|
isLoadingOlderMessages,
|
|
1291
1124
|
hasOlderMessages: !!olderMessagesCursor,
|
|
@@ -1303,6 +1136,7 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
1303
1136
|
removePendingFile,
|
|
1304
1137
|
clearPendingFiles,
|
|
1305
1138
|
loadOlderMessages,
|
|
1139
|
+
loadMoreConversations,
|
|
1306
1140
|
resolveUserApproval,
|
|
1307
1141
|
clearMessages,
|
|
1308
1142
|
stop,
|
|
@@ -1316,12 +1150,15 @@ export function useAssistantController({ client, podId, agentName, assistantName
|
|
|
1316
1150
|
conversationModel,
|
|
1317
1151
|
conversations,
|
|
1318
1152
|
error,
|
|
1153
|
+
conversationsCursor,
|
|
1319
1154
|
isActiveConversationRunning,
|
|
1320
1155
|
isLoading,
|
|
1321
1156
|
isLoadingConversations,
|
|
1157
|
+
isLoadingMoreConversations,
|
|
1322
1158
|
isLoadingMessages,
|
|
1323
1159
|
isLoadingOlderMessages,
|
|
1324
1160
|
isUploadingFiles,
|
|
1161
|
+
loadMoreConversations,
|
|
1325
1162
|
loadOlderMessages,
|
|
1326
1163
|
messages,
|
|
1327
1164
|
olderMessagesCursor,
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
function
|
|
3
|
-
return
|
|
4
|
-
}
|
|
5
|
-
function messageText(content) {
|
|
6
|
-
if (typeof content === "string") {
|
|
7
|
-
return content.trim();
|
|
8
|
-
}
|
|
9
|
-
if (isRecord(content)) {
|
|
10
|
-
if (typeof content.content === "string")
|
|
11
|
-
return content.content.trim();
|
|
12
|
-
if (typeof content.text === "string")
|
|
13
|
-
return content.text.trim();
|
|
14
|
-
}
|
|
15
|
-
return "";
|
|
2
|
+
function messageText(message) {
|
|
3
|
+
return typeof message.text === "string" ? message.text.trim() : "";
|
|
16
4
|
}
|
|
17
5
|
function messageTime(message) {
|
|
18
6
|
const timestamp = new Date(message.created_at).getTime();
|
|
@@ -30,7 +18,7 @@ function upsertRuntimeMessage(previous, incoming) {
|
|
|
30
18
|
return next;
|
|
31
19
|
}
|
|
32
20
|
if (incoming.role === "user") {
|
|
33
|
-
const incomingText = messageText(incoming
|
|
21
|
+
const incomingText = messageText(incoming);
|
|
34
22
|
if (incomingText) {
|
|
35
23
|
const incomingTimestamp = messageTime(incoming);
|
|
36
24
|
let optimisticIndex = -1;
|
|
@@ -38,7 +26,7 @@ function upsertRuntimeMessage(previous, incoming) {
|
|
|
38
26
|
next.forEach((message, index) => {
|
|
39
27
|
if (message.role !== "user"
|
|
40
28
|
|| !isOptimisticId(message.id)
|
|
41
|
-
|| messageText(message
|
|
29
|
+
|| messageText(message) !== incomingText) {
|
|
42
30
|
return;
|
|
43
31
|
}
|
|
44
32
|
const distance = Math.abs(messageTime(message) - incomingTimestamp);
|
|
@@ -100,7 +88,8 @@ export function useAssistantRuntime({ conversationId = null, sessionConversation
|
|
|
100
88
|
const optimistic = {
|
|
101
89
|
id: buildOptimisticId(),
|
|
102
90
|
role: "user",
|
|
103
|
-
|
|
91
|
+
kind: "text",
|
|
92
|
+
text: trimmed,
|
|
104
93
|
created_at: new Date().toISOString(),
|
|
105
94
|
metadata: null,
|
|
106
95
|
...(optimisticConversationId ? { conversation_id: optimisticConversationId } : {}),
|
|
@@ -45,6 +45,8 @@ export interface CreateConversationInput {
|
|
|
45
45
|
agentRuntime?: AgentRuntimeConfig | null;
|
|
46
46
|
podId?: string | null;
|
|
47
47
|
agentName?: string | null;
|
|
48
|
+
/** Parent conversation id for sub-agent (child) conversations. */
|
|
49
|
+
parentId?: string | null;
|
|
48
50
|
/**
|
|
49
51
|
* @deprecated Use agentName instead.
|
|
50
52
|
*/
|
|
@@ -82,9 +84,9 @@ export interface UseAssistantSessionResult {
|
|
|
82
84
|
status?: string;
|
|
83
85
|
messages: ConversationMessage[];
|
|
84
86
|
latestAssistantMessage: ConversationMessage | null;
|
|
85
|
-
output: ConversationMessage
|
|
87
|
+
output: ConversationMessage | null;
|
|
86
88
|
outputText: string;
|
|
87
|
-
finalOutput: ConversationMessage
|
|
89
|
+
finalOutput: ConversationMessage | null;
|
|
88
90
|
finalOutputText: string;
|
|
89
91
|
streamingText: string;
|
|
90
92
|
streamingTool: AssistantStreamingTool | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { parseSSEJson, readSSE } from "../streams.js";
|
|
3
3
|
import { parseAssistantStreamEvent, upsertConversationMessage } from "../assistant-events.js";
|
|
4
|
-
import {
|
|
4
|
+
import { conversationMessageText, getLatestAssistantMessage, } from "./assistant-output.js";
|
|
5
5
|
import { normalizeError } from "./utils.js";
|
|
6
6
|
function applyPodScope(client, podId) {
|
|
7
7
|
const resolvedPodId = podId ?? client.podId;
|
|
@@ -84,8 +84,8 @@ function parseStreamingToolToken(token) {
|
|
|
84
84
|
return null;
|
|
85
85
|
const rawToolCallId = [parsed.tool_call_id, parsed.toolCallId, parsed.call_id, parsed.id]
|
|
86
86
|
.find((value) => typeof value === "string" && value.trim().length > 0);
|
|
87
|
-
const rawArgs = parsed.tool_input ?? parsed.args ?? parsed.arguments ?? parsed.input;
|
|
88
|
-
const rawResult = parsed.tool_output ?? parsed.result ?? parsed.output;
|
|
87
|
+
const rawArgs = parsed.tool_args ?? parsed.tool_input ?? parsed.args ?? parsed.arguments ?? parsed.input;
|
|
88
|
+
const rawResult = parsed.tool_result ?? parsed.tool_output ?? parsed.result ?? parsed.output;
|
|
89
89
|
const hasResult = typeof rawResult !== "undefined";
|
|
90
90
|
return {
|
|
91
91
|
...(typeof rawToolCallId === "string" ? { toolCallId: rawToolCallId } : {}),
|
|
@@ -292,6 +292,7 @@ export function useAssistantSession(options) {
|
|
|
292
292
|
agent_runtime: typeof input.agentRuntime === "undefined"
|
|
293
293
|
? undefined
|
|
294
294
|
: input.agentRuntime,
|
|
295
|
+
parent_id: input.parentId ?? undefined,
|
|
295
296
|
};
|
|
296
297
|
const created = await scopedClient.conversations.create(payload);
|
|
297
298
|
if (input.setActive !== false) {
|
|
@@ -699,10 +700,8 @@ export function useAssistantSession(options) {
|
|
|
699
700
|
};
|
|
700
701
|
}, [autoLoad, autoResume, conversationId, loadMessages, refreshConversation, resumeIfRunning]);
|
|
701
702
|
const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
|
|
702
|
-
const output = latestAssistantMessage
|
|
703
|
-
const latestAssistantText = latestAssistantMessage
|
|
704
|
-
? extractConversationMessageText(latestAssistantMessage.content)
|
|
705
|
-
: "";
|
|
703
|
+
const output = latestAssistantMessage ?? null;
|
|
704
|
+
const latestAssistantText = conversationMessageText(latestAssistantMessage);
|
|
706
705
|
const outputText = streamingText.trim() || latestAssistantText;
|
|
707
706
|
const finalOutput = !isStreaming && !isConversationRunningStatus(status) ? output : null;
|
|
708
707
|
const finalOutputText = !isStreaming && !isConversationRunningStatus(status) ? latestAssistantText : "";
|
|
@@ -40,9 +40,9 @@ export interface UseConversationMessagesResult {
|
|
|
40
40
|
nextPageToken: string | null;
|
|
41
41
|
streamingText: string;
|
|
42
42
|
latestAssistantMessage: ConversationMessage | null;
|
|
43
|
-
output: ConversationMessage
|
|
43
|
+
output: ConversationMessage | null;
|
|
44
44
|
outputText: string;
|
|
45
|
-
finalOutput: ConversationMessage
|
|
45
|
+
finalOutput: ConversationMessage | null;
|
|
46
46
|
finalOutputText: string;
|
|
47
47
|
error: Error | null;
|
|
48
48
|
refresh: (options?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { conversationMessageText, getLatestAssistantMessage, isConversationRunningStatus, normalizeConversationStatus, sortConversationMessagesByCreatedAt, } from "./assistant-output.js";
|
|
3
3
|
import { useAssistantSession, } from "./useAssistantSession.js";
|
|
4
4
|
function resolveConversationId(preferred, fallback) {
|
|
5
5
|
return preferred ?? fallback ?? null;
|
|
@@ -117,10 +117,8 @@ export function useConversationMessages({ client, podId, agentName, assistantNam
|
|
|
117
117
|
}, [autoLoad, autoResume, cancel, clearSessionMessages, conversationId, enabled, limit, refresh, resumeIfRunning]);
|
|
118
118
|
const messages = useMemo(() => sortConversationMessagesByCreatedAt(sessionMessages), [sessionMessages]);
|
|
119
119
|
const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
|
|
120
|
-
const output = latestAssistantMessage
|
|
121
|
-
const latestAssistantText = latestAssistantMessage
|
|
122
|
-
? extractConversationMessageText(latestAssistantMessage.content)
|
|
123
|
-
: "";
|
|
120
|
+
const output = latestAssistantMessage ?? null;
|
|
121
|
+
const latestAssistantText = conversationMessageText(latestAssistantMessage);
|
|
124
122
|
const outputText = streamingText.trim() || latestAssistantText;
|
|
125
123
|
const finalOutput = isSettledStatus(status, isStreaming) ? output : null;
|
|
126
124
|
const finalOutputText = isSettledStatus(status, isStreaming) ? latestAssistantText : "";
|
|
@@ -17,4 +17,4 @@ export interface UseRecordsResult<TRecord extends Record<string, unknown> = Reco
|
|
|
17
17
|
refresh: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
|
|
18
18
|
loadMore: (overrides?: Partial<ListRecordsOptions>) => Promise<TRecord[]>;
|
|
19
19
|
}
|
|
20
|
-
export declare function useRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, filters, sort, limit, pageToken, offset,
|
|
20
|
+
export declare function useRecords<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, filters, sort, limit, pageToken, offset, enabled, autoLoad, }: UseRecordsOptions): UseRecordsResult<TRecord>;
|