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:
@@ -4,8 +4,11 @@ import type { McpOrchestrator, ContainerSpec, ContainerInfo } from './orchestrat
|
||||
import { NotFoundError } from './mcp-server.service.js';
|
||||
import { resolveServerEnv } from './env-resolver.js';
|
||||
|
||||
/** Default image for npm-based MCP servers (STDIO with packageName, no dockerImage). */
|
||||
const DEFAULT_NODE_RUNNER_IMAGE = process.env['MCPD_NODE_RUNNER_IMAGE'] ?? 'mysources.co.uk/michal/mcpctl-node-runner:latest';
|
||||
/** Runner images for package-based MCP servers, keyed by runtime name. */
|
||||
const RUNNER_IMAGES: Record<string, string> = {
|
||||
node: process.env['MCPD_NODE_RUNNER_IMAGE'] ?? 'mysources.co.uk/michal/mcpctl-node-runner:latest',
|
||||
python: process.env['MCPD_PYTHON_RUNNER_IMAGE'] ?? 'mysources.co.uk/michal/mcpctl-python-runner:latest',
|
||||
};
|
||||
|
||||
/** Network for MCP server containers (matches docker-compose mcp-servers network). */
|
||||
const MCP_SERVERS_NETWORK = process.env['MCPD_MCP_NETWORK'] ?? 'mcp-servers';
|
||||
@@ -183,18 +186,19 @@ export class InstanceService {
|
||||
|
||||
// Determine image + command based on server config:
|
||||
// 1. Explicit dockerImage → use as-is
|
||||
// 2. packageName (npm) → use node-runner image + npx command
|
||||
// 2. packageName → use runtime-specific runner image (node/python/go/...)
|
||||
// 3. Fallback → server name (legacy)
|
||||
let image: string;
|
||||
let npmCommand: string[] | undefined;
|
||||
let pkgCommand: string[] | undefined;
|
||||
|
||||
if (server.dockerImage) {
|
||||
image = server.dockerImage;
|
||||
} else if (server.packageName) {
|
||||
image = DEFAULT_NODE_RUNNER_IMAGE;
|
||||
// Build npx command: entrypoint is ["npx", "-y"], so CMD = [packageName, ...args]
|
||||
const runtime = (server.runtime as string | null) ?? 'node';
|
||||
image = RUNNER_IMAGES[runtime] ?? RUNNER_IMAGES['node']!;
|
||||
// Runner entrypoint handles package execution (npx -y / uvx / go run)
|
||||
const serverCommand = server.command as string[] | null;
|
||||
npmCommand = [server.packageName, ...(serverCommand ?? [])];
|
||||
pkgCommand = [server.packageName, ...(serverCommand ?? [])];
|
||||
} else {
|
||||
image = server.name;
|
||||
}
|
||||
@@ -218,10 +222,10 @@ export class InstanceService {
|
||||
if (server.transport === 'SSE' || server.transport === 'STREAMABLE_HTTP') {
|
||||
spec.containerPort = server.containerPort ?? 3000;
|
||||
}
|
||||
// npm-based servers: command = [packageName, ...args] (entrypoint handles npx -y)
|
||||
// Package-based servers: command = [packageName, ...args] (entrypoint handles execution)
|
||||
// Docker-image servers: use explicit command if provided
|
||||
if (npmCommand) {
|
||||
spec.command = npmCommand;
|
||||
if (pkgCommand) {
|
||||
spec.command = pkgCommand;
|
||||
} else {
|
||||
const command = server.command as string[] | null;
|
||||
if (command) {
|
||||
|
||||
Reference in New Issue
Block a user