Download packages/api/src/lib/generation-outcome.ts from Atmint/cpns: direct link, hf CLI and curl.
- Browser
- Download file 4.86 kB
-
https://huggingface.co/spaces/Atmint/cpns/resolve/main/packages/api/src/lib/generation-outcome.ts
- Command line
-
hf download hf://spaces/Atmint/cpns/packages/api/src/lib/generation-outcome.ts
-
curl -L -o generation-outcome.ts https://huggingface.co/spaces/Atmint/cpns/resolve/main/packages/api/src/lib/generation-outcome.ts
4.86 kB
| export function isNonRetryableProviderError(error: unknown): boolean { | |
| const message = error instanceof Error ? error.message : String(error); | |
| const lower = message.toLowerCase(); | |
| if (/openai-compatible api error 5\d\d/i.test(message)) return true; | |
| if (/api error 5\d\d/i.test(message)) return true; | |
| if (lower.includes("midstreamfallbackerror")) return true; | |
| if (lower.includes("apiconnectionerror")) return true; | |
| if (lower.includes("upstream returned an html error page")) return true; | |
| if (lower.includes("provider returned html")) return true; | |
| if (/\berror 502\b/.test(lower) || /\berror 503\b/.test(lower) || /\berror 504\b/.test(lower)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function truncate(text: string, maxLen: number): string { | |
| return text.length <= maxLen ? text : `${text.slice(0, maxLen)}…`; | |
| } | |
| function sectionHint(failedSections: string[]): string { | |
| return failedSections.length > 0 ? ` Section gagal: ${failedSections.join(", ")}.` : ""; | |
| } | |
| /** Turn raw pipeline/client errors into user-facing messages that name the real cause. */ | |
| export function formatGenerationErrorMessage(input: { | |
| cause?: string; | |
| failedSections?: string[]; | |
| }): string { | |
| const failedSections = [...new Set(input.failedSections ?? [])]; | |
| const hint = sectionHint(failedSections); | |
| const cause = input.cause?.trim(); | |
| if (!cause) { | |
| return `Tidak ada soal yang berhasil dibuat.${hint}`; | |
| } | |
| const lower = cause.toLowerCase(); | |
| if ( | |
| lower.includes("redacted_thinking") || | |
| lower.includes("<thinking>") || | |
| /<\/?think>/.test(lower) | |
| ) { | |
| return `Model reasoning tidak kompatibel dengan parser Pram.${hint} Coba non-reasoning model, atau update server ke versi terbaru.`; | |
| } | |
| if ( | |
| lower.includes("json response was truncated") || | |
| lower.includes("unterminated string") | |
| ) { | |
| return `Respons model terpotong sebelum JSON selesai (biasanya max tokens terlalu kecil).${hint} Naikkan Max Tokens di Settings atau kurangi jumlah soal/format.`; | |
| } | |
| if (lower.includes("failed to parse ai response") || lower.includes("invalid json response")) { | |
| return `Model merespons, tapi output tidak valid JSON.${hint} ${truncate(cause, 280)}`; | |
| } | |
| if (lower.includes("empty response from ai") || lower.includes("empty response body")) { | |
| return `Model tidak mengembalikan konten.${hint} Coba model lain atau kurangi jumlah soal.`; | |
| } | |
| if (lower.includes("no valid questions")) { | |
| return `Model menghasilkan soal, tapi semuanya gagal validasi.${hint} ${truncate(cause, 280)}`; | |
| } | |
| if ( | |
| lower.includes("upstream returned an html error page") || | |
| lower.includes("provider returned html") || | |
| lower.includes("openai-compatible api error") || | |
| /api error 5\d\d/i.test(cause) || | |
| lower.includes("apiconnectionerror") || | |
| lower.includes("midstreamfallbackerror") | |
| ) { | |
| return `Gagal menghubungi API provider.${hint} ${truncate(cause, 280)}`; | |
| } | |
| if (lower.includes("metadata/private network") || lower.includes("invalid base url")) { | |
| return `Base URL provider tidak valid atau tidak dapat diakses dari server.${hint}`; | |
| } | |
| return `Generasi gagal.${hint} ${truncate(cause, 280)}`; | |
| } | |
| export function resolveGenerationJobOutcome(input: { | |
| requestedCount: number; | |
| generatedCount: number; | |
| failedSections?: string[]; | |
| cause?: string; | |
| }): { | |
| status: "completed" | "completed_partial" | "failed"; | |
| progressMessage: string; | |
| errorMessage: string | null; | |
| } { | |
| const failedSections = [...new Set(input.failedSections ?? [])]; | |
| if (input.generatedCount <= 0) { | |
| return { | |
| status: "failed", | |
| progressMessage: "Generation failed", | |
| errorMessage: formatGenerationErrorMessage({ | |
| cause: input.cause, | |
| failedSections, | |
| }), | |
| }; | |
| } | |
| if (input.generatedCount < input.requestedCount) { | |
| const sectionHintText = | |
| failedSections.length > 0 ? ` Section gagal: ${failedSections.join(", ")}.` : ""; | |
| return { | |
| status: "completed_partial", | |
| progressMessage: `Selesai sebagian: ${input.generatedCount}/${input.requestedCount} soal`, | |
| errorMessage: `Hanya ${input.generatedCount} dari ${input.requestedCount} soal berhasil dibuat.${sectionHintText}`, | |
| }; | |
| } | |
| return { | |
| status: "completed", | |
| progressMessage: "Completed", | |
| errorMessage: null, | |
| }; | |
| } | |
| export function sectionsWithNoQuestions( | |
| sectionSplits: Array<{ section: string; count: number }>, | |
| questions: Array<{ section: string }>, | |
| ): string[] { | |
| return sectionSplits | |
| .filter((split) => !questions.some((q) => q.section === split.section)) | |
| .map((split) => split.section); | |
| } | |
| export function collectShardFailureCause( | |
| failures: Iterable<{ error: string } | undefined>, | |
| ): string | undefined { | |
| for (const failure of failures) { | |
| const message = failure?.error?.trim(); | |
| if (message) return message; | |
| } | |
| return undefined; | |
| } | |