refactor(agents): dedupe transient error copy (#16324)

This commit is contained in:
Peter Steinberger
2026-02-14 17:49:25 +01:00
committed by GitHub
parent 3e6d1e9cf8
commit d714ac7797
4 changed files with 29 additions and 13 deletions
+18 -11
View File
@@ -14,6 +14,18 @@ export function formatBillingErrorMessage(provider?: string): string {
export const BILLING_ERROR_USER_MESSAGE = formatBillingErrorMessage(); export const BILLING_ERROR_USER_MESSAGE = formatBillingErrorMessage();
const RATE_LIMIT_ERROR_USER_MESSAGE = "⚠️ API rate limit reached. Please try again later."; const RATE_LIMIT_ERROR_USER_MESSAGE = "⚠️ API rate limit reached. Please try again later.";
const OVERLOADED_ERROR_USER_MESSAGE =
"The AI service is temporarily overloaded. Please try again in a moment.";
function formatRateLimitOrOverloadedErrorCopy(raw: string): string | undefined {
if (isRateLimitErrorMessage(raw)) {
return RATE_LIMIT_ERROR_USER_MESSAGE;
}
if (isOverloadedErrorMessage(raw)) {
return OVERLOADED_ERROR_USER_MESSAGE;
}
return undefined;
}
export function isContextOverflowError(errorMessage?: string): boolean { export function isContextOverflowError(errorMessage?: string): boolean {
if (!errorMessage) { if (!errorMessage) {
@@ -463,12 +475,9 @@ export function formatAssistantErrorText(
return `LLM request rejected: ${invalidRequest[1]}`; return `LLM request rejected: ${invalidRequest[1]}`;
} }
if (isRateLimitErrorMessage(raw)) { const transientCopy = formatRateLimitOrOverloadedErrorCopy(raw);
return RATE_LIMIT_ERROR_USER_MESSAGE; if (transientCopy) {
} return transientCopy;
if (isOverloadedErrorMessage(raw)) {
return "The AI service is temporarily overloaded. Please try again in a moment.";
} }
if (isBillingErrorMessage(raw)) { if (isBillingErrorMessage(raw)) {
@@ -523,11 +532,9 @@ export function sanitizeUserFacingText(text: string, opts?: { errorContext?: boo
} }
if (ERROR_PREFIX_RE.test(trimmed)) { if (ERROR_PREFIX_RE.test(trimmed)) {
if (isRateLimitErrorMessage(trimmed)) { const prefixedCopy = formatRateLimitOrOverloadedErrorCopy(trimmed);
return RATE_LIMIT_ERROR_USER_MESSAGE; if (prefixedCopy) {
} return prefixedCopy;
if (isOverloadedErrorMessage(trimmed)) {
return "The AI service is temporarily overloaded. Please try again in a moment.";
} }
if (isTimeoutErrorMessage(trimmed)) { if (isTimeoutErrorMessage(trimmed)) {
return "LLM request timed out."; return "LLM request timed out.";
@@ -57,7 +57,7 @@ export function handleMessageUpdate(
return; return;
} }
ctx.state.lastAssistant = msg; ctx.noteLastAssistant(msg);
const assistantEvent = evt.assistantMessageEvent; const assistantEvent = evt.assistantMessageEvent;
const assistantRecord = const assistantRecord =
@@ -200,7 +200,7 @@ export function handleMessageEnd(
} }
const assistantMessage = msg; const assistantMessage = msg;
ctx.state.lastAssistant = assistantMessage; ctx.noteLastAssistant(assistantMessage);
ctx.recordAssistantUsage((assistantMessage as { usage?: unknown }).usage); ctx.recordAssistantUsage((assistantMessage as { usage?: unknown }).usage);
promoteThinkingTagsToBlocks(assistantMessage); promoteThinkingTagsToBlocks(assistantMessage);
@@ -72,6 +72,7 @@ export type EmbeddedPiSubscribeContext = {
blockChunking?: BlockReplyChunking; blockChunking?: BlockReplyChunking;
blockChunker: EmbeddedBlockChunker | null; blockChunker: EmbeddedBlockChunker | null;
hookRunner?: HookRunner; hookRunner?: HookRunner;
noteLastAssistant: (msg: AgentMessage) => void;
shouldEmitToolResult: () => boolean; shouldEmitToolResult: () => boolean;
shouldEmitToolOutput: () => boolean; shouldEmitToolOutput: () => boolean;
+8
View File
@@ -1,3 +1,4 @@
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { InlineCodeState } from "../markdown/code-spans.js"; import type { InlineCodeState } from "../markdown/code-spans.js";
import type { import type {
EmbeddedPiSubscribeContext, EmbeddedPiSubscribeContext,
@@ -569,6 +570,12 @@ export function subscribeEmbeddedPiSession(params: SubscribeEmbeddedPiSessionPar
resetAssistantMessageState(0); resetAssistantMessageState(0);
}; };
const noteLastAssistant = (msg: AgentMessage) => {
if (msg?.role === "assistant") {
state.lastAssistant = msg;
}
};
const ctx: EmbeddedPiSubscribeContext = { const ctx: EmbeddedPiSubscribeContext = {
params, params,
state, state,
@@ -576,6 +583,7 @@ export function subscribeEmbeddedPiSession(params: SubscribeEmbeddedPiSessionPar
blockChunking, blockChunking,
blockChunker, blockChunker,
hookRunner: params.hookRunner, hookRunner: params.hookRunner,
noteLastAssistant,
shouldEmitToolResult, shouldEmitToolResult,
shouldEmitToolOutput, shouldEmitToolOutput,
emitToolSummary, emitToolSummary,