snow-ai 0.2.16 → 0.2.17
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/models.d.ts +3 -0
- package/dist/api/models.js +101 -17
- package/dist/ui/components/ChatInput.js +3 -3
- package/dist/ui/pages/ChatScreen.js +19 -18
- package/package.json +1 -1
package/dist/api/models.d.ts
CHANGED
|
@@ -8,5 +8,8 @@ export interface ModelsResponse {
|
|
|
8
8
|
object: string;
|
|
9
9
|
data: Model[];
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Fetch available models based on configured request method
|
|
13
|
+
*/
|
|
11
14
|
export declare function fetchAvailableModels(): Promise<Model[]>;
|
|
12
15
|
export declare function filterModels(models: Model[], searchTerm: string): Model[];
|
package/dist/api/models.js
CHANGED
|
@@ -1,28 +1,112 @@
|
|
|
1
|
-
import { getOpenAiConfig } from '../utils/apiConfig.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { getOpenAiConfig, getCustomHeaders } from '../utils/apiConfig.js';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch models from OpenAI-compatible API
|
|
4
|
+
*/
|
|
5
|
+
async function fetchOpenAIModels(baseUrl, apiKey, customHeaders) {
|
|
6
|
+
const url = `${baseUrl.replace(/\/$/, '')}/models`;
|
|
7
|
+
const headers = {
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
...customHeaders,
|
|
10
|
+
};
|
|
11
|
+
if (apiKey) {
|
|
12
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
method: 'GET',
|
|
16
|
+
headers,
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
|
|
20
|
+
}
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
return data.data || [];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fetch models from Gemini API
|
|
26
|
+
*/
|
|
27
|
+
async function fetchGeminiModels(baseUrl, apiKey) {
|
|
28
|
+
// Gemini uses API key as query parameter
|
|
29
|
+
const url = `${baseUrl.replace(/\/$/, '')}/models?key=${apiKey}`;
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
method: 'GET',
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
|
|
6
38
|
}
|
|
7
|
-
const
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
// Convert Gemini format to standard Model format
|
|
41
|
+
return (data.models || []).map(model => ({
|
|
42
|
+
id: model.name.replace('models/', ''), // Remove "models/" prefix
|
|
43
|
+
object: 'model',
|
|
44
|
+
created: 0,
|
|
45
|
+
owned_by: 'google',
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fetch models from Anthropic API
|
|
50
|
+
*/
|
|
51
|
+
async function fetchAnthropicModels(baseUrl, apiKey, customHeaders) {
|
|
52
|
+
const url = `${baseUrl.replace(/\/$/, '')}/models`;
|
|
8
53
|
const headers = {
|
|
9
54
|
'Content-Type': 'application/json',
|
|
55
|
+
'anthropic-version': '2023-06-01',
|
|
56
|
+
...customHeaders,
|
|
10
57
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
58
|
+
if (apiKey) {
|
|
59
|
+
headers['x-api-key'] = apiKey;
|
|
60
|
+
}
|
|
61
|
+
const response = await fetch(url, {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
headers,
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
|
|
67
|
+
}
|
|
68
|
+
const data = await response.json();
|
|
69
|
+
// Convert Anthropic format to standard Model format
|
|
70
|
+
return (data.data || []).map(model => ({
|
|
71
|
+
id: model.id,
|
|
72
|
+
object: 'model',
|
|
73
|
+
created: new Date(model.created_at).getTime() / 1000, // Convert to Unix timestamp
|
|
74
|
+
owned_by: 'anthropic',
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fetch available models based on configured request method
|
|
79
|
+
*/
|
|
80
|
+
export async function fetchAvailableModels() {
|
|
81
|
+
const config = getOpenAiConfig();
|
|
82
|
+
if (!config.baseUrl) {
|
|
83
|
+
throw new Error('Base URL not configured. Please configure API settings first.');
|
|
14
84
|
}
|
|
85
|
+
const customHeaders = getCustomHeaders();
|
|
15
86
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
87
|
+
let models;
|
|
88
|
+
switch (config.requestMethod) {
|
|
89
|
+
case 'gemini':
|
|
90
|
+
if (!config.apiKey) {
|
|
91
|
+
throw new Error('API key is required for Gemini API');
|
|
92
|
+
}
|
|
93
|
+
models = await fetchGeminiModels(config.baseUrl.replace(/\/$/, '') + '/v1beta', config.apiKey);
|
|
94
|
+
break;
|
|
95
|
+
case 'anthropic':
|
|
96
|
+
if (!config.apiKey) {
|
|
97
|
+
throw new Error('API key is required for Anthropic API');
|
|
98
|
+
}
|
|
99
|
+
models = await fetchAnthropicModels(config.baseUrl.replace(/\/$/, '') + '/v1', config.apiKey, customHeaders);
|
|
100
|
+
break;
|
|
101
|
+
case 'chat':
|
|
102
|
+
case 'responses':
|
|
103
|
+
default:
|
|
104
|
+
// OpenAI-compatible API
|
|
105
|
+
models = await fetchOpenAIModels(config.baseUrl, config.apiKey, customHeaders);
|
|
106
|
+
break;
|
|
22
107
|
}
|
|
23
|
-
const data = await response.json();
|
|
24
108
|
// Sort models alphabetically by id for better UX
|
|
25
|
-
return
|
|
109
|
+
return models.sort((a, b) => a.id.localeCompare(b.id));
|
|
26
110
|
}
|
|
27
111
|
catch (error) {
|
|
28
112
|
if (error instanceof Error) {
|
|
@@ -136,8 +136,8 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
136
136
|
React.createElement(Text, { color: disabled ? 'darkGray' : 'gray', dimColor: true }, disabled ? 'Waiting for response...' : placeholder)));
|
|
137
137
|
}
|
|
138
138
|
}, [buffer, disabled, placeholder]);
|
|
139
|
-
return (React.createElement(Box, { flexDirection: "column",
|
|
140
|
-
showHistoryMenu && (React.createElement(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "#A9C13E", padding: 1 },
|
|
139
|
+
return (React.createElement(Box, { flexDirection: "column", paddingX: 1, width: "100%", key: `input-${showFilePicker ? 'picker' : 'normal'}` },
|
|
140
|
+
showHistoryMenu && (React.createElement(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "#A9C13E", padding: 1, width: "100%" },
|
|
141
141
|
React.createElement(Box, { marginBottom: 1 },
|
|
142
142
|
React.createElement(Text, { color: "cyan" }, "Use \u2191\u2193 keys to navigate, press Enter to select:")),
|
|
143
143
|
React.createElement(Box, { flexDirection: "column" },
|
|
@@ -177,7 +177,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
177
177
|
getUserMessages().length - 8,
|
|
178
178
|
" more items")))))),
|
|
179
179
|
!showHistoryMenu && (React.createElement(React.Fragment, null,
|
|
180
|
-
React.createElement(Box, { flexDirection: "row", borderStyle: "round", borderColor: "gray", paddingX: 1, paddingY: 0, flexGrow: 1 },
|
|
180
|
+
React.createElement(Box, { flexDirection: "row", borderStyle: "round", borderColor: "gray", paddingX: 1, paddingY: 0, flexGrow: 1, width: "100%" },
|
|
181
181
|
React.createElement(Text, { color: "cyan", bold: true },
|
|
182
182
|
"\u27A3",
|
|
183
183
|
' '),
|
|
@@ -416,19 +416,20 @@ export default function ChatScreen({}) {
|
|
|
416
416
|
React.createElement(Box, { marginTop: 1 },
|
|
417
417
|
React.createElement(Text, { color: "gray", dimColor: true }, "Please resize your terminal window to continue."))));
|
|
418
418
|
}
|
|
419
|
-
return (React.createElement(Box, { flexDirection: "column", height: "100%" },
|
|
419
|
+
return (React.createElement(Box, { flexDirection: "column", height: "100%", width: "100%" },
|
|
420
420
|
React.createElement(Static, { key: remountKey, items: [
|
|
421
|
-
React.createElement(Box, { key: "header",
|
|
422
|
-
React.createElement(Box, {
|
|
423
|
-
React.createElement(
|
|
424
|
-
React.createElement(Text, { color: "
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
421
|
+
React.createElement(Box, { key: "header", paddingX: 1, width: "100%" },
|
|
422
|
+
React.createElement(Box, { borderColor: 'cyan', borderStyle: "round", paddingX: 2, paddingY: 1, width: "100%" },
|
|
423
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
424
|
+
React.createElement(Text, { color: "white", bold: true },
|
|
425
|
+
React.createElement(Text, { color: "cyan" }, "\u2746 "),
|
|
426
|
+
React.createElement(Gradient, { name: "rainbow" }, "Programming efficiency x10!"),
|
|
427
|
+
React.createElement(Text, { color: "white" }, " \u26C7")),
|
|
428
|
+
React.createElement(Text, null, "\u2022 Ask for code explanations and debugging help"),
|
|
429
|
+
React.createElement(Text, null, "\u2022 Press ESC during response to interrupt"),
|
|
430
|
+
React.createElement(Text, null,
|
|
431
|
+
"\u2022 Working directory: ",
|
|
432
|
+
workingDirectory)))),
|
|
432
433
|
...messages
|
|
433
434
|
.filter(m => !m.streaming && !m.toolPending)
|
|
434
435
|
.map((message, index) => {
|
|
@@ -452,7 +453,7 @@ export default function ChatScreen({}) {
|
|
|
452
453
|
toolStatusColor = 'blue';
|
|
453
454
|
}
|
|
454
455
|
}
|
|
455
|
-
return (React.createElement(Box, { key: `msg-${index}`, marginBottom: isToolMessage ? 0 : 1,
|
|
456
|
+
return (React.createElement(Box, { key: `msg-${index}`, marginBottom: isToolMessage ? 0 : 1, paddingX: 1, flexDirection: "column", width: "100%" },
|
|
456
457
|
React.createElement(Box, null,
|
|
457
458
|
React.createElement(Text, { color: message.role === 'user'
|
|
458
459
|
? 'green'
|
|
@@ -565,14 +566,14 @@ export default function ChatScreen({}) {
|
|
|
565
566
|
] }, item => item),
|
|
566
567
|
messages
|
|
567
568
|
.filter(m => m.toolPending)
|
|
568
|
-
.map((message, index) => (React.createElement(Box, { key: `pending-tool-${index}`, marginBottom: 1,
|
|
569
|
+
.map((message, index) => (React.createElement(Box, { key: `pending-tool-${index}`, marginBottom: 1, paddingX: 1, width: "100%" },
|
|
569
570
|
React.createElement(Text, { color: "yellowBright", bold: true }, "\u2746"),
|
|
570
571
|
React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "row" },
|
|
571
572
|
React.createElement(MarkdownRenderer, { content: message.content || ' ', color: "yellow" }),
|
|
572
573
|
React.createElement(Box, { marginLeft: 1 },
|
|
573
574
|
React.createElement(Text, { color: "yellow" },
|
|
574
575
|
React.createElement(Spinner, { type: "dots" }))))))),
|
|
575
|
-
(streamingState.isStreaming || isSaving) && !pendingToolConfirmation && (React.createElement(Box, { marginBottom: 1,
|
|
576
|
+
(streamingState.isStreaming || isSaving) && !pendingToolConfirmation && (React.createElement(Box, { marginBottom: 1, paddingX: 1, width: "100%" },
|
|
576
577
|
React.createElement(Text, { color: ['#FF6EBF', 'green', 'blue', 'cyan', '#B588F8'][streamingState.animationFrame], bold: true }, "\u2746"),
|
|
577
578
|
React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" }, streamingState.isStreaming ? (React.createElement(React.Fragment, null, streamingState.retryStatus && streamingState.retryStatus.isRetrying ? (
|
|
578
579
|
// Retry status display - hide "Thinking" and show retry info
|
|
@@ -605,15 +606,15 @@ export default function ChatScreen({}) {
|
|
|
605
606
|
' ',
|
|
606
607
|
"tokens"),
|
|
607
608
|
")")))) : (React.createElement(Text, { color: "gray", dimColor: true }, "Create the first dialogue record file..."))))),
|
|
608
|
-
React.createElement(Box, {
|
|
609
|
+
React.createElement(Box, { paddingX: 1, width: "100%" },
|
|
609
610
|
React.createElement(PendingMessages, { pendingMessages: pendingMessages })),
|
|
610
611
|
pendingToolConfirmation && (React.createElement(ToolConfirmation, { toolName: pendingToolConfirmation.batchToolNames ||
|
|
611
612
|
pendingToolConfirmation.tool.function.name, toolArguments: !pendingToolConfirmation.allTools
|
|
612
613
|
? pendingToolConfirmation.tool.function.arguments
|
|
613
614
|
: undefined, allTools: pendingToolConfirmation.allTools, onConfirm: pendingToolConfirmation.resolve })),
|
|
614
|
-
showSessionPanel && (React.createElement(Box, {
|
|
615
|
+
showSessionPanel && (React.createElement(Box, { paddingX: 1, width: "100%" },
|
|
615
616
|
React.createElement(SessionListPanel, { onSelectSession: handleSessionPanelSelect, onClose: () => setShowSessionPanel(false) }))),
|
|
616
|
-
showMcpPanel && (React.createElement(Box, {
|
|
617
|
+
showMcpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: "100%" },
|
|
617
618
|
React.createElement(MCPInfoPanel, null),
|
|
618
619
|
React.createElement(Box, { marginTop: 1 },
|
|
619
620
|
React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to close")))),
|