lemma-sdk 0.2.27 → 0.2.30

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.
Files changed (90) hide show
  1. package/README.md +113 -233
  2. package/bin/lemma-sdk.js +108 -0
  3. package/dist/browser/lemma-client.js +125 -4
  4. package/dist/client.d.ts +2 -0
  5. package/dist/client.js +3 -0
  6. package/dist/config.d.ts +2 -2
  7. package/dist/config.js +2 -2
  8. package/dist/datastore-query.d.ts +54 -0
  9. package/dist/datastore-query.js +157 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.js +3 -0
  12. package/dist/namespaces/datastore.d.ts +9 -0
  13. package/dist/namespaces/datastore.js +13 -0
  14. package/dist/namespaces/records.d.ts +1 -1
  15. package/dist/openapi_client/index.d.ts +4 -0
  16. package/dist/openapi_client/index.js +1 -0
  17. package/dist/openapi_client/models/ConvertedArtifactResponse.d.ts +6 -0
  18. package/dist/openapi_client/models/ConvertedArtifactResponse.js +1 -0
  19. package/dist/openapi_client/models/ConvertedFileResponse.d.ts +10 -0
  20. package/dist/openapi_client/models/ConvertedFileResponse.js +1 -0
  21. package/dist/openapi_client/models/CreateFolderRequest.d.ts +3 -1
  22. package/dist/openapi_client/models/FlowRunEntity.d.ts +1 -0
  23. package/dist/openapi_client/models/ScheduledFlowStart.d.ts +3 -6
  24. package/dist/openapi_client/models/ScheduledFlowStartType.d.ts +4 -0
  25. package/dist/openapi_client/models/ScheduledFlowStartType.js +9 -0
  26. package/dist/openapi_client/models/WorkflowInstallRequest.d.ts +5 -0
  27. package/dist/openapi_client/models/WorkflowTimeInstallConfig.d.ts +19 -0
  28. package/dist/openapi_client/models/WorkflowTimeInstallConfig.js +1 -0
  29. package/dist/openapi_client/services/FilesService.d.ts +27 -1
  30. package/dist/openapi_client/services/FilesService.js +69 -1
  31. package/dist/openapi_client/services/WorkflowsService.d.ts +1 -1
  32. package/dist/openapi_client/services/WorkflowsService.js +1 -1
  33. package/dist/react/assistant-output.d.ts +6 -0
  34. package/dist/react/assistant-output.js +90 -0
  35. package/dist/react/components/AssistantExperience.js +6 -4
  36. package/dist/react/index.d.ts +42 -8
  37. package/dist/react/index.js +21 -4
  38. package/dist/react/useAgentRun.d.ts +17 -0
  39. package/dist/react/useAgentRun.js +58 -0
  40. package/dist/react/useAssistantRun.d.ts +9 -0
  41. package/dist/react/useAssistantRun.js +19 -9
  42. package/dist/react/useAssistantSession.d.ts +5 -0
  43. package/dist/react/useAssistantSession.js +123 -70
  44. package/dist/react/useBulkRecords.d.ts +20 -0
  45. package/dist/react/useBulkRecords.js +72 -0
  46. package/dist/react/useConversation.d.ts +18 -0
  47. package/dist/react/useConversation.js +59 -0
  48. package/dist/react/useConversationMessages.d.ts +59 -0
  49. package/dist/react/useConversationMessages.js +167 -0
  50. package/dist/react/useConversations.d.ts +48 -0
  51. package/dist/react/useConversations.js +182 -0
  52. package/dist/react/useCreateRecord.d.ts +18 -0
  53. package/dist/react/useCreateRecord.js +58 -0
  54. package/dist/react/useDeleteRecord.d.ts +21 -0
  55. package/dist/react/useDeleteRecord.js +59 -0
  56. package/dist/react/useForeignKeyOptions.d.ts +31 -0
  57. package/dist/react/useForeignKeyOptions.js +150 -0
  58. package/dist/react/useJoinedRecords.d.ts +18 -0
  59. package/dist/react/useJoinedRecords.js +79 -0
  60. package/dist/react/useMembers.d.ts +22 -0
  61. package/dist/react/useMembers.js +59 -0
  62. package/dist/react/useRecord.d.ts +18 -0
  63. package/dist/react/useRecord.js +64 -0
  64. package/dist/react/useRecordForm.d.ts +42 -0
  65. package/dist/react/useRecordForm.js +238 -0
  66. package/dist/react/useRecordSchema.d.ts +20 -0
  67. package/dist/react/useRecordSchema.js +24 -0
  68. package/dist/react/useRecords.d.ts +18 -0
  69. package/dist/react/useRecords.js +106 -0
  70. package/dist/react/useRelatedRecords.d.ts +43 -0
  71. package/dist/react/useRelatedRecords.js +232 -0
  72. package/dist/react/useReverseRelatedRecords.d.ts +47 -0
  73. package/dist/react/useReverseRelatedRecords.js +226 -0
  74. package/dist/react/useSchemaForm.d.ts +24 -0
  75. package/dist/react/useSchemaForm.js +116 -0
  76. package/dist/react/useTable.d.ts +16 -0
  77. package/dist/react/useTable.js +59 -0
  78. package/dist/react/useTables.d.ts +22 -0
  79. package/dist/react/useTables.js +71 -0
  80. package/dist/react/useUpdateRecord.d.ts +21 -0
  81. package/dist/react/useUpdateRecord.js +62 -0
  82. package/dist/react/useWorkflowStart.d.ts +33 -0
  83. package/dist/react/useWorkflowStart.js +155 -0
  84. package/dist/record-form.d.ts +30 -0
  85. package/dist/record-form.js +199 -0
  86. package/dist/schema-form.d.ts +41 -0
  87. package/dist/schema-form.js +200 -0
  88. package/dist/types.d.ts +5 -1
  89. package/package.json +10 -5
  90. package/dist/react/styles.css +0 -2401
