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

@@ -1,5 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProviderRegistry } from '../src/providers/registry.js';
import { AnthropicProvider } from '../src/providers/anthropic.js';
import type { LlmProvider, CompletionOptions, CompletionResult } from '../src/providers/types.js';
function mockProvider(name: string): LlmProvider {
@@ -217,3 +218,64 @@ describe('ProviderRegistry', () => {
});
});
});
vi.mock('node:https', () => {
let capturedOpts: Record<string, unknown> = {};
const mockRequest = vi.fn((opts: unknown, cb?: unknown) => {
capturedOpts = opts as Record<string, unknown>;
const mockRes = {
on: (event: string, handler: (data?: unknown) => void) => {
if (event === 'data') {
handler(Buffer.from(JSON.stringify({
content: [{ type: 'text', text: 'ok' }],
usage: { input_tokens: 1, output_tokens: 1 },
stop_reason: 'end_turn',
})));
}
if (event === 'end') handler();
return mockRes;
},
};
if (typeof cb === 'function') cb(mockRes);
return {
on: vi.fn().mockReturnThis(),
write: vi.fn(),
end: vi.fn(),
};
});
return {
default: { request: mockRequest },
__capturedOpts: () => capturedOpts,
__mockRequest: mockRequest,
};
});
describe('AnthropicProvider auth headers', () => {
it('uses Authorization: Bearer for OAuth tokens', async () => {
const { __capturedOpts } = await import('node:https') as unknown as { __capturedOpts: () => Record<string, unknown> };
const provider = new AnthropicProvider({ apiKey: 'sk-ant-oat01-test-token' });
await provider.complete({ messages: [{ role: 'user', content: 'hi' }], maxTokens: 1 });
const headers = __capturedOpts().headers as Record<string, string>;
expect(headers['Authorization']).toBe('Bearer sk-ant-oat01-test-token');
expect(headers['x-api-key']).toBeUndefined();
});
it('uses x-api-key for standard API keys', async () => {
const { __capturedOpts } = await import('node:https') as unknown as { __capturedOpts: () => Record<string, unknown> };
const provider = new AnthropicProvider({ apiKey: 'sk-ant-api03-standard-key' });
await provider.complete({ messages: [{ role: 'user', content: 'hi' }], maxTokens: 1 });
const headers = __capturedOpts().headers as Record<string, string>;
expect(headers['x-api-key']).toBe('sk-ant-api03-standard-key');
expect(headers['Authorization']).toBeUndefined();
});
it('includes claude-sonnet-4-5 in model list', async () => {
const provider = new AnthropicProvider({ apiKey: 'test' });
const models = await provider.listModels();
expect(models).toContain('claude-sonnet-4-5-20250514');
expect(models).toContain('claude-opus-4-20250514');
expect(models).toContain('claude-haiku-3-5-20241022');
});
});