diff --git a/libs/sdk-js/.gitignore b/libs/sdk-js/.gitignore index fd0d01027..4d7124e5f 100644 --- a/libs/sdk-js/.gitignore +++ b/libs/sdk-js/.gitignore @@ -6,6 +6,10 @@ client.cjs client.js client.d.ts client.d.cts +react.cjs +react.js +react.d.ts +react.d.cts node_modules dist .yarn diff --git a/libs/sdk-js/langchain.config.js b/libs/sdk-js/langchain.config.js index eeb79ab47..b644f4b3c 100644 --- a/libs/sdk-js/langchain.config.js +++ b/libs/sdk-js/langchain.config.js @@ -10,8 +10,8 @@ function abs(relativePath) { } export const config = { - internals: [], - entrypoints: { index: "index", client: "client" }, + internals: [/react/], + entrypoints: { index: "index", client: "client", react: "react/index" }, tsConfigPath: resolve("./tsconfig.json"), cjsSource: "./dist-cjs", cjsDestination: "./dist", diff --git a/libs/sdk-js/package.json b/libs/sdk-js/package.json index aae3d0219..2555acbd3 100644 --- a/libs/sdk-js/package.json +++ b/libs/sdk-js/package.json @@ -22,18 +22,25 @@ }, "devDependencies": { "@jest/globals": "^29.7.0", + "@langchain/core": "^0.3.31", "@langchain/scripts": "^0.1.4", "@tsconfig/recommended": "^1.0.2", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", "@types/uuid": "^9.0.1", + "@types/react": "18.3.2", "concat-md": "^0.5.1", "jest": "^29.7.0", "prettier": "^3.2.5", "ts-jest": "^29.1.2", "typedoc": "^0.26.1", "typedoc-plugin-markdown": "^4.1.0", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "react": "^18.3.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "@langchain/core": ">=0.2.31 <0.4.0" }, "exports": { ".": { @@ -54,6 +61,15 @@ "import": "./client.js", "require": "./client.cjs" }, + "./react": { + "types": { + "import": "./react.d.ts", + "require": "./react.d.cts", + "default": "./react.d.ts" + }, + "import": "./react.js", + "require": "./react.cjs" + }, "./package.json": "./package.json" }, "files": [ @@ -65,6 +81,10 @@ "client.cjs", "client.js", "client.d.ts", - "client.d.cts" + "client.d.cts", + "react.cjs", + "react.js", + "react.d.ts", + "react.d.cts" ] } diff --git a/libs/sdk-js/src/client.ts b/libs/sdk-js/src/client.ts index c4ffcb7ed..0406ae4a1 100644 --- a/libs/sdk-js/src/client.ts +++ b/libs/sdk-js/src/client.ts @@ -68,7 +68,7 @@ export function getApiKey(apiKey?: string): string | undefined { return undefined; } -interface ClientConfig { +export interface ClientConfig { apiUrl?: string; apiKey?: string; callerOptions?: AsyncCallerParams; diff --git a/libs/sdk-js/src/react/debug.tsx b/libs/sdk-js/src/react/debug.tsx new file mode 100644 index 000000000..b5fc462fc --- /dev/null +++ b/libs/sdk-js/src/react/debug.tsx @@ -0,0 +1,102 @@ +import { ThreadState } from "../schema.js"; + +interface Node { + type: "node"; + value: ThreadState; + path: string[]; +} + +interface Fork { + type: "fork"; + items: Array>; +} + +interface Sequence { + type: "sequence"; + items: Array | Fork>; +} + +interface ValidFork { + type: "fork"; + items: Array>; +} + +interface ValidSequence { + type: "sequence"; + items: [Node, ...(Node | ValidFork)[]]; +} + +// forks +export type CheckpointBranchPath = string[]; + +export type MessageBranch = { + current: CheckpointBranchPath; + options: CheckpointBranchPath[]; +}; + +export function DebugSegmentsView(props: { + sequence: ValidSequence; +}) { + const concatContent = (value: ThreadState) => { + let content; + try { + content = value.values?.messages?.at(-1)?.content ?? ""; + } catch { + content = JSON.stringify(value.values); + } + + content = content.replace(/(\n|\r\n)/g, ""); + if (content.length <= 23) return content; + return `${content.slice(0, 10)}...${content.slice(-10)}`; + }; + + return ( +
+ {props.sequence.items.map((item, index) => { + if (item.type === "fork") { + return ( +
+ {item.items.map((fork, idx) => { + const [first] = fork.items; + return ( +
+ + Fork{" "} + + ...{first.path.at(-1)?.slice(-4)} + + +
+ +
+
+ ); + })} +
+ ); + } + + if (item.type === "node") { + return ( +
+
+                ({item.value.metadata?.step}) ...
+                {item.value.checkpoint.checkpoint_id?.slice(-4)} (
+                {item.value.metadata?.source}): {concatContent(item.value)}
+              
+ +
+ ); + } + + return null; + })} +
+ ); +} diff --git a/libs/sdk-js/src/react/index.ts b/libs/sdk-js/src/react/index.ts new file mode 100644 index 000000000..97a58f3d1 --- /dev/null +++ b/libs/sdk-js/src/react/index.ts @@ -0,0 +1 @@ +export { useStream, type MessageMetadata } from "./stream.js"; diff --git a/libs/sdk-js/src/react/stream.tsx b/libs/sdk-js/src/react/stream.tsx new file mode 100644 index 000000000..0f229da40 --- /dev/null +++ b/libs/sdk-js/src/react/stream.tsx @@ -0,0 +1,665 @@ +/* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */ +"use client"; + +import { Client, type ClientConfig } from "../client.js"; +import type { + Command, + DisconnectMode, + MultitaskStrategy, + OnCompletionBehavior, +} from "../types.js"; +import type { Message } from "../types.messages.js"; +import type { Checkpoint, Config, Metadata, ThreadState } from "../schema.js"; +import type { + CustomStreamEvent, + DebugStreamEvent, + ErrorStreamEvent, + EventsStreamEvent, + FeedbackStreamEvent, + MessagesStreamEvent, + MessagesTupleStreamEvent, + MetadataStreamEvent, + StreamMode, + UpdatesStreamEvent, + ValuesStreamEvent, +} from "../types.stream.js"; + +import { + type MutableRefObject, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + type BaseMessageChunk, + type BaseMessage, + coerceMessageLikeToMessage, + convertToChunk, +} from "@langchain/core/messages"; + +class StreamError extends Error { + constructor(data: { error?: string; name?: string; message: string }) { + super(data.message); + this.name = data.name ?? data.error ?? "StreamError"; + } + + static isStructuredError(error: unknown): error is { + error?: string; + name?: string; + message: string; + } { + return typeof error === "object" && error != null && "message" in error; + } +} + +class MessageTupleManager { + chunks: Record = {}; + + constructor() { + this.chunks = {}; + } + + add(serialized: Message): string | null { + const chunk = convertToChunk(coerceMessageLikeToMessage(serialized)); + + const id = chunk.id; + if (!id) return null; + + this.chunks[id] ??= {}; + this.chunks[id].chunk = this.chunks[id]?.chunk?.concat(chunk) ?? chunk; + + return id; + } + + clear() { + this.chunks = {}; + } + + get(id: string, defaultIndex: number) { + if (this.chunks[id] == null) return null; + this.chunks[id].index ??= defaultIndex; + + return this.chunks[id]; + } +} + +const toMessageDict = (chunk: BaseMessage): Message => { + const { type, data } = chunk.toDict(); + return { ...data, type } as Message; +}; + +function unique(array: T[]) { + return [...new Set(array)] as T[]; +} + +function findLastIndex(array: T[], predicate: (item: T) => boolean) { + for (let i = array.length - 1; i >= 0; i--) { + if (predicate(array[i])) return i; + } + return -1; +} + +interface Node { + type: "node"; + value: ThreadState; + path: string[]; +} + +interface Fork { + type: "fork"; + items: Array>; +} + +interface Sequence { + type: "sequence"; + items: Array | Fork>; +} + +interface ValidFork { + type: "fork"; + items: Array>; +} + +interface ValidSequence { + type: "sequence"; + items: [Node, ...(Node | ValidFork)[]]; +} + +export type MessageMetadata> = { + messageId: string; + firstSeenState: ThreadState | undefined; + + branch: string | undefined; + branchOptions: string[] | undefined; +}; + +function fetchHistory>( + client: Client, + threadId: string, +) { + return client.threads.getHistory(threadId, { limit: 1000 }); +} + +function useThreadHistory>( + threadId: string | undefined | null, + client: Client, + clearCallbackRef: MutableRefObject<(() => void) | undefined>, + submittingRef: MutableRefObject, +) { + const [history, setHistory] = useState[]>([]); + + const fetcher = useCallback( + ( + threadId: string | undefined | null, + ): Promise[]> => { + if (threadId != null) { + return fetchHistory(client, threadId).then((history) => { + setHistory(history); + return history; + }); + } + + setHistory([]); + clearCallbackRef.current?.(); + return Promise.resolve([]); + }, + [], + ); + + useEffect(() => { + if (submittingRef.current) return; + fetcher(threadId); + }, [fetcher, submittingRef, threadId]); + + return { + data: history, + mutate: (mutateId?: string) => fetcher(mutateId ?? threadId), + }; +} + +export function useStream< + StateType extends Record = Record, + UpdateType extends Record = Partial, + CustomType = unknown, +>(options: { + assistantId: string; + + apiUrl: ClientConfig["apiUrl"]; + apiKey?: ClientConfig["apiKey"]; + + withMessages?: string; + + onError?: (error: unknown) => void; + onFinish?: (state: ThreadState) => void; + + onUpdateEvent?: (data: UpdatesStreamEvent["data"]) => void; + onCustomEvent?: (data: CustomStreamEvent["data"]) => void; + onMetadataEvent?: (data: MetadataStreamEvent["data"]) => void; + + // TODO: can we make threadId uncontrollable / controllable? + threadId?: string | null; + onThreadId?: (threadId: string) => void; +}) { + type EventStreamEvent = + | ValuesStreamEvent + | UpdatesStreamEvent + | CustomStreamEvent + | DebugStreamEvent + | MessagesStreamEvent + | MessagesTupleStreamEvent + | EventsStreamEvent + | MetadataStreamEvent + | ErrorStreamEvent + | FeedbackStreamEvent; + + const { assistantId, threadId, withMessages, onError, onFinish } = options; + const client = useMemo( + () => new Client({ apiUrl: options.apiUrl, apiKey: options.apiKey }), + [options.apiKey, options.apiUrl], + ); + + const [branchPath, setBranchPath] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [_, setEvents] = useState([]); + + const [streamError, setStreamError] = useState(undefined); + const [streamValues, setStreamValues] = useState(null); + + const messageManagerRef = useRef(new MessageTupleManager()); + const submittingRef = useRef(false); + const abortRef = useRef(null); + + const trackStreamModeRef = useRef< + Array<"values" | "updates" | "events" | "custom" | "messages-tuple"> + >(["values", "messages-tuple"]); + + const trackStreamMode = useCallback( + (mode: Exclude) => { + if (!trackStreamModeRef.current.includes(mode)) + trackStreamModeRef.current.push(mode); + }, + [], + ); + + const hasUpdateListener = options.onUpdateEvent != null; + const hasCustomListener = options.onCustomEvent != null; + + const callbackStreamMode = useMemo(() => { + const modes: Exclude[] = []; + if (hasUpdateListener) modes.push("updates"); + if (hasCustomListener) modes.push("custom"); + return modes; + }, [hasUpdateListener, hasCustomListener]); + + const clearCallbackRef = useRef<() => void>(null!); + clearCallbackRef.current = () => { + setStreamError(undefined); + setStreamValues(null); + }; + + // TODO: this should be done on the server to avoid pagination + // TODO: should we permit adapter? SWR / React Query? + const history = useThreadHistory( + threadId, + client, + clearCallbackRef, + submittingRef, + ); + + const getMessages = useMemo(() => { + if (withMessages == null) return undefined; + return (value: StateType) => + Array.isArray(value[withMessages]) + ? (value[withMessages] as Message[]) + : []; + }, [withMessages]); + + const [sequence, pathMap] = (() => { + const childrenMap: Record[]> = {}; + + // First pass - collect nodes for each checkpoint + history.data.forEach((state) => { + const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$"; + childrenMap[checkpointId] ??= []; + childrenMap[checkpointId].push(state); + }); + + // Second pass - create a tree of sequences + type Task = { id: string; sequence: Sequence; path: string[] }; + const rootSequence: Sequence = { type: "sequence", items: [] }; + const queue: Task[] = [{ id: "$", sequence: rootSequence, path: [] }]; + + const paths: string[][] = []; + + const visited = new Set(); + while (queue.length > 0) { + const task = queue.shift()!; + if (visited.has(task.id)) continue; + visited.add(task.id); + + const children = childrenMap[task.id]; + if (children == null || children.length === 0) continue; + + // If we've encountered a fork (2+ children), push the fork + // to the sequence and add a new sequence for each child + let fork: Fork | undefined; + if (children.length > 1) { + fork = { type: "fork", items: [] }; + task.sequence.items.push(fork); + } + + for (const value of children) { + const id = value.checkpoint.checkpoint_id!; + + let sequence = task.sequence; + let path = task.path; + if (fork != null) { + sequence = { type: "sequence", items: [] }; + fork.items.unshift(sequence); + + path = path.slice(); + path.push(id); + paths.push(path); + } + + sequence.items.push({ type: "node", value, path }); + queue.push({ id, sequence, path }); + } + } + + // Third pass, create a map for available forks + const pathMap: Record = {}; + for (const path of paths) { + const parent = path.at(-2) ?? "$"; + pathMap[parent] ??= []; + pathMap[parent].unshift(path); + } + + return [rootSequence as ValidSequence, pathMap]; + })(); + + const [flatValues, flatPaths] = (() => { + const result: ThreadState[] = []; + const flatPaths: Record< + string, + { current: string[] | undefined; branches: string[][] | undefined } + > = {}; + + const forkStack = branchPath.slice(); + const queue: (Node | Fork)[] = [...sequence.items]; + + while (queue.length > 0) { + const item = queue.shift()!; + + if (item.type === "node") { + result.push(item.value); + flatPaths[item.value.checkpoint.checkpoint_id!] = { + current: item.path, + branches: + item.path.length > 0 ? pathMap[item.path.at(-2) ?? "$"] ?? [] : [], + }; + } + if (item.type === "fork") { + const forkId = forkStack.shift(); + const index = + forkId != null + ? item.items.findIndex((value) => { + const firstItem = value.items.at(0); + if (!firstItem || firstItem.type !== "node") return false; + return firstItem.value.checkpoint.checkpoint_id === forkId; + }) + : -1; + + const nextItems = item.items.at(index)?.items ?? []; + queue.push(...nextItems); + } + } + + return [result, flatPaths]; + })(); + + const threadHead: ThreadState | undefined = flatValues.at(-1); + const historyValues = threadHead?.values ?? ({} as StateType); + const historyError = (() => { + const error = threadHead?.tasks?.at(-1)?.error; + if (error == null) return undefined; + try { + const parsed = JSON.parse(error) as unknown; + if (StreamError.isStructuredError(parsed)) { + return new StreamError(parsed); + } + + return parsed; + } catch { + // do nothing + } + return error; + })(); + + const messageMetadata = (() => { + if (getMessages == null) return undefined; + + const alreadyShown = new Set(); + return getMessages(historyValues).map( + (message, idx): MessageMetadata => { + const messageId = message.id ?? idx; + const firstSeenIdx = findLastIndex(history.data, (state) => + getMessages(state.values) + .map((m, idx) => m.id ?? idx) + .includes(messageId), + ); + + const firstSeen = history.data[firstSeenIdx] as + | ThreadState + | undefined; + + let branch = firstSeen + ? flatPaths[firstSeen.checkpoint.checkpoint_id!] + : undefined; + + if (!branch?.current?.length) branch = undefined; + + // serialize branches + const optionsShown = branch?.branches?.flat(2).join(","); + if (optionsShown) { + if (alreadyShown.has(optionsShown)) branch = undefined; + alreadyShown.add(optionsShown); + } + + return { + messageId: messageId.toString(), + firstSeenState: firstSeen, + branch: branch?.current?.join(">"), + branchOptions: branch?.branches?.map((b) => b.join(">")), + }; + }, + ); + })(); + + const stop = useCallback(() => { + if (abortRef.current != null) abortRef.current.abort(); + abortRef.current = null; + }, []); + + const submit = async ( + values: UpdateType | undefined, + submitOptions?: { + config?: Config; + checkpoint?: Omit | null; + command?: Command; + interruptBefore?: "*" | string[]; + interruptAfter?: "*" | string[]; + metadata?: Metadata; + multitaskStrategy?: MultitaskStrategy; + onCompletion?: OnCompletionBehavior; + onDisconnect?: DisconnectMode; + feedbackKeys?: string[]; + streamMode?: Array; + optimisticValues?: + | Partial + | ((prev: StateType) => Partial); + }, + ) => { + try { + setIsLoading(true); + setStreamError(undefined); + + submittingRef.current = true; + abortRef.current = new AbortController(); + + let usableThreadId = threadId; + if (!usableThreadId) { + const thread = await client.threads.create(); + options?.onThreadId?.(thread.thread_id); + usableThreadId = thread.thread_id; + } + + const streamMode = unique([ + ...(submitOptions?.streamMode ?? []), + ...trackStreamModeRef.current, + ...callbackStreamMode, + ]); + + const checkpoint = + submitOptions?.checkpoint ?? threadHead?.checkpoint ?? undefined; + // @ts-expect-error + if (checkpoint != null) delete checkpoint.thread_id; + + const run = (await client.runs.stream(usableThreadId, assistantId, { + input: values as Record, + config: submitOptions?.config, + command: submitOptions?.command, + + interruptBefore: submitOptions?.interruptBefore, + interruptAfter: submitOptions?.interruptAfter, + metadata: submitOptions?.metadata, + multitaskStrategy: submitOptions?.multitaskStrategy, + onCompletion: submitOptions?.onCompletion, + onDisconnect: submitOptions?.onDisconnect ?? "cancel", + + signal: abortRef.current.signal, + + checkpoint, + streamMode, + })) as AsyncGenerator; + + // Unbranch things + const newPath = submitOptions?.checkpoint?.checkpoint_id + ? flatPaths[submitOptions?.checkpoint?.checkpoint_id]?.current + : undefined; + if (newPath != null) setBranchPath(newPath ?? []); + + // Assumption: we're setting the initial value + // Used for instant feedback + setStreamValues(() => { + const values = { ...historyValues }; + + if (submitOptions?.optimisticValues != null) { + return { + ...values, + ...(typeof submitOptions.optimisticValues === "function" + ? submitOptions.optimisticValues(values) + : submitOptions.optimisticValues), + }; + } + + return values; + }); + + let streamError: StreamError | undefined; + for await (const { event, data } of run) { + setEvents((events) => [...events, { event, data } as EventStreamEvent]); + + if (event === "error") { + streamError = new StreamError(data); + break; + } + + if (event === "updates") { + options.onUpdateEvent?.(data); + } + + if (event === "custom") { + options.onCustomEvent?.(data); + } + + if (event === "metadata") { + options.onMetadataEvent?.(data); + } + + if (event === "values") { + setStreamValues(data); + } + + if (event === "messages") { + if (!getMessages) continue; + + const [serialized] = data; + + const messageId = messageManagerRef.current.add(serialized); + if (!messageId) { + console.warn( + "Failed to add message to manager, no message ID found", + ); + continue; + } + + setStreamValues((streamValues) => { + const values = { ...historyValues, ...streamValues }; + + // Assumption: we're concatenating the message + const messages = getMessages(values).slice(); + const { chunk, index } = + messageManagerRef.current.get(messageId, messages.length) ?? {}; + + if (!chunk || index == null) return values; + messages[index] = toMessageDict(chunk); + + return { ...values, [withMessages!]: messages }; + }); + } + } + + // TODO: stream created checkpoints to avoid an unnecessary network request + const result = await history.mutate(usableThreadId); + + // TODO: write tests verifying that stream values are properly handled lifecycle-wise + setStreamValues(null); + + if (streamError != null) throw streamError; + + const lastHead = result.at(0); + if (lastHead) onFinish?.(lastHead); + } catch (error) { + if ( + !( + error instanceof Error && + (error.name === "AbortError" || error.name === "TimeoutError") + ) + ) { + setStreamError(error); + onError?.(error); + } + } finally { + setIsLoading(false); + + // Assumption: messages are already handled, we can clear the manager + messageManagerRef.current.clear(); + submittingRef.current = false; + abortRef.current = null; + } + }; + + const error = isLoading ? streamError : historyError; + const values = streamValues ?? historyValues; + + const setBranch = useCallback( + (path: string) => setBranchPath(path.split(">")), + [setBranchPath], + ); + + return { + get values() { + trackStreamMode("values"); + return values; + }, + + error, + isLoading, + + stop, + submit, + setBranch, + + get messages() { + trackStreamMode("messages-tuple"); + + if (getMessages == null) { + throw new Error( + "No messages key provided. Make sure that `useStream` contains the `messagesKey` property.", + ); + } + + return getMessages(values); + }, + + getMessagesMetadata( + message: Message, + index?: number, + ): MessageMetadata | undefined { + trackStreamMode("messages-tuple"); + + if (getMessages == null) { + throw new Error( + "No messages key provided. Make sure that `useStream` contains the `messagesKey` property.", + ); + } + + return messageMetadata?.find( + (m) => m.messageId === (message.id ?? index), + ); + }, + }; +} diff --git a/libs/sdk-js/src/types.messages.ts b/libs/sdk-js/src/types.messages.ts index 79ed78473..a2eb34a9a 100644 --- a/libs/sdk-js/src/types.messages.ts +++ b/libs/sdk-js/src/types.messages.ts @@ -8,19 +8,10 @@ type MessageContentText = { type: "text"; text: string }; type MessageContentComplex = MessageContentText | MessageContentImageUrl; type MessageContent = string | MessageContentComplex[]; -type MessageAdditionalKwargs = { - [x: string]: unknown; - - function_call?: { arguments: string; name: string } | undefined; - tool_calls?: - | { - id: string; - function: { arguments: string; name: string }; - type: "function"; - index?: number | undefined; - }[] - | undefined; -}; +/** + * Model-specific additional kwargs, which is passed back to the underlying LLM. + */ +type MessageAdditionalKwargs = Record; export type HumanMessage = { type: "human"; diff --git a/libs/sdk-js/src/types.ts b/libs/sdk-js/src/types.ts index 643128e83..6945e9eba 100644 --- a/libs/sdk-js/src/types.ts +++ b/libs/sdk-js/src/types.ts @@ -26,7 +26,7 @@ export interface Command { /** * An object to update the thread state with. */ - update?: Record | [string, unknown][]; + update?: Record | [string, unknown][] | null; /** * The value to return from an `interrupt` function call. diff --git a/libs/sdk-js/tsconfig.json b/libs/sdk-js/tsconfig.json index f72c108ed..9c6561a09 100644 --- a/libs/sdk-js/tsconfig.json +++ b/libs/sdk-js/tsconfig.json @@ -19,6 +19,7 @@ "strictPropertyInitialization": false, "allowJs": true, "strict": true, + "jsx": "react-jsx", "outDir": "dist" }, "include": [ diff --git a/libs/sdk-js/yarn.lock b/libs/sdk-js/yarn.lock index fbde16b49..4d4e4ccf8 100644 --- a/libs/sdk-js/yarn.lock +++ b/libs/sdk-js/yarn.lock @@ -296,6 +296,11 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cfworker/json-schema@^4.0.2": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6" + integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -548,6 +553,24 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@langchain/core@^0.3.31": + version "0.3.39" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.39.tgz#81002e0cacacb7c1011264d6612f923816a3965f" + integrity sha512-muXs4asy1A7qDtcdznxqyBfxf4N6qxofY/S0c95vbsWa0r9YAE2PttHIjcuxSy1q2jUiTkpCcgFEjNJRQRVhEw== + dependencies: + "@cfworker/json-schema" "^4.0.2" + ansi-styles "^5.0.0" + camelcase "6" + decamelize "1.2.0" + js-tiktoken "^1.0.12" + langsmith ">=0.2.8 <0.4.0" + mustache "^4.2.0" + p-queue "^6.6.2" + p-retry "4" + uuid "^10.0.0" + zod "^3.22.4" + zod-to-json-schema "^3.22.3" + "@langchain/scripts@^0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@langchain/scripts/-/scripts-0.1.4.tgz#8c5d03d627686f20b9522213c12e2b329e73e381" @@ -950,6 +973,19 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +"@types/prop-types@*": + version "15.7.14" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" + integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== + +"@types/react@18.3.2": + version "18.3.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.2.tgz#462ae4904973bc212fa910424d901e3d137dbfcd" + integrity sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -965,6 +1001,11 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + "@types/uuid@^9.0.1": version "9.0.8" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" @@ -1181,6 +1222,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + before-after-hook@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-3.0.2.tgz#d5665a5fa8b62294a5aa0a499f933f4a1016195d" @@ -1262,16 +1308,16 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" +camelcase@6, camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - caniuse-lite@^1.0.30001688: version "1.0.30001692" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz#4585729d95e6b95be5b439da6ab55250cd125bf9" @@ -1291,7 +1337,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1406,6 +1452,13 @@ concat-md@^0.5.1: meow "^9.0.0" transform-markdown-links "^2.0.0" +console-table-printer@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.12.1.tgz#4a9646537a246a6d8de57075d4fae1e08abae267" + integrity sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ== + dependencies: + simple-wcswidth "^1.0.1" + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -1442,6 +1495,11 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -1491,7 +1549,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.2.0: +decamelize@1.2.0, decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== @@ -2794,7 +2852,14 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -js-tokens@^4.0.0: +js-tiktoken@^1.0.12: + version "1.0.18" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.18.tgz#aaf68dda155bc693e6f0a572b9a359d569cb53df" + integrity sha512-hFYx4xYf6URgcttcGvGuOBJhTxPYZ2R5eIesqCaNRJmYH8sNmsfTeWg4yu//7u1VD/qIUkgKJTpGom9oHXmB4g== + dependencies: + base64-js "^1.5.1" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -2832,6 +2897,19 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +"langsmith@>=0.2.8 <0.4.0": + version "0.3.7" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.7.tgz#c29362f78ea2872252a60a680d6adb8b67e18b74" + integrity sha512-wakN1hxGkm1JR2PpAV7fiT7oC99LKcgxiuUrYGZWPbuj7Y8EPF19F7VNr4B+hA219bfaeWTa4Lxy2YrtPSKnQA== + dependencies: + "@types/uuid" "^10.0.0" + chalk "^4.1.2" + console-table-printer "^2.12.1" + p-queue "^6.6.2" + p-retry "4" + semver "^7.6.3" + uuid "^10.0.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -2876,6 +2954,13 @@ longest-streak@^2.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" @@ -3234,6 +3319,11 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3518,6 +3608,13 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== + dependencies: + loose-envify "^1.1.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -3787,6 +3884,11 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-wcswidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" + integrity sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -4230,6 +4332,11 @@ update-section@^0.3.3: resolved "https://registry.yarnpkg.com/update-section/-/update-section-0.3.3.tgz#458f17820d37820dc60e20b86d94391b00123158" integrity sha512-BpRZMZpgXLuTiKeiu7kK0nIPwGdyrqrs6EDSaXtjD/aQ2T+qVo9a5hRC3HN3iJjCMxNT/VxoLGQ7E/OzE5ucnw== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -4394,6 +4501,16 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zod-to-json-schema@^3.22.3: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz#f08c6725091aadabffa820ba8d50c7ab527f227a" + integrity sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w== + +zod@^3.22.4: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" + integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"