feat: MetonaAI Desktop 初始项目
- Electron + React + TypeScript 架构 - 三栏布局: Sidebar | ChatPanel | DetailPanel - 9 个内置工具 (文件系统/网络/记忆/命令) - SQLite 持久化 (better-sqlite3) - MUI 暗色/亮色主题系统 - Agent Loop ReAct 状态机引擎 - DeepSeek / Agnes AI / Ollama Provider 适配器 - MCP 协议集成 - 系统托盘 + 全局快捷键 - Tailwind CSS v4 + Tailwind Merge - 修复: Sidebar 缺失 TextField 导入导致黑屏
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Provider Adapter — 基类
|
||||
*
|
||||
* 所有 Provider 适配器共享的基类逻辑:
|
||||
* - 请求超时处理
|
||||
* - 错误映射到 MetonaError
|
||||
* - 流式事件标准化
|
||||
*/
|
||||
|
||||
import type {
|
||||
IMetonaProviderAdapter,
|
||||
AdapterConfig,
|
||||
MetonaRequest,
|
||||
MetonaResponse,
|
||||
MetonaStreamEvent,
|
||||
MetonaError,
|
||||
} from '../types';
|
||||
import { MetonaErrorCode } from '../types';
|
||||
|
||||
export abstract class BaseAdapter implements IMetonaProviderAdapter {
|
||||
abstract readonly provider: string;
|
||||
abstract readonly supportedModels: string[];
|
||||
abstract readonly supportsToolCalling: boolean;
|
||||
abstract readonly supportsThinking: boolean;
|
||||
|
||||
constructor(protected config: AdapterConfig) {}
|
||||
|
||||
abstract chat(request: MetonaRequest): Promise<MetonaResponse>;
|
||||
abstract chatStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent>;
|
||||
|
||||
async healthCheck(): Promise<boolean> {
|
||||
try {
|
||||
await this.listModels?.();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async listModels(): Promise<string[]> {
|
||||
return this.supportedModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将原生错误映射为 MetonaError
|
||||
*/
|
||||
protected mapError(error: unknown): MetonaError {
|
||||
if (error instanceof Error) {
|
||||
const msg = error.message.toLowerCase();
|
||||
|
||||
if (msg.includes('timeout') || msg.includes('ETIMEDOUT')) {
|
||||
return {
|
||||
code: MetonaErrorCode.NETWORK_TIMEOUT,
|
||||
message: error.message,
|
||||
provider: this.provider,
|
||||
retryable: true,
|
||||
retryAfterMs: 3000,
|
||||
};
|
||||
}
|
||||
|
||||
if (msg.includes('401') || msg.includes('unauthorized')) {
|
||||
return {
|
||||
code: MetonaErrorCode.AUTH_INVALID,
|
||||
message: 'API key 无效或已过期',
|
||||
provider: this.provider,
|
||||
retryable: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (msg.includes('429') || msg.includes('rate limit')) {
|
||||
return {
|
||||
code: MetonaErrorCode.RATE_LIMITED,
|
||||
message: '请求过于频繁,请稍后重试',
|
||||
provider: this.provider,
|
||||
retryable: true,
|
||||
retryAfterMs: 5000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: MetonaErrorCode.UNKNOWN,
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
provider: this.provider,
|
||||
retryable: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user