feat: eager vLLM warmup and smart page titles in paginate stage
- Add warmup() to LlmProvider interface for eager subprocess startup - ManagedVllmProvider.warmup() starts vLLM in background on project load - ProviderRegistry.warmupAll() triggers all managed providers - NamedProvider proxies warmup() to inner provider - paginate stage generates LLM-powered descriptive page titles when available, cached by content hash, falls back to generic "Page N" - project-mcp-endpoint calls warmupAll() on router creation so vLLM is loading while the session initializes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
80
src/mcplocal/tests/proxymodel-endpoint.test.ts
Normal file
80
src/mcplocal/tests/proxymodel-endpoint.test.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import Fastify from 'fastify';
|
||||
import { registerProxymodelEndpoint } from '../src/http/proxymodel-endpoint.js';
|
||||
|
||||
describe('ProxyModel endpoint', () => {
|
||||
it('GET /proxymodels returns built-in models', async () => {
|
||||
const app = Fastify({ logger: false });
|
||||
registerProxymodelEndpoint(app);
|
||||
await app.ready();
|
||||
|
||||
const res = await app.inject({ method: 'GET', url: '/proxymodels' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
|
||||
const body = res.json<Array<{ name: string; source: string }>>();
|
||||
expect(Array.isArray(body)).toBe(true);
|
||||
|
||||
const names = body.map((m) => m.name);
|
||||
expect(names).toContain('default');
|
||||
expect(names).toContain('subindex');
|
||||
|
||||
// Each entry has required fields
|
||||
for (const model of body) {
|
||||
expect(model).toHaveProperty('name');
|
||||
expect(model).toHaveProperty('source');
|
||||
expect(model).toHaveProperty('controller');
|
||||
expect(model).toHaveProperty('stages');
|
||||
expect(model).toHaveProperty('cacheable');
|
||||
}
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('GET /proxymodels/:name returns a specific model', async () => {
|
||||
const app = Fastify({ logger: false });
|
||||
registerProxymodelEndpoint(app);
|
||||
await app.ready();
|
||||
|
||||
const res = await app.inject({ method: 'GET', url: '/proxymodels/default' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
|
||||
const body = res.json<{ name: string; source: string; controller: string; stages: unknown[] }>();
|
||||
expect(body.name).toBe('default');
|
||||
expect(body.source).toBe('built-in');
|
||||
expect(body.controller).toBe('gate');
|
||||
expect(Array.isArray(body.stages)).toBe(true);
|
||||
expect(body.stages.length).toBeGreaterThan(0);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('GET /proxymodels/:name returns 404 for unknown model', async () => {
|
||||
const app = Fastify({ logger: false });
|
||||
registerProxymodelEndpoint(app);
|
||||
await app.ready();
|
||||
|
||||
const res = await app.inject({ method: 'GET', url: '/proxymodels/nonexistent' });
|
||||
expect(res.statusCode).toBe(404);
|
||||
|
||||
const body = res.json<{ error: string }>();
|
||||
expect(body.error).toContain('nonexistent');
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('GET /proxymodels/subindex returns subindex model details', async () => {
|
||||
const app = Fastify({ logger: false });
|
||||
registerProxymodelEndpoint(app);
|
||||
await app.ready();
|
||||
|
||||
const res = await app.inject({ method: 'GET', url: '/proxymodels/subindex' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
|
||||
const body = res.json<{ name: string; cacheable: boolean; stages: Array<{ type: string }> }>();
|
||||
expect(body.name).toBe('subindex');
|
||||
expect(body.cacheable).toBe(true);
|
||||
expect(body.stages.some((s) => s.type === 'section-split')).toBe(true);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user