chat-agent-toolkit 1.2.32 → 1.2.34
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/research-agent.cjs.js +1 -1
- package/dist/research-agent.cjs.js.map +1 -1
- package/dist/research-agent.es.js +1 -1
- package/dist/research-agent.es.js.map +1 -1
- package/package.json +1 -1
- package/src/config/config-manager.ts +295 -295
- package/src/config/config-types.ts +65 -65
- package/src/config/environment-variables.ts +16 -16
- package/src/config/index.ts +26 -26
- package/src/config/language-models-database.ts +1434 -1434
- package/src/config/mcp-server-registry.ts +24 -24
- package/src/config/model-registry.ts +271 -271
- package/src/config/model-tester.ts +211 -211
- package/src/config/model-utils.ts +193 -193
- package/src/config/provider-ui-config.ts +196 -196
- package/src/connectors/open-connector.json +130 -130
- package/src/index.ts +26 -26
- package/src/memory/ARCHITECTURE.md +302 -302
- package/src/memory/README.md +224 -224
- package/src/memory/agent-memory-manager.ts +416 -416
- package/src/memory/example.ts +343 -343
- package/src/memory/index.ts +47 -47
- package/src/memory/mastra-integration.ts +604 -604
- package/src/memory/storage/drizzle-storage.ts +236 -236
- package/src/memory/storage/in-memory-storage.ts +551 -551
- package/src/memory/storage/storage-interface.ts +68 -68
- package/src/memory/types.ts +125 -125
- package/src/models/types.ts +4 -4
- package/src/provider-logos/01-ai.png +0 -0
- package/src/provider-logos/anthropic.png +0 -0
- package/src/provider-logos/aws-bedrock.png +0 -0
- package/src/provider-logos/aws-sagemaker.png +0 -0
- package/src/provider-logos/azure-openai-service.png +0 -0
- package/src/provider-logos/chatglm.png +0 -0
- package/src/provider-logos/cohere.png +0 -0
- package/src/provider-logos/gemini.png +0 -0
- package/src/provider-logos/groqcloud.png +0 -0
- package/src/provider-logos/huggingface.png +0 -0
- package/src/provider-logos/jina.png +0 -0
- package/src/provider-logos/localai.png +0 -0
- package/src/provider-logos/mistral-ai.png +0 -0
- package/src/provider-logos/moonshot-ai.png +0 -0
- package/src/provider-logos/ollama.png +0 -0
- package/src/provider-logos/openai.png +0 -0
- package/src/provider-logos/openllm.png +0 -0
- package/src/provider-logos/openrouter.png +0 -0
- package/src/provider-logos/replicate.png +0 -0
- package/src/provider-logos/together-ai.png +0 -0
- package/src/provider-logos/tongyi.png +0 -0
- package/src/provider-logos/xorbits-inference.png +0 -0
- package/src/provider-logos/zhipu-ai.png +0 -0
- package/src/tools/index.ts +13 -13
- package/src/tools/open-connector-mastra.ts +270 -270
- package/src/tools/open-connector-mcp.ts +170 -170
- package/src/tools/qwksearch-api-tools.ts +326 -326
- package/src/tools/search/doc-utils.ts +139 -139
- package/src/tools/search/document.ts +47 -47
- package/src/tools/search/index.ts +14 -14
- package/src/tools/search/link-summarizer.ts +112 -112
- package/src/tools/search/meta-search-types.ts +62 -62
- package/src/tools/search/metaSearchAgent.ts +465 -465
- package/src/tools/search/search-handlers.ts +97 -97
- package/src/tools/search/suggestionGeneratorAgent.ts +56 -56
- package/src/types.d.ts +137 -137
- package/src/utils/index.ts +2 -2
- package/src/utils/markdown-to-html.ts +53 -53
- package/src/utils/outputParser.ts +73 -73
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-agent-toolkit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.34",
|
|
4
4
|
"description": "Multi-provider AI agent toolkit: generate language responses, search the web, extract content, and manage memory across 10+ LLM providers.",
|
|
5
5
|
"author": "vtempest <grokthiscontact@gmail.com>",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -1,295 +1,295 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Configuration Manager
|
|
3
|
-
*
|
|
4
|
-
* Manages model providers, MCP servers, and search configuration in memory.
|
|
5
|
-
* Handles environment variable loading, provider hashing, and config updates.
|
|
6
|
-
*/
|
|
7
|
-
import type { ConfigModelProvider, MCPServerConfig, Config, UIConfigSections, Model } from "./config-types";
|
|
8
|
-
import { getModelProvidersUIConfigSection } from "./provider-ui-config";
|
|
9
|
-
import { getEnv } from "./environment-variables";
|
|
10
|
-
import { LANGUAGE_MODELS } from "./language-models-database";
|
|
11
|
-
|
|
12
|
-
// Maps a provider UI key (from provider-ui-config) to the matching
|
|
13
|
-
// `provider` name in the LANGUAGE_MODELS database. Names differ in a few
|
|
14
|
-
// cases (e.g. the "gemini" UI key corresponds to the "Google" model list).
|
|
15
|
-
const PROVIDER_KEY_TO_DB_NAME: Record<string, string> = {
|
|
16
|
-
openai: "openai",
|
|
17
|
-
anthropic: "anthropic",
|
|
18
|
-
gemini: "google",
|
|
19
|
-
groq: "groq",
|
|
20
|
-
deepseek: "deepseek",
|
|
21
|
-
nvidia: "nvidia",
|
|
22
|
-
openrouter: "openrouter",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Returns the default chat models for a provider from the LANGUAGE_MODELS
|
|
27
|
-
* database so that env-based providers expose a usable model list instead of
|
|
28
|
-
* an empty one. Returns [] when no matching provider list exists.
|
|
29
|
-
*/
|
|
30
|
-
const getDefaultChatModels = (providerKey: string): Model[] => {
|
|
31
|
-
const dbName = PROVIDER_KEY_TO_DB_NAME[providerKey] ?? providerKey;
|
|
32
|
-
const entry = LANGUAGE_MODELS.find(
|
|
33
|
-
(p) => p.provider.toLowerCase() === dbName.toLowerCase(),
|
|
34
|
-
);
|
|
35
|
-
if (!entry?.models) return [];
|
|
36
|
-
|
|
37
|
-
return entry.models.map((m: any) => ({
|
|
38
|
-
name: m.name,
|
|
39
|
-
key: m.id,
|
|
40
|
-
}));
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const hashObj = (obj: { [key: string]: any }) => {
|
|
44
|
-
const str = JSON.stringify(obj, Object.keys(obj).sort());
|
|
45
|
-
let hash = 0;
|
|
46
|
-
for (let i = 0; i < str.length; i++) {
|
|
47
|
-
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
48
|
-
hash |= 0;
|
|
49
|
-
}
|
|
50
|
-
return String(Math.abs(hash).toString(36));
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
class ConfigManager {
|
|
54
|
-
configVersion = 1;
|
|
55
|
-
currentConfig: Config = {
|
|
56
|
-
version: this.configVersion,
|
|
57
|
-
setupComplete: getEnv("SETUP_COMPLETE") === "true" || false,
|
|
58
|
-
preferences: {},
|
|
59
|
-
personalization: {},
|
|
60
|
-
modelProviders: [],
|
|
61
|
-
mcpServers: [],
|
|
62
|
-
search: {
|
|
63
|
-
searxngURL: "",
|
|
64
|
-
tavilyApiKey: "",
|
|
65
|
-
sourceScrapeCount: 3,
|
|
66
|
-
sourceScrapeTimeout: 5,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
uiConfigSections: UIConfigSections = {
|
|
70
|
-
preferences: [],
|
|
71
|
-
personalization: [],
|
|
72
|
-
modelProviders: [],
|
|
73
|
-
mcpServers: [],
|
|
74
|
-
search: [
|
|
75
|
-
{
|
|
76
|
-
name: "SearXNG URL",
|
|
77
|
-
key: "searxngURL",
|
|
78
|
-
type: "string",
|
|
79
|
-
required: false,
|
|
80
|
-
description: "The URL of your SearXNG instance",
|
|
81
|
-
placeholder: "http://localhost:4000",
|
|
82
|
-
default: "",
|
|
83
|
-
scope: "server",
|
|
84
|
-
env: "SEARXNG_API_URL",
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: "Tavily API Key",
|
|
88
|
-
key: "tavilyApiKey",
|
|
89
|
-
type: "string",
|
|
90
|
-
required: false,
|
|
91
|
-
description: "Your Tavily API key for enhanced search capabilities.",
|
|
92
|
-
placeholder: "tvly-...",
|
|
93
|
-
default: "",
|
|
94
|
-
scope: "server",
|
|
95
|
-
env: "TAVILY_API_KEY",
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: "Scrape timeout (seconds)",
|
|
99
|
-
key: "sourceScrapeTimeout",
|
|
100
|
-
type: "select",
|
|
101
|
-
options: [
|
|
102
|
-
{ name: "3 seconds", value: "3" },
|
|
103
|
-
{ name: "5 seconds (default)", value: "5" },
|
|
104
|
-
{ name: "10 seconds", value: "10" },
|
|
105
|
-
{ name: "15 seconds", value: "15" },
|
|
106
|
-
{ name: "20 seconds", value: "20" },
|
|
107
|
-
],
|
|
108
|
-
required: false,
|
|
109
|
-
description: "Maximum time to wait when scraping each source URL.",
|
|
110
|
-
default: "5",
|
|
111
|
-
scope: "server",
|
|
112
|
-
},
|
|
113
|
-
],
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
private initialized = false;
|
|
117
|
-
|
|
118
|
-
constructor() {
|
|
119
|
-
// Don't initialize in constructor to avoid circular dependency
|
|
120
|
-
// Initialize lazily when config is first accessed
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private ensureInitialized() {
|
|
124
|
-
if (this.initialized) return;
|
|
125
|
-
this.initialize();
|
|
126
|
-
this.initialized = true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private initialize() {
|
|
130
|
-
const providerConfigSections = getModelProvidersUIConfigSection();
|
|
131
|
-
this.uiConfigSections.modelProviders = providerConfigSections;
|
|
132
|
-
|
|
133
|
-
const newProviders: ConfigModelProvider[] = [];
|
|
134
|
-
|
|
135
|
-
providerConfigSections.forEach((provider) => {
|
|
136
|
-
|
|
137
|
-
const tempConfig: Record<string, any> = {};
|
|
138
|
-
const required: string[] = [];
|
|
139
|
-
|
|
140
|
-
provider.fields.forEach((field) => {
|
|
141
|
-
tempConfig[field.key] =
|
|
142
|
-
getEnv(field.env!) || field.default || "";
|
|
143
|
-
if (field.required) required.push(field.key);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
let configured = true;
|
|
147
|
-
required.forEach((r) => {
|
|
148
|
-
if (!tempConfig[r]) configured = false;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (configured) {
|
|
152
|
-
const hash = hashObj(tempConfig);
|
|
153
|
-
const exists = this.currentConfig.modelProviders.find(
|
|
154
|
-
(p) => p.hash === hash,
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
if (!exists) {
|
|
158
|
-
newProviders.push({
|
|
159
|
-
id: hash,
|
|
160
|
-
name: `${provider.name}`,
|
|
161
|
-
type: provider.key,
|
|
162
|
-
chatModels: getDefaultChatModels(provider.key),
|
|
163
|
-
config: tempConfig,
|
|
164
|
-
hash: hash,
|
|
165
|
-
isEnvBased: true,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
if (newProviders.length > 0) {
|
|
172
|
-
this.currentConfig.modelProviders.push(...newProviders);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Search config from env
|
|
176
|
-
this.uiConfigSections.search.forEach((f) => {
|
|
177
|
-
if (f.env && !this.currentConfig.search[f.key]) {
|
|
178
|
-
this.currentConfig.search[f.key] =
|
|
179
|
-
getEnv(f.env) ?? f.default ?? "";
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
public getConfig(key: string, defaultValue?: any): any {
|
|
185
|
-
this.ensureInitialized();
|
|
186
|
-
const nested = key.split(".");
|
|
187
|
-
let obj: any = this.currentConfig;
|
|
188
|
-
|
|
189
|
-
for (let i = 0; i < nested.length; i++) {
|
|
190
|
-
const part = nested[i];
|
|
191
|
-
if (obj == null) return defaultValue;
|
|
192
|
-
obj = obj[part];
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return obj === undefined ? defaultValue : obj;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
public updateConfig(key: string, val: any) {
|
|
199
|
-
const parts = key.split(".");
|
|
200
|
-
if (parts.length === 0) return;
|
|
201
|
-
|
|
202
|
-
let target: any = this.currentConfig;
|
|
203
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
204
|
-
const part = parts[i];
|
|
205
|
-
if (target[part] === null || typeof target[part] !== "object") {
|
|
206
|
-
target[part] = {};
|
|
207
|
-
}
|
|
208
|
-
target = target[part];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const finalKey = parts[parts.length - 1];
|
|
212
|
-
target[finalKey] = val;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
public addModelProvider(type: string, name: string, config: any) {
|
|
216
|
-
this.ensureInitialized();
|
|
217
|
-
const hash = hashObj(config);
|
|
218
|
-
|
|
219
|
-
const newModelProvider: ConfigModelProvider = {
|
|
220
|
-
id: hash,
|
|
221
|
-
name,
|
|
222
|
-
type,
|
|
223
|
-
config,
|
|
224
|
-
chatModels: [],
|
|
225
|
-
hash: hash,
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
this.currentConfig.modelProviders.push(newModelProvider);
|
|
229
|
-
return newModelProvider;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
public removeModelProvider(id: string) {
|
|
233
|
-
this.currentConfig.modelProviders =
|
|
234
|
-
this.currentConfig.modelProviders.filter((p) => p.id !== id);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
public async updateModelProvider(id: string, name: string, config: any) {
|
|
238
|
-
const provider = this.currentConfig.modelProviders.find(
|
|
239
|
-
(p) => p.id === id,
|
|
240
|
-
);
|
|
241
|
-
if (!provider) throw new Error("Provider not found");
|
|
242
|
-
|
|
243
|
-
provider.name = name;
|
|
244
|
-
provider.config = config;
|
|
245
|
-
return provider;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
public addProviderModel(providerId: string, type: "chat", model: any) {
|
|
249
|
-
const provider = this.currentConfig.modelProviders.find(
|
|
250
|
-
(p) => p.id === providerId,
|
|
251
|
-
);
|
|
252
|
-
if (!provider) throw new Error("Invalid provider id");
|
|
253
|
-
|
|
254
|
-
delete model.type;
|
|
255
|
-
provider.chatModels.push(model);
|
|
256
|
-
return model;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
public removeProviderModel(
|
|
260
|
-
providerId: string,
|
|
261
|
-
type: "chat",
|
|
262
|
-
modelKey: string,
|
|
263
|
-
) {
|
|
264
|
-
const provider = this.currentConfig.modelProviders.find(
|
|
265
|
-
(p) => p.id === providerId,
|
|
266
|
-
);
|
|
267
|
-
if (!provider) throw new Error("Invalid provider id");
|
|
268
|
-
|
|
269
|
-
provider.chatModels = provider.chatModels.filter((m) => m.key !== modelKey);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
public isSetupComplete() {
|
|
273
|
-
return this.currentConfig.setupComplete;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
public markSetupComplete() {
|
|
277
|
-
if (!this.currentConfig.setupComplete) {
|
|
278
|
-
this.currentConfig.setupComplete = true;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
public getUIConfigSections(): UIConfigSections {
|
|
283
|
-
this.ensureInitialized();
|
|
284
|
-
return this.uiConfigSections;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
public getCurrentConfig(): Config {
|
|
288
|
-
this.ensureInitialized();
|
|
289
|
-
return JSON.parse(JSON.stringify(this.currentConfig));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const configManager = new ConfigManager();
|
|
294
|
-
|
|
295
|
-
export default configManager;
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages model providers, MCP servers, and search configuration in memory.
|
|
5
|
+
* Handles environment variable loading, provider hashing, and config updates.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConfigModelProvider, MCPServerConfig, Config, UIConfigSections, Model } from "./config-types";
|
|
8
|
+
import { getModelProvidersUIConfigSection } from "./provider-ui-config";
|
|
9
|
+
import { getEnv } from "./environment-variables";
|
|
10
|
+
import { LANGUAGE_MODELS } from "./language-models-database";
|
|
11
|
+
|
|
12
|
+
// Maps a provider UI key (from provider-ui-config) to the matching
|
|
13
|
+
// `provider` name in the LANGUAGE_MODELS database. Names differ in a few
|
|
14
|
+
// cases (e.g. the "gemini" UI key corresponds to the "Google" model list).
|
|
15
|
+
const PROVIDER_KEY_TO_DB_NAME: Record<string, string> = {
|
|
16
|
+
openai: "openai",
|
|
17
|
+
anthropic: "anthropic",
|
|
18
|
+
gemini: "google",
|
|
19
|
+
groq: "groq",
|
|
20
|
+
deepseek: "deepseek",
|
|
21
|
+
nvidia: "nvidia",
|
|
22
|
+
openrouter: "openrouter",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the default chat models for a provider from the LANGUAGE_MODELS
|
|
27
|
+
* database so that env-based providers expose a usable model list instead of
|
|
28
|
+
* an empty one. Returns [] when no matching provider list exists.
|
|
29
|
+
*/
|
|
30
|
+
const getDefaultChatModels = (providerKey: string): Model[] => {
|
|
31
|
+
const dbName = PROVIDER_KEY_TO_DB_NAME[providerKey] ?? providerKey;
|
|
32
|
+
const entry = LANGUAGE_MODELS.find(
|
|
33
|
+
(p) => p.provider.toLowerCase() === dbName.toLowerCase(),
|
|
34
|
+
);
|
|
35
|
+
if (!entry?.models) return [];
|
|
36
|
+
|
|
37
|
+
return entry.models.map((m: any) => ({
|
|
38
|
+
name: m.name,
|
|
39
|
+
key: m.id,
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const hashObj = (obj: { [key: string]: any }) => {
|
|
44
|
+
const str = JSON.stringify(obj, Object.keys(obj).sort());
|
|
45
|
+
let hash = 0;
|
|
46
|
+
for (let i = 0; i < str.length; i++) {
|
|
47
|
+
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
48
|
+
hash |= 0;
|
|
49
|
+
}
|
|
50
|
+
return String(Math.abs(hash).toString(36));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
class ConfigManager {
|
|
54
|
+
configVersion = 1;
|
|
55
|
+
currentConfig: Config = {
|
|
56
|
+
version: this.configVersion,
|
|
57
|
+
setupComplete: getEnv("SETUP_COMPLETE") === "true" || false,
|
|
58
|
+
preferences: {},
|
|
59
|
+
personalization: {},
|
|
60
|
+
modelProviders: [],
|
|
61
|
+
mcpServers: [],
|
|
62
|
+
search: {
|
|
63
|
+
searxngURL: "",
|
|
64
|
+
tavilyApiKey: "",
|
|
65
|
+
sourceScrapeCount: 3,
|
|
66
|
+
sourceScrapeTimeout: 5,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
uiConfigSections: UIConfigSections = {
|
|
70
|
+
preferences: [],
|
|
71
|
+
personalization: [],
|
|
72
|
+
modelProviders: [],
|
|
73
|
+
mcpServers: [],
|
|
74
|
+
search: [
|
|
75
|
+
{
|
|
76
|
+
name: "SearXNG URL",
|
|
77
|
+
key: "searxngURL",
|
|
78
|
+
type: "string",
|
|
79
|
+
required: false,
|
|
80
|
+
description: "The URL of your SearXNG instance",
|
|
81
|
+
placeholder: "http://localhost:4000",
|
|
82
|
+
default: "",
|
|
83
|
+
scope: "server",
|
|
84
|
+
env: "SEARXNG_API_URL",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "Tavily API Key",
|
|
88
|
+
key: "tavilyApiKey",
|
|
89
|
+
type: "string",
|
|
90
|
+
required: false,
|
|
91
|
+
description: "Your Tavily API key for enhanced search capabilities.",
|
|
92
|
+
placeholder: "tvly-...",
|
|
93
|
+
default: "",
|
|
94
|
+
scope: "server",
|
|
95
|
+
env: "TAVILY_API_KEY",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "Scrape timeout (seconds)",
|
|
99
|
+
key: "sourceScrapeTimeout",
|
|
100
|
+
type: "select",
|
|
101
|
+
options: [
|
|
102
|
+
{ name: "3 seconds", value: "3" },
|
|
103
|
+
{ name: "5 seconds (default)", value: "5" },
|
|
104
|
+
{ name: "10 seconds", value: "10" },
|
|
105
|
+
{ name: "15 seconds", value: "15" },
|
|
106
|
+
{ name: "20 seconds", value: "20" },
|
|
107
|
+
],
|
|
108
|
+
required: false,
|
|
109
|
+
description: "Maximum time to wait when scraping each source URL.",
|
|
110
|
+
default: "5",
|
|
111
|
+
scope: "server",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
private initialized = false;
|
|
117
|
+
|
|
118
|
+
constructor() {
|
|
119
|
+
// Don't initialize in constructor to avoid circular dependency
|
|
120
|
+
// Initialize lazily when config is first accessed
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private ensureInitialized() {
|
|
124
|
+
if (this.initialized) return;
|
|
125
|
+
this.initialize();
|
|
126
|
+
this.initialized = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private initialize() {
|
|
130
|
+
const providerConfigSections = getModelProvidersUIConfigSection();
|
|
131
|
+
this.uiConfigSections.modelProviders = providerConfigSections;
|
|
132
|
+
|
|
133
|
+
const newProviders: ConfigModelProvider[] = [];
|
|
134
|
+
|
|
135
|
+
providerConfigSections.forEach((provider) => {
|
|
136
|
+
|
|
137
|
+
const tempConfig: Record<string, any> = {};
|
|
138
|
+
const required: string[] = [];
|
|
139
|
+
|
|
140
|
+
provider.fields.forEach((field) => {
|
|
141
|
+
tempConfig[field.key] =
|
|
142
|
+
getEnv(field.env!) || field.default || "";
|
|
143
|
+
if (field.required) required.push(field.key);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
let configured = true;
|
|
147
|
+
required.forEach((r) => {
|
|
148
|
+
if (!tempConfig[r]) configured = false;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (configured) {
|
|
152
|
+
const hash = hashObj(tempConfig);
|
|
153
|
+
const exists = this.currentConfig.modelProviders.find(
|
|
154
|
+
(p) => p.hash === hash,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (!exists) {
|
|
158
|
+
newProviders.push({
|
|
159
|
+
id: hash,
|
|
160
|
+
name: `${provider.name}`,
|
|
161
|
+
type: provider.key,
|
|
162
|
+
chatModels: getDefaultChatModels(provider.key),
|
|
163
|
+
config: tempConfig,
|
|
164
|
+
hash: hash,
|
|
165
|
+
isEnvBased: true,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (newProviders.length > 0) {
|
|
172
|
+
this.currentConfig.modelProviders.push(...newProviders);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Search config from env
|
|
176
|
+
this.uiConfigSections.search.forEach((f) => {
|
|
177
|
+
if (f.env && !this.currentConfig.search[f.key]) {
|
|
178
|
+
this.currentConfig.search[f.key] =
|
|
179
|
+
getEnv(f.env) ?? f.default ?? "";
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public getConfig(key: string, defaultValue?: any): any {
|
|
185
|
+
this.ensureInitialized();
|
|
186
|
+
const nested = key.split(".");
|
|
187
|
+
let obj: any = this.currentConfig;
|
|
188
|
+
|
|
189
|
+
for (let i = 0; i < nested.length; i++) {
|
|
190
|
+
const part = nested[i];
|
|
191
|
+
if (obj == null) return defaultValue;
|
|
192
|
+
obj = obj[part];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return obj === undefined ? defaultValue : obj;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public updateConfig(key: string, val: any) {
|
|
199
|
+
const parts = key.split(".");
|
|
200
|
+
if (parts.length === 0) return;
|
|
201
|
+
|
|
202
|
+
let target: any = this.currentConfig;
|
|
203
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
204
|
+
const part = parts[i];
|
|
205
|
+
if (target[part] === null || typeof target[part] !== "object") {
|
|
206
|
+
target[part] = {};
|
|
207
|
+
}
|
|
208
|
+
target = target[part];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const finalKey = parts[parts.length - 1];
|
|
212
|
+
target[finalKey] = val;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public addModelProvider(type: string, name: string, config: any) {
|
|
216
|
+
this.ensureInitialized();
|
|
217
|
+
const hash = hashObj(config);
|
|
218
|
+
|
|
219
|
+
const newModelProvider: ConfigModelProvider = {
|
|
220
|
+
id: hash,
|
|
221
|
+
name,
|
|
222
|
+
type,
|
|
223
|
+
config,
|
|
224
|
+
chatModels: [],
|
|
225
|
+
hash: hash,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
this.currentConfig.modelProviders.push(newModelProvider);
|
|
229
|
+
return newModelProvider;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public removeModelProvider(id: string) {
|
|
233
|
+
this.currentConfig.modelProviders =
|
|
234
|
+
this.currentConfig.modelProviders.filter((p) => p.id !== id);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public async updateModelProvider(id: string, name: string, config: any) {
|
|
238
|
+
const provider = this.currentConfig.modelProviders.find(
|
|
239
|
+
(p) => p.id === id,
|
|
240
|
+
);
|
|
241
|
+
if (!provider) throw new Error("Provider not found");
|
|
242
|
+
|
|
243
|
+
provider.name = name;
|
|
244
|
+
provider.config = config;
|
|
245
|
+
return provider;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public addProviderModel(providerId: string, type: "chat", model: any) {
|
|
249
|
+
const provider = this.currentConfig.modelProviders.find(
|
|
250
|
+
(p) => p.id === providerId,
|
|
251
|
+
);
|
|
252
|
+
if (!provider) throw new Error("Invalid provider id");
|
|
253
|
+
|
|
254
|
+
delete model.type;
|
|
255
|
+
provider.chatModels.push(model);
|
|
256
|
+
return model;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public removeProviderModel(
|
|
260
|
+
providerId: string,
|
|
261
|
+
type: "chat",
|
|
262
|
+
modelKey: string,
|
|
263
|
+
) {
|
|
264
|
+
const provider = this.currentConfig.modelProviders.find(
|
|
265
|
+
(p) => p.id === providerId,
|
|
266
|
+
);
|
|
267
|
+
if (!provider) throw new Error("Invalid provider id");
|
|
268
|
+
|
|
269
|
+
provider.chatModels = provider.chatModels.filter((m) => m.key !== modelKey);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public isSetupComplete() {
|
|
273
|
+
return this.currentConfig.setupComplete;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
public markSetupComplete() {
|
|
277
|
+
if (!this.currentConfig.setupComplete) {
|
|
278
|
+
this.currentConfig.setupComplete = true;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
public getUIConfigSections(): UIConfigSections {
|
|
283
|
+
this.ensureInitialized();
|
|
284
|
+
return this.uiConfigSections;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public getCurrentConfig(): Config {
|
|
288
|
+
this.ensureInitialized();
|
|
289
|
+
return JSON.parse(JSON.stringify(this.currentConfig));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const configManager = new ConfigManager();
|
|
294
|
+
|
|
295
|
+
export default configManager;
|