feat: pre-request token validation, mid-stream error handling, and usage stats improvements

- Qoder: handle mid-stream errors by returning proper error Response instead of embedding in stream
- Qoder: add refreshCredentials() to validate token via quota endpoint before requests
- chatCore: validate and refresh provider tokens before sending chat requests
- chatCore: bail early with 401 on unrecoverable token refresh errors
- Usage stats: track apiKey, comboName, fallbackHistory in request details
- Dashboard: improve Combos, Endpoint, Provider, Usage, and RequestDetails pages
- API keys route: upsert logic with provider_type support
- DB repos: usageRepo query improvements, requestDetailsRepo pagination, apiKeysRepo updates
This commit is contained in:
2026-06-29 10:08:07 +07:00
parent 1fe8115dad
commit 2305e26e25
27 changed files with 1433 additions and 459 deletions

View File

@@ -49,7 +49,9 @@ export function createSSEStream(options = {}) {
connectionId = null,
body = null,
onStreamComplete = null,
apiKey = null
apiKey = null,
endpoint = null,
comboName = null
} = options;
let buffer = "";
@@ -335,11 +337,11 @@ export function createSSEStream(options = {}) {
}
if (hasValidUsage(usage)) {
saveUsageStats({ provider, model, tokens: usage, connectionId, apiKey });
saveUsageStats({ provider, model, tokens: usage, connectionId, apiKey, endpoint, comboName });
} else {
appendRequestLog({ model, provider, connectionId, tokens: null, status: "200 OK" }).catch(() => { });
}
// IMPORTANT: In passthrough mode we still must terminate the SSE stream.
// Some clients (e.g. OpenClaw) expect the OpenAI-style sentinel:
// data: [DONE]\n\n
@@ -418,7 +420,7 @@ export function createSSEStream(options = {}) {
}
if (hasValidUsage(state?.usage)) {
saveUsageStats({ provider: state.provider || targetFormat, model, tokens: state.usage, connectionId, apiKey });
saveUsageStats({ provider: state.provider || targetFormat, model, tokens: state.usage, connectionId, apiKey, endpoint, comboName });
} else {
appendRequestLog({ model, provider, connectionId, tokens: null, status: "200 OK" }).catch(() => { });
}
@@ -436,7 +438,7 @@ export function createSSEStream(options = {}) {
});
}
export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, provider = null, reqLogger = null, toolNameMap = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null) {
export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, provider = null, reqLogger = null, toolNameMap = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null, endpoint = null, comboName = null) {
return createSSEStream({
mode: STREAM_MODE.TRANSLATE,
targetFormat,
@@ -448,11 +450,13 @@ export function createSSETransformStreamWithLogger(targetFormat, sourceFormat, p
connectionId,
body,
onStreamComplete,
apiKey
apiKey,
endpoint,
comboName
});
}
export function createPassthroughStreamWithLogger(provider = null, reqLogger = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null) {
export function createPassthroughStreamWithLogger(provider = null, reqLogger = null, model = null, connectionId = null, body = null, onStreamComplete = null, apiKey = null, endpoint = null, comboName = null) {
return createSSEStream({
mode: STREAM_MODE.PASSTHROUGH,
provider,
@@ -461,6 +465,8 @@ export function createPassthroughStreamWithLogger(provider = null, reqLogger = n
connectionId,
body,
onStreamComplete,
apiKey
apiKey,
endpoint,
comboName
});
}