Q61: a failed status check no longer ends recovery of a background response
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88463e4396
commit
46cd167c2c
6 files changed
+93
-2
No files matched your search
@@ -0,0 +1,7 @@
|
||||
export const backgroundPollFailuresSchema = `
|
||||
alter table provider_requests add column poll_failures integer not null default 0;
|
||||
alter table provider_requests add column last_poll_error text;
|
||||
|
||||
insert into schema_migrations(version, name, applied_at)
|
||||
values (71, 'background_poll_failures', datetime('now'));
|
||||
`;
|
||||
@@ -69,6 +69,7 @@ import { taskRunBudgetSchema } from "./migrations/067-task-run-budget";
|
||||
import { taskReviewLinkSchema } from "./migrations/068-task-review-link";
|
||||
import { goalStallSchema } from "./migrations/069-goal-stall";
|
||||
import { runActionsSchema } from "./migrations/070-run-actions";
|
||||
import { backgroundPollFailuresSchema } from "./migrations/071-background-poll-failures";
|
||||
|
||||
interface Migration {
|
||||
version: number;
|
||||
@@ -147,6 +148,7 @@ const MIGRATIONS: Migration[] = [
|
||||
{ version: 68, name: "task_review_link", sql: taskReviewLinkSchema },
|
||||
{ version: 69, name: "goal_stall", sql: goalStallSchema },
|
||||
{ version: 70, name: "run_actions", sql: runActionsSchema },
|
||||
{ version: 71, name: "background_poll_failures", sql: backgroundPollFailuresSchema },
|
||||
];
|
||||
|
||||
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
||||
|
||||
@@ -24,6 +24,7 @@ const AUTH_STATE_CONFIGURED = "configured";
|
||||
const HEALTH_STATE_HEALTHY = "healthy";
|
||||
const ACTIVE_BACKGROUND_STATUSES = new Set(["queued", "in_progress"]);
|
||||
const FAILED_BACKGROUND_STATUSES = new Set(["failed", "incomplete"]);
|
||||
const MAX_POLL_FAILURES = 10;
|
||||
const CANCELLED_BACKGROUND_STATUSES = new Set(["cancelled"]);
|
||||
|
||||
type BackgroundCreateInput = CreateProviderResponseInput & {
|
||||
@@ -137,6 +138,7 @@ export class ProviderBackgroundService {
|
||||
});
|
||||
this.recordObservedRateLimit(request.org_id, providerRow, providerRow.model, result.rateLimitState);
|
||||
if (ACTIVE_BACKGROUND_STATUSES.has(result.status)) {
|
||||
this.requestStore.clearPollFailures(request.id);
|
||||
this.requestStore.markBackgroundProgress({
|
||||
orgId: request.org_id,
|
||||
requestId: request.id,
|
||||
@@ -149,6 +151,7 @@ export class ProviderBackgroundService {
|
||||
}
|
||||
if (result.status === "completed") {
|
||||
const responseText = requireBackgroundText(result.text, provider.provider_key);
|
||||
this.requestStore.clearPollFailures(request.id);
|
||||
this.requestStore.markSucceeded({
|
||||
orgId: request.org_id,
|
||||
requestId: request.id,
|
||||
@@ -170,6 +173,7 @@ export class ProviderBackgroundService {
|
||||
return;
|
||||
}
|
||||
if (CANCELLED_BACKGROUND_STATUSES.has(result.status)) {
|
||||
this.requestStore.clearPollFailures(request.id);
|
||||
this.requestStore.markCancelled({
|
||||
orgId: request.org_id,
|
||||
requestId: request.id,
|
||||
@@ -185,6 +189,15 @@ export class ProviderBackgroundService {
|
||||
}
|
||||
throw new Error(`Provider background response returned unsupported status ${result.status}.`);
|
||||
} catch (error) {
|
||||
// The provider reporting failure is final; a failed status check is not, since the remote job may still finish.
|
||||
let finalError = error;
|
||||
if (!(error instanceof ProviderBackgroundStatusError)) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const failures = this.requestStore.recordPollFailure(request.id, message);
|
||||
console.warn(`[provider-background] status check ${failures}/${MAX_POLL_FAILURES} for request ${request.id} failed: ${message}`);
|
||||
if (failures < MAX_POLL_FAILURES) return;
|
||||
finalError = new Error(`Gave up after ${MAX_POLL_FAILURES} failed status checks: ${message}`);
|
||||
}
|
||||
this.responseObserver.recordFailure({
|
||||
orgId: request.org_id,
|
||||
provider,
|
||||
@@ -192,7 +205,7 @@ export class ProviderBackgroundService {
|
||||
executionMode: PROVIDER_EXECUTION_MODE.BACKGROUND,
|
||||
startedAt,
|
||||
inputCharCount: request.input_char_count,
|
||||
error,
|
||||
error: finalError,
|
||||
rawResponseJson: error instanceof ProviderBackgroundStatusError ? error.rawResponseJson : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,6 +156,16 @@ export class ProviderRequestStore {
|
||||
);
|
||||
}
|
||||
|
||||
/** Counts a failed status check on a background request and returns how many happened in a row. */
|
||||
recordPollFailure(requestId: string, message: string): number {
|
||||
this.db.prepare("update provider_requests set poll_failures = poll_failures + 1, last_poll_error = ? where id = ?").run(message, requestId);
|
||||
return (this.db.prepare("select poll_failures from provider_requests where id = ?").get(requestId) as { poll_failures: number }).poll_failures;
|
||||
}
|
||||
|
||||
clearPollFailures(requestId: string): void {
|
||||
this.db.prepare("update provider_requests set poll_failures = 0, last_poll_error = null where id = ?").run(requestId);
|
||||
}
|
||||
|
||||
markBackgroundProgress(input: ProviderBackgroundProgressInput): void {
|
||||
this.db.prepare(`
|
||||
update provider_requests
|
||||
|
||||
Reference in new issue
Block a user