codex-proxy / src /routes /models.ts
icebear
feat: add POST /admin/refresh-models endpoint + plan routing integration tests (#104)
fce5280 unverified
raw
history blame
3.48 kB
/**
* Model routes — pure route handlers reading from model-store singleton.
*/
import { Hono } from "hono";
import type { OpenAIModel, OpenAIModelList } from "../types/openai.js";
import {
getModelCatalog,
getModelAliases,
getModelInfo,
getModelStoreDebug,
type CodexModelInfo,
} from "../models/model-store.js";
import { triggerImmediateRefresh } from "../models/model-fetcher.js";
import { getConfig } from "../config.js";
// --- Routes ---
/** Stable timestamp used for all model `created` fields (2023-11-14T22:13:20Z). */
const MODEL_CREATED_TIMESTAMP = 1700000000;
function toOpenAIModel(info: CodexModelInfo): OpenAIModel {
return {
id: info.id,
object: "model",
created: MODEL_CREATED_TIMESTAMP,
owned_by: "openai",
};
}
export function createModelRoutes(): Hono {
const app = new Hono();
app.get("/v1/models", (c) => {
const catalog = getModelCatalog();
const aliases = getModelAliases();
// Include catalog models + aliases as separate entries
const models: OpenAIModel[] = catalog.map(toOpenAIModel);
for (const alias of Object.keys(aliases)) {
models.push({
id: alias,
object: "model",
created: MODEL_CREATED_TIMESTAMP,
owned_by: "openai",
});
}
const response: OpenAIModelList = { object: "list", data: models };
return c.json(response);
});
// Full catalog with reasoning efforts (for dashboard UI)
// Must be before :modelId to avoid being matched as a model ID
app.get("/v1/models/catalog", (c) => {
return c.json(getModelCatalog());
});
app.get("/v1/models/:modelId", (c) => {
const modelId = c.req.param("modelId");
const catalog = getModelCatalog();
const aliases = getModelAliases();
// Try direct match
const info = catalog.find((m) => m.id === modelId);
if (info) return c.json(toOpenAIModel(info));
// Try alias
const resolved = aliases[modelId];
if (resolved) {
return c.json({
id: modelId,
object: "model",
created: MODEL_CREATED_TIMESTAMP,
owned_by: "openai",
});
}
c.status(404);
return c.json({
error: {
message: `Model '${modelId}' not found`,
type: "invalid_request_error",
param: "model",
code: "model_not_found",
},
});
});
// Extended endpoint: model details with reasoning efforts
app.get("/v1/models/:modelId/info", (c) => {
const modelId = c.req.param("modelId");
const aliases = getModelAliases();
const resolved = aliases[modelId] ?? modelId;
const info = getModelInfo(resolved);
if (!info) {
c.status(404);
return c.json({ error: `Model '${modelId}' not found` });
}
return c.json(info);
});
// Debug endpoint: model store internals
app.get("/debug/models", (c) => {
return c.json(getModelStoreDebug());
});
// Admin endpoint: trigger immediate model refresh
app.post("/admin/refresh-models", (c) => {
const config = getConfig();
const configKey = config.server.proxy_api_key;
if (configKey) {
const authHeader = c.req.header("Authorization") ?? "";
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
if (token !== configKey) {
c.status(401);
return c.json({ error: "Unauthorized" });
}
}
triggerImmediateRefresh();
return c.json({ ok: true, message: "Model refresh triggered" });
});
return app;
}