aiexecode 1.0.92 → 1.0.96
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +210 -87
- package/index.js +33 -1
- package/package.json +3 -3
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/_next/static/chunks/{37d0cd2587a38f79.js → b6c0459f3789d25c.js} +1 -1
- package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +3 -3
- package/payload_viewer/web_server.js +361 -0
- package/src/LLMClient/client.js +392 -16
- package/src/LLMClient/converters/responses-to-claude.js +67 -18
- package/src/LLMClient/converters/responses-to-zai.js +608 -0
- package/src/LLMClient/errors.js +18 -4
- package/src/LLMClient/index.js +5 -0
- package/src/ai_based/completion_judge.js +35 -4
- package/src/ai_based/orchestrator.js +146 -35
- package/src/commands/agents.js +70 -0
- package/src/commands/commands.js +51 -0
- package/src/commands/debug.js +52 -0
- package/src/commands/help.js +11 -1
- package/src/commands/model.js +43 -7
- package/src/commands/skills.js +46 -0
- package/src/config/ai_models.js +96 -5
- package/src/config/constants.js +71 -0
- package/src/frontend/App.js +4 -5
- package/src/frontend/components/ConversationItem.js +25 -24
- package/src/frontend/components/HelpView.js +106 -2
- package/src/frontend/components/SetupWizard.js +53 -8
- package/src/frontend/utils/syntaxHighlighter.js +4 -4
- package/src/frontend/utils/toolUIFormatter.js +261 -0
- package/src/system/agents_loader.js +289 -0
- package/src/system/ai_request.js +147 -9
- package/src/system/command_parser.js +33 -3
- package/src/system/conversation_state.js +265 -0
- package/src/system/custom_command_loader.js +386 -0
- package/src/system/session.js +59 -35
- package/src/system/skill_loader.js +318 -0
- package/src/system/tool_approval.js +10 -0
- package/src/tools/file_reader.js +49 -9
- package/src/tools/glob.js +0 -3
- package/src/tools/ripgrep.js +5 -7
- package/src/tools/skill_tool.js +122 -0
- package/src/tools/web_downloader.js +0 -3
- package/src/util/clone.js +174 -0
- package/src/util/config.js +38 -2
- package/src/util/config_migration.js +174 -0
- package/src/util/path_validator.js +178 -0
- package/src/util/prompt_loader.js +68 -1
- package/src/util/safe_fs.js +43 -3
- package/payload_viewer/out/_next/static/chunks/ecd2072ebf41611f.css +0 -3
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{d0-fu2rgYnshgGFPxr1CR → lHmNygVpv4N1VR0LdnwkJ}/_ssgManifest.js +0 -0
|
@@ -5,7 +5,24 @@
|
|
|
5
5
|
import React, { useState, useRef } from 'react';
|
|
6
6
|
import { Box, Text, useInput } from 'ink';
|
|
7
7
|
import { theme } from '../design/themeColors.js';
|
|
8
|
-
import { AI_MODELS, getAllModelIds, DEFAULT_MODEL } from '../../config/ai_models.js';
|
|
8
|
+
import { AI_MODELS, getAllModelIds, getModelsByProvider, DEFAULT_MODEL } from '../../config/ai_models.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* API 키 스타일로 provider 감지
|
|
12
|
+
* @param {string} apiKey - API 키
|
|
13
|
+
* @returns {string|null} provider 이름 또는 null
|
|
14
|
+
*/
|
|
15
|
+
function detectProviderFromApiKey(apiKey) {
|
|
16
|
+
if (!apiKey || typeof apiKey !== 'string') return null;
|
|
17
|
+
|
|
18
|
+
if (apiKey.startsWith('sk-proj-')) {
|
|
19
|
+
return 'openai';
|
|
20
|
+
} else if (/^[a-f0-9]{32}\.[A-Za-z0-9]{16}$/.test(apiKey)) {
|
|
21
|
+
// Z.AI API 키 형식: 32자리 hex + '.' + 16자리 영숫자
|
|
22
|
+
return 'zai';
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
9
26
|
|
|
10
27
|
const STEPS = {
|
|
11
28
|
API_KEY: 'api_key',
|
|
@@ -17,6 +34,8 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
17
34
|
const [step, setStep] = useState(STEPS.API_KEY);
|
|
18
35
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
19
36
|
const [textInput, setTextInput] = useState('');
|
|
37
|
+
const [detectedProvider, setDetectedProvider] = useState(null);
|
|
38
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
20
39
|
|
|
21
40
|
// settings를 ref로 관리하여 stale closure 문제 방지
|
|
22
41
|
const settingsRef = useRef({
|
|
@@ -25,6 +44,14 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
25
44
|
REASONING_EFFORT: 'medium'
|
|
26
45
|
});
|
|
27
46
|
|
|
47
|
+
// 감지된 provider에 따른 모델 목록
|
|
48
|
+
const getAvailableModels = () => {
|
|
49
|
+
if (detectedProvider) {
|
|
50
|
+
return getModelsByProvider(detectedProvider);
|
|
51
|
+
}
|
|
52
|
+
return getAllModelIds();
|
|
53
|
+
};
|
|
54
|
+
|
|
28
55
|
// 현재 스텝이 텍스트 입력인지 선택지인지 판단
|
|
29
56
|
const isTextInputStep = step === STEPS.API_KEY;
|
|
30
57
|
|
|
@@ -41,14 +68,26 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
41
68
|
if (!textInput.trim()) {
|
|
42
69
|
return;
|
|
43
70
|
}
|
|
44
|
-
|
|
71
|
+
const apiKey = textInput.trim();
|
|
72
|
+
// API 키 스타일로 provider 감지
|
|
73
|
+
const provider = detectProviderFromApiKey(apiKey);
|
|
74
|
+
if (!provider) {
|
|
75
|
+
// 유효하지 않은 API 키 형식
|
|
76
|
+
setErrorMessage('Invalid API key. Please use OpenAI (sk-proj-...) or Z.AI format.');
|
|
77
|
+
setTextInput('');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// 유효한 API 키
|
|
81
|
+
setErrorMessage('');
|
|
82
|
+
settingsRef.current.API_KEY = apiKey;
|
|
83
|
+
setDetectedProvider(provider);
|
|
45
84
|
setStep(STEPS.MODEL);
|
|
46
85
|
setTextInput('');
|
|
47
86
|
setSelectedIndex(0);
|
|
48
87
|
break;
|
|
49
88
|
|
|
50
89
|
case STEPS.MODEL:
|
|
51
|
-
const models =
|
|
90
|
+
const models = getAvailableModels();
|
|
52
91
|
const selectedModel = models[selectedIndex];
|
|
53
92
|
settingsRef.current.MODEL = selectedModel;
|
|
54
93
|
|
|
@@ -97,6 +136,10 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
97
136
|
|
|
98
137
|
if (input && !key.ctrl && !key.meta) {
|
|
99
138
|
setTextInput(prev => prev + input);
|
|
139
|
+
// 입력 시 에러 메시지 클리어
|
|
140
|
+
if (errorMessage) {
|
|
141
|
+
setErrorMessage('');
|
|
142
|
+
}
|
|
100
143
|
}
|
|
101
144
|
return;
|
|
102
145
|
}
|
|
@@ -118,7 +161,7 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
118
161
|
const getMaxIndexForStep = (currentStep) => {
|
|
119
162
|
switch (currentStep) {
|
|
120
163
|
case STEPS.MODEL:
|
|
121
|
-
return
|
|
164
|
+
return getAvailableModels().length - 1;
|
|
122
165
|
case STEPS.REASONING_EFFORT:
|
|
123
166
|
return 3; // 4 options
|
|
124
167
|
default:
|
|
@@ -145,16 +188,18 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
145
188
|
case STEPS.API_KEY:
|
|
146
189
|
return React.createElement(Box, { flexDirection: 'column' },
|
|
147
190
|
React.createElement(Text, { bold: true, color: theme.text.accent }, '1. API Key:'),
|
|
148
|
-
React.createElement(Text, { color: theme.text.secondary }, ' Get your API key from: https://platform.openai.com/account/api-keys or https://
|
|
191
|
+
React.createElement(Text, { color: theme.text.secondary }, ' Get your API key from: https://platform.openai.com/account/api-keys or https://z.ai/manage-apikey/apikey-list'),
|
|
149
192
|
React.createElement(Text, null),
|
|
150
193
|
React.createElement(Box, {
|
|
151
194
|
borderStyle: 'round',
|
|
152
|
-
borderColor: theme.border.focused,
|
|
195
|
+
borderColor: errorMessage ? theme.status.error : theme.border.focused,
|
|
153
196
|
paddingX: 1
|
|
154
197
|
},
|
|
155
198
|
React.createElement(Text, null, textInput ? '*'.repeat(textInput.length) : ' ')
|
|
156
199
|
),
|
|
157
|
-
|
|
200
|
+
errorMessage
|
|
201
|
+
? React.createElement(Text, { color: theme.status.error }, errorMessage)
|
|
202
|
+
: React.createElement(Text, null),
|
|
158
203
|
React.createElement(Text, { dimColor: true }, 'Type your API key and press Enter')
|
|
159
204
|
);
|
|
160
205
|
|
|
@@ -163,7 +208,7 @@ export function SetupWizard({ onComplete, onCancel }) {
|
|
|
163
208
|
React.createElement(Text, { bold: true, color: theme.text.accent }, '2. Choose Model:'),
|
|
164
209
|
React.createElement(Text, null),
|
|
165
210
|
renderOptions(
|
|
166
|
-
|
|
211
|
+
getAvailableModels().map(modelId => {
|
|
167
212
|
const model = AI_MODELS[modelId];
|
|
168
213
|
return `${modelId} (${model.name})`;
|
|
169
214
|
})
|
|
@@ -114,11 +114,11 @@ export function colorizeCode(code, language, showLineNumbers = true) {
|
|
|
114
114
|
const lines = codeToHighlight.split('\n');
|
|
115
115
|
const padWidth = String(lines.length).length;
|
|
116
116
|
|
|
117
|
-
return React.createElement(Box, { flexDirection: 'column' },
|
|
117
|
+
return React.createElement(Box, { flexDirection: 'column', width: '100%' },
|
|
118
118
|
lines.map((line, index) => {
|
|
119
119
|
const contentToRender = highlightLine(line, language);
|
|
120
120
|
|
|
121
|
-
return React.createElement(Box, { key: index },
|
|
121
|
+
return React.createElement(Box, { key: index, width: '100%' },
|
|
122
122
|
showLineNumbers && React.createElement(Text, { color: 'gray' },
|
|
123
123
|
`${String(index + 1).padStart(padWidth, ' ')} `
|
|
124
124
|
),
|
|
@@ -135,9 +135,9 @@ export function colorizeCode(code, language, showLineNumbers = true) {
|
|
|
135
135
|
const lines = codeToHighlight.split('\n');
|
|
136
136
|
const padWidth = String(lines.length).length;
|
|
137
137
|
|
|
138
|
-
return React.createElement(Box, { flexDirection: 'column' },
|
|
138
|
+
return React.createElement(Box, { flexDirection: 'column', width: '100%' },
|
|
139
139
|
lines.map((line, index) =>
|
|
140
|
-
React.createElement(Box, { key: index },
|
|
140
|
+
React.createElement(Box, { key: index, width: '100%' },
|
|
141
141
|
showLineNumbers && React.createElement(Text, { color: 'gray' },
|
|
142
142
|
`${String(index + 1).padStart(padWidth, ' ')} `
|
|
143
143
|
),
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool UI Formatter
|
|
3
|
+
* 도구 스키마에서 UI 포맷팅 로직을 분리하여 tools → frontend 의존성을 제거합니다.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { theme } from '../design/themeColors.js';
|
|
7
|
+
import { toDisplayPath } from '../../util/path_helper.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 도구별 UI 포맷터 정의
|
|
11
|
+
* 각 도구의 ui_display 설정을 중앙에서 관리합니다.
|
|
12
|
+
*/
|
|
13
|
+
export const toolUIFormatters = {
|
|
14
|
+
// ============================================
|
|
15
|
+
// File Reader
|
|
16
|
+
// ============================================
|
|
17
|
+
read_file: {
|
|
18
|
+
show_tool_call: true,
|
|
19
|
+
show_tool_result: true,
|
|
20
|
+
display_name: 'Read',
|
|
21
|
+
format_tool_call: (args) => {
|
|
22
|
+
return `(${toDisplayPath(args.filePath)})`;
|
|
23
|
+
},
|
|
24
|
+
format_tool_result: (result) => {
|
|
25
|
+
if (result.operation_successful) {
|
|
26
|
+
const lines = result.total_line_count || 0;
|
|
27
|
+
return {
|
|
28
|
+
type: 'formatted',
|
|
29
|
+
parts: [
|
|
30
|
+
{ text: 'Read ', style: {} },
|
|
31
|
+
{ text: String(lines), style: { color: theme.brand.light, bold: true } },
|
|
32
|
+
{ text: ` line${lines !== 1 ? 's' : ''}`, style: {} }
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return result.error_message || 'Error reading file';
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
read_file_range: {
|
|
41
|
+
show_tool_call: true,
|
|
42
|
+
show_tool_result: true,
|
|
43
|
+
display_name: 'Read',
|
|
44
|
+
format_tool_call: (args) => {
|
|
45
|
+
return `(${toDisplayPath(args.filePath)}, lines ${args.startLine}-${args.endLine})`;
|
|
46
|
+
},
|
|
47
|
+
format_tool_result: (result) => {
|
|
48
|
+
if (result.operation_successful) {
|
|
49
|
+
const lineCount = result.file_content ? result.file_content.split('\n').length : 0;
|
|
50
|
+
return {
|
|
51
|
+
type: 'formatted',
|
|
52
|
+
parts: [
|
|
53
|
+
{ text: 'Read ', style: {} },
|
|
54
|
+
{ text: String(lineCount), style: { color: theme.brand.light, bold: true } },
|
|
55
|
+
{ text: ` line${lineCount !== 1 ? 's' : ''}`, style: {} }
|
|
56
|
+
]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return result.error_message || 'Error reading file';
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// ============================================
|
|
64
|
+
// Code Editor
|
|
65
|
+
// ============================================
|
|
66
|
+
write_file: {
|
|
67
|
+
show_tool_call: true,
|
|
68
|
+
show_tool_result: true,
|
|
69
|
+
display_name: 'Write',
|
|
70
|
+
format_tool_call: (args) => {
|
|
71
|
+
return `(${toDisplayPath(args.file_path)})`;
|
|
72
|
+
},
|
|
73
|
+
format_tool_result: (result) => {
|
|
74
|
+
if (result.operation_successful) {
|
|
75
|
+
const lines = result.total_line_count || 0;
|
|
76
|
+
const action = result.file_existed ? 'Overwrote' : 'Created';
|
|
77
|
+
return {
|
|
78
|
+
type: 'formatted',
|
|
79
|
+
parts: [
|
|
80
|
+
{ text: `${action} `, style: {} },
|
|
81
|
+
{ text: String(lines), style: { color: theme.brand.light, bold: true } },
|
|
82
|
+
{ text: ` line${lines !== 1 ? 's' : ''}`, style: {} }
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return result.error_message || 'Error writing file';
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
edit_file_replace: {
|
|
91
|
+
show_tool_call: true,
|
|
92
|
+
show_tool_result: true,
|
|
93
|
+
display_name: 'Replace',
|
|
94
|
+
format_tool_call: (args) => {
|
|
95
|
+
return `(${toDisplayPath(args.file_path)})`;
|
|
96
|
+
},
|
|
97
|
+
format_tool_result: (result) => {
|
|
98
|
+
if (result.operation_successful) {
|
|
99
|
+
const count = result.replacement_count || 0;
|
|
100
|
+
return {
|
|
101
|
+
type: 'formatted',
|
|
102
|
+
parts: [
|
|
103
|
+
{ text: 'Replaced ', style: {} },
|
|
104
|
+
{ text: String(count), style: { color: theme.brand.light, bold: true } },
|
|
105
|
+
{ text: ` occurrence${count !== 1 ? 's' : ''}`, style: {} }
|
|
106
|
+
]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return result.error_message || 'Error replacing string';
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
edit_file_range: {
|
|
114
|
+
show_tool_call: true,
|
|
115
|
+
show_tool_result: true,
|
|
116
|
+
display_name: 'Edit',
|
|
117
|
+
format_tool_call: (args) => {
|
|
118
|
+
return `(${toDisplayPath(args.file_path)}, lines ${args.start_line}-${args.end_line})`;
|
|
119
|
+
},
|
|
120
|
+
format_tool_result: (result) => {
|
|
121
|
+
if (result.operation_successful) {
|
|
122
|
+
const op = result.operation_type;
|
|
123
|
+
if (op === 'delete') {
|
|
124
|
+
return `Deleted lines`;
|
|
125
|
+
} else if (op === 'insert') {
|
|
126
|
+
return `Inserted lines`;
|
|
127
|
+
} else {
|
|
128
|
+
return `Replaced lines`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return result.error_message || 'Error editing file';
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// ============================================
|
|
136
|
+
// Search Tools
|
|
137
|
+
// ============================================
|
|
138
|
+
glob_search: {
|
|
139
|
+
show_tool_call: true,
|
|
140
|
+
show_tool_result: true,
|
|
141
|
+
display_name: 'Search',
|
|
142
|
+
format_tool_call: (args) => {
|
|
143
|
+
const pattern = args.pattern || '';
|
|
144
|
+
return `(${pattern})`;
|
|
145
|
+
},
|
|
146
|
+
format_tool_result: (result) => {
|
|
147
|
+
if (result.operation_successful) {
|
|
148
|
+
const matches = result.total_matches || 0;
|
|
149
|
+
return {
|
|
150
|
+
type: 'formatted',
|
|
151
|
+
parts: [
|
|
152
|
+
{ text: 'Found ', style: {} },
|
|
153
|
+
{ text: String(matches), style: { color: theme.brand.light, bold: true } },
|
|
154
|
+
{ text: ` match${matches !== 1 ? 'es' : ''}`, style: {} }
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return result.error_message || 'Search failed';
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
ripgrep: {
|
|
163
|
+
show_tool_call: true,
|
|
164
|
+
show_tool_result: true,
|
|
165
|
+
display_name: 'Grep',
|
|
166
|
+
format_tool_call: (args) => {
|
|
167
|
+
const pattern = args.pattern || '';
|
|
168
|
+
const shortened = pattern.length > 30 ? pattern.substring(0, 27) + '...' : pattern;
|
|
169
|
+
return `(${shortened})`;
|
|
170
|
+
},
|
|
171
|
+
format_tool_result: (result) => {
|
|
172
|
+
if (result.operation_successful) {
|
|
173
|
+
const matches = result.totalMatches || 0;
|
|
174
|
+
return {
|
|
175
|
+
type: 'formatted',
|
|
176
|
+
parts: [
|
|
177
|
+
{ text: 'Found ', style: {} },
|
|
178
|
+
{ text: String(matches), style: { color: theme.brand.light, bold: true } },
|
|
179
|
+
{ text: ` match${matches !== 1 ? 'es' : ''}`, style: {} }
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return result.error_message || 'Search failed';
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// ============================================
|
|
188
|
+
// Web Downloader
|
|
189
|
+
// ============================================
|
|
190
|
+
fetch_web_page: {
|
|
191
|
+
show_tool_call: true,
|
|
192
|
+
show_tool_result: true,
|
|
193
|
+
display_name: 'Fetch',
|
|
194
|
+
format_tool_call: (args) => {
|
|
195
|
+
const url = args.url || '';
|
|
196
|
+
const shortened = url.length > 50 ? url.substring(0, 47) + '...' : url;
|
|
197
|
+
return `(${shortened})`;
|
|
198
|
+
},
|
|
199
|
+
format_tool_result: (result) => {
|
|
200
|
+
if (result.operation_successful) {
|
|
201
|
+
const contentLength = result.content?.length || 0;
|
|
202
|
+
return {
|
|
203
|
+
type: 'formatted',
|
|
204
|
+
parts: [
|
|
205
|
+
{ text: 'Fetched ', style: {} },
|
|
206
|
+
{ text: String(contentLength), style: { color: theme.brand.light, bold: true } },
|
|
207
|
+
{ text: ' characters', style: {} }
|
|
208
|
+
]
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return result.error_message || 'Fetch failed';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 도구 이름으로 UI 포맷터를 가져옵니다.
|
|
218
|
+
* @param {string} toolName - 도구 이름
|
|
219
|
+
* @returns {Object|null} UI 포맷터 또는 null
|
|
220
|
+
*/
|
|
221
|
+
export function getToolUIFormatter(toolName) {
|
|
222
|
+
return toolUIFormatters[toolName] || null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 도구 호출을 포맷팅합니다.
|
|
227
|
+
* @param {string} toolName - 도구 이름
|
|
228
|
+
* @param {Object} args - 도구 인자
|
|
229
|
+
* @returns {string} 포맷팅된 문자열
|
|
230
|
+
*/
|
|
231
|
+
export function formatToolCall(toolName, args) {
|
|
232
|
+
const formatter = getToolUIFormatter(toolName);
|
|
233
|
+
if (formatter && formatter.format_tool_call) {
|
|
234
|
+
return formatter.format_tool_call(args);
|
|
235
|
+
}
|
|
236
|
+
return `(${JSON.stringify(args)})`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 도구 결과를 포맷팅합니다.
|
|
241
|
+
* @param {string} toolName - 도구 이름
|
|
242
|
+
* @param {Object} result - 도구 실행 결과
|
|
243
|
+
* @returns {string|Object} 포맷팅된 결과
|
|
244
|
+
*/
|
|
245
|
+
export function formatToolResult(toolName, result) {
|
|
246
|
+
const formatter = getToolUIFormatter(toolName);
|
|
247
|
+
if (formatter && formatter.format_tool_result) {
|
|
248
|
+
return formatter.format_tool_result(result);
|
|
249
|
+
}
|
|
250
|
+
return result.operation_successful ? 'Success' : (result.error_message || 'Error');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 도구의 표시 이름을 가져옵니다.
|
|
255
|
+
* @param {string} toolName - 도구 이름
|
|
256
|
+
* @returns {string} 표시 이름
|
|
257
|
+
*/
|
|
258
|
+
export function getToolDisplayName(toolName) {
|
|
259
|
+
const formatter = getToolUIFormatter(toolName);
|
|
260
|
+
return formatter?.display_name || toolName;
|
|
261
|
+
}
|