Skip to content

Commit b464f4a

Browse files
committed
refactor(web-v2): centralize error CTA copy for byok provider failures
1 parent 0162949 commit b464f4a

4 files changed

Lines changed: 49 additions & 46 deletions

File tree

apps/web-v2/src/lib/error-presenter.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ export type UiErrorKind =
1212
export type UiErrorPresentation = {
1313
kind: UiErrorKind
1414
message: string
15-
ctaKey?: "refresh_shots" | "check_api_base" | "retry" | "check_fields" | "check_provider_quota"
15+
ctaKey?: UiErrorCtaKey
1616
}
1717

18+
export type UiErrorCtaKey = "refresh_shots" | "check_api_base" | "retry" | "check_fields" | "check_provider_quota"
19+
1820
export function classifyUiError(error: unknown): UiErrorKind {
1921
if (error instanceof HttpError) {
2022
const payloadMsg = (() => {
@@ -93,3 +95,22 @@ export function getUiErrorPresentation(error: unknown, locale: "en" | "zh-CN"):
9395
}
9496
return { kind, message, ctaKey: ctaByKind[kind] }
9597
}
98+
99+
export function getUiErrorCtaText(locale: "en" | "zh-CN", ctaKey?: UiErrorCtaKey): string {
100+
if (!ctaKey) return ""
101+
const zh: Record<UiErrorCtaKey, string> = {
102+
refresh_shots: "建议:刷新分镜列表",
103+
check_api_base: "建议:检查 API 地址与连接状态",
104+
retry: "建议:稍后重试",
105+
check_fields: "建议:检查输入字段",
106+
check_provider_quota: "建议:检查 BYOK Provider 的余额/额度",
107+
}
108+
const en: Record<UiErrorCtaKey, string> = {
109+
refresh_shots: "Tip: refresh shot list",
110+
check_api_base: "Tip: check API base URL and connection",
111+
retry: "Tip: retry later",
112+
check_fields: "Tip: check form fields",
113+
check_provider_quota: "Tip: check BYOK provider credits / balance",
114+
}
115+
return locale === "zh-CN" ? zh[ctaKey] : en[ctaKey]
116+
}

apps/web-v2/src/pages/script-desk/ScriptDesk.tsx

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from "lucide-react"
1616
import { useGlobalApiConnectionState, useGlobalDirectorModeState, useGlobalLocaleState } from "@/lib/workbench-ui"
1717
import { createApiClient } from "@/lib/http-client"
18-
import { getUiErrorPresentation } from "@/lib/error-presenter"
18+
import { getUiErrorCtaText, getUiErrorPresentation } from "@/lib/error-presenter"
1919
import { useQueryTask } from "@/lib/use-query-task"
2020
import { typo } from "@/components/workbench/typography"
2121
import { getProviderLabelFromAny, getProviderOptionsFromCapabilities, type ProviderUiValue, uiProviderToApi } from "@/lib/provider-registry"
@@ -156,26 +156,6 @@ function t(locale: Locale) {
156156
return copy[locale]
157157
}
158158

159-
function errorCtaText(locale: Locale, ctaKey?: "refresh_shots" | "check_api_base" | "retry" | "check_fields" | "check_provider_quota") {
160-
if (!ctaKey) return ""
161-
const map = locale === "zh-CN"
162-
? {
163-
refresh_shots: "建议:刷新分镜列表",
164-
check_api_base: "建议:检查 API 地址与连接状态",
165-
retry: "建议:稍后重试",
166-
check_fields: "建议:检查输入字段",
167-
check_provider_quota: "建议:检查 BYOK Provider 的余额/额度",
168-
}
169-
: {
170-
refresh_shots: "Tip: refresh shot list",
171-
check_api_base: "Tip: check API base URL and connection",
172-
retry: "Tip: retry later",
173-
check_fields: "Tip: check form fields",
174-
check_provider_quota: "Tip: check BYOK provider credits / balance",
175-
}
176-
return map[ctaKey]
177-
}
178-
179159
function Skeleton({ className }: { className?: string }) {
180160
return <div className={cn("animate-pulse rounded-md bg-zinc-800/80", className)} />
181161
}
@@ -461,7 +441,7 @@ export function ScriptDesk() {
461441
onError: (err) =>
462442
(() => {
463443
const ui = getUiErrorPresentation(err, locale)
464-
const cta = errorCtaText(locale, ui.ctaKey)
444+
const cta = getUiErrorCtaText(locale, ui.ctaKey)
465445
addLog(`${locale === "zh-CN" ? "加载分镜失败" : "Failed to load shots"} · ${ui.message}${cta ? ` · ${cta}` : ""}`, err, "error")
466446
})(),
467447
}
@@ -489,7 +469,7 @@ export function ScriptDesk() {
489469
return true
490470
} catch (err) {
491471
const ui = getUiErrorPresentation(err, locale)
492-
const cta = errorCtaText(locale, ui.ctaKey)
472+
const cta = getUiErrorCtaText(locale, ui.ctaKey)
493473
addLog(`${locale === "zh-CN" ? `生成失败 ${shotId}` : `Generate failed ${shotId}`} · ${ui.message}${cta ? ` · ${cta}` : ""}`, err, "error")
494474
return false
495475
}

apps/web-v2/src/pages/video-desk/VideoDesk.tsx

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313

1414
import { Button } from "@/components/ui/Button"
1515
import { Card, CardContent } from "@/components/ui/Card"
16-
import { getUiErrorPresentation } from "@/lib/error-presenter"
16+
import { getUiErrorCtaText, getUiErrorPresentation } from "@/lib/error-presenter"
1717
import { cn } from "@/lib/utils"
1818
import { useGlobalApiConnectionState, useGlobalDirectorModeState, useGlobalLocaleState } from "@/lib/workbench-ui"
1919
import { CinematicWorkspaceShell } from "@/components/workbench/CinematicWorkspaceShell"
@@ -146,24 +146,6 @@ interface CandidateTileProps {
146146

147147
function CandidateTile({ shotId, candidate, locale, onSelect, onReplace, disabled, compact }: CandidateTileProps) {
148148
const txt = t(locale)
149-
const errorCtaLabel = (ctaKey?: string) => {
150-
if (!ctaKey) return ""
151-
const mapEn: Record<string, string> = {
152-
check_provider_quota: "check BYOK provider quota",
153-
check_api_base: "check API connection",
154-
refresh_shots: "refresh shots",
155-
check_fields: "check fields",
156-
retry: "retry",
157-
}
158-
const mapZh: Record<string, string> = {
159-
check_provider_quota: "检查 BYOK Provider 额度",
160-
check_api_base: "检查 API 连接",
161-
refresh_shots: "刷新分镜列表",
162-
check_fields: "检查输入字段",
163-
retry: "重试",
164-
}
165-
return (locale === "zh-CN" ? mapZh : mapEn)[ctaKey] || ""
166-
}
167149
const score = candidate.score?.total ?? 0
168150
const isSelected = candidate.selected || (candidate.id && candidate.id === candidate.candidateId)
169151
const previewSrc = candidate.thumbnailUrl
@@ -343,7 +325,7 @@ export function VideoDesk() {
343325
addLog(locale === "zh-CN" ? `已加载 ${data.shots?.length || 0} 个分镜` : `Loaded ${data.shots?.length || 0} shots`, undefined, "success")
344326
} catch (error) {
345327
const ui = getUiErrorPresentation(error, locale)
346-
const cta = errorCtaLabel(ui.ctaKey)
328+
const cta = getUiErrorCtaText(locale, ui.ctaKey)
347329
addLog(`${locale === "zh-CN" ? "加载分镜失败" : "Load shots failed"} · ${ui.message}${cta ? ` · ${cta}` : ""}`, error, "error")
348330
} finally {
349331
setIsLoading(false)
@@ -373,7 +355,7 @@ export function VideoDesk() {
373355
await loadShots()
374356
} catch (error) {
375357
const ui = getUiErrorPresentation(error, locale)
376-
const cta = errorCtaLabel(ui.ctaKey)
358+
const cta = getUiErrorCtaText(locale, ui.ctaKey)
377359
addLog(`${locale === "zh-CN" ? `选择候选失败 (${shotId})` : `Select candidate failed (${shotId})`} · ${ui.message}${cta ? ` · ${cta}` : ""}`, error, "error")
378360
} finally {
379361
setActiveShotId(null)
@@ -395,7 +377,7 @@ export function VideoDesk() {
395377
await loadShots()
396378
} catch (error) {
397379
const ui = getUiErrorPresentation(error, locale)
398-
const cta = errorCtaLabel(ui.ctaKey)
380+
const cta = getUiErrorCtaText(locale, ui.ctaKey)
399381
addLog(`${locale === "zh-CN" ? `低分替换失败 (${shotId})` : `Replace low-score failed (${shotId})`} · ${ui.message}${cta ? ` · ${cta}` : ""}`, error, "error")
400382
} finally {
401383
setActiveShotId(null)

docs/providers/byok-setup.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,23 @@ Do **not** expose provider keys to frontend env vars.
6161
- provider account credits are insufficient
6262
- this is a provider account issue, not a Cineweave transport issue
6363

64+
### Kling `1102` (`Account balance not enough`)
65+
66+
This is a common real-provider BYOK failure when Cineweave has already reached Kling successfully.
67+
68+
What it means:
69+
70+
- AK/SK is valid enough for request authentication
71+
- Cineweave request/signature path is working
72+
- Kling rejected the task because provider credits/balance are insufficient
73+
74+
Recommended actions:
75+
76+
1. Recharge / top up the Kling account used by your AK/SK
77+
2. Confirm the key belongs to the expected account/workspace
78+
3. Retry the generation request from `Script Desk` or `Video Desk`
79+
80+
How Cineweave surfaces it:
81+
82+
- UI error category: `provider_quota_error`
83+
- CTA hint: check BYOK provider quota / balance

0 commit comments

Comments
 (0)