berget 1.1.0 → 1.3.0

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.
@@ -2,7 +2,7 @@ import { Command } from 'commander'
2
2
  import chalk from 'chalk'
3
3
  import readline from 'readline'
4
4
  import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
5
- import { ChatService, ChatMessage } from '../services/chat-service'
5
+ import { ChatService, ChatMessage, ChatCompletionOptions } from '../services/chat-service'
6
6
  import { ApiKeyService } from '../services/api-key-service'
7
7
  import { AuthService } from '../services/auth-service'
8
8
  import { handleError } from '../utils/error-handler'
@@ -36,7 +36,7 @@ export function registerChatCommands(program: Command): void {
36
36
  chat
37
37
  .command(SUBCOMMANDS.CHAT.RUN)
38
38
  .description('Run a chat session with a specified model')
39
- .argument('[model]', 'Model to use (default: berget-70b-instruct)')
39
+ .argument('[model]', 'Model to use (default: google/gemma-3-27b-it)')
40
40
  .option('-s, --system <message>', 'System message')
41
41
  .option('-t, --temperature <temp>', 'Temperature (0-1)', parseFloat)
42
42
  .option('-m, --max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
@@ -45,6 +45,7 @@ export function registerChatCommands(program: Command): void {
45
45
  '--api-key-id <id>',
46
46
  'ID of the API key to use from your saved keys'
47
47
  )
48
+ .option('--stream', 'Stream the response')
48
49
  .action(async (options) => {
49
50
  try {
50
51
  const chatService = ChatService.getInstance()
@@ -52,19 +53,92 @@ export function registerChatCommands(program: Command): void {
52
53
  // Check if we have an API key or need to get one
53
54
  let apiKey = options.apiKey
54
55
  let apiKeyId = options.apiKeyId
55
-
56
- // If no API key or API key ID provided, check for default API key
57
- if (!apiKey && !apiKeyId) {
58
- const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
59
- const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
60
-
61
- if (defaultApiKey) {
62
- apiKeyId = defaultApiKey.id
56
+
57
+ // Check for environment variable first
58
+ const envApiKey = process.env.BERGET_API_KEY;
59
+ if (envApiKey) {
60
+ console.log(
61
+ chalk.dim(`Using API key from BERGET_API_KEY environment variable`)
62
+ )
63
+ apiKey = envApiKey;
64
+
65
+ // Debug the API key (first few characters only)
66
+ if (process.argv.includes('--debug')) {
63
67
  console.log(
64
- chalk.dim(`Using default API key: ${defaultApiKey.name}`)
68
+ chalk.yellow(`DEBUG: API key from env starts with: ${envApiKey.substring(0, 4)}...`)
65
69
  )
66
70
  }
67
71
  }
72
+ // If API key is already provided via command line, use it
73
+ else if (options.apiKey) {
74
+ console.log(
75
+ chalk.dim(`Using API key from command line argument`)
76
+ )
77
+ apiKey = options.apiKey;
78
+ }
79
+ // If no API key or API key ID provided and no env var, check for default API key
80
+ else if (!apiKey && !apiKeyId) {
81
+ try {
82
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
83
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
84
+
85
+ if (defaultApiKeyData) {
86
+ apiKeyId = defaultApiKeyData.id
87
+ apiKey = defaultApiKeyData.key
88
+
89
+ if (apiKey) {
90
+ console.log(
91
+ chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
92
+ )
93
+ } else {
94
+ console.log(
95
+ chalk.yellow(`Default API key "${defaultApiKeyData.name}" exists but the key value is missing.`)
96
+ )
97
+ console.log(
98
+ chalk.yellow(`Try rotating the key with: berget api-keys rotate ${defaultApiKeyData.id}`)
99
+ )
100
+ }
101
+ } else {
102
+ // No default API key, prompt the user to create one
103
+ console.log(chalk.yellow('No default API key set.'))
104
+
105
+ // Try to prompt for a default API key
106
+ apiKey = await defaultApiKeyManager.promptForDefaultApiKey()
107
+
108
+ if (!apiKey) {
109
+ console.log(
110
+ chalk.red(
111
+ 'Error: An API key is required to use the chat command.'
112
+ )
113
+ )
114
+ console.log(chalk.yellow('You can:'))
115
+ console.log(
116
+ chalk.yellow(
117
+ '1. Create an API key with: berget api-keys create --name "My Key"'
118
+ )
119
+ )
120
+ console.log(
121
+ chalk.yellow(
122
+ '2. Set a default API key with: berget api-keys set-default <id>'
123
+ )
124
+ )
125
+ console.log(
126
+ chalk.yellow(
127
+ '3. Provide an API key with the --api-key option'
128
+ )
129
+ )
130
+ return
131
+ }
132
+ }
133
+ } catch (error) {
134
+ if (process.argv.includes('--debug')) {
135
+ console.log(
136
+ chalk.yellow('DEBUG: Error checking default API key:')
137
+ )
138
+ console.log(chalk.yellow(String(error)))
139
+ }
140
+ }
141
+ }
68
142
 
69
143
  // If no direct API key, try to get one from API key ID
70
144
  if (!apiKey && apiKeyId) {
@@ -106,9 +180,19 @@ export function registerChatCommands(program: Command): void {
106
180
  }
107
181
  }
108
182
  } catch (error) {
109
- console.error(chalk.red('Error fetching API key:'))
110
- console.error(error)
111
- console.log(chalk.yellow('Using default authentication instead.'))
183
+ // Check if this is an authentication error
184
+ const errorMessage = error instanceof Error ? error.message : String(error);
185
+ const isAuthError = errorMessage.includes('Unauthorized') ||
186
+ errorMessage.includes('Authentication failed') ||
187
+ errorMessage.includes('AUTH_FAILED');
188
+
189
+ if (isAuthError) {
190
+ console.log(chalk.yellow('Authentication required. Please run `berget auth login` first.'));
191
+ } else {
192
+ console.error(chalk.red('Error fetching API key:'));
193
+ console.error(error);
194
+ }
195
+ console.log(chalk.yellow('Using default authentication instead.'));
112
196
  }
113
197
  }
114
198
 
@@ -171,14 +255,50 @@ export function registerChatCommands(program: Command): void {
171
255
 
172
256
  try {
173
257
  // Call the API
174
- const response = await chatService.createCompletion({
175
- model: options.args?.[0] || 'berget-70b-instruct',
258
+ const completionOptions: ChatCompletionOptions = {
259
+ model: options.args?.[0] || 'google/gemma-3-27b-it',
176
260
  messages: messages,
177
261
  temperature:
178
262
  options.temperature !== undefined ? options.temperature : 0.7,
179
263
  max_tokens: options.maxTokens || 4096,
180
- apiKey: apiKey,
181
- })
264
+ stream: options.stream || false
265
+ }
266
+
267
+ // Only add apiKey if it actually exists
268
+ if (apiKey) {
269
+ completionOptions.apiKey = apiKey
270
+ }
271
+
272
+ // Add streaming support
273
+ if (options.stream) {
274
+ let assistantResponse = ''
275
+ process.stdout.write(chalk.blue('Assistant: '))
276
+
277
+ completionOptions.onChunk = (chunk: any) => {
278
+ if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta && chunk.choices[0].delta.content) {
279
+ const content = chunk.choices[0].delta.content
280
+ process.stdout.write(content)
281
+ assistantResponse += content
282
+ }
283
+ }
284
+
285
+ await chatService.createCompletion(completionOptions)
286
+ console.log('\n')
287
+
288
+ // Add assistant response to messages
289
+ messages.push({
290
+ role: 'assistant',
291
+ content: assistantResponse
292
+ })
293
+
294
+ // Continue the conversation
295
+ askQuestion()
296
+ return
297
+ }
298
+
299
+ const response = await chatService.createCompletion(
300
+ completionOptions
301
+ )
182
302
 
183
303
  // Debug output
184
304
  if (program.opts().debug) {
@@ -252,12 +372,12 @@ export function registerChatCommands(program: Command): void {
252
372
  // If no API key or API key ID provided, check for default API key
253
373
  if (!apiKey && !apiKeyId) {
254
374
  const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
255
- const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
375
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
256
376
 
257
- if (defaultApiKey) {
258
- apiKeyId = defaultApiKey.id
377
+ if (defaultApiKeyData) {
378
+ apiKeyId = defaultApiKeyData.id
259
379
  console.log(
260
- chalk.dim(`Using default API key: ${defaultApiKey.name}`)
380
+ chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
261
381
  )
262
382
  }
263
383
  }
@@ -33,10 +33,10 @@ export interface ApiKeyResponse {
33
33
  export class ApiKeyService {
34
34
  private static instance: ApiKeyService
35
35
  private client = createAuthenticatedClient()
36
-
36
+
37
37
  // Command group name for this service
38
38
  public static readonly COMMAND_GROUP = COMMAND_GROUPS.API_KEYS
39
-
39
+
40
40
  // Subcommands for this service
41
41
  public static readonly COMMANDS = SUBCOMMANDS.API_KEYS
42
42
 
@@ -56,18 +56,7 @@ export class ApiKeyService {
56
56
  public async list(): Promise<ApiKey[]> {
57
57
  try {
58
58
  const { data, error } = await this.client.GET('/v1/api-keys')
59
- if (error) {
60
- // Check if this is an authentication error
61
- const errorObj = typeof error === 'string' ? JSON.parse(error) : error;
62
- if (errorObj.status === 401) {
63
- throw new Error(JSON.stringify({
64
- error: "Authentication failed. Your session may have expired.",
65
- code: "AUTH_FAILED",
66
- details: "Please run 'berget login' to authenticate again."
67
- }))
68
- }
69
- throw new Error(JSON.stringify(error))
70
- }
59
+ if (error) throw error
71
60
  return data || []
72
61
  } catch (error) {
73
62
  handleError('Failed to list API keys', error)
@@ -82,7 +71,7 @@ export class ApiKeyService {
82
71
  public async create(options: CreateApiKeyOptions): Promise<ApiKeyResponse> {
83
72
  try {
84
73
  const { data, error } = await this.client.POST('/v1/api-keys', {
85
- body: options
74
+ body: options,
86
75
  })
87
76
  if (error) throw new Error(JSON.stringify(error))
88
77
  return data!
@@ -99,7 +88,7 @@ export class ApiKeyService {
99
88
  public async delete(id: string): Promise<boolean> {
100
89
  try {
101
90
  const { error } = await this.client.DELETE('/v1/api-keys/{id}', {
102
- params: { path: { id } }
91
+ params: { path: { id } },
103
92
  })
104
93
  if (error) throw new Error(JSON.stringify(error))
105
94
  return true
@@ -115,9 +104,12 @@ export class ApiKeyService {
115
104
  */
116
105
  public async rotate(id: string): Promise<ApiKeyResponse> {
117
106
  try {
118
- const { data, error } = await this.client.PUT('/v1/api-keys/{id}/rotate', {
119
- params: { path: { id } }
120
- })
107
+ const { data, error } = await this.client.PUT(
108
+ '/v1/api-keys/{id}/rotate',
109
+ {
110
+ params: { path: { id } },
111
+ }
112
+ )
121
113
  if (error) throw new Error(JSON.stringify(error))
122
114
  return data!
123
115
  } catch (error) {
@@ -133,7 +125,7 @@ export class ApiKeyService {
133
125
  public async describe(id: string): Promise<any> {
134
126
  try {
135
127
  const { data, error } = await this.client.GET('/v1/api-keys/{id}/usage', {
136
- params: { path: { id } }
128
+ params: { path: { id } },
137
129
  })
138
130
  if (error) throw new Error(JSON.stringify(error))
139
131
  return data