@next-open-ai/openclawx 0.8.40 → 0.8.58

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.
Files changed (48) hide show
  1. package/README.md +10 -0
  2. package/apps/desktop/renderer/dist/assets/index-M5VGUUpo.js +93 -0
  3. package/apps/desktop/renderer/dist/assets/index-y8oE2q_u.css +10 -0
  4. package/apps/desktop/renderer/dist/index.html +2 -2
  5. package/dist/cli/cli.js +107 -0
  6. package/dist/core/agent/agent-manager.js +13 -2
  7. package/dist/core/agent/proxy/adapters/local-adapter.js +1 -1
  8. package/dist/core/config/desktop-config.d.ts +4 -1
  9. package/dist/core/config/desktop-config.js +108 -21
  10. package/dist/core/config/provider-support-default.js +26 -0
  11. package/dist/core/local-llm-server/download-model.d.ts +16 -0
  12. package/dist/core/local-llm-server/download-model.js +37 -0
  13. package/dist/core/local-llm-server/index.d.ts +32 -0
  14. package/dist/core/local-llm-server/index.js +147 -0
  15. package/dist/core/local-llm-server/llm-context.d.ts +65 -0
  16. package/dist/core/local-llm-server/llm-context.js +242 -0
  17. package/dist/core/local-llm-server/model-resolve.d.ts +27 -0
  18. package/dist/core/local-llm-server/model-resolve.js +90 -0
  19. package/dist/core/local-llm-server/server.d.ts +1 -0
  20. package/dist/core/local-llm-server/server.js +234 -0
  21. package/dist/core/local-llm-server/start-from-config.d.ts +5 -0
  22. package/dist/core/local-llm-server/start-from-config.js +50 -0
  23. package/dist/core/mcp/transport/stdio.d.ts +6 -0
  24. package/dist/core/mcp/transport/stdio.js +107 -27
  25. package/dist/core/memory/local-embedding-llama.js +2 -4
  26. package/dist/core/memory/local-embedding.d.ts +4 -3
  27. package/dist/core/memory/local-embedding.js +43 -3
  28. package/dist/gateway/methods/agent-chat.js +80 -41
  29. package/dist/gateway/server.js +10 -0
  30. package/dist/server/agent-config/agent-config.controller.d.ts +1 -1
  31. package/dist/server/agent-config/agent-config.service.d.ts +2 -0
  32. package/dist/server/agent-config/agent-config.service.js +5 -0
  33. package/dist/server/bootstrap.d.ts +1 -0
  34. package/dist/server/bootstrap.js +3 -0
  35. package/dist/server/config/config.controller.d.ts +81 -4
  36. package/dist/server/config/config.controller.js +185 -3
  37. package/dist/server/config/config.module.js +3 -2
  38. package/dist/server/config/config.service.d.ts +4 -1
  39. package/dist/server/config/config.service.js +62 -9
  40. package/dist/server/config/local-models.service.d.ts +67 -0
  41. package/dist/server/config/local-models.service.js +243 -0
  42. package/package.json +1 -1
  43. package/presets/preset-agents.json +6 -2
  44. package/presets/preset-config.json +24 -6
  45. package/presets/recommended-local-models.json +42 -0
  46. package/apps/desktop/renderer/dist/assets/index-BSfTiTKo.css +0 -10
  47. package/apps/desktop/renderer/dist/assets/index-DgLpQsA-.js +0 -89
  48. package/presets/workspaces/finance-expert/skills/akshare-helper/SKILL.md +0 -9
@@ -1,7 +1,9 @@
1
1
  import { ConfigService, AppConfig } from './config.service.js';
