mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
feat(x): thread the models.dev reasoning flag to the renderer
Widens the models.dev schema to keep the per-model `reasoning` boolean and carries it through normalizeModels → ProviderSummary → models:list, so the composer can gate the reasoning-effort control on actual model capability. Gateway model lists (bare "vendor/model" ids from the server) are annotated from the models.dev cache in one batched, cache-only read; unknown models keep the flag absent, which the UI treats as "hide the control". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
125d39ee0b
commit
1ffe29db9d
3 changed files with 50 additions and 2 deletions
|
|
@ -3,6 +3,7 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|||
import { getAccessToken } from '../auth/tokens.js';
|
||||
import { getCurrentUseCase } from '../analytics/use_case.js';
|
||||
import { API_URL } from '../config/env.js';
|
||||
import { annotateReasoningFlags } from './models-dev.js';
|
||||
|
||||
const authedFetch: typeof fetch = async (input, init) => {
|
||||
const token = await getAccessToken();
|
||||
|
|
@ -30,6 +31,7 @@ type ProviderSummary = {
|
|||
id: string;
|
||||
name?: string;
|
||||
release_date?: string;
|
||||
reasoning?: boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
|
|
@ -42,7 +44,9 @@ export async function listGatewayModels(): Promise<{ providers: ProviderSummary[
|
|||
throw new Error(`Gateway /v1/models failed: ${response.status}`);
|
||||
}
|
||||
const body = await response.json() as { data: Array<{ id: string }> };
|
||||
const models = body.data.map((m) => ({ id: m.id }));
|
||||
// The gateway returns bare "vendor/model" ids; the models.dev cache
|
||||
// supplies the reasoning capability the composer's effort control needs.
|
||||
const models = await annotateReasoningFlags(body.data.map((m) => ({ id: m.id })));
|
||||
return {
|
||||
providers: [{
|
||||
id: 'rowboat',
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ type ProviderSummary = {
|
|||
id: string;
|
||||
name?: string;
|
||||
release_date?: string;
|
||||
// Supports reasoning/extended thinking per models.dev; absent = unknown.
|
||||
reasoning?: boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
|
|
@ -195,10 +197,11 @@ function normalizeModels(models: Record<string, z.infer<typeof ModelsDevModel>>)
|
|||
name: model.name,
|
||||
release_date: model.release_date,
|
||||
tool_call: model.tool_call,
|
||||
reasoning: model.reasoning,
|
||||
status: model.status,
|
||||
}))
|
||||
.filter((model) => isStableModel(model) && supportsToolCall(model))
|
||||
.map(({ id, name, release_date }) => ({ id, name, release_date }));
|
||||
.map(({ id, name, release_date, reasoning }) => ({ id, name, release_date, reasoning }));
|
||||
|
||||
list.sort((a, b) => {
|
||||
const aDate = a.release_date ? Date.parse(a.release_date) : 0;
|
||||
|
|
@ -268,6 +271,44 @@ export async function isReasoningModel(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate gateway-style "vendor/model" ids with the models.dev reasoning
|
||||
* flag. Reads the cache once for the whole batch; ids whose vendor or model
|
||||
* is unknown keep `reasoning` absent (= unknown). Cache-only, like
|
||||
* isReasoningModel.
|
||||
*/
|
||||
export async function annotateReasoningFlags<T extends { id: string }>(
|
||||
models: T[],
|
||||
): Promise<Array<T & { reasoning?: boolean }>> {
|
||||
let catalog: z.infer<typeof ModelsDevResponse> | null = null;
|
||||
try {
|
||||
const cached = await readCache();
|
||||
if (cached) {
|
||||
const parsed = ModelsDevResponse.safeParse(cached.data);
|
||||
if (parsed.success) catalog = parsed.data;
|
||||
}
|
||||
} catch {
|
||||
catalog = null;
|
||||
}
|
||||
if (!catalog) return models;
|
||||
|
||||
const flags = new Map<string, boolean>();
|
||||
for (const vendor of ["openai", "anthropic", "google"] as const) {
|
||||
const provider = pickProvider(catalog, vendor);
|
||||
if (!provider) continue;
|
||||
for (const [key, model] of Object.entries(provider.models)) {
|
||||
if (typeof model.reasoning === "boolean") {
|
||||
flags.set(`${vendor}/${model.id ?? key}`, model.reasoning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return models.map((model) => {
|
||||
const reasoning = flags.get(model.id);
|
||||
return reasoning === undefined ? model : { ...model, reasoning };
|
||||
});
|
||||
}
|
||||
|
||||
export async function getChatModelIds(
|
||||
flavor: "openai" | "anthropic" | "google",
|
||||
): Promise<Set<string>> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue