merge: pull mhiqrambg/9router-mibp-version into master
Merge the MIBP fork (v1.0.14, synced to decolua v0.5.81) into our master
(0.5.86) at merge-base a8c9d380. Keep HEAD's infra policy (untracked
lockfile, mirror-configurable Dockerfile, decolua GHCR/DockerHub, README)
while absorbing the fork's engine features:
- feat(providers): freebuff provider + executor + OAuth + usage tracking
- feat(providers): cline free-tier models, Freebuff catalog sync
- feat(proxy-pools): pool egress geo probe, proxy-pool fitness + retry
- fix(usage): hide noAuth providers (devin-cli, mimo-free) from usage list
- test(harness): DATA_DIR isolation so tests never write the real DB
- fix(codebuddy-intl): probe token in connection test, OAuth by identity
- chore(guards): durable markers so fixes aren't silently dropped
Resolutions:
- registry/index.js regenerated deterministically (122 providers, alpha
order). trae/windsurf/devin-cli stay hidden per HEAD security posture
(no tool-calling / local-agent shell access) — not re-enabled.
- nonStreamingHandler: drop the generic unconditional unwrapDataEnvelope
call; envelope unwrap stays scoped to clineEnvelope-quirk providers
(unwrapClineEnvelope), fixing a latent mibp bug where non-opted-in
providers ({success,data} bodies) were stripped.
- Drop fork-local Docker lockfile policy (package-lock.json, AGENTS.md,
.npmrc verify scripts): this repo keeps package-lock untracked (nix
build deploy). .npmrc (audit=false/fund=false) kept.
- Keep gitbook-pages workflow enabled (ours); mibp disabled it.
- Restore 13 upstream tests mibp deleted (they cover features we keep).
Verified: 2841 tests, 2687 pass, fail set byte-identical to HEAD (zero
new regressions); providers/alias/oauth baselines regenerated to merged
code and all green.
This commit is contained in:
@@ -69,7 +69,14 @@ export async function parseUpstreamError(response, executor = null) {
|
||||
const parsed = executor.parseError(response, bodyText);
|
||||
if (parsed && typeof parsed === "object") {
|
||||
const msg = parsed.message || DEFAULT_ERROR_MESSAGES[response.status] || `Upstream error: ${response.status}`;
|
||||
return { statusCode: parsed.status || response.status, message: msg, resetsAtMs: parsed.resetsAtMs };
|
||||
return {
|
||||
statusCode: parsed.status || response.status,
|
||||
message: msg,
|
||||
resetsAtMs: parsed.resetsAtMs,
|
||||
// Executors declare IP/pool-scoped failures (e.g. per-IP rate limits)
|
||||
// here; chatCore completes poolId/scope and retries via another pool.
|
||||
poolScoped: parsed.poolScoped,
|
||||
};
|
||||
}
|
||||
} catch { /* fall through to default parsing */ }
|
||||
}
|
||||
|
||||
@@ -381,8 +381,6 @@ export function createSSEStream(options = {}) {
|
||||
},
|
||||
|
||||
flush(controller) {
|
||||
const evtSummary = Object.entries(eventTypeCounts).map(([k, v]) => `${k}=${v}`).join(",") || "none";
|
||||
dbg("SSE", `flush | provider=${provider} | model=${model} | recvLines=${sseLineCount} | emitted=${sseEmittedCount} | events=[${evtSummary}]`);
|
||||
trackPendingRequest(model, provider, connectionId, false);
|
||||
try {
|
||||
const remaining = decoder.decode();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Stream handler with disconnect detection - shared for all providers
|
||||
import { STREAM_STALL_TIMEOUT_MS } from "../config/runtimeConfig.js";
|
||||
import { dbg, isDebugEnabled } from "./debugLog.js";
|
||||
import { dbg } from "./debugLog.js";
|
||||
|
||||
// Get HH:MM:SS timestamp
|
||||
function getTimeString() {
|
||||
@@ -79,7 +79,7 @@ export function createStreamController({ onDisconnect, onError, log, provider, m
|
||||
return;
|
||||
}
|
||||
|
||||
logStream("✗", `ERROR: ${error.message}${error.stack ? `\n ${error.stack}` : ""}`, true);
|
||||
logStream("✗", `ERROR: ${error.message}`, true);
|
||||
onError?.(error);
|
||||
},
|
||||
|
||||
@@ -194,12 +194,7 @@ export function createDisconnectAwareStream(transformStream, streamController, o
|
||||
*/
|
||||
export function pipeWithDisconnect(providerResponse, transformStream, streamController, onAbortTerminal = null, stallTimeoutMs = STREAM_STALL_TIMEOUT_MS) {
|
||||
let stallTimer = null;
|
||||
let chunkCount = 0;
|
||||
let totalBytes = 0;
|
||||
let lastChunkAt = Date.now();
|
||||
let abortMessage = "upstream connection lost";
|
||||
const t0 = Date.now();
|
||||
const tag = "STREAM";
|
||||
const clearStall = () => {
|
||||
if (stallTimer) { clearTimeout(stallTimer); stallTimer = null; }
|
||||
};
|
||||
@@ -208,7 +203,6 @@ export function pipeWithDisconnect(providerResponse, transformStream, streamCont
|
||||
stallTimer = setTimeout(() => {
|
||||
stallTimer = null;
|
||||
abortMessage = "stream stall timeout";
|
||||
dbg(tag, `STALL TIMEOUT ${stallTimeoutMs}ms | chunks=${chunkCount} | bytes=${totalBytes} | sinceLast=${Date.now() - lastChunkAt}ms`);
|
||||
streamController.handleError?.(new Error("stream stall timeout"));
|
||||
streamController.abort?.();
|
||||
}, stallTimeoutMs);
|
||||
@@ -221,30 +215,20 @@ export function pipeWithDisconnect(providerResponse, transformStream, streamCont
|
||||
signal: streamController.signal,
|
||||
startTime: streamController.startTime,
|
||||
isConnected: () => streamController.isConnected(),
|
||||
handleComplete: () => { dbg(tag, `complete | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleComplete(); },
|
||||
handleError: (e) => { dbg(tag, `error: ${e?.message} | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleError(e); },
|
||||
handleDisconnect: (r) => { dbg(tag, `disconnect: ${r} | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); streamController.handleDisconnect(r); },
|
||||
handleComplete: () => { clearStall(); streamController.handleComplete(); },
|
||||
handleError: (e) => { clearStall(); streamController.handleError(e); },
|
||||
handleDisconnect: (r) => { clearStall(); streamController.handleDisconnect(r); },
|
||||
abort: () => { clearStall(); streamController.abort(); }
|
||||
};
|
||||
|
||||
armStall();
|
||||
dbg(tag, `pipe start | stallTimeout=${stallTimeoutMs}ms`);
|
||||
|
||||
const upstreamTap = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
chunkCount++;
|
||||
const sz = chunk?.byteLength || chunk?.length || 0;
|
||||
totalBytes += sz;
|
||||
const now = Date.now();
|
||||
const gap = now - lastChunkAt;
|
||||
lastChunkAt = now;
|
||||
if (isDebugEnabled && (chunkCount <= 5 || chunkCount % 20 === 0 || gap > 5000)) {
|
||||
dbg(tag, `chunk #${chunkCount} | size=${sz}B | gap=${gap}ms | total=${totalBytes}B`);
|
||||
}
|
||||
armStall();
|
||||
controller.enqueue(chunk);
|
||||
},
|
||||
flush() { dbg(tag, `upstream EOF | chunks=${chunkCount} | bytes=${totalBytes} | dur=${Date.now() - t0}ms`); clearStall(); }
|
||||
flush() { clearStall(); }
|
||||
});
|
||||
|
||||
const transformedBody = providerResponse.body
|
||||
|
||||
Reference in New Issue
Block a user