lemma-sdk 0.2.28 → 0.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -201
- package/bin/lemma-sdk.js +108 -0
- package/dist/browser/lemma-client.js +125 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +3 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -2
- package/dist/datastore-query.d.ts +54 -0
- package/dist/datastore-query.js +157 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +3 -0
- package/dist/namespaces/datastore.d.ts +9 -0
- package/dist/namespaces/datastore.js +13 -0
- package/dist/namespaces/records.d.ts +1 -1
- package/dist/openapi_client/index.d.ts +4 -0
- package/dist/openapi_client/index.js +1 -0
- package/dist/openapi_client/models/ConvertedArtifactResponse.d.ts +6 -0
- package/dist/openapi_client/models/ConvertedFileResponse.d.ts +10 -0
- package/dist/openapi_client/models/ConvertedFileResponse.js +1 -0
- package/dist/openapi_client/models/CreateFolderRequest.d.ts +3 -1
- package/dist/openapi_client/models/FlowRunEntity.d.ts +1 -0
- package/dist/openapi_client/models/ScheduledFlowStart.d.ts +3 -6
- package/dist/openapi_client/models/ScheduledFlowStartType.d.ts +4 -0
- package/dist/openapi_client/models/ScheduledFlowStartType.js +9 -0
- package/dist/openapi_client/models/WorkflowInstallRequest.d.ts +5 -0
- package/dist/openapi_client/models/WorkflowTimeInstallConfig.d.ts +19 -0
- package/dist/openapi_client/models/WorkflowTimeInstallConfig.js +1 -0
- package/dist/openapi_client/services/FilesService.d.ts +27 -1
- package/dist/openapi_client/services/FilesService.js +69 -1
- package/dist/openapi_client/services/WorkflowsService.d.ts +1 -1
- package/dist/openapi_client/services/WorkflowsService.js +1 -1
- package/dist/react/assistant-output.d.ts +6 -0
- package/dist/react/assistant-output.js +90 -0
- package/dist/react/index.d.ts +62 -8
- package/dist/react/index.js +31 -4
- package/dist/react/useAgentInputSchema.d.ts +19 -0
- package/dist/react/useAgentInputSchema.js +73 -0
- package/dist/react/useAgentRun.d.ts +17 -0
- package/dist/react/useAgentRun.js +56 -0
- package/dist/react/useAgentRuns.d.ts +33 -0
- package/dist/react/useAgentRuns.js +149 -0
- package/dist/react/useAssistantRun.d.ts +9 -0
- package/dist/react/useAssistantRun.js +28 -17
- package/dist/react/useAssistantSession.d.ts +5 -0
- package/dist/react/useAssistantSession.js +135 -86
- package/dist/react/useBulkRecords.d.ts +20 -0
- package/dist/react/useBulkRecords.js +65 -0
- package/dist/react/useConversation.d.ts +18 -0
- package/dist/react/useConversation.js +75 -0
- package/dist/react/useConversationMessages.d.ts +59 -0
- package/dist/react/useConversationMessages.js +167 -0
- package/dist/react/useConversations.d.ts +52 -0
- package/dist/react/useConversations.js +228 -0
- package/dist/react/useCreateRecord.d.ts +18 -0
- package/dist/react/useCreateRecord.js +51 -0
- package/dist/react/useCurrentUser.d.ts +14 -0
- package/dist/react/useCurrentUser.js +68 -0
- package/dist/react/useDeleteRecord.d.ts +21 -0
- package/dist/react/useDeleteRecord.js +52 -0
- package/dist/react/useFlowRunHistory.js +1 -5
- package/dist/react/useFlowSession.js +41 -33
- package/dist/react/useForeignKeyOptions.d.ts +31 -0
- package/dist/react/useForeignKeyOptions.js +161 -0
- package/dist/react/useFunctionRun.d.ts +19 -0
- package/dist/react/useFunctionRun.js +30 -0
- package/dist/react/useFunctionRuns.d.ts +33 -0
- package/dist/react/useFunctionRuns.js +149 -0
- package/dist/react/useFunctionSession.js +37 -29
- package/dist/react/useJoinedRecords.d.ts +18 -0
- package/dist/react/useJoinedRecords.js +80 -0
- package/dist/react/useMembers.d.ts +26 -0
- package/dist/react/useMembers.js +98 -0
- package/dist/react/useOrganizationMembers.d.ts +26 -0
- package/dist/react/useOrganizationMembers.js +113 -0
- package/dist/react/usePodAccess.d.ts +22 -0
- package/dist/react/usePodAccess.js +128 -0
- package/dist/react/useRecord.d.ts +18 -0
- package/dist/react/useRecord.js +75 -0
- package/dist/react/useRecordForm.d.ts +42 -0
- package/dist/react/useRecordForm.js +221 -0
- package/dist/react/useRecordSchema.d.ts +20 -0
- package/dist/react/useRecordSchema.js +24 -0
- package/dist/react/useRecords.d.ts +20 -0
- package/dist/react/useRecords.js +146 -0
- package/dist/react/useRelatedRecords.d.ts +43 -0
- package/dist/react/useRelatedRecords.js +239 -0
- package/dist/react/useReverseRelatedRecords.d.ts +47 -0
- package/dist/react/useReverseRelatedRecords.js +235 -0
- package/dist/react/useSchemaForm.d.ts +24 -0
- package/dist/react/useSchemaForm.js +104 -0
- package/dist/react/useTable.d.ts +16 -0
- package/dist/react/useTable.js +70 -0
- package/dist/react/useTables.d.ts +26 -0
- package/dist/react/useTables.js +113 -0
- package/dist/react/useTaskSession.js +11 -22
- package/dist/react/useUpdateRecord.d.ts +21 -0
- package/dist/react/useUpdateRecord.js +55 -0
- package/dist/react/useWorkflowResume.d.ts +18 -0
- package/dist/react/useWorkflowResume.js +45 -0
- package/dist/react/useWorkflowRun.d.ts +21 -0
- package/dist/react/useWorkflowRun.js +49 -0
- package/dist/react/useWorkflowRuns.d.ts +33 -0
- package/dist/react/useWorkflowRuns.js +149 -0
- package/dist/react/useWorkflowStart.d.ts +33 -0
- package/dist/react/useWorkflowStart.js +148 -0
- package/dist/react/utils.d.ts +5 -0
- package/dist/react/utils.js +25 -0
- package/dist/record-form.d.ts +30 -0
- package/dist/record-form.js +199 -0
- package/dist/schema-form.d.ts +41 -0
- package/dist/schema-form.js +200 -0
- package/dist/types.d.ts +6 -1
- package/package.json +11 -8
- package/dist/react/components/AssistantChrome.d.ts +0 -86
- package/dist/react/components/AssistantChrome.js +0 -48
- package/dist/react/components/AssistantEmbedded.d.ts +0 -10
- package/dist/react/components/AssistantEmbedded.js +0 -15
- package/dist/react/components/AssistantExperience.d.ts +0 -96
- package/dist/react/components/AssistantExperience.js +0 -1294
- package/dist/react/components/assistant-types.d.ts +0 -80
- package/dist/react/styles.css +0 -2407
- /package/dist/{react/components/assistant-types.js → openapi_client/models/ConvertedArtifactResponse.js} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useAgentRuns({ client, podId, agentName, enabled = true, autoLoad = true, limit = 100, pageToken, initialTaskId = null, }) {
|
|
4
|
+
const [runs, setRuns] = useState([]);
|
|
5
|
+
const [total, setTotal] = useState(0);
|
|
6
|
+
const [nextPageToken, setNextPageToken] = useState(null);
|
|
7
|
+
const [selectedTaskId, setSelectedTaskId] = useState(initialTaskId);
|
|
8
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
9
|
+
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const refresh = useCallback(async (overrides = {}, signal) => {
|
|
12
|
+
if (!enabled) {
|
|
13
|
+
setRuns([]);
|
|
14
|
+
setTotal(0);
|
|
15
|
+
setNextPageToken(null);
|
|
16
|
+
setError(null);
|
|
17
|
+
setIsLoading(false);
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
setIsLoading(true);
|
|
21
|
+
setError(null);
|
|
22
|
+
try {
|
|
23
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
24
|
+
const response = await scopedClient.tasks.list({
|
|
25
|
+
agent_name: agentName,
|
|
26
|
+
limit: overrides.limit ?? limit,
|
|
27
|
+
page_token: overrides.pageToken ?? pageToken,
|
|
28
|
+
});
|
|
29
|
+
if (signal?.aborted)
|
|
30
|
+
return [];
|
|
31
|
+
const nextRuns = response.items ?? [];
|
|
32
|
+
setRuns(nextRuns);
|
|
33
|
+
setTotal(response.total ?? nextRuns.length);
|
|
34
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
35
|
+
setSelectedTaskId((current) => (current && nextRuns.some((run) => run.id === current) ? current : initialTaskId));
|
|
36
|
+
return nextRuns;
|
|
37
|
+
}
|
|
38
|
+
catch (refreshError) {
|
|
39
|
+
if (signal?.aborted)
|
|
40
|
+
return [];
|
|
41
|
+
const normalized = normalizeError(refreshError, "Failed to load agent runs.");
|
|
42
|
+
setError(normalized);
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
if (!signal?.aborted)
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
}
|
|
49
|
+
}, [agentName, client, enabled, initialTaskId, limit, pageToken, podId]);
|
|
50
|
+
const loadMore = useCallback(async (overrides = {}) => {
|
|
51
|
+
if (!enabled || !nextPageToken || isLoading || isLoadingMore) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
setIsLoadingMore(true);
|
|
55
|
+
setError(null);
|
|
56
|
+
try {
|
|
57
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
58
|
+
const response = await scopedClient.tasks.list({
|
|
59
|
+
agent_name: agentName,
|
|
60
|
+
limit: overrides.limit ?? limit,
|
|
61
|
+
page_token: nextPageToken,
|
|
62
|
+
});
|
|
63
|
+
const moreRuns = response.items ?? [];
|
|
64
|
+
setRuns((previous) => [...previous, ...moreRuns]);
|
|
65
|
+
setTotal(response.total ?? runs.length + moreRuns.length);
|
|
66
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
67
|
+
return moreRuns;
|
|
68
|
+
}
|
|
69
|
+
catch (loadError) {
|
|
70
|
+
const normalized = normalizeError(loadError, "Failed to load more agent runs.");
|
|
71
|
+
setError(normalized);
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
setIsLoadingMore(false);
|
|
76
|
+
}
|
|
77
|
+
}, [agentName, client, enabled, isLoading, isLoadingMore, limit, nextPageToken, podId]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setSelectedTaskId(initialTaskId);
|
|
80
|
+
}, [initialTaskId]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!enabled) {
|
|
83
|
+
setRuns([]);
|
|
84
|
+
setTotal(0);
|
|
85
|
+
setNextPageToken(null);
|
|
86
|
+
setError(null);
|
|
87
|
+
setIsLoading(false);
|
|
88
|
+
setIsLoadingMore(false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!autoLoad)
|
|
92
|
+
return;
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
let cancelled = false;
|
|
95
|
+
(async () => {
|
|
96
|
+
try {
|
|
97
|
+
await refresh({}, controller.signal);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
if (!cancelled) {
|
|
101
|
+
setError(normalizeError(new Error("Failed to load agent runs."), "Failed to load agent runs."));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
})();
|
|
105
|
+
return () => {
|
|
106
|
+
cancelled = true;
|
|
107
|
+
controller.abort();
|
|
108
|
+
};
|
|
109
|
+
}, [autoLoad, enabled, refresh]);
|
|
110
|
+
const selectRun = useCallback((taskId) => {
|
|
111
|
+
setSelectedTaskId(taskId);
|
|
112
|
+
}, []);
|
|
113
|
+
const clearSelection = useCallback(() => {
|
|
114
|
+
setSelectedTaskId(null);
|
|
115
|
+
}, []);
|
|
116
|
+
return useMemo(() => {
|
|
117
|
+
const effectiveSelectedTaskId = selectedTaskId ?? runs[0]?.id ?? null;
|
|
118
|
+
const selectedRun = effectiveSelectedTaskId
|
|
119
|
+
? runs.find((run) => run.id === effectiveSelectedTaskId) ?? null
|
|
120
|
+
: null;
|
|
121
|
+
return {
|
|
122
|
+
runs,
|
|
123
|
+
total,
|
|
124
|
+
nextPageToken,
|
|
125
|
+
selectedTaskId,
|
|
126
|
+
effectiveSelectedTaskId,
|
|
127
|
+
selectedRun,
|
|
128
|
+
isLoading,
|
|
129
|
+
isLoadingMore,
|
|
130
|
+
error,
|
|
131
|
+
selectRun,
|
|
132
|
+
clearSelection,
|
|
133
|
+
refresh,
|
|
134
|
+
loadMore,
|
|
135
|
+
};
|
|
136
|
+
}, [
|
|
137
|
+
clearSelection,
|
|
138
|
+
error,
|
|
139
|
+
isLoading,
|
|
140
|
+
isLoadingMore,
|
|
141
|
+
loadMore,
|
|
142
|
+
nextPageToken,
|
|
143
|
+
refresh,
|
|
144
|
+
runs,
|
|
145
|
+
selectRun,
|
|
146
|
+
selectedTaskId,
|
|
147
|
+
total,
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LemmaClient } from "../client.js";
|
|
2
2
|
import type { SseRawEvent } from "../streams.js";
|
|
3
|
+
import type { ConversationMessage } from "../types.js";
|
|
3
4
|
export interface UseAssistantRunOptions {
|
|
4
5
|
client: LemmaClient;
|
|
5
6
|
podId?: string;
|
|
@@ -10,6 +11,14 @@ export interface UseAssistantRunOptions {
|
|
|
10
11
|
export interface UseAssistantRunResult {
|
|
11
12
|
isStreaming: boolean;
|
|
12
13
|
error: Error | null;
|
|
14
|
+
status?: string;
|
|
15
|
+
messages: ConversationMessage[];
|
|
16
|
+
output: ConversationMessage["content"] | null;
|
|
17
|
+
outputText: string;
|
|
18
|
+
finalOutput: ConversationMessage["content"] | null;
|
|
19
|
+
finalOutputText: string;
|
|
20
|
+
latestAssistantMessage: ConversationMessage | null;
|
|
21
|
+
refresh: () => Promise<ConversationMessage[]>;
|
|
13
22
|
sendMessage: (content: string) => Promise<void>;
|
|
14
23
|
resume: () => Promise<void>;
|
|
15
24
|
stop: () => Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { useConversationMessages } from "./useConversationMessages.js";
|
|
2
3
|
function requireConversationId(conversationId) {
|
|
3
4
|
if (!conversationId) {
|
|
4
5
|
throw new Error("conversationId is required.");
|
|
@@ -6,31 +7,41 @@ function requireConversationId(conversationId) {
|
|
|
6
7
|
return conversationId;
|
|
7
8
|
}
|
|
8
9
|
export function useAssistantRun({ client, podId, conversationId, onEvent, onError, }) {
|
|
9
|
-
const
|
|
10
|
+
const messages = useConversationMessages({
|
|
10
11
|
client,
|
|
11
12
|
podId,
|
|
12
13
|
conversationId,
|
|
14
|
+
autoLoad: true,
|
|
15
|
+
autoResume: false,
|
|
13
16
|
onEvent,
|
|
14
17
|
onError,
|
|
15
18
|
});
|
|
16
|
-
const sendMessage = async (content) => {
|
|
17
|
-
await
|
|
18
|
-
conversationId: requireConversationId(conversationId ??
|
|
19
|
+
const sendMessage = useCallback(async (content) => {
|
|
20
|
+
await messages.sendMessage(content, {
|
|
21
|
+
conversationId: requireConversationId(conversationId ?? messages.conversationId),
|
|
19
22
|
createIfMissing: false,
|
|
20
23
|
});
|
|
21
|
-
};
|
|
22
|
-
const resume = async () => {
|
|
23
|
-
await
|
|
24
|
-
};
|
|
25
|
-
const stop = async () => {
|
|
26
|
-
await
|
|
27
|
-
};
|
|
28
|
-
return {
|
|
29
|
-
isStreaming:
|
|
30
|
-
error:
|
|
24
|
+
}, [conversationId, messages]);
|
|
25
|
+
const resume = useCallback(async () => {
|
|
26
|
+
await messages.resume(requireConversationId(conversationId ?? messages.conversationId));
|
|
27
|
+
}, [conversationId, messages]);
|
|
28
|
+
const stop = useCallback(async () => {
|
|
29
|
+
await messages.stop(requireConversationId(conversationId ?? messages.conversationId));
|
|
30
|
+
}, [conversationId, messages]);
|
|
31
|
+
return useMemo(() => ({
|
|
32
|
+
isStreaming: messages.isStreaming,
|
|
33
|
+
error: messages.error,
|
|
34
|
+
status: messages.status,
|
|
35
|
+
messages: messages.messages,
|
|
36
|
+
output: messages.output,
|
|
37
|
+
outputText: messages.outputText,
|
|
38
|
+
finalOutput: messages.finalOutput,
|
|
39
|
+
finalOutputText: messages.finalOutputText,
|
|
40
|
+
latestAssistantMessage: messages.latestAssistantMessage,
|
|
41
|
+
refresh: messages.refresh,
|
|
31
42
|
sendMessage,
|
|
32
43
|
resume,
|
|
33
44
|
stop,
|
|
34
|
-
cancel:
|
|
35
|
-
};
|
|
45
|
+
cancel: messages.cancel,
|
|
46
|
+
}), [messages, sendMessage, resume, stop]);
|
|
36
47
|
}
|
|
@@ -59,6 +59,11 @@ export interface UseAssistantSessionResult {
|
|
|
59
59
|
conversation: Conversation | null;
|
|
60
60
|
status?: string;
|
|
61
61
|
messages: ConversationMessage[];
|
|
62
|
+
latestAssistantMessage: ConversationMessage | null;
|
|
63
|
+
output: ConversationMessage["content"] | null;
|
|
64
|
+
outputText: string;
|
|
65
|
+
finalOutput: ConversationMessage["content"] | null;
|
|
66
|
+
finalOutputText: string;
|
|
62
67
|
streamingText: string;
|
|
63
68
|
isStreaming: boolean;
|
|
64
69
|
error: Error | null;
|
|
@@ -1,15 +1,14 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
import { extractConversationMessageText, getLatestAssistantMessage, } from "./assistant-output.js";
|
|
5
|
+
import { normalizeError } from "./utils.js";
|
|
7
6
|
function applyPodScope(client, podId) {
|
|
8
|
-
const resolvedPodId =
|
|
9
|
-
if (resolvedPodId) {
|
|
10
|
-
client.
|
|
7
|
+
const resolvedPodId = podId ?? client.podId;
|
|
8
|
+
if (resolvedPodId && resolvedPodId !== client.podId) {
|
|
9
|
+
return client.withPod(resolvedPodId);
|
|
11
10
|
}
|
|
12
|
-
return
|
|
11
|
+
return client;
|
|
13
12
|
}
|
|
14
13
|
function requireConversationId(conversationId) {
|
|
15
14
|
if (!conversationId) {
|
|
@@ -17,11 +16,6 @@ function requireConversationId(conversationId) {
|
|
|
17
16
|
}
|
|
18
17
|
return conversationId;
|
|
19
18
|
}
|
|
20
|
-
function normalizeError(error, fallback) {
|
|
21
|
-
if (error instanceof Error)
|
|
22
|
-
return error;
|
|
23
|
-
return new Error(fallback);
|
|
24
|
-
}
|
|
25
19
|
function normalizeScope(client, defaults, override) {
|
|
26
20
|
const resolvedAssistantName = override?.assistantName
|
|
27
21
|
?? override?.assistantId
|
|
@@ -140,10 +134,11 @@ export function useAssistantSession(options) {
|
|
|
140
134
|
organizationId: defaultOrganizationId ?? null,
|
|
141
135
|
}), [defaultAssistantId, defaultAssistantName, defaultOrganizationId, defaultPodId]);
|
|
142
136
|
const listConversations = useCallback(async (input = {}) => {
|
|
137
|
+
setError(null);
|
|
143
138
|
try {
|
|
144
139
|
const scope = normalizeScope(client, defaultScope, input.scope);
|
|
145
|
-
applyPodScope(client, scope.podId);
|
|
146
|
-
const response = await
|
|
140
|
+
const scopedClient = applyPodScope(client, scope.podId);
|
|
141
|
+
const response = await scopedClient.conversations.list({
|
|
147
142
|
pod_id: scope.podId ?? undefined,
|
|
148
143
|
assistant_name: scope.assistantName ?? scope.assistantId ?? undefined,
|
|
149
144
|
organization_id: scope.organizationId ?? undefined,
|
|
@@ -154,6 +149,7 @@ export function useAssistantSession(options) {
|
|
|
154
149
|
items: response.items ?? [],
|
|
155
150
|
limit: response.limit ?? input.limit ?? 20,
|
|
156
151
|
next_page_token: response.next_page_token,
|
|
152
|
+
total: response.total,
|
|
157
153
|
};
|
|
158
154
|
}
|
|
159
155
|
catch (listError) {
|
|
@@ -168,30 +164,39 @@ export function useAssistantSession(options) {
|
|
|
168
164
|
}
|
|
169
165
|
}, [client, defaultScope]);
|
|
170
166
|
const createConversation = useCallback(async (input = {}) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
: input.model
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
167
|
+
setError(null);
|
|
168
|
+
try {
|
|
169
|
+
const scopedClient = applyPodScope(client, input.podId ?? defaultPodId ?? null);
|
|
170
|
+
const payload = {
|
|
171
|
+
title: input.title ?? undefined,
|
|
172
|
+
pod_id: input.podId ?? defaultPodId ?? scopedClient.podId ?? undefined,
|
|
173
|
+
assistant_name: input.assistantName
|
|
174
|
+
?? input.assistantId
|
|
175
|
+
?? defaultAssistantName
|
|
176
|
+
?? defaultAssistantId
|
|
177
|
+
?? undefined,
|
|
178
|
+
organization_id: input.organizationId ?? defaultOrganizationId ?? undefined,
|
|
179
|
+
model: typeof input.model === "undefined"
|
|
180
|
+
? undefined
|
|
181
|
+
: input.model,
|
|
182
|
+
};
|
|
183
|
+
const created = await scopedClient.conversations.create(payload);
|
|
184
|
+
if (input.setActive !== false) {
|
|
185
|
+
setConversationIdState(created.id);
|
|
186
|
+
setConversation(created);
|
|
187
|
+
setConversationStatus(created.status);
|
|
188
|
+
setMessages([]);
|
|
189
|
+
clearStreamingText();
|
|
190
|
+
autoResumedKeyRef.current = null;
|
|
191
|
+
}
|
|
192
|
+
return created;
|
|
193
|
+
}
|
|
194
|
+
catch (createError) {
|
|
195
|
+
const normalized = normalizeError(createError, "Failed to create conversation.");
|
|
196
|
+
setError(normalized);
|
|
197
|
+
onErrorRef.current?.(createError);
|
|
198
|
+
throw normalized;
|
|
193
199
|
}
|
|
194
|
-
return created;
|
|
195
200
|
}, [
|
|
196
201
|
clearStreamingText,
|
|
197
202
|
client,
|
|
@@ -205,10 +210,11 @@ export function useAssistantSession(options) {
|
|
|
205
210
|
const id = explicitConversationId ?? conversationId;
|
|
206
211
|
if (!id)
|
|
207
212
|
return null;
|
|
213
|
+
setError(null);
|
|
208
214
|
try {
|
|
209
215
|
const scope = normalizeScope(client, defaultScope);
|
|
210
|
-
applyPodScope(client, scope.podId);
|
|
211
|
-
const nextConversation = await
|
|
216
|
+
const scopedClient = applyPodScope(client, scope.podId);
|
|
217
|
+
const nextConversation = await scopedClient.conversations.get(id, {
|
|
212
218
|
pod_id: scope.podId ?? undefined,
|
|
213
219
|
});
|
|
214
220
|
setConversation(nextConversation);
|
|
@@ -230,6 +236,7 @@ export function useAssistantSession(options) {
|
|
|
230
236
|
if (!id) {
|
|
231
237
|
return { items: [], limit: input.limit ?? 20, next_page_token: null };
|
|
232
238
|
}
|
|
239
|
+
setError(null);
|
|
233
240
|
try {
|
|
234
241
|
const response = await client.conversations.messages.list(id, {
|
|
235
242
|
limit: input.limit,
|
|
@@ -351,48 +358,66 @@ export function useAssistantSession(options) {
|
|
|
351
358
|
});
|
|
352
359
|
}, [conversation, conversationId, createConversation, refreshConversation]);
|
|
353
360
|
const sendMessage = useCallback(async (content, input = {}) => {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
361
|
+
setError(null);
|
|
362
|
+
try {
|
|
363
|
+
const resolvedConversation = await ensureConversation(input.conversationId, input);
|
|
364
|
+
const resolvedConversationId = requireConversationId(resolvedConversation.id);
|
|
365
|
+
cancel();
|
|
366
|
+
const controller = new AbortController();
|
|
367
|
+
abortRef.current = controller;
|
|
368
|
+
const scope = normalizeScope(client, defaultScope, input.createConversation);
|
|
369
|
+
const scopedClient = applyPodScope(client, scope.podId);
|
|
370
|
+
const stream = await scopedClient.conversations.sendMessageStream(resolvedConversationId, { content }, {
|
|
371
|
+
pod_id: scope.podId ?? undefined,
|
|
372
|
+
signal: controller.signal,
|
|
373
|
+
});
|
|
374
|
+
setConversationStatus("RUNNING");
|
|
375
|
+
await consume({
|
|
376
|
+
stream,
|
|
377
|
+
controller,
|
|
378
|
+
streamConversationId: resolvedConversationId,
|
|
379
|
+
syncAfterStream: input.syncOnTurnEnd,
|
|
380
|
+
});
|
|
381
|
+
return resolvedConversation;
|
|
382
|
+
}
|
|
383
|
+
catch (sendError) {
|
|
384
|
+
const normalized = normalizeError(sendError, "Failed to send assistant message.");
|
|
385
|
+
setError(normalized);
|
|
386
|
+
onErrorRef.current?.(sendError);
|
|
387
|
+
throw normalized;
|
|
388
|
+
}
|
|
373
389
|
}, [cancel, client, consume, defaultScope, ensureConversation, setConversationStatus]);
|
|
374
390
|
const resume = useCallback(async (input) => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
391
|
+
setError(null);
|
|
392
|
+
try {
|
|
393
|
+
const resumeInput = resolveResumeInput(input);
|
|
394
|
+
const id = requireConversationId(resumeInput.conversationId ?? conversationId);
|
|
395
|
+
if (resumeInput.onlyIfRunning && !isConversationRunningStatus(statusRef.current)) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
cancel();
|
|
399
|
+
const controller = new AbortController();
|
|
400
|
+
abortRef.current = controller;
|
|
401
|
+
const scope = normalizeScope(client, defaultScope);
|
|
402
|
+
const scopedClient = applyPodScope(client, scope.podId);
|
|
403
|
+
const stream = await scopedClient.conversations.resumeStream(id, {
|
|
404
|
+
pod_id: scope.podId ?? undefined,
|
|
405
|
+
signal: controller.signal,
|
|
406
|
+
});
|
|
407
|
+
setConversationStatus("RUNNING");
|
|
408
|
+
await consume({
|
|
409
|
+
stream,
|
|
410
|
+
controller,
|
|
411
|
+
streamConversationId: id,
|
|
412
|
+
syncAfterStream: resumeInput.syncOnTurnEnd,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
catch (resumeError) {
|
|
416
|
+
const normalized = normalizeError(resumeError, "Failed to resume assistant run.");
|
|
417
|
+
setError(normalized);
|
|
418
|
+
onErrorRef.current?.(resumeError);
|
|
419
|
+
throw normalized;
|
|
379
420
|
}
|
|
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
421
|
}, [cancel, client, consume, conversationId, defaultScope, setConversationStatus]);
|
|
397
422
|
const resumeIfRunning = useCallback(async (explicitConversationId) => {
|
|
398
423
|
const id = explicitConversationId ?? conversationId;
|
|
@@ -429,14 +454,23 @@ export function useAssistantSession(options) {
|
|
|
429
454
|
}
|
|
430
455
|
}, [conversationId, isStreaming, refreshConversation, resume]);
|
|
431
456
|
const stop = useCallback(async (explicitConversationId) => {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
457
|
+
setError(null);
|
|
458
|
+
try {
|
|
459
|
+
const id = requireConversationId(explicitConversationId ?? conversationId);
|
|
460
|
+
const scope = normalizeScope(client, defaultScope);
|
|
461
|
+
const scopedClient = applyPodScope(client, scope.podId);
|
|
462
|
+
await scopedClient.conversations.stopRun(id, {
|
|
463
|
+
pod_id: scope.podId ?? undefined,
|
|
464
|
+
});
|
|
465
|
+
setConversationStatus("WAITING");
|
|
466
|
+
clearStreamingText();
|
|
467
|
+
}
|
|
468
|
+
catch (stopError) {
|
|
469
|
+
const normalized = normalizeError(stopError, "Failed to stop assistant run.");
|
|
470
|
+
setError(normalized);
|
|
471
|
+
onErrorRef.current?.(stopError);
|
|
472
|
+
throw normalized;
|
|
473
|
+
}
|
|
440
474
|
}, [client, conversationId, defaultScope]);
|
|
441
475
|
const clearMessages = useCallback(() => {
|
|
442
476
|
setMessages([]);
|
|
@@ -453,6 +487,7 @@ export function useAssistantSession(options) {
|
|
|
453
487
|
if (!autoLoad || !conversationId) {
|
|
454
488
|
return;
|
|
455
489
|
}
|
|
490
|
+
const controller = new AbortController();
|
|
456
491
|
let cancelled = false;
|
|
457
492
|
const bootstrapConversation = async () => {
|
|
458
493
|
const latestConversation = await refreshConversation(conversationId);
|
|
@@ -471,13 +506,27 @@ export function useAssistantSession(options) {
|
|
|
471
506
|
void bootstrapConversation();
|
|
472
507
|
return () => {
|
|
473
508
|
cancelled = true;
|
|
509
|
+
controller.abort();
|
|
474
510
|
};
|
|
475
511
|
}, [autoLoad, autoResume, conversationId, loadMessages, refreshConversation, resumeIfRunning]);
|
|
512
|
+
const latestAssistantMessage = useMemo(() => getLatestAssistantMessage(messages), [messages]);
|
|
513
|
+
const output = latestAssistantMessage?.content ?? null;
|
|
514
|
+
const latestAssistantText = latestAssistantMessage
|
|
515
|
+
? extractConversationMessageText(latestAssistantMessage.content)
|
|
516
|
+
: "";
|
|
517
|
+
const outputText = streamingText.trim() || latestAssistantText;
|
|
518
|
+
const finalOutput = !isStreaming && !isConversationRunningStatus(status) ? output : null;
|
|
519
|
+
const finalOutputText = !isStreaming && !isConversationRunningStatus(status) ? latestAssistantText : "";
|
|
476
520
|
return {
|
|
477
521
|
conversationId,
|
|
478
522
|
conversation,
|
|
479
523
|
status,
|
|
480
524
|
messages,
|
|
525
|
+
latestAssistantMessage,
|
|
526
|
+
output,
|
|
527
|
+
outputText,
|
|
528
|
+
finalOutput,
|
|
529
|
+
finalOutputText,
|
|
481
530
|
streamingText,
|
|
482
531
|
isStreaming,
|
|
483
532
|
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,65 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useBulkRecords({ client, podId, tableName, enabled = true, onSuccess, onError, }) {
|
|
4
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
5
|
+
const [error, setError] = useState(null);
|
|
6
|
+
const [lastMessage, setLastMessage] = useState(null);
|
|
7
|
+
const onSuccessRef = useRef(onSuccess);
|
|
8
|
+
const onErrorRef = useRef(onError);
|
|
9
|
+
useEffect(() => { onSuccessRef.current = onSuccess; }, [onSuccess]);
|
|
10
|
+
useEffect(() => { onErrorRef.current = onError; }, [onError]);
|
|
11
|
+
const trimmedTableName = tableName.trim();
|
|
12
|
+
const isEnabled = enabled && trimmedTableName.length > 0;
|
|
13
|
+
const runBulkOperation = useCallback(async (action, fallbackError) => {
|
|
14
|
+
if (!isEnabled) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
setIsSubmitting(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
const scopedClient = resolvePodClient(client, podId);
|
|
21
|
+
const response = await action(scopedClient);
|
|
22
|
+
setLastMessage(response.message ?? null);
|
|
23
|
+
onSuccessRef.current?.(response);
|
|
24
|
+
return response;
|
|
25
|
+
}
|
|
26
|
+
catch (mutationError) {
|
|
27
|
+
const normalized = normalizeError(mutationError, fallbackError);
|
|
28
|
+
setError(normalized);
|
|
29
|
+
onErrorRef.current?.(mutationError);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
setIsSubmitting(false);
|
|
34
|
+
}
|
|
35
|
+
}, [client, isEnabled, podId]);
|
|
36
|
+
const createMany = useCallback(async (records) => {
|
|
37
|
+
if (records.length === 0)
|
|
38
|
+
return null;
|
|
39
|
+
return runBulkOperation((scopedClient) => scopedClient.records.bulk.create(trimmedTableName, records), "Failed to bulk create records.");
|
|
40
|
+
}, [runBulkOperation, trimmedTableName]);
|
|
41
|
+
const updateMany = useCallback(async (records) => {
|
|
42
|
+
if (records.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
return runBulkOperation((scopedClient) => scopedClient.records.bulk.update(trimmedTableName, records), "Failed to bulk update records.");
|
|
45
|
+
}, [runBulkOperation, trimmedTableName]);
|
|
46
|
+
const deleteMany = useCallback(async (recordIds) => {
|
|
47
|
+
if (recordIds.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
return runBulkOperation((scopedClient) => scopedClient.records.bulk.delete(trimmedTableName, recordIds), "Failed to bulk delete records.");
|
|
50
|
+
}, [runBulkOperation, trimmedTableName]);
|
|
51
|
+
const reset = useCallback(() => {
|
|
52
|
+
setError(null);
|
|
53
|
+
setIsSubmitting(false);
|
|
54
|
+
setLastMessage(null);
|
|
55
|
+
}, []);
|
|
56
|
+
return useMemo(() => ({
|
|
57
|
+
isSubmitting,
|
|
58
|
+
error,
|
|
59
|
+
lastMessage,
|
|
60
|
+
createMany,
|
|
61
|
+
updateMany,
|
|
62
|
+
deleteMany,
|
|
63
|
+
reset,
|
|
64
|
+
}), [createMany, deleteMany, error, isSubmitting, lastMessage, reset, updateMany]);
|
|
65
|
+
}
|
|
@@ -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;
|