187 lines
6.0 KiB
JavaScript
187 lines
6.0 KiB
JavaScript
/**
|
|
* orchestrator/presets.js — defaults for IPC integration tests (extend here as suites grow).
|
|
*
|
|
* Llama local runner (llama-basics.test.ts): OpenAI-compatible API at http://localhost:8888/v1,
|
|
* router `ollama` + `base_url` override, model `default` (server picks loaded GGUF).
|
|
*/
|
|
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { spawn } from 'node:child_process';
|
|
import { existsSync } from 'node:fs';
|
|
|
|
import { probeTcpPort } from './test-commons.js';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
export const platform = {
|
|
isWin: process.platform === 'win32',
|
|
};
|
|
|
|
/** kbot/cpp root (parent of orchestrator/). */
|
|
export const paths = {
|
|
orchestratorDir: __dirname,
|
|
cppRoot: resolve(__dirname, '..'),
|
|
/** Same as packages/kbot/cpp/scripts/run-7b.sh — llama-server on :8888 */
|
|
run7bScript: resolve(__dirname, '../scripts/run-7b.sh'),
|
|
};
|
|
|
|
/** Dist binary name for the current OS. */
|
|
export function exeName() {
|
|
return platform.isWin ? 'kbot.exe' : 'kbot';
|
|
}
|
|
|
|
/** Absolute path to kbot binary given orchestrator/ directory (where test-ipc.mjs lives). */
|
|
export function distExePath(orchestratorDir) {
|
|
return resolve(orchestratorDir, '..', 'dist', exeName());
|
|
}
|
|
|
|
/** UDS / TCP listen argument passed to `kbot worker --uds <arg>`. */
|
|
export const uds = {
|
|
tcpPort: 4001,
|
|
unixPath: '/tmp/kbot-test-ipc.sock',
|
|
/** Value for `--uds` on this OS (Windows: port string; Unix: socket path). */
|
|
workerArg() {
|
|
return platform.isWin ? String(this.tcpPort) : this.unixPath;
|
|
},
|
|
/** Options for `net.connect` to reach the worker. */
|
|
connectOpts(cppUdsArg) {
|
|
return platform.isWin
|
|
? { port: this.tcpPort, host: '127.0.0.1' }
|
|
: cppUdsArg;
|
|
},
|
|
};
|
|
|
|
/** Millisecond timeouts — tune per step in new tests. */
|
|
export const timeouts = {
|
|
ipcDefault: 5000,
|
|
kbotAi: 180_000,
|
|
/** Llama local arithmetic (same order of magnitude as kbot-ai). */
|
|
llamaKbotAi: 180_000,
|
|
/** Max wait for :8888 after spawning run-7b.sh (model load can be slow). */
|
|
llamaServerStart: Number(process.env.KBOT_LLAMA_START_TIMEOUT_MS || 600_000),
|
|
connectAttempts: 15,
|
|
connectRetryMs: 400,
|
|
postShutdownMs: 200,
|
|
};
|
|
|
|
export const router = {
|
|
default: 'openrouter',
|
|
fromEnv() {
|
|
return process.env.KBOT_ROUTER || this.default;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Local llama.cpp HTTP server — mirrors tests/unit/llama-basics.test.ts (LLAMA_OPTS).
|
|
* Uses router `ollama` so api_key resolves to dummy `ollama`; `base_url` points at :8888/v1.
|
|
*/
|
|
export const llama = {
|
|
get port() {
|
|
return Number(process.env.KBOT_LLAMA_PORT || 8888);
|
|
},
|
|
get host() {
|
|
return process.env.KBOT_LLAMA_HOST || '127.0.0.1';
|
|
},
|
|
get baseURL() {
|
|
return process.env.KBOT_LLAMA_BASE_URL || `http://localhost:${this.port}/v1`;
|
|
},
|
|
router: 'ollama',
|
|
get model() {
|
|
return process.env.KBOT_LLAMA_MODEL || 'default';
|
|
},
|
|
prompts: {
|
|
/** Same idea as llama-basics completion tests. */
|
|
add5_3: 'What is 5 + 3? Reply with just the number, nothing else.',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* IPC payload for kbot-ai → local llama-server (OpenAI-compatible).
|
|
* Pass `base_url` so LLMClient uses port 8888 instead of default ollama :11434.
|
|
*/
|
|
export function kbotAiPayloadLlamaLocal(overrides = {}) {
|
|
const merged = {
|
|
prompt: llama.prompts.add5_3,
|
|
router: llama.router,
|
|
model: llama.model,
|
|
base_url: llama.baseURL,
|
|
...overrides,
|
|
};
|
|
merged.base_url = merged.base_url ?? merged.baseURL ?? llama.baseURL;
|
|
delete merged.baseURL;
|
|
return merged;
|
|
}
|
|
|
|
/** Stock prompts and assertions helpers for LLM smoke tests. */
|
|
export const prompts = {
|
|
germanyCapital: 'What is the capital of Germany? Reply in one short sentence.',
|
|
};
|
|
|
|
/** Build `kbot-ai` IPC payload from env + presets (OpenRouter-friendly defaults). */
|
|
export function kbotAiPayloadFromEnv() {
|
|
const payload = {
|
|
prompt: process.env.KBOT_IPC_PROMPT || prompts.germanyCapital,
|
|
router: router.fromEnv(),
|
|
};
|
|
if (process.env.KBOT_IPC_MODEL) {
|
|
payload.model = process.env.KBOT_IPC_MODEL;
|
|
}
|
|
return payload;
|
|
}
|
|
|
|
/** True when using the default Germany prompt (for optional Berlin assertion). */
|
|
export function usingDefaultGermanyPrompt() {
|
|
return !process.env.KBOT_IPC_PROMPT;
|
|
}
|
|
|
|
/**
|
|
* If nothing listens on llama.port, optionally spawn `scripts/run-7b.sh` (requires `sh` on PATH, e.g. Git Bash on Windows).
|
|
*
|
|
* @param {{ autostart?: boolean, startTimeoutMs?: number }} [opts]
|
|
* @returns {Promise<{ ok: boolean, alreadyRunning: boolean, started?: boolean, pid?: number }>}
|
|
*/
|
|
export async function ensureLlamaLocalServer(opts = {}) {
|
|
const autostart = opts.autostart ?? true;
|
|
const startTimeoutMs = opts.startTimeoutMs ?? timeouts.llamaServerStart;
|
|
const host = llama.host;
|
|
const port = llama.port;
|
|
const scriptPath = paths.run7bScript;
|
|
|
|
if (await probeTcpPort(host, port, 1500)) {
|
|
return { ok: true, alreadyRunning: true };
|
|
}
|
|
|
|
if (!autostart) {
|
|
throw new Error(
|
|
`[llama] Nothing listening on ${host}:${port}. Start the server (e.g. sh scripts/run-7b.sh), or remove KBOT_IPC_LLAMA_AUTOSTART=0 to allow autostart`
|
|
);
|
|
}
|
|
|
|
if (!existsSync(scriptPath)) {
|
|
throw new Error(`[llama] Script missing: ${scriptPath}`);
|
|
}
|
|
|
|
console.log(`[llama] Port ${port} closed — starting ${scriptPath} (timeout ${startTimeoutMs}ms) …`);
|
|
|
|
const child = spawn('sh', [scriptPath], {
|
|
detached: true,
|
|
stdio: 'ignore',
|
|
cwd: dirname(scriptPath),
|
|
env: { ...process.env },
|
|
});
|
|
child.unref();
|
|
|
|
const deadline = Date.now() + startTimeoutMs;
|
|
while (Date.now() < deadline) {
|
|
if (await probeTcpPort(host, port, 1500)) {
|
|
return { ok: true, alreadyRunning: false, started: true, pid: child.pid };
|
|
}
|
|
await new Promise((r) => setTimeout(r, 1500));
|
|
}
|
|
|
|
throw new Error(
|
|
`[llama] Server did not open ${host}:${port} within ${startTimeoutMs}ms — check llama-server / GPU / model path`
|
|
);
|
|
}
|