feat: add model scan progress callbacks
This commit is contained in:
@@ -53,7 +53,11 @@ describe("scanOpenRouterModels", () => {
|
|||||||
"acme/free-by-suffix:free",
|
"acme/free-by-suffix:free",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const byPricing = results[0]!;
|
const [byPricing] = results;
|
||||||
|
expect(byPricing).toBeTruthy();
|
||||||
|
if (!byPricing) {
|
||||||
|
throw new Error("Expected pricing-based model result.");
|
||||||
|
}
|
||||||
expect(byPricing.supportsToolsMeta).toBe(true);
|
expect(byPricing.supportsToolsMeta).toBe(true);
|
||||||
expect(byPricing.supportedParametersCount).toBe(3);
|
expect(byPricing.supportedParametersCount).toBe(3);
|
||||||
expect(byPricing.isFree).toBe(true);
|
expect(byPricing.isFree).toBe(true);
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ export type OpenRouterScanOptions = {
|
|||||||
maxAgeDays?: number;
|
maxAgeDays?: number;
|
||||||
providerFilter?: string;
|
providerFilter?: string;
|
||||||
probe?: boolean;
|
probe?: boolean;
|
||||||
|
onProgress?: (update: {
|
||||||
|
phase: "catalog" | "probe";
|
||||||
|
completed: number;
|
||||||
|
total: number;
|
||||||
|
}) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OpenAIModel = Model<"openai-completions">;
|
type OpenAIModel = Model<"openai-completions">;
|
||||||
@@ -333,10 +338,12 @@ async function mapWithConcurrency<T, R>(
|
|||||||
items: T[],
|
items: T[],
|
||||||
concurrency: number,
|
concurrency: number,
|
||||||
fn: (item: T, index: number) => Promise<R>,
|
fn: (item: T, index: number) => Promise<R>,
|
||||||
|
opts?: { onProgress?: (completed: number, total: number) => void },
|
||||||
): Promise<R[]> {
|
): Promise<R[]> {
|
||||||
const limit = Math.max(1, Math.floor(concurrency));
|
const limit = Math.max(1, Math.floor(concurrency));
|
||||||
const results = Array.from({ length: items.length }) as R[];
|
const results = Array.from({ length: items.length }) as R[];
|
||||||
let nextIndex = 0;
|
let nextIndex = 0;
|
||||||
|
let completed = 0;
|
||||||
|
|
||||||
const worker = async () => {
|
const worker = async () => {
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -344,9 +351,16 @@ async function mapWithConcurrency<T, R>(
|
|||||||
nextIndex += 1;
|
nextIndex += 1;
|
||||||
if (current >= items.length) return;
|
if (current >= items.length) return;
|
||||||
results[current] = await fn(items[current] as T, current);
|
results[current] = await fn(items[current] as T, current);
|
||||||
|
completed += 1;
|
||||||
|
opts?.onProgress?.(completed, items.length);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
opts?.onProgress?.(0, 0);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from({ length: Math.min(limit, items.length) }, () => worker()),
|
Array.from({ length: Math.min(limit, items.length) }, () => worker()),
|
||||||
);
|
);
|
||||||
@@ -400,7 +414,16 @@ export async function scanOpenRouterModels(
|
|||||||
|
|
||||||
const baseModel = getModel("openrouter", "openrouter/auto") as OpenAIModel;
|
const baseModel = getModel("openrouter", "openrouter/auto") as OpenAIModel;
|
||||||
|
|
||||||
return mapWithConcurrency(filtered, concurrency, async (entry) => {
|
options.onProgress?.({
|
||||||
|
phase: "probe",
|
||||||
|
completed: 0,
|
||||||
|
total: filtered.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapWithConcurrency(
|
||||||
|
filtered,
|
||||||
|
concurrency,
|
||||||
|
async (entry) => {
|
||||||
const isFree = isFreeOpenRouterModel(entry);
|
const isFree = isFreeOpenRouterModel(entry);
|
||||||
if (!probe) {
|
if (!probe) {
|
||||||
return {
|
return {
|
||||||
@@ -454,7 +477,16 @@ export async function scanOpenRouterModels(
|
|||||||
tool: toolResult,
|
tool: toolResult,
|
||||||
image: imageResult,
|
image: imageResult,
|
||||||
} satisfies ModelScanResult;
|
} satisfies ModelScanResult;
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
onProgress: (completed, total) =>
|
||||||
|
options.onProgress?.({
|
||||||
|
phase: "probe",
|
||||||
|
completed,
|
||||||
|
total,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { OPENROUTER_MODELS_URL };
|
export { OPENROUTER_MODELS_URL };
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
type ModelScanResult,
|
type ModelScanResult,
|
||||||
scanOpenRouterModels,
|
scanOpenRouterModels,
|
||||||
} from "../../agents/model-scan.js";
|
} from "../../agents/model-scan.js";
|
||||||
|
import { withProgress } from "../../cli/progress.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
|
||||||
import type { RuntimeEnv } from "../../runtime.js";
|
import type { RuntimeEnv } from "../../runtime.js";
|
||||||
import { formatMs, formatTokenK, updateConfig } from "./shared.js";
|
import { formatMs, formatTokenK, updateConfig } from "./shared.js";
|
||||||
@@ -188,15 +189,30 @@ export async function modelsScanCommand(
|
|||||||
storedKey = undefined;
|
storedKey = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const results = await scanOpenRouterModels({
|
const results = await withProgress(
|
||||||
apiKey: storedKey ?? undefined,
|
{
|
||||||
minParamB: minParams,
|
label: "Scanning OpenRouter models...",
|
||||||
maxAgeDays,
|
indeterminate: false,
|
||||||
providerFilter: opts.provider,
|
enabled: opts.json !== true,
|
||||||
timeoutMs: timeout,
|
},
|
||||||
concurrency,
|
async (progress) =>
|
||||||
probe,
|
await scanOpenRouterModels({
|
||||||
});
|
apiKey: storedKey ?? undefined,
|
||||||
|
minParamB: minParams,
|
||||||
|
maxAgeDays,
|
||||||
|
providerFilter: opts.provider,
|
||||||
|
timeoutMs: timeout,
|
||||||
|
concurrency,
|
||||||
|
probe,
|
||||||
|
onProgress: ({ phase, completed, total }) => {
|
||||||
|
if (phase !== "probe") return;
|
||||||
|
if (total <= 0) return;
|
||||||
|
const labelBase = probe ? "Probing models" : "Scanning models";
|
||||||
|
progress.setLabel(`${labelBase} (${completed}/${total})`);
|
||||||
|
progress.setPercent((completed / total) * 100);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
if (!probe) {
|
if (!probe) {
|
||||||
if (!opts.json) {
|
if (!opts.json) {
|
||||||
|
|||||||
Reference in New Issue
Block a user