Compare commits
2 Commits
feat/tiere
...
fix/per-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39ca134201 | ||
| 78a1dc9c8a |
@@ -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)') {
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user