fix(cowork-service): audit H.7 — fix LLM timeout, harden test mocks, add Ollama config

Bug fixes from systematic review of H.7 LLM router wiring:
- lib/llm-router.ts: remove RUST_RUNTIME_TIMEOUT_MS (300s IPC timeout) override
  — let LlmRouter use its built-in 30s default, appropriate for cloud API calls
- server.test.ts: add debug/error to appMock.log — prevents fragile failures
  if any startup path hits those log levels
- server.test.ts: add OLLAMA_URL + OLLAMA_MODELS to config mock

New feature:
- config.ts: add OLLAMA_URL + OLLAMA_MODELS env vars for local Ollama support
- server.ts: wire Ollama env vars into initLlmRouter() — set
  OLLAMA_MODELS=model1,model2 to auto-add local Ollama as a provider

57 tests passing, 9 test files, typecheck clean
This commit is contained in:
saravanakumardb1 2026-04-02 23:37:50 -07:00
parent f542160784
commit ca7c3e571e
5 changed files with 17 additions and 7 deletions

View File

@ -1,9 +1,9 @@
Last refresh: 2026-04-03T06:00:06Z (2026-04-02 23:00:06 PDT)
Cascade conversations: 50 (297M)
Last refresh: 2026-04-03T06:28:08Z (2026-04-02 23:28:08 PDT)
Cascade conversations: 50 (298M)
Memories: 121
Implicit context: 20
Code tracker dirs: 106
File edit history: 4222 entries
Code tracker dirs: 103
File edit history: 4225 entries
Workspace storage: 37 workspaces
Repo docs: 7 files across 2 repos
Repo workflows: 54 files across 12 repos

View File

@ -29,6 +29,10 @@ const envSchema = baseBackendConfigSchema.extend({
// Anthropic (passed through to Rust runtime)
ANTHROPIC_API_KEY: z.string().optional(),
// LLM Router — local Ollama support (H.7)
OLLAMA_URL: z.string().default('http://localhost:11434/v1'),
OLLAMA_MODELS: z.string().optional(),
// Ecosystem toggles
TELEMETRY_ENABLED: z.coerce.boolean().default(false),
FEATURE_FLAGS_ENABLED: z.coerce.boolean().default(false),

View File

@ -10,7 +10,6 @@
*/
import { LlmRouter, createLocalOllamaProvider, type ProviderConfig, type TelemetryEntry } from '@bytelyst/llm-router';
import { config } from './config.js';
let _router: LlmRouter | null = null;
@ -47,9 +46,10 @@ export function initLlmRouter(opts?: LlmRouterOptions): LlmRouter {
// If no explicit providers, use defaults (cloud free-tier providers)
const routerConfig = providers.length > 0 ? { providers } : undefined;
// Use LlmRouter's built-in 30s timeout — appropriate for cloud LLM API calls.
// (config.RUST_RUNTIME_TIMEOUT_MS is 300s and only for IPC, not HTTP.)
_router = new LlmRouter({
...routerConfig,
timeoutMs: config.RUST_RUNTIME_TIMEOUT_MS,
onTelemetry: opts?.onTelemetry,
});

View File

@ -8,7 +8,7 @@ const appMock = {
register: vi.fn(async () => undefined),
inject: vi.fn(),
get: vi.fn(),
log: { info: vi.fn(), warn: vi.fn() },
log: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
addHook: vi.fn(),
};
@ -32,6 +32,8 @@ vi.mock('./lib/config.js', () => ({
RUST_RUNTIME_TIMEOUT_MS: 300_000,
JWT_SECRET: 'test-secret',
ANTHROPIC_API_KEY: undefined,
OLLAMA_URL: 'http://localhost:11434/v1',
OLLAMA_MODELS: undefined,
},
}));
vi.mock('./lib/product-config.js', () => ({

View File

@ -73,8 +73,12 @@ try {
}
// Initialize LLM router (best-effort — works without API keys in dev)
// Set OLLAMA_MODELS=model1,model2 to add local Ollama as a provider.
const ollamaModels = config.OLLAMA_MODELS?.split(',').map(s => s.trim()).filter(Boolean);
try {
const llm = initLlmRouter({
ollamaModels,
ollamaBaseUrl: config.OLLAMA_URL,
onTelemetry: (entry) => app.log.debug({ llmTelemetry: entry }, 'llm-router event'),
});
app.log.info({ providers: llm.getProviders() }, 'LLM router initialized');