实现RAG参考文献显示模式

This commit is contained in:
吉鑫升 committed 2026-05-27 17:40:10 +08:00
1 parent eda0ccffdb
commit c56db15a03
22 files changed
+1733 -192

No files matched your search

+75 -8
View File
@@ -3,17 +3,19 @@ import type { ThreadsClient } from "@langchain/langgraph-sdk/client";
import { useStream } from "@langchain/langgraph-sdk/react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
import { getAPIClient } from "../api";
import { apiFetch } from "../api";
import { getStoredAuth } from "../auth";
import { getBackendBaseURL } from "../config";
import { useI18n } from "../i18n/hooks";
import type { FileInMessage } from "../messages/utils";
import type { LocalSettings } from "../settings";
import { useLocalSettings } from "../settings";
import type { LocalSettings } from "../settings";
import { isThreadRagModeLocked, lockThreadRagMode, useLocalSettings } from "../settings";
import { useUpdateSubtask } from "../tasks/context";
import type { UploadedFileInfo } from "../uploads";
import { promptInputFilePartToFile, uploadFiles } from "../uploads";
@@ -137,6 +139,42 @@ function getStreamErrorMessage(error: unknown): string {
return "Request failed.";
}
/**
* Detect "thread not found" / forbidden errors from the LangGraph SDK.
*
* Backend returns 404 both when a thread truly doesn't exist and when the
* currently authenticated user doesn't own it — the SDK surfaces both as
* ``HTTP 404: {"detail":"Thread <uuid> not found"}``. The most common cause
* we see in practice is a stale tab whose URL still points at the previous
* login session's thread. Raw-toasting the 404 just confuses the user, so
* we re-frame it around the current login identity.
*/
function isThreadNotAccessibleError(message: string): boolean {
const lowered = message.toLowerCase();
if (/thread\s+[\w-]{8,}\s+not\s+found/i.test(message)) {
return true;
}
// Fallback: a bare 404/403 from the threads endpoint when the SDK has
// wrapped the detail differently.
return (
(lowered.includes("http 404") || lowered.includes("http 403")) &&
lowered.includes("thread")
);
}
function buildCurrentUserLabel(): string {
const auth = getStoredAuth();
if (!auth) {
return "未登录";
}
const email = auth.email?.trim();
if (email) {
return email;
}
const userId = auth.user_id?.trim();
return userId ? userId : "未知用户";
}
export function useThreadStream({
threadId,
context,
@@ -195,6 +233,7 @@ export function useThreadStream({
const queryClient = useQueryClient();
const updateSubtask = useUpdateSubtask();
const navigate = useNavigate();
const runMetadataStorageRef = useRef<
ReturnType<typeof getRunMetadataStorage> | undefined
>(undefined);
@@ -294,7 +333,29 @@ export function useThreadStream({
},
onError(error) {
setOptimisticMessages([]);
toast.error(getStreamErrorMessage(error));
const message = getStreamErrorMessage(error);
// A "thread not found" 404 almost always means the URL points at a
// thread from a previous login session — either this tab outlived a
// logout/re-login or the thread genuinely belongs to a different user.
// Re-frame the error around the *current* logged-in identity and bounce
// the user to a fresh chat instead of leaving them stuck on the stale
// route looking at a raw HTTP 404.
if (isThreadNotAccessibleError(message)) {
const userLabel = buildCurrentUserLabel();
toast.info(
`当前登录用户为 ${userLabel},原对话不存在或无权访问,已为你打开新对话。`,
);
// Prefer staying inside an agent chat context when the user is on an
// agent thread URL — otherwise fall back to the global new chat.
const hash = typeof window !== "undefined" ? window.location.hash : "";
const agentMatch = hash.match(/#\/page\/workspace\/agents\/([^/]+)\//);
const target = agentMatch
? `/page/workspace/agents/${agentMatch[1]}/chats/new`
: "/page/workspace/chats/new";
navigate(target, { replace: true });
return;
}
toast.error(message);
},
onFinish(state) {
listeners.current.onFinish?.(state.values);
@@ -494,7 +555,13 @@ export function useThreadStream({
}),
);
await thread.submit(
const ragModeEnabled =
localSettings.display.ragModeEnabled || isThreadRagModeLocked(threadId);
if (ragModeEnabled) {
lockThreadRagMode(threadId);
}
await thread.submit(
{
messages: [
{
@@ -537,10 +604,10 @@ export function useThreadStream({
? "low"
: undefined),
memory_injection_enabled: context.memory_injection_enabled ?? true,
rag_mode_enabled: localSettings.display.ragModeEnabled,
thread_id: threadId,
},
},
rag_mode_enabled: ragModeEnabled,
thread_id: threadId,
},
},
);
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
} catch (error) {