项目文件夹

文件
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

83 行
2.4 KiB
TypeScript

"use client";
import type { LangChainMessage } from "./types";
import type { MessageTiming } from "@assistant-ui/core";
import {
useStreamingTiming,
type StreamingTimingAccessors,
} from "@assistant-ui/core/react";
const reasoningTextLength = (part: {
readonly summary?: ReadonlyArray<{ readonly text?: string }>;
readonly reasoning?: string;
}): number => {
if (part.summary && part.summary.length > 0)
return part.summary.map((s) => s?.text ?? "").join("\n\n\n").length;
return part.reasoning?.length ?? 0;
};
const getMessageTextLength = (
messages: readonly LangChainMessage[],
messageId: string,
): number => {
const m = messages.find((msg) => msg.type === "ai" && msg.id === messageId);
if (!m) return 0;
const content = m.content;
if (typeof content === "string") return content.length;
if (!Array.isArray(content)) return 0;
let len = 0;
for (const part of content) {
switch (part.type) {
case "text":
case "text_delta":
if (typeof part.text === "string") len += part.text.length;
break;
case "thinking":
if (typeof part.thinking === "string") len += part.thinking.length;
break;
case "reasoning":
len += reasoningTextLength(part);
break;
}
}
return len;
};
const getMessageToolCallCount = (
messages: readonly LangChainMessage[],
messageId: string,
): number => {
const m = messages.find((msg) => msg.type === "ai" && msg.id === messageId);
if (!m || m.type !== "ai") return 0;
return m.tool_calls?.length ?? 0;
};
const getLastAssistantId = (
messages: readonly LangChainMessage[],
): string | undefined => {
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i]?.type === "ai" && messages[i]?.id) {
return messages[i]!.id;
}
}
return undefined;
};
const langGraphStreamingTimingAccessors: StreamingTimingAccessors<LangChainMessage> =
{
getAssistantMessageId: getLastAssistantId,
getTextLength: getMessageTextLength,
getToolCallCount: getMessageToolCallCount,
};
/**
* Tracks per-message streaming timing for LangGraph messages. Delegates to
* the shared `useStreamingTiming` primitive in `@assistant-ui/core/react`,
* adapted to the LangGraph message shape via the accessors above.
*/
export const useLangGraphStreamingTiming = (
messages: readonly LangChainMessage[],
isRunning: boolean,
): Record<string, MessageTiming> =>
useStreamingTiming(messages, isRunning, langGraphStreamingTimingAccessors);