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:
Michal
2026-03-03 19:07:39 +00:00
parent 0427d7dc1a
commit 03827f11e4
147 changed files with 17561 additions and 2093 deletions

View File

@@ -16,9 +16,12 @@ function makeProject(overrides: Partial<ProjectWithRelations> = {}): ProjectWith
description: '',
ownerId: 'user-1',
proxyMode: 'direct',
prompt: '',
proxyModel: '',
gated: true,
llmProvider: null,
llmModel: null,
serverOverrides: null,
version: 1,
createdAt: new Date(),
updatedAt: new Date(),
@@ -149,6 +152,21 @@ describe('Project Routes', () => {
expect(res.statusCode).toBe(201);
});
it('creates a project with proxyModel', async () => {
const repo = mockProjectRepo();
vi.mocked(repo.findById).mockResolvedValue(makeProject({ name: 'pm-proj', proxyModel: 'subindex' }));
await createApp(repo);
const res = await app.inject({
method: 'POST',
url: '/api/v1/projects',
payload: { name: 'pm-proj', proxyModel: 'subindex' },
});
expect(res.statusCode).toBe(201);
expect(repo.create).toHaveBeenCalledWith(
expect.objectContaining({ proxyModel: 'subindex' }),
);
});
it('returns 400 for invalid input', async () => {
const repo = mockProjectRepo();
await createApp(repo);
@@ -186,6 +204,19 @@ describe('Project Routes', () => {
expect(res.statusCode).toBe(200);
});
it('updates proxyModel on a project', async () => {
const repo = mockProjectRepo();
vi.mocked(repo.findById).mockResolvedValue(makeProject({ id: 'p1' }));
await createApp(repo);
const res = await app.inject({
method: 'PUT',
url: '/api/v1/projects/p1',
payload: { proxyModel: 'subindex' },
});
expect(res.statusCode).toBe(200);
expect(repo.update).toHaveBeenCalledWith('p1', expect.objectContaining({ proxyModel: 'subindex' }));
});
it('returns 404 when not found', async () => {
const repo = mockProjectRepo();
await createApp(repo);
@@ -281,4 +312,50 @@ describe('Project Routes', () => {
expect(res.statusCode).toBe(404);
});
});
describe('serverOverrides', () => {
it('accepts serverOverrides in project create', async () => {
const repo = mockProjectRepo();
vi.mocked(repo.findById).mockResolvedValue(
makeProject({ name: 'override-proj', serverOverrides: { ha: { proxyModel: 'ha-special' } } }),
);
await createApp(repo);
const res = await app.inject({
method: 'POST',
url: '/api/v1/projects',
payload: { name: 'override-proj', serverOverrides: { ha: { proxyModel: 'ha-special' } } },
});
expect(res.statusCode).toBe(201);
expect(repo.create).toHaveBeenCalledWith(
expect.objectContaining({ serverOverrides: { ha: { proxyModel: 'ha-special' } } }),
);
});
it('accepts serverOverrides in project update', async () => {
const repo = mockProjectRepo();
vi.mocked(repo.findById).mockResolvedValue(makeProject({ id: 'p1' }));
await createApp(repo);
const res = await app.inject({
method: 'PUT',
url: '/api/v1/projects/p1',
payload: { serverOverrides: { ha: { proxyModel: 'ha-special' } } },
});
expect(res.statusCode).toBe(200);
expect(repo.update).toHaveBeenCalledWith('p1', expect.objectContaining({
serverOverrides: { ha: { proxyModel: 'ha-special' } },
}));
});
it('returns serverOverrides in project GET', async () => {
const repo = mockProjectRepo();
vi.mocked(repo.findById).mockResolvedValue(
makeProject({ id: 'p1', name: 'ha-proj', serverOverrides: { ha: { proxyModel: 'ha-special' } } }),
);
await createApp(repo);
const res = await app.inject({ method: 'GET', url: '/api/v1/projects/p1' });
expect(res.statusCode).toBe(200);
const body = res.json<{ serverOverrides: unknown }>();
expect(body.serverOverrides).toEqual({ ha: { proxyModel: 'ha-special' } });
});
});
});