Spaces:
Paused
Paused
icebear
feat: add POST /admin/refresh-models endpoint + plan routing integration tests (#104)
fce5280 unverified | /** | |
| * 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; | |
| } | |