Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion open-sse/handlers/chatCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
let pxpipeSummary = null;
if (pxpipeEnabled) {
const pxpipeResult = await compressWithPxpipe(translatedBody, {
enabled: true, format: finalFormat, model: upstreamModel,
enabled: tokenSaverEnabled, format: finalFormat, model: upstreamModel,
minChars: pxpipeMinChars, timeoutMs: pxpipeTimeoutMs, transform: pxpipeTransform,
});
pxpipeSummary = pxpipeResult.summary;
Expand Down
3 changes: 3 additions & 0 deletions src/dashboardGuard.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ const LOCAL_ONLY_PATHS = [
"/api/headroom/start",
"/api/headroom/stop",
"/api/headroom/proxy",
"/api/headroom/extras",
"/api/headroom/restart",
"/api/pxpipe",
];

const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/dashboard-guard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,43 @@ describe("dashboard guard local-only access", () => {

expect(response).toBe(mocks.nextResponse);
});

it.each([
"/api/pxpipe/install",
"/api/pxpipe/status",
"/api/headroom/extras",
"/api/headroom/restart",
])("denies management route %s from remote host even when requireLogin=false", async (pathname) => {
mocks.getSettings.mockResolvedValue({ requireLogin: false });

const response = await proxy(request(pathname, { host: "router.example.com" }));

expect(response.status).toBe(403);
expect(response.body.error).toBe("Local only: CLI token required");
});

it.each(["/api/pxpipe/install", "/api/headroom/extras"])(
"allows management route %s on loopback when requireLogin=false",
async (pathname) => {
mocks.getSettings.mockResolvedValue({ requireLogin: false });

const response = await proxy(localRequest(pathname, {
host: "localhost:20128",
origin: "http://localhost:20128",
}));

expect(response).toBe(mocks.nextResponse);
}
);

it("allows pxpipe install from remote host with valid CLI token", async () => {
const response = await proxy(request("/api/pxpipe/install", {
host: "router.example.com",
"x-9r-cli-token": "cli-token",
}));

expect(response).toBe(mocks.nextResponse);
});
});

describe("dashboard guard helpers", () => {
Expand Down
111 changes: 90 additions & 21 deletions tests/unit/headroom-chat-core.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { FORMATS } from "../../open-sse/translator/formats.js";

const { executeMock } = vi.hoisted(() => ({
executeMock: vi.fn(),
Expand Down Expand Up @@ -251,47 +252,115 @@ describe("handleChatCore Headroom diagnostics", () => {
);
});

it("bypasses token savers when requested by the client", async () => {
it.each(["off", "OFF"])("pxpipe honors token-saver header %s on claude body above threshold", async (headerValue) => {
const log = { debug: vi.fn(), info: vi.fn(), warn: vi.fn() };
const pxpipeTransform = vi.fn();
const messages = [{ role: "user", content: "Write polished prose." }];

global.fetch = vi.fn(async (url) => {
throw new Error(`unexpected fetch: ${url}`);
});
const onPxpipeEvent = vi.fn();
const transformedBody = { model: "claude-3-5-sonnet", stream: false, max_tokens: 100, system: "base", messages: [{ role: "user", content: "PXPIPE_SENTINEL" }] };
const pxpipeTransform = vi.fn(async () => ({
applied: true,
reason: "applied",
body: new TextEncoder().encode(JSON.stringify(transformedBody)),
info: { compressedChars: 25000, imageCount: 1, imageBytes: 1000, imagePixels: 750000 },
cache: { ownsCacheControl: true },
}));

await handleChatCore({
body: { model: "gpt-4o", stream: false, messages },
modelInfo: { provider: "openai", model: "gpt-4o" },
body: {
model: "claude-3-5-sonnet",
stream: false,
max_tokens: 100,
system: "base",
messages: [{ role: "user", content: [{ type: "text", text: "x".repeat(30000) }] }],
},
sourceFormatOverride: FORMATS.CLAUDE,
modelInfo: { provider: "anthropic", model: "claude-3-5-sonnet" },
credentials: { apiKey: "test-key", providerSpecificData: {} },
log,
connectionId: "test-conn",
headroomEnabled: true,
headroomUrl: "http://localhost:8787",
headroomCompressUserMessages: true,
rtkEnabled: true,
cavemanEnabled: true,
cavemanLevel: "full",
ponytailEnabled: true,
ponytailLevel: "full",
headroomEnabled: false,
rtkEnabled: false,
cavemanEnabled: false,
ponytailEnabled: false,
pxpipeEnabled: true,
pxpipeMinChars: 1000,
pxpipeTransform,
onPxpipeEvent,
clientRawRequest: {
endpoint: "/v1/chat/completions",
endpoint: "/v1/messages",
body: {},
headers: {
accept: "application/json",
"x-9router-token-saver": "off",
"user-agent": "claude-code",
"x-9router-token-saver": headerValue,
},
},
});

expect(global.fetch).not.toHaveBeenCalled();
expect(pxpipeTransform).not.toHaveBeenCalled();
expect(onPxpipeEvent).toHaveBeenCalledWith(expect.objectContaining({
applied: false,
reason: "disabled",
}));
expect(executeMock).toHaveBeenCalledWith(expect.objectContaining({
body: expect.objectContaining({
messages: [{ role: "user", content: "Write polished prose." }],
system: "base",
messages: [expect.objectContaining({
content: [expect.objectContaining({ text: expect.stringContaining("xxxxx") })],
})],
}),
}));
});

it("pxpipe applies transform when no opt-out header is present", async () => {
const log = { debug: vi.fn(), info: vi.fn(), warn: vi.fn() };
const onPxpipeEvent = vi.fn();
const transformedBody = { model: "claude-3-5-sonnet", stream: false, max_tokens: 100, system: "base", messages: [{ role: "user", content: [{ type: "text", text: "PXPIPE_SENTINEL" }] }] };
const pxpipeTransform = vi.fn(async () => ({
applied: true,
reason: "applied",
body: new TextEncoder().encode(JSON.stringify(transformedBody)),
info: { compressedChars: 25000, imageCount: 1, imageBytes: 1000, imagePixels: 750000 },
cache: { ownsCacheControl: true },
}));

await handleChatCore({
body: {
model: "claude-3-5-sonnet",
stream: false,
max_tokens: 100,
system: "base",
messages: [{ role: "user", content: [{ type: "text", text: "x".repeat(30000) }] }],
},
sourceFormatOverride: FORMATS.CLAUDE,
modelInfo: { provider: "anthropic", model: "claude-3-5-sonnet" },
credentials: { apiKey: "test-key", providerSpecificData: {} },
log,
connectionId: "test-conn",
headroomEnabled: false,
rtkEnabled: false,
cavemanEnabled: false,
ponytailEnabled: false,
pxpipeEnabled: true,
pxpipeMinChars: 1000,
pxpipeTransform,
onPxpipeEvent,
clientRawRequest: {
endpoint: "/v1/messages",
body: {},
headers: {
accept: "application/json",
"user-agent": "claude-code",
},
},
});

expect(pxpipeTransform).toHaveBeenCalledTimes(1);
expect(onPxpipeEvent).toHaveBeenCalledWith(expect.objectContaining({
applied: true,
reason: "applied",
}));
const sentBody = executeMock.mock.calls[0][0].body;
expect(JSON.stringify(sentBody)).toContain("PXPIPE_SENTINEL");
expect(JSON.stringify(sentBody)).not.toContain("x".repeat(1000));
});
});