snow-ai 0.3.0 → 0.3.2
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/dist/api/anthropic.d.ts +13 -9
- package/dist/api/anthropic.js +77 -34
- package/dist/api/chat.d.ts +14 -29
- package/dist/api/chat.js +62 -19
- package/dist/api/gemini.d.ts +1 -10
- package/dist/api/gemini.js +104 -82
- package/dist/api/models.js +6 -7
- package/dist/api/responses.d.ts +2 -17
- package/dist/api/responses.js +59 -17
- package/dist/api/types.d.ts +39 -0
- package/dist/api/types.js +4 -0
- package/dist/ui/pages/ConfigScreen.js +67 -49
- package/dist/ui/pages/WelcomeScreen.js +1 -1
- package/dist/utils/contextCompressor.d.ts +1 -1
- package/dist/utils/contextCompressor.js +193 -81
- package/package.json +1 -4
package/dist/api/models.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getOpenAiConfig, getCustomHeaders } from '../utils/apiConfig.js';
|
|
|
3
3
|
* Fetch models from OpenAI-compatible API
|
|
4
4
|
*/
|
|
5
5
|
async function fetchOpenAIModels(baseUrl, apiKey, customHeaders) {
|
|
6
|
-
const url = `${baseUrl
|
|
6
|
+
const url = `${baseUrl}/models`;
|
|
7
7
|
const headers = {
|
|
8
8
|
'Content-Type': 'application/json',
|
|
9
9
|
...customHeaders,
|
|
@@ -26,7 +26,7 @@ async function fetchOpenAIModels(baseUrl, apiKey, customHeaders) {
|
|
|
26
26
|
*/
|
|
27
27
|
async function fetchGeminiModels(baseUrl, apiKey) {
|
|
28
28
|
// Gemini uses API key as query parameter
|
|
29
|
-
const url = `${baseUrl
|
|
29
|
+
const url = `${baseUrl}/models?key=${apiKey}`;
|
|
30
30
|
const response = await fetch(url, {
|
|
31
31
|
method: 'GET',
|
|
32
32
|
headers: {
|
|
@@ -50,10 +50,9 @@ async function fetchGeminiModels(baseUrl, apiKey) {
|
|
|
50
50
|
* Supports both Anthropic native format and OpenAI-compatible format for backward compatibility
|
|
51
51
|
*/
|
|
52
52
|
async function fetchAnthropicModels(baseUrl, apiKey, customHeaders) {
|
|
53
|
-
const url = `${baseUrl
|
|
53
|
+
const url = `${baseUrl}/models`;
|
|
54
54
|
const headers = {
|
|
55
55
|
'Content-Type': 'application/json',
|
|
56
|
-
'anthropic-version': '2023-06-01',
|
|
57
56
|
...customHeaders,
|
|
58
57
|
};
|
|
59
58
|
if (apiKey) {
|
|
@@ -106,19 +105,19 @@ export async function fetchAvailableModels() {
|
|
|
106
105
|
if (!config.apiKey) {
|
|
107
106
|
throw new Error('API key is required for Gemini API');
|
|
108
107
|
}
|
|
109
|
-
models = await fetchGeminiModels(config.baseUrl.replace(/\/$/, '')
|
|
108
|
+
models = await fetchGeminiModels(config.baseUrl.replace(/\/$/, ''), config.apiKey);
|
|
110
109
|
break;
|
|
111
110
|
case 'anthropic':
|
|
112
111
|
if (!config.apiKey) {
|
|
113
112
|
throw new Error('API key is required for Anthropic API');
|
|
114
113
|
}
|
|
115
|
-
models = await fetchAnthropicModels(config.baseUrl.replace(/\/$/, '')
|
|
114
|
+
models = await fetchAnthropicModels(config.baseUrl.replace(/\/$/, ''), config.apiKey, customHeaders);
|
|
116
115
|
break;
|
|
117
116
|
case 'chat':
|
|
118
117
|
case 'responses':
|
|
119
118
|
default:
|
|
120
119
|
// OpenAI-compatible API
|
|
121
|
-
models = await fetchOpenAIModels(config.baseUrl, config.apiKey, customHeaders);
|
|
120
|
+
models = await fetchOpenAIModels(config.baseUrl.replace(/\/$/, ''), config.apiKey, customHeaders);
|
|
122
121
|
break;
|
|
123
122
|
}
|
|
124
123
|
// Sort models alphabetically by id for better UX
|
package/dist/api/responses.d.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import type { ChatMessage, ToolCall } from './
|
|
1
|
+
import type { ChatMessage, ToolCall, ChatCompletionTool, UsageInfo } from './types.js';
|
|
2
2
|
export interface ResponseOptions {
|
|
3
3
|
model: string;
|
|
4
4
|
messages: ChatMessage[];
|
|
5
5
|
stream?: boolean;
|
|
6
6
|
temperature?: number;
|
|
7
7
|
max_tokens?: number;
|
|
8
|
-
tools?:
|
|
9
|
-
type: 'function';
|
|
10
|
-
function: {
|
|
11
|
-
name: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
parameters?: Record<string, any>;
|
|
14
|
-
};
|
|
15
|
-
}>;
|
|
8
|
+
tools?: ChatCompletionTool[];
|
|
16
9
|
tool_choice?: 'auto' | 'none' | 'required';
|
|
17
10
|
reasoning?: {
|
|
18
11
|
summary?: 'auto' | 'none';
|
|
@@ -22,14 +15,6 @@ export interface ResponseOptions {
|
|
|
22
15
|
store?: boolean;
|
|
23
16
|
include?: string[];
|
|
24
17
|
}
|
|
25
|
-
export interface UsageInfo {
|
|
26
|
-
prompt_tokens: number;
|
|
27
|
-
completion_tokens: number;
|
|
28
|
-
total_tokens: number;
|
|
29
|
-
cache_creation_input_tokens?: number;
|
|
30
|
-
cache_read_input_tokens?: number;
|
|
31
|
-
cached_tokens?: number;
|
|
32
|
-
}
|
|
33
18
|
export interface ResponseStreamChunk {
|
|
34
19
|
type: 'content' | 'tool_calls' | 'tool_call_delta' | 'reasoning_delta' | 'reasoning_started' | 'done' | 'usage';
|
|
35
20
|
content?: string;
|
package/dist/api/responses.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
2
1
|
import { getOpenAiConfig, getCustomSystemPrompt, getCustomHeaders } from '../utils/apiConfig.js';
|
|
3
2
|
import { SYSTEM_PROMPT } from './systemPrompt.js';
|
|
4
3
|
import { withRetryGenerator } from '../utils/retryUtils.js';
|
|
@@ -52,27 +51,24 @@ function convertToolsForResponses(tools) {
|
|
|
52
51
|
strict: false
|
|
53
52
|
}));
|
|
54
53
|
}
|
|
55
|
-
let
|
|
56
|
-
function
|
|
57
|
-
if (!
|
|
54
|
+
let openaiConfig = null;
|
|
55
|
+
function getOpenAIConfig() {
|
|
56
|
+
if (!openaiConfig) {
|
|
58
57
|
const config = getOpenAiConfig();
|
|
59
58
|
if (!config.apiKey || !config.baseUrl) {
|
|
60
59
|
throw new Error('OpenAI API configuration is incomplete. Please configure API settings first.');
|
|
61
60
|
}
|
|
62
|
-
// Get custom headers
|
|
63
61
|
const customHeaders = getCustomHeaders();
|
|
64
|
-
|
|
62
|
+
openaiConfig = {
|
|
65
63
|
apiKey: config.apiKey,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
});
|
|
64
|
+
baseUrl: config.baseUrl,
|
|
65
|
+
customHeaders
|
|
66
|
+
};
|
|
71
67
|
}
|
|
72
|
-
return
|
|
68
|
+
return openaiConfig;
|
|
73
69
|
}
|
|
74
70
|
export function resetOpenAIClient() {
|
|
75
|
-
|
|
71
|
+
openaiConfig = null;
|
|
76
72
|
}
|
|
77
73
|
function convertToResponseInput(messages) {
|
|
78
74
|
const customSystemPrompt = getCustomSystemPrompt();
|
|
@@ -177,11 +173,43 @@ function convertToResponseInput(messages) {
|
|
|
177
173
|
}
|
|
178
174
|
return { input: result, systemInstructions };
|
|
179
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Parse Server-Sent Events (SSE) stream
|
|
178
|
+
*/
|
|
179
|
+
async function* parseSSEStream(reader) {
|
|
180
|
+
const decoder = new TextDecoder();
|
|
181
|
+
let buffer = '';
|
|
182
|
+
while (true) {
|
|
183
|
+
const { done, value } = await reader.read();
|
|
184
|
+
if (done)
|
|
185
|
+
break;
|
|
186
|
+
buffer += decoder.decode(value, { stream: true });
|
|
187
|
+
const lines = buffer.split('\n');
|
|
188
|
+
buffer = lines.pop() || '';
|
|
189
|
+
for (const line of lines) {
|
|
190
|
+
const trimmed = line.trim();
|
|
191
|
+
if (!trimmed || trimmed.startsWith(':'))
|
|
192
|
+
continue;
|
|
193
|
+
if (trimmed === 'data: [DONE]') {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (trimmed.startsWith('data: ')) {
|
|
197
|
+
const data = trimmed.slice(6);
|
|
198
|
+
try {
|
|
199
|
+
yield JSON.parse(data);
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
console.error('Failed to parse SSE data:', data);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
180
208
|
/**
|
|
181
209
|
* 使用 Responses API 创建流式响应(带自动工具调用)
|
|
182
210
|
*/
|
|
183
211
|
export async function* createStreamingResponse(options, abortSignal, onRetry) {
|
|
184
|
-
const
|
|
212
|
+
const config = getOpenAIConfig();
|
|
185
213
|
// 提取系统提示词和转换后的消息
|
|
186
214
|
const { input: requestInput, systemInstructions } = convertToResponseInput(options.messages);
|
|
187
215
|
// 使用重试包装生成器
|
|
@@ -198,15 +226,29 @@ export async function* createStreamingResponse(options, abortSignal, onRetry) {
|
|
|
198
226
|
include: options.include || ['reasoning.encrypted_content'],
|
|
199
227
|
prompt_cache_key: options.prompt_cache_key,
|
|
200
228
|
};
|
|
201
|
-
const
|
|
202
|
-
|
|
229
|
+
const response = await fetch(`${config.baseUrl}/responses`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
234
|
+
...config.customHeaders
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify(requestPayload),
|
|
237
|
+
signal: abortSignal
|
|
203
238
|
});
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const errorText = await response.text();
|
|
241
|
+
throw new Error(`OpenAI Responses API error: ${response.status} ${response.statusText} - ${errorText}`);
|
|
242
|
+
}
|
|
243
|
+
if (!response.body) {
|
|
244
|
+
throw new Error('No response body from OpenAI Responses API');
|
|
245
|
+
}
|
|
204
246
|
let contentBuffer = '';
|
|
205
247
|
let toolCallsBuffer = {};
|
|
206
248
|
let hasToolCalls = false;
|
|
207
249
|
let currentFunctionCallId = null;
|
|
208
250
|
let usageData;
|
|
209
|
-
for await (const chunk of
|
|
251
|
+
for await (const chunk of parseSSEStream(response.body.getReader())) {
|
|
210
252
|
if (abortSignal?.aborted) {
|
|
211
253
|
return;
|
|
212
254
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared API types for all AI providers
|
|
3
|
+
*/
|
|
4
|
+
export interface ImageContent {
|
|
5
|
+
type: 'image';
|
|
6
|
+
data: string;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ToolCall {
|
|
10
|
+
id: string;
|
|
11
|
+
type: 'function';
|
|
12
|
+
function: {
|
|
13
|
+
name: string;
|
|
14
|
+
arguments: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface ChatMessage {
|
|
18
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
19
|
+
content: string;
|
|
20
|
+
tool_call_id?: string;
|
|
21
|
+
tool_calls?: ToolCall[];
|
|
22
|
+
images?: ImageContent[];
|
|
23
|
+
}
|
|
24
|
+
export interface ChatCompletionTool {
|
|
25
|
+
type: 'function';
|
|
26
|
+
function: {
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
parameters?: Record<string, any>;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface UsageInfo {
|
|
33
|
+
prompt_tokens: number;
|
|
34
|
+
completion_tokens: number;
|
|
35
|
+
total_tokens: number;
|
|
36
|
+
cache_creation_input_tokens?: number;
|
|
37
|
+
cache_read_input_tokens?: number;
|
|
38
|
+
cached_tokens?: number;
|
|
39
|
+
}
|
|
@@ -66,6 +66,7 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
66
66
|
const [searchTerm, setSearchTerm] = useState('');
|
|
67
67
|
const [manualInputMode, setManualInputMode] = useState(false);
|
|
68
68
|
const [manualInputValue, setManualInputValue] = useState('');
|
|
69
|
+
const [, forceUpdate] = useState(0);
|
|
69
70
|
const requestMethodOptions = [
|
|
70
71
|
{
|
|
71
72
|
label: 'Chat Completions - Modern chat API (GPT-4, GPT-3.5-turbo)',
|
|
@@ -166,6 +167,7 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
166
167
|
if (value === '__DELETE__') {
|
|
167
168
|
if (activeProfile === 'default') {
|
|
168
169
|
setErrors(['Cannot delete the default profile']);
|
|
170
|
+
setIsEditing(false); // Exit editing mode to prevent Select component error
|
|
169
171
|
return;
|
|
170
172
|
}
|
|
171
173
|
setProfileMode('deleting');
|
|
@@ -222,6 +224,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
222
224
|
const handleDeleteProfile = () => {
|
|
223
225
|
try {
|
|
224
226
|
deleteProfile(activeProfile);
|
|
227
|
+
// Important: Update activeProfile state BEFORE loading profiles
|
|
228
|
+
// because deleteProfile switches to 'default' if the active profile is deleted
|
|
229
|
+
const newActiveProfile = getActiveProfileName();
|
|
230
|
+
setActiveProfile(newActiveProfile);
|
|
225
231
|
loadProfilesAndConfig();
|
|
226
232
|
setProfileMode('normal');
|
|
227
233
|
setIsEditing(false);
|
|
@@ -387,6 +393,8 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
387
393
|
key.escape) {
|
|
388
394
|
setIsEditing(false);
|
|
389
395
|
setSearchTerm('');
|
|
396
|
+
// Force re-render to clear Select component artifacts
|
|
397
|
+
forceUpdate(prev => prev + 1);
|
|
390
398
|
return;
|
|
391
399
|
}
|
|
392
400
|
// Handle editing mode
|
|
@@ -615,26 +623,58 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
615
623
|
activeProfile && (React.createElement(Text, { color: "cyan", dimColor: true },
|
|
616
624
|
"Active Profile: ",
|
|
617
625
|
activeProfile))))),
|
|
618
|
-
|
|
626
|
+
isEditing &&
|
|
627
|
+
(currentField === 'profile' ||
|
|
628
|
+
currentField === 'requestMethod' ||
|
|
629
|
+
currentField === 'advancedModel' ||
|
|
630
|
+
currentField === 'basicModel' ||
|
|
631
|
+
currentField === 'compactModelName') ? (React.createElement(Box, { flexDirection: "column" },
|
|
632
|
+
React.createElement(Text, { color: "green" },
|
|
633
|
+
"\u276F ",
|
|
634
|
+
currentField === 'profile' && 'Profile',
|
|
635
|
+
currentField === 'requestMethod' && 'Request Method',
|
|
636
|
+
currentField === 'advancedModel' && 'Advanced Model',
|
|
637
|
+
currentField === 'basicModel' && 'Basic Model',
|
|
638
|
+
currentField === 'compactModelName' && 'Compact Model',
|
|
639
|
+
":"),
|
|
640
|
+
React.createElement(Box, { marginLeft: 3, marginTop: 1 },
|
|
641
|
+
currentField === 'profile' && (React.createElement(Select, { options: [
|
|
642
|
+
...profiles.map(p => ({
|
|
643
|
+
label: `${p.displayName}${p.isActive ? ' (Active)' : ''}`,
|
|
644
|
+
value: p.name,
|
|
645
|
+
})),
|
|
646
|
+
{
|
|
647
|
+
label: chalk.green('+ New Profile'),
|
|
648
|
+
value: '__CREATE_NEW__',
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
label: chalk.red('🆇 Delete Profile'),
|
|
652
|
+
value: '__DELETE__',
|
|
653
|
+
},
|
|
654
|
+
], defaultValue: activeProfile, onChange: handleProfileChange })),
|
|
655
|
+
currentField === 'requestMethod' && (React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: value => {
|
|
656
|
+
setRequestMethod(value);
|
|
657
|
+
setIsEditing(false);
|
|
658
|
+
} })),
|
|
659
|
+
(currentField === 'advancedModel' ||
|
|
660
|
+
currentField === 'basicModel' ||
|
|
661
|
+
currentField === 'compactModelName') && (React.createElement(Box, { flexDirection: "column" },
|
|
662
|
+
searchTerm && React.createElement(Text, { color: "cyan" },
|
|
663
|
+
"Filter: ",
|
|
664
|
+
searchTerm),
|
|
665
|
+
React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
|
|
666
|
+
React.createElement(Box, { marginTop: 1 },
|
|
667
|
+
React.createElement(Alert, { variant: "info" },
|
|
668
|
+
(currentField === 'advancedModel' ||
|
|
669
|
+
currentField === 'basicModel' ||
|
|
670
|
+
currentField === 'compactModelName') &&
|
|
671
|
+
'Type to filter, ↑↓ to select, Enter to confirm, Esc to cancel',
|
|
672
|
+
(currentField === 'profile' || currentField === 'requestMethod') &&
|
|
673
|
+
'↑↓ to select, Enter to confirm, Esc to cancel')))) : (React.createElement(Box, { flexDirection: "column" },
|
|
619
674
|
React.createElement(Box, { flexDirection: "column" },
|
|
620
675
|
React.createElement(Text, { color: currentField === 'profile' ? 'green' : 'white' },
|
|
621
676
|
currentField === 'profile' ? '❯ ' : ' ',
|
|
622
677
|
"Profile:"),
|
|
623
|
-
currentField === 'profile' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
624
|
-
React.createElement(Select, { options: [
|
|
625
|
-
...profiles.map(p => ({
|
|
626
|
-
label: `${p.displayName}${p.isActive ? ' (Active)' : ''}`,
|
|
627
|
-
value: p.name,
|
|
628
|
-
})),
|
|
629
|
-
{
|
|
630
|
-
label: chalk.green('+ New Profile'),
|
|
631
|
-
value: '__CREATE_NEW__',
|
|
632
|
-
},
|
|
633
|
-
{
|
|
634
|
-
label: chalk.red('🆇 Delete Profile'),
|
|
635
|
-
value: '__DELETE__',
|
|
636
|
-
},
|
|
637
|
-
], defaultValue: activeProfile, onChange: handleProfileChange }))),
|
|
638
678
|
(!isEditing || currentField !== 'profile') && (React.createElement(Box, { marginLeft: 3 },
|
|
639
679
|
React.createElement(Text, { color: "gray" }, profiles.find(p => p.name === activeProfile)?.displayName ||
|
|
640
680
|
activeProfile)))),
|
|
@@ -658,11 +698,6 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
658
698
|
React.createElement(Text, { color: currentField === 'requestMethod' ? 'green' : 'white' },
|
|
659
699
|
currentField === 'requestMethod' ? '❯ ' : ' ',
|
|
660
700
|
"Request Method:"),
|
|
661
|
-
currentField === 'requestMethod' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
662
|
-
React.createElement(Select, { options: requestMethodOptions, defaultValue: requestMethod, onChange: value => {
|
|
663
|
-
setRequestMethod(value);
|
|
664
|
-
setIsEditing(false);
|
|
665
|
-
} }))),
|
|
666
701
|
(!isEditing || currentField !== 'requestMethod') && (React.createElement(Box, { marginLeft: 3 },
|
|
667
702
|
React.createElement(Text, { color: "gray" }, requestMethodOptions.find(opt => opt.value === requestMethod)
|
|
668
703
|
?.label || 'Not set')))),
|
|
@@ -678,36 +713,18 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
678
713
|
React.createElement(Text, { color: currentField === 'advancedModel' ? 'green' : 'white' },
|
|
679
714
|
currentField === 'advancedModel' ? '❯ ' : ' ',
|
|
680
715
|
"Advanced Model:"),
|
|
681
|
-
currentField === 'advancedModel' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
682
|
-
React.createElement(Box, { flexDirection: "column" },
|
|
683
|
-
searchTerm && React.createElement(Text, { color: "cyan" },
|
|
684
|
-
"Filter: ",
|
|
685
|
-
searchTerm),
|
|
686
|
-
React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
|
|
687
716
|
(!isEditing || currentField !== 'advancedModel') && (React.createElement(Box, { marginLeft: 3 },
|
|
688
717
|
React.createElement(Text, { color: "gray" }, advancedModel || 'Not set')))),
|
|
689
718
|
React.createElement(Box, { flexDirection: "column" },
|
|
690
719
|
React.createElement(Text, { color: currentField === 'basicModel' ? 'green' : 'white' },
|
|
691
720
|
currentField === 'basicModel' ? '❯ ' : ' ',
|
|
692
721
|
"Basic Model:"),
|
|
693
|
-
currentField === 'basicModel' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
694
|
-
React.createElement(Box, { flexDirection: "column" },
|
|
695
|
-
searchTerm && React.createElement(Text, { color: "cyan" },
|
|
696
|
-
"Filter: ",
|
|
697
|
-
searchTerm),
|
|
698
|
-
React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
|
|
699
722
|
(!isEditing || currentField !== 'basicModel') && (React.createElement(Box, { marginLeft: 3 },
|
|
700
723
|
React.createElement(Text, { color: "gray" }, basicModel || 'Not set')))),
|
|
701
724
|
React.createElement(Box, { flexDirection: "column" },
|
|
702
725
|
React.createElement(Text, { color: currentField === 'compactModelName' ? 'green' : 'white' },
|
|
703
726
|
currentField === 'compactModelName' ? '❯ ' : ' ',
|
|
704
727
|
"Compact Model:"),
|
|
705
|
-
currentField === 'compactModelName' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
706
|
-
React.createElement(Box, { flexDirection: "column" },
|
|
707
|
-
searchTerm && React.createElement(Text, { color: "cyan" },
|
|
708
|
-
"Filter: ",
|
|
709
|
-
searchTerm),
|
|
710
|
-
React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange })))),
|
|
711
728
|
(!isEditing || currentField !== 'compactModelName') && (React.createElement(Box, { marginLeft: 3 },
|
|
712
729
|
React.createElement(Text, { color: "gray" }, compactModelName || 'Not set')))),
|
|
713
730
|
React.createElement(Box, { flexDirection: "column" },
|
|
@@ -729,20 +746,21 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
729
746
|
"Enter value: ",
|
|
730
747
|
maxTokens))),
|
|
731
748
|
(!isEditing || currentField !== 'maxTokens') && (React.createElement(Box, { marginLeft: 3 },
|
|
732
|
-
React.createElement(Text, { color: "gray" }, maxTokens))))),
|
|
749
|
+
React.createElement(Text, { color: "gray" }, maxTokens)))))),
|
|
733
750
|
errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
734
751
|
React.createElement(Text, { color: "red", bold: true }, "Errors:"),
|
|
735
752
|
errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
|
|
736
753
|
"\u2022 ",
|
|
737
754
|
error))))),
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
' ',
|
|
755
|
+
!(isEditing &&
|
|
756
|
+
(currentField === 'profile' ||
|
|
757
|
+
currentField === 'requestMethod' ||
|
|
742
758
|
currentField === 'advancedModel' ||
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
759
|
+
currentField === 'basicModel' ||
|
|
760
|
+
currentField === 'compactModelName')) && (React.createElement(Box, { flexDirection: "column", marginTop: 1 }, isEditing ? (React.createElement(Alert, { variant: "info" },
|
|
761
|
+
"Editing mode:",
|
|
762
|
+
' ',
|
|
763
|
+
currentField === 'maxContextTokens' || currentField === 'maxTokens'
|
|
764
|
+
? 'Type to edit, Enter to save'
|
|
765
|
+
: 'Press Enter to save and exit editing')) : (React.createElement(Alert, { variant: "info" }, "Use \u2191\u2193 to navigate, Enter to edit, M for manual input, Ctrl+S or Esc to save"))))));
|
|
748
766
|
}
|
|
@@ -92,7 +92,7 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
92
92
|
}, [terminalWidth, stdout]);
|
|
93
93
|
return (React.createElement(Box, { flexDirection: "column", width: terminalWidth },
|
|
94
94
|
React.createElement(Static, { key: remountKey, items: [
|
|
95
|
-
React.createElement(Box, { key: "welcome-header", flexDirection: "row", paddingLeft: 2, paddingTop: 1, paddingBottom:
|
|
95
|
+
React.createElement(Box, { key: "welcome-header", flexDirection: "row", paddingLeft: 2, paddingTop: 1, paddingBottom: 0, width: terminalWidth },
|
|
96
96
|
React.createElement(Box, { flexDirection: "column", justifyContent: "center" },
|
|
97
97
|
React.createElement(Text, { bold: true },
|
|
98
98
|
React.createElement(Gradient, { name: "rainbow" }, "\u2746 SNOW AI CLI")),
|