新增用户意图确认功能
This commit is contained in:
1 parent
cf2e4bdd9c
commit
b8926f00d7
574 files changed
+97664
-97208
No files matched your search
@@ -1,140 +1,140 @@
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
import {
|
||||
extractContentFromMessage,
|
||||
extractReasoningContentFromMessage,
|
||||
hasContent,
|
||||
hasToolCalls,
|
||||
stripUploadedFilesTag,
|
||||
} from "../messages/utils";
|
||||
|
||||
import type { AgentThread } from "./types";
|
||||
import { titleOfThread } from "./utils";
|
||||
|
||||
function formatMessageContent(message: Message): string {
|
||||
const text = extractContentFromMessage(message);
|
||||
if (!text) return "";
|
||||
return stripUploadedFilesTag(text);
|
||||
}
|
||||
|
||||
function formatToolCalls(message: Message): string {
|
||||
if (message.type !== "ai" || !hasToolCalls(message)) return "";
|
||||
const calls = message.tool_calls ?? [];
|
||||
return calls.map((call) => `- **Tool:** \`${call.name}\``).join("\n");
|
||||
}
|
||||
|
||||
export function formatThreadAsMarkdown(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
): string {
|
||||
const title = titleOfThread(thread);
|
||||
const createdAt = thread.created_at
|
||||
? new Date(thread.created_at).toLocaleString()
|
||||
: "Unknown";
|
||||
|
||||
const lines: string[] = [
|
||||
`# ${title}`,
|
||||
"",
|
||||
`*Exported on ${new Date().toLocaleString()} · Created ${createdAt}*`,
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
];
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.type === "human") {
|
||||
const content = formatMessageContent(message);
|
||||
if (content) {
|
||||
lines.push(`## 🧑 User`, "", content, "", "---", "");
|
||||
}
|
||||
} else if (message.type === "ai") {
|
||||
const reasoning = extractReasoningContentFromMessage(message);
|
||||
const content = formatMessageContent(message);
|
||||
const toolCalls = formatToolCalls(message);
|
||||
|
||||
if (!content && !toolCalls && !reasoning) continue;
|
||||
|
||||
lines.push(`## 🤖 Assistant`);
|
||||
|
||||
if (reasoning) {
|
||||
lines.push(
|
||||
"",
|
||||
"<details>",
|
||||
"<summary>Thinking</summary>",
|
||||
"",
|
||||
reasoning,
|
||||
"",
|
||||
"</details>",
|
||||
);
|
||||
}
|
||||
|
||||
if (toolCalls) {
|
||||
lines.push("", toolCalls);
|
||||
}
|
||||
|
||||
if (content && hasContent(message)) {
|
||||
lines.push("", content);
|
||||
}
|
||||
|
||||
lines.push("", "---", "");
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join("\n").trimEnd() + "\n";
|
||||
}
|
||||
|
||||
export function formatThreadAsJSON(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
): string {
|
||||
const exportData = {
|
||||
title: titleOfThread(thread),
|
||||
thread_id: thread.thread_id,
|
||||
created_at: thread.created_at,
|
||||
exported_at: new Date().toISOString(),
|
||||
messages: messages.map((msg) => ({
|
||||
type: msg.type,
|
||||
id: msg.id,
|
||||
content: typeof msg.content === "string" ? msg.content : msg.content,
|
||||
...(msg.type === "ai" && msg.tool_calls?.length
|
||||
? { tool_calls: msg.tool_calls }
|
||||
: {}),
|
||||
})),
|
||||
};
|
||||
return JSON.stringify(exportData, null, 2);
|
||||
}
|
||||
|
||||
function sanitizeFilename(name: string): string {
|
||||
return name.replace(/[^\p{L}\p{N}_\- ]/gu, "").trim() || "conversation";
|
||||
}
|
||||
|
||||
export function downloadAsFile(
|
||||
content: string,
|
||||
filename: string,
|
||||
mimeType: string,
|
||||
) {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export function exportThreadAsMarkdown(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
) {
|
||||
const markdown = formatThreadAsMarkdown(thread, messages);
|
||||
const filename = `${sanitizeFilename(titleOfThread(thread))}.md`;
|
||||
downloadAsFile(markdown, filename, "text/markdown;charset=utf-8");
|
||||
}
|
||||
|
||||
export function exportThreadAsJSON(thread: AgentThread, messages: Message[]) {
|
||||
const json = formatThreadAsJSON(thread, messages);
|
||||
const filename = `${sanitizeFilename(titleOfThread(thread))}.json`;
|
||||
downloadAsFile(json, filename, "application/json;charset=utf-8");
|
||||
}
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
import {
|
||||
extractContentFromMessage,
|
||||
extractReasoningContentFromMessage,
|
||||
hasContent,
|
||||
hasToolCalls,
|
||||
stripUploadedFilesTag,
|
||||
} from "../messages/utils";
|
||||
|
||||
import type { AgentThread } from "./types";
|
||||
import { titleOfThread } from "./utils";
|
||||
|
||||
function formatMessageContent(message: Message): string {
|
||||
const text = extractContentFromMessage(message);
|
||||
if (!text) return "";
|
||||
return stripUploadedFilesTag(text);
|
||||
}
|
||||
|
||||
function formatToolCalls(message: Message): string {
|
||||
if (message.type !== "ai" || !hasToolCalls(message)) return "";
|
||||
const calls = message.tool_calls ?? [];
|
||||
return calls.map((call) => `- **Tool:** \`${call.name}\``).join("\n");
|
||||
}
|
||||
|
||||
export function formatThreadAsMarkdown(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
): string {
|
||||
const title = titleOfThread(thread);
|
||||
const createdAt = thread.created_at
|
||||
? new Date(thread.created_at).toLocaleString()
|
||||
: "Unknown";
|
||||
|
||||
const lines: string[] = [
|
||||
`# ${title}`,
|
||||
"",
|
||||
`*Exported on ${new Date().toLocaleString()} · Created ${createdAt}*`,
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
];
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.type === "human") {
|
||||
const content = formatMessageContent(message);
|
||||
if (content) {
|
||||
lines.push(`## 🧑 User`, "", content, "", "---", "");
|
||||
}
|
||||
} else if (message.type === "ai") {
|
||||
const reasoning = extractReasoningContentFromMessage(message);
|
||||
const content = formatMessageContent(message);
|
||||
const toolCalls = formatToolCalls(message);
|
||||
|
||||
if (!content && !toolCalls && !reasoning) continue;
|
||||
|
||||
lines.push(`## 🤖 Assistant`);
|
||||
|
||||
if (reasoning) {
|
||||
lines.push(
|
||||
"",
|
||||
"<details>",
|
||||
"<summary>Thinking</summary>",
|
||||
"",
|
||||
reasoning,
|
||||
"",
|
||||
"</details>",
|
||||
);
|
||||
}
|
||||
|
||||
if (toolCalls) {
|
||||
lines.push("", toolCalls);
|
||||
}
|
||||
|
||||
if (content && hasContent(message)) {
|
||||
lines.push("", content);
|
||||
}
|
||||
|
||||
lines.push("", "---", "");
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join("\n").trimEnd() + "\n";
|
||||
}
|
||||
|
||||
export function formatThreadAsJSON(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
): string {
|
||||
const exportData = {
|
||||
title: titleOfThread(thread),
|
||||
thread_id: thread.thread_id,
|
||||
created_at: thread.created_at,
|
||||
exported_at: new Date().toISOString(),
|
||||
messages: messages.map((msg) => ({
|
||||
type: msg.type,
|
||||
id: msg.id,
|
||||
content: typeof msg.content === "string" ? msg.content : msg.content,
|
||||
...(msg.type === "ai" && msg.tool_calls?.length
|
||||
? { tool_calls: msg.tool_calls }
|
||||
: {}),
|
||||
})),
|
||||
};
|
||||
return JSON.stringify(exportData, null, 2);
|
||||
}
|
||||
|
||||
function sanitizeFilename(name: string): string {
|
||||
return name.replace(/[^\p{L}\p{N}_\- ]/gu, "").trim() || "conversation";
|
||||
}
|
||||
|
||||
export function downloadAsFile(
|
||||
content: string,
|
||||
filename: string,
|
||||
mimeType: string,
|
||||
) {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export function exportThreadAsMarkdown(
|
||||
thread: AgentThread,
|
||||
messages: Message[],
|
||||
) {
|
||||
const markdown = formatThreadAsMarkdown(thread, messages);
|
||||
const filename = `${sanitizeFilename(titleOfThread(thread))}.md`;
|
||||
downloadAsFile(markdown, filename, "text/markdown;charset=utf-8");
|
||||
}
|
||||
|
||||
export function exportThreadAsJSON(thread: AgentThread, messages: Message[]) {
|
||||
const json = formatThreadAsJSON(thread, messages);
|
||||
const filename = `${sanitizeFilename(titleOfThread(thread))}.json`;
|
||||
downloadAsFile(json, filename, "application/json;charset=utf-8");
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1 +1 @@
|
||||
export * from "./types";
|
||||
export * from "./types";
|
||||
@@ -1,26 +1,26 @@
|
||||
import type { Message, Thread } from "@langchain/langgraph-sdk";
|
||||
|
||||
import type { Todo } from "../todos";
|
||||
|
||||
export interface AgentThreadState extends Record<string, unknown> {
|
||||
title: string;
|
||||
messages: Message[];
|
||||
artifacts: string[];
|
||||
todos?: Todo[];
|
||||
}
|
||||
|
||||
export interface AgentThreadContext extends Record<string, unknown> {
|
||||
thread_id: string;
|
||||
model_name: string | undefined;
|
||||
thinking_enabled: boolean;
|
||||
is_plan_mode: boolean;
|
||||
subagent_enabled: boolean;
|
||||
reasoning_effort?: "minimal" | "low" | "medium" | "high";
|
||||
agent_id?: string;
|
||||
/** @deprecated kept for reading legacy thread metadata; prefer agent_id */
|
||||
agent_name?: string;
|
||||
}
|
||||
|
||||
export interface AgentThread extends Thread<AgentThreadState> {
|
||||
context?: AgentThreadContext;
|
||||
}
|
||||
import type { Message, Thread } from "@langchain/langgraph-sdk";
|
||||
|
||||
import type { Todo } from "../todos";
|
||||
|
||||
export interface AgentThreadState extends Record<string, unknown> {
|
||||
title: string;
|
||||
messages: Message[];
|
||||
artifacts: string[];
|
||||
todos?: Todo[];
|
||||
}
|
||||
|
||||
export interface AgentThreadContext extends Record<string, unknown> {
|
||||
thread_id: string;
|
||||
model_name: string | undefined;
|
||||
thinking_enabled: boolean;
|
||||
is_plan_mode: boolean;
|
||||
subagent_enabled: boolean;
|
||||
reasoning_effort?: "minimal" | "low" | "medium" | "high";
|
||||
agent_id?: string;
|
||||
/** @deprecated kept for reading legacy thread metadata; prefer agent_id */
|
||||
agent_name?: string;
|
||||
}
|
||||
|
||||
export interface AgentThread extends Thread<AgentThreadState> {
|
||||
context?: AgentThreadContext;
|
||||
}
|
||||
@@ -1,58 +1,58 @@
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
import type { AgentThread, AgentThreadContext } from "./types";
|
||||
|
||||
type ThreadAgentRef = Pick<AgentThreadContext, "agent_id" | "agent_name">;
|
||||
|
||||
type ThreadRouteTarget =
|
||||
| string
|
||||
| {
|
||||
thread_id: string;
|
||||
context?: ThreadAgentRef | null;
|
||||
metadata?: Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
function pickAgentRef(
|
||||
context: ThreadAgentRef | null | undefined,
|
||||
metadata: Record<string, unknown> | null | undefined,
|
||||
): string | undefined {
|
||||
if (context?.agent_id) return context.agent_id;
|
||||
if (context?.agent_name) return context.agent_name;
|
||||
const metaId = metadata?.agent_id;
|
||||
if (typeof metaId === "string" && metaId) return metaId;
|
||||
const metaName = metadata?.agent_name;
|
||||
if (typeof metaName === "string" && metaName) return metaName;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function pathOfThread(
|
||||
thread: ThreadRouteTarget,
|
||||
context?: ThreadAgentRef | null,
|
||||
) {
|
||||
const threadId = typeof thread === "string" ? thread : thread.thread_id;
|
||||
const agentRef =
|
||||
typeof thread === "string"
|
||||
? pickAgentRef(context, null)
|
||||
: pickAgentRef(thread.context, thread.metadata);
|
||||
|
||||
return agentRef
|
||||
? `/workspace/agents/${encodeURIComponent(agentRef)}/chats/${threadId}`
|
||||
: `/workspace/chats/${threadId}`;
|
||||
}
|
||||
|
||||
export function textOfMessage(message: Message) {
|
||||
if (typeof message.content === "string") {
|
||||
return message.content;
|
||||
} else if (Array.isArray(message.content)) {
|
||||
for (const part of message.content) {
|
||||
if (part.type === "text") {
|
||||
return part.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function titleOfThread(thread: AgentThread) {
|
||||
return thread.values?.title ?? "Untitled";
|
||||
}
|
||||
import type { Message } from "@langchain/langgraph-sdk";
|
||||
|
||||
import type { AgentThread, AgentThreadContext } from "./types";
|
||||
|
||||
type ThreadAgentRef = Pick<AgentThreadContext, "agent_id" | "agent_name">;
|
||||
|
||||
type ThreadRouteTarget =
|
||||
| string
|
||||
| {
|
||||
thread_id: string;
|
||||
context?: ThreadAgentRef | null;
|
||||
metadata?: Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
function pickAgentRef(
|
||||
context: ThreadAgentRef | null | undefined,
|
||||
metadata: Record<string, unknown> | null | undefined,
|
||||
): string | undefined {
|
||||
if (context?.agent_id) return context.agent_id;
|
||||
if (context?.agent_name) return context.agent_name;
|
||||
const metaId = metadata?.agent_id;
|
||||
if (typeof metaId === "string" && metaId) return metaId;
|
||||
const metaName = metadata?.agent_name;
|
||||
if (typeof metaName === "string" && metaName) return metaName;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function pathOfThread(
|
||||
thread: ThreadRouteTarget,
|
||||
context?: ThreadAgentRef | null,
|
||||
) {
|
||||
const threadId = typeof thread === "string" ? thread : thread.thread_id;
|
||||
const agentRef =
|
||||
typeof thread === "string"
|
||||
? pickAgentRef(context, null)
|
||||
: pickAgentRef(thread.context, thread.metadata);
|
||||
|
||||
return agentRef
|
||||
? `/workspace/agents/${encodeURIComponent(agentRef)}/chats/${threadId}`
|
||||
: `/workspace/chats/${threadId}`;
|
||||
}
|
||||
|
||||
export function textOfMessage(message: Message) {
|
||||
if (typeof message.content === "string") {
|
||||
return message.content;
|
||||
} else if (Array.isArray(message.content)) {
|
||||
for (const part of message.content) {
|
||||
if (part.type === "text") {
|
||||
return part.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function titleOfThread(thread: AgentThread) {
|
||||
return thread.values?.title ?? "Untitled";
|
||||
}
|
||||
Reference in new issue
Block a user