mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-01 04:39:01 +02:00
feat(sdk-js): add experimental useStream hook (#3361)
Experimental `useStream` React hook to make streaming as simple as possible. Supports branching / forking, message-tuple stream mode for rendering messages. Usage: https://github.com/langchain-ai/langchain-nextjs-template/pull/62
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export function getApiKey(apiKey?: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
interface ClientConfig {
|
||||
export interface ClientConfig {
|
||||
apiUrl?: string;
|
||||
apiKey?: string;
|
||||
callerOptions?: AsyncCallerParams;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { ThreadState } from "../schema.js";
|
||||
|
||||
interface Node<StateType = any> {
|
||||
type: "node";
|
||||
value: ThreadState<StateType>;
|
||||
path: string[];
|
||||
}
|
||||
|
||||
interface Fork<StateType = any> {
|
||||
type: "fork";
|
||||
items: Array<Sequence<StateType>>;
|
||||
}
|
||||
|
||||
interface Sequence<StateType = any> {
|
||||
type: "sequence";
|
||||
items: Array<Node<StateType> | Fork<StateType>>;
|
||||
}
|
||||
|
||||
interface ValidFork<StateType = any> {
|
||||
type: "fork";
|
||||
items: Array<ValidSequence<StateType>>;
|
||||
}
|
||||
|
||||
interface ValidSequence<StateType = any> {
|
||||
type: "sequence";
|
||||
items: [Node<StateType>, ...(Node<StateType> | ValidFork<StateType>)[]];
|
||||
}
|
||||
|
||||
// forks
|
||||
export type CheckpointBranchPath = string[];
|
||||
|
||||
export type MessageBranch = {
|
||||
current: CheckpointBranchPath;
|
||||
options: CheckpointBranchPath[];
|
||||
};
|
||||
|
||||
export function DebugSegmentsView(props: {
|
||||
sequence: ValidSequence<ThreadState>;
|
||||
}) {
|
||||
const concatContent = (value: ThreadState<any>) => {
|
||||
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 (
|
||||
<div>
|
||||
{props.sequence.items.map((item, index) => {
|
||||
if (item.type === "fork") {
|
||||
return (
|
||||
<div key={index}>
|
||||
{item.items.map((fork, idx) => {
|
||||
const [first] = fork.items;
|
||||
return (
|
||||
<details key={idx}>
|
||||
<summary>
|
||||
Fork{" "}
|
||||
<span className="font-mono">
|
||||
...{first.path.at(-1)?.slice(-4)}
|
||||
</span>
|
||||
</summary>
|
||||
<div className="ml-4">
|
||||
<DebugSegmentsView sequence={fork} />
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.type === "node") {
|
||||
return (
|
||||
<div key={index} className="flex items-center gap-2">
|
||||
<pre>
|
||||
({item.value.metadata?.step}) ...
|
||||
{item.value.checkpoint.checkpoint_id?.slice(-4)} (
|
||||
{item.value.metadata?.source}): {concatContent(item.value)}
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
className="border rounded-sm text-sm py-0.5 px-1 text-muted-foreground"
|
||||
onClick={() => console.log(item.path, item.value)}
|
||||
>
|
||||
console.log
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { useStream, type MessageMetadata } from "./stream.js";
|
||||
@@ -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<string, { chunk?: BaseMessageChunk; index?: number }> = {};
|
||||
|
||||
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<T>(array: T[]) {
|
||||
return [...new Set(array)] as T[];
|
||||
}
|
||||
|
||||
function findLastIndex<T>(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<StateType = any> {
|
||||
type: "node";
|
||||
value: ThreadState<StateType>;
|
||||
path: string[];
|
||||
}
|
||||
|
||||
interface Fork<StateType = any> {
|
||||
type: "fork";
|
||||
items: Array<Sequence<StateType>>;
|
||||
}
|
||||
|
||||
interface Sequence<StateType = any> {
|
||||
type: "sequence";
|
||||
items: Array<Node<StateType> | Fork<StateType>>;
|
||||
}
|
||||
|
||||
interface ValidFork<StateType = any> {
|
||||
type: "fork";
|
||||
items: Array<ValidSequence<StateType>>;
|
||||
}
|
||||
|
||||
interface ValidSequence<StateType = any> {
|
||||
type: "sequence";
|
||||
items: [Node<StateType>, ...(Node<StateType> | ValidFork<StateType>)[]];
|
||||
}
|
||||
|
||||
export type MessageMetadata<StateType extends Record<string, unknown>> = {
|
||||
messageId: string;
|
||||
firstSeenState: ThreadState<StateType> | undefined;
|
||||
|
||||
branch: string | undefined;
|
||||
branchOptions: string[] | undefined;
|
||||
};
|
||||
|
||||
function fetchHistory<StateType extends Record<string, unknown>>(
|
||||
client: Client,
|
||||
threadId: string,
|
||||
) {
|
||||
return client.threads.getHistory<StateType>(threadId, { limit: 1000 });
|
||||
}
|
||||
|
||||
function useThreadHistory<StateType extends Record<string, unknown>>(
|
||||
threadId: string | undefined | null,
|
||||
client: Client,
|
||||
clearCallbackRef: MutableRefObject<(() => void) | undefined>,
|
||||
submittingRef: MutableRefObject<boolean>,
|
||||
) {
|
||||
const [history, setHistory] = useState<ThreadState<StateType>[]>([]);
|
||||
|
||||
const fetcher = useCallback(
|
||||
(
|
||||
threadId: string | undefined | null,
|
||||
): Promise<ThreadState<StateType>[]> => {
|
||||
if (threadId != null) {
|
||||
return fetchHistory<StateType>(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<string, unknown> = Record<string, unknown>,
|
||||
UpdateType extends Record<string, unknown> = Partial<StateType>,
|
||||
CustomType = unknown,
|
||||
>(options: {
|
||||
assistantId: string;
|
||||
|
||||
apiUrl: ClientConfig["apiUrl"];
|
||||
apiKey?: ClientConfig["apiKey"];
|
||||
|
||||
withMessages?: string;
|
||||
|
||||
onError?: (error: unknown) => void;
|
||||
onFinish?: (state: ThreadState<StateType>) => void;
|
||||
|
||||
onUpdateEvent?: (data: UpdatesStreamEvent<UpdateType>["data"]) => void;
|
||||
onCustomEvent?: (data: CustomStreamEvent<CustomType>["data"]) => void;
|
||||
onMetadataEvent?: (data: MetadataStreamEvent["data"]) => void;
|
||||
|
||||
// TODO: can we make threadId uncontrollable / controllable?
|
||||
threadId?: string | null;
|
||||
onThreadId?: (threadId: string) => void;
|
||||
}) {
|
||||
type EventStreamEvent =
|
||||
| ValuesStreamEvent<StateType>
|
||||
| UpdatesStreamEvent<UpdateType>
|
||||
| CustomStreamEvent<CustomType>
|
||||
| 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<string[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [_, setEvents] = useState<EventStreamEvent[]>([]);
|
||||
|
||||
const [streamError, setStreamError] = useState<unknown>(undefined);
|
||||
const [streamValues, setStreamValues] = useState<StateType | null>(null);
|
||||
|
||||
const messageManagerRef = useRef(new MessageTupleManager());
|
||||
const submittingRef = useRef(false);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
|
||||
const trackStreamModeRef = useRef<
|
||||
Array<"values" | "updates" | "events" | "custom" | "messages-tuple">
|
||||
>(["values", "messages-tuple"]);
|
||||
|
||||
const trackStreamMode = useCallback(
|
||||
(mode: Exclude<StreamMode, "debug" | "messages">) => {
|
||||
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<StreamMode, "debug" | "messages">[] = [];
|
||||
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<StateType>(
|
||||
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<string, ThreadState<StateType>[]> = {};
|
||||
|
||||
// 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<string>();
|
||||
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<string, string[][]> = {};
|
||||
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<StateType>[] = [];
|
||||
const flatPaths: Record<
|
||||
string,
|
||||
{ current: string[] | undefined; branches: string[][] | undefined }
|
||||
> = {};
|
||||
|
||||
const forkStack = branchPath.slice();
|
||||
const queue: (Node<StateType> | Fork<StateType>)[] = [...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<StateType> | 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<string>();
|
||||
return getMessages(historyValues).map(
|
||||
(message, idx): MessageMetadata<StateType> => {
|
||||
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<StateType>
|
||||
| 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<Checkpoint, "thread_id"> | null;
|
||||
command?: Command;
|
||||
interruptBefore?: "*" | string[];
|
||||
interruptAfter?: "*" | string[];
|
||||
metadata?: Metadata;
|
||||
multitaskStrategy?: MultitaskStrategy;
|
||||
onCompletion?: OnCompletionBehavior;
|
||||
onDisconnect?: DisconnectMode;
|
||||
feedbackKeys?: string[];
|
||||
streamMode?: Array<StreamMode>;
|
||||
optimisticValues?:
|
||||
| Partial<StateType>
|
||||
| ((prev: StateType) => Partial<StateType>);
|
||||
},
|
||||
) => {
|
||||
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<string, unknown>,
|
||||
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<EventStreamEvent>;
|
||||
|
||||
// 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<StateType> | 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),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -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<string, unknown>;
|
||||
|
||||
export type HumanMessage = {
|
||||
type: "human";
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface Command {
|
||||
/**
|
||||
* An object to update the thread state with.
|
||||
*/
|
||||
update?: Record<string, unknown> | [string, unknown][];
|
||||
update?: Record<string, unknown> | [string, unknown][] | null;
|
||||
|
||||
/**
|
||||
* The value to return from an `interrupt` function call.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"strictPropertyInitialization": false,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
|
||||
+125
-8
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user