snow-ai 0.2.24 → 0.2.25

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.
@@ -1,5 +1,342 @@
1
1
  import OpenAI from 'openai';
2
- import { getOpenAiConfig } from './apiConfig.js';
2
+ import { GoogleGenAI } from '@google/genai';
3
+ import Anthropic from '@anthropic-ai/sdk';
4
+ import { getOpenAiConfig, getCustomHeaders, getCustomSystemPrompt } from './apiConfig.js';
5
+ import { SYSTEM_PROMPT } from '../api/systemPrompt.js';
6
+ /**
7
+ * Compression request prompt - asks AI to summarize conversation with focus on task continuity
8
+ */
9
+ const COMPRESSION_PROMPT = 'Please provide a concise summary of our conversation so far. Focus on: 1) The current task or goal we are working on, 2) Key decisions and approaches we have agreed upon, 3) Important context needed to continue, 4) Any pending or unfinished work. Keep it brief but ensure I can seamlessly continue assisting with the task.';
10
+ /**
11
+ * Compress context using OpenAI Chat Completions API
12
+ */
13
+ async function compressWithChatCompletions(baseUrl, apiKey, modelName, conversationMessages, systemPrompt) {
14
+ const client = new OpenAI({
15
+ apiKey,
16
+ baseURL: baseUrl,
17
+ });
18
+ const customHeaders = getCustomHeaders();
19
+ // Build messages with system prompt support
20
+ const messages = [];
21
+ if (systemPrompt) {
22
+ // If custom system prompt exists: custom as system, default as first user message
23
+ messages.push({ role: 'system', content: systemPrompt });
24
+ messages.push({ role: 'user', content: SYSTEM_PROMPT });
25
+ }
26
+ else {
27
+ // No custom system prompt: default as system
28
+ messages.push({ role: 'system', content: SYSTEM_PROMPT });
29
+ }
30
+ // Add all conversation history (exclude system messages)
31
+ for (const msg of conversationMessages) {
32
+ if (msg.role !== 'system' && msg.role !== 'tool') {
33
+ messages.push({
34
+ role: msg.role,
35
+ content: msg.content,
36
+ });
37
+ }
38
+ }
39
+ // Add compression request as final user message
40
+ messages.push({
41
+ role: 'user',
42
+ content: COMPRESSION_PROMPT,
43
+ });
44
+ // Build request payload (no tools for compression)
45
+ const requestPayload = {
46
+ model: modelName,
47
+ messages,
48
+ stream: true,
49
+ stream_options: { include_usage: true },
50
+ };
51
+ // Log request payload
52
+ console.log('[ContextCompressor] Chat Completions Request:', JSON.stringify(requestPayload, null, 2));
53
+ // Use streaming to avoid timeout
54
+ const stream = (await client.chat.completions.create(requestPayload, {
55
+ headers: customHeaders,
56
+ }));
57
+ let summary = '';
58
+ let usage = {
59
+ prompt_tokens: 0,
60
+ completion_tokens: 0,
61
+ total_tokens: 0,
62
+ };
63
+ for await (const chunk of stream) {
64
+ const delta = chunk.choices[0]?.delta;
65
+ if (delta?.content) {
66
+ summary += delta.content;
67
+ }
68
+ // Collect usage info (usually in the last chunk)
69
+ if (chunk.usage) {
70
+ usage = {
71
+ prompt_tokens: chunk.usage.prompt_tokens || 0,
72
+ completion_tokens: chunk.usage.completion_tokens || 0,
73
+ total_tokens: chunk.usage.total_tokens || 0,
74
+ };
75
+ }
76
+ }
77
+ if (!summary) {
78
+ throw new Error('Failed to generate summary from compact model');
79
+ }
80
+ return {
81
+ summary,
82
+ usage,
83
+ };
84
+ }
85
+ /**
86
+ * Compress context using OpenAI Responses API
87
+ */
88
+ async function compressWithResponses(baseUrl, apiKey, modelName, conversationMessages, systemPrompt) {
89
+ const client = new OpenAI({
90
+ apiKey,
91
+ baseURL: baseUrl,
92
+ });
93
+ const customHeaders = getCustomHeaders();
94
+ // Build instructions
95
+ const instructions = systemPrompt || SYSTEM_PROMPT;
96
+ // Build input array with conversation history
97
+ const input = [];
98
+ // If custom system prompt exists, add default as first user message
99
+ if (systemPrompt) {
100
+ input.push({
101
+ type: 'message',
102
+ role: 'user',
103
+ content: [{ type: 'input_text', text: SYSTEM_PROMPT }],
104
+ });
105
+ }
106
+ // Add all conversation history (exclude system messages)
107
+ for (const msg of conversationMessages) {
108
+ if (msg.role !== 'system' && msg.role !== 'tool') {
109
+ input.push({
110
+ type: 'message',
111
+ role: msg.role,
112
+ content: [{
113
+ type: msg.role === 'user' ? 'input_text' : 'output_text',
114
+ text: msg.content,
115
+ }],
116
+ });
117
+ }
118
+ }
119
+ // Add compression request as final user message
120
+ input.push({
121
+ type: 'message',
122
+ role: 'user',
123
+ content: [{
124
+ type: 'input_text',
125
+ text: COMPRESSION_PROMPT,
126
+ }],
127
+ });
128
+ // Build request payload (no tools for compression)
129
+ const requestPayload = {
130
+ model: modelName,
131
+ instructions,
132
+ input,
133
+ stream: true,
134
+ };
135
+ // Log request payload
136
+ console.log('[ContextCompressor] Responses API Request:', JSON.stringify(requestPayload, null, 2));
137
+ // Use streaming to avoid timeout
138
+ const stream = await client.responses.create(requestPayload, {
139
+ headers: customHeaders,
140
+ });
141
+ let summary = '';
142
+ let usage = {
143
+ prompt_tokens: 0,
144
+ completion_tokens: 0,
145
+ total_tokens: 0,
146
+ };
147
+ for await (const chunk of stream) {
148
+ const eventType = chunk.type;
149
+ // Handle text content delta
150
+ if (eventType === 'response.output_text.delta') {
151
+ const delta = chunk.delta;
152
+ if (delta) {
153
+ summary += delta;
154
+ }
155
+ }
156
+ // Handle usage info
157
+ if (eventType === 'response.done') {
158
+ const response = chunk.response;
159
+ if (response?.usage) {
160
+ usage = {
161
+ prompt_tokens: response.usage.input_tokens || 0,
162
+ completion_tokens: response.usage.output_tokens || 0,
163
+ total_tokens: (response.usage.input_tokens || 0) + (response.usage.output_tokens || 0),
164
+ };
165
+ }
166
+ }
167
+ }
168
+ if (!summary) {
169
+ throw new Error('Failed to generate summary from compact model (Responses API)');
170
+ }
171
+ return {
172
+ summary,
173
+ usage,
174
+ };
175
+ }
176
+ /**
177
+ * Compress context using Gemini API
178
+ */
179
+ async function compressWithGemini(baseUrl, apiKey, modelName, conversationMessages, systemPrompt) {
180
+ const clientConfig = {
181
+ apiKey,
182
+ };
183
+ const customHeaders = getCustomHeaders();
184
+ // Support custom baseUrl and headers for proxy servers
185
+ if (baseUrl && baseUrl !== 'https://api.openai.com/v1') {
186
+ clientConfig.httpOptions = {
187
+ baseUrl,
188
+ headers: {
189
+ 'x-goog-api-key': apiKey,
190
+ ...customHeaders,
191
+ },
192
+ };
193
+ }
194
+ else if (Object.keys(customHeaders).length > 0) {
195
+ clientConfig.httpOptions = {
196
+ headers: customHeaders,
197
+ };
198
+ }
199
+ const client = new GoogleGenAI(clientConfig);
200
+ // Build system instruction
201
+ const systemInstruction = systemPrompt || SYSTEM_PROMPT;
202
+ // Build contents array with conversation history
203
+ const contents = [];
204
+ // If custom system prompt exists, add default as first user message
205
+ if (systemPrompt) {
206
+ contents.push({
207
+ role: 'user',
208
+ parts: [{ text: SYSTEM_PROMPT }],
209
+ });
210
+ }
211
+ // Add all conversation history (exclude system messages)
212
+ for (const msg of conversationMessages) {
213
+ if (msg.role !== 'system' && msg.role !== 'tool') {
214
+ contents.push({
215
+ role: msg.role === 'assistant' ? 'model' : 'user',
216
+ parts: [{ text: msg.content }],
217
+ });
218
+ }
219
+ }
220
+ // Add compression request as final user message
221
+ contents.push({
222
+ role: 'user',
223
+ parts: [{
224
+ text: COMPRESSION_PROMPT,
225
+ }],
226
+ });
227
+ const requestConfig = {
228
+ model: modelName,
229
+ systemInstruction,
230
+ contents,
231
+ };
232
+ // Log request payload
233
+ console.log('[ContextCompressor] Gemini Request:', JSON.stringify(requestConfig, null, 2));
234
+ // Use streaming to avoid timeout
235
+ const stream = await client.models.generateContentStream(requestConfig);
236
+ let summary = '';
237
+ let usage = {
238
+ prompt_tokens: 0,
239
+ completion_tokens: 0,
240
+ total_tokens: 0,
241
+ };
242
+ for await (const chunk of stream) {
243
+ if (chunk.text) {
244
+ summary += chunk.text;
245
+ }
246
+ // Collect usage info
247
+ if (chunk.usageMetadata) {
248
+ usage = {
249
+ prompt_tokens: chunk.usageMetadata.promptTokenCount || 0,
250
+ completion_tokens: chunk.usageMetadata.candidatesTokenCount || 0,
251
+ total_tokens: chunk.usageMetadata.totalTokenCount || 0,
252
+ };
253
+ }
254
+ }
255
+ if (!summary) {
256
+ throw new Error('Failed to generate summary from Gemini model');
257
+ }
258
+ return {
259
+ summary,
260
+ usage,
261
+ };
262
+ }
263
+ /**
264
+ * Compress context using Anthropic API
265
+ */
266
+ async function compressWithAnthropic(baseUrl, apiKey, modelName, conversationMessages, systemPrompt) {
267
+ const clientConfig = {
268
+ apiKey,
269
+ };
270
+ if (baseUrl && baseUrl !== 'https://api.openai.com/v1') {
271
+ clientConfig.baseURL = baseUrl;
272
+ }
273
+ const customHeaders = getCustomHeaders();
274
+ clientConfig.defaultHeaders = {
275
+ 'Authorization': `Bearer ${apiKey}`,
276
+ ...customHeaders,
277
+ };
278
+ const client = new Anthropic(clientConfig);
279
+ // Build messages array with conversation history
280
+ const messages = [];
281
+ // If custom system prompt exists, add default as first user message
282
+ if (systemPrompt) {
283
+ messages.push({ role: 'user', content: SYSTEM_PROMPT });
284
+ }
285
+ // Add all conversation history (exclude system messages)
286
+ for (const msg of conversationMessages) {
287
+ if (msg.role !== 'system' && msg.role !== 'tool') {
288
+ messages.push({
289
+ role: msg.role,
290
+ content: msg.content,
291
+ });
292
+ }
293
+ }
294
+ // Add compression request as final user message
295
+ messages.push({
296
+ role: 'user',
297
+ content: COMPRESSION_PROMPT,
298
+ });
299
+ // Anthropic uses system parameter separately
300
+ const systemParam = systemPrompt || SYSTEM_PROMPT;
301
+ // Build request payload (no tools for compression)
302
+ const requestPayload = {
303
+ model: modelName,
304
+ max_tokens: 4096,
305
+ system: systemParam,
306
+ messages,
307
+ };
308
+ // Log request payload
309
+ console.log('[ContextCompressor] Anthropic Request:', JSON.stringify(requestPayload, null, 2));
310
+ // Use streaming to avoid timeout
311
+ const stream = await client.messages.stream(requestPayload);
312
+ let summary = '';
313
+ let usage = {
314
+ prompt_tokens: 0,
315
+ completion_tokens: 0,
316
+ total_tokens: 0,
317
+ };
318
+ for await (const event of stream) {
319
+ if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
320
+ summary += event.delta.text;
321
+ }
322
+ // Collect usage info from message_stop event
323
+ if (event.type === 'message_stop') {
324
+ const finalMessage = await stream.finalMessage();
325
+ usage = {
326
+ prompt_tokens: finalMessage.usage.input_tokens,
327
+ completion_tokens: finalMessage.usage.output_tokens,
328
+ total_tokens: finalMessage.usage.input_tokens + finalMessage.usage.output_tokens,
329
+ };
330
+ }
331
+ }
332
+ if (!summary) {
333
+ throw new Error('Failed to generate summary from Anthropic model');
334
+ }
335
+ return {
336
+ summary,
337
+ usage,
338
+ };
339
+ }
3
340
  /**
4
341
  * Compress conversation history using the compact model
5
342
  * @param messages - Array of messages to compress
@@ -8,57 +345,34 @@ import { getOpenAiConfig } from './apiConfig.js';
8
345
  export async function compressContext(messages) {
9
346
  const config = getOpenAiConfig();
10
347
  // Check if compact model is configured
11
- if (!config.compactModel || !config.compactModel.baseUrl || !config.compactModel.apiKey || !config.compactModel.modelName) {
12
- throw new Error('Compact model not configured. Please configure it in Model Settings.');
348
+ if (!config.compactModel || !config.compactModel.modelName) {
349
+ throw new Error('Compact model not configured. Please configure it in API & Model Settings.');
13
350
  }
14
- // Create OpenAI client with compact model config
15
- const client = new OpenAI({
16
- apiKey: config.compactModel.apiKey,
17
- baseURL: config.compactModel.baseUrl,
18
- });
19
- // Filter out system messages and create a conversation text
20
- const conversationText = messages
21
- .filter(msg => msg.role !== 'system')
22
- .map(msg => {
23
- const role = msg.role === 'user' ? 'User' : msg.role === 'assistant' ? 'Assistant' : 'Tool';
24
- return `${role}: ${msg.content}`;
25
- })
26
- .join('\n\n');
27
- // Create compression prompt
28
- const compressionPrompt = `Please summarize the following conversation history in a concise way, preserving all important context, decisions, and key information. The summary should be detailed enough to continue the conversation seamlessly.
29
-
30
- Conversation:
31
- ${conversationText}
32
-
33
- Summary:`;
351
+ // Use shared API credentials
352
+ const baseUrl = config.baseUrl;
353
+ const apiKey = config.apiKey;
354
+ const modelName = config.compactModel.modelName;
355
+ const requestMethod = config.requestMethod;
356
+ if (!baseUrl || !apiKey) {
357
+ throw new Error('API configuration incomplete. Please configure Base URL and API Key.');
358
+ }
359
+ // Get custom system prompt if configured
360
+ const customSystemPrompt = getCustomSystemPrompt();
34
361
  try {
35
- const response = await client.chat.completions.create({
36
- model: config.compactModel.modelName,
37
- messages: [
38
- {
39
- role: 'user',
40
- content: compressionPrompt,
41
- },
42
- ]
43
- });
44
- const summary = response.choices[0]?.message?.content;
45
- if (!summary) {
46
- throw new Error('Failed to generate summary from compact model');
362
+ // Choose compression method based on request method
363
+ switch (requestMethod) {
364
+ case 'gemini':
365
+ return await compressWithGemini(baseUrl, apiKey, modelName, messages, customSystemPrompt || null);
366
+ case 'anthropic':
367
+ return await compressWithAnthropic(baseUrl, apiKey, modelName, messages, customSystemPrompt || null);
368
+ case 'responses':
369
+ // OpenAI Responses API
370
+ return await compressWithResponses(baseUrl, apiKey, modelName, messages, customSystemPrompt || null);
371
+ case 'chat':
372
+ default:
373
+ // OpenAI Chat Completions API
374
+ return await compressWithChatCompletions(baseUrl, apiKey, modelName, messages, customSystemPrompt || null);
47
375
  }
48
- // Extract usage information
49
- const usage = response.usage || {
50
- prompt_tokens: 0,
51
- completion_tokens: 0,
52
- total_tokens: 0
53
- };
54
- return {
55
- summary,
56
- usage: {
57
- prompt_tokens: usage.prompt_tokens,
58
- completion_tokens: usage.completion_tokens,
59
- total_tokens: usage.total_tokens
60
- }
61
- };
62
376
  }
63
377
  catch (error) {
64
378
  if (error instanceof Error) {
@@ -64,6 +64,12 @@ function isRetriableError(error) {
64
64
  errorMessage.includes('unavailable')) {
65
65
  return true;
66
66
  }
67
+ // Connection terminated by server
68
+ if (errorMessage.includes('terminated') ||
69
+ errorMessage.includes('connection reset') ||
70
+ errorMessage.includes('socket hang up')) {
71
+ return true;
72
+ }
67
73
  return false;
68
74
  }
69
75
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.24",
3
+ "version": "0.2.25",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -1,161 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } from 'ink';
3
- import Gradient from 'ink-gradient';
4
- import { Select, Alert } from '@inkjs/ui';
5
- import TextInput from 'ink-text-input';
6
- import { getOpenAiConfig, updateOpenAiConfig, validateApiConfig, } from '../../utils/apiConfig.js';
7
- export default function ApiConfigScreen({ onBack, onSave, inlineMode = false }) {
8
- const [baseUrl, setBaseUrl] = useState('');
9
- const [apiKey, setApiKey] = useState('');
10
- const [requestMethod, setRequestMethod] = useState('chat');
11
- const [anthropicBeta, setAnthropicBeta] = useState(false);
12
- const [currentField, setCurrentField] = useState('baseUrl');
13
- const [errors, setErrors] = useState([]);
14
- const [isEditing, setIsEditing] = useState(false);
15
- const requestMethodOptions = [
16
- {
17
- label: 'Chat Completions - Modern chat API (GPT-4, GPT-3.5-turbo)',
18
- value: 'chat',
19
- },
20
- {
21
- label: 'Responses - New responses API (2025, with built-in tools)',
22
- value: 'responses',
23
- },
24
- {
25
- label: 'Gemini - Google Gemini API',
26
- value: 'gemini',
27
- },
28
- {
29
- label: 'Anthropic - Claude API',
30
- value: 'anthropic',
31
- },
32
- ];
33
- useEffect(() => {
34
- const config = getOpenAiConfig();
35
- setBaseUrl(config.baseUrl);
36
- setApiKey(config.apiKey);
37
- setRequestMethod(config.requestMethod || 'chat');
38
- setAnthropicBeta(config.anthropicBeta || false);
39
- }, []);
40
- useInput((input, key) => {
41
- // Allow Escape key to exit Select component without changes
42
- if (isEditing && currentField === 'requestMethod' && key.escape) {
43
- setIsEditing(false);
44
- return;
45
- }
46
- // Don't handle other input when Select component is active
47
- if (isEditing && currentField === 'requestMethod') {
48
- return;
49
- }
50
- // Handle save/exit globally
51
- if (input === 's' && (key.ctrl || key.meta)) {
52
- const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
53
- if (validationErrors.length === 0) {
54
- updateOpenAiConfig({ baseUrl, apiKey, requestMethod, anthropicBeta });
55
- setErrors([]);
56
- onSave();
57
- }
58
- else {
59
- setErrors(validationErrors);
60
- }
61
- }
62
- else if (key.escape) {
63
- const validationErrors = validateApiConfig({ baseUrl, apiKey, requestMethod });
64
- if (validationErrors.length === 0) {
65
- updateOpenAiConfig({ baseUrl, apiKey, requestMethod, anthropicBeta });
66
- setErrors([]);
67
- }
68
- onBack();
69
- }
70
- else if (key.return) {
71
- if (isEditing) {
72
- // Exit edit mode, return to navigation
73
- setIsEditing(false);
74
- }
75
- else {
76
- // Enter edit mode for current field (toggle for checkbox)
77
- if (currentField === 'anthropicBeta') {
78
- setAnthropicBeta(!anthropicBeta);
79
- }
80
- else {
81
- setIsEditing(true);
82
- }
83
- }
84
- }
85
- else if (!isEditing && key.upArrow) {
86
- if (currentField === 'apiKey') {
87
- setCurrentField('baseUrl');
88
- }
89
- else if (currentField === 'requestMethod') {
90
- setCurrentField('apiKey');
91
- }
92
- else if (currentField === 'anthropicBeta') {
93
- setCurrentField('requestMethod');
94
- }
95
- }
96
- else if (!isEditing && key.downArrow) {
97
- if (currentField === 'baseUrl') {
98
- setCurrentField('apiKey');
99
- }
100
- else if (currentField === 'apiKey') {
101
- setCurrentField('requestMethod');
102
- }
103
- else if (currentField === 'requestMethod') {
104
- setCurrentField('anthropicBeta');
105
- }
106
- }
107
- });
108
- return (React.createElement(Box, { flexDirection: "column", padding: 1 },
109
- !inlineMode && (React.createElement(Box, { marginBottom: 2, borderStyle: "double", borderColor: "cyan", paddingX: 2, paddingY: 1 },
110
- React.createElement(Box, { flexDirection: "column" },
111
- React.createElement(Gradient, { name: "rainbow" }, "OpenAI API Configuration"),
112
- React.createElement(Text, { color: "gray", dimColor: true }, "Configure your OpenAI API settings")))),
113
- React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
114
- React.createElement(Box, { marginBottom: 1 },
115
- React.createElement(Box, { flexDirection: "column" },
116
- React.createElement(Text, { color: currentField === 'baseUrl' ? 'green' : 'white' },
117
- currentField === 'baseUrl' ? '❯ ' : ' ',
118
- "Base URL:"),
119
- currentField === 'baseUrl' && isEditing && (React.createElement(Box, { marginLeft: 3 },
120
- React.createElement(TextInput, { value: baseUrl, onChange: setBaseUrl, placeholder: "https://api.openai.com/v1" }))),
121
- (!isEditing || currentField !== 'baseUrl') && (React.createElement(Box, { marginLeft: 3 },
122
- React.createElement(Text, { color: "gray" }, baseUrl || 'Not set'))))),
123
- React.createElement(Box, { marginBottom: 1 },
124
- React.createElement(Box, { flexDirection: "column" },
125
- React.createElement(Text, { color: currentField === 'apiKey' ? 'green' : 'white' },
126
- currentField === 'apiKey' ? '❯ ' : ' ',
127
- "API Key:"),
128
- currentField === 'apiKey' && isEditing && (React.createElement(Box, { marginLeft: 3 },
129
- React.createElement(TextInput, { value: apiKey, onChange: setApiKey, placeholder: "sk-...", mask: "*" }))),
130
- (!isEditing || currentField !== 'apiKey') && (React.createElement(Box, { marginLeft: 3 },
131
- React.createElement(Text, { color: "gray" }, apiKey ? '*'.repeat(Math.min(apiKey.length, 20)) : 'Not set'))))),
132
- React.createElement(Box, { marginBottom: 1 },
133
- React.createElement(Box, { flexDirection: "column" },
134
- React.createElement(Text, { color: currentField === 'requestMethod' ? 'green' : 'white' },
135
- currentField === 'requestMethod' ? '❯ ' : ' ',
136
- "Request Method:"),
137
- currentField === 'requestMethod' && isEditing && (React.createElement(Box, { marginLeft: 3 },
138
- React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: (value) => {
139
- setRequestMethod(value);
140
- setIsEditing(false); // Auto exit edit mode after selection
141
- } }))),
142
- (!isEditing || currentField !== 'requestMethod') && (React.createElement(Box, { marginLeft: 3 },
143
- React.createElement(Text, { color: "gray" }, requestMethodOptions.find(opt => opt.value === requestMethod)?.label || 'Not set'))))),
144
- React.createElement(Box, { marginBottom: 1 },
145
- React.createElement(Box, { flexDirection: "column" },
146
- React.createElement(Text, { color: currentField === 'anthropicBeta' ? 'green' : 'white' },
147
- currentField === 'anthropicBeta' ? '❯ ' : ' ',
148
- "Anthropic Beta (for Claude API):"),
149
- React.createElement(Box, { marginLeft: 3 },
150
- React.createElement(Text, { color: "gray" },
151
- anthropicBeta ? '☑ Enabled' : '☐ Disabled',
152
- " (Press Enter to toggle)"))))),
153
- errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
154
- React.createElement(Text, { color: "red", bold: true }, "Errors:"),
155
- errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
156
- "\u2022 ",
157
- error))))),
158
- React.createElement(Box, { flexDirection: "column" }, isEditing ? (React.createElement(React.Fragment, null,
159
- React.createElement(Alert, { variant: "info" }, "Editing mode: Press Enter to save and exit editing (Make your changes and press Enter when done)"))) : (React.createElement(React.Fragment, null,
160
- React.createElement(Alert, { variant: "info" }, "Use \u2191\u2193 to navigate between fields, press Enter to edit, and press Ctrl+S or Esc to save and return"))))));
161
- }
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- type Props = {
3
- onBack: () => void;
4
- onSave: () => void;
5
- inlineMode?: boolean;
6
- };
7
- export default function ModelConfigScreen({ onBack, onSave, inlineMode, }: Props): React.JSX.Element;
8
- export {};