Browse Source
New users now land on Popi gateway defaults that match the tested production-style path: APIYI Nano Banana 2 for images, Doubao Seedance for video, and Doubao Seed 2.0 Lite for LLM chat/tool use. Settings and FTUX screens render these effective defaults instead of empty placeholders, while existing local node defaults keep taking precedence. The same pass keeps provider-overload errors user-facing by formatting the model name that the node currently displays, so fallback and switched-model failures are easier to understand across media nodes. Constraint: Popi mode should hide internal NewApiWG naming and default to models available through the Popi gateway. Rejected: Persist default values into localStorage for every new user | would make future system default changes harder and could overwrite explicit user preferences. Confidence: high Scope-risk: moderate Directive: Do not change these defaults without checking the Popi gateway model ids and www.popi.art login/model-routing assumptions. Tested: npm run test:run -- src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/ProjectSetupModal.test.tsx src/components/__tests__/GenerationComposer.test.tsx src/app/api/chat/__tests__/route.test.ts src/components/__tests__/LLMGenerateNode.test.tsx src/utils/__tests__/userFacingErrors.test.ts Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build Tested: git diff --check Not-tested: npm run lint currently fails because next lint is no longer a valid project command in this Next.js setupfeature/add_agent_md
21 changed files with 381 additions and 255 deletions
@ -0,0 +1,31 @@ |
|||
import { describe, expect, it } from "vitest"; |
|||
import type { TranslationKey } from "@/i18n"; |
|||
import { formatUserFacingError } from "@/utils/userFacingErrors"; |
|||
|
|||
const t = (key: TranslationKey, values?: Record<string, string | number>) => { |
|||
if (key === "error.providerOverloaded") { |
|||
return `${values?.model}: localized overload`; |
|||
} |
|||
return key; |
|||
}; |
|||
|
|||
describe("formatUserFacingError", () => { |
|||
it("localizes normalized provider overload errors", () => { |
|||
expect(formatUserFacingError( |
|||
"apiyi_nano_banana_pro: provider is temporarily overloaded. Please retry in a moment or switch models.", |
|||
t |
|||
)).toBe("apiyi_nano_banana_pro: localized overload"); |
|||
}); |
|||
|
|||
it("uses the currently displayed model name when provided", () => { |
|||
expect(formatUserFacingError( |
|||
"apiyi_nano_banana_pro: provider is temporarily overloaded. Please retry in a moment or switch models.", |
|||
t, |
|||
"PopiArt Nano Pro" |
|||
)).toBe("PopiArt Nano Pro: localized overload"); |
|||
}); |
|||
|
|||
it("preserves unknown upstream errors", () => { |
|||
expect(formatUserFacingError("unexpected upstream failure", t)).toBe("unexpected upstream failure"); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,23 @@ |
|||
import type { TranslationKey } from "@/i18n"; |
|||
|
|||
type Translate = (key: TranslationKey, values?: Record<string, string | number>) => string; |
|||
|
|||
const PROVIDER_OVERLOADED_RE = |
|||
/^(.*?): provider is temporarily overloaded\. Please retry in a moment or switch models\.$/i; |
|||
|
|||
export function formatUserFacingError( |
|||
message: string | null | undefined, |
|||
t: Translate, |
|||
modelDisplayName?: string | null |
|||
): string | null { |
|||
if (!message) return null; |
|||
|
|||
const overloadedMatch = message.match(PROVIDER_OVERLOADED_RE); |
|||
if (overloadedMatch) { |
|||
return t("error.providerOverloaded", { |
|||
model: modelDisplayName?.trim() || (overloadedMatch[1] ?? "Model").trim(), |
|||
}); |
|||
} |
|||
|
|||
return message; |
|||
} |
|||
Loading…
Reference in new issue