diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js index 85b13537..6a34b260 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.js +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js @@ -96,6 +96,7 @@ export default function ProviderDetailPage() { const [providerStickyLimit, setProviderStickyLimit] = useState(""); const [thinkingMode, setThinkingMode] = useState("auto"); const [providerTimeout, setProviderTimeout] = useState(""); + const [streamErrorPatternsText, setStreamErrorPatternsText] = useState(""); const [autoPing, setAutoPing] = useState({ enabled: false, connections: {} }); const [suggestedModels, setSuggestedModels] = useState([]); const [liveModels, setLiveModels] = useState([]); @@ -444,6 +445,12 @@ export default function ProviderDetailPage() { setProviderTimeout( timeoutCfg.timeoutMs != null ? String(timeoutCfg.timeoutMs) : "", ); + // Load per-provider stream error patterns (one per line) + setStreamErrorPatternsText( + ((settingsData.streamErrorPatterns || {})[providerId] || []).join( + "\n", + ), + ); const autoPingSettingsKey = AUTO_PING_SETTINGS_KEYS[providerId]; const apCfg = autoPingSettingsKey ? settingsData[autoPingSettingsKey] || {} @@ -595,6 +602,30 @@ export default function ProviderDetailPage() { } }; + const saveStreamErrorPatterns = async () => { + try { + const settingsRes = await fetch("/api/settings", { + cache: "no-store", + }); + const settingsData = settingsRes.ok ? await settingsRes.json() : {}; + const current = settingsData.streamErrorPatterns || {}; + const updated = { ...current }; + const lines = streamErrorPatternsText + .split("\n") + .map((s) => s.trim()) + .filter(Boolean); + if (lines.length > 0) updated[providerId] = lines; + else delete updated[providerId]; + await fetch("/api/settings", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ streamErrorPatterns: updated }), + }); + } catch (error) { + console.log("Error saving stream error patterns:", error); + } + }; + const handleTimeoutChange = (value) => { const cleaned = value.replace(/[^0-9]/g, ""); setProviderTimeout(cleaned); @@ -2459,6 +2490,38 @@ export default function ProviderDetailPage() { )} + {/* Stream Error Patterns */} + +
+

Stream Error Patterns

+

+ Treat HTTP-200 streams whose first bytes match one of these + patterns as failed requests (enables fallback + FAILED logs). One + pattern per line: plain text (case-insensitive substring) or{" "} + + /regex/flags + + . Only the first few KB of the stream are scanned. +

+
+