berget 2.0.6 → 2.1.1

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.
@@ -269,6 +269,7 @@ class ChatService {
269
269
  * @returns A promise that resolves when the stream is complete
270
270
  */
271
271
  handleStreamingResponse(options, headers) {
272
+ var _a, _b, _c, _d;
272
273
  return __awaiter(this, void 0, void 0, function* () {
273
274
  // Use the same base URL as the client
274
275
  const baseUrl = process.env.API_BASE_URL || 'https://api.berget.ai';
@@ -299,22 +300,74 @@ class ChatService {
299
300
  const decoder = new TextDecoder();
300
301
  let fullContent = '';
301
302
  let fullResponse = null;
303
+ let buffer = ''; // Buffer to accumulate partial JSON data
302
304
  while (true) {
303
305
  const { done, value } = yield reader.read();
304
306
  if (done)
305
307
  break;
306
308
  const chunk = decoder.decode(value, { stream: true });
307
309
  logger_1.logger.debug(`Received chunk: ${chunk.length} bytes`);
308
- // Process the chunk - it may contain multiple SSE events
309
- const lines = chunk.split('\n');
310
- for (const line of lines) {
310
+ // Add chunk to buffer
311
+ buffer += chunk;
312
+ logger_1.logger.debug(`Added chunk to buffer. Buffer length: ${buffer.length}`);
313
+ // Process the buffer - it may contain multiple SSE events
314
+ const lines = buffer.split('\n');
315
+ logger_1.logger.debug(`Processing ${lines.length} lines from buffer`);
316
+ // Keep track of processed lines to update buffer
317
+ let processedLines = 0;
318
+ for (let i = 0; i < lines.length; i++) {
319
+ const line = lines[i];
320
+ logger_1.logger.debug(`Line ${i}: "${line}"`);
311
321
  if (line.startsWith('data:')) {
312
322
  const jsonData = line.slice(5).trim();
323
+ logger_1.logger.debug(`Extracted JSON data: "${jsonData}"`);
313
324
  // Skip empty data or [DONE] marker
314
- if (jsonData === '' || jsonData === '[DONE]')
325
+ if (jsonData === '' || jsonData === '[DONE]') {
326
+ logger_1.logger.debug(`Skipping empty data or [DONE] marker`);
327
+ processedLines = i + 1;
315
328
  continue;
329
+ }
330
+ // Check if JSON looks complete (basic validation)
331
+ if (!jsonData.startsWith('{')) {
332
+ logger_1.logger.warn(`JSON data doesn't start with '{', might be partial: "${jsonData.substring(0, 50)}..."`);
333
+ // Don't process this line yet, keep it in buffer
334
+ break;
335
+ }
336
+ // Count braces to check if JSON is complete
337
+ let braceCount = 0;
338
+ let inString = false;
339
+ let escaped = false;
340
+ for (let j = 0; j < jsonData.length; j++) {
341
+ const char = jsonData[j];
342
+ if (escaped) {
343
+ escaped = false;
344
+ continue;
345
+ }
346
+ if (char === '\\') {
347
+ escaped = true;
348
+ continue;
349
+ }
350
+ if (char === '"') {
351
+ inString = !inString;
352
+ continue;
353
+ }
354
+ if (!inString && char === '{') {
355
+ braceCount++;
356
+ }
357
+ else if (!inString && char === '}') {
358
+ braceCount--;
359
+ }
360
+ }
361
+ if (braceCount !== 0) {
362
+ logger_1.logger.warn(`JSON braces don't balance (${braceCount}), treating as partial: "${jsonData.substring(0, 50)}..."`);
363
+ // Don't process this line yet, keep it in buffer
364
+ break;
365
+ }
316
366
  try {
367
+ logger_1.logger.debug(`Attempting to parse JSON of length: ${jsonData.length}`);
317
368
  const parsedData = JSON.parse(jsonData);
369
+ logger_1.logger.debug(`Successfully parsed JSON: ${JSON.stringify(parsedData, null, 2)}`);
370
+ processedLines = i + 1; // Mark this line as processed
318
371
  // Call the onChunk callback with the parsed data
319
372
  if (options.onChunk) {
320
373
  options.onChunk(parsedData);
@@ -334,10 +387,29 @@ class ChatService {
334
387
  }
335
388
  catch (e) {
336
389
  logger_1.logger.error(`Error parsing chunk: ${e}`);
337
- logger_1.logger.debug(`Problematic chunk: ${jsonData}`);
390
+ logger_1.logger.error(`JSON parse error at position ${((_b = (_a = e.message) === null || _a === void 0 ? void 0 : _a.match(/position (\d+)/)) === null || _b === void 0 ? void 0 : _b[1]) || 'unknown'}`);
391
+ logger_1.logger.error(`Problematic chunk length: ${jsonData.length}`);
392
+ logger_1.logger.error(`Problematic chunk content: "${jsonData}"`);
393
+ logger_1.logger.error(`Chunk starts with: "${jsonData.substring(0, 50)}..."`);
394
+ logger_1.logger.error(`Chunk ends with: "...${jsonData.substring(jsonData.length - 50)}"`);
395
+ // Show character codes around the error position
396
+ const errorPos = parseInt(((_d = (_c = e.message) === null || _c === void 0 ? void 0 : _c.match(/position (\d+)/)) === null || _d === void 0 ? void 0 : _d[1]) || '0');
397
+ if (errorPos > 0) {
398
+ const start = Math.max(0, errorPos - 20);
399
+ const end = Math.min(jsonData.length, errorPos + 20);
400
+ logger_1.logger.error(`Context around error position ${errorPos}:`);
401
+ logger_1.logger.error(`"${jsonData.substring(start, end)}"`);
402
+ logger_1.logger.error(`Character codes: ${Array.from(jsonData.substring(start, end)).map(c => c.charCodeAt(0)).join(' ')}`);
403
+ }
338
404
  }
339
405
  }
340
406
  }
407
+ // Update buffer to only contain unprocessed lines
408
+ if (processedLines > 0) {
409
+ const remainingLines = lines.slice(processedLines);
410
+ buffer = remainingLines.join('\n');
411
+ logger_1.logger.debug(`Updated buffer. Remaining lines: ${remainingLines.length}, Buffer length: ${buffer.length}`);
412
+ }
341
413
  }
342
414
  // Construct the final response object similar to non-streaming response
343
415
  if (fullResponse) {
@@ -39,6 +39,12 @@ class ConfigLoader {
39
39
  }
40
40
  return ConfigLoader.instance;
41
41
  }
42
+ /**
43
+ * Clear the singleton instance (for testing purposes)
44
+ */
45
+ static clearInstance() {
46
+ ConfigLoader.instance = null;
47
+ }
42
48
  /**
43
49
  * Load configuration from opencode.json
44
50
  */
@@ -65,72 +71,122 @@ class ConfigLoader {
65
71
  */
66
72
  getAgentConfig(agentName) {
67
73
  var _a;
68
- const config = this.loadConfig();
69
- return ((_a = config.agent) === null || _a === void 0 ? void 0 : _a[agentName]) || null;
74
+ try {
75
+ const config = this.loadConfig();
76
+ return ((_a = config.agent) === null || _a === void 0 ? void 0 : _a[agentName]) || null;
77
+ }
78
+ catch (error) {
79
+ // Config file doesn't exist, return null
80
+ return null;
81
+ }
70
82
  }
71
83
  /**
72
84
  * Get all agent configurations
73
85
  */
74
86
  getAllAgentConfigs() {
75
- const config = this.loadConfig();
76
- return config.agent || {};
87
+ try {
88
+ const config = this.loadConfig();
89
+ return config.agent || {};
90
+ }
91
+ catch (error) {
92
+ // Config file doesn't exist, return empty object
93
+ return {};
94
+ }
77
95
  }
78
96
  /**
79
97
  * Get model configuration
80
98
  */
81
99
  getModelConfig() {
82
- const config = this.loadConfig();
83
- // Extract from config or fall back to defaults
84
- const primary = config.model || 'berget/deepseek-r1';
85
- const small = config.small_model || 'berget/gpt-oss';
86
- return { primary, small };
100
+ try {
101
+ const config = this.loadConfig();
102
+ // Extract from config or fall back to defaults
103
+ const primary = config.model || 'berget/glm-4.7';
104
+ const small = config.small_model || 'berget/gpt-oss';
105
+ return { primary, small };
106
+ }
107
+ catch (error) {
108
+ // Fallback to defaults when no config exists (init scenario)
109
+ return {
110
+ primary: 'berget/glm-4.7',
111
+ small: 'berget/gpt-oss',
112
+ };
113
+ }
87
114
  }
88
115
  /**
89
116
  * Get provider model configuration
90
117
  */
91
118
  getProviderModels() {
92
119
  var _a, _b;
93
- const config = this.loadConfig();
94
- // Extract from provider configuration
95
- if ((_b = (_a = config.provider) === null || _a === void 0 ? void 0 : _a.berget) === null || _b === void 0 ? void 0 : _b.models) {
96
- return config.provider.berget.models;
120
+ try {
121
+ const config = this.loadConfig();
122
+ // Extract from provider configuration
123
+ if ((_b = (_a = config.provider) === null || _a === void 0 ? void 0 : _a.berget) === null || _b === void 0 ? void 0 : _b.models) {
124
+ return config.provider.berget.models;
125
+ }
126
+ }
127
+ catch (error) {
128
+ // Config file doesn't exist, use fallback defaults
97
129
  }
98
130
  // Fallback to defaults
99
131
  return {
100
- 'deepseek-r1': {
101
- name: 'GLM-4.6',
102
- limit: { output: 4000, context: 90000 }
132
+ 'glm-4.7': {
133
+ name: 'GLM-4.7',
134
+ limit: { output: 4000, context: 90000 },
103
135
  },
104
136
  'gpt-oss': {
105
137
  name: 'GPT-OSS',
106
- limit: { output: 4000, context: 128000 }
138
+ limit: { output: 4000, context: 128000 },
139
+ modalities: {
140
+ input: ['text', 'image'],
141
+ output: ['text'],
142
+ },
107
143
  },
108
144
  'llama-8b': {
109
145
  name: 'llama-3.1-8b',
110
- limit: { output: 4000, context: 128000 }
111
- }
146
+ limit: { output: 4000, context: 128000 },
147
+ },
112
148
  };
113
149
  }
114
150
  /**
115
151
  * Get command configurations
116
152
  */
117
153
  getCommandConfigs() {
118
- const config = this.loadConfig();
119
- return config.command || {};
154
+ try {
155
+ const config = this.loadConfig();
156
+ return config.command || {};
157
+ }
158
+ catch (error) {
159
+ // Config file doesn't exist, return empty object
160
+ return {};
161
+ }
120
162
  }
121
163
  /**
122
164
  * Get watcher configuration
123
165
  */
124
166
  getWatcherConfig() {
125
- const config = this.loadConfig();
126
- return config.watcher || { ignore: ['node_modules', 'dist', '.git', 'coverage'] };
167
+ try {
168
+ const config = this.loadConfig();
169
+ return (config.watcher || {
170
+ ignore: ['node_modules', 'dist', '.git', 'coverage'],
171
+ });
172
+ }
173
+ catch (error) {
174
+ // Config file doesn't exist, return default watcher config
175
+ return { ignore: ['node_modules', 'dist', '.git', 'coverage'] };
176
+ }
127
177
  }
128
178
  /**
129
179
  * Get provider configuration
130
180
  */
131
181
  getProviderConfig() {
132
- const config = this.loadConfig();
133
- return config.provider || {};
182
+ try {
183
+ const config = this.loadConfig();
184
+ return config.provider || {};
185
+ }
186
+ catch (error) {
187
+ // Config file doesn't exist, return empty object
188
+ return {};
189
+ }
134
190
  }
135
191
  /**
136
192
  * Check if an agent exists
@@ -149,14 +205,14 @@ class ConfigLoader {
149
205
  */
150
206
  getPrimaryAgentNames() {
151
207
  const agents = this.getAllAgentConfigs();
152
- return Object.keys(agents).filter(name => agents[name].mode === 'primary');
208
+ return Object.keys(agents).filter((name) => agents[name].mode === 'primary');
153
209
  }
154
210
  /**
155
211
  * Get list of subagents (mode: 'subagent')
156
212
  */
157
213
  getSubagentNames() {
158
214
  const agents = this.getAllAgentConfigs();
159
- return Object.keys(agents).filter(name => agents[name].mode === 'subagent');
215
+ return Object.keys(agents).filter((name) => agents[name].mode === 'subagent');
160
216
  }
161
217
  /**
162
218
  * Reload configuration from file
@@ -47,13 +47,13 @@ vitest_1.vi.mock('readline', () => ({
47
47
  vitest_1.vi.clearAllMocks();
48
48
  });
49
49
  (0, vitest_1.describe)('chat run command', () => {
50
- (0, vitest_1.it)('should use openai/gpt-oss as default model', () => {
50
+ (0, vitest_1.it)('should use berget/glm-4.7 as default model', () => {
51
51
  const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
52
52
  const runCommand = chatCommand === null || chatCommand === void 0 ? void 0 : chatCommand.commands.find((cmd) => cmd.name() === 'run');
53
53
  (0, vitest_1.expect)(runCommand).toBeDefined();
54
54
  // Check the help text which contains the default model
55
55
  const helpText = runCommand === null || runCommand === void 0 ? void 0 : runCommand.helpInformation();
56
- (0, vitest_1.expect)(helpText).toContain('openai/gpt-oss');
56
+ (0, vitest_1.expect)(helpText).toContain('glm-4.7');
57
57
  });
58
58
  (0, vitest_1.it)('should have streaming enabled by default', () => {
59
59
  const chatCommand = program.commands.find((cmd) => cmd.name() === 'chat');
@@ -188,14 +188,14 @@ vitest_1.vi.mock('readline', () => ({
188
188
  (0, vitest_1.it)('should create opencode.json with correct structure', () => __awaiter(void 0, void 0, void 0, function* () {
189
189
  // This tests the expected config structure
190
190
  const expectedConfig = {
191
- model: 'berget/deepseek-r1',
191
+ model: 'berget/glm-4-6',
192
192
  apiKey: 'test-api-key',
193
193
  projectName: 'testproject',
194
194
  provider: 'berget',
195
195
  created: vitest_1.expect.any(String),
196
196
  version: '1.0.0',
197
197
  };
198
- (0, vitest_1.expect)(expectedConfig.model).toBe('berget/deepseek-r1');
198
+ (0, vitest_1.expect)(expectedConfig.model).toBe('berget/glm-4-6');
199
199
  (0, vitest_1.expect)(expectedConfig.provider).toBe('berget');
200
200
  (0, vitest_1.expect)(expectedConfig.version).toBe('1.0.0');
201
201
  }));
@@ -230,7 +230,7 @@ vitest_1.vi.mock('readline', () => ({
230
230
  });
231
231
  (0, vitest_1.it)('should load configuration from opencode.json', () => __awaiter(void 0, void 0, void 0, function* () {
232
232
  const mockConfig = {
233
- model: 'berget/deepseek-r1',
233
+ model: 'berget/glm-4-6',
234
234
  apiKey: 'test-api-key',
235
235
  projectName: 'testproject',
236
236
  provider: 'berget',
@@ -253,7 +253,7 @@ vitest_1.vi.mock('readline', () => ({
253
253
  return { on: vitest_1.vi.fn() };
254
254
  });
255
255
  // Verify config structure expectations
256
- (0, vitest_1.expect)(mockConfig.model).toBe('berget/deepseek-r1');
256
+ (0, vitest_1.expect)(mockConfig.model).toBe('berget/glm-4-6');
257
257
  (0, vitest_1.expect)(mockConfig.apiKey).toBe('test-api-key');
258
258
  (0, vitest_1.expect)(mockConfig.projectName).toBe('testproject');
259
259
  }));