refactor: modularize portal server and harden session recovery
This commit is contained in:
@@ -33,6 +33,25 @@ function messageContentItems(event) {
|
||||
return candidates.find((content) => Array.isArray(content)) ?? [];
|
||||
}
|
||||
|
||||
function providerReplyError(event) {
|
||||
if (event?.type !== 'Message') return null;
|
||||
const text = messageContentItems(event)
|
||||
.filter((item) => item?.type === 'text')
|
||||
.map((item) => String(item?.text ?? ''))
|
||||
.join('')
|
||||
.trim();
|
||||
if (!/^Ran into this error:\s*/i.test(text)) return null;
|
||||
const normalized = text
|
||||
.replace(/^Ran into this error:\s*/i, '')
|
||||
.replace(/\n\nPlease retry if you think this is a transient or recoverable error\.?\s*$/i, '')
|
||||
.trim();
|
||||
const error = new Error(normalized || 'session reply failed');
|
||||
error.code = /tool_calls|tool_call_id|insufficient tool messages/i.test(normalized)
|
||||
? 'SESSION_TOOL_HISTORY_POISONED'
|
||||
: 'SESSION_REPLY_ERROR';
|
||||
return error;
|
||||
}
|
||||
|
||||
function parseGenerateImageResult(toolResult) {
|
||||
const content = toolResult?.value?.content;
|
||||
if (!Array.isArray(content)) return null;
|
||||
@@ -117,6 +136,7 @@ export async function consumeSessionEventsUntilFinish(
|
||||
const reader = Readable.fromWeb(body);
|
||||
const decoder = new TextDecoder();
|
||||
const toolEvidence = createToolEvidenceCollector();
|
||||
let pendingProviderError = null;
|
||||
let buffer = '';
|
||||
const deadline = Date.now() + Math.max(1, Number(timeoutMs) || 1);
|
||||
|
||||
@@ -137,6 +157,7 @@ export async function consumeSessionEventsUntilFinish(
|
||||
const event = parseSessionStreamEvent(trimmed);
|
||||
if (!event) continue;
|
||||
if (!eventMatchesRequest(event, requestId)) continue;
|
||||
pendingProviderError = providerReplyError(event) ?? pendingProviderError;
|
||||
toolEvidence.observe(event);
|
||||
onEvent?.(event);
|
||||
if (event.type === 'Error') {
|
||||
@@ -146,6 +167,7 @@ export async function consumeSessionEventsUntilFinish(
|
||||
throw err;
|
||||
}
|
||||
if (event.type === 'Finish') {
|
||||
if (pendingProviderError) throw pendingProviderError;
|
||||
return {
|
||||
finishEvent: event,
|
||||
tokenState: event.token_state ?? null,
|
||||
|
||||
Reference in New Issue
Block a user