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:
78
src/mcplocal/tests/proxymodel-llm-adapter.test.ts
Normal file
78
src/mcplocal/tests/proxymodel-llm-adapter.test.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { LLMProviderAdapter } from '../src/proxymodel/llm-adapter.js';
|
||||
import { ProviderRegistry } from '../src/providers/registry.js';
|
||||
import type { LlmProvider, CompletionResult } from '../src/providers/types.js';
|
||||
|
||||
function mockProvider(name: string, response = 'mock response'): LlmProvider {
|
||||
return {
|
||||
name,
|
||||
complete: vi.fn().mockResolvedValue({
|
||||
content: response,
|
||||
toolCalls: [],
|
||||
usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15 },
|
||||
finishReason: 'stop',
|
||||
} satisfies CompletionResult),
|
||||
listModels: vi.fn().mockResolvedValue([]),
|
||||
isAvailable: vi.fn().mockResolvedValue(true),
|
||||
};
|
||||
}
|
||||
|
||||
describe('LLMProviderAdapter', () => {
|
||||
it('available() returns true when a provider is registered', () => {
|
||||
const registry = new ProviderRegistry();
|
||||
registry.register(mockProvider('test'));
|
||||
registry.assignTier('test', 'fast');
|
||||
|
||||
const adapter = new LLMProviderAdapter(registry);
|
||||
expect(adapter.available()).toBe(true);
|
||||
});
|
||||
|
||||
it('available() returns false when no provider is registered', () => {
|
||||
const registry = new ProviderRegistry();
|
||||
const adapter = new LLMProviderAdapter(registry);
|
||||
expect(adapter.available()).toBe(false);
|
||||
});
|
||||
|
||||
it('complete() sends prompt as user message', async () => {
|
||||
const provider = mockProvider('test');
|
||||
const registry = new ProviderRegistry();
|
||||
registry.register(provider);
|
||||
registry.assignTier('test', 'fast');
|
||||
|
||||
const adapter = new LLMProviderAdapter(registry);
|
||||
const result = await adapter.complete('summarize this');
|
||||
|
||||
expect(result).toBe('mock response');
|
||||
expect(provider.complete).toHaveBeenCalledWith({
|
||||
messages: [{ role: 'user', content: 'summarize this' }],
|
||||
maxTokens: undefined,
|
||||
temperature: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('complete() includes system message when provided', async () => {
|
||||
const provider = mockProvider('test');
|
||||
const registry = new ProviderRegistry();
|
||||
registry.register(provider);
|
||||
registry.assignTier('test', 'fast');
|
||||
|
||||
const adapter = new LLMProviderAdapter(registry);
|
||||
await adapter.complete('summarize', { system: 'You are a summarizer', maxTokens: 200 });
|
||||
|
||||
expect(provider.complete).toHaveBeenCalledWith({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a summarizer' },
|
||||
{ role: 'user', content: 'summarize' },
|
||||
],
|
||||
maxTokens: 200,
|
||||
temperature: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('complete() throws when no provider available', async () => {
|
||||
const registry = new ProviderRegistry();
|
||||
const adapter = new LLMProviderAdapter(registry);
|
||||
|
||||
await expect(adapter.complete('test')).rejects.toThrow('No LLM provider available');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user