Improve AI model compatibility warnings and error handling

Add detection for OpenAI models that only support v1/responses and are not compatible with chat completions, providing user-friendly warnings during model selection and clearer error messages upon connection testing or AI analysis execution.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0d01f99a-ea6a-447d-82fd-311715434a39
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ac489071-6c6a-4584-9740-76bf6ca16040
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/e32d2b99-1721-47dd-833c-98b372f48008/0d01f99a-ea6a-447d-82fd-311715434a39/upEITG1
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
Replit Agent 2026-06-16 21:35:24 +00:00
parent 1451ce790f
commit 29853219bc
6 changed files with 32 additions and 1 deletions

View file

@ -137,6 +137,12 @@ async function callOpenAiCompatible(
}); });
if (!res.ok) { if (!res.ok) {
const body = await res.text(); const body = await res.text();
if (body.includes("v1/responses")) {
throw new Error(
`Das Modell "${provider.model}" unterstützt nur /v1/responses, nicht /v1/chat/completions. ` +
`Bitte wählen Sie ein Chat-kompatibles Modell (z.\u202fB. gpt-4o, gpt-4-turbo, gpt-3.5-turbo).`,
);
}
throw new Error( throw new Error(
`HTTP ${res.status}: ${redactSecrets(body.slice(0, 300), provider.apiToken)}`, `HTTP ${res.status}: ${redactSecrets(body.slice(0, 300), provider.apiToken)}`,
); );

View file

@ -16,6 +16,8 @@ export default {
noneFoundTried: "Keine Modelle gefunden bitte das Modell manuell eingeben.", noneFoundTried: "Keine Modelle gefunden bitte das Modell manuell eingeben.",
noneFoundHint: noneFoundHint:
"Testen Sie die Verbindung, um verfügbare Modelle automatisch zu laden, oder geben Sie das Modell manuell ein.", "Testen Sie die Verbindung, um verfügbare Modelle automatisch zu laden, oder geben Sie das Modell manuell ein.",
responsesOnlyWarning:
"Dieses Modell unterstützt nur /v1/responses, nicht /v1/chat/completions. Die KI-Analyse wird fehlschlagen. Bitte wählen Sie ein Chat-kompatibles Modell (z.\u202fB. gpt-4o, gpt-4-turbo).",
}, },
providers: { providers: {
heading: "KI-Provider", heading: "KI-Provider",

View file

@ -16,6 +16,8 @@ export default {
noneFoundTried: "No models found please enter the model manually.", noneFoundTried: "No models found please enter the model manually.",
noneFoundHint: noneFoundHint:
"Test the connection to load available models automatically, or enter the model manually.", "Test the connection to load available models automatically, or enter the model manually.",
responsesOnlyWarning:
"This model only supports /v1/responses, not /v1/chat/completions. AI analysis will fail. Please choose a chat-compatible model (e.g. gpt-4o, gpt-4-turbo).",
}, },
providers: { providers: {
heading: "AI providers", heading: "AI providers",

View file

@ -16,6 +16,8 @@ export default {
noneFoundTried: "No se encontraron modelos: introduzca el modelo manualmente.", noneFoundTried: "No se encontraron modelos: introduzca el modelo manualmente.",
noneFoundHint: noneFoundHint:
"Pruebe la conexión para cargar automáticamente los modelos disponibles, o introduzca el modelo manualmente.", "Pruebe la conexión para cargar automáticamente los modelos disponibles, o introduzca el modelo manualmente.",
responsesOnlyWarning:
"Este modelo solo admite /v1/responses, no /v1/chat/completions. El análisis de IA fallará. Seleccione un modelo compatible con chat (p.\u202fej. gpt-4o, gpt-4-turbo).",
}, },
providers: { providers: {
heading: "Proveedores de IA", heading: "Proveedores de IA",

View file

@ -42,14 +42,29 @@ const PROVIDER_PRESETS: Partial<Record<AiProviderApiType, ProviderPreset[]>> = {
], ],
}; };
function ModelField({ models, loading, tried, value, onChange }: { const RESPONSES_ONLY_RE = /^o\d/i;
function isResponsesOnlyModel(model: string, apiType: string): boolean {
return apiType === "openai" && RESPONSES_ONLY_RE.test(model.trim());
}
function ModelField({ models, loading, tried, value, onChange, apiType }: {
models: string[]; models: string[];
loading: boolean; loading: boolean;
tried: boolean; tried: boolean;
value: string; value: string;
onChange: (v: string) => void; onChange: (v: string) => void;
apiType: string;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const warnResponsesOnly = value && isResponsesOnlyModel(value, apiType);
const warning = warnResponsesOnly ? (
<p className="text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded-md px-2 py-1.5">
{t("admin.modelField.responsesOnlyWarning")}
</p>
) : null;
if (loading) { if (loading) {
return ( return (
<div className="grid gap-2"> <div className="grid gap-2">
@ -71,6 +86,7 @@ function ModelField({ models, loading, tried, value, onChange }: {
</SelectContent> </SelectContent>
</Select> </Select>
<p className="text-xs text-muted-foreground">{t("admin.modelField.found", { count: models.length })}</p> <p className="text-xs text-muted-foreground">{t("admin.modelField.found", { count: models.length })}</p>
{warning}
</div> </div>
); );
} }
@ -83,6 +99,7 @@ function ModelField({ models, loading, tried, value, onChange }: {
? t("admin.modelField.noneFoundTried") ? t("admin.modelField.noneFoundTried")
: t("admin.modelField.noneFoundHint")} : t("admin.modelField.noneFoundHint")}
</p> </p>
{warning}
</div> </div>
); );
} }
@ -368,6 +385,7 @@ function ProviderTab() {
models={addModels} models={addModels}
loading={addModelsLoading} loading={addModelsLoading}
tried={addModelsTried} tried={addModelsTried}
apiType={addForm.apiType}
value={addForm.model} value={addForm.model}
onChange={(v) => setAddForm(f => ({ ...f, model: v }))} onChange={(v) => setAddForm(f => ({ ...f, model: v }))}
/> />
@ -498,6 +516,7 @@ function ProviderTab() {
models={editModels} models={editModels}
loading={editModelsLoading} loading={editModelsLoading}
tried={editModelsTried} tried={editModelsTried}
apiType={editForm.apiType}
value={editForm.model} value={editForm.model}
onChange={(v) => setEditForm(f => ({ ...f, model: v }))} onChange={(v) => setEditForm(f => ({ ...f, model: v }))}
/> />

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB