mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-19 09:44:07 +00:00
Internal refactor: extract BackendAdapter, ModelRegistry, AgentRouter, SessionManager, and StatsCollector as standalone modules. Claude CLI logic encapsulated in ClaudeCliAdapter. External API completely unchanged — all existing endpoints behave identically. Legacy request path (callClaude/callClaudeStreaming) untouched. v4 modules load in parallel for validation. New endpoints: GET /backends — backend health and status GET /routing — agent routing table Architecture: unified-chunk.mjs — streaming protocol (delta/done/error) backends/base.mjs — BackendAdapter interface backends/claude-cli.mjs — Claude CLI adapter (core) model-registry.mjs — model → backend mapping (Layer 1) agent-router.mjs — agent policy routing (Layer 2) session-manager.mjs — conversation session management stats-collector.mjs — per-model/backend/agent metrics Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
100 lines
2.6 KiB
JavaScript
100 lines
2.6 KiB
JavaScript
/**
|
|
* Model Registry — Layer 1 of OCP routing.
|
|
*
|
|
* Static mapping: model ID → backend ID.
|
|
* This is infrastructure, not policy. Every model must map to exactly one backend.
|
|
*
|
|
* Non-agent callers (Cline, Aider, etc.) only send a model name — this module
|
|
* is the only thing that tells OCP where to route the request.
|
|
*/
|
|
|
|
export class ModelRegistry {
|
|
constructor() {
|
|
/** @type {Map<string, {backendId: string, canonical: string}>} */
|
|
this._models = new Map();
|
|
|
|
/** @type {Map<string, Object>} model id → display metadata */
|
|
this._metadata = new Map();
|
|
}
|
|
|
|
/**
|
|
* Register models from a backend adapter.
|
|
* Called during startup for each enabled backend.
|
|
*
|
|
* @param {import("./backends/base.mjs").BackendAdapter} adapter
|
|
*/
|
|
registerBackend(adapter) {
|
|
for (const modelId of adapter.models) {
|
|
this._models.set(modelId, {
|
|
backendId: adapter.id,
|
|
canonical: modelId,
|
|
});
|
|
this._metadata.set(modelId, {
|
|
backendId: adapter.id,
|
|
backendName: adapter.displayName,
|
|
tier: adapter.tier,
|
|
costType: adapter.costType,
|
|
});
|
|
}
|
|
|
|
// Also register aliases if the adapter supports resolveModel
|
|
if (typeof adapter.resolveModel === "function") {
|
|
// We don't auto-register aliases here — they're handled by
|
|
// supportsModel() on the adapter. The registry only knows canonical IDs.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Look up which backend serves a given model.
|
|
*
|
|
* @param {string} model — model ID (could be canonical or alias)
|
|
* @returns {{backendId: string, canonical: string} | null}
|
|
*/
|
|
resolve(model) {
|
|
// Direct lookup first
|
|
const direct = this._models.get(model);
|
|
if (direct) return direct;
|
|
|
|
// No alias resolution here — that's the adapter's job.
|
|
// The router will ask each adapter if it supports the model.
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get all registered models with metadata.
|
|
* Used by GET /v1/models endpoint.
|
|
*/
|
|
listModels() {
|
|
const result = [];
|
|
for (const [id, meta] of this._metadata) {
|
|
result.push({ id, ...meta });
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check if a model is known to the registry.
|
|
*/
|
|
has(model) {
|
|
return this._models.has(model);
|
|
}
|
|
|
|
/**
|
|
* Get the backend ID for a model, or null.
|
|
*/
|
|
getBackendId(model) {
|
|
return this._models.get(model)?.backendId || null;
|
|
}
|
|
|
|
/**
|
|
* Get all models for a specific backend.
|
|
*/
|
|
getModelsForBackend(backendId) {
|
|
const models = [];
|
|
for (const [id, entry] of this._models) {
|
|
if (entry.backendId === backendId) models.push(id);
|
|
}
|
|
return models;
|
|
}
|
|
}
|