Merge branch 'feat/models-pubkey-env'

This commit is contained in:
redshift
2026-08-26 21:26:17 +01:00
3 changed files with 53 additions and 5 deletions
+17 -4
View File
@@ -69,17 +69,30 @@ function repairConfigPermissions(): void {
}
}
/**
* Environment-variable overrides take precedence over config.json so operators
* can set values via Cloudron/docker env (e.g. ROUTSTRD_MODELS_PUBKEY) without
* hand-editing the config file.
*/
function envConfigOverrides(): Partial<RoutstrdConfig> {
const overrides: Partial<RoutstrdConfig> = {};
if (process.env.ROUTSTRD_MODELS_PUBKEY) {
overrides.routstrModelsPubkey = process.env.ROUTSTRD_MODELS_PUBKEY;
}
return overrides;
}
export async function loadDaemonConfig(): Promise<RoutstrdConfig> {
try {
if (existsSync(CONFIG_FILE)) {
repairConfigPermissions();
const content = await Bun.file(CONFIG_FILE).text();
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
return { ...DEFAULT_CONFIG, ...JSON.parse(content), ...envConfigOverrides() };
}
} catch (error) {
logger.error("Failed to load config:", error);
}
return DEFAULT_CONFIG;
return { ...DEFAULT_CONFIG, ...envConfigOverrides() };
}
export function loadDaemonConfigSync(): RoutstrdConfig {
@@ -87,12 +100,12 @@ export function loadDaemonConfigSync(): RoutstrdConfig {
if (existsSync(CONFIG_FILE)) {
repairConfigPermissions();
const content = readFileSync(CONFIG_FILE, "utf-8");
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
return { ...DEFAULT_CONFIG, ...JSON.parse(content), ...envConfigOverrides() };
}
} catch (error) {
logger.error("Failed to load config:", error);
}
return DEFAULT_CONFIG;
return { ...DEFAULT_CONFIG, ...envConfigOverrides() };
}
/**
+27 -1
View File
@@ -8,7 +8,10 @@ import { callDaemon, getDaemonBaseUrl } from "../utils/daemon-client";
type PiModelEntry = {
id: string;
contextWindow?: number;
// Preserve arbitrary manual fields (reasoning, thinkingLevelMap, compat, cost, maxTokens, etc.)
name?: string;
input?: string[];
maxTokens?: number;
// Preserve arbitrary manual fields (reasoning, thinkingLevelMap, compat, cost, etc.)
[key: string]: unknown;
};
@@ -75,8 +78,31 @@ export async function installPiIntegration(
const entry: PiModelEntry = previous ? { ...previous } : { id: model.id };
entry.id = model.id;
if (model.context_length !== undefined && model.context_length > 0) {
entry.contextWindow = model.context_length;
} else {
delete entry.contextWindow;
}
if (model.name) {
entry.name = model.name;
} else {
delete entry.name;
}
// Map the daemon's input modalities to Pi's ["text", "image"] vocabulary.
const mods = model.architecture?.input_modalities ?? [];
const input: string[] = [];
if (mods.includes("text")) input.push("text");
if (mods.includes("image")) input.push("image");
entry.input = input;
const maxTokens = model.top_provider?.max_completion_tokens;
if (maxTokens !== undefined && maxTokens !== null && maxTokens > 0) {
entry.maxTokens = maxTokens;
} else {
delete entry.maxTokens;
}
return entry;
+9
View File
@@ -18,6 +18,15 @@ 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;
};
};
export type IntegrationFn = (