berget 2.2.6 → 2.2.7
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/.github/workflows/publish.yml +6 -6
- package/.github/workflows/test.yml +11 -5
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +5 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +21 -21
- package/dist/package.json +28 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +54 -62
- package/dist/src/commands/api-keys.js +132 -140
- package/dist/src/commands/auth.js +9 -9
- package/dist/src/commands/autocomplete.js +9 -9
- package/dist/src/commands/billing.js +7 -9
- package/dist/src/commands/chat.js +90 -92
- package/dist/src/commands/clusters.js +12 -12
- package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +5 -7
- package/dist/src/commands/code/__tests__/fake-file-store.js +9 -0
- package/dist/src/commands/code/__tests__/fake-prompter.js +60 -18
- package/dist/src/commands/code/__tests__/setup-flow.test.js +374 -107
- package/dist/src/commands/code/adapters/clack-prompter.js +10 -0
- package/dist/src/commands/code/adapters/fs-file-store.js +8 -3
- package/dist/src/commands/code/adapters/spawn-command-runner.js +15 -11
- package/dist/src/commands/code/auth-sync.js +283 -0
- package/dist/src/commands/code/errors.js +4 -4
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +234 -93
- package/dist/src/commands/code.js +139 -251
- package/dist/src/commands/models.js +13 -15
- package/dist/src/commands/users.js +6 -8
- package/dist/src/constants/command-structure.js +116 -116
- package/dist/src/services/api-key-service.js +43 -48
- package/dist/src/services/auth-service.js +60 -299
- package/dist/src/services/browser-auth.js +278 -0
- package/dist/src/services/chat-service.js +78 -91
- package/dist/src/services/cluster-service.js +6 -6
- package/dist/src/services/collaborator-service.js +5 -8
- package/dist/src/services/flux-service.js +5 -8
- package/dist/src/services/helm-service.js +5 -8
- package/dist/src/services/kubectl-service.js +7 -10
- package/dist/src/utils/config-checker.js +5 -5
- package/dist/src/utils/config-loader.js +25 -25
- package/dist/src/utils/default-api-key.js +23 -23
- package/dist/src/utils/env-manager.js +7 -7
- package/dist/src/utils/error-handler.js +60 -61
- package/dist/src/utils/logger.js +7 -7
- package/dist/src/utils/markdown-renderer.js +2 -2
- package/dist/src/utils/opencode-validator.js +17 -20
- package/dist/src/utils/token-manager.js +38 -11
- package/dist/tests/commands/chat.test.js +24 -24
- package/dist/tests/commands/code.test.js +147 -147
- package/dist/tests/utils/config-loader.test.js +114 -114
- package/dist/tests/utils/env-manager.test.js +57 -57
- package/dist/tests/utils/opencode-validator.test.js +33 -33
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +47 -0
- package/index.ts +42 -48
- package/package.json +28 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +71 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +125 -167
- package/src/commands/api-keys.ts +261 -358
- package/src/commands/auth.ts +24 -30
- package/src/commands/autocomplete.ts +12 -12
- package/src/commands/billing.ts +22 -27
- package/src/commands/chat.ts +230 -323
- package/src/commands/clusters.ts +33 -33
- package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +39 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +107 -69
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -270
- package/src/commands/code/adapters/clack-prompter.ts +50 -38
- package/src/commands/code/adapters/fs-file-store.ts +31 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +33 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +15 -15
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +545 -317
- package/src/commands/code.ts +271 -473
- package/src/commands/index.ts +19 -19
- package/src/commands/models.ts +32 -37
- package/src/commands/users.ts +15 -22
- package/src/constants/command-structure.ts +119 -142
- package/src/services/api-key-service.ts +96 -113
- package/src/services/auth-service.ts +92 -339
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +246 -279
- package/src/services/cluster-service.ts +29 -32
- package/src/services/collaborator-service.ts +13 -18
- package/src/services/flux-service.ts +16 -18
- package/src/services/helm-service.ts +16 -18
- package/src/services/kubectl-service.ts +12 -14
- package/src/types/api.d.ts +924 -926
- package/src/types/json.d.ts +3 -3
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +110 -127
- package/src/utils/default-api-key.ts +81 -93
- package/src/utils/env-manager.ts +36 -40
- package/src/utils/error-handler.ts +83 -78
- package/src/utils/logger.ts +41 -41
- package/src/utils/markdown-renderer.ts +11 -11
- package/src/utils/opencode-validator.ts +51 -56
- package/src/utils/token-manager.ts +84 -64
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +60 -70
- package/tests/commands/code.test.ts +330 -376
- package/tests/utils/config-loader.test.ts +260 -260
- package/tests/utils/env-manager.test.ts +127 -134
- package/tests/utils/opencode-validator.test.ts +58 -63
- package/tsconfig.json +2 -2
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { createAuthenticatedClient } from
|
|
2
|
-
import { logger } from
|
|
1
|
+
import { createAuthenticatedClient } from "../client";
|
|
2
|
+
import { logger } from "../utils/logger";
|
|
3
3
|
|
|
4
4
|
export interface ChatMessage {
|
|
5
|
-
role:
|
|
6
|
-
content: string
|
|
5
|
+
role: "system" | "user" | "assistant";
|
|
6
|
+
content: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface ChatCompletionOptions {
|
|
10
|
-
model?: string
|
|
11
|
-
messages: ChatMessage[]
|
|
12
|
-
temperature?: number
|
|
13
|
-
max_tokens?: number
|
|
14
|
-
stream?: boolean
|
|
15
|
-
top_p?: number
|
|
16
|
-
apiKey?: string
|
|
17
|
-
onChunk?: (chunk: any) => void
|
|
10
|
+
model?: string;
|
|
11
|
+
messages: ChatMessage[];
|
|
12
|
+
temperature?: number;
|
|
13
|
+
max_tokens?: number;
|
|
14
|
+
stream?: boolean;
|
|
15
|
+
top_p?: number;
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
onChunk?: (chunk: any) => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -22,25 +22,25 @@ export interface ChatCompletionOptions {
|
|
|
22
22
|
* Command group: chat
|
|
23
23
|
*/
|
|
24
24
|
export class ChatService {
|
|
25
|
-
private static instance: ChatService
|
|
26
|
-
private client = createAuthenticatedClient()
|
|
25
|
+
private static instance: ChatService;
|
|
26
|
+
private client = createAuthenticatedClient();
|
|
27
27
|
|
|
28
28
|
// Command group name for this service
|
|
29
|
-
public static readonly COMMAND_GROUP =
|
|
29
|
+
public static readonly COMMAND_GROUP = "chat";
|
|
30
30
|
|
|
31
31
|
// Subcommands for this service
|
|
32
32
|
public static readonly COMMANDS = {
|
|
33
|
-
RUN:
|
|
34
|
-
LIST:
|
|
35
|
-
}
|
|
33
|
+
RUN: "run",
|
|
34
|
+
LIST: "list",
|
|
35
|
+
};
|
|
36
36
|
|
|
37
37
|
private constructor() {}
|
|
38
38
|
|
|
39
39
|
public static getInstance(): ChatService {
|
|
40
40
|
if (!ChatService.instance) {
|
|
41
|
-
ChatService.instance = new ChatService()
|
|
41
|
+
ChatService.instance = new ChatService();
|
|
42
42
|
}
|
|
43
|
-
return ChatService.instance
|
|
43
|
+
return ChatService.instance;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -49,177 +49,166 @@ export class ChatService {
|
|
|
49
49
|
*/
|
|
50
50
|
public async createCompletion(options: ChatCompletionOptions): Promise<any> {
|
|
51
51
|
try {
|
|
52
|
-
logger.debug(
|
|
52
|
+
logger.debug("Starting createCompletion method");
|
|
53
53
|
|
|
54
54
|
// Initialize options if undefined
|
|
55
|
-
const optionsCopy = options ? { ...options } : { messages: [] }
|
|
55
|
+
const optionsCopy = options ? { ...options } : { messages: [] };
|
|
56
56
|
|
|
57
57
|
// Check if messages are defined
|
|
58
58
|
if (!optionsCopy.messages || !Array.isArray(optionsCopy.messages)) {
|
|
59
|
-
logger.error(
|
|
60
|
-
optionsCopy.messages = []
|
|
59
|
+
logger.error("messages is undefined or not an array");
|
|
60
|
+
optionsCopy.messages = [];
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// Log the options object
|
|
64
|
-
logger.debug(
|
|
64
|
+
logger.debug("Starting createCompletion with options:");
|
|
65
65
|
try {
|
|
66
66
|
logger.debug(
|
|
67
67
|
JSON.stringify(
|
|
68
68
|
{
|
|
69
69
|
...optionsCopy,
|
|
70
|
-
apiKey: optionsCopy.apiKey ?
|
|
70
|
+
apiKey: optionsCopy.apiKey ? "***" : undefined,
|
|
71
71
|
messages: optionsCopy.messages
|
|
72
72
|
? `${optionsCopy.messages.length} messages`
|
|
73
|
-
:
|
|
73
|
+
: "0 messages",
|
|
74
74
|
},
|
|
75
75
|
null,
|
|
76
|
-
2
|
|
77
|
-
)
|
|
78
|
-
)
|
|
76
|
+
2
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
79
|
} catch (error) {
|
|
80
|
-
logger.error(
|
|
80
|
+
logger.error("Failed to stringify options:", error);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const headers: Record<string, string> = {}
|
|
83
|
+
const headers: Record<string, string> = {};
|
|
84
84
|
|
|
85
85
|
// First try to use the authenticated client (with refresh token support)
|
|
86
86
|
// Only fall back to API key flow if explicitly requested or no auth tokens available
|
|
87
|
-
const { TokenManager } = await import(
|
|
88
|
-
const tokenManagerInstance = TokenManager.getInstance()
|
|
89
|
-
const hasValidAuth =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
87
|
+
const { TokenManager } = await import("../utils/token-manager");
|
|
88
|
+
const tokenManagerInstance = TokenManager.getInstance();
|
|
89
|
+
const hasValidAuth =
|
|
90
|
+
tokenManagerInstance.getAccessToken() && !tokenManagerInstance.isTokenExpired();
|
|
91
|
+
|
|
92
|
+
const envApiKeyForAuth = process.env.BERGET_API_KEY;
|
|
93
|
+
const hasExplicitApiKey = !!optionsCopy.apiKey || !!envApiKeyForAuth;
|
|
93
94
|
|
|
94
95
|
// If we have valid auth tokens and no explicit API key, use authenticated client
|
|
95
96
|
if (hasValidAuth && !hasExplicitApiKey) {
|
|
96
|
-
logger.debug(
|
|
97
|
+
logger.debug("Using authenticated client with refresh token support");
|
|
97
98
|
// Create a copy without apiKey to let the authenticated client handle auth automatically
|
|
98
|
-
const { apiKey, ...optionsWithoutKey } = optionsCopy
|
|
99
|
-
return this.executeCompletion(optionsWithoutKey, {})
|
|
99
|
+
const { apiKey: _apiKey, ...optionsWithoutKey } = optionsCopy;
|
|
100
|
+
return this.executeCompletion(optionsWithoutKey, {});
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
// Check for environment variables first - prioritize this over everything else
|
|
103
|
-
const envApiKey = process.env.BERGET_API_KEY
|
|
104
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
104
105
|
if (envApiKey) {
|
|
105
|
-
logger.debug(
|
|
106
|
-
optionsCopy.apiKey = envApiKey
|
|
106
|
+
logger.debug("Using API key from BERGET_API_KEY environment variable");
|
|
107
|
+
optionsCopy.apiKey = envApiKey;
|
|
107
108
|
// Skip the default API key logic if we already have a key
|
|
108
|
-
return this.executeCompletion(optionsCopy, headers)
|
|
109
|
+
return this.executeCompletion(optionsCopy, headers);
|
|
109
110
|
}
|
|
110
111
|
// If API key is already provided, use it directly
|
|
111
112
|
else if (optionsCopy.apiKey) {
|
|
112
|
-
logger.debug(
|
|
113
|
+
logger.debug("Using API key provided in options");
|
|
113
114
|
// Skip the default API key logic if we already have a key
|
|
114
|
-
return this.executeCompletion(optionsCopy, headers)
|
|
115
|
+
return this.executeCompletion(optionsCopy, headers);
|
|
115
116
|
}
|
|
116
117
|
// Only try to get the default API key if no API key is provided and no env var is set
|
|
117
118
|
else {
|
|
118
|
-
logger.debug(
|
|
119
|
+
logger.debug("No API key provided, trying to get default");
|
|
119
120
|
|
|
120
121
|
try {
|
|
121
122
|
// Import the DefaultApiKeyManager directly
|
|
122
|
-
logger.debug(
|
|
123
|
+
logger.debug("Importing DefaultApiKeyManager");
|
|
123
124
|
|
|
124
|
-
const DefaultApiKeyManager = (
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
125
|
+
const DefaultApiKeyManager = (await import("../utils/default-api-key"))
|
|
126
|
+
.DefaultApiKeyManager;
|
|
127
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
128
128
|
|
|
129
|
-
logger.debug(
|
|
129
|
+
logger.debug("Got DefaultApiKeyManager instance");
|
|
130
130
|
|
|
131
131
|
// Try to get the default API key
|
|
132
|
-
logger.debug(
|
|
132
|
+
logger.debug("Calling promptForDefaultApiKey");
|
|
133
133
|
|
|
134
|
-
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
|
|
134
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
135
135
|
const apiKey =
|
|
136
|
-
defaultApiKeyData?.key ||
|
|
137
|
-
(await defaultApiKeyManager.promptForDefaultApiKey())
|
|
136
|
+
defaultApiKeyData?.key || (await defaultApiKeyManager.promptForDefaultApiKey());
|
|
138
137
|
|
|
139
|
-
logger.debug(`Default API key data exists: ${!!defaultApiKeyData}`)
|
|
140
|
-
logger.debug(
|
|
141
|
-
`promptForDefaultApiKey returned: ${apiKey ? 'a key' : 'null'}`,
|
|
142
|
-
)
|
|
138
|
+
logger.debug(`Default API key data exists: ${!!defaultApiKeyData}`);
|
|
139
|
+
logger.debug(`promptForDefaultApiKey returned: ${apiKey ? "a key" : "null"}`);
|
|
143
140
|
|
|
144
141
|
if (apiKey) {
|
|
145
|
-
logger.debug(
|
|
146
|
-
optionsCopy.apiKey = apiKey
|
|
142
|
+
logger.debug("Using API key from default API key manager");
|
|
143
|
+
optionsCopy.apiKey = apiKey;
|
|
147
144
|
} else {
|
|
148
|
-
logger.warn(
|
|
149
|
-
logger.warn(
|
|
150
|
-
|
|
151
|
-
)
|
|
152
|
-
logger.warn(
|
|
153
|
-
|
|
154
|
-
)
|
|
155
|
-
logger.warn(
|
|
156
|
-
logger.warn(
|
|
157
|
-
|
|
158
|
-
logger.warn(' export BERGET_API_KEY=your_api_key_here')
|
|
159
|
-
logger.warn(' # or for a single command:')
|
|
160
|
-
logger.warn(
|
|
161
|
-
' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it',
|
|
162
|
-
)
|
|
163
|
-
throw new Error('No API key provided and no default API key set')
|
|
145
|
+
logger.warn("No API key available. You need to either:");
|
|
146
|
+
logger.warn('1. Create an API key with: berget api-keys create --name "My Key"');
|
|
147
|
+
logger.warn("2. Set a default API key with: berget api-keys set-default <id>");
|
|
148
|
+
logger.warn("3. Provide an API key with the --api-key option");
|
|
149
|
+
logger.warn("4. Set the BERGET_API_KEY environment variable");
|
|
150
|
+
logger.warn("\nExample:");
|
|
151
|
+
logger.warn(" export BERGET_API_KEY=your_api_key_here");
|
|
152
|
+
logger.warn(" # or for a single command:");
|
|
153
|
+
logger.warn(" BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it");
|
|
154
|
+
throw new Error("No API key provided and no default API key set");
|
|
164
155
|
}
|
|
165
156
|
|
|
166
157
|
// Set the API key in the options
|
|
167
|
-
logger.debug(
|
|
158
|
+
logger.debug("Setting API key in options");
|
|
168
159
|
|
|
169
160
|
// Only set the API key if it's not null
|
|
170
161
|
if (apiKey) {
|
|
171
|
-
optionsCopy.apiKey = apiKey
|
|
162
|
+
optionsCopy.apiKey = apiKey;
|
|
172
163
|
}
|
|
173
164
|
} catch (error) {
|
|
174
|
-
logger.error(
|
|
165
|
+
logger.error("Error getting API key:");
|
|
175
166
|
if (error instanceof Error) {
|
|
176
|
-
logger.error(error.message)
|
|
167
|
+
logger.error(error.message);
|
|
177
168
|
}
|
|
178
|
-
logger.warn(
|
|
179
|
-
|
|
180
|
-
)
|
|
181
|
-
throw new Error('Failed to get API key')
|
|
169
|
+
logger.warn('Please create an API key with: berget api-keys create --name "My Key"');
|
|
170
|
+
throw new Error("Failed to get API key");
|
|
182
171
|
}
|
|
183
172
|
}
|
|
184
173
|
|
|
185
174
|
// Set default model if not provided
|
|
186
175
|
if (!optionsCopy.model) {
|
|
187
|
-
logger.debug(
|
|
188
|
-
optionsCopy.model =
|
|
176
|
+
logger.debug("No model specified, using default: google/gemma-3-27b-it");
|
|
177
|
+
optionsCopy.model = "google/gemma-3-27b-it";
|
|
189
178
|
}
|
|
190
179
|
|
|
191
|
-
logger.debug(
|
|
180
|
+
logger.debug("Chat completion options:");
|
|
192
181
|
logger.debug(
|
|
193
182
|
JSON.stringify(
|
|
194
183
|
{
|
|
195
184
|
...optionsCopy,
|
|
196
|
-
apiKey: optionsCopy.apiKey ?
|
|
185
|
+
apiKey: optionsCopy.apiKey ? "***" : undefined, // Hide the actual API key in debug output
|
|
197
186
|
},
|
|
198
187
|
null,
|
|
199
|
-
2
|
|
200
|
-
)
|
|
201
|
-
)
|
|
188
|
+
2
|
|
189
|
+
)
|
|
190
|
+
);
|
|
202
191
|
|
|
203
|
-
return this.executeCompletion(optionsCopy, headers)
|
|
192
|
+
return this.executeCompletion(optionsCopy, headers);
|
|
204
193
|
} catch (error) {
|
|
205
194
|
// Improved error handling
|
|
206
|
-
let errorMessage =
|
|
195
|
+
let errorMessage = "Failed to create chat completion";
|
|
207
196
|
|
|
208
197
|
if (error instanceof Error) {
|
|
209
198
|
try {
|
|
210
199
|
// Try to parse the error message as JSON
|
|
211
|
-
const parsedError = JSON.parse(error.message)
|
|
200
|
+
const parsedError = JSON.parse(error.message);
|
|
212
201
|
if (parsedError.error && parsedError.error.message) {
|
|
213
|
-
errorMessage = `Chat error: ${parsedError.error.message}
|
|
202
|
+
errorMessage = `Chat error: ${parsedError.error.message}`;
|
|
214
203
|
}
|
|
215
|
-
} catch
|
|
204
|
+
} catch {
|
|
216
205
|
// If parsing fails, use the original error message
|
|
217
|
-
errorMessage = `Chat error: ${error.message}
|
|
206
|
+
errorMessage = `Chat error: ${error.message}`;
|
|
218
207
|
}
|
|
219
208
|
}
|
|
220
209
|
|
|
221
|
-
logger.error(errorMessage)
|
|
222
|
-
throw new Error(errorMessage)
|
|
210
|
+
logger.error(errorMessage);
|
|
211
|
+
throw new Error(errorMessage);
|
|
223
212
|
}
|
|
224
213
|
}
|
|
225
214
|
|
|
@@ -231,104 +220,76 @@ export class ChatService {
|
|
|
231
220
|
*/
|
|
232
221
|
private async executeCompletion(
|
|
233
222
|
options: ChatCompletionOptions,
|
|
234
|
-
headers: Record<string, string> = {}
|
|
223
|
+
headers: Record<string, string> = {}
|
|
235
224
|
): Promise<any> {
|
|
236
225
|
try {
|
|
237
226
|
// If an API key is provided, use it for this request
|
|
238
227
|
if (options.apiKey) {
|
|
239
228
|
// API keys should be sent directly, not with Bearer prefix
|
|
240
|
-
headers[
|
|
229
|
+
headers["Authorization"] = options.apiKey;
|
|
241
230
|
}
|
|
242
231
|
|
|
243
232
|
// Remove apiKey and onChunk from options before sending to API
|
|
244
|
-
const { apiKey, onChunk, ...requestOptions } = options
|
|
233
|
+
const { apiKey, onChunk, ...requestOptions } = options;
|
|
245
234
|
|
|
246
|
-
logger.debug(
|
|
235
|
+
logger.debug("Request options:");
|
|
247
236
|
logger.debug(
|
|
248
237
|
JSON.stringify(
|
|
249
238
|
{
|
|
250
239
|
...requestOptions,
|
|
251
240
|
messages: requestOptions.messages
|
|
252
241
|
? `${requestOptions.messages.length} messages`
|
|
253
|
-
:
|
|
242
|
+
: "0 messages",
|
|
254
243
|
},
|
|
255
244
|
null,
|
|
256
|
-
2
|
|
257
|
-
)
|
|
258
|
-
)
|
|
245
|
+
2
|
|
246
|
+
)
|
|
247
|
+
);
|
|
259
248
|
|
|
260
249
|
// Handle streaming responses differently
|
|
261
250
|
if (requestOptions.stream && onChunk) {
|
|
262
|
-
return await this.handleStreamingResponse(
|
|
263
|
-
{ ...requestOptions, onChunk },
|
|
264
|
-
headers,
|
|
265
|
-
)
|
|
251
|
+
return await this.handleStreamingResponse({ ...requestOptions, onChunk }, headers);
|
|
266
252
|
} else {
|
|
267
253
|
// Ensure model is always defined for the API call
|
|
268
254
|
const requestBody = {
|
|
269
255
|
...requestOptions,
|
|
270
|
-
model: requestOptions.model ||
|
|
271
|
-
}
|
|
256
|
+
model: requestOptions.model || "google/gemma-3-27b-it",
|
|
257
|
+
};
|
|
272
258
|
|
|
273
259
|
// Debug the headers being sent
|
|
274
|
-
logger.debug(
|
|
275
|
-
logger.debug(JSON.stringify(headers, null, 2))
|
|
260
|
+
logger.debug("Headers being sent:");
|
|
261
|
+
logger.debug(JSON.stringify(headers, null, 2));
|
|
276
262
|
|
|
277
|
-
const response = await this.client.POST(
|
|
263
|
+
const response = await this.client.POST("/v1/chat/completions", {
|
|
278
264
|
body: requestBody,
|
|
279
265
|
headers,
|
|
280
|
-
})
|
|
266
|
+
});
|
|
281
267
|
|
|
282
268
|
// Check if response has an error property
|
|
283
|
-
const responseAny = response as any
|
|
284
|
-
if (responseAny && responseAny.error)
|
|
285
|
-
throw new Error(JSON.stringify(responseAny.error))
|
|
269
|
+
const responseAny = response as any;
|
|
270
|
+
if (responseAny && responseAny.error) throw new Error(JSON.stringify(responseAny.error));
|
|
286
271
|
|
|
287
|
-
logger.debug(
|
|
288
|
-
logger.debug(JSON.stringify(response, null, 2))
|
|
272
|
+
logger.debug("API response:");
|
|
273
|
+
logger.debug(JSON.stringify(response, null, 2));
|
|
289
274
|
|
|
290
275
|
// Output the complete response data for debugging
|
|
291
|
-
logger.debug(
|
|
292
|
-
logger.debug(JSON.stringify(response.data, null, 2))
|
|
276
|
+
logger.debug("Complete response data:");
|
|
277
|
+
logger.debug(JSON.stringify(response.data, null, 2));
|
|
293
278
|
|
|
294
|
-
return response.data
|
|
279
|
+
return response.data;
|
|
295
280
|
}
|
|
296
281
|
} catch (requestError) {
|
|
297
282
|
logger.debug(
|
|
298
283
|
`Request error: ${
|
|
299
|
-
requestError instanceof Error
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
)
|
|
304
|
-
throw requestError
|
|
284
|
+
requestError instanceof Error ? requestError.message : String(requestError)
|
|
285
|
+
}`
|
|
286
|
+
);
|
|
287
|
+
throw requestError;
|
|
305
288
|
}
|
|
306
289
|
}
|
|
307
290
|
|
|
308
291
|
/**
|
|
309
|
-
|
|
310
|
-
*/
|
|
311
|
-
private handleNoApiKey(): never {
|
|
312
|
-
// We've exhausted all options for getting an API key
|
|
313
|
-
logger.warn('No API key available. You need to either:')
|
|
314
|
-
logger.warn(
|
|
315
|
-
'1. Create an API key with: berget api-keys create --name "My Key"',
|
|
316
|
-
)
|
|
317
|
-
logger.warn(
|
|
318
|
-
'2. Set a default API key with: berget api-keys set-default <id>',
|
|
319
|
-
)
|
|
320
|
-
logger.warn('3. Provide an API key with the --api-key option')
|
|
321
|
-
logger.warn('4. Set the BERGET_API_KEY environment variable')
|
|
322
|
-
logger.warn('\nExample:')
|
|
323
|
-
logger.warn(' export BERGET_API_KEY=your_api_key_here')
|
|
324
|
-
logger.warn(' # or for a single command:')
|
|
325
|
-
logger.warn(
|
|
326
|
-
' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it',
|
|
327
|
-
)
|
|
328
|
-
throw new Error(
|
|
329
|
-
'No API key available. Please provide an API key or set a default API key.',
|
|
330
|
-
)
|
|
331
|
-
}
|
|
292
|
+
|
|
332
293
|
|
|
333
294
|
/**
|
|
334
295
|
* Handle streaming response from the API
|
|
@@ -338,142 +299,144 @@ export class ChatService {
|
|
|
338
299
|
*/
|
|
339
300
|
private async handleStreamingResponse(
|
|
340
301
|
options: any,
|
|
341
|
-
headers: Record<string, string
|
|
302
|
+
headers: Record<string, string>
|
|
342
303
|
): Promise<any> {
|
|
343
304
|
// Use the same base URL as the client
|
|
344
|
-
const baseUrl = process.env.API_BASE_URL ||
|
|
345
|
-
const url = new URL(`${baseUrl}/v1/chat/completions`)
|
|
305
|
+
const baseUrl = process.env.API_BASE_URL || "https://api.berget.ai";
|
|
306
|
+
const url = new URL(`${baseUrl}/v1/chat/completions`);
|
|
346
307
|
|
|
347
308
|
try {
|
|
348
|
-
logger.debug(`Making streaming request to: ${url.toString()}`)
|
|
349
|
-
logger.debug(`Headers:`, JSON.stringify(headers, null, 2))
|
|
350
|
-
logger.debug(`Body:`, JSON.stringify(options, null, 2))
|
|
309
|
+
logger.debug(`Making streaming request to: ${url.toString()}`);
|
|
310
|
+
logger.debug(`Headers:`, JSON.stringify(headers, null, 2));
|
|
311
|
+
logger.debug(`Body:`, JSON.stringify(options, null, 2));
|
|
351
312
|
|
|
352
313
|
// Make fetch request directly to handle streaming
|
|
353
314
|
const response = await fetch(url.toString(), {
|
|
354
|
-
method:
|
|
315
|
+
method: "POST",
|
|
355
316
|
headers: {
|
|
356
|
-
|
|
357
|
-
Accept:
|
|
317
|
+
"Content-Type": "application/json",
|
|
318
|
+
Accept: "text/event-stream",
|
|
358
319
|
...headers,
|
|
359
320
|
},
|
|
360
321
|
body: JSON.stringify(options),
|
|
361
|
-
})
|
|
322
|
+
});
|
|
362
323
|
|
|
363
|
-
logger.debug(`Response status: ${response.status}`)
|
|
324
|
+
logger.debug(`Response status: ${response.status}`);
|
|
364
325
|
logger.debug(
|
|
365
326
|
`Response headers:`,
|
|
366
|
-
JSON.stringify(Object.fromEntries(response.headers.entries()), null, 2)
|
|
367
|
-
)
|
|
327
|
+
JSON.stringify(Object.fromEntries(response.headers.entries()), null, 2)
|
|
328
|
+
);
|
|
368
329
|
|
|
369
330
|
if (!response.ok) {
|
|
370
|
-
const errorText = await response.text()
|
|
371
|
-
logger.error(
|
|
372
|
-
|
|
373
|
-
)
|
|
374
|
-
logger.error(`Error response: ${errorText}`)
|
|
331
|
+
const errorText = await response.text();
|
|
332
|
+
logger.error(`Stream request failed: ${response.status} ${response.statusText}`);
|
|
333
|
+
logger.error(`Error response: ${errorText}`);
|
|
375
334
|
throw new Error(
|
|
376
|
-
`Stream request failed: ${response.status} ${response.statusText} - ${errorText}
|
|
377
|
-
)
|
|
335
|
+
`Stream request failed: ${response.status} ${response.statusText} - ${errorText}`
|
|
336
|
+
);
|
|
378
337
|
}
|
|
379
338
|
|
|
380
339
|
if (!response.body) {
|
|
381
|
-
throw new Error(
|
|
340
|
+
throw new Error("No response body received");
|
|
382
341
|
}
|
|
383
342
|
|
|
384
343
|
// Process the stream
|
|
385
|
-
const reader = response.body.getReader()
|
|
386
|
-
const decoder = new TextDecoder()
|
|
387
|
-
let fullContent =
|
|
388
|
-
let fullResponse: any = null
|
|
389
|
-
let buffer =
|
|
344
|
+
const reader = response.body.getReader();
|
|
345
|
+
const decoder = new TextDecoder();
|
|
346
|
+
let fullContent = "";
|
|
347
|
+
let fullResponse: any = null;
|
|
348
|
+
let buffer = ""; // Buffer to accumulate partial JSON data
|
|
390
349
|
|
|
391
350
|
while (true) {
|
|
392
|
-
const { done, value } = await reader.read()
|
|
393
|
-
if (done) break
|
|
351
|
+
const { done, value } = await reader.read();
|
|
352
|
+
if (done) break;
|
|
394
353
|
|
|
395
|
-
const chunk = decoder.decode(value, { stream: true })
|
|
396
|
-
logger.debug(`Received chunk: ${chunk.length} bytes`)
|
|
354
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
355
|
+
logger.debug(`Received chunk: ${chunk.length} bytes`);
|
|
397
356
|
|
|
398
357
|
// Add chunk to buffer
|
|
399
|
-
buffer += chunk
|
|
400
|
-
logger.debug(`Added chunk to buffer. Buffer length: ${buffer.length}`)
|
|
358
|
+
buffer += chunk;
|
|
359
|
+
logger.debug(`Added chunk to buffer. Buffer length: ${buffer.length}`);
|
|
401
360
|
|
|
402
361
|
// Process the buffer - it may contain multiple SSE events
|
|
403
|
-
const lines = buffer.split(
|
|
404
|
-
logger.debug(`Processing ${lines.length} lines from buffer`)
|
|
405
|
-
|
|
362
|
+
const lines = buffer.split("\n");
|
|
363
|
+
logger.debug(`Processing ${lines.length} lines from buffer`);
|
|
364
|
+
|
|
406
365
|
// Keep track of processed lines to update buffer
|
|
407
|
-
let processedLines = 0
|
|
408
|
-
|
|
366
|
+
let processedLines = 0;
|
|
367
|
+
|
|
409
368
|
for (let i = 0; i < lines.length; i++) {
|
|
410
|
-
const line = lines[i]
|
|
411
|
-
logger.debug(`Line ${i}: "${line}"`)
|
|
412
|
-
|
|
413
|
-
if (line.startsWith(
|
|
414
|
-
const jsonData = line.slice(5).trim()
|
|
415
|
-
logger.debug(`Extracted JSON data: "${jsonData}"`)
|
|
369
|
+
const line = lines[i];
|
|
370
|
+
logger.debug(`Line ${i}: "${line}"`);
|
|
371
|
+
|
|
372
|
+
if (line.startsWith("data:")) {
|
|
373
|
+
const jsonData = line.slice(5).trim();
|
|
374
|
+
logger.debug(`Extracted JSON data: "${jsonData}"`);
|
|
416
375
|
|
|
417
376
|
// Skip empty data or [DONE] marker
|
|
418
|
-
if (jsonData ===
|
|
419
|
-
logger.debug(`Skipping empty data or [DONE] marker`)
|
|
420
|
-
processedLines = i + 1
|
|
421
|
-
continue
|
|
377
|
+
if (jsonData === "" || jsonData === "[DONE]") {
|
|
378
|
+
logger.debug(`Skipping empty data or [DONE] marker`);
|
|
379
|
+
processedLines = i + 1;
|
|
380
|
+
continue;
|
|
422
381
|
}
|
|
423
382
|
|
|
424
383
|
// Check if JSON looks complete (basic validation)
|
|
425
|
-
if (!jsonData.startsWith(
|
|
426
|
-
logger.warn(
|
|
384
|
+
if (!jsonData.startsWith("{")) {
|
|
385
|
+
logger.warn(
|
|
386
|
+
`JSON data doesn't start with '{', might be partial: "${jsonData.substring(0, 50)}..."`
|
|
387
|
+
);
|
|
427
388
|
// Don't process this line yet, keep it in buffer
|
|
428
|
-
break
|
|
389
|
+
break;
|
|
429
390
|
}
|
|
430
391
|
|
|
431
392
|
// Count braces to check if JSON is complete
|
|
432
|
-
let braceCount = 0
|
|
433
|
-
let inString = false
|
|
434
|
-
let escaped = false
|
|
435
|
-
|
|
393
|
+
let braceCount = 0;
|
|
394
|
+
let inString = false;
|
|
395
|
+
let escaped = false;
|
|
396
|
+
|
|
436
397
|
for (let j = 0; j < jsonData.length; j++) {
|
|
437
|
-
const char = jsonData[j]
|
|
398
|
+
const char = jsonData[j];
|
|
438
399
|
if (escaped) {
|
|
439
|
-
escaped = false
|
|
440
|
-
continue
|
|
400
|
+
escaped = false;
|
|
401
|
+
continue;
|
|
441
402
|
}
|
|
442
|
-
if (char ===
|
|
443
|
-
escaped = true
|
|
444
|
-
continue
|
|
403
|
+
if (char === "\\") {
|
|
404
|
+
escaped = true;
|
|
405
|
+
continue;
|
|
445
406
|
}
|
|
446
407
|
if (char === '"') {
|
|
447
|
-
inString = !inString
|
|
448
|
-
continue
|
|
408
|
+
inString = !inString;
|
|
409
|
+
continue;
|
|
449
410
|
}
|
|
450
|
-
if (!inString && char ===
|
|
451
|
-
braceCount
|
|
452
|
-
} else if (!inString && char ===
|
|
453
|
-
braceCount
|
|
411
|
+
if (!inString && char === "{") {
|
|
412
|
+
braceCount++;
|
|
413
|
+
} else if (!inString && char === "}") {
|
|
414
|
+
braceCount--;
|
|
454
415
|
}
|
|
455
416
|
}
|
|
456
|
-
|
|
417
|
+
|
|
457
418
|
if (braceCount !== 0) {
|
|
458
|
-
logger.warn(
|
|
419
|
+
logger.warn(
|
|
420
|
+
`JSON braces don't balance (${braceCount}), treating as partial: "${jsonData.substring(0, 50)}..."`
|
|
421
|
+
);
|
|
459
422
|
// Don't process this line yet, keep it in buffer
|
|
460
|
-
break
|
|
423
|
+
break;
|
|
461
424
|
}
|
|
462
425
|
|
|
463
426
|
try {
|
|
464
|
-
logger.debug(`Attempting to parse JSON of length: ${jsonData.length}`)
|
|
465
|
-
const parsedData = JSON.parse(jsonData)
|
|
466
|
-
logger.debug(`Successfully parsed JSON: ${JSON.stringify(parsedData, null, 2)}`)
|
|
467
|
-
processedLines = i + 1 // Mark this line as processed
|
|
427
|
+
logger.debug(`Attempting to parse JSON of length: ${jsonData.length}`);
|
|
428
|
+
const parsedData = JSON.parse(jsonData);
|
|
429
|
+
logger.debug(`Successfully parsed JSON: ${JSON.stringify(parsedData, null, 2)}`);
|
|
430
|
+
processedLines = i + 1; // Mark this line as processed
|
|
468
431
|
|
|
469
432
|
// Call the onChunk callback with the parsed data
|
|
470
433
|
if (options.onChunk) {
|
|
471
|
-
options.onChunk(parsedData)
|
|
434
|
+
options.onChunk(parsedData);
|
|
472
435
|
}
|
|
473
436
|
|
|
474
437
|
// Keep track of the full response
|
|
475
438
|
if (!fullResponse) {
|
|
476
|
-
fullResponse = parsedData
|
|
439
|
+
fullResponse = parsedData;
|
|
477
440
|
} else if (
|
|
478
441
|
parsedData.choices &&
|
|
479
442
|
parsedData.choices[0] &&
|
|
@@ -481,35 +444,43 @@ export class ChatService {
|
|
|
481
444
|
) {
|
|
482
445
|
// Accumulate content for the full response
|
|
483
446
|
if (parsedData.choices[0].delta.content) {
|
|
484
|
-
fullContent += parsedData.choices[0].delta.content
|
|
447
|
+
fullContent += parsedData.choices[0].delta.content;
|
|
485
448
|
}
|
|
486
449
|
}
|
|
487
450
|
} catch (e) {
|
|
488
|
-
logger.error(`Error parsing chunk: ${e}`)
|
|
489
|
-
logger.error(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
logger.error(`
|
|
493
|
-
logger.error(`
|
|
494
|
-
|
|
451
|
+
logger.error(`Error parsing chunk: ${e}`);
|
|
452
|
+
logger.error(
|
|
453
|
+
`JSON parse error at position ${(e as any).message?.match(/position (\d+)/)?.[1] || "unknown"}`
|
|
454
|
+
);
|
|
455
|
+
logger.error(`Problematic chunk length: ${jsonData.length}`);
|
|
456
|
+
logger.error(`Problematic chunk content: "${jsonData}"`);
|
|
457
|
+
logger.error(`Chunk starts with: "${jsonData.substring(0, 50)}..."`);
|
|
458
|
+
logger.error(`Chunk ends with: "...${jsonData.substring(jsonData.length - 50)}"`);
|
|
459
|
+
|
|
495
460
|
// Show character codes around the error position
|
|
496
|
-
const errorPos = parseInt((e as any).message?.match(/position (\d+)/)?.[1] ||
|
|
461
|
+
const errorPos = parseInt((e as any).message?.match(/position (\d+)/)?.[1] || "0");
|
|
497
462
|
if (errorPos > 0) {
|
|
498
|
-
const start = Math.max(0, errorPos - 20)
|
|
499
|
-
const end = Math.min(jsonData.length, errorPos + 20)
|
|
500
|
-
logger.error(`Context around error position ${errorPos}:`)
|
|
501
|
-
logger.error(`"${jsonData.substring(start, end)}"`)
|
|
502
|
-
logger.error(
|
|
463
|
+
const start = Math.max(0, errorPos - 20);
|
|
464
|
+
const end = Math.min(jsonData.length, errorPos + 20);
|
|
465
|
+
logger.error(`Context around error position ${errorPos}:`);
|
|
466
|
+
logger.error(`"${jsonData.substring(start, end)}"`);
|
|
467
|
+
logger.error(
|
|
468
|
+
`Character codes: ${Array.from(jsonData.substring(start, end))
|
|
469
|
+
.map(c => c.charCodeAt(0))
|
|
470
|
+
.join(" ")}`
|
|
471
|
+
);
|
|
503
472
|
}
|
|
504
473
|
}
|
|
505
474
|
}
|
|
506
475
|
}
|
|
507
|
-
|
|
476
|
+
|
|
508
477
|
// Update buffer to only contain unprocessed lines
|
|
509
478
|
if (processedLines > 0) {
|
|
510
|
-
const remainingLines = lines.slice(processedLines)
|
|
511
|
-
buffer = remainingLines.join(
|
|
512
|
-
logger.debug(
|
|
479
|
+
const remainingLines = lines.slice(processedLines);
|
|
480
|
+
buffer = remainingLines.join("\n");
|
|
481
|
+
logger.debug(
|
|
482
|
+
`Updated buffer. Remaining lines: ${remainingLines.length}, Buffer length: ${buffer.length}`
|
|
483
|
+
);
|
|
513
484
|
}
|
|
514
485
|
}
|
|
515
486
|
|
|
@@ -517,23 +488,19 @@ export class ChatService {
|
|
|
517
488
|
if (fullResponse) {
|
|
518
489
|
if (fullContent) {
|
|
519
490
|
fullResponse.choices[0].message = {
|
|
520
|
-
role:
|
|
491
|
+
role: "assistant",
|
|
521
492
|
content: fullContent,
|
|
522
|
-
}
|
|
493
|
+
};
|
|
523
494
|
}
|
|
524
|
-
return fullResponse
|
|
495
|
+
return fullResponse;
|
|
525
496
|
}
|
|
526
497
|
|
|
527
498
|
return {
|
|
528
|
-
choices: [{ message: { role:
|
|
529
|
-
}
|
|
499
|
+
choices: [{ message: { role: "assistant", content: fullContent } }],
|
|
500
|
+
};
|
|
530
501
|
} catch (error) {
|
|
531
|
-
logger.error(
|
|
532
|
-
|
|
533
|
-
error instanceof Error ? error.message : String(error)
|
|
534
|
-
}`,
|
|
535
|
-
)
|
|
536
|
-
throw error
|
|
502
|
+
logger.error(`Streaming error: ${error instanceof Error ? error.message : String(error)}`);
|
|
503
|
+
throw error;
|
|
537
504
|
}
|
|
538
505
|
}
|
|
539
506
|
|
|
@@ -544,45 +511,45 @@ export class ChatService {
|
|
|
544
511
|
public async listModels(apiKey?: string): Promise<any> {
|
|
545
512
|
try {
|
|
546
513
|
// Check for environment variable first, then fallback to provided API key
|
|
547
|
-
const envApiKey = process.env.BERGET_API_KEY
|
|
548
|
-
const effectiveApiKey = envApiKey || apiKey
|
|
514
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
515
|
+
const effectiveApiKey = envApiKey || apiKey;
|
|
549
516
|
|
|
550
517
|
if (effectiveApiKey) {
|
|
551
518
|
const headers = {
|
|
552
519
|
Authorization: effectiveApiKey,
|
|
553
|
-
}
|
|
520
|
+
};
|
|
554
521
|
|
|
555
|
-
const { data, error } = await this.client.GET(
|
|
556
|
-
if (error) throw new Error(JSON.stringify(error))
|
|
557
|
-
return data
|
|
522
|
+
const { data, error } = await this.client.GET("/v1/models", { headers });
|
|
523
|
+
if (error) throw new Error(JSON.stringify(error));
|
|
524
|
+
return data;
|
|
558
525
|
} else {
|
|
559
|
-
const { data, error } = await this.client.GET(
|
|
560
|
-
if (error) throw new Error(JSON.stringify(error))
|
|
561
|
-
return data
|
|
526
|
+
const { data, error } = await this.client.GET("/v1/models");
|
|
527
|
+
if (error) throw new Error(JSON.stringify(error));
|
|
528
|
+
return data;
|
|
562
529
|
}
|
|
563
530
|
} catch (error) {
|
|
564
531
|
// Improved error handling
|
|
565
|
-
let errorMessage =
|
|
532
|
+
let errorMessage = "Failed to list models";
|
|
566
533
|
|
|
567
534
|
if (error instanceof Error) {
|
|
568
535
|
try {
|
|
569
536
|
// Try to parse the error message as JSON
|
|
570
|
-
const parsedError = JSON.parse(error.message)
|
|
537
|
+
const parsedError = JSON.parse(error.message);
|
|
571
538
|
if (parsedError.error) {
|
|
572
539
|
errorMessage = `Models error: ${
|
|
573
|
-
typeof parsedError.error ===
|
|
540
|
+
typeof parsedError.error === "string"
|
|
574
541
|
? parsedError.error
|
|
575
542
|
: parsedError.error.message || JSON.stringify(parsedError.error)
|
|
576
|
-
}
|
|
543
|
+
}`;
|
|
577
544
|
}
|
|
578
|
-
} catch
|
|
545
|
+
} catch {
|
|
579
546
|
// If parsing fails, use the original error message
|
|
580
|
-
errorMessage = `Models error: ${error.message}
|
|
547
|
+
errorMessage = `Models error: ${error.message}`;
|
|
581
548
|
}
|
|
582
549
|
}
|
|
583
550
|
|
|
584
|
-
logger.error(errorMessage)
|
|
585
|
-
throw new Error(errorMessage)
|
|
551
|
+
logger.error(errorMessage);
|
|
552
|
+
throw new Error(errorMessage);
|
|
586
553
|
}
|
|
587
554
|
}
|
|
588
555
|
}
|