mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 14:22:09 +03:00
* fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API) Cline's API (api.cline.bot) only implements streaming (streamText). A non-streaming request returns HTTP 500 "generateText is not implemented" (Claude models) or HTTP 502 "empty response" (others). Live-verified on the VPS: stream:true → works (STREAM_OK), stream:false → fails. This is why testing a Cline model in the dashboard (the test button sends stream:false) failed. Fix (reuses the existing isClaudeCodeCompatible mechanism, no new handler): - Flag `cline` and `clinepass` registry entries with `forceStream: true`. - In chatCore, OR `providerRequiresStreaming` into `upstreamStream` (line 1591) so the upstream request always streams for these providers, while the client's original `stream` intent still drives the response format. The existing non-streaming branch (parseNonStreamingResponseBody) already accumulates the upstream SSE and converts it back to JSON for stream:false clients — the same path Claude-Code-compatible providers already use. Tests (Rule #18): tests/unit/cline-force-stream.test.ts pins the registry flags + resolveStreamFlag forcing behavior. Live VPS before/after recorded on the PR. * fix(sse): cline forceStream must stream upstream only, keep client JSON The #2081 wiring fed providerRequiresStreaming into resolveStreamFlag, forcing the client-facing stream flag to true for forceStream providers. That skips the if(!stream) branch that drains a forced upstream SSE and converts it back to JSON, so a stream:false caller (model-test button, plain JSON API) got STREAM_EARLY_EOF instead of a JSON body. Keep providerRequiresStreaming only on upstreamStream (force upstream to stream); leave the client-facing stream as the client sent it, so readNonStreamingResponseBody accumulates the SSE into JSON. The promised handleForcedSSEToJson (#2081 comment) was never implemented — this uses the existing non-streaming SSE-buffering path (same as isClaudeCodeCompatible). Live-verified on VPS: cline stream:true worked, stream:false failed.
44 lines
2.4 KiB
TypeScript
44 lines
2.4 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { REGISTRY } from "@omniroute/open-sse/config/providers/index.ts";
|
|
import { resolveStreamFlag } from "@omniroute/open-sse/utils/aiSdkCompat.ts";
|
|
|
|
// Cline / ClinePass only implement upstream streaming — a non-streaming request
|
|
// returns "generateText is not implemented" / an empty body. They carry
|
|
// `forceStream: true` so chatCore forces the UPSTREAM request to stream
|
|
// (`upstreamStream = stream || isClaudeCodeCompatible || providerRequiresStreaming`)
|
|
// even when the client wants JSON. The client-facing `stream` flag stays as the
|
|
// client sent it, so the `if (!stream)` branch drains the forced upstream SSE and
|
|
// converts it back to JSON via readNonStreamingResponseBody. Regression guard for
|
|
// the "cline model test → generateText is not implemented / STREAM_EARLY_EOF" bug
|
|
// (live-verified on the VPS: stream:true works, stream:false failed). (#6126)
|
|
|
|
test("cline provider is flagged forceStream (streaming-only upstream)", () => {
|
|
assert.equal(REGISTRY.cline?.forceStream, true);
|
|
});
|
|
|
|
test("clinepass provider is flagged forceStream (streaming-only upstream)", () => {
|
|
assert.equal(REGISTRY.clinepass?.forceStream, true);
|
|
});
|
|
|
|
test("upstreamStream is forced true for a forceStream provider even when the client sent stream:false", () => {
|
|
// Mirror the chatCore wiring: providerRequiresStreaming derives from the
|
|
// registry flag, and upstreamStream ORs it in so the upstream always streams.
|
|
const providerRequiresStreaming = REGISTRY.cline?.forceStream === true;
|
|
const isClaudeCodeCompatible = false;
|
|
const clientStream = false; // client asked for JSON
|
|
const upstreamStream = clientStream || isClaudeCodeCompatible || providerRequiresStreaming;
|
|
assert.equal(upstreamStream, true);
|
|
});
|
|
|
|
test("client-facing stream stays false for a stream:false JSON caller (so SSE→JSON conversion runs)", () => {
|
|
// chatCore MUST NOT pass providerRequiresStreaming into resolveStreamFlag:
|
|
// a stream:false client keeps stream=false so the `if (!stream)` branch drains
|
|
// the forced upstream SSE and returns JSON. Forcing stream=true here would skip
|
|
// that conversion and yield STREAM_EARLY_EOF for JSON callers.
|
|
assert.equal(resolveStreamFlag(false, "application/json", "openai"), false);
|
|
// A stream:true client still streams end-to-end.
|
|
assert.equal(resolveStreamFlag(true, "application/json", "openai"), true);
|
|
});
|