mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
fix(native): record OpenCode's rejected model as a model rejection
A driver-classified model_unavailable failure that requires action now maps to native_provider_model_rejected for any provider, so the task is blocked for a model choice and chat shows Model unavailable instead of a generic adapter failure. The shared blocker text no longer names a ChatGPT connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7eaf28031e
commit
d22e4962f9
2 files changed
+29
-2
No files matched your search
@@ -32,6 +32,22 @@ describe("native provider failure observation", () => {
|
||||
expect(observed.providerFailureForTerminal("other-turn", terminal)).toBeNull();
|
||||
expect(observed.providerFailureForTerminal("turn", { ...terminal, runTerminalState: "succeeded" })).toBeNull();
|
||||
});
|
||||
it.each(["opencode", "codex"])("classifies an action-required model_unavailable failure from %s as a model rejection", kind => {
|
||||
const observed = createNativeProviderFailureObservation({ kind });
|
||||
const providerFailure = { schema: "paperclip.provider-failure.v1", provider: "openrouter", phase: "turn",
|
||||
category: "model_unavailable", message: "vendor/private-model is not a valid model ID", retryability: "requires_action", httpStatus: 400 };
|
||||
observed.observe({ ...event, payload: { status: "failed", error: { providerFailure } } });
|
||||
const result = observed.forTerminal("turn", terminal);
|
||||
expect(result).toMatchObject({ errorCode: "native_provider_model_rejected", errorMessage: NATIVE_MODEL_REJECTION_MESSAGE });
|
||||
expect(JSON.stringify(result)).not.toContain("private-model");
|
||||
expect(observed.forTerminal("other-turn", terminal)).toBeNull();
|
||||
expect(observed.forTerminal("turn", { ...terminal, runTerminalState: "succeeded" })).toBeNull();
|
||||
});
|
||||
it.each(["transient", "unknown"])("does not treat a %s model_unavailable failure as a model rejection", retryability => {
|
||||
const observed = createNativeProviderFailureObservation({ kind: "opencode" });
|
||||
observed.observe({ ...event, payload: { status: "failed", error: { providerFailure: { schema: "paperclip.provider-failure.v1", provider: "openrouter", phase: "turn", category: "model_unavailable", message: "busy", retryability } } } });
|
||||
expect(observed.forTerminal("turn", terminal)).toBeNull();
|
||||
});
|
||||
it("surfaces a structured capacity failure without copying arbitrary provider text", () => {
|
||||
const observed = createNativeProviderFailureObservation({ kind: "codex" });
|
||||
observed.observe({ ...event, payload: { status: "failed", error: { codexErrorInfo: "serverOverloaded", message: "Private arbitrary response" } } });
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { PrpEvent, PrpTerminalState } from "../../vendor/paperclip-runner/index.js";
|
||||
import { readProviderFailure, type ProviderFailure } from "@paperclipai/shared";
|
||||
|
||||
// Shared by every provider whose selected model is rejected, so it names no specific connection.
|
||||
export const NATIVE_MODEL_REJECTION_MESSAGE =
|
||||
"The selected model is not supported by the current ChatGPT connection. Choose a supported model or a compatible AI connection.";
|
||||
"The selected model is not available through the current AI connection. Choose a supported model or a compatible AI connection.";
|
||||
|
||||
export const NATIVE_PROVIDER_OVERLOADED_MESSAGE =
|
||||
"Selected model is at capacity. Please try a different model.";
|
||||
@@ -266,7 +267,17 @@ export function createNativeProviderFailureObservation(provider: { kind: string;
|
||||
errorMessage: NATIVE_PROVIDER_OVERLOADED_MESSAGE,
|
||||
diagnostic: { ...NATIVE_PROVIDER_OVERLOADED_DIAGNOSTIC },
|
||||
};
|
||||
if (turnId !== rejectedTurnId) return null;
|
||||
if (turnId !== rejectedTurnId) {
|
||||
// A driver's closed category marks a rejected model for any provider. Only an
|
||||
// action-required rejection qualifies; a transient one may succeed on retry.
|
||||
const failure = providerFailures.get(turnId);
|
||||
if (failure?.category !== "model_unavailable" || failure.retryability !== "requires_action") return null;
|
||||
return {
|
||||
errorCode: "native_provider_model_rejected" as const,
|
||||
errorMessage: NATIVE_MODEL_REJECTION_MESSAGE,
|
||||
diagnostic: failedTurns.get(turnId) ?? { provider: failure.provider, category: failure.category },
|
||||
};
|
||||
}
|
||||
return {
|
||||
errorCode: "native_provider_model_rejected" as const,
|
||||
errorMessage: NATIVE_MODEL_REJECTION_MESSAGE,
|
||||
|
||||
Reference in new issue
Block a user