fix: per-provider health checks in status display #44

Merged
michal merged 1 commits from fix/per-provider-health-check into main 2026-02-25 02:25:29 +00:00
2 changed files with 44 additions and 14 deletions
Showing only changes of commit 39ca134201 - Show all commits

View File

@@ -17,6 +17,7 @@ const CLEAR_LINE = '\x1b[2K\r';
interface ProvidersInfo { interface ProvidersInfo {
providers: string[]; providers: string[];
tiers: { fast: string[]; heavy: string[] }; tiers: { fast: string[]; heavy: string[] };
health: Record<string, boolean>;
} }
export interface StatusCommandDeps { export interface StatusCommandDeps {
@@ -234,13 +235,17 @@ export function createStatusCommand(deps?: Partial<StatusCommandDeps>): Command
clearInterval(interval); clearInterval(interval);
if (providersInfo && (providersInfo.tiers.fast.length > 0 || providersInfo.tiers.heavy.length > 0)) { if (providersInfo && (providersInfo.tiers.fast.length > 0 || providersInfo.tiers.heavy.length > 0)) {
// Tiered display // Tiered display with per-provider health
write(`${CLEAR_LINE}`); write(`${CLEAR_LINE}`);
if (providersInfo.tiers.fast.length > 0) { for (const tier of ['fast', 'heavy'] as const) {
log(`LLM (fast): ${providersInfo.tiers.fast.join(', ')} ${GREEN}${RESET}`); const names = providersInfo.tiers[tier];
} if (names.length === 0) continue;
if (providersInfo.tiers.heavy.length > 0) { const label = tier === 'fast' ? 'LLM (fast): ' : 'LLM (heavy):';
log(`LLM (heavy): ${providersInfo.tiers.heavy.join(', ')} ${GREEN}${RESET}`); const parts = names.map((n) => {
const ok = providersInfo.health[n];
return ok ? `${n} ${GREEN}${RESET}` : `${n} ${RED}${RESET}`;
});
log(`${label} ${parts.join(', ')}`);
} }
} else { } else {
// Legacy single provider display // Legacy single provider display
@@ -258,11 +263,15 @@ export function createStatusCommand(deps?: Partial<StatusCommandDeps>): Command
const [llmStatus, models, providersInfo] = await Promise.all([llmPromise, modelsPromise, providersPromise]); const [llmStatus, models, providersInfo] = await Promise.all([llmPromise, modelsPromise, providersPromise]);
if (providersInfo && (providersInfo.tiers.fast.length > 0 || providersInfo.tiers.heavy.length > 0)) { if (providersInfo && (providersInfo.tiers.fast.length > 0 || providersInfo.tiers.heavy.length > 0)) {
if (providersInfo.tiers.fast.length > 0) { for (const tier of ['fast', 'heavy'] as const) {
log(`LLM (fast): ${providersInfo.tiers.fast.join(', ')}`); const names = providersInfo.tiers[tier];
} if (names.length === 0) continue;
if (providersInfo.tiers.heavy.length > 0) { const label = tier === 'fast' ? 'LLM (fast): ' : 'LLM (heavy):';
log(`LLM (heavy): ${providersInfo.tiers.heavy.join(', ')}`); const parts = names.map((n) => {
const ok = providersInfo.health[n];
return ok ? `${n}` : `${n}`;
});
log(`${label} ${parts.join(', ')}`);
} }
} else { } else {
if (llmStatus === 'ok' || llmStatus === 'ok (key stored)') { if (llmStatus === 'ok' || llmStatus === 'ok (key stored)') {

View File

@@ -140,19 +140,40 @@ export async function createHttpServer(
} }
}); });
// LLM providers — list all registered providers with tier assignments // LLM providers — list all registered providers with tier assignments and health
app.get('/llm/providers', async (_request, reply) => { app.get('/llm/providers', async (_request, reply) => {
const registry = deps.providerRegistry; const registry = deps.providerRegistry;
if (!registry) { if (!registry) {
reply.code(200).send({ providers: [], tiers: { fast: [], heavy: [] } }); reply.code(200).send({ providers: [], tiers: { fast: [], heavy: [] }, health: {} });
return; return;
} }
// Run isAvailable() on all providers in parallel (lightweight, no tokens burned)
const names = registry.list();
const healthChecks = await Promise.all(
names.map(async (name) => {
const provider = registry.get(name);
if (!provider) return { name, available: false };
try {
const available = await provider.isAvailable();
return { name, available };
} catch {
return { name, available: false };
}
}),
);
const health: Record<string, boolean> = {};
for (const check of healthChecks) {
health[check.name] = check.available;
}
reply.code(200).send({ reply.code(200).send({
providers: registry.list(), providers: names,
tiers: { tiers: {
fast: registry.getTierProviders('fast'), fast: registry.getTierProviders('fast'),
heavy: registry.getTierProviders('heavy'), heavy: registry.getTierProviders('heavy'),
}, },
health,
}); });
}); });