refactor(chat): centralize agent session URLs

This commit is contained in:
2026-08-06 10:40:13 +08:00
parent 5037089057
commit 71bde7d9e4
2 changed files with 85 additions and 30 deletions
@@ -3,14 +3,50 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { abortAgentChat, forkAgentChat, rejectAgentQuestion, replyAgentCredentialRefresh, replyAgentPermission, replyAgentQuestion, resumeAgentChatStream, streamAgentChat } from "@/lib/chatStream"; import {
abortAgentChat,
forkAgentChat,
rejectAgentQuestion,
replyAgentCredentialRefresh,
replyAgentPermission,
replyAgentQuestion,
resumeAgentChatStream,
streamAgentChat,
} from "@/lib/chatStream";
import type { PermissionReply, StreamEvent } from "@/lib/chatStream"; import type { PermissionReply, StreamEvent } from "@/lib/chatStream";
import { useAuthStore } from "@/store/authStore"; import { useAuthStore } from "@/store/authStore";
import type { AgentArtifact, ChatSessionSummary, Message } from "../GlobalChatbox.types"; import type {
AgentArtifact,
ChatSessionSummary,
Message,
} from "../GlobalChatbox.types";
import { cloneMessages } from "../globalChatboxUtils"; import { cloneMessages } from "../globalChatboxUtils";
import { createEmptyChatState, deleteChatSession, listChatSessions, loadChatSessionById, updateChatSessionTitle } from "../chatStorage"; import {
import { applyQuestionResponse, cancelRunningTodos, completeRunningProgress, createAssistantMessage, createTodoUpdateFromEvent, createUserMessage, dedupeQuestionsAcrossMessages, finalizeAssistantMessageAfterAbort, normalizeSessionTodos, toPermissionStatus, upsertPermission, upsertProgress, upsertQuestionAcrossMessages } from "./agentChatSessionState"; createEmptyChatState,
import type { PromptRunOptions, UseAgentChatSessionOptions } from "./useAgentChatSession.types"; deleteChatSession,
listChatSessions,
loadChatSessionById,
updateChatSessionTitle,
} from "../chatStorage";
import {
applyQuestionResponse,
cancelRunningTodos,
completeRunningProgress,
createAssistantMessage,
createTodoUpdateFromEvent,
createUserMessage,
dedupeQuestionsAcrossMessages,
finalizeAssistantMessageAfterAbort,
normalizeSessionTodos,
toPermissionStatus,
upsertPermission,
upsertProgress,
upsertQuestionAcrossMessages,
} from "./agentChatSessionState";
import type {
PromptRunOptions,
UseAgentChatSessionOptions,
} from "./useAgentChatSession.types";
const TOKEN_PLAYBACK_INTERVAL_MS = 16; const TOKEN_PLAYBACK_INTERVAL_MS = 16;
const TOKEN_PLAYBACK_BASE_CHARS = 28; const TOKEN_PLAYBACK_BASE_CHARS = 28;
+44 -25
View File
@@ -1,6 +1,11 @@
import { apiFetch } from "@/lib/apiFetch"; import { apiFetch } from "@/lib/apiFetch";
import { config } from "@config/config"; import { config } from "@config/config";
const AGENT_SESSIONS_URL = `${config.AGENT_URL}/api/v1/agent/sessions`;
const getAgentSessionUrl = (sessionId: string, suffix = "") =>
`${AGENT_SESSIONS_URL}/${encodeURIComponent(sessionId)}${suffix}`;
export type AgentModel = string; export type AgentModel = string;
export type PermissionReply = "once" | "always" | "reject"; export type PermissionReply = "once" | "always" | "reject";
@@ -509,7 +514,7 @@ const readStreamEvents = async (
const ensureAgentSession = async (sessionId?: string) => { const ensureAgentSession = async (sessionId?: string) => {
if (sessionId) return sessionId; if (sessionId) return sessionId;
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/sessions`, { const response = await apiFetch(AGENT_SESSIONS_URL, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -519,7 +524,10 @@ const ensureAgentSession = async (sessionId?: string) => {
skipAuthRedirect: true, skipAuthRedirect: true,
}); });
if (!response.ok) { if (!response.ok) {
throw new Error((await response.text()) || `session creation failed: ${response.status}`); throw new Error(
(await response.text()) ||
`session creation failed: ${response.status}`,
);
} }
const payload = (await response.json()) as { session_id?: string }; const payload = (await response.json()) as { session_id?: string };
if (!payload.session_id) { if (!payload.session_id) {
@@ -540,7 +548,7 @@ export const streamAgentChat = async ({
try { try {
const effectiveSessionId = await ensureAgentSession(sessionId); const effectiveSessionId = await ensureAgentSession(sessionId);
response = await apiFetch( response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(effectiveSessionId)}/runs`, getAgentSessionUrl(effectiveSessionId, "/runs"),
{ {
method: "POST", method: "POST",
signal, signal,
@@ -597,7 +605,7 @@ export const resumeAgentChatStream = async ({
let response: Response; let response: Response;
try { try {
response = await apiFetch( response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/runs/current/events`, getAgentSessionUrl(sessionId, "/runs/current/events"),
{ {
method: "GET", method: "GET",
signal, signal,
@@ -638,11 +646,14 @@ export const abortAgentChat = async (sessionId?: string) => {
return; return;
} }
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/runs/current`, { const response = await apiFetch(
method: "DELETE", getAgentSessionUrl(sessionId, "/runs/current"),
projectHeaderMode: "include", {
skipAuthRedirect: true, method: "DELETE",
}); projectHeaderMode: "include",
skipAuthRedirect: true,
},
);
if (!response.ok) { if (!response.ok) {
const detail = await response.text(); const detail = await response.text();
@@ -656,7 +667,7 @@ export const replyAgentPermission = async (
reply: PermissionReply, reply: PermissionReply,
) => { ) => {
const response = await apiFetch( const response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/permission-responses`, getAgentSessionUrl(sessionId, "/permission-responses"),
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -682,7 +693,7 @@ export const replyAgentCredentialRefresh = async (
requestId: string, requestId: string,
) => { ) => {
const response = await apiFetch( const response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/credential-refreshes`, getAgentSessionUrl(sessionId, "/credential-refreshes"),
{ {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@@ -693,7 +704,9 @@ export const replyAgentCredentialRefresh = async (
); );
if (!response.ok) { if (!response.ok) {
const detail = await response.text(); const detail = await response.text();
throw new Error(detail || `credential refresh reply failed: ${response.status}`); throw new Error(
detail || `credential refresh reply failed: ${response.status}`,
);
} }
}; };
@@ -703,7 +716,7 @@ export const replyAgentQuestion = async (
answers: string[][], answers: string[][],
) => { ) => {
const response = await apiFetch( const response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/question-responses`, getAgentSessionUrl(sessionId, "/question-responses"),
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -730,7 +743,7 @@ export const rejectAgentQuestion = async (
requestId: string, requestId: string,
) => { ) => {
const response = await apiFetch( const response = await apiFetch(
`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId)}/question-responses`, getAgentSessionUrl(sessionId, "/question-responses"),
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -751,18 +764,24 @@ export const rejectAgentQuestion = async (
} }
}; };
export const forkAgentChat = async (sessionId: string | undefined, keepMessageCount: number) => { export const forkAgentChat = async (
const response = await apiFetch(`${config.AGENT_URL}/api/v1/agent/sessions/${encodeURIComponent(sessionId ?? "")}/forks`, { sessionId: string | undefined,
method: "POST", keepMessageCount: number,
headers: { ) => {
"Content-Type": "application/json", const response = await apiFetch(
getAgentSessionUrl(sessionId ?? "", "/forks"),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
keep_message_count: keepMessageCount,
}),
projectHeaderMode: "include",
skipAuthRedirect: true,
}, },
body: JSON.stringify({ );
keep_message_count: keepMessageCount,
}),
projectHeaderMode: "include",
skipAuthRedirect: true,
});
if (!response.ok) { if (!response.ok) {
const detail = await response.text(); const detail = await response.text();