@huyooo/ai-chat-core 0.2.44 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/index.d.ts +11 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/model-adapter.d.ts +25 -0
- package/dist/adapter/model-adapter.d.ts.map +1 -0
- package/dist/adapter/model-options.d.ts +53 -0
- package/dist/adapter/model-options.d.ts.map +1 -0
- package/dist/adapter/types.d.ts +28 -0
- package/dist/adapter/types.d.ts.map +1 -0
- package/dist/chat-runtime.d.ts +96 -0
- package/dist/chat-runtime.d.ts.map +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/events.d.ts +605 -1
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +1 -1
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.d.ts.map +1 -0
- package/dist/extension/types.d.ts +46 -0
- package/dist/extension/types.d.ts.map +1 -0
- package/dist/families/index.d.ts +11 -0
- package/dist/families/index.d.ts.map +1 -0
- package/dist/families/presets.d.ts +31 -0
- package/dist/families/presets.d.ts.map +1 -0
- package/dist/families/resolver.d.ts +11 -0
- package/dist/families/resolver.d.ts.map +1 -0
- package/dist/families/types.d.ts +29 -0
- package/dist/families/types.d.ts.map +1 -0
- package/dist/governance/command-safety.d.ts +34 -0
- package/dist/governance/command-safety.d.ts.map +1 -0
- package/dist/governance/governance.d.ts +19 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/index.d.ts +12 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/types.d.ts +29 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/index.d.ts +72 -804
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -1
- package/dist/internal/management-args.d.ts +13 -0
- package/dist/internal/management-args.d.ts.map +1 -0
- package/dist/internal/management-results.d.ts +21 -0
- package/dist/internal/management-results.d.ts.map +1 -0
- package/dist/llm-config.d.ts +108 -0
- package/dist/llm-config.d.ts.map +1 -0
- package/dist/logger/core.d.ts +31 -0
- package/dist/logger/core.d.ts.map +1 -0
- package/dist/logger/index.d.ts +9 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/orchestrator/compression-handler.d.ts +29 -0
- package/dist/orchestrator/compression-handler.d.ts.map +1 -0
- package/dist/orchestrator/context-compressor.d.ts +51 -0
- package/dist/orchestrator/context-compressor.d.ts.map +1 -0
- package/dist/orchestrator/context-summarizer.d.ts +41 -0
- package/dist/orchestrator/context-summarizer.d.ts.map +1 -0
- package/dist/orchestrator/index.d.ts +12 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +46 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/types.d.ts +58 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/parts/index.d.ts +13 -0
- package/dist/parts/index.d.ts.map +1 -0
- package/dist/parts/registry.d.ts +11 -0
- package/dist/parts/registry.d.ts.map +1 -0
- package/dist/parts/summaries.d.ts +9 -0
- package/dist/parts/summaries.d.ts.map +1 -0
- package/dist/parts/types.d.ts +61 -0
- package/dist/parts/types.d.ts.map +1 -0
- package/dist/platform.d.ts +17 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +1 -0
- package/dist/protocols/anthropic.d.ts +20 -0
- package/dist/protocols/anthropic.d.ts.map +1 -0
- package/dist/protocols/ark.d.ts +36 -0
- package/dist/protocols/ark.d.ts.map +1 -0
- package/dist/protocols/deepseek.d.ts +24 -0
- package/dist/protocols/deepseek.d.ts.map +1 -0
- package/dist/protocols/error-utils.d.ts +14 -0
- package/dist/protocols/error-utils.d.ts.map +1 -0
- package/dist/protocols/gemini.d.ts +24 -0
- package/dist/protocols/gemini.d.ts.map +1 -0
- package/dist/protocols/glm.d.ts +20 -0
- package/dist/protocols/glm.d.ts.map +1 -0
- package/dist/protocols/grok.d.ts +20 -0
- package/dist/protocols/grok.d.ts.map +1 -0
- package/dist/protocols/index.d.ts +31 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/minimax.d.ts +38 -0
- package/dist/protocols/minimax.d.ts.map +1 -0
- package/dist/protocols/moonshot.d.ts +20 -0
- package/dist/protocols/moonshot.d.ts.map +1 -0
- package/dist/protocols/openai-sse.d.ts +33 -0
- package/dist/protocols/openai-sse.d.ts.map +1 -0
- package/dist/protocols/openai.d.ts +19 -0
- package/dist/protocols/openai.d.ts.map +1 -0
- package/dist/protocols/qwen.d.ts +26 -0
- package/dist/protocols/qwen.d.ts.map +1 -0
- package/dist/protocols/responses-sse.d.ts +30 -0
- package/dist/protocols/responses-sse.d.ts.map +1 -0
- package/dist/protocols/sse-reader.d.ts +23 -0
- package/dist/protocols/sse-reader.d.ts.map +1 -0
- package/dist/protocols/tool-arguments.d.ts +8 -0
- package/dist/protocols/tool-arguments.d.ts.map +1 -0
- package/dist/protocols/types.d.ts +148 -0
- package/dist/protocols/types.d.ts.map +1 -0
- package/dist/protocols/vercel-gateway.d.ts +15 -0
- package/dist/protocols/vercel-gateway.d.ts.map +1 -0
- package/dist/runtime.d.ts +151 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/skills/index.d.ts +14 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/management/admin.d.ts +10 -0
- package/dist/skills/management/admin.d.ts.map +1 -0
- package/dist/skills/management/index.d.ts +11 -0
- package/dist/skills/management/index.d.ts.map +1 -0
- package/dist/skills/management/inputs.d.ts +44 -0
- package/dist/skills/management/inputs.d.ts.map +1 -0
- package/dist/skills/management/operations.d.ts +78 -0
- package/dist/skills/management/operations.d.ts.map +1 -0
- package/dist/skills/management/types.d.ts +70 -0
- package/dist/skills/management/types.d.ts.map +1 -0
- package/dist/skills/registry.d.ts +37 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/summaries.d.ts +9 -0
- package/dist/skills/summaries.d.ts.map +1 -0
- package/dist/skills/types.d.ts +61 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/test-utils/mock-sse.d.ts +13 -0
- package/dist/test-utils/mock-sse.d.ts.map +1 -0
- package/dist/tool-manager/define-tool.d.ts +35 -0
- package/dist/tool-manager/define-tool.d.ts.map +1 -0
- package/dist/tool-manager/formats.d.ts +46 -0
- package/dist/tool-manager/formats.d.ts.map +1 -0
- package/dist/tool-manager/identity.d.ts +18 -0
- package/dist/tool-manager/identity.d.ts.map +1 -0
- package/dist/tool-manager/in-process-provider.d.ts +15 -0
- package/dist/tool-manager/in-process-provider.d.ts.map +1 -0
- package/dist/tool-manager/index.d.ts +18 -0
- package/dist/tool-manager/index.d.ts.map +1 -0
- package/dist/tool-manager/manager.d.ts +18 -0
- package/dist/tool-manager/manager.d.ts.map +1 -0
- package/dist/tool-manager/mcp-provider.d.ts +21 -0
- package/dist/tool-manager/mcp-provider.d.ts.map +1 -0
- package/dist/tool-manager/summaries.d.ts +39 -0
- package/dist/tool-manager/summaries.d.ts.map +1 -0
- package/dist/tool-manager/types.d.ts +314 -0
- package/dist/tool-manager/types.d.ts.map +1 -0
- package/dist/types.d.ts +663 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +26 -15
- package/src/adapter/index.ts +25 -0
- package/src/adapter/model-adapter.ts +196 -0
- package/src/adapter/model-options.ts +143 -0
- package/src/adapter/types.ts +41 -0
- package/src/chat-runtime.ts +515 -0
- package/src/constants.ts +9 -102
- package/src/events.ts +364 -150
- package/src/extension/index.ts +24 -0
- package/src/extension/types.ts +49 -0
- package/src/families/index.ts +28 -0
- package/src/families/presets.ts +124 -0
- package/src/families/resolver.ts +22 -0
- package/src/families/types.ts +55 -0
- package/src/governance/command-safety.ts +224 -0
- package/src/governance/governance.ts +125 -0
- package/src/governance/index.ts +38 -0
- package/src/governance/types.ts +44 -0
- package/src/index.ts +250 -145
- package/src/internal/management-args.ts +39 -0
- package/src/internal/management-results.ts +60 -0
- package/src/llm-config.ts +137 -0
- package/src/logger/core.ts +96 -0
- package/src/logger/index.ts +8 -0
- package/src/orchestrator/compression-handler.ts +137 -0
- package/src/{providers → orchestrator}/context-compressor.ts +79 -47
- package/src/orchestrator/context-summarizer.ts +123 -0
- package/src/orchestrator/index.ts +20 -0
- package/src/orchestrator/orchestrator.ts +1002 -0
- package/src/orchestrator/types.ts +70 -0
- package/src/parts/index.ts +20 -0
- package/src/parts/registry.ts +95 -0
- package/src/parts/summaries.ts +40 -0
- package/src/parts/types.ts +63 -0
- package/src/platform.ts +73 -0
- package/src/protocols/anthropic.ts +377 -0
- package/src/protocols/ark.ts +300 -0
- package/src/protocols/deepseek.ts +192 -0
- package/src/{providers/protocols → protocols}/error-utils.ts +17 -20
- package/src/protocols/gemini.ts +352 -0
- package/src/protocols/glm.ts +212 -0
- package/src/protocols/grok.ts +98 -0
- package/src/protocols/index.ts +48 -0
- package/src/protocols/minimax.ts +308 -0
- package/src/protocols/moonshot.ts +186 -0
- package/src/protocols/openai-sse.ts +156 -0
- package/src/protocols/openai.ts +97 -0
- package/src/protocols/qwen.ts +358 -0
- package/src/protocols/responses-sse.ts +224 -0
- package/src/protocols/sse-reader.ts +54 -0
- package/src/protocols/tool-arguments.ts +32 -0
- package/src/{providers/protocols → protocols}/types.ts +46 -37
- package/src/protocols/vercel-gateway.ts +391 -0
- package/src/runtime.ts +167 -0
- package/src/skills/index.ts +29 -0
- package/src/skills/management/admin.ts +170 -0
- package/src/skills/management/index.ts +27 -0
- package/src/skills/management/inputs.ts +79 -0
- package/src/skills/management/operations.ts +256 -0
- package/src/skills/management/types.ts +57 -0
- package/src/skills/registry.ts +120 -0
- package/src/skills/summaries.ts +48 -0
- package/src/skills/types.ts +65 -0
- package/src/test-utils/mock-sse.ts +3 -3
- package/src/tool-manager/define-tool.ts +201 -0
- package/src/tool-manager/formats.ts +146 -0
- package/src/tool-manager/identity.ts +80 -0
- package/src/tool-manager/in-process-provider.ts +164 -0
- package/src/tool-manager/index.ts +63 -0
- package/src/tool-manager/manager.ts +562 -0
- package/src/tool-manager/mcp-provider.ts +509 -0
- package/src/tool-manager/summaries.ts +136 -0
- package/src/tool-manager/types.ts +389 -0
- package/src/types.ts +750 -191
- package/dist/events-CU5D5ray.d.ts +0 -1128
- package/src/agent.ts +0 -409
- package/src/internal/update-plan.ts +0 -2
- package/src/internal/web-search.ts +0 -77
- package/src/mcp/client-manager.ts +0 -302
- package/src/mcp/index.ts +0 -2
- package/src/mcp/types.ts +0 -43
- package/src/providers/context-summarizer.ts +0 -70
- package/src/providers/index.ts +0 -125
- package/src/providers/model-registry.ts +0 -466
- package/src/providers/orchestrator.ts +0 -839
- package/src/providers/protocols/anthropic.ts +0 -406
- package/src/providers/protocols/ark.ts +0 -362
- package/src/providers/protocols/deepseek.ts +0 -344
- package/src/providers/protocols/gemini.ts +0 -350
- package/src/providers/protocols/index.ts +0 -36
- package/src/providers/protocols/openai.ts +0 -420
- package/src/providers/protocols/qwen.ts +0 -315
- package/src/providers/types.ts +0 -264
- package/src/providers/unified-adapter.ts +0 -367
- package/src/router.ts +0 -72
- package/src/tools.ts +0 -162
- package/src/utils.ts +0 -86
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-chat-core",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "AI Chat Core - HybridAgent with Doubao + Gemini",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,10 +17,20 @@
|
|
|
17
17
|
"import": "./dist/events.js",
|
|
18
18
|
"default": "./dist/events.js"
|
|
19
19
|
},
|
|
20
|
-
"./
|
|
21
|
-
"types": "./dist/
|
|
22
|
-
"import": "./dist/
|
|
23
|
-
"default": "./dist/
|
|
20
|
+
"./tool-manager": {
|
|
21
|
+
"types": "./dist/tool-manager/index.d.ts",
|
|
22
|
+
"import": "./dist/tool-manager/index.js",
|
|
23
|
+
"default": "./dist/tool-manager/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./runtime": {
|
|
26
|
+
"types": "./dist/runtime.d.ts",
|
|
27
|
+
"import": "./dist/runtime.js",
|
|
28
|
+
"default": "./dist/runtime.js"
|
|
29
|
+
},
|
|
30
|
+
"./platform": {
|
|
31
|
+
"types": "./dist/platform.d.ts",
|
|
32
|
+
"import": "./dist/platform.js",
|
|
33
|
+
"default": "./dist/platform.js"
|
|
24
34
|
}
|
|
25
35
|
},
|
|
26
36
|
"files": [
|
|
@@ -28,25 +38,26 @@
|
|
|
28
38
|
"src"
|
|
29
39
|
],
|
|
30
40
|
"scripts": {
|
|
31
|
-
"build": "
|
|
32
|
-
"
|
|
41
|
+
"build": "npm run clean && npm run build:js && npm run build:types",
|
|
42
|
+
"build:js": "tsdown",
|
|
43
|
+
"build:types": "tsc -b tsconfig.json --emitDeclarationOnly",
|
|
44
|
+
"dev": "tsdown --watch",
|
|
33
45
|
"test": "vitest run",
|
|
46
|
+
"test:live": "vitest run --config vitest.live.config.ts",
|
|
47
|
+
"test:all": "npm run test && npm run test:live",
|
|
34
48
|
"test:watch": "vitest",
|
|
35
49
|
"typecheck": "tsc --noEmit",
|
|
36
|
-
"clean": "rm -rf dist"
|
|
50
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
37
51
|
},
|
|
38
52
|
"dependencies": {
|
|
39
|
-
"@ai-sdk/anthropic": "^3.0.1",
|
|
40
|
-
"@ai-sdk/openai": "^1.0.0",
|
|
41
|
-
"@google/genai": "^1.0.0",
|
|
42
53
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
54
|
+
"@sinclair/typebox": "^0.34.48",
|
|
55
|
+
"pino": "^10.3.1",
|
|
56
|
+
"pino-pretty": "^13.1.3"
|
|
46
57
|
},
|
|
47
58
|
"devDependencies": {
|
|
48
59
|
"@types/node": "^22.0.0",
|
|
49
|
-
"
|
|
60
|
+
"tsdown": "^0.21.0",
|
|
50
61
|
"typescript": "^5.0.0",
|
|
51
62
|
"vitest": "^4.0.18"
|
|
52
63
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter 模块导出(barrel)
|
|
3
|
+
*
|
|
4
|
+
* - ModelAdapter:按 LLMConfig 路由协议、流式消费 RawEvent
|
|
5
|
+
* - model-options:从前端 LLMConfig 构建模型列表与上下文展示配置
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { ModelAdapter } from './model-adapter';
|
|
9
|
+
export {
|
|
10
|
+
buildModelOptions,
|
|
11
|
+
getModelContextConfigFromLLM,
|
|
12
|
+
formatContextWindow,
|
|
13
|
+
formatPricing,
|
|
14
|
+
} from './model-options';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
ModelOption,
|
|
18
|
+
ModelContextConfig,
|
|
19
|
+
} from './model-options';
|
|
20
|
+
|
|
21
|
+
export type {
|
|
22
|
+
ProviderAdapter,
|
|
23
|
+
AdapterConfig,
|
|
24
|
+
StreamOnceOptions,
|
|
25
|
+
} from './types';
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModelAdapter - 模型适配器(Model 中心)
|
|
3
|
+
*
|
|
4
|
+
* 直接使用 LLMConfig.models 的 ModelRoute 链,按顺序尝试,支持降级。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
Protocol,
|
|
9
|
+
ProtocolConfig,
|
|
10
|
+
ProtocolMessage,
|
|
11
|
+
ProtocolToolDefinition,
|
|
12
|
+
RawEvent,
|
|
13
|
+
} from '../protocols/types';
|
|
14
|
+
import { createArkProtocol } from '../protocols/ark';
|
|
15
|
+
import { createDeepSeekProtocol } from '../protocols/deepseek';
|
|
16
|
+
import { createQwenProtocol } from '../protocols/qwen';
|
|
17
|
+
import { createGlmProtocol } from '../protocols/glm';
|
|
18
|
+
import { createMoonshotProtocol } from '../protocols/moonshot';
|
|
19
|
+
import { createMiniMaxProtocol } from '../protocols/minimax';
|
|
20
|
+
import { createVercelGatewayProtocol } from '../protocols/vercel-gateway';
|
|
21
|
+
import { createAnthropicProtocol } from '../protocols/anthropic';
|
|
22
|
+
import { createGeminiProtocol } from '../protocols/gemini';
|
|
23
|
+
import { createOpenAIProtocol } from '../protocols/openai';
|
|
24
|
+
import { createGrokProtocol } from '../protocols/grok';
|
|
25
|
+
import {
|
|
26
|
+
resolveModelFamilyConfig,
|
|
27
|
+
type ModelFamilyConfig,
|
|
28
|
+
} from '../families';
|
|
29
|
+
import type { ProviderAdapter, StreamOnceOptions } from './types';
|
|
30
|
+
import type { LLMConfig, ModelRoute } from '../llm-config';
|
|
31
|
+
import { getRouteChain, resolveRouteUrl } from '../llm-config';
|
|
32
|
+
import { createModuleLogger } from '../logger';
|
|
33
|
+
|
|
34
|
+
const logger = createModuleLogger('ModelAdapter');
|
|
35
|
+
|
|
36
|
+
/** 内置协议工厂 */
|
|
37
|
+
const BUILTIN_PROTOCOL_FACTORIES: Record<string, (config: ProtocolConfig) => Protocol> = {
|
|
38
|
+
ark_v1: createArkProtocol,
|
|
39
|
+
deepseek_v1: createDeepSeekProtocol,
|
|
40
|
+
qwen_v1: createQwenProtocol,
|
|
41
|
+
glm_v1: createGlmProtocol,
|
|
42
|
+
moonshot_v1: createMoonshotProtocol,
|
|
43
|
+
minimax_v1: createMiniMaxProtocol,
|
|
44
|
+
vercel_gateway_v1: createVercelGatewayProtocol,
|
|
45
|
+
anthropic_v1: createAnthropicProtocol,
|
|
46
|
+
gemini_v1: createGeminiProtocol,
|
|
47
|
+
openai_v1: createOpenAIProtocol,
|
|
48
|
+
grok_v1: createGrokProtocol,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export class ModelAdapter implements ProviderAdapter {
|
|
52
|
+
readonly name = 'model';
|
|
53
|
+
private llmConfig: LLMConfig;
|
|
54
|
+
/** 合并内置 + 自定义协议工厂 */
|
|
55
|
+
private protocolFactories: Record<string, (config: ProtocolConfig) => Protocol>;
|
|
56
|
+
|
|
57
|
+
constructor(config: LLMConfig) {
|
|
58
|
+
this.llmConfig = config;
|
|
59
|
+
this.protocolFactories = config.protocols
|
|
60
|
+
? { ...BUILTIN_PROTOCOL_FACTORIES, ...config.protocols }
|
|
61
|
+
: BUILTIN_PROTOCOL_FACTORIES;
|
|
62
|
+
this.validateModels();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 启动时校验:所有模型的 family 必须可解析,失败立即 throw */
|
|
66
|
+
private validateModels(): void {
|
|
67
|
+
const errors: string[] = [];
|
|
68
|
+
for (const [modelId, modelConfig] of Object.entries(this.llmConfig.models)) {
|
|
69
|
+
if (!modelConfig) continue;
|
|
70
|
+
const familyConfig = resolveModelFamilyConfig(modelId, modelConfig, this.llmConfig);
|
|
71
|
+
if (!familyConfig) {
|
|
72
|
+
errors.push(`模型 ${modelId}: family "${String(modelConfig.family)}" 无法解析,请检查是否在 MODEL_FAMILIES 或 LLMConfig.families 中定义`);
|
|
73
|
+
}
|
|
74
|
+
if (!modelConfig.maxOutputTokens || modelConfig.maxOutputTokens <= 0) {
|
|
75
|
+
errors.push(`模型 ${modelId}: maxOutputTokens 未指定或无效,请为每个模型显式配置`);
|
|
76
|
+
}
|
|
77
|
+
if (!modelConfig.contextWindowTokens || modelConfig.contextWindowTokens <= 0) {
|
|
78
|
+
errors.push(`模型 ${modelId}: contextWindowTokens 未指定或无效,压缩器依赖此值`);
|
|
79
|
+
}
|
|
80
|
+
if (!modelConfig.routes?.length) {
|
|
81
|
+
errors.push(`模型 ${modelId}: routes 为空,至少需要一条路由`);
|
|
82
|
+
}
|
|
83
|
+
for (const route of modelConfig.routes ?? []) {
|
|
84
|
+
if (!this.protocolFactories[route.protocol]) {
|
|
85
|
+
errors.push(`模型 ${modelId}: 协议 "${route.protocol}" 未注册,请检查是否在内置协议或 LLMConfig.protocols 中定义`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (errors.length > 0) {
|
|
90
|
+
throw new Error(`LLMConfig 校验失败:\n${errors.join('\n')}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private createProtocolForRoute(route: ModelRoute): Protocol {
|
|
95
|
+
const apiUrl = resolveRouteUrl(route);
|
|
96
|
+
const apiKey = route.vendorKey ?? route.accessKey;
|
|
97
|
+
const factory = this.protocolFactories[route.protocol];
|
|
98
|
+
if (!factory) throw new Error(`未知协议: ${route.protocol},请在 LLMConfig.protocols 中注册`);
|
|
99
|
+
return factory({ apiKey, apiUrl });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get supportedModels(): string[] {
|
|
103
|
+
return Object.keys(this.llmConfig.models);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
supportsModel(model: string): boolean {
|
|
107
|
+
return getRouteChain(this.llmConfig, model).length > 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getModelFamilyConfig(model: string): ModelFamilyConfig | undefined {
|
|
111
|
+
const modelConfig = this.llmConfig.models[model];
|
|
112
|
+
if (!modelConfig) return undefined;
|
|
113
|
+
return resolveModelFamilyConfig(model, modelConfig, this.llmConfig);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async *streamOnce(
|
|
117
|
+
messages: ProtocolMessage[],
|
|
118
|
+
tools: ProtocolToolDefinition[],
|
|
119
|
+
options: StreamOnceOptions,
|
|
120
|
+
): AsyncGenerator<RawEvent> {
|
|
121
|
+
const filtered = messages.filter(
|
|
122
|
+
m => !(m.role === 'assistant' && !m.content && !m.toolCalls?.length),
|
|
123
|
+
);
|
|
124
|
+
yield* this.streamWithFallback(filtered, tools, options);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private async *streamWithFallback(
|
|
128
|
+
messages: ProtocolMessage[],
|
|
129
|
+
tools: ProtocolToolDefinition[],
|
|
130
|
+
options: StreamOnceOptions,
|
|
131
|
+
): AsyncGenerator<RawEvent> {
|
|
132
|
+
const { model } = options;
|
|
133
|
+
|
|
134
|
+
const modelConfig = this.llmConfig.models[model];
|
|
135
|
+
if (!modelConfig) {
|
|
136
|
+
yield { type: 'error', error: `模型 ${model} 未在 LLMConfig.models 中配置` };
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const familyConfig = resolveModelFamilyConfig(model, modelConfig, this.llmConfig);
|
|
141
|
+
if (!familyConfig) {
|
|
142
|
+
yield { type: 'error', error: `模型 ${model} 无法解析家族配置,请在 ModelConfig.family 或 LLMConfig.families 中指定` };
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const chain = getRouteChain(this.llmConfig, model);
|
|
147
|
+
if (chain.length === 0) {
|
|
148
|
+
yield { type: 'error', error: `模型 ${model} 无可用路径,请检查 LLMConfig.models` };
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let lastError = '';
|
|
153
|
+
|
|
154
|
+
for (let i = 0; i < chain.length; i++) {
|
|
155
|
+
const route = chain[i];
|
|
156
|
+
const isLast = i === chain.length - 1;
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const protocol = this.createProtocolForRoute(route);
|
|
160
|
+
let committed = false;
|
|
161
|
+
|
|
162
|
+
logger.info({ model, protocol: route.protocol, providerModelId: route.providerModelId, attempt: i + 1, total: chain.length }, '请求 LLM');
|
|
163
|
+
|
|
164
|
+
for await (const event of protocol.stream(messages, tools, {
|
|
165
|
+
model: route.providerModelId,
|
|
166
|
+
familyConfig,
|
|
167
|
+
maxOutputTokens: modelConfig.maxOutputTokens,
|
|
168
|
+
enableThinking: options.enableThinking ?? false,
|
|
169
|
+
signal: options.signal,
|
|
170
|
+
})) {
|
|
171
|
+
if (!committed && event.type === 'error' && !isLast) {
|
|
172
|
+
lastError = event.error ?? 'unknown';
|
|
173
|
+
logger.warn({ protocol: route.protocol, error: lastError }, '路径返回错误,降级');
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!committed && event.type !== 'error') committed = true;
|
|
178
|
+
yield event;
|
|
179
|
+
// 不在 done 时提前 return:否则内层 protocol.stream 的 async generator
|
|
180
|
+
// 在最后一 yield 之后的收尾(如 GlmProtocol 空流诊断)不会执行。
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (committed) return;
|
|
184
|
+
} catch (err) {
|
|
185
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
186
|
+
if (isLast) {
|
|
187
|
+
yield { type: 'error', error: lastError };
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
logger.warn({ protocol: route.protocol, error: lastError }, '路径异常,尝试降级');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
yield { type: 'error', error: `所有路径均失败: ${lastError}` };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型选项与上下文展示配置
|
|
3
|
+
*
|
|
4
|
+
* - 从 LLMConfig 推导前端下拉模型列表(capabilities、定价文案、上下文窗口)
|
|
5
|
+
* - formatPricing / formatContextWindow:人类可读展示
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { LLMConfig, ModelConfig, ModelPricing } from '../llm-config';
|
|
9
|
+
import { resolveModelFamilyConfig, type ModelFamilyConfig } from '../families';
|
|
10
|
+
|
|
11
|
+
// ==================== 工具函数 ====================
|
|
12
|
+
|
|
13
|
+
/** 将 ModelPricing 格式化为展示用字符串数组:["¥2/M 输入", "¥3/M 输出"] */
|
|
14
|
+
export function formatPricing(pricing: ModelPricing): string[] {
|
|
15
|
+
const sym = pricing.currency === 'CNY' ? '¥' : '$';
|
|
16
|
+
const fmtNum = (n: number) => Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/0+$/, '').replace(/\.$/, '');
|
|
17
|
+
const result: string[] = [];
|
|
18
|
+
|
|
19
|
+
result.push(`${sym}${fmtNum(pricing.input)}/M 输入`);
|
|
20
|
+
|
|
21
|
+
if (Array.isArray(pricing.output)) {
|
|
22
|
+
result.push(`${sym}${fmtNum(pricing.output[0])}~${fmtNum(pricing.output[1])}/M 输出`);
|
|
23
|
+
} else {
|
|
24
|
+
result.push(`${sym}${fmtNum(pricing.output)}/M 输出`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (pricing.cached != null) {
|
|
28
|
+
result.push(`${sym}${fmtNum(pricing.cached)}/M 缓存`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 将 token 数自动推导为人类可读格式:"256K" / "1M" / "10M" */
|
|
35
|
+
export function formatContextWindow(tokens: number): string {
|
|
36
|
+
if (tokens >= 1_000_000 && tokens % 1_000_000 === 0) {
|
|
37
|
+
return `${tokens / 1_000_000}M`;
|
|
38
|
+
}
|
|
39
|
+
if (tokens >= 1_000_000) {
|
|
40
|
+
const m = tokens / 1_000_000;
|
|
41
|
+
return Number.isInteger(m) ? `${m}M` : `${parseFloat(m.toFixed(1))}M`;
|
|
42
|
+
}
|
|
43
|
+
const k = tokens / 1_000;
|
|
44
|
+
return Number.isInteger(k) ? `${k}K` : `${parseFloat(k.toFixed(1))}K`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ==================== ModelOption(前端显示用) ====================
|
|
48
|
+
|
|
49
|
+
export interface ModelOption {
|
|
50
|
+
modelId: string;
|
|
51
|
+
displayName: string;
|
|
52
|
+
supportsThinking: boolean;
|
|
53
|
+
thinkingAlwaysOn?: boolean;
|
|
54
|
+
supportsVision: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 模型上下文窗口(token)。
|
|
57
|
+
* 前端据此为 @ 资源区块、历史消息等分配 token 预算,避免拍脑袋硬编码。
|
|
58
|
+
* 与 {@link ModelContextConfig.contextWindowTokens} 来自同一 ModelConfig 字段。
|
|
59
|
+
*/
|
|
60
|
+
contextWindowTokens: number;
|
|
61
|
+
/**
|
|
62
|
+
* 单次生成允许的最大输出 token。
|
|
63
|
+
* 前端在估算可用 prompt 预算时需要从 context window 中扣除这部分。
|
|
64
|
+
*/
|
|
65
|
+
maxOutputTokens: number;
|
|
66
|
+
tooltip?: {
|
|
67
|
+
features?: string[];
|
|
68
|
+
cost?: string[];
|
|
69
|
+
description?: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 从 LLMConfig 动态构建前端模型列表
|
|
75
|
+
*
|
|
76
|
+
* 只返回 visible !== false 的模型。
|
|
77
|
+
* 所有元数据只从 ModelConfig + familyConfig 取,不查预设库。
|
|
78
|
+
*/
|
|
79
|
+
export function buildModelOptions(config: LLMConfig): ModelOption[] {
|
|
80
|
+
const result: ModelOption[] = [];
|
|
81
|
+
|
|
82
|
+
for (const [modelId, modelConfig] of Object.entries(config.models)) {
|
|
83
|
+
if (!modelConfig || modelConfig.visible === false) continue;
|
|
84
|
+
|
|
85
|
+
const familyConfig = resolveModelFamilyConfig(modelId, modelConfig, config);
|
|
86
|
+
|
|
87
|
+
const displayName = modelConfig.displayName ?? modelId;
|
|
88
|
+
const supportsThinking = modelConfig.supportsThinking;
|
|
89
|
+
const supportsVision = modelConfig.supportsVision;
|
|
90
|
+
const thinkingAlwaysOn = familyConfig?.thinkingAlwaysOn ?? false;
|
|
91
|
+
|
|
92
|
+
const features: string[] = [];
|
|
93
|
+
if (supportsVision) features.push('多模态');
|
|
94
|
+
if (supportsThinking) {
|
|
95
|
+
features.push(thinkingAlwaysOn ? '深度思考(始终开启)' : '深度思考');
|
|
96
|
+
}
|
|
97
|
+
if (modelConfig.contextWindowTokens) {
|
|
98
|
+
features.push(`长上下文(${formatContextWindow(modelConfig.contextWindowTokens)})`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
result.push({
|
|
102
|
+
modelId,
|
|
103
|
+
displayName,
|
|
104
|
+
supportsThinking,
|
|
105
|
+
thinkingAlwaysOn,
|
|
106
|
+
supportsVision,
|
|
107
|
+
contextWindowTokens: modelConfig.contextWindowTokens,
|
|
108
|
+
maxOutputTokens: modelConfig.maxOutputTokens,
|
|
109
|
+
tooltip: {
|
|
110
|
+
features: features.length > 0 ? features : undefined,
|
|
111
|
+
cost: modelConfig.pricing ? formatPricing(modelConfig.pricing) : undefined,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ==================== ModelContextConfig(运行时 LLMConfig 感知) ====================
|
|
120
|
+
|
|
121
|
+
export interface ModelContextConfig {
|
|
122
|
+
contextWindowTokens: number;
|
|
123
|
+
maxOutputTokens: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 获取模型的 context 配置(从 LLMConfig 感知)
|
|
128
|
+
*
|
|
129
|
+
* 只从 ModelConfig 取,不做注册表 fallback。
|
|
130
|
+
* contextWindowTokens 和 maxOutputTokens 均为必填字段。
|
|
131
|
+
*/
|
|
132
|
+
export function getModelContextConfigFromLLM(
|
|
133
|
+
modelId: string,
|
|
134
|
+
config: LLMConfig,
|
|
135
|
+
): ModelContextConfig | undefined {
|
|
136
|
+
const modelConfig = config.models[modelId];
|
|
137
|
+
if (!modelConfig) return undefined;
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
contextWindowTokens: modelConfig.contextWindowTokens,
|
|
141
|
+
maxOutputTokens: modelConfig.maxOutputTokens,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter 层类型定义
|
|
3
|
+
*
|
|
4
|
+
* 仅包含适配器接口和配置,不含编排器类型。
|
|
5
|
+
* 编排器类型定义在 orchestrator/types.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
RawEvent,
|
|
10
|
+
ProtocolMessage,
|
|
11
|
+
ProtocolToolDefinition,
|
|
12
|
+
} from '../protocols/types';
|
|
13
|
+
|
|
14
|
+
export interface AdapterConfig {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
apiUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface StreamOnceOptions {
|
|
20
|
+
model: string;
|
|
21
|
+
enableThinking?: boolean;
|
|
22
|
+
signal: AbortSignal;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Provider Adapter 接口
|
|
27
|
+
*
|
|
28
|
+
* 职责:协议路由 + 消息格式转换 + 返回 RawEvent 流
|
|
29
|
+
*/
|
|
30
|
+
export interface ProviderAdapter {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly supportedModels: string[];
|
|
33
|
+
|
|
34
|
+
streamOnce(
|
|
35
|
+
messages: ProtocolMessage[],
|
|
36
|
+
tools: ProtocolToolDefinition[],
|
|
37
|
+
options: StreamOnceOptions
|
|
38
|
+
): AsyncGenerator<RawEvent>;
|
|
39
|
+
|
|
40
|
+
supportsModel(model: string): boolean;
|
|
41
|
+
}
|