berget 1.2.0 → 1.3.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.
- package/-27b-it +0 -0
- package/dist/package.json +5 -1
- package/dist/src/client.js +33 -43
- package/dist/src/commands/chat.js +50 -22
- package/dist/src/commands/models.js +2 -2
- package/dist/src/services/chat-service.js +230 -125
- package/dist/src/utils/default-api-key.js +20 -37
- package/dist/src/utils/logger.js +160 -0
- package/dist/src/utils/markdown-renderer.js +73 -0
- package/dist/src/utils/token-manager.js +6 -9
- package/package.json +5 -1
- package/src/client.ts +75 -90
- package/src/commands/chat.ts +64 -29
- package/src/commands/models.ts +4 -4
- package/src/services/chat-service.ts +386 -184
- package/src/utils/default-api-key.ts +20 -37
- package/src/utils/logger.ts +159 -0
- package/src/utils/markdown-renderer.ts +68 -0
- package/src/utils/token-manager.ts +6 -5
package/-27b-it
ADDED
|
File without changes
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "berget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"berget": "dist/index.js"
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"description": "This is a cli command for interacting with the AI infrastructure provider Berget",
|
|
21
21
|
"devDependencies": {
|
|
22
|
+
"@types/marked": "^5.0.2",
|
|
23
|
+
"@types/marked-terminal": "^6.1.1",
|
|
22
24
|
"@types/node": "^20.11.20",
|
|
23
25
|
"tsx": "^4.19.3",
|
|
24
26
|
"typescript": "^5.3.3"
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
"chalk": "^4.1.2",
|
|
28
30
|
"commander": "^12.0.0",
|
|
29
31
|
"fs-extra": "^11.3.0",
|
|
32
|
+
"marked": "^9.1.6",
|
|
33
|
+
"marked-terminal": "^6.2.0",
|
|
30
34
|
"open": "^9.1.0",
|
|
31
35
|
"openapi-fetch": "^0.9.1",
|
|
32
36
|
"openapi-typescript": "^6.7.4",
|
package/dist/src/client.js
CHANGED
|
@@ -12,21 +12,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.createAuthenticatedClient = exports.clearAuthToken = exports.saveAuthToken = exports.getAuthToken = exports.apiClient = void 0;
|
|
15
|
+
exports.createAuthenticatedClient = exports.clearAuthToken = exports.saveAuthToken = exports.getAuthToken = exports.apiClient = exports.API_BASE_URL = void 0;
|
|
16
16
|
const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
|
|
17
17
|
const chalk_1 = __importDefault(require("chalk"));
|
|
18
18
|
const token_manager_1 = require("./utils/token-manager");
|
|
19
|
+
const logger_1 = require("./utils/logger");
|
|
19
20
|
// API Base URL
|
|
20
21
|
// Use --local flag to test against local API
|
|
21
22
|
const isLocalMode = process.argv.includes('--local');
|
|
22
|
-
|
|
23
|
+
exports.API_BASE_URL = process.env.BERGET_API_URL ||
|
|
23
24
|
(isLocalMode ? 'http://localhost:3000' : 'https://api.berget.ai');
|
|
24
|
-
if (isLocalMode && !process.env.BERGET_API_URL
|
|
25
|
-
|
|
25
|
+
if (isLocalMode && !process.env.BERGET_API_URL) {
|
|
26
|
+
logger_1.logger.debug('Using local API endpoint: http://localhost:3000');
|
|
26
27
|
}
|
|
27
28
|
// Create a typed client for the Berget API
|
|
28
29
|
exports.apiClient = (0, openapi_fetch_1.default)({
|
|
29
|
-
baseUrl: API_BASE_URL,
|
|
30
|
+
baseUrl: exports.API_BASE_URL,
|
|
30
31
|
headers: {
|
|
31
32
|
'Content-Type': 'application/json',
|
|
32
33
|
Accept: 'application/json',
|
|
@@ -51,12 +52,12 @@ exports.clearAuthToken = clearAuthToken;
|
|
|
51
52
|
// Create an authenticated client with refresh token support
|
|
52
53
|
const createAuthenticatedClient = () => {
|
|
53
54
|
const tokenManager = token_manager_1.TokenManager.getInstance();
|
|
54
|
-
if (!tokenManager.getAccessToken()
|
|
55
|
-
|
|
55
|
+
if (!tokenManager.getAccessToken()) {
|
|
56
|
+
logger_1.logger.debug('No authentication token found. Please run `berget auth login` first.');
|
|
56
57
|
}
|
|
57
58
|
// Create the base client
|
|
58
59
|
const client = (0, openapi_fetch_1.default)({
|
|
59
|
-
baseUrl: API_BASE_URL,
|
|
60
|
+
baseUrl: exports.API_BASE_URL,
|
|
60
61
|
headers: tokenManager.getAccessToken()
|
|
61
62
|
? {
|
|
62
63
|
Authorization: `Bearer ${tokenManager.getAccessToken()}`,
|
|
@@ -75,13 +76,14 @@ const createAuthenticatedClient = () => {
|
|
|
75
76
|
if (typeof target[prop] === 'function' &&
|
|
76
77
|
['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(String(prop))) {
|
|
77
78
|
return (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
-
var _a, _b;
|
|
79
|
+
var _a, _b, _c, _d;
|
|
79
80
|
// Check if token is expired before making the request
|
|
80
81
|
if (tokenManager.isTokenExpired() && tokenManager.getRefreshToken()) {
|
|
81
82
|
yield refreshAccessToken(tokenManager);
|
|
82
83
|
}
|
|
83
84
|
// Update the Authorization header with the current token
|
|
84
|
-
if (
|
|
85
|
+
if (!((_b = (_a = args[1]) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b.Authorization) &&
|
|
86
|
+
tokenManager.getAccessToken()) {
|
|
85
87
|
if (!args[1])
|
|
86
88
|
args[1] = {};
|
|
87
89
|
if (!args[1].headers)
|
|
@@ -94,11 +96,9 @@ const createAuthenticatedClient = () => {
|
|
|
94
96
|
result = yield target[prop](...args);
|
|
95
97
|
}
|
|
96
98
|
catch (requestError) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: String(requestError)}`));
|
|
101
|
-
}
|
|
99
|
+
logger_1.logger.debug(`Request error: ${requestError instanceof Error
|
|
100
|
+
? requestError.message
|
|
101
|
+
: String(requestError)}`);
|
|
102
102
|
return {
|
|
103
103
|
error: {
|
|
104
104
|
message: `Request failed: ${requestError instanceof Error
|
|
@@ -113,7 +113,8 @@ const createAuthenticatedClient = () => {
|
|
|
113
113
|
let isAuthError = false;
|
|
114
114
|
try {
|
|
115
115
|
// Standard 401 Unauthorized
|
|
116
|
-
if (typeof result.error === 'object' &&
|
|
116
|
+
if (typeof result.error === 'object' &&
|
|
117
|
+
result.error.status === 401) {
|
|
117
118
|
isAuthError = true;
|
|
118
119
|
}
|
|
119
120
|
// OAuth specific errors
|
|
@@ -121,8 +122,8 @@ const createAuthenticatedClient = () => {
|
|
|
121
122
|
(result.error.error.code === 'invalid_token' ||
|
|
122
123
|
result.error.error.code === 'token_expired' ||
|
|
123
124
|
result.error.error.message === 'Invalid API key' ||
|
|
124
|
-
((
|
|
125
|
-
((
|
|
125
|
+
((_c = result.error.error.message) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes('token')) ||
|
|
126
|
+
((_d = result.error.error.message) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes('unauthorized')))) {
|
|
126
127
|
isAuthError = true;
|
|
127
128
|
}
|
|
128
129
|
// Message-based detection as fallback
|
|
@@ -143,15 +144,11 @@ const createAuthenticatedClient = () => {
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
if (isAuthError && tokenManager.getRefreshToken()) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
console.log(chalk_1.default.yellow(`DEBUG: Error details: ${JSON.stringify(result.error, null, 2)}`));
|
|
149
|
-
}
|
|
147
|
+
logger_1.logger.debug('Auth error detected, attempting token refresh');
|
|
148
|
+
logger_1.logger.debug(`Error details: ${JSON.stringify(result.error, null, 2)}`);
|
|
150
149
|
const refreshed = yield refreshAccessToken(tokenManager);
|
|
151
150
|
if (refreshed) {
|
|
152
|
-
|
|
153
|
-
console.log(chalk_1.default.green('DEBUG: Token refreshed successfully, retrying request'));
|
|
154
|
-
}
|
|
151
|
+
logger_1.logger.debug('Token refreshed successfully, retrying request');
|
|
155
152
|
// Update the Authorization header with the new token
|
|
156
153
|
if (!args[1])
|
|
157
154
|
args[1] = {};
|
|
@@ -162,12 +159,11 @@ const createAuthenticatedClient = () => {
|
|
|
162
159
|
return yield target[prop](...args);
|
|
163
160
|
}
|
|
164
161
|
else {
|
|
165
|
-
|
|
166
|
-
console.log(chalk_1.default.red('DEBUG: Token refresh failed'));
|
|
167
|
-
}
|
|
162
|
+
logger_1.logger.debug('Token refresh failed');
|
|
168
163
|
// Add a more helpful error message for users
|
|
169
164
|
if (typeof result.error === 'object') {
|
|
170
|
-
result.error.userMessage =
|
|
165
|
+
result.error.userMessage =
|
|
166
|
+
'Your session has expired. Please run `berget auth login` to log in again.';
|
|
171
167
|
}
|
|
172
168
|
}
|
|
173
169
|
}
|
|
@@ -188,12 +184,10 @@ function refreshAccessToken(tokenManager) {
|
|
|
188
184
|
const refreshToken = tokenManager.getRefreshToken();
|
|
189
185
|
if (!refreshToken)
|
|
190
186
|
return false;
|
|
191
|
-
|
|
192
|
-
console.log(chalk_1.default.yellow('DEBUG: Attempting to refresh access token'));
|
|
193
|
-
}
|
|
187
|
+
logger_1.logger.debug('Attempting to refresh access token');
|
|
194
188
|
// Use fetch directly since this endpoint might not be in the OpenAPI spec
|
|
195
189
|
try {
|
|
196
|
-
const response = yield fetch(`${API_BASE_URL}/v1/auth/refresh`, {
|
|
190
|
+
const response = yield fetch(`${exports.API_BASE_URL}/v1/auth/refresh`, {
|
|
197
191
|
method: 'POST',
|
|
198
192
|
headers: {
|
|
199
193
|
'Content-Type': 'application/json',
|
|
@@ -203,9 +197,7 @@ function refreshAccessToken(tokenManager) {
|
|
|
203
197
|
});
|
|
204
198
|
// Handle HTTP errors
|
|
205
199
|
if (!response.ok) {
|
|
206
|
-
|
|
207
|
-
console.log(chalk_1.default.yellow(`DEBUG: Token refresh error: HTTP ${response.status} ${response.statusText}`));
|
|
208
|
-
}
|
|
200
|
+
logger_1.logger.debug(`Token refresh error: HTTP ${response.status} ${response.statusText}`);
|
|
209
201
|
// Check if the refresh token itself is expired or invalid
|
|
210
202
|
if (response.status === 401 || response.status === 403) {
|
|
211
203
|
console.warn(chalk_1.default.yellow('Your refresh token has expired. Please run `berget auth login` again.'));
|
|
@@ -229,21 +221,19 @@ function refreshAccessToken(tokenManager) {
|
|
|
229
221
|
console.warn(chalk_1.default.yellow('Invalid token response. Please run `berget auth login` again.'));
|
|
230
222
|
return false;
|
|
231
223
|
}
|
|
232
|
-
|
|
233
|
-
console.log(chalk_1.default.green('DEBUG: Token refreshed successfully'));
|
|
234
|
-
}
|
|
224
|
+
logger_1.logger.debug('Token refreshed successfully');
|
|
235
225
|
// Update the token
|
|
236
226
|
tokenManager.updateAccessToken(data.token, data.expires_in || 3600);
|
|
237
227
|
// If a new refresh token was provided, update that too
|
|
238
228
|
if (data.refresh_token) {
|
|
239
229
|
tokenManager.setTokens(data.token, data.refresh_token, data.expires_in || 3600);
|
|
240
|
-
|
|
241
|
-
console.log(chalk_1.default.green('DEBUG: Refresh token also updated'));
|
|
242
|
-
}
|
|
230
|
+
logger_1.logger.debug('Refresh token also updated');
|
|
243
231
|
}
|
|
244
232
|
}
|
|
245
233
|
catch (fetchError) {
|
|
246
|
-
console.warn(chalk_1.default.yellow(`Failed to refresh token: ${fetchError instanceof Error
|
|
234
|
+
console.warn(chalk_1.default.yellow(`Failed to refresh token: ${fetchError instanceof Error
|
|
235
|
+
? fetchError.message
|
|
236
|
+
: String(fetchError)}`));
|
|
247
237
|
return false;
|
|
248
238
|
}
|
|
249
239
|
return true;
|
|
@@ -21,6 +21,7 @@ const api_key_service_1 = require("../services/api-key-service");
|
|
|
21
21
|
const auth_service_1 = require("../services/auth-service");
|
|
22
22
|
const error_handler_1 = require("../utils/error-handler");
|
|
23
23
|
const default_api_key_1 = require("../utils/default-api-key");
|
|
24
|
+
const markdown_renderer_1 = require("../utils/markdown-renderer");
|
|
24
25
|
/**
|
|
25
26
|
* Helper function to get user confirmation
|
|
26
27
|
*/
|
|
@@ -54,6 +55,7 @@ function registerChatCommands(program) {
|
|
|
54
55
|
.option('-m, --max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
|
|
55
56
|
.option('-k, --api-key <key>', 'API key to use for this chat session')
|
|
56
57
|
.option('--api-key-id <id>', 'ID of the API key to use from your saved keys')
|
|
58
|
+
.option('--stream', 'Stream the response')
|
|
57
59
|
.action((options) => __awaiter(this, void 0, void 0, function* () {
|
|
58
60
|
try {
|
|
59
61
|
const chatService = chat_service_1.ChatService.getInstance();
|
|
@@ -65,6 +67,15 @@ function registerChatCommands(program) {
|
|
|
65
67
|
if (envApiKey) {
|
|
66
68
|
console.log(chalk_1.default.dim(`Using API key from BERGET_API_KEY environment variable`));
|
|
67
69
|
apiKey = envApiKey;
|
|
70
|
+
// Debug the API key (first few characters only)
|
|
71
|
+
if (process.argv.includes('--debug')) {
|
|
72
|
+
console.log(chalk_1.default.yellow(`DEBUG: API key from env starts with: ${envApiKey.substring(0, 4)}...`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// If API key is already provided via command line, use it
|
|
76
|
+
else if (options.apiKey) {
|
|
77
|
+
console.log(chalk_1.default.dim(`Using API key from command line argument`));
|
|
78
|
+
apiKey = options.apiKey;
|
|
68
79
|
}
|
|
69
80
|
// If no API key or API key ID provided and no env var, check for default API key
|
|
70
81
|
else if (!apiKey && !apiKeyId) {
|
|
@@ -190,33 +201,41 @@ function registerChatCommands(program) {
|
|
|
190
201
|
});
|
|
191
202
|
try {
|
|
192
203
|
// Call the API
|
|
193
|
-
console.log(chalk_1.default.yellow('DEBUG: Preparing completion options'));
|
|
194
204
|
const completionOptions = {
|
|
195
205
|
model: ((_a = options.args) === null || _a === void 0 ? void 0 : _a[0]) || 'google/gemma-3-27b-it',
|
|
196
206
|
messages: messages,
|
|
197
207
|
temperature: options.temperature !== undefined ? options.temperature : 0.7,
|
|
198
208
|
max_tokens: options.maxTokens || 4096,
|
|
209
|
+
stream: options.stream || false
|
|
199
210
|
};
|
|
200
211
|
// Only add apiKey if it actually exists
|
|
201
212
|
if (apiKey) {
|
|
202
213
|
completionOptions.apiKey = apiKey;
|
|
203
|
-
console.log(chalk_1.default.yellow('DEBUG: Using API key from command options or default'));
|
|
204
214
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
// Add streaming support
|
|
216
|
+
if (options.stream) {
|
|
217
|
+
let assistantResponse = '';
|
|
218
|
+
console.log(chalk_1.default.blue('Assistant: '));
|
|
219
|
+
// For streaming, we'll collect the response and render it at the end
|
|
220
|
+
// since markdown needs the complete text to render properly
|
|
221
|
+
completionOptions.onChunk = (chunk) => {
|
|
222
|
+
if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta && chunk.choices[0].delta.content) {
|
|
223
|
+
const content = chunk.choices[0].delta.content;
|
|
224
|
+
process.stdout.write(content);
|
|
225
|
+
assistantResponse += content;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
yield chatService.createCompletion(completionOptions);
|
|
229
|
+
console.log('\n');
|
|
230
|
+
// Add assistant response to messages
|
|
231
|
+
messages.push({
|
|
232
|
+
role: 'assistant',
|
|
233
|
+
content: assistantResponse
|
|
234
|
+
});
|
|
235
|
+
// Continue the conversation
|
|
236
|
+
askQuestion();
|
|
237
|
+
return;
|
|
212
238
|
}
|
|
213
|
-
// Debug output
|
|
214
|
-
console.log(chalk_1.default.yellow('DEBUG: Completion options:'));
|
|
215
|
-
console.log(chalk_1.default.yellow(JSON.stringify(Object.assign(Object.assign({}, completionOptions), { apiKey: completionOptions.apiKey ? '***' : undefined, messages: completionOptions.messages.map((m) => ({
|
|
216
|
-
role: m.role,
|
|
217
|
-
content: m.content.length > 50 ? m.content.substring(0, 50) + '...' : m.content
|
|
218
|
-
})) }), null, 2)));
|
|
219
|
-
console.log(chalk_1.default.yellow('DEBUG: Calling chatService.createCompletion'));
|
|
220
239
|
const response = yield chatService.createCompletion(completionOptions);
|
|
221
240
|
// Debug output
|
|
222
241
|
if (program.opts().debug) {
|
|
@@ -240,7 +259,14 @@ function registerChatCommands(program) {
|
|
|
240
259
|
content: assistantMessage,
|
|
241
260
|
});
|
|
242
261
|
// Display the response
|
|
243
|
-
console.log(chalk_1.default.blue('Assistant: ')
|
|
262
|
+
console.log(chalk_1.default.blue('Assistant: '));
|
|
263
|
+
// Check if the response contains markdown and render it if it does
|
|
264
|
+
if ((0, markdown_renderer_1.containsMarkdown)(assistantMessage)) {
|
|
265
|
+
console.log((0, markdown_renderer_1.renderMarkdown)(assistantMessage));
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
console.log(assistantMessage);
|
|
269
|
+
}
|
|
244
270
|
console.log(); // Empty line for better readability
|
|
245
271
|
// Continue the conversation
|
|
246
272
|
askQuestion();
|
|
@@ -317,11 +343,12 @@ function registerChatCommands(program) {
|
|
|
317
343
|
}
|
|
318
344
|
console.log(chalk_1.default.bold('Available Chat Models:'));
|
|
319
345
|
console.log(chalk_1.default.dim('─'.repeat(70)));
|
|
320
|
-
console.log(chalk_1.default.dim('MODEL ID'.padEnd(
|
|
321
|
-
chalk_1.default.dim('OWNER'.padEnd(25)) +
|
|
346
|
+
console.log(chalk_1.default.dim('MODEL ID'.padEnd(40)) +
|
|
322
347
|
chalk_1.default.dim('CAPABILITIES'));
|
|
323
348
|
console.log(chalk_1.default.dim('─'.repeat(70)));
|
|
324
|
-
|
|
349
|
+
// Filter to only show active models
|
|
350
|
+
const activeModels = models.data.filter((model) => model.active === true);
|
|
351
|
+
activeModels.forEach((model) => {
|
|
325
352
|
const capabilities = [];
|
|
326
353
|
if (model.capabilities.vision)
|
|
327
354
|
capabilities.push('vision');
|
|
@@ -329,8 +356,9 @@ function registerChatCommands(program) {
|
|
|
329
356
|
capabilities.push('function_calling');
|
|
330
357
|
if (model.capabilities.json_mode)
|
|
331
358
|
capabilities.push('json_mode');
|
|
332
|
-
|
|
333
|
-
|
|
359
|
+
// Format model ID in Huggingface compatible format (owner/model)
|
|
360
|
+
const modelId = `${model.owned_by.toLowerCase()}/${model.id}`.padEnd(40);
|
|
361
|
+
console.log(modelId +
|
|
334
362
|
capabilities.join(', '));
|
|
335
363
|
});
|
|
336
364
|
}
|
|
@@ -44,7 +44,7 @@ function registerModelCommands(program) {
|
|
|
44
44
|
throw new Error(JSON.stringify(error));
|
|
45
45
|
response = data;
|
|
46
46
|
console.log('Available Models:');
|
|
47
|
-
console.log('ID
|
|
47
|
+
console.log('ID OWNED BY CAPABILITIES');
|
|
48
48
|
// Ensure response has the expected structure
|
|
49
49
|
const modelData = response;
|
|
50
50
|
if (modelData.data) {
|
|
@@ -56,7 +56,7 @@ function registerModelCommands(program) {
|
|
|
56
56
|
capabilities.push('function_calling');
|
|
57
57
|
if (model.capabilities.json_mode)
|
|
58
58
|
capabilities.push('json_mode');
|
|
59
|
-
console.log(`${model.
|
|
59
|
+
console.log(`${model.root.padEnd(50)} ${model.owned_by.padEnd(24)} ${capabilities.join(', ')}`);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
}
|