zhizhi/exe-engine/src/adapters/base-adapter.js

134 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// exe-engine/src/adapters/base-adapter.js
// EXE-Engine · 模型适配器基类
// 统一抽象 EXEModelAdapter 接口
// PRJ-EXE-001 · Phase 0
// 版权:国作登字-2026-A-00037559
'use strict';
/**
* EXEModelAdapter 基类
*
* 所有模型适配器DeepSeek、Qwen 等)均继承此基类,
* 统一暴露 execute() 和 healthCheck() 接口。
*
* 本体论锚定:适配器 = 墨水瓶的接口。
* 不同品牌的墨水瓶,瓶口规格不同,但笔尖只认一种接口。
*/
class BaseAdapter {
/**
* @param {object} config
* @param {string} config.name 模型标识
* @param {string} config.provider 供应商标识
* @param {string} config.endpoint API 端点
* @param {string} config.apiKey API 密钥
* @param {string[]} config.capabilities 能力标签
* @param {object} config.costPerToken 成本 { input, output, currency, unit }
* @param {number} config.maxContext 最大上下文窗口
* @param {number} config.defaultTemperature 默认温度
* @param {number} config.defaultMaxTokens 默认最大 token
*/
constructor(config) {
if (new.target === BaseAdapter) {
throw new Error('BaseAdapter 不可直接实例化,请使用具体子类');
}
this.name = config.name;
this.provider = config.provider;
this.endpoint = config.endpoint;
this.apiKey = config.apiKey;
this.capabilities = config.capabilities || [];
this.costPerToken = config.costPerToken || { input: 0, output: 0 };
this.maxContext = config.maxContext || 32000;
this.defaultTemperature = config.defaultTemperature || 0.7;
this.defaultMaxTokens = config.defaultMaxTokens || 4096;
// 运行时状态
this._healthy = true;
this._lastHealthCheck = null;
this._failureCount = 0;
this._cooldownUntil = null;
}
/**
* 执行推理请求(子类必须实现)
* @param {object} request EXE 标准请求
* @returns {Promise<object>} EXE 标准响应
*/
async execute(request) {
throw new Error('子类必须实现 execute() 方法');
}
/**
* 健康检查(子类可覆写)
* @returns {Promise<boolean>}
*/
async healthCheck() {
this._lastHealthCheck = new Date().toISOString();
return this._healthy;
}
/**
* 检测该适配器是否支持指定能力
* @param {string} capability
* @returns {boolean}
*/
supports(capability) {
return this.capabilities.includes(capability);
}
/**
* 记录失败并判断是否进入冷却期
* @param {string} reason
*/
recordFailure(reason) {
this._failureCount++;
if (this._failureCount >= 3) {
// 连续 3 次失败,冷却 5 分钟
this._cooldownUntil = Date.now() + 5 * 60 * 1000;
this._healthy = false;
}
}
/**
* 重置失败计数
*/
resetFailures() {
this._failureCount = 0;
this._cooldownUntil = null;
this._healthy = true;
}
/**
* 是否处于冷却期
* @returns {boolean}
*/
isInCooldown() {
if (!this._cooldownUntil) return false;
if (Date.now() >= this._cooldownUntil) {
this.resetFailures();
return false;
}
return true;
}
/**
* 获取适配器状态摘要
* @returns {object}
*/
getStatus() {
return {
name: this.name,
provider: this.provider,
healthy: this._healthy,
inCooldown: this.isInCooldown(),
failureCount: this._failureCount,
lastHealthCheck: this._lastHealthCheck,
capabilities: this.capabilities,
costPerToken: this.costPerToken,
maxContext: this.maxContext
};
}
}
module.exports = BaseAdapter;