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:
@@ -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' } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user