2
+ import { LocalModelsService } from './local-models.service.js';
2
3
  export declare class ConfigController {
3
4
  private readonly configService;
4
- constructor(configService: ConfigService);
5
+ private readonly localModelsService;
6
+ constructor(configService: ConfigService, localModelsService: LocalModelsService);
5
7
  getConfig(): Promise<{
6
8
  success: boolean;
7
9
  data: {
@@ -16,7 +18,7 @@ export declare class ConfigController {
16
18
  loginUsername?: string;
17
19
  providers: {
18
20
  [key: string]: {
19
- apiKey? /** OpenCode 免费/推荐模型列表,供代理配置界面下拉选择(本地/远程模式默认模型) */: string;
21
+ apiKey?: string;
20
22
  baseUrl?: string;
21
23
  alias?: string;
22
24
  };
@@ -68,7 +70,7 @@ export declare class ConfigController {
68
70
  loginUsername?: string;
69
71
  providers: {
70
72
  [key: string]: {
71
- apiKey? /** OpenCode 免费/推荐模型列表,供代理配置界面下拉选择(本地/远程模式默认模型) */: string;
73
+ apiKey?: string;
72
74
  baseUrl?: string;
73
75
  alias?: string;
74
76
  };
@@ -112,7 +114,7 @@ export declare class ConfigController {
112
114
  }>;
113
115
  getProviderSupport(): Promise<{
114
116
  success: boolean;
115
- data: import("../../index.js").ProviderSupport;
117
+ data: import("../../core/config/provider-support-default.js").ProviderSupport;
116
118
  }>;
117
119
  getModels(provider: string, type?: string): Promise<{
118
120
  success: boolean;
@@ -127,4 +129,79 @@ export declare class ConfigController {
127
129
  success: boolean;
128
130
  data: import("../../core/agent/proxy/adapters/opencode-free-models.js").OpenCodeFreeModelOption[];
129
131
  };
132
+ /** 列出本地已缓存的 GGUF 模型文件 */
133
+ listLocalModels(): Promise<{
134
+ success: boolean;
135
+ data: import("./local-models.service.js").LocalModelInfo[];
136
+ }>;
137
+ /** 推荐的 GGUF 模型列表(来自 preset,与已安装展示名称一致) */
138
+ getRecommendedModels(): Promise<{
139
+ success: boolean;
140
+ data: import("./local-models.service.js").RecommendedModel[];
141
+ }>;
142
+ /** 仅返回尚未安装的推荐模型(已安装的不显示在下载区) */
143
+ getRecommendedToDownload(): Promise<{
144
+ success: boolean;
145
+ data: import("./local-models.service.js").RecommendedModel[];
146
+ }>;
147
+ /** 本地模型服务状态:是否可用、不可用原因、当前 baseUrl */
148
+ getLocalLlmStatus(): {
149
+ success: boolean;
150
+ data: {
151
+ available: boolean;
152
+ error: string | undefined;
153
+ baseUrl: string | undefined;
154
+ };
155
+ };
156
+ /** 启动本地模型服务:LLM/Embedding 任一已下载即可启动,只启动已存在的模型并提示;失败仅返回错误信息不抛错 */
157
+ startLocalLlm(body: {
158
+ llmModelUri?: string;
159
+ embeddingModelUri?: string;
160
+ }): Promise<{
161
+ success: boolean;
162
+ data: {
163
+ error: string;
164
+ baseUrl?: undefined;
165
+ message?: undefined;
166
+ };
167
+ } | {
168
+ success: boolean;
169
+ data: {
170
+ baseUrl: string;
171
+ message: string;
172
+ error?: undefined;
173
+ };
174
+ }>;
175
+ /** 开始后台下载模型(立即返回,进度通过 GET local-models/progress 轮询)。useMirror=true 使用国内镜像。 */
176
+ startDownload(body: {
177
+ modelUri: string;
178
+ useMirror?: boolean;
179
+ }): Promise<{
180
+ success: boolean;
181
+ data: {
182
+ filename: string;
183
+ };
184
+ }>;
185
+ /** 取消指定模型的下载 */
186
+ cancelDownload(body: {
187
+ modelUri: string;
188
+ }): {
189
+ success: boolean;
190
+ };
191
+ /** 推荐模型列表(含是否已安装),用于展示「已下载」或中国/全球下载按钮 */
192
+ getRecommendedWithStatus(): Promise<{
193
+ success: boolean;
194
+ data: (import("./local-models.service.js").RecommendedModel & {
195
+ isInstalled: boolean;
196
+ })[];
197
+ }>;
198
+ /** 查询下载进度 */
199
+ getDownloadProgress(uri: string): {
200
+ success: boolean;
201
+ data: import("./local-models.service.js").DownloadProgress | null;
202
+ };
203
+ /** 删除本地缓存的 GGUF 模型文件 */
204
+ deleteLocalModel(filename: string): Promise<{
205
+ success: boolean;
206
+ }>;
130
207
  }
@@ -10,13 +10,19 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
- import { Controller, Get, Put, Body, Param, Query } from '@nestjs/common';
13
+ import { Controller, Get, Put, Body, Param, Query, Delete, Post } from '@nestjs/common';
14
14
  import { OPENCODE_FREE_MODELS } from '../../core/agent/proxy/adapters/opencode-free-models.js';
15
+ import { loadDesktopAgentConfig, setDefaultModel } from '../../core/config/desktop-config.js';
16
+ import { startLocalLlmServer, stopLocalLlmServer } from '../../core/local-llm-server/index.js';
17
+ import { resolveModelPathInCache, LOCAL_LLM_CACHE_DIR } from '../../core/local-llm-server/model-resolve.js';
15
18
  import { ConfigService } from './config.service.js';
19
+ import { LocalModelsService } from './local-models.service.js';
16
20
  let ConfigController = class ConfigController {
17
21
  configService;
18
- constructor(configService) {
22
+ localModelsService;
23
+ constructor(configService, localModelsService) {
19
24
  this.configService = configService;
25
+ this.localModelsService = localModelsService;
20
26
  }
21
27
  async getConfig() {
22
28
  const config = await this.configService.getConfig();
@@ -62,6 +68,116 @@ let ConfigController = class ConfigController {
62
68
  data: OPENCODE_FREE_MODELS,
63
69
  };
64
70
  }
71
+ // ─── 本地模型管理(node-llama-cpp GGUF)────────────────────────────────────
72
+ /** 列出本地已缓存的 GGUF 模型文件 */
73
+ async listLocalModels() {
74
+ return { success: true, data: await this.localModelsService.listModels() };
75
+ }
76
+ /** 推荐的 GGUF 模型列表(来自 preset,与已安装展示名称一致) */
77
+ async getRecommendedModels() {
78
+ const data = await this.localModelsService.getRecommendedModels();
79
+ return { success: true, data };
80
+ }
81
+ /** 仅返回尚未安装的推荐模型(已安装的不显示在下载区) */
82
+ async getRecommendedToDownload() {
83
+ const data = await this.localModelsService.getRecommendedToDownload();
84
+ return { success: true, data };
85
+ }
86
+ /** 本地模型服务状态:是否可用、不可用原因、当前 baseUrl */
87
+ getLocalLlmStatus() {
88
+ const baseUrl = process.env.LOCAL_LLM_BASE_URL;
89
+ const error = process.env.LOCAL_LLM_START_FAILED;
90
+ const available = !!baseUrl;
91
+ return {
92
+ success: true,
93
+ data: { available, error: error || undefined, baseUrl: baseUrl || undefined },
94
+ };
95
+ }
96
+ /** 启动本地模型服务:LLM/Embedding 任一已下载即可启动,只启动已存在的模型并提示;失败仅返回错误信息不抛错 */
97
+ async startLocalLlm(body) {
98
+ stopLocalLlmServer();
99
+ delete process.env.LOCAL_LLM_BASE_URL;
100
+ delete process.env.LOCAL_LLM_START_FAILED;
101
+ const llmPath = body.llmModelUri?.trim();
102
+ const embPath = body.embeddingModelUri?.trim();
103
+ if (!llmPath && !embPath) {
104
+ return { success: false, data: { error: '请至少选择 LLM 或 Embedding 模型之一' } };
105
+ }
106
+ const llmResolved = llmPath ? resolveModelPathInCache(llmPath, LOCAL_LLM_CACHE_DIR) : '';
107
+ const embResolved = embPath ? resolveModelPathInCache(embPath, LOCAL_LLM_CACHE_DIR) : '';
108
+ if (!llmResolved && !embResolved) {
109
+ return { success: false, data: { error: '未找到已下载的模型文件,请先在「模型管理」中下载' } };
110
+ }
111
+ if (llmPath) {
112
+ try {
113
+ await setDefaultModel('local', llmPath);
114
+ }
115
+ catch {
116
+ // 保存失败不阻断启动
117
+ }
118
+ }
119
+ let contextSize;
120
+ try {
121
+ const defaultAgent = await loadDesktopAgentConfig('default');
122
+ if (defaultAgent?.contextSize != null && defaultAgent.contextSize > 0) {
123
+ contextSize = defaultAgent.contextSize;
124
+ }
125
+ }
126
+ catch {
127
+ // ignore
128
+ }
129
+ if (contextSize == null) {
130
+ const envMax = process.env.LOCAL_LLM_CONTEXT_MAX;
131
+ contextSize = envMax != null && String(envMax).trim() !== '' ? parseInt(envMax, 10) || 32768 : 32768;
132
+ }
133
+ const opts = {
134
+ ...(llmResolved ? { llmModelPath: llmResolved } : {}),
135
+ ...(embResolved ? { embeddingModelPath: embResolved } : {}),
136
+ contextSize,
137
+ };
138
+ try {
139
+ const handle = await startLocalLlmServer(opts);
140
+ process.env.LOCAL_LLM_BASE_URL = handle.baseUrl;
141
+ const message = llmResolved && embResolved
142
+ ? '已启动 LLM + Embedding'
143
+ : llmResolved
144
+ ? '已启动 LLM 模型(当前未使用 Embedding)'
145
+ : '已启动 Embedding 模型(当前未使用 LLM)';
146
+ return { success: true, data: { baseUrl: handle.baseUrl, message } };
147
+ }
148
+ catch (e) {
149
+ const msg = e instanceof Error ? e.message : String(e);
150
+ process.env.LOCAL_LLM_START_FAILED = msg;
151
+ return { success: false, data: { error: msg } };
152
+ }
153
+ }
154
+ /** 开始后台下载模型(立即返回,进度通过 GET local-models/progress 轮询)。useMirror=true 使用国内镜像。 */
155
+ async startDownload(body) {
156
+ const result = await this.localModelsService.startDownload(body.modelUri, {
157
+ useMirror: body.useMirror === true,
158
+ });
159
+ return { success: true, data: result };
160
+ }
161
+ /** 取消指定模型的下载 */
162
+ cancelDownload(body) {
163
+ this.localModelsService.cancelDownload(body.modelUri);
164
+ return { success: true };
165
+ }
166
+ /** 推荐模型列表(含是否已安装),用于展示「已下载」或中国/全球下载按钮 */
167
+ async getRecommendedWithStatus() {
168
+ const models = await this.localModelsService.getRecommendedWithStatus();
169
+ return { success: true, data: models };
170
+ }
171
+ /** 查询下载进度 */
172
+ getDownloadProgress(uri) {
173
+ const progress = this.localModelsService.getDownloadProgress(uri);
174
+ return { success: true, data: progress };
175
+ }
176
+ /** 删除本地缓存的 GGUF 模型文件 */
177
+ async deleteLocalModel(filename) {
178
+ await this.localModelsService.deleteModel(filename);
179
+ return { success: true };
180
+ }
65
181
  };
66
182
  __decorate([
67
183
  Get(),
@@ -102,8 +218,74 @@ __decorate([
102
218
  __metadata("design:paramtypes", []),
103
219
  __metadata("design:returntype", void 0)
104
220
  ], ConfigController.prototype, "getOpencodeFreeModels", null);
221
+ __decorate([
222
+ Get('local-models'),
223
+ __metadata("design:type", Function),
224
+ __metadata("design:paramtypes", []),
225
+ __metadata("design:returntype", Promise)
226
+ ], ConfigController.prototype, "listLocalModels", null);
227
+ __decorate([
228
+ Get('local-models/recommended'),
229
+ __metadata("design:type", Function),
230
+ __metadata("design:paramtypes", []),
231
+ __metadata("design:returntype", Promise)
232
+ ], ConfigController.prototype, "getRecommendedModels", null);
233
+ __decorate([
234
+ Get('local-models/recommended-to-download'),
235
+ __metadata("design:type", Function),
236
+ __metadata("design:paramtypes", []),
237
+ __metadata("design:returntype", Promise)
238
+ ], ConfigController.prototype, "getRecommendedToDownload", null);
239
+ __decorate([
240
+ Get('local-models/status'),
241
+ __metadata("design:type", Function),
242
+ __metadata("design:paramtypes", []),
243
+ __metadata("design:returntype", void 0)
244
+ ], ConfigController.prototype, "getLocalLlmStatus", null);
245
+ __decorate([
246
+ Post('local-models/start'),
247
+ __param(0, Body()),
248
+ __metadata("design:type", Function),
249
+ __metadata("design:paramtypes", [Object]),
250
+ __metadata("design:returntype", Promise)
251
+ ], ConfigController.prototype, "startLocalLlm", null);
252
+ __decorate([
253
+ Post('local-models/download'),
254
+ __param(0, Body()),
255
+ __metadata("design:type", Function),
256
+ __metadata("design:paramtypes", [Object]),
257
+ __metadata("design:returntype", Promise)
258
+ ], ConfigController.prototype, "startDownload", null);
259
+ __decorate([
260
+ Post('local-models/cancel-download'),
261
+ __param(0, Body()),
262
+ __metadata("design:type", Function),
263
+ __metadata("design:paramtypes", [Object]),
264
+ __metadata("design:returntype", void 0)
265
+ ], ConfigController.prototype, "cancelDownload", null);
266
+ __decorate([
267
+ Get('local-models/recommended-with-status'),
268
+ __metadata("design:type", Function),
269
+ __metadata("design:paramtypes", []),
270
+ __metadata("design:returntype", Promise)
271
+ ], ConfigController.prototype, "getRecommendedWithStatus", null);
272
+ __decorate([
273
+ Get('local-models/progress'),
274
+ __param(0, Query('uri')),
275
+ __metadata("design:type", Function),
276
+ __metadata("design:paramtypes", [String]),
277
+ __metadata("design:returntype", void 0)
278
+ ], ConfigController.prototype, "getDownloadProgress", null);
279
+ __decorate([
280
+ Delete('local-models/:filename'),
281
+ __param(0, Param('filename')),
282
+ __metadata("design:type", Function),
283
+ __metadata("design:paramtypes", [String]),
284
+ __metadata("design:returntype", Promise)
285
+ ], ConfigController.prototype, "deleteLocalModel", null);
105
286
  ConfigController = __decorate([
106
287
  Controller('config'),
107
- __metadata("design:paramtypes", [ConfigService])
288
+ __metadata("design:paramtypes", [ConfigService,
289
+ LocalModelsService])
108
290
  ], ConfigController);
109
291
  export { ConfigController };
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { Module, forwardRef } from '@nestjs/common';
8
8
  import { ConfigController } from './config.controller.js';
9
9
  import { ConfigService } from './config.service.js';
10
+ import { LocalModelsService } from './local-models.service.js';
10
11
  import { AgentConfigModule } from '../agent-config/agent-config.module.js';
11
12
  let ConfigModule = class ConfigModule {
12
13
  };
@@ -14,8 +15,8 @@ ConfigModule = __decorate([
14
15
  Module({
15
16
  imports: [forwardRef(() => AgentConfigModule)],
16
17
  controllers: [ConfigController],
17
- providers: [ConfigService],
18
- exports: [ConfigService],
18
+ providers: [ConfigService, LocalModelsService],
19
+ exports: [ConfigService, LocalModelsService],
19
20
  })
20
21
  ], ConfigModule);
21
22
  export { ConfigModule };
@@ -91,11 +91,14 @@ export declare class ConfigService {
91
91
  private configPath;
92
92
  private config;
93
93
  constructor(agentConfigService: AgentConfigService);
94
+ /** 预装本地推理缺省:与 desktop-config 的 DEFAULT_LOCAL_LLM_MODEL_ID / DEFAULT_LOCAL_MODEL_ITEM_CODE 一致 */
95
+ private static readonly DEFAULT_LOCAL_MODEL_ID;
96
+ private static readonly DEFAULT_LOCAL_MODEL_ITEM_CODE;
94
97
  private getDefaultConfig;
95
98
  /** 当前缺省智能体 id */
96
99
  getDefaultAgentId(config?: AppConfig): string;
97
100
  private loadConfig;
98
- /** 每次获取前从磁盘重新读取,保证打开配置界面时显示最新(含 CLI 写入的配置) */
101
+ /** 每次获取前从磁盘重新读取,保证打开配置界面时显示最新(含 CLI 写入的配置)。本地 LLM 可用时注入 local 与缺省模型项,供所有智能体使用。 */
99
102
  getConfig(): Promise<AppConfig>;
100
103
  updateConfig(updates: Partial<AppConfig>): Promise<AppConfig>;
101
104
  private saveConfig;
@@ -7,14 +7,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ var ConfigService_1;
10
11
  import { Injectable } from '@nestjs/common';
11
12
  import { readFile, writeFile, mkdir } from 'fs/promises';
12
- import { join } from 'path';
13
+ import { join, dirname } from 'path';
13
14
  import { existsSync } from 'fs';
14
15
  import { homedir } from 'os';
15
16
  import { getProviderSupport, syncDesktopConfigToModelsJson } from '../../core/config/desktop-config.js';
16
17
  import { AgentConfigService } from '../agent-config/agent-config.service.js';
17
18
  let ConfigService = class ConfigService {
19
+ static { ConfigService_1 = this; }
18
20
  agentConfigService;
19
21
  configPath;
20
22
  config;
@@ -30,16 +32,30 @@ let ConfigService = class ConfigService {
30
32
  this.config = this.getDefaultConfig();
31
33
  this.loadConfig();
32
34
  }
35
+ /** 预装本地推理缺省:与 desktop-config 的 DEFAULT_LOCAL_LLM_MODEL_ID / DEFAULT_LOCAL_MODEL_ITEM_CODE 一致 */
36
+ static DEFAULT_LOCAL_MODEL_ID = 'hf_Qwen_Qwen3-4B-GGUF_Qwen3-4B-Q4_K_M.gguf';
37
+ static DEFAULT_LOCAL_MODEL_ITEM_CODE = 'local-qwen3-4b';
33
38
  getDefaultConfig() {
34
39
  return {
35
40
  gatewayUrl: 'ws://localhost:38080',
36
- defaultProvider: 'deepseek',
37
- defaultModel: 'deepseek-chat',
41
+ defaultProvider: 'local',
42
+ defaultModel: ConfigService_1.DEFAULT_LOCAL_MODEL_ID,
43
+ defaultModelItemCode: ConfigService_1.DEFAULT_LOCAL_MODEL_ITEM_CODE,
38
44
  defaultAgentId: 'default',
39
45
  theme: 'dark',
40
46
  maxAgentSessions: 5,
41
- providers: {},
42
- configuredModels: [],
47
+ providers: {
48
+ local: { baseUrl: 'http://127.0.0.1:11435/v1' },
49
+ },
50
+ configuredModels: [
51
+ {
52
+ provider: 'local',
53
+ modelId: ConfigService_1.DEFAULT_LOCAL_MODEL_ID,
54
+ modelItemCode: ConfigService_1.DEFAULT_LOCAL_MODEL_ITEM_CODE,
55
+ type: 'llm',
56
+ alias: 'Qwen3 4B Q4_K_M',
57
+ },
58
+ ],
43
59
  rag: undefined,
44
60
  memory: {},
45
61
  channels: {},
@@ -54,18 +70,54 @@ let ConfigService = class ConfigService {
54
70
  try {
55
71
  if (existsSync(this.configPath)) {
56
72
  const content = await readFile(this.configPath, 'utf-8');
57
- this.config = { ...this.getDefaultConfig(), ...JSON.parse(content) };
73
+ const parsed = JSON.parse(content);
74
+ this.config = { ...this.getDefaultConfig(), ...parsed };
58
75
  this.config.defaultAgentId = this.getDefaultAgentId(this.config);
76
+ // 保证 local 与缺省本地模型始终存在,避免旧配置或空配置覆盖产品默认
77
+ const def = this.getDefaultConfig();
78
+ if (!this.config.providers?.local) {
79
+ this.config.providers = { ...(this.config.providers || {}), local: def.providers.local };
80
+ }
81
+ if (!(this.config.configuredModels || []).some((m) => m.provider === 'local')) {
82
+ this.config.configuredModels = [...(def.configuredModels || []), ...(this.config.configuredModels || [])];
83
+ }
84
+ }
85
+ else {
86
+ this.config = this.getDefaultConfig();
87
+ await this.saveConfig();
59
88
  }
60
89
  }
61
90
  catch (error) {
62
91
  console.error('Error loading config:', error);
63
92
  }
64
93
  }
65
- /** 每次获取前从磁盘重新读取,保证打开配置界面时显示最新(含 CLI 写入的配置) */
94
+ /** 每次获取前从磁盘重新读取,保证打开配置界面时显示最新(含 CLI 写入的配置)。本地 LLM 可用时注入 local 与缺省模型项,供所有智能体使用。 */
66
95
  async getConfig() {
67
96
  await this.loadConfig();
68
- return this.config;
97
+ const baseUrl = process.env.LOCAL_LLM_BASE_URL?.trim();
98
+ if (!baseUrl)
99
+ return this.config;
100
+ const out = { ...this.config };
101
+ out.providers = { ...(this.config.providers || {}) };
102
+ if (!out.providers['local']) {
103
+ out.providers['local'] = { baseUrl: baseUrl || 'http://127.0.0.1:11435/v1' };
104
+ }
105
+ else if (!out.providers['local'].baseUrl?.trim()) {
106
+ out.providers['local'] = { ...out.providers['local'], baseUrl: baseUrl || 'http://127.0.0.1:11435/v1' };
107
+ }
108
+ const list = [...(out.configuredModels || [])];
109
+ const hasLocal = list.some((m) => m.provider === 'local' && m.modelId === 'local-llm');
110
+ if (!hasLocal) {
111
+ list.push({
112
+ provider: 'local',
113
+ modelId: 'local-llm',
114
+ modelItemCode: 'local-llm',
115
+ type: 'llm',
116
+ alias: '本地 LLM(当前加载)',
117
+ });
118
+ out.configuredModels = list;
119
+ }
120
+ return out;
69
121
  }
70
122
  async updateConfig(updates) {
71
123
  this.config = { ...this.config, ...updates };
@@ -85,6 +137,7 @@ let ConfigService = class ConfigService {
85
137
  }
86
138
  async saveConfig() {
87
139
  try {
140
+ await mkdir(dirname(this.configPath), { recursive: true });
88
141
  await writeFile(this.configPath, JSON.stringify(this.config, null, 2));
89
142
  }
90
143
  catch (error) {
@@ -119,7 +172,7 @@ let ConfigService = class ConfigService {
119
172
  return getProviderSupport();
120
173
  }
121
174
  };
122
- ConfigService = __decorate([
175
+ ConfigService = ConfigService_1 = __decorate([
123
176
  Injectable(),
124
177
  __metadata("design:paramtypes", [AgentConfigService])
125
178
  ], ConfigService);
@@ -0,0 +1,67 @@
1
+ export interface RecommendedModel {
2
+ id: string;
3
+ name: string;
4
+ type: 'llm' | 'embedding';
5
+ sizeHint: string;
6
+ }
7
+ export interface LocalModelInfo {
8
+ filename: string;
9
+ /** 文件大小(字节) */
10
+ size: number;
11
+ /** 最后修改时间 ISO 字符串 */
12
+ updatedAt: string;
13
+ /** 推断的模型类型:llm / embedding(根据文件名关键词) */
14
+ inferredType: 'llm' | 'embedding' | 'unknown';
15
+ /** 与推荐列表一致的展示名称(能匹配到预设时),否则为 filename */
16
+ displayName: string;
17
+ }
18
+ export interface DownloadProgress {
19
+ status: string;
20
+ completed?: number;
21
+ total?: number;
22
+ percent?: number;
23
+ }
24
+ export declare class LocalModelsService {
25
+ private readonly cacheDir;
26
+ private recommendedCache;
27
+ /** 正在下载的任务:modelUri → 进度 */
28
+ private downloadingMap;
29
+ constructor();
30
+ /** 从 presets/recommended-local-models.json 加载推荐列表,失败则用内置默认 */
31
+ private getRecommendedModelsFromPreset;
32
+ /** 列出本地已缓存的 GGUF 模型文件,displayName 与推荐列表一致(来自 preset 匹配) */
33
+ listModels(): Promise<LocalModelInfo[]>;
34
+ /** 删除本地缓存的 GGUF 模型文件 */
35
+ deleteModel(filename: string): Promise<void>;
36
+ /** 获取推荐模型列表(来自 preset,与已安装展示名称一致) */
37
+ getRecommendedModels(): Promise<RecommendedModel[]>;
38
+ /** 仅返回尚未安装的推荐模型;已安装的视为「推荐列表中的一员」不再出现在备下载区 */
39
+ getRecommendedToDownload(): Promise<RecommendedModel[]>;
40
+ /** 检查指定模型(uri 或文件名)是否已在缓存目录存在 */
41
+ isModelFilePresent(modelIdOrUri: string): boolean;
42
+ /** 正在下载任务的 AbortController,用于取消 */
43
+ private abortControllerMap;
44
+ /** 获取某个 modelUri 的下载进度(不存在则表示未在下载) */
45
+ getDownloadProgress(modelUri: string): DownloadProgress | null;
46
+ /**
47
+ * 取消指定 modelUri 的下载(若正在下载)。
48
+ */
49
+ cancelDownload(modelUri: string): void;
50
+ /**
51
+ * 返回所有推荐模型及是否已安装,用于前端展示「已下载」或下载按钮。
52
+ */
53
+ getRecommendedWithStatus(): Promise<Array<RecommendedModel & {
54
+ isInstalled: boolean;
55
+ }>>;
56
+ /**
57
+ * 后台下载模型(通过 node-llama-cpp resolveModelFile)。
58
+ * useMirror=true 使用国内镜像 hf-mirror.com,false 使用官方 huggingface.co。
59
+ * 返回后立即响应,进度通过 getDownloadProgress 轮询获取。
60
+ */
61
+ startDownload(modelUri: string, options?: {
62
+ useMirror?: boolean;
63
+ }): Promise<{
64
+ filename: string;
65
+ }>;
66
+ private runDownload;
67
+ }