mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 12:13:30 +00:00
* test: cover parent activity phase finalization * test(e2e): stabilize parent phase coverage * fix(agents): reanchor parent activity phase bounds * fix(agents): preserve delayed tools in activity phases * test(agents): keep phase slice bounds typed * fix(agents): preserve sparse activity phase bounds * test(e2e): read structured phase replies
195 lines
6 KiB
JavaScript
195 lines
6 KiB
JavaScript
/**
|
|
* OpenAI-compatible HTTP fixture for activity-label e2e tests.
|
|
*
|
|
* Activity labels are the one model call in a mock run that is NOT served by
|
|
* `e2e/setup/fake-model.js`: that hook swaps the GRAPH's model via
|
|
* `run.Graph.overrideTestModel(...)`, while the label call goes out through
|
|
* `run.generateActivityLabel()` against client options resolved from the
|
|
* endpoint config. Those options carry the template's `baseURL`
|
|
* (http://127.0.0.1:8889/v1), so a real server on that port serves label
|
|
* calls — and only label calls — with no production seam. Every mock endpoint
|
|
* sets `titleConvo: false`, so nothing else lands here.
|
|
*
|
|
* Beyond returning a label it RECORDS each request, which is what lets a spec
|
|
* assert the prompt contract (that the register and the tool OUTPUTS actually
|
|
* reached the model) rather than just that some text rendered.
|
|
*/
|
|
const http = require('http');
|
|
|
|
const PORT = Number(process.env.E2E_LABEL_PORT) || 8889;
|
|
const PHASE_PROMPT_MARKER = 'Summarize what this phase of an agent run accomplished';
|
|
|
|
/** Recorded label requests, newest last. */
|
|
const requests = [];
|
|
/** Test-controlled response behavior; `reset` restores these defaults. */
|
|
const DEFAULT_BEHAVIOR = {
|
|
mode: 'ok',
|
|
label: null,
|
|
phaseLabel: null,
|
|
labelsByPrompt: {},
|
|
delayMs: 0,
|
|
};
|
|
let behavior = { ...DEFAULT_BEHAVIOR };
|
|
let labelCount = 0;
|
|
|
|
function readBody(req) {
|
|
return new Promise((resolve) => {
|
|
let raw = '';
|
|
req.on('data', (chunk) => {
|
|
raw += chunk;
|
|
});
|
|
req.on('end', () => {
|
|
try {
|
|
resolve(raw ? JSON.parse(raw) : {});
|
|
} catch {
|
|
resolve({});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function sendJson(res, status, payload) {
|
|
const body = JSON.stringify(payload);
|
|
res.writeHead(status, {
|
|
'Content-Type': 'application/json',
|
|
'Content-Length': Buffer.byteLength(body),
|
|
});
|
|
res.end(body);
|
|
}
|
|
|
|
function messageText(content) {
|
|
if (typeof content === 'string') {
|
|
return content;
|
|
}
|
|
if (!Array.isArray(content)) {
|
|
return '';
|
|
}
|
|
return content.map((part) => (typeof part === 'string' ? part : (part?.text ?? ''))).join('\n');
|
|
}
|
|
|
|
/** Flattened prompt text so specs can assert on the register and tool outputs. */
|
|
function flattenPrompt(messages) {
|
|
return (messages ?? []).map((message) => messageText(message?.content)).join('\n\n');
|
|
}
|
|
|
|
/** Non-streaming OpenAI chat completion. */
|
|
function completionPayload(model, label) {
|
|
return {
|
|
id: `chatcmpl-e2e-${labelCount}`,
|
|
object: 'chat.completion',
|
|
created: 0,
|
|
model: model ?? 'mock-label-model',
|
|
choices: [
|
|
{
|
|
index: 0,
|
|
message: { role: 'assistant', content: label },
|
|
finish_reason: 'stop',
|
|
},
|
|
],
|
|
usage: { prompt_tokens: 42, completion_tokens: 7, total_tokens: 49 },
|
|
};
|
|
}
|
|
|
|
/**
|
|
* SSE form of the same completion. The label call inherits the endpoint's
|
|
* client options, which may leave streaming on, so both shapes are served.
|
|
*/
|
|
function sendStream(res, model, label) {
|
|
res.writeHead(200, {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
Connection: 'keep-alive',
|
|
});
|
|
const base = {
|
|
id: `chatcmpl-e2e-${labelCount}`,
|
|
object: 'chat.completion.chunk',
|
|
created: 0,
|
|
model,
|
|
};
|
|
res.write(
|
|
`data: ${JSON.stringify({ ...base, choices: [{ index: 0, delta: { role: 'assistant', content: label }, finish_reason: null }] })}\n\n`,
|
|
);
|
|
res.write(
|
|
`data: ${JSON.stringify({ ...base, choices: [{ index: 0, delta: {}, finish_reason: 'stop' }], usage: { prompt_tokens: 42, completion_tokens: 7, total_tokens: 49 } })}\n\n`,
|
|
);
|
|
res.write('data: [DONE]\n\n');
|
|
res.end();
|
|
}
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const url = new URL(req.url, `http://127.0.0.1:${PORT}`);
|
|
|
|
/** Playwright's webServer readiness probe. */
|
|
if (req.method === 'GET' && url.pathname === '/') {
|
|
sendJson(res, 200, { ok: true, service: 'fake-label-server' });
|
|
return;
|
|
}
|
|
|
|
if (req.method === 'GET' && url.pathname === '/__e2e/requests') {
|
|
sendJson(res, 200, { count: requests.length, requests });
|
|
return;
|
|
}
|
|
|
|
/** Specs reset between cases so counts and prompts stay per-test. */
|
|
if (req.method === 'POST' && url.pathname === '/__e2e/reset') {
|
|
requests.length = 0;
|
|
labelCount = 0;
|
|
behavior = { ...DEFAULT_BEHAVIOR };
|
|
sendJson(res, 200, { ok: true });
|
|
return;
|
|
}
|
|
|
|
if (req.method === 'POST' && url.pathname === '/__e2e/behavior') {
|
|
const body = await readBody(req);
|
|
behavior = { ...DEFAULT_BEHAVIOR, ...body };
|
|
sendJson(res, 200, { ok: true, behavior });
|
|
return;
|
|
}
|
|
|
|
if (req.method === 'POST' && url.pathname === '/v1/chat/completions') {
|
|
const body = await readBody(req);
|
|
labelCount += 1;
|
|
const prompt = flattenPrompt(body.messages);
|
|
requests.push({
|
|
model: body.model,
|
|
stream: body.stream === true,
|
|
prompt,
|
|
messages: body.messages ?? [],
|
|
});
|
|
|
|
if (behavior.delayMs > 0) {
|
|
await new Promise((resolve) => setTimeout(resolve, behavior.delayMs));
|
|
}
|
|
|
|
/** Generation failure: the run must finish cleanly with no header. */
|
|
if (behavior.mode === 'error') {
|
|
sendJson(res, 500, { error: { message: 'E2E forced label failure' } });
|
|
return;
|
|
}
|
|
|
|
/** Whitespace-only output must fill null, leaving the block unlabeled. */
|
|
const promptLabel = Object.entries(behavior.labelsByPrompt ?? {}).find(([needle]) =>
|
|
prompt.includes(needle),
|
|
)?.[1];
|
|
const isPhase = prompt.includes(PHASE_PROMPT_MARKER);
|
|
const label =
|
|
behavior.mode === 'blank'
|
|
? ' '
|
|
: ((isPhase ? behavior.phaseLabel : promptLabel) ??
|
|
behavior.label ??
|
|
`E2E activity label ${labelCount}`);
|
|
|
|
if (body.stream === true) {
|
|
sendStream(res, body.model, label);
|
|
return;
|
|
}
|
|
sendJson(res, 200, completionPayload(body.model, label));
|
|
return;
|
|
}
|
|
|
|
sendJson(res, 404, { error: { message: `Unhandled ${req.method} ${url.pathname}` } });
|
|
});
|
|
|
|
server.listen(PORT, '127.0.0.1', () => {
|
|
console.log(`[e2e] fake label server listening on http://127.0.0.1:${PORT}`);
|
|
});
|