mirror of
https://github.com/Routstr/routstrd.git
synced 2026-10-05 12:28:23 +00:00
The generated models.json previously left reasoning/thinkingLevelMap to hand curation, so a fresh install advertised no thinking levels at all and a stale hand-written map could disagree with what the model accepts. routstr-core now publishes a per-model `reasoning` object (OpenRouter shape) with `supported_efforts`. Derive pi's thinking fields from it: - write every pi level explicitly, because pi reads an omitted level as "provider default" for levels through `high` and as "unsupported" for `xhigh`/`max` — a partial map would advertise levels the model rejects - map pi's `off` to the provider's `none`, and never offer `off` on models that report `mandatory: true` - return null when the daemon publishes no allowlist (models that only report `mandatory`, or no reasoning at all), and keep the user's hand-curated values in that case Extract buildPiModelEntry so the projection is testable, and cover the derivation plus the preserve/overwrite behaviour in tests.
104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import { join } from "path";
|
|
import type { RoutstrdConfig } from "../utils/config";
|
|
import { installOpencodeIntegration } from "./opencode";
|
|
import { installPiIntegration } from "./pi";
|
|
import { installOpenClawIntegration } from "./openclaw";
|
|
import { installClaudeCodeIntegration } from "./claudecode";
|
|
import { installHermesIntegration } from "./hermes";
|
|
|
|
const HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
|
|
export interface IntegrationConfig {
|
|
clientId: string;
|
|
name: string;
|
|
configPath: string;
|
|
}
|
|
|
|
/**
|
|
* Per-model reasoning metadata as published by routstr-core (OpenRouter shape).
|
|
* Models with no reasoning support omit the whole object, and models whose
|
|
* upstream publishes no effort allowlist carry only `mandatory`.
|
|
*/
|
|
export type RoutstrReasoning = {
|
|
mandatory?: boolean | null;
|
|
default_enabled?: boolean | null;
|
|
supported_efforts?: string[] | null;
|
|
default_effort?: string | null;
|
|
supports_max_tokens?: boolean | null;
|
|
};
|
|
|
|
export type RoutstrModel = {
|
|
id: string;
|
|
name?: string;
|
|
context_length?: number;
|
|
architecture?: {
|
|
modality?: string;
|
|
input_modalities?: string[];
|
|
output_modalities?: string[];
|
|
};
|
|
top_provider?: {
|
|
context_length?: number;
|
|
max_completion_tokens?: number;
|
|
};
|
|
reasoning?: RoutstrReasoning | null;
|
|
};
|
|
|
|
export type IntegrationFn = (
|
|
config: RoutstrdConfig,
|
|
apiKey: string,
|
|
integrationConfig: IntegrationConfig,
|
|
) => Promise<void>;
|
|
|
|
export const CLIENT_CONFIGS: Record<string, IntegrationConfig> = {
|
|
opencode: {
|
|
clientId: "opencode",
|
|
name: "OpenCode",
|
|
configPath: join(HOME, ".config/opencode/opencode.json"),
|
|
},
|
|
"pi-agent": {
|
|
clientId: "pi-agent",
|
|
name: "Pi Agent",
|
|
configPath: join(HOME, ".pi/agent/models.json"),
|
|
},
|
|
openclaw: {
|
|
clientId: "openclaw",
|
|
name: "OpenClaw",
|
|
configPath: join(HOME, ".openclaw/openclaw.json"),
|
|
},
|
|
"claude-code": {
|
|
clientId: "claude-code",
|
|
name: "Claude Code",
|
|
configPath: join(HOME, ".claude/settings.json"),
|
|
},
|
|
hermes: {
|
|
clientId: "hermes",
|
|
name: "Hermes",
|
|
configPath: join(HOME, ".hermes/config.yaml"),
|
|
},
|
|
};
|
|
|
|
export const CLIENT_INTEGRATIONS: Record<string, IntegrationFn> = {
|
|
opencode: installOpencodeIntegration,
|
|
"pi-agent": installPiIntegration,
|
|
openclaw: installOpenClawIntegration,
|
|
"claude-code": installClaudeCodeIntegration,
|
|
hermes: installHermesIntegration,
|
|
};
|
|
|
|
export async function runIntegrationsForClients(
|
|
clientIds: Array<{ clientId: string; apiKey?: string }>,
|
|
config: RoutstrdConfig,
|
|
): Promise<void> {
|
|
for (const client of clientIds) {
|
|
const integrationFn = CLIENT_INTEGRATIONS[client.clientId];
|
|
const integrationConfig = CLIENT_CONFIGS[client.clientId];
|
|
if (integrationFn && integrationConfig && client.apiKey) {
|
|
try {
|
|
await integrationFn(config, client.apiKey, integrationConfig);
|
|
} catch (error) {
|
|
console.error(`Integration failed for ${client.clientId}:`, error);
|
|
}
|
|
}
|
|
}
|
|
}
|