feat(api): classify openai-compatible provider failures (#708)

* feat(api): classify openai-compatible provider failures

* Update src/services/api/providerConfig.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/errors.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat(api): harden openai-compatible diagnostics and env fallback

* Update src/services/api/openaiShim.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/openaiShim.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/errors.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/errors.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix openaiShim duplicate requests and diagnostics

* remove unused url from http failure classifier

* dedupe env diagnostic warnings

* Remove hardcoded URLs from OpenAI error tests

Removed hardcoded URLs from network failure classification tests.

* Update providerConfig.envDiagnostics.test.ts

* fix(openai-shim): return successful responses and restore localhost classifier tests

* Update src/services/api/openaiShim.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/openaiShim.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/services/api/openaiShim.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
nehan
2026-04-17 14:01:40 +04:00
committed by GitHub
parent eed77e6579
commit 80a00acc2c
9 changed files with 1117 additions and 18 deletions

View File

@@ -8,6 +8,7 @@ import {
readCodexCredentials,
type CodexCredentialBlob,
} from '../../utils/codexCredentials.js'
import { logForDebugging } from '../../utils/debug.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import {
asTrimmedString,
@@ -19,6 +20,7 @@ export const DEFAULT_CODEX_BASE_URL = 'https://chatgpt.com/backend-api/codex'
export const DEFAULT_MISTRAL_BASE_URL = 'https://api.mistral.ai/v1'
/** Default GitHub Copilot API model when user selects copilot / github:copilot */
export const DEFAULT_GITHUB_MODELS_API_MODEL = 'gpt-4o'
const warnedUndefinedEnvNames = new Set<string>()
const CODEX_ALIAS_MODELS: Record<
string,
@@ -129,7 +131,33 @@ function isPrivateIpv6Address(hostname: string): boolean {
function asEnvUrl(value: string | undefined): string | undefined {
if (!value) return undefined
const trimmed = value.trim()
if (!trimmed || trimmed === 'undefined') return undefined
if (!trimmed) return undefined
if (trimmed === 'undefined') {
return undefined
}
return trimmed
}
function asNamedEnvUrl(
value: string | undefined,
envName: string,
): string | undefined {
if (!value) return undefined
const trimmed = value.trim()
if (!trimmed) return undefined
if (trimmed === 'undefined') {
if (!warnedUndefinedEnvNames.has(envName)) {
warnedUndefinedEnvNames.add(envName)
logForDebugging(
`[provider-config] Environment variable ${envName} is the literal string "undefined"; ignoring it.`,
{ level: 'warn' },
)
}
return undefined
}
return trimmed
}
@@ -362,14 +390,28 @@ export function resolveProviderRequest(options?: {
(isGithubMode ? 'github:copilot' : 'gpt-4o')
const descriptor = parseModelDescriptor(requestedModel)
const explicitBaseUrl = asEnvUrl(options?.baseUrl)
const normalizedMistralEnvBaseUrl = asNamedEnvUrl(
process.env.MISTRAL_BASE_URL,
'MISTRAL_BASE_URL',
)
const primaryEnvBaseUrl = isMistralMode
? normalizedMistralEnvBaseUrl
: asNamedEnvUrl(process.env.OPENAI_BASE_URL, 'OPENAI_BASE_URL')
const fallbackEnvBaseUrl = isMistralMode
? (primaryEnvBaseUrl === undefined
? asNamedEnvUrl(process.env.OPENAI_API_BASE, 'OPENAI_API_BASE') ?? DEFAULT_MISTRAL_BASE_URL
: undefined)
: (primaryEnvBaseUrl === undefined
? asNamedEnvUrl(process.env.OPENAI_API_BASE, 'OPENAI_API_BASE')
: undefined)
const envBaseUrlRaw =
explicitBaseUrl ??
asEnvUrl(
isMistralMode
? (process.env.MISTRAL_BASE_URL ?? DEFAULT_MISTRAL_BASE_URL)
: process.env.OPENAI_BASE_URL
) ??
asEnvUrl(process.env.OPENAI_API_BASE)
primaryEnvBaseUrl ??
fallbackEnvBaseUrl
const isCodexModelForGithub = isGithubMode && isCodexAlias(requestedModel)
const envBaseUrl =