mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
feat: Phase 1 — modular backend architecture (v4.0-alpha)
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>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* SessionManager — manages conversation sessions across requests.
|
||||
*
|
||||
* Maps caller-provided conversation IDs to backend-specific session state.
|
||||
* For Claude CLI, this means mapping to CLI session UUIDs for --resume.
|
||||
*/
|
||||
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
export class SessionManager {
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {number} [options.ttl=3600000] — Session TTL in ms (default 1h)
|
||||
*/
|
||||
constructor({ ttl = 3600000 } = {}) {
|
||||
this._ttl = ttl;
|
||||
/** @type {Map<string, SessionEntry>} */
|
||||
this._sessions = new Map();
|
||||
|
||||
// Cleanup expired sessions every 60s
|
||||
this._cleanupInterval = setInterval(() => this._cleanup(), 60000);
|
||||
}
|
||||
|
||||
get ttl() { return this._ttl; }
|
||||
set ttl(val) { this._ttl = val; }
|
||||
get size() { return this._sessions.size; }
|
||||
|
||||
/**
|
||||
* Get or create a session for a conversation.
|
||||
*
|
||||
* @param {string|null} conversationId
|
||||
* @param {string} model — model being used
|
||||
* @param {string} [agent] — agent identity
|
||||
* @returns {{session: {uuid: string, resume: boolean}, isNew: boolean, isOneOff: boolean}}
|
||||
*/
|
||||
resolve(conversationId, model, agent = "default") {
|
||||
if (!conversationId) {
|
||||
return { session: null, isNew: false, isOneOff: true };
|
||||
}
|
||||
|
||||
if (this._sessions.has(conversationId)) {
|
||||
const entry = this._sessions.get(conversationId);
|
||||
entry.lastUsed = Date.now();
|
||||
entry.messageCount++;
|
||||
return {
|
||||
session: { uuid: entry.uuid, resume: true },
|
||||
isNew: false,
|
||||
isOneOff: false,
|
||||
};
|
||||
}
|
||||
|
||||
// New session
|
||||
const uuid = randomUUID();
|
||||
this._sessions.set(conversationId, {
|
||||
uuid,
|
||||
model,
|
||||
agent,
|
||||
messageCount: 1,
|
||||
lastUsed: Date.now(),
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
return {
|
||||
session: { uuid, resume: false },
|
||||
isNew: true,
|
||||
isOneOff: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session (e.g. on resume failure).
|
||||
*/
|
||||
delete(conversationId) {
|
||||
this._sessions.delete(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all sessions.
|
||||
* @returns {number} Number of sessions cleared
|
||||
*/
|
||||
clear() {
|
||||
const count = this._sessions.size;
|
||||
this._sessions.clear();
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all active sessions (for /sessions endpoint).
|
||||
*/
|
||||
list() {
|
||||
const result = [];
|
||||
for (const [id, s] of this._sessions) {
|
||||
result.push({
|
||||
id,
|
||||
uuid: s.uuid,
|
||||
model: s.model,
|
||||
agent: s.agent,
|
||||
messages: s.messageCount,
|
||||
lastUsed: new Date(s.lastUsed).toISOString(),
|
||||
idleMs: Date.now() - s.lastUsed,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown — clear interval.
|
||||
*/
|
||||
shutdown() {
|
||||
clearInterval(this._cleanupInterval);
|
||||
}
|
||||
|
||||
// ── Internal ──────────────────────────────────────────────────────────
|
||||
|
||||
_cleanup() {
|
||||
const now = Date.now();
|
||||
for (const [id, s] of this._sessions) {
|
||||
if (now - s.lastUsed > this._ttl) {
|
||||
this._sessions.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user