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,75 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { normalizeError } from "./utils.js";
|
|
3
|
+
export function useConversation({ client, podId, conversationId = null, enabled = true, autoLoad = true, }) {
|
|
4
|
+
const [conversation, setConversation] = useState(null);
|
|
5
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
const trimmedConversationId = typeof conversationId === "string" ? conversationId.trim() : "";
|
|
8
|
+
const isEnabled = enabled && trimmedConversationId.length > 0;
|
|
9
|
+
const refresh = useCallback(async (overrides = {}, signal) => {
|
|
10
|
+
const nextConversationId = typeof overrides.conversationId === "string"
|
|
11
|
+
? overrides.conversationId.trim()
|
|
12
|
+
: trimmedConversationId;
|
|
13
|
+
if (!enabled || nextConversationId.length === 0) {
|
|
14
|
+
setConversation(null);
|
|
15
|
+
setError(null);
|
|
16
|
+
setIsLoading(false);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
const scopedClient = podId ? client.withPod(podId) : client;
|
|
23
|
+
const nextConversation = await scopedClient.conversations.get(nextConversationId, {
|
|
24
|
+
pod_id: podId,
|
|
25
|
+
});
|
|
26
|
+
if (signal?.aborted)
|
|
27
|
+
return null;
|
|
28
|
+
setConversation(nextConversation);
|
|
29
|
+
return nextConversation;
|
|
30
|
+
}
|
|
31
|
+
catch (refreshError) {
|
|
32
|
+
if (signal?.aborted)
|
|
33
|
+
return null;
|
|
34
|
+
const normalized = normalizeError(refreshError, "Failed to load conversation.");
|
|
35
|
+
setError(normalized);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
if (!signal?.aborted)
|
|
40
|
+
setIsLoading(false);
|
|
41
|
+
}
|
|
42
|
+
}, [client, enabled, podId, trimmedConversationId]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!isEnabled) {
|
|
45
|
+
setConversation(null);
|
|
46
|
+
setError(null);
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!autoLoad)
|
|
51
|
+
return;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
let cancelled = false;
|
|
54
|
+
(async () => {
|
|
55
|
+
try {
|
|
56
|
+
await refresh({}, controller.signal);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
if (!cancelled) {
|
|
60
|
+
setError(normalizeError(new Error("Failed to load conversation."), "Failed to load conversation."));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
return () => {
|
|
65
|
+
cancelled = true;
|
|
66
|
+
controller.abort();
|
|
67
|
+
};
|
|
68
|
+
}, [autoLoad, isEnabled, refresh]);
|
|
69
|
+
return useMemo(() => ({
|
|
70
|
+
conversation,
|
|
71
|
+
isLoading,
|
|
72
|
+
error,
|
|
73
|
+
refresh,
|
|
74
|
+
}), [conversation, error, isLoading, refresh]);
|
|
75
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { Conversation } from "../types.js";
|
|
3
|
+
import { type CreateConversationInput } from "./useAssistantSession.js";
|
|
4
|
+
export interface UseConversationsOptions {
|
|
5
|
+
client: LemmaClient;
|
|
6
|
+
podId?: string;
|
|
7
|
+
assistantName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Use assistantName instead.
|
|
10
|
+
*/
|
|
11
|
+
assistantId?: string;
|
|
12
|
+
organizationId?: string;
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
autoLoad?: boolean;
|
|
15
|
+
autoSelectFirst?: boolean;
|
|
16
|
+
limit?: number;
|
|
17
|
+
pageToken?: string;
|
|
18
|
+
initialConversationId?: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface UseConversationsResult {
|
|
21
|
+
conversations: Conversation[];
|
|
22
|
+
total: number;
|
|
23
|
+
nextPageToken: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated Use selectedConversationId instead.
|
|
26
|
+
*/
|
|
27
|
+
activeConversationId: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Use selectedConversation instead.
|
|
30
|
+
*/
|
|
31
|
+
activeConversation: Conversation | null;
|
|
32
|
+
selectedConversationId: string | null;
|
|
33
|
+
effectiveSelectedConversationId: string | null;
|
|
34
|
+
selectedConversation: Conversation | null;
|
|
35
|
+
isLoading: boolean;
|
|
36
|
+
isLoadingMore: boolean;
|
|
37
|
+
error: Error | null;
|
|
38
|
+
selectConversation: (conversationId: string | null) => void;
|
|
39
|
+
clearSelection: () => void;
|
|
40
|
+
selectLatestConversation: () => string | null;
|
|
41
|
+
refresh: (overrides?: {
|
|
42
|
+
limit?: number;
|
|
43
|
+
pageToken?: string;
|
|
44
|
+
}) => Promise<Conversation[]>;
|
|
45
|
+
loadMore: (overrides?: {
|
|
46
|
+
limit?: number;
|
|
47
|
+
}) => Promise<Conversation[]>;
|
|
48
|
+
createConversation: (input?: CreateConversationInput) => Promise<Conversation>;
|
|
49
|
+
createAndSelectConversation: (input?: Omit<CreateConversationInput, "setActive">) => Promise<Conversation>;
|
|
50
|
+
ensureConversation: (input?: Omit<CreateConversationInput, "setActive">) => Promise<Conversation>;
|
|
51
|
+
}
|
|
52
|
+
export declare function useConversations({ client, podId, assistantName, assistantId, organizationId, enabled, autoLoad, autoSelectFirst, limit, pageToken, initialConversationId, }: UseConversationsOptions): UseConversationsResult;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useAssistantSession, } from "./useAssistantSession.js";
|
|
3
|
+
import { normalizeError } from "./utils.js";
|
|
4
|
+
function sortConversationsByUpdatedAt(conversations) {
|
|
5
|
+
return [...conversations].sort((a, b) => {
|
|
6
|
+
const aTime = new Date(a.updated_at || a.created_at).getTime();
|
|
7
|
+
const bTime = new Date(b.updated_at || b.created_at).getTime();
|
|
8
|
+
return bTime - aTime;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function useConversations({ client, podId, assistantName, assistantId, organizationId, enabled = true, autoLoad = true, autoSelectFirst = true, limit = 20, pageToken, initialConversationId = null, }) {
|
|
12
|
+
const [conversations, setConversations] = useState([]);
|
|
13
|
+
const [total, setTotal] = useState(0);
|
|
14
|
+
const [nextPageToken, setNextPageToken] = useState(null);
|
|
15
|
+
const [selectedConversationId, setSelectedConversationId] = useState(initialConversationId);
|
|
16
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
17
|
+
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
18
|
+
const scopeKey = useMemo(() => JSON.stringify({
|
|
19
|
+
podId: podId ?? null,
|
|
20
|
+
assistantName: assistantName ?? assistantId ?? null,
|
|
21
|
+
assistantId: assistantId ?? null,
|
|
22
|
+
organizationId: organizationId ?? null,
|
|
23
|
+
}), [assistantId, assistantName, organizationId, podId]);
|
|
24
|
+
const { error, listConversations, createConversation: sessionCreateConversation, } = useAssistantSession({
|
|
25
|
+
client,
|
|
26
|
+
podId,
|
|
27
|
+
assistantName,
|
|
28
|
+
assistantId,
|
|
29
|
+
organizationId,
|
|
30
|
+
autoLoad: false,
|
|
31
|
+
});
|
|
32
|
+
const refresh = useCallback(async (overrides = {}) => {
|
|
33
|
+
if (!enabled) {
|
|
34
|
+
setConversations([]);
|
|
35
|
+
setTotal(0);
|
|
36
|
+
setNextPageToken(null);
|
|
37
|
+
setIsLoading(false);
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
setIsLoading(true);
|
|
41
|
+
try {
|
|
42
|
+
const response = await listConversations({
|
|
43
|
+
limit: overrides.limit ?? limit,
|
|
44
|
+
pageToken: overrides.pageToken ?? pageToken,
|
|
45
|
+
});
|
|
46
|
+
const nextConversations = sortConversationsByUpdatedAt(response.items ?? []);
|
|
47
|
+
setConversations(nextConversations);
|
|
48
|
+
setTotal(response.total ?? nextConversations.length);
|
|
49
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
50
|
+
setSelectedConversationId((current) => {
|
|
51
|
+
if (current)
|
|
52
|
+
return current;
|
|
53
|
+
if (initialConversationId)
|
|
54
|
+
return initialConversationId;
|
|
55
|
+
if (!autoSelectFirst)
|
|
56
|
+
return null;
|
|
57
|
+
return nextConversations[0]?.id ?? null;
|
|
58
|
+
});
|
|
59
|
+
return nextConversations;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
setIsLoading(false);
|
|
63
|
+
}
|
|
64
|
+
}, [autoSelectFirst, enabled, initialConversationId, limit, listConversations, pageToken]);
|
|
65
|
+
const loadMore = useCallback(async (overrides = {}) => {
|
|
66
|
+
if (!enabled || !nextPageToken || isLoading || isLoadingMore) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
setIsLoadingMore(true);
|
|
70
|
+
try {
|
|
71
|
+
const response = await listConversations({
|
|
72
|
+
limit: overrides.limit ?? limit,
|
|
73
|
+
pageToken: nextPageToken,
|
|
74
|
+
});
|
|
75
|
+
const moreConversations = sortConversationsByUpdatedAt(response.items ?? []);
|
|
76
|
+
setConversations((previous) => [...previous, ...moreConversations]);
|
|
77
|
+
setTotal(response.total ?? conversations.length + moreConversations.length);
|
|
78
|
+
setNextPageToken(response.next_page_token ?? null);
|
|
79
|
+
return moreConversations;
|
|
80
|
+
}
|
|
81
|
+
catch (loadError) {
|
|
82
|
+
const normalized = normalizeError(loadError, "Failed to load more conversations.");
|
|
83
|
+
throw normalized;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setIsLoadingMore(false);
|
|
87
|
+
}
|
|
88
|
+
}, [conversations.length, enabled, isLoading, isLoadingMore, limit, listConversations, nextPageToken]);
|
|
89
|
+
const createConversation = useCallback(async (input = {}) => {
|
|
90
|
+
const createdConversation = await sessionCreateConversation({
|
|
91
|
+
...input,
|
|
92
|
+
podId: input.podId ?? podId ?? undefined,
|
|
93
|
+
assistantName: input.assistantName ?? assistantName ?? assistantId ?? undefined,
|
|
94
|
+
organizationId: input.organizationId ?? organizationId ?? undefined,
|
|
95
|
+
setActive: input.setActive ?? true,
|
|
96
|
+
});
|
|
97
|
+
setConversations((previous) => {
|
|
98
|
+
const nextConversations = sortConversationsByUpdatedAt([
|
|
99
|
+
createdConversation,
|
|
100
|
+
...previous.filter((conversation) => conversation.id !== createdConversation.id),
|
|
101
|
+
]);
|
|
102
|
+
setTotal(nextConversations.length);
|
|
103
|
+
return nextConversations;
|
|
104
|
+
});
|
|
105
|
+
if (input.setActive !== false) {
|
|
106
|
+
setSelectedConversationId(createdConversation.id);
|
|
107
|
+
}
|
|
108
|
+
return createdConversation;
|
|
109
|
+
}, [assistantId, assistantName, organizationId, podId, sessionCreateConversation]);
|
|
110
|
+
const createAndSelectConversation = useCallback(async (input = {}) => {
|
|
111
|
+
return createConversation({
|
|
112
|
+
...input,
|
|
113
|
+
setActive: true,
|
|
114
|
+
});
|
|
115
|
+
}, [createConversation]);
|
|
116
|
+
const selectConversation = useCallback((conversationId) => {
|
|
117
|
+
setSelectedConversationId(conversationId);
|
|
118
|
+
}, []);
|
|
119
|
+
const clearSelection = useCallback(() => {
|
|
120
|
+
setSelectedConversationId(null);
|
|
121
|
+
}, []);
|
|
122
|
+
const selectLatestConversation = useCallback(() => {
|
|
123
|
+
const latestConversationId = conversations[0]?.id ?? null;
|
|
124
|
+
setSelectedConversationId(latestConversationId);
|
|
125
|
+
return latestConversationId;
|
|
126
|
+
}, []);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
setSelectedConversationId(initialConversationId);
|
|
129
|
+
}, [initialConversationId]);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (!enabled) {
|
|
132
|
+
setConversations([]);
|
|
133
|
+
setTotal(0);
|
|
134
|
+
setNextPageToken(null);
|
|
135
|
+
setSelectedConversationId(initialConversationId);
|
|
136
|
+
setIsLoading(false);
|
|
137
|
+
setIsLoadingMore(false);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
setConversations([]);
|
|
141
|
+
setTotal(0);
|
|
142
|
+
setNextPageToken(null);
|
|
143
|
+
setSelectedConversationId(initialConversationId);
|
|
144
|
+
if (!autoLoad)
|
|
145
|
+
return;
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
let cancelled = false;
|
|
148
|
+
(async () => {
|
|
149
|
+
try {
|
|
150
|
+
await refresh();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
if (!cancelled) {
|
|
154
|
+
// refresh handles errors internally
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
return () => {
|
|
159
|
+
cancelled = true;
|
|
160
|
+
controller.abort();
|
|
161
|
+
};
|
|
162
|
+
}, [autoLoad, enabled, initialConversationId, refresh, scopeKey]);
|
|
163
|
+
const effectiveSelectedConversationId = useMemo(() => {
|
|
164
|
+
if (selectedConversationId)
|
|
165
|
+
return selectedConversationId;
|
|
166
|
+
if (!autoSelectFirst)
|
|
167
|
+
return null;
|
|
168
|
+
return conversations[0]?.id ?? null;
|
|
169
|
+
}, [autoSelectFirst, conversations, selectedConversationId]);
|
|
170
|
+
const selectedConversation = useMemo(() => conversations.find((conversation) => conversation.id === effectiveSelectedConversationId) ?? null, [conversations, effectiveSelectedConversationId]);
|
|
171
|
+
const ensureConversation = useCallback(async (input = {}) => {
|
|
172
|
+
const existingConversation = selectedConversation
|
|
173
|
+
?? conversations.find((conversation) => conversation.id === effectiveSelectedConversationId)
|
|
174
|
+
?? null;
|
|
175
|
+
if (existingConversation) {
|
|
176
|
+
if (selectedConversationId !== existingConversation.id) {
|
|
177
|
+
setSelectedConversationId(existingConversation.id);
|
|
178
|
+
}
|
|
179
|
+
return existingConversation;
|
|
180
|
+
}
|
|
181
|
+
return createAndSelectConversation(input);
|
|
182
|
+
}, [
|
|
183
|
+
conversations,
|
|
184
|
+
createAndSelectConversation,
|
|
185
|
+
effectiveSelectedConversationId,
|
|
186
|
+
selectedConversation,
|
|
187
|
+
selectedConversationId,
|
|
188
|
+
]);
|
|
189
|
+
return useMemo(() => ({
|
|
190
|
+
conversations,
|
|
191
|
+
total,
|
|
192
|
+
nextPageToken,
|
|
193
|
+
activeConversationId: selectedConversationId,
|
|
194
|
+
activeConversation: selectedConversation,
|
|
195
|
+
selectedConversationId,
|
|
196
|
+
effectiveSelectedConversationId,
|
|
197
|
+
selectedConversation,
|
|
198
|
+
isLoading,
|
|
199
|
+
isLoadingMore,
|
|
200
|
+
error,
|
|
201
|
+
selectConversation,
|
|
202
|
+
clearSelection,
|
|
203
|
+
selectLatestConversation,
|
|
204
|
+
refresh,
|
|
205
|
+
loadMore,
|
|
206
|
+
createConversation,
|
|
207
|
+
createAndSelectConversation,
|
|
208
|
+
ensureConversation,
|
|
209
|
+
}), [
|
|
210
|
+
clearSelection,
|
|
211
|
+
conversations,
|
|
212
|
+
createAndSelectConversation,
|
|
213
|
+
createConversation,
|
|
214
|
+
effectiveSelectedConversationId,
|
|
215
|
+
error,
|
|
216
|
+
isLoading,
|
|
217
|
+
isLoadingMore,
|
|
218
|
+
loadMore,
|
|
219
|
+
nextPageToken,
|
|
220
|
+
refresh,
|
|
221
|
+
ensureConversation,
|
|
222
|
+
selectConversation,
|
|
223
|
+
selectLatestConversation,
|
|
224
|
+
selectedConversation,
|
|
225
|
+
selectedConversationId,
|
|
226
|
+
total,
|
|
227
|
+
]);
|
|
228
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { RecordResponse } from "../types.js";
|
|
3
|
+
export interface UseCreateRecordOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
podId?: string;
|
|
6
|
+
tableName: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
onSuccess?: (record: Record<string, unknown>, response: RecordResponse) => void;
|
|
9
|
+
onError?: (error: unknown) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface UseCreateRecordResult<TRecord extends Record<string, unknown> = Record<string, unknown>> {
|
|
12
|
+
createdRecord: TRecord | null;
|
|
13
|
+
isSubmitting: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
create: (data: Record<string, unknown>) => Promise<TRecord | null>;
|
|
16
|
+
reset: () => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function useCreateRecord<TRecord extends Record<string, unknown> = Record<string, unknown>>({ client, podId, tableName, enabled, onSuccess, onError, }: UseCreateRecordOptions): UseCreateRecordResult<TRecord>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { normalizeError, resolvePodClient } from "./utils.js";
|
|
3
|
+
export function useCreateRecord({ client, podId, tableName, enabled = true, onSuccess, onError, }) {
|
|
4
|
+
const [createdRecord, setCreatedRecord] = useState(null);
|
|
5
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
6
|
+
const [error, setError] = 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 create = useCallback(async (data) => {
|
|
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 scopedClient.records.create(trimmedTableName, data);
|
|
22
|
+
const nextRecord = (response.data ?? null);
|
|
23
|
+
setCreatedRecord(nextRecord);
|
|
24
|
+
if (nextRecord) {
|
|
25
|
+
onSuccessRef.current?.(nextRecord, response);
|
|
26
|
+
}
|
|
27
|
+
return nextRecord;
|
|
28
|
+
}
|
|
29
|
+
catch (mutationError) {
|
|
30
|
+
const normalized = normalizeError(mutationError, "Failed to create record.");
|
|
31
|
+
setError(normalized);
|
|
32
|
+
onErrorRef.current?.(mutationError);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
setIsSubmitting(false);
|
|
37
|
+
}
|
|
38
|
+
}, [client, isEnabled, podId, trimmedTableName]);
|
|
39
|
+
const reset = useCallback(() => {
|
|
40
|
+
setCreatedRecord(null);
|
|
41
|
+
setError(null);
|
|
42
|
+
setIsSubmitting(false);
|
|
43
|
+
}, []);
|
|
44
|
+
return useMemo(() => ({
|
|
45
|
+
createdRecord,
|
|
46
|
+
isSubmitting,
|
|
47
|
+
error,
|
|
48
|
+
create,
|
|
49
|
+
reset,
|
|
50
|
+
}), [create, createdRecord, error, isSubmitting, reset]);
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LemmaClient } from "../client.js";
|
|
2
|
+
import type { User } from "../types.js";
|
|
3
|
+
export interface UseCurrentUserOptions {
|
|
4
|
+
client: LemmaClient;
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
autoLoad?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface UseCurrentUserResult {
|
|
9
|
+
user: User | null;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
error: Error | null;
|
|
12
|
+
refresh: () => Promise<User | null>;
|
|
13
|
+
}
|
|
14
|
+
export declare function useCurrentUser({ client, enabled, autoLoad, }: UseCurrentUserOptions): UseCurrentUserResult;
|