@@ -1,6 +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 { extractConversationMessageText, getLatestAssistantMessage, } from "./assistant-output.js";
4
5
  function resolveOptionalPodId(client, podId) {
5
6
  return podId ?? client.podId;
6
7
  }
@@ -140,6 +141,7 @@ export function useAssistantSession(options) {
140
141
  organizationId: defaultOrganizationId ?? null,
141
142
  }), [defaultAssistantId, defaultAssistantName, defaultOrganizationId, defaultPodId]);
142
143
  const listConversations = useCallback(async (input = {}) => {
144
+ setError(null);
143
145
  try {
144
146
  const scope = normalizeScope(client, defaultScope, input.scope);
145
147
  applyPodScope(client, scope.podId);
@@ -168,30 +170,39 @@ export function useAssistantSession(options) {
168
170
  }
169
171
  }, [client, defaultScope]);
170
172
  const createConversation = useCallback(async (input = {}) => {
171
- applyPodScope(client, input.podId ?? defaultPodId ?? null);
172
- const payload = {
173
- title: input.title ?? undefined,
174
- pod_id: input.podId ?? defaultPodId ?? client.podId ?? undefined,
175
- assistant_name: input.assistantName
176
- ?? input.assistantId
177
- ?? defaultAssistantName
178
- ?? defaultAssistantId
179
- ?? undefined,
180
- organization_id: input.organizationId ?? defaultOrganizationId ?? undefined,
181
- model: typeof input.model === "undefined"
182
- ? undefined
183
- : input.model,
184
- };
185
- const created = await client.conversations.create(payload);
186
- if (input.setActive !== false) {
187
- setConversationIdState(created.id);
188
- setConversation(created);
189
- setConversationStatus(created.status);
190
- setMessages([]);
191
- clearStreamingText();
192
- autoResumedKeyRef.current = null;
173
+ setError(null);
174
+ try {
175
+ applyPodScope(client, input.podId ?? defaultPodId ?? null);
176
+ const payload = {
177
+ title: input.title ?? undefined,
178
+ pod_id: input.podId ?? defaultPodId ?? client.podId ?? undefined,
179
+ assistant_name: input.assistantName
180
+ ?? input.assistantId
181
+ ?? defaultAssistantName
182
+ ?? defaultAssistantId
183
+ ?? undefined,
184
+ organization_id: input.organizationId ?? defaultOrganizationId ?? undefined,
185
+ model: typeof input.model === "undefined"
186
+ ? undefined
187
+ : input.model,
188
+ };
189
+ const created = await client.conversations.create(payload);
190
+ if (input.setActive !== false) {
191
+ setConversationIdState(created.id);
192
+ setConversation(created);
193
+ setConversationStatus(created.status);
194
+ setMessages([]);
195
+ clearStreamingText();
196
+ autoResumedKeyRef.current = null;
197
+ }
198
+ return created;
199
+ }
200
+ catch (createError) {
201
+ const normalized = normalizeError(createError, "Failed to create conversation.");
202
+ setError(normalized);
203
+ onErrorRef.current?.(createError);
204
+ throw normalized;
193
205
  }
194
- return created;
195
206
  }, [
196
207
  clearStreamingText,
197
208
  client,
@@ -205,6 +216,7 @@ export function useAssistantSession(options) {
205
216
  const id = explicitConversationId ?? conversationId;
206
217
  if (!id)
207
218
  return null;
219
+ setError(null);
208
220
  try {
209
221
  const scope = normalizeScope(client, defaultScope);
210
222
  applyPodScope(client, scope.podId);
@@ -230,6 +242,7 @@ export function useAssistantSession(options) {
230
242
  if (!id) {
231
243
  return { items: [], limit: input.limit ?? 20, next_page_token: null };
232
244
  }
245
+ setError(null);
233
246
  try {
234
247
  const response = await client.conversations.messages.list(id, {
235
248
  limit: input.limit,
@@ -351,48 +364,66 @@ export function useAssistantSession(options) {
351
364
  });
352
365
  }, [conversation, conversationId, createConversation, refreshConversation]);
353
366
  const sendMessage = useCallback(async (content, input = {}) => {
354
- const resolvedConversation = await ensureConversation(input.conversationId, input);
355
- const resolvedConversationId = requireConversationId(resolvedConversation.id);
356
- cancel();
357
- const controller = new AbortController();
358
- abortRef.current = controller;
359
- const scope = normalizeScope(client, defaultScope, input.createConversation);
360
- applyPodScope(client, scope.podId);
361
- const stream = await client.conversations.sendMessageStream(resolvedConversationId, { content }, {
362
- pod_id: scope.podId ?? undefined,
363
- signal: controller.signal,
364
- });
365
- setConversationStatus("RUNNING");
366
- await consume({
367
- stream,
368
- controller,
369
- streamConversationId: resolvedConversationId,
370
- syncAfterStream: input.syncOnTurnEnd,
371
- });
372
- return resolvedConversation;
367
+ setError(null);
368
+ try {
369
+ const resolvedConversation = await ensureConversation(input.conversationId, input);
370
+ const resolvedConversationId = requireConversationId(resolvedConversation.id);
371
+ cancel();
372
+ const controller = new AbortController();
373
+ abortRef.current = controller;
374
+ const scope = normalizeScope(client, defaultScope, input.createConversation);
375
+ applyPodScope(client, scope.podId);
376
+ const stream = await client.conversations.sendMessageStream(resolvedConversationId, { content }, {
377
+ pod_id: scope.podId ?? undefined,
378
+ signal: controller.signal,
379
+ });
380
+ setConversationStatus("RUNNING");
381
+ await consume({
382
+ stream,
383
+ controller,
384
+ streamConversationId: resolvedConversationId,
385
+ syncAfterStream: input.syncOnTurnEnd,
386
+ });
387
+ return resolvedConversation;
388
+ }
389
+ catch (sendError) {
390
+ const normalized = normalizeError(sendError, "Failed to send assistant message.");
391
+ setError(normalized);
392
+ onErrorRef.current?.(sendError);
393
+ throw normalized;
394
+ }
373
395
  }, [cancel, client, consume, defaultScope, ensureConversation, setConversationStatus]);
374
396
  const resume = useCallback(async (input) => {
375
- const resumeInput = resolveResumeInput(input);
376
- const id = requireConversationId(resumeInput.conversationId ?? conversationId);
377
- if (resumeInput.onlyIfRunning && !isConversationRunningStatus(statusRef.current)) {
378
- return;
397
+ setError(null);
398
+ try {
399
+ const resumeInput = resolveResumeInput(input);
400
+ const id = requireConversationId(resumeInput.conversationId ?? conversationId);
401
+ if (resumeInput.onlyIfRunning && !isConversationRunningStatus(statusRef.current)) {
402
+ return;
403
+ }
404
+ cancel();
405
+ const controller = new AbortController();
406
+ abortRef.current = controller;
407
+ const scope = normalizeScope(client, defaultScope);
408
+ applyPodScope(client, scope.podId);
409
+ const stream = await client.conversations.resumeStream(id, {
410
+ pod_id: scope.podId ?? undefined,
411
+ signal: controller.signal,
412
+ });
413
+ setConversationStatus("RUNNING");
414
+ await consume({
415
+ stream,
416
+ controller,
417
+ streamConversationId: id,
418
+ syncAfterStream: resumeInput.syncOnTurnEnd,
419
+ });
420
+ }
421
+ catch (resumeError) {
422
+ const normalized = normalizeError(resumeError, "Failed to resume assistant run.");
423
+ setError(normalized);
424
+ onErrorRef.current?.(resumeError);
425
+ throw normalized;
379
426
  }
380
- cancel();
381
- const controller = new AbortController();
382
- abortRef.current = controller;
383
- const scope = normalizeScope(client, defaultScope);
384
- applyPodScope(client, scope.podId);
385
- const stream = await client.conversations.resumeStream(id, {
386
- pod_id: scope.podId ?? undefined,
387
- signal: controller.signal,
388
- });
389
- setConversationStatus("RUNNING");
390
- await consume({
391
- stream,
392
- controller,
393
- streamConversationId: id,
394
- syncAfterStream: resumeInput.syncOnTurnEnd,
395
- });
396
427
  }, [cancel, client, consume, conversationId, defaultScope, setConversationStatus]);
397
428
  const resumeIfRunning = useCallback(async (explicitConversationId) => {
398
429
  const id = explicitConversationId ?? conversationId;
@@ -429,14 +460,23 @@ export function useAssistantSession(options) {
429
460
  }
430
461
  }, [conversationId, isStreaming, refreshConversation, resume]);
431
462
  const stop = useCallback(async (explicitConversationId) => {
432
- const id = requireConversationId(explicitConversationId ?? conversationId);
433
- const scope = normalizeScope(client, defaultScope);
434
- applyPodScope(client, scope.podId);
435
- await client.conversations.stopRun(id, {
436
- pod_id: scope.podId ?? undefined,
437
- });
438
- setConversationStatus("WAITING");
439
- clearStreamingText();
463
+ setError(null);
464
+ try {
465
+ const id = requireConversationId(explicitConversationId ?? conversationId);
466
+ const scope = normalizeScope(client, defaultScope);
467
+ applyPodScope(client, scope.podId);
468
+ await client.conversations.stopRun(id, {
469
+ pod_id: scope.podId ?? undefined,
470
+ });
471
+ setConversationStatus("WAITING");
472
+ clearStreamingText();
473
+ }
474
+ catch (stopError) {
475
+ const normalized = normalizeError(stopError, "Failed to stop assistant run.");
476
+ setError(normalized);
477
+ onErrorRef.current?.(stopError);
478
+ throw normalized;
479
+ }
440
480
  }, [client, conversationId, defaultScope]);
441
481
  const clearMessages = useCallback(() => {
442
482
  setMessages([]);
@@ -473,11 +513,24 @@ export function useAssistantSession(options) {
473
513
  cancelled = true;
474
514
  };
475
515
  }, [autoLoad, autoResume, conversationId, loadMessages, refreshConversation, resumeIfRunning]);
516
+ const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
517
+ const output = latestAssistantMessage?.content ?? null;
518
+ const latestAssistantText = latestAssistantMessage
519
+ ? extractConversationMessageText(latestAssistantMessage.content)
520
+ : "";
521
+ const outputText = streamingText.trim() || latestAssistantText;
522
+ const finalOutput = !isStreaming && !isConversationRunningStatus(status) ? output : null;
523
+ const finalOutputText = !isStreaming && !isConversationRunningStatus(status) ? latestAssistantText : "";
476
524
  return {
477
525
  conversationId,
478
526
  conversation,
479
527
  status,
480
528
  messages,
529
+ latestAssistantMessage,
530
+ output,
531
+ outputText,
532
+ finalOutput,
533
+ finalOutputText,
481
534
  streamingText,
482
535
  isStreaming,
483
536
  error,
@@ -0,0 +1,20 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { DatastoreMessageResponse } from "../types.js";
3
+ export interface UseBulkRecordsOptions {
4
+ client: LemmaClient;
5
+ podId?: string;
6
+ tableName: string;
7
+ enabled?: boolean;
8
+ onSuccess?: (response: DatastoreMessageResponse) => void;
9
+ onError?: (error: unknown) => void;
10
+ }
11
+ export interface UseBulkRecordsResult {
12
+ isSubmitting: boolean;
13
+ error: Error | null;
14
+ lastMessage: string | null;
15
+ createMany: (records: Record<string, unknown>[]) => Promise<DatastoreMessageResponse | null>;
16
+ updateMany: (records: Record<string, unknown>[]) => Promise<DatastoreMessageResponse | null>;
17
+ deleteMany: (recordIds: Array<string | number>) => Promise<DatastoreMessageResponse | null>;
18
+ reset: () => void;
19
+ }
20
+ export declare function useBulkRecords({ client, podId, tableName, enabled, onSuccess, onError, }: UseBulkRecordsOptions): UseBulkRecordsResult;
@@ -0,0 +1,72 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ function resolvePodClient(client, podId) {
3
+ if (!podId || podId === client.podId)
4
+ return client;
5
+ return client.withPod(podId);
6
+ }
7
+ function normalizeError(error, fallback) {
8
+ if (error instanceof Error)
9
+ return error;
10
+ return new Error(fallback);
11
+ }
12
+ export function useBulkRecords({ client, podId, tableName, enabled = true, onSuccess, onError, }) {
13
+ const [isSubmitting, setIsSubmitting] = useState(false);
14
+ const [error, setError] = useState(null);
15
+ const [lastMessage, setLastMessage] = useState(null);
16
+ const trimmedTableName = tableName.trim();
17
+ const isEnabled = enabled && trimmedTableName.length > 0;
18
+ const runBulkOperation = useCallback(async (action, fallbackError) => {
19
+ if (!isEnabled) {
20
+ const disabledError = new Error("Bulk record operations are disabled.");
21
+ setError(disabledError);
22
+ return null;
23
+ }
24
+ setIsSubmitting(true);
25
+ setError(null);
26
+ try {
27
+ const scopedClient = resolvePodClient(client, podId);
28
+ const response = await action(scopedClient);
29
+ setLastMessage(response.message ?? null);
30
+ onSuccess?.(response);
31
+ return response;
32
+ }
33
+ catch (mutationError) {
34
+ const normalized = normalizeError(mutationError, fallbackError);
35
+ setError(normalized);
36
+ onError?.(mutationError);
37
+ return null;
38
+ }
39
+ finally {
40
+ setIsSubmitting(false);
41
+ }
42
+ }, [client, isEnabled, onError, onSuccess, podId]);
43
+ const createMany = useCallback(async (records) => {
44
+ if (records.length === 0)
45
+ return null;
46
+ return runBulkOperation((scopedClient) => scopedClient.records.bulk.create(trimmedTableName, records), "Failed to bulk create records.");
47
+ }, [runBulkOperation, trimmedTableName]);
48
+ const updateMany = useCallback(async (records) => {
49
+ if (records.length === 0)
50
+ return null;
51
+ return runBulkOperation((scopedClient) => scopedClient.records.bulk.update(trimmedTableName, records), "Failed to bulk update records.");
52
+ }, [runBulkOperation, trimmedTableName]);
53
+ const deleteMany = useCallback(async (recordIds) => {
54
+ if (recordIds.length === 0)
55
+ return null;
56
+ return runBulkOperation((scopedClient) => scopedClient.records.bulk.delete(trimmedTableName, recordIds), "Failed to bulk delete records.");
57
+ }, [runBulkOperation, trimmedTableName]);
58
+ const reset = useCallback(() => {
59
+ setError(null);
60
+ setIsSubmitting(false);
61
+ setLastMessage(null);
62
+ }, []);
63
+ return useMemo(() => ({
64
+ isSubmitting,
65
+ error,
66
+ lastMessage,
67
+ createMany,
68
+ updateMany,
69
+ deleteMany,
70
+ reset,
71
+ }), [createMany, deleteMany, error, isSubmitting, lastMessage, reset, updateMany]);
72
+ }
@@ -0,0 +1,18 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { Conversation } from "../types.js";
3
+ export interface UseConversationOptions {
4
+ client: LemmaClient;
5
+ podId?: string;
6
+ conversationId?: string | null;
7
+ enabled?: boolean;
8
+ autoLoad?: boolean;
9
+ }
10
+ export interface UseConversationResult {
11
+ conversation: Conversation | null;
12
+ isLoading: boolean;
13
+ error: Error | null;
14
+ refresh: (overrides?: {
15
+ conversationId?: string | null;
16
+ }) => Promise<Conversation | null>;
17
+ }
18
+ export declare function useConversation({ client, podId, conversationId, enabled, autoLoad, }: UseConversationOptions): UseConversationResult;
@@ -0,0 +1,59 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ function normalizeError(error, fallback) {
3
+ if (error instanceof Error)
4
+ return error;
5
+ return new Error(fallback);
6
+ }
7
+ export function useConversation({ client, podId, conversationId = null, enabled = true, autoLoad = true, }) {
8
+ const [conversation, setConversation] = useState(null);
9
+ const [isLoading, setIsLoading] = useState(false);
10
+ const [error, setError] = useState(null);
11
+ const trimmedConversationId = typeof conversationId === "string" ? conversationId.trim() : "";
12
+ const isEnabled = enabled && trimmedConversationId.length > 0;
13
+ const refresh = useCallback(async (overrides = {}) => {
14
+ const nextConversationId = typeof overrides.conversationId === "string"
15
+ ? overrides.conversationId.trim()
16
+ : trimmedConversationId;
17
+ if (!enabled || nextConversationId.length === 0) {
18
+ setConversation(null);
19
+ setError(null);
20
+ setIsLoading(false);
21
+ return null;
22
+ }
23
+ setIsLoading(true);
24
+ setError(null);
25
+ try {
26
+ const scopedClient = podId ? client.withPod(podId) : client;
27
+ const nextConversation = await scopedClient.conversations.get(nextConversationId, {
28
+ pod_id: podId,
29
+ });
30
+ setConversation(nextConversation);
31
+ return nextConversation;
32
+ }
33
+ catch (refreshError) {
34
+ const normalized = normalizeError(refreshError, "Failed to load conversation.");
35
+ setError(normalized);
36
+ return null;
37
+ }
38
+ finally {
39
+ setIsLoading(false);
40
+ }
41
+ }, [client, enabled, podId, trimmedConversationId]);
42
+ useEffect(() => {
43
+ if (!isEnabled) {
44
+ setConversation(null);
45
+ setError(null);
46
+ setIsLoading(false);
47
+ return;
48
+ }
49
+ if (!autoLoad)
50
+ return;
51
+ void refresh();
52
+ }, [autoLoad, isEnabled, refresh]);
53
+ return useMemo(() => ({
54
+ conversation,
55
+ isLoading,
56
+ error,
57
+ refresh,
58
+ }), [conversation, error, isLoading, refresh]);
59
+ }
@@ -0,0 +1,59 @@
1
+ import type { LemmaClient } from "../client.js";
2
+ import type { SseRawEvent } from "../streams.js";
3
+ import type { Conversation, ConversationMessage } from "../types.js";
4
+ import { type CreateConversationInput, type ResumeAssistantOptions, type SendAssistantMessageOptions } from "./useAssistantSession.js";
5
+ export interface UseConversationMessagesOptions {
6
+ client: LemmaClient;
7
+ podId?: string;
8
+ assistantName?: string;
9
+ /**
10
+ * @deprecated Use assistantName instead.
11
+ */
12
+ assistantId?: string;
13
+ organizationId?: string;
14
+ conversationId?: string | null;
15
+ enabled?: boolean;
16
+ autoLoad?: boolean;
17
+ autoResume?: boolean;
18
+ limit?: number;
19
+ syncOnTurnEnd?: boolean;
20
+ onEvent?: (event: SseRawEvent, payload: unknown | null) => void;
21
+ onStatus?: (status: string) => void;
22
+ onMessage?: (message: ConversationMessage) => void;
23
+ onError?: (error: unknown) => void;
24
+ }
25
+ export interface UseConversationMessagesResult {
26
+ conversationId: string | null;
27
+ conversation: Conversation | null;
28
+ messages: ConversationMessage[];
29
+ status?: string;
30
+ isRunning: boolean;
31
+ isStreaming: boolean;
32
+ isLoading: boolean;
33
+ isLoadingOlder: boolean;
34
+ hasOlderMessages: boolean;
35
+ nextPageToken: string | null;
36
+ streamingText: string;
37
+ latestAssistantMessage: ConversationMessage | null;
38
+ output: ConversationMessage["content"] | null;
39
+ outputText: string;
40
+ finalOutput: ConversationMessage["content"] | null;
41
+ finalOutputText: string;
42
+ error: Error | null;
43
+ refresh: (options?: {
44
+ conversationId?: string | null;
45
+ limit?: number;
46
+ pageToken?: string;
47
+ }) => Promise<ConversationMessage[]>;
48
+ loadOlder: (options?: {
49
+ limit?: number;
50
+ }) => Promise<ConversationMessage[]>;
51
+ sendMessage: (content: string, options?: SendAssistantMessageOptions) => Promise<Conversation>;
52
+ resume: (conversationId?: string | null | ResumeAssistantOptions) => Promise<void>;
53
+ resumeIfRunning: (conversationId?: string | null) => Promise<boolean>;
54
+ stop: (conversationId?: string | null) => Promise<void>;
55
+ cancel: () => void;
56
+ clearMessages: () => void;
57
+ createConversation: (input?: CreateConversationInput) => Promise<Conversation>;
58
+ }
59
+ export declare function useConversationMessages({ client, podId, assistantName, assistantId, organizationId, conversationId, enabled, autoLoad, autoResume, limit, syncOnTurnEnd, onEvent, onStatus, onMessage, onError, }: UseConversationMessagesOptions): UseConversationMessagesResult;
@@ -0,0 +1,167 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { extractConversationMessageText, getLatestAssistantMessage, isConversationRunningStatus, normalizeConversationStatus, sortConversationMessagesByCreatedAt, } from "./assistant-output.js";
3
+ import { useAssistantSession, } from "./useAssistantSession.js";
4
+ function resolveConversationId(preferred, fallback) {
5
+ return preferred ?? fallback ?? null;
6
+ }
7
+ function isSettledStatus(status, isStreaming) {
8
+ if (isStreaming)
9
+ return false;
10
+ const normalized = normalizeConversationStatus(status);
11
+ if (!normalized)
12
+ return true;
13
+ return !isConversationRunningStatus(normalized);
14
+ }
15
+ export function useConversationMessages({ client, podId, assistantName, assistantId, organizationId, conversationId = null, enabled = true, autoLoad = true, autoResume = false, limit = 100, syncOnTurnEnd = false, onEvent, onStatus, onMessage, onError, }) {
16
+ const [nextPageToken, setNextPageToken] = useState(null);
17
+ const [isLoading, setIsLoading] = useState(false);
18
+ const [isLoadingOlder, setIsLoadingOlder] = useState(false);
19
+ const { conversation: sessionConversation, conversationId: sessionConversationId, messages: sessionMessages, status, streamingText, isStreaming, error, refreshConversation, loadMessages, sendMessage, resume, resumeIfRunning, stop, cancel, clearMessages: clearSessionMessages, createConversation, } = useAssistantSession({
20
+ client,
21
+ podId,
22
+ assistantName,
23
+ assistantId,
24
+ organizationId,
25
+ conversationId: conversationId ?? undefined,
26
+ autoLoad: false,
27
+ autoResume: false,
28
+ syncOnTurnEnd,
29
+ onEvent,
30
+ onStatus,
31
+ onMessage,
32
+ onError,
33
+ });
34
+ const refresh = useCallback(async (options = {}) => {
35
+ const targetConversationId = resolveConversationId(options.conversationId, sessionConversationId);
36
+ if (!enabled || !targetConversationId) {
37
+ setNextPageToken(null);
38
+ setIsLoading(false);
39
+ return [];
40
+ }
41
+ setIsLoading(true);
42
+ try {
43
+ await refreshConversation(targetConversationId);
44
+ const response = await loadMessages({
45
+ conversationId: targetConversationId,
46
+ limit: options.limit ?? limit,
47
+ pageToken: options.pageToken,
48
+ });
49
+ setNextPageToken(response.next_page_token ?? null);
50
+ return sortConversationMessagesByCreatedAt(response.items ?? []);
51
+ }
52
+ finally {
53
+ setIsLoading(false);
54
+ }
55
+ }, [enabled, limit, loadMessages, refreshConversation, sessionConversationId]);
56
+ const loadOlder = useCallback(async (options = {}) => {
57
+ const targetConversationId = sessionConversationId;
58
+ if (!enabled || !targetConversationId || !nextPageToken || isLoading || isLoadingOlder) {
59
+ return [];
60
+ }
61
+ setIsLoadingOlder(true);
62
+ try {
63
+ const response = await loadMessages({
64
+ conversationId: targetConversationId,
65
+ limit: options.limit ?? limit,
66
+ pageToken: nextPageToken,
67
+ });
68
+ setNextPageToken(response.next_page_token ?? null);
69
+ return sortConversationMessagesByCreatedAt(response.items ?? []);
70
+ }
71
+ finally {
72
+ setIsLoadingOlder(false);
73
+ }
74
+ }, [enabled, isLoading, isLoadingOlder, limit, loadMessages, nextPageToken, sessionConversationId]);
75
+ useEffect(() => {
76
+ if (!enabled || !conversationId) {
77
+ setNextPageToken(null);
78
+ setIsLoading(false);
79
+ setIsLoadingOlder(false);
80
+ cancel();
81
+ clearSessionMessages();
82
+ return;
83
+ }
84
+ setNextPageToken(null);
85
+ if (!autoLoad)
86
+ return;
87
+ let cancelled = false;
88
+ const bootstrap = async () => {
89
+ await refresh({ conversationId, limit });
90
+ if (cancelled || !autoResume)
91
+ return;
92
+ await resumeIfRunning(conversationId);
93
+ };
94
+ void bootstrap();
95
+ return () => {
96
+ cancelled = true;
97
+ };
98
+ }, [autoLoad, autoResume, cancel, clearSessionMessages, conversationId, enabled, limit, refresh, resumeIfRunning]);
99
+ const messages = useMemo(() => sortConversationMessagesByCreatedAt(sessionMessages), [sessionMessages]);
100
+ const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
101
+ const output = latestAssistantMessage?.content ?? null;
102
+ const latestAssistantText = latestAssistantMessage
103
+ ? extractConversationMessageText(latestAssistantMessage.content)
104
+ : "";
105
+ const outputText = streamingText.trim() || latestAssistantText;
106
+ const finalOutput = isSettledStatus(status, isStreaming) ? output : null;
107
+ const finalOutputText = isSettledStatus(status, isStreaming) ? latestAssistantText : "";
108
+ const isRunning = isConversationRunningStatus(status) || isStreaming;
109
+ const clearMessages = useCallback(() => {
110
+ clearSessionMessages();
111
+ setNextPageToken(null);
112
+ }, [clearSessionMessages]);
113
+ return useMemo(() => ({
114
+ conversationId: sessionConversationId,
115
+ conversation: sessionConversation,
116
+ messages,
117
+ status,
118
+ isRunning,
119
+ isStreaming,
120
+ isLoading,
121
+ isLoadingOlder,
122
+ hasOlderMessages: !!nextPageToken,
123
+ nextPageToken,
124
+ streamingText,
125
+ latestAssistantMessage,
126
+ output,
127
+ outputText,
128
+ finalOutput,
129
+ finalOutputText,
130
+ error,
131
+ refresh,
132
+ loadOlder,
133
+ sendMessage,
134
+ resume,
135
+ resumeIfRunning,
136
+ stop,
137
+ cancel,
138
+ clearMessages,
139
+ createConversation,
140
+ }), [
141
+ cancel,
142
+ clearMessages,
143
+ createConversation,
144
+ error,
145
+ finalOutput,
146
+ finalOutputText,
147
+ isLoading,
148
+ isLoadingOlder,
149
+ isRunning,
150
+ isStreaming,
151
+ latestAssistantMessage,
152
+ loadOlder,
153
+ messages,
154
+ nextPageToken,
155
+ output,
156
+ outputText,
157
+ refresh,
158
+ resume,
159
+ resumeIfRunning,
160
+ sendMessage,
161
+ sessionConversation,
162
+ sessionConversationId,
163
+ status,
164
+ stop,
165
+ streamingText,
166
+ ]);
167
+ }