v2.1.0-provider-picker: BooCoder systemd migration + provider picker
- BooCoder moves from Docker to host systemd service (boocoder.service) - Agent dispatch (ACP + PTY) switches from SSH to direct spawn/exec - SSH helpers marked @deprecated (kept for one release cycle) - Provider registry (5 providers: boocode, opencode, goose, claude, qwen) - Agent probe with direct which/exec + model discovery (qwen settings, static claude models) - GET /api/providers route with installed status, models, transport fallback - ProviderPicker frontend component in CoderPane header - External provider messages route through tasks row instead of inference enqueue - Smart scroll: MessageList only auto-scrolls when near bottom (150px threshold) - DB: available_agents gets models, label, transport columns - Bug fix: loadContext SELECT includes allowed_read_paths - Bug fix: cap hit sentinel inserted before buildMessagesPayload - docker-compose.yml: boocoder service commented out, BOOCODER_URL env var added - CLAUDE.md: updated docs for systemd, provider registry, JSONB gotcha, loadContext
This commit is contained in:
80
apps/coder/src/routes/providers.ts
Normal file
80
apps/coder/src/routes/providers.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { Sql } from '../db.js';
|
||||
import type { Config } from '../config.js';
|
||||
import { PROVIDERS } from '../services/provider-registry.js';
|
||||
|
||||
interface ProviderModel {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface ProviderResponse {
|
||||
name: string;
|
||||
label: string;
|
||||
transport: string;
|
||||
installed: boolean;
|
||||
models: ProviderModel[];
|
||||
}
|
||||
|
||||
interface LlamaSwapModel {
|
||||
id: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
async function fetchLlamaSwapModels(config: Config): Promise<ProviderModel[]> {
|
||||
try {
|
||||
const res = await fetch(`${config.LLAMA_SWAP_URL}/v1/models`);
|
||||
if (!res.ok) return [];
|
||||
const parsed = (await res.json()) as { data?: LlamaSwapModel[] };
|
||||
return (parsed.data ?? []).map((m) => ({ id: m.id, label: m.id }));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function registerProviderRoutes(app: FastifyInstance, sql: Sql, config: Config): void {
|
||||
app.get('/api/providers', async (_req, _reply) => {
|
||||
const llamaModels = await fetchLlamaSwapModels(config);
|
||||
|
||||
const agents = await sql<{ name: string; models: ProviderModel[]; label: string | null; transport: string | null; supports_acp: boolean }[]>`
|
||||
SELECT name, models, label, transport, supports_acp FROM available_agents
|
||||
`;
|
||||
const agentMap = new Map(agents.map((a) => [a.name, a]));
|
||||
|
||||
const result: ProviderResponse[] = [];
|
||||
|
||||
for (const provider of PROVIDERS) {
|
||||
const isNative = provider.name === 'boocode';
|
||||
const agentRow = agentMap.get(provider.name);
|
||||
const installed = isNative || !!agentRow;
|
||||
|
||||
if (!installed) continue;
|
||||
|
||||
let models: ProviderModel[];
|
||||
if (provider.modelSource === 'llama-swap') {
|
||||
models = llamaModels;
|
||||
} else if (agentRow?.models && agentRow.models.length > 0) {
|
||||
models = agentRow.models;
|
||||
} else if (provider.staticModels) {
|
||||
models = provider.staticModels;
|
||||
} else {
|
||||
models = [];
|
||||
}
|
||||
|
||||
let transport: string = provider.transport;
|
||||
if (agentRow) {
|
||||
transport = provider.transport === 'acp' && !agentRow.supports_acp ? 'pty' : provider.transport;
|
||||
}
|
||||
|
||||
result.push({
|
||||
name: provider.name,
|
||||
label: agentRow?.label ?? provider.label,
|
||||
transport,
|
||||
installed,
|
||||
models,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user