Prevent stale session overwrite on new tasks
This commit is contained in:
+34
-2
@@ -2382,6 +2382,19 @@ def create_app(state: AgentState) -> FastAPI:
|
|||||||
) or uuid4().hex
|
) or uuid4().hex
|
||||||
account_key = state._account_key(request.account_id)
|
account_key = state._account_key(request.account_id)
|
||||||
prompt = request.prompt.strip()
|
prompt = request.prompt.strip()
|
||||||
|
session_directory = state.account_paths(request.account_id)['sessions']
|
||||||
|
if (
|
||||||
|
request.resume_session_id is None
|
||||||
|
and run_record is None
|
||||||
|
and _agent_session_exists(session_directory, requested_session_id)
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=(
|
||||||
|
'Session already exists; pass resume_session_id to continue '
|
||||||
|
'or create a fresh session_id.'
|
||||||
|
),
|
||||||
|
)
|
||||||
if run_record is None:
|
if run_record is None:
|
||||||
run_record = state.run_manager.start(
|
run_record = state.run_manager.start(
|
||||||
account_key,
|
account_key,
|
||||||
@@ -2398,7 +2411,6 @@ def create_app(state: AgentState) -> FastAPI:
|
|||||||
run_lock = state.run_lock_for(request.account_id, requested_session_id)
|
run_lock = state.run_lock_for(request.account_id, requested_session_id)
|
||||||
agent = state.agent_for(request.account_id, requested_session_id)
|
agent = state.agent_for(request.account_id, requested_session_id)
|
||||||
config = state.config_for(request.account_id)
|
config = state.config_for(request.account_id)
|
||||||
session_directory = state.account_paths(request.account_id)['sessions']
|
|
||||||
jupyter_runtime = _jupyter_runtime_for_session(
|
jupyter_runtime = _jupyter_runtime_for_session(
|
||||||
state,
|
state,
|
||||||
request.account_id,
|
request.account_id,
|
||||||
@@ -2658,6 +2670,18 @@ def create_app(state: AgentState) -> FastAPI:
|
|||||||
) or uuid4().hex
|
) or uuid4().hex
|
||||||
account_key = state._account_key(request.account_id)
|
account_key = state._account_key(request.account_id)
|
||||||
prompt = request.prompt.strip()
|
prompt = request.prompt.strip()
|
||||||
|
session_directory = state.account_paths(request.account_id)['sessions']
|
||||||
|
if (
|
||||||
|
request.resume_session_id is None
|
||||||
|
and _agent_session_exists(session_directory, requested_session_id)
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=(
|
||||||
|
'Session already exists; pass resume_session_id to continue '
|
||||||
|
'or create a fresh session_id.'
|
||||||
|
),
|
||||||
|
)
|
||||||
run_record = state.run_manager.start(account_key, requested_session_id, prompt)
|
run_record = state.run_manager.start(account_key, requested_session_id, prompt)
|
||||||
state.run_state_store.start(
|
state.run_state_store.start(
|
||||||
run_id=run_record.run_id,
|
run_id=run_record.run_id,
|
||||||
@@ -2672,7 +2696,7 @@ def create_app(state: AgentState) -> FastAPI:
|
|||||||
try:
|
try:
|
||||||
agent = state.agent_for(request.account_id, requested_session_id)
|
agent = state.agent_for(request.account_id, requested_session_id)
|
||||||
_save_in_progress_session(
|
_save_in_progress_session(
|
||||||
directory=state.account_paths(request.account_id)['sessions'],
|
directory=session_directory,
|
||||||
agent=agent,
|
agent=agent,
|
||||||
session_id=requested_session_id,
|
session_id=requested_session_id,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
@@ -3347,6 +3371,14 @@ def _save_in_progress_session(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _agent_session_exists(directory: Path, session_id: str) -> bool:
|
||||||
|
try:
|
||||||
|
load_agent_session(session_id, directory=directory)
|
||||||
|
except (FileNotFoundError, OSError, json.JSONDecodeError):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _derive_initial_session_title(prompt: str) -> str | None:
|
def _derive_initial_session_title(prompt: str) -> str | None:
|
||||||
# 第一条消息刚发出时先给侧边栏一个可读标题,后续再由模型摘要精修。
|
# 第一条消息刚发出时先给侧边栏一个可读标题,后续再由模型摘要精修。
|
||||||
stripped = _strip_session_context(prompt)
|
stripped = _strip_session_context(prompt)
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export async function POST(req: Request) {
|
|||||||
if (!account) return new Response("请先登录账号", { status: 401 });
|
if (!account) return new Response("请先登录账号", { status: 401 });
|
||||||
|
|
||||||
const { id, messages, resumeSessionId }: ChatRequestBody = await req.json();
|
const { id, messages, resumeSessionId }: ChatRequestBody = await req.json();
|
||||||
const resumeId = resumeSessionId ?? getLastSessionId(messages);
|
const resumeId = resumeSessionId;
|
||||||
const sessionId = safeSessionId(resumeId ?? id ?? crypto.randomUUID());
|
const sessionId = safeSessionId(resumeId ?? id ?? crypto.randomUUID());
|
||||||
await ensureSessionDirectories(account.id, sessionId);
|
await ensureSessionDirectories(account.id, sessionId);
|
||||||
const userPrompt = await getLastUserText(messages, account.id, sessionId);
|
const userPrompt = await getLastUserText(messages, account.id, sessionId);
|
||||||
@@ -362,18 +362,6 @@ function renderSessionRuntimeContext(accountId: string, sessionId: string) {
|
|||||||
].join("\n\n");
|
].join("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLastSessionId(messages: UIMessage[]) {
|
|
||||||
for (const message of [...messages].reverse()) {
|
|
||||||
if (message.role !== "assistant") continue;
|
|
||||||
const metadata = message.metadata;
|
|
||||||
if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
|
|
||||||
const sessionId = (metadata as { sessionId?: unknown }).sessionId;
|
|
||||||
if (typeof sessionId === "string" && sessionId) return sessionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveFilePart(
|
async function saveFilePart(
|
||||||
part: Extract<UIMessage["parts"][number], { type: "file" }>,
|
part: Extract<UIMessage["parts"][number], { type: "file" }>,
|
||||||
accountId: string,
|
accountId: string,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ACTIVE_SESSION_CHANGED_EVENT,
|
ACTIVE_SESSION_CHANGED_EVENT,
|
||||||
consumeFreshLocalId,
|
consumeFreshLocalId,
|
||||||
|
createLocalSessionId,
|
||||||
readActiveSessionId,
|
readActiveSessionId,
|
||||||
readPendingWorkspaceSessionId,
|
readPendingWorkspaceSessionId,
|
||||||
writeActiveSessionId,
|
writeActiveSessionId,
|
||||||
@@ -60,11 +61,7 @@ export const Assistant = ({ initialSessionId }: AssistantProps = {}) => {
|
|||||||
const body = options.body as Record<string, unknown>;
|
const body = options.body as Record<string, unknown>;
|
||||||
const messages = options.messages as UIMessage[];
|
const messages = options.messages as UIMessage[];
|
||||||
const selectedSessionId =
|
const selectedSessionId =
|
||||||
readSessionIdFromUrl() ??
|
readSessionIdFromUrl() ?? readActiveSessionId() ?? null;
|
||||||
readActiveSessionId() ??
|
|
||||||
initialSessionId?.trim() ??
|
|
||||||
null;
|
|
||||||
const lastSessionId = getLastSessionId(messages);
|
|
||||||
const pendingWorkspaceSessionId =
|
const pendingWorkspaceSessionId =
|
||||||
!selectedSessionId && messages.length <= 1
|
!selectedSessionId && messages.length <= 1
|
||||||
? readPendingWorkspaceSessionId()
|
? readPendingWorkspaceSessionId()
|
||||||
@@ -73,8 +70,7 @@ export const Assistant = ({ initialSessionId }: AssistantProps = {}) => {
|
|||||||
const outgoingSessionId =
|
const outgoingSessionId =
|
||||||
selectedResumeSessionId ??
|
selectedResumeSessionId ??
|
||||||
pendingWorkspaceSessionId ??
|
pendingWorkspaceSessionId ??
|
||||||
lastSessionId ??
|
createLocalSessionId();
|
||||||
options.id;
|
|
||||||
const lastUserMessage = [...messages]
|
const lastUserMessage = [...messages]
|
||||||
.reverse()
|
.reverse()
|
||||||
.find((message) => message.role === "user");
|
.find((message) => message.role === "user");
|
||||||
@@ -86,13 +82,12 @@ export const Assistant = ({ initialSessionId }: AssistantProps = {}) => {
|
|||||||
...body,
|
...body,
|
||||||
id: outgoingSessionId,
|
id: outgoingSessionId,
|
||||||
messages: lastUserMessage ? [lastUserMessage] : [],
|
messages: lastUserMessage ? [lastUserMessage] : [],
|
||||||
resumeSessionId:
|
resumeSessionId: selectedResumeSessionId ?? undefined,
|
||||||
selectedResumeSessionId ?? lastSessionId ?? undefined,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[initialSessionId],
|
[],
|
||||||
);
|
);
|
||||||
const runtime = useChatRuntime({
|
const runtime = useChatRuntime({
|
||||||
transport,
|
transport,
|
||||||
@@ -410,15 +405,3 @@ function ActivityDrawerTrigger() {
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLastSessionId(messages: UIMessage[]) {
|
|
||||||
for (const message of [...messages].reverse()) {
|
|
||||||
if (message.role !== "assistant") continue;
|
|
||||||
const metadata = message.metadata;
|
|
||||||
if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
|
|
||||||
const sessionId = (metadata as { sessionId?: unknown }).sessionId;
|
|
||||||
if (typeof sessionId === "string" && sessionId) return sessionId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ export function clearActiveSessionId() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createLocalSessionId() {
|
||||||
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
||||||
|
return `__LOCALID_${crypto.randomUUID().replace(/-/g, "").slice(0, 8)}`;
|
||||||
|
}
|
||||||
|
return `__LOCALID_${Math.random().toString(36).slice(2, 10)}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function writePendingWorkspaceSessionId(
|
export function writePendingWorkspaceSessionId(
|
||||||
sessionId: string | null | undefined,
|
sessionId: string | null | undefined,
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user