snow-ai 0.3.36 → 0.4.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.
- package/dist/agents/codebaseIndexAgent.js +1 -0
- package/dist/agents/codebaseReviewAgent.d.ts +61 -0
- package/dist/agents/codebaseReviewAgent.js +301 -0
- package/dist/agents/promptOptimizeAgent.d.ts +54 -0
- package/dist/agents/promptOptimizeAgent.js +268 -0
- package/dist/api/anthropic.js +1 -0
- package/dist/api/chat.js +1 -0
- package/dist/api/embedding.js +1 -0
- package/dist/api/gemini.js +2 -1
- package/dist/api/responses.js +1 -0
- package/dist/api/systemPrompt.d.ts +1 -5
- package/dist/api/systemPrompt.js +168 -100
- package/dist/app.js +14 -6
- package/dist/cli.js +1 -1
- package/dist/hooks/useCommandPanel.js +48 -46
- package/dist/hooks/useConversation.d.ts +2 -1
- package/dist/hooks/useConversation.js +116 -30
- package/dist/hooks/useGlobalExit.js +4 -2
- package/dist/hooks/useStreamingState.d.ts +9 -0
- package/dist/hooks/useStreamingState.js +3 -0
- package/dist/i18n/I18nContext.d.ts +14 -0
- package/dist/i18n/I18nContext.js +24 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/lang/en.d.ts +2 -0
- package/dist/i18n/lang/en.js +483 -0
- package/dist/i18n/lang/es.d.ts +2 -0
- package/dist/i18n/lang/es.js +483 -0
- package/dist/i18n/lang/ja.d.ts +2 -0
- package/dist/i18n/lang/ja.js +483 -0
- package/dist/i18n/lang/ko.d.ts +2 -0
- package/dist/i18n/lang/ko.js +483 -0
- package/dist/i18n/lang/zh-TW.d.ts +2 -0
- package/dist/i18n/lang/zh-TW.js +483 -0
- package/dist/i18n/lang/zh.d.ts +2 -0
- package/dist/i18n/lang/zh.js +483 -0
- package/dist/i18n/translations.d.ts +2 -0
- package/dist/i18n/translations.js +14 -0
- package/dist/i18n/types.d.ts +459 -0
- package/dist/i18n/types.js +1 -0
- package/dist/mcp/aceCodeSearch.d.ts +17 -48
- package/dist/mcp/aceCodeSearch.js +24 -56
- package/dist/mcp/bash.js +8 -1
- package/dist/mcp/codebaseSearch.d.ts +1 -1
- package/dist/mcp/codebaseSearch.js +159 -30
- package/dist/mcp/filesystem.d.ts +3 -80
- package/dist/mcp/filesystem.js +23 -103
- package/dist/mcp/subagent.d.ts +2 -1
- package/dist/mcp/subagent.js +54 -5
- package/dist/ui/components/ChatInput.js +22 -25
- package/dist/ui/components/CommandPanel.d.ts +1 -1
- package/dist/ui/components/CommandPanel.js +20 -13
- package/dist/ui/components/DiffViewer.d.ts +1 -1
- package/dist/ui/components/DiffViewer.js +101 -91
- package/dist/ui/components/FileList.js +22 -11
- package/dist/ui/components/HelpPanel.js +47 -21
- package/dist/ui/components/Menu.js +6 -2
- package/dist/ui/components/MessageList.d.ts +6 -0
- package/dist/ui/components/MessageList.js +1 -1
- package/dist/ui/components/ToolConfirmation.d.ts +4 -1
- package/dist/ui/components/ToolConfirmation.js +28 -2
- package/dist/ui/components/ToolResultPreview.d.ts +2 -1
- package/dist/ui/components/ToolResultPreview.js +41 -25
- package/dist/ui/pages/ChatScreen.js +177 -56
- package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
- package/dist/ui/pages/ConfigScreen.js +138 -98
- package/dist/ui/pages/CustomHeadersScreen.js +75 -69
- package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
- package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
- package/dist/ui/pages/ProxyConfigScreen.js +27 -23
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
- package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
- package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
- package/dist/ui/pages/WelcomeScreen.js +40 -26
- package/dist/utils/apiConfig.d.ts +2 -0
- package/dist/utils/codebaseConfig.d.ts +1 -5
- package/dist/utils/codebaseConfig.js +2 -10
- package/dist/utils/codebaseSearchEvents.d.ts +16 -0
- package/dist/utils/codebaseSearchEvents.js +13 -0
- package/dist/utils/commands/agent.js +2 -2
- package/dist/utils/commands/init.js +1 -1
- package/dist/utils/configManager.js +26 -5
- package/dist/utils/contextCompressor.js +1 -1
- package/dist/utils/languageConfig.d.ts +21 -0
- package/dist/utils/languageConfig.js +61 -0
- package/dist/utils/mcpToolsManager.js +0 -9
- package/dist/utils/notebookManager.js +11 -4
- package/dist/utils/sessionConverter.js +13 -3
- package/dist/utils/sessionManager.d.ts +1 -0
- package/dist/utils/subAgentConfig.d.ts +10 -5
- package/dist/utils/subAgentConfig.js +112 -19
- package/dist/utils/subAgentExecutor.d.ts +9 -1
- package/dist/utils/subAgentExecutor.js +122 -9
- package/dist/utils/toolExecutor.d.ts +2 -1
- package/dist/utils/toolExecutor.js +1 -2
- package/dist/utils/usageLogger.js +18 -3
- package/package.json +2 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codebase Review Agent Service
|
|
3
|
+
*
|
|
4
|
+
* Reviews codebase search results to filter out irrelevant items.
|
|
5
|
+
* Uses basicModel for efficient, low-cost relevance checking.
|
|
6
|
+
* Can also suggest better search keywords if results are not relevant.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CodebaseReviewAgent {
|
|
9
|
+
private modelName;
|
|
10
|
+
private requestMethod;
|
|
11
|
+
private initialized;
|
|
12
|
+
private readonly MAX_RETRIES;
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the review agent with current configuration
|
|
15
|
+
*/
|
|
16
|
+
private initialize;
|
|
17
|
+
/**
|
|
18
|
+
* Check if review agent is available
|
|
19
|
+
*/
|
|
20
|
+
isAvailable(): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Call the model with streaming API and assemble complete response
|
|
23
|
+
*/
|
|
24
|
+
private callModel;
|
|
25
|
+
/**
|
|
26
|
+
* Try to parse JSON response with retry logic
|
|
27
|
+
*/
|
|
28
|
+
private tryParseJSON;
|
|
29
|
+
/**
|
|
30
|
+
* Review search results with retry mechanism
|
|
31
|
+
*/
|
|
32
|
+
private reviewWithRetry;
|
|
33
|
+
/**
|
|
34
|
+
* Sleep utility for retry backoff
|
|
35
|
+
*/
|
|
36
|
+
private sleep;
|
|
37
|
+
/**
|
|
38
|
+
* Review search results and filter out irrelevant ones
|
|
39
|
+
* With retry mechanism and graceful degradation
|
|
40
|
+
*
|
|
41
|
+
* @param query - Original search query
|
|
42
|
+
* @param results - Search results to review
|
|
43
|
+
* @param abortSignal - Optional abort signal
|
|
44
|
+
* @returns Object with filtered results and optional suggestions
|
|
45
|
+
*/
|
|
46
|
+
reviewResults(query: string, results: Array<{
|
|
47
|
+
rank: number;
|
|
48
|
+
filePath: string;
|
|
49
|
+
startLine: number;
|
|
50
|
+
endLine: number;
|
|
51
|
+
content: string;
|
|
52
|
+
similarityScore: string;
|
|
53
|
+
location: string;
|
|
54
|
+
}>, abortSignal?: AbortSignal): Promise<{
|
|
55
|
+
filteredResults: typeof results;
|
|
56
|
+
removedCount: number;
|
|
57
|
+
suggestions?: string[];
|
|
58
|
+
reviewFailed?: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export declare const codebaseReviewAgent: CodebaseReviewAgent;
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { getOpenAiConfig } from '../utils/apiConfig.js';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
import { createStreamingChatCompletion } from '../api/chat.js';
|
|
4
|
+
import { createStreamingResponse } from '../api/responses.js';
|
|
5
|
+
import { createStreamingGeminiCompletion } from '../api/gemini.js';
|
|
6
|
+
import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
|
|
7
|
+
/**
|
|
8
|
+
* Codebase Review Agent Service
|
|
9
|
+
*
|
|
10
|
+
* Reviews codebase search results to filter out irrelevant items.
|
|
11
|
+
* Uses basicModel for efficient, low-cost relevance checking.
|
|
12
|
+
* Can also suggest better search keywords if results are not relevant.
|
|
13
|
+
*/
|
|
14
|
+
export class CodebaseReviewAgent {
|
|
15
|
+
constructor() {
|
|
16
|
+
Object.defineProperty(this, "modelName", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: ''
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "requestMethod", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: 'chat'
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "initialized", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: false
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "MAX_RETRIES", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: 3
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initialize the review agent with current configuration
|
|
43
|
+
*/
|
|
44
|
+
async initialize() {
|
|
45
|
+
try {
|
|
46
|
+
const config = getOpenAiConfig();
|
|
47
|
+
if (!config.basicModel) {
|
|
48
|
+
logger.warn('Codebase review agent: Basic model not configured, using advanced model as fallback');
|
|
49
|
+
if (!config.advancedModel) {
|
|
50
|
+
logger.warn('Codebase review agent: No model configured');
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
this.modelName = config.advancedModel;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.modelName = config.basicModel;
|
|
57
|
+
}
|
|
58
|
+
this.requestMethod = config.requestMethod;
|
|
59
|
+
this.initialized = true;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.warn('Codebase review agent: Failed to initialize:', error);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if review agent is available
|
|
69
|
+
*/
|
|
70
|
+
async isAvailable() {
|
|
71
|
+
if (!this.initialized) {
|
|
72
|
+
return await this.initialize();
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Call the model with streaming API and assemble complete response
|
|
78
|
+
*/
|
|
79
|
+
async callModel(messages, abortSignal) {
|
|
80
|
+
let streamGenerator;
|
|
81
|
+
switch (this.requestMethod) {
|
|
82
|
+
case 'anthropic':
|
|
83
|
+
streamGenerator = createStreamingAnthropicCompletion({
|
|
84
|
+
model: this.modelName,
|
|
85
|
+
messages,
|
|
86
|
+
includeBuiltinSystemPrompt: false,
|
|
87
|
+
disableThinking: true,
|
|
88
|
+
}, abortSignal);
|
|
89
|
+
break;
|
|
90
|
+
case 'gemini':
|
|
91
|
+
streamGenerator = createStreamingGeminiCompletion({
|
|
92
|
+
model: this.modelName,
|
|
93
|
+
messages,
|
|
94
|
+
includeBuiltinSystemPrompt: false,
|
|
95
|
+
}, abortSignal);
|
|
96
|
+
break;
|
|
97
|
+
case 'responses':
|
|
98
|
+
streamGenerator = createStreamingResponse({
|
|
99
|
+
model: this.modelName,
|
|
100
|
+
messages,
|
|
101
|
+
stream: true,
|
|
102
|
+
includeBuiltinSystemPrompt: false,
|
|
103
|
+
}, abortSignal);
|
|
104
|
+
break;
|
|
105
|
+
case 'chat':
|
|
106
|
+
default:
|
|
107
|
+
streamGenerator = createStreamingChatCompletion({
|
|
108
|
+
model: this.modelName,
|
|
109
|
+
messages,
|
|
110
|
+
stream: true,
|
|
111
|
+
includeBuiltinSystemPrompt: false,
|
|
112
|
+
}, abortSignal);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
let completeContent = '';
|
|
116
|
+
try {
|
|
117
|
+
for await (const chunk of streamGenerator) {
|
|
118
|
+
if (abortSignal?.aborted) {
|
|
119
|
+
throw new Error('Request aborted');
|
|
120
|
+
}
|
|
121
|
+
if (this.requestMethod === 'chat') {
|
|
122
|
+
if (chunk.choices && chunk.choices[0]?.delta?.content) {
|
|
123
|
+
completeContent += chunk.choices[0].delta.content;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
if (chunk.type === 'content' && chunk.content) {
|
|
128
|
+
completeContent += chunk.content;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (streamError) {
|
|
134
|
+
logger.error('Codebase review agent: Streaming error:', streamError);
|
|
135
|
+
throw streamError;
|
|
136
|
+
}
|
|
137
|
+
return completeContent;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Try to parse JSON response with retry logic
|
|
141
|
+
*/
|
|
142
|
+
tryParseJSON(response) {
|
|
143
|
+
try {
|
|
144
|
+
// Extract JSON from markdown code blocks if present
|
|
145
|
+
let jsonStr = response.trim();
|
|
146
|
+
const jsonMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
147
|
+
if (jsonMatch) {
|
|
148
|
+
jsonStr = jsonMatch[1].trim();
|
|
149
|
+
}
|
|
150
|
+
const parsed = JSON.parse(jsonStr);
|
|
151
|
+
// Validate structure
|
|
152
|
+
if (!Array.isArray(parsed.relevantIndices)) {
|
|
153
|
+
logger.warn('Codebase review agent: Invalid JSON structure - missing relevantIndices array');
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
logger.warn('Codebase review agent: JSON parse error:', error);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Review search results with retry mechanism
|
|
165
|
+
*/
|
|
166
|
+
async reviewWithRetry(query, results, abortSignal) {
|
|
167
|
+
const reviewPrompt = `You are a code search result reviewer. Your task is to analyze search results and determine which ones are truly relevant to the user's query.
|
|
168
|
+
|
|
169
|
+
Search Query: "${query}"
|
|
170
|
+
|
|
171
|
+
Search Results (${results.length} items):
|
|
172
|
+
${results
|
|
173
|
+
.map((r, idx) => `\n[Result ${idx + 1}]
|
|
174
|
+
File: ${r.filePath}
|
|
175
|
+
Lines: ${r.startLine}-${r.endLine}
|
|
176
|
+
Similarity Score: ${r.similarityScore}%
|
|
177
|
+
Code:
|
|
178
|
+
\`\`\`
|
|
179
|
+
${r.content}
|
|
180
|
+
\`\`\``)
|
|
181
|
+
.join('\n---')}
|
|
182
|
+
|
|
183
|
+
Your Tasks:
|
|
184
|
+
1. Identify which results are RELEVANT to the search query
|
|
185
|
+
2. Mark irrelevant results for removal
|
|
186
|
+
3. If most results are irrelevant, suggest better search keywords
|
|
187
|
+
|
|
188
|
+
Output in JSON format:
|
|
189
|
+
{
|
|
190
|
+
"relevantIndices": [1, 3, 5],
|
|
191
|
+
"removedIndices": [2, 4],
|
|
192
|
+
"suggestions": ["keyword1", "keyword2"]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Guidelines:
|
|
196
|
+
- Be strict but fair: code doesn't need to match exactly, but should be semantically related
|
|
197
|
+
- Consider file paths, code content, and context
|
|
198
|
+
- If a result is marginally relevant, keep it
|
|
199
|
+
- Only suggest new keywords if >50% of results are irrelevant
|
|
200
|
+
- Return ONLY valid JSON, no other text or explanation`;
|
|
201
|
+
const messages = [
|
|
202
|
+
{
|
|
203
|
+
role: 'user',
|
|
204
|
+
content: reviewPrompt,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
// Retry loop
|
|
208
|
+
for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
|
|
209
|
+
try {
|
|
210
|
+
logger.info(`Codebase review agent: Attempt ${attempt}/${this.MAX_RETRIES}`);
|
|
211
|
+
const response = await this.callModel(messages, abortSignal);
|
|
212
|
+
if (!response || response.trim().length === 0) {
|
|
213
|
+
logger.warn(`Codebase review agent: Empty response on attempt ${attempt}`);
|
|
214
|
+
if (attempt < this.MAX_RETRIES) {
|
|
215
|
+
await this.sleep(500 * attempt); // Exponential backoff
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
// Try to parse JSON
|
|
221
|
+
const parsed = this.tryParseJSON(response);
|
|
222
|
+
if (parsed) {
|
|
223
|
+
logger.info(`Codebase review agent: Successfully parsed on attempt ${attempt}`);
|
|
224
|
+
return { parsed, attempt };
|
|
225
|
+
}
|
|
226
|
+
// If parse failed and we have retries left
|
|
227
|
+
if (attempt < this.MAX_RETRIES) {
|
|
228
|
+
logger.warn(`Codebase review agent: Parse failed on attempt ${attempt}, retrying...`);
|
|
229
|
+
await this.sleep(500 * attempt); // Exponential backoff
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
logger.error(`Codebase review agent: Error on attempt ${attempt}:`, error);
|
|
236
|
+
if (attempt < this.MAX_RETRIES) {
|
|
237
|
+
await this.sleep(500 * attempt); // Exponential backoff
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Sleep utility for retry backoff
|
|
247
|
+
*/
|
|
248
|
+
sleep(ms) {
|
|
249
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Review search results and filter out irrelevant ones
|
|
253
|
+
* With retry mechanism and graceful degradation
|
|
254
|
+
*
|
|
255
|
+
* @param query - Original search query
|
|
256
|
+
* @param results - Search results to review
|
|
257
|
+
* @param abortSignal - Optional abort signal
|
|
258
|
+
* @returns Object with filtered results and optional suggestions
|
|
259
|
+
*/
|
|
260
|
+
async reviewResults(query, results, abortSignal) {
|
|
261
|
+
const available = await this.isAvailable();
|
|
262
|
+
if (!available) {
|
|
263
|
+
logger.warn('Codebase review agent: Not available, returning original results');
|
|
264
|
+
return {
|
|
265
|
+
filteredResults: results,
|
|
266
|
+
removedCount: 0,
|
|
267
|
+
reviewFailed: true,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
// Attempt review with retry
|
|
271
|
+
const reviewResult = await this.reviewWithRetry(query, results, abortSignal);
|
|
272
|
+
// If all retries failed, gracefully degrade
|
|
273
|
+
if (!reviewResult) {
|
|
274
|
+
logger.warn('Codebase review agent: All retry attempts failed, returning original results');
|
|
275
|
+
return {
|
|
276
|
+
filteredResults: results,
|
|
277
|
+
removedCount: 0,
|
|
278
|
+
reviewFailed: true,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// Success - filter results
|
|
282
|
+
const { parsed, attempt } = reviewResult;
|
|
283
|
+
const filteredResults = results.filter((_, idx) => parsed.relevantIndices.includes(idx + 1));
|
|
284
|
+
const removedCount = results.length - filteredResults.length;
|
|
285
|
+
logger.info('Codebase review agent: Review completed', {
|
|
286
|
+
originalCount: results.length,
|
|
287
|
+
filteredCount: filteredResults.length,
|
|
288
|
+
removedCount,
|
|
289
|
+
attempts: attempt,
|
|
290
|
+
hasSuggestions: !!parsed.suggestions?.length,
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
filteredResults,
|
|
294
|
+
removedCount,
|
|
295
|
+
suggestions: parsed.suggestions || undefined,
|
|
296
|
+
reviewFailed: false,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Export singleton instance
|
|
301
|
+
export const codebaseReviewAgent = new CodebaseReviewAgent();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ChatMessage } from '../api/chat.js';
|
|
2
|
+
/**
|
|
3
|
+
* Prompt Optimization Agent Service
|
|
4
|
+
*
|
|
5
|
+
* Optimizes user prompts for better AI understanding and response quality.
|
|
6
|
+
* This service operates using the basic model for efficient, low-cost optimization.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Uses basicModel for efficient prompt optimization
|
|
10
|
+
* - Follows the same API routing as main flow (chat, responses, gemini, anthropic)
|
|
11
|
+
* - Filters context to only include user->assistant pairs without tool calls
|
|
12
|
+
* - Returns optimized prompt that preserves user intent while improving clarity
|
|
13
|
+
* - Silent execution with error handling to prevent main flow disruption
|
|
14
|
+
*/
|
|
15
|
+
export declare class PromptOptimizeAgent {
|
|
16
|
+
private modelName;
|
|
17
|
+
private requestMethod;
|
|
18
|
+
private initialized;
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the prompt optimization agent with current configuration
|
|
21
|
+
* @returns true if initialized successfully, false otherwise
|
|
22
|
+
*/
|
|
23
|
+
private initialize;
|
|
24
|
+
/**
|
|
25
|
+
* Check if prompt optimization agent is available
|
|
26
|
+
*/
|
|
27
|
+
isAvailable(): Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Call the model with streaming API and assemble complete response
|
|
30
|
+
* Uses the same routing logic as main flow for consistency
|
|
31
|
+
*
|
|
32
|
+
* @param messages - Chat messages
|
|
33
|
+
* @param abortSignal - Optional abort signal to cancel the request
|
|
34
|
+
*/
|
|
35
|
+
private callModel;
|
|
36
|
+
/**
|
|
37
|
+
* Filter conversation history to only include user->assistant pairs without tool calls
|
|
38
|
+
* This creates a lightweight context for prompt optimization
|
|
39
|
+
*
|
|
40
|
+
* @param messages - Full conversation history
|
|
41
|
+
* @returns Filtered messages containing only user->assistant exchanges
|
|
42
|
+
*/
|
|
43
|
+
private filterContextMessages;
|
|
44
|
+
/**
|
|
45
|
+
* Optimize user prompt for better AI understanding
|
|
46
|
+
*
|
|
47
|
+
* @param userPrompt - Original user prompt
|
|
48
|
+
* @param conversationHistory - Full conversation history for context
|
|
49
|
+
* @param abortSignal - Optional abort signal to cancel optimization
|
|
50
|
+
* @returns Optimized prompt, or original prompt if optimization fails
|
|
51
|
+
*/
|
|
52
|
+
optimizePrompt(userPrompt: string, conversationHistory: ChatMessage[], abortSignal?: AbortSignal): Promise<string>;
|
|
53
|
+
}
|
|
54
|
+
export declare const promptOptimizeAgent: PromptOptimizeAgent;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { getOpenAiConfig } from '../utils/apiConfig.js';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
import { createStreamingChatCompletion } from '../api/chat.js';
|
|
4
|
+
import { createStreamingResponse } from '../api/responses.js';
|
|
5
|
+
import { createStreamingGeminiCompletion } from '../api/gemini.js';
|
|
6
|
+
import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
|
|
7
|
+
/**
|
|
8
|
+
* Prompt Optimization Agent Service
|
|
9
|
+
*
|
|
10
|
+
* Optimizes user prompts for better AI understanding and response quality.
|
|
11
|
+
* This service operates using the basic model for efficient, low-cost optimization.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Uses basicModel for efficient prompt optimization
|
|
15
|
+
* - Follows the same API routing as main flow (chat, responses, gemini, anthropic)
|
|
16
|
+
* - Filters context to only include user->assistant pairs without tool calls
|
|
17
|
+
* - Returns optimized prompt that preserves user intent while improving clarity
|
|
18
|
+
* - Silent execution with error handling to prevent main flow disruption
|
|
19
|
+
*/
|
|
20
|
+
export class PromptOptimizeAgent {
|
|
21
|
+
constructor() {
|
|
22
|
+
Object.defineProperty(this, "modelName", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: ''
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "requestMethod", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: 'chat'
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "initialized", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: false
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initialize the prompt optimization agent with current configuration
|
|
43
|
+
* @returns true if initialized successfully, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
async initialize() {
|
|
46
|
+
try {
|
|
47
|
+
const config = getOpenAiConfig();
|
|
48
|
+
// Check if basic model is configured
|
|
49
|
+
if (!config.basicModel) {
|
|
50
|
+
logger.warn('Prompt optimize agent: Basic model not configured');
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
this.modelName = config.basicModel;
|
|
54
|
+
this.requestMethod = config.requestMethod;
|
|
55
|
+
this.initialized = true;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.warn('Prompt optimize agent: Failed to initialize:', error);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if prompt optimization agent is available
|
|
65
|
+
*/
|
|
66
|
+
async isAvailable() {
|
|
67
|
+
if (!this.initialized) {
|
|
68
|
+
return await this.initialize();
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Call the model with streaming API and assemble complete response
|
|
74
|
+
* Uses the same routing logic as main flow for consistency
|
|
75
|
+
*
|
|
76
|
+
* @param messages - Chat messages
|
|
77
|
+
* @param abortSignal - Optional abort signal to cancel the request
|
|
78
|
+
*/
|
|
79
|
+
async callModel(messages, abortSignal) {
|
|
80
|
+
let streamGenerator;
|
|
81
|
+
// Route to appropriate streaming API based on request method
|
|
82
|
+
switch (this.requestMethod) {
|
|
83
|
+
case 'anthropic':
|
|
84
|
+
streamGenerator = createStreamingAnthropicCompletion({
|
|
85
|
+
model: this.modelName,
|
|
86
|
+
messages,
|
|
87
|
+
max_tokens: 1000, // Limited tokens for prompt optimization
|
|
88
|
+
includeBuiltinSystemPrompt: false,
|
|
89
|
+
disableThinking: true, // Agents don't use Extended Thinking
|
|
90
|
+
}, abortSignal);
|
|
91
|
+
break;
|
|
92
|
+
case 'gemini':
|
|
93
|
+
streamGenerator = createStreamingGeminiCompletion({
|
|
94
|
+
model: this.modelName,
|
|
95
|
+
messages,
|
|
96
|
+
includeBuiltinSystemPrompt: false,
|
|
97
|
+
}, abortSignal);
|
|
98
|
+
break;
|
|
99
|
+
case 'responses':
|
|
100
|
+
streamGenerator = createStreamingResponse({
|
|
101
|
+
model: this.modelName,
|
|
102
|
+
messages,
|
|
103
|
+
stream: true,
|
|
104
|
+
includeBuiltinSystemPrompt: false,
|
|
105
|
+
}, abortSignal);
|
|
106
|
+
break;
|
|
107
|
+
case 'chat':
|
|
108
|
+
default:
|
|
109
|
+
streamGenerator = createStreamingChatCompletion({
|
|
110
|
+
model: this.modelName,
|
|
111
|
+
messages,
|
|
112
|
+
stream: true,
|
|
113
|
+
includeBuiltinSystemPrompt: false,
|
|
114
|
+
}, abortSignal);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
// Assemble complete content from streaming response
|
|
118
|
+
let completeContent = '';
|
|
119
|
+
try {
|
|
120
|
+
for await (const chunk of streamGenerator) {
|
|
121
|
+
// Check abort signal
|
|
122
|
+
if (abortSignal?.aborted) {
|
|
123
|
+
throw new Error('Request aborted');
|
|
124
|
+
}
|
|
125
|
+
// Handle different chunk formats based on request method
|
|
126
|
+
if (this.requestMethod === 'chat') {
|
|
127
|
+
// Chat API uses standard OpenAI format
|
|
128
|
+
if (chunk.choices && chunk.choices[0]?.delta?.content) {
|
|
129
|
+
completeContent += chunk.choices[0].delta.content;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Responses, Gemini, and Anthropic APIs use unified format
|
|
134
|
+
if (chunk.type === 'content' && chunk.content) {
|
|
135
|
+
completeContent += chunk.content;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (streamError) {
|
|
141
|
+
logger.error('Prompt optimize agent: Streaming error:', streamError);
|
|
142
|
+
throw streamError;
|
|
143
|
+
}
|
|
144
|
+
return completeContent;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Filter conversation history to only include user->assistant pairs without tool calls
|
|
148
|
+
* This creates a lightweight context for prompt optimization
|
|
149
|
+
*
|
|
150
|
+
* @param messages - Full conversation history
|
|
151
|
+
* @returns Filtered messages containing only user->assistant exchanges
|
|
152
|
+
*/
|
|
153
|
+
filterContextMessages(messages) {
|
|
154
|
+
const filtered = [];
|
|
155
|
+
for (const msg of messages) {
|
|
156
|
+
// Only include user and assistant messages
|
|
157
|
+
if (msg.role === 'user' || msg.role === 'assistant') {
|
|
158
|
+
// For assistant messages, skip if they contain tool calls
|
|
159
|
+
if (msg.role === 'assistant') {
|
|
160
|
+
// Check if message has tool_calls (OpenAI format) or tool_use content (Anthropic format)
|
|
161
|
+
const hasToolCalls = !!msg.tool_calls;
|
|
162
|
+
const hasToolUseContent = Array.isArray(msg.content) &&
|
|
163
|
+
msg.content.some((c) => c.type === 'tool_use' || c.type === 'tool_call');
|
|
164
|
+
if (hasToolCalls || hasToolUseContent) {
|
|
165
|
+
continue; // Skip assistant messages with tool calls
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Add message to filtered list
|
|
169
|
+
filtered.push(msg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return filtered;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Optimize user prompt for better AI understanding
|
|
176
|
+
*
|
|
177
|
+
* @param userPrompt - Original user prompt
|
|
178
|
+
* @param conversationHistory - Full conversation history for context
|
|
179
|
+
* @param abortSignal - Optional abort signal to cancel optimization
|
|
180
|
+
* @returns Optimized prompt, or original prompt if optimization fails
|
|
181
|
+
*/
|
|
182
|
+
async optimizePrompt(userPrompt, conversationHistory, abortSignal) {
|
|
183
|
+
const available = await this.isAvailable();
|
|
184
|
+
if (!available) {
|
|
185
|
+
return userPrompt;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
// Check word count - if prompt > 100 words, skip optimization
|
|
189
|
+
// User likely provided detailed/important original text that should be preserved as-is
|
|
190
|
+
const wordCount = userPrompt.trim().split(/\s+/).length;
|
|
191
|
+
if (wordCount > 100) {
|
|
192
|
+
return userPrompt;
|
|
193
|
+
}
|
|
194
|
+
// Filter conversation history to lightweight context (only user<->assistant, no tool calls)
|
|
195
|
+
const contextMessages = this.filterContextMessages(conversationHistory);
|
|
196
|
+
// Build context summary if there's conversation history
|
|
197
|
+
let contextSummary = '';
|
|
198
|
+
if (contextMessages.length > 0) {
|
|
199
|
+
// Take last 8 messages to keep context focused, but use full content (no truncation)
|
|
200
|
+
const recentContext = contextMessages.slice(-8);
|
|
201
|
+
contextSummary =
|
|
202
|
+
'\n\nRecent conversation context:\n' +
|
|
203
|
+
recentContext
|
|
204
|
+
.map((msg) => {
|
|
205
|
+
const content = typeof msg.content === 'string'
|
|
206
|
+
? msg.content
|
|
207
|
+
: JSON.stringify(msg.content);
|
|
208
|
+
// Use full message content (no truncation)
|
|
209
|
+
return `${msg.role}: ${content}`;
|
|
210
|
+
})
|
|
211
|
+
.join('\n');
|
|
212
|
+
}
|
|
213
|
+
const optimizationPrompt = `You are a prompt optimization assistant. Your task is to improve user prompts for better AI understanding while maintaining HIGH FIDELITY to the original content.
|
|
214
|
+
|
|
215
|
+
User's original prompt:
|
|
216
|
+
${userPrompt}${contextSummary}
|
|
217
|
+
|
|
218
|
+
Your optimization goals (in priority order):
|
|
219
|
+
1. **HIGH FIDELITY REQUIREMENT**: Preserve ALL important information, details, and requirements from the user's original prompt - DO NOT lose or omit any critical content
|
|
220
|
+
2. Preserve the EXACT SAME LANGUAGE as the user (if Chinese, stay Chinese; if English, stay English)
|
|
221
|
+
3. Keep the core intent and meaning unchanged
|
|
222
|
+
4. Make the prompt clearer and more specific ONLY if vague - if already clear, keep it as-is
|
|
223
|
+
5. Add relevant context if the user is asking follow-up questions
|
|
224
|
+
6. Break down complex requests into clear requirements without losing details
|
|
225
|
+
7. Keep the tone natural and conversational
|
|
226
|
+
8. DO NOT add unnecessary formality or change the user's communication style
|
|
227
|
+
9. If the prompt is already clear and specific, return it as-is
|
|
228
|
+
|
|
229
|
+
CRITICAL RULES:
|
|
230
|
+
- NEVER remove important details, specific requirements, file paths, code snippets, or technical specifications
|
|
231
|
+
- NEVER simplify the prompt if it means losing user-provided information
|
|
232
|
+
- When in doubt, prefer preserving the original over optimizing
|
|
233
|
+
- The goal is CLARITY, not BREVITY - keep all important content
|
|
234
|
+
|
|
235
|
+
IMPORTANT: Output ONLY the optimized prompt text. No explanations, no meta-commentary, no JSON format. Just the optimized prompt itself.`;
|
|
236
|
+
const messages = [
|
|
237
|
+
{
|
|
238
|
+
role: 'user',
|
|
239
|
+
content: optimizationPrompt,
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
const optimizedPrompt = await this.callModel(messages, abortSignal);
|
|
243
|
+
if (!optimizedPrompt || optimizedPrompt.trim().length === 0) {
|
|
244
|
+
logger.warn('Prompt optimize agent: Empty response, using original prompt');
|
|
245
|
+
return userPrompt;
|
|
246
|
+
}
|
|
247
|
+
// Clean up the response (remove any markdown formatting if present)
|
|
248
|
+
let cleanedPrompt = optimizedPrompt.trim();
|
|
249
|
+
// Remove markdown code blocks if present
|
|
250
|
+
const codeBlockMatch = cleanedPrompt.match(/```[\s\S]*?\n([\s\S]*?)```/);
|
|
251
|
+
if (codeBlockMatch) {
|
|
252
|
+
cleanedPrompt = codeBlockMatch[1].trim();
|
|
253
|
+
}
|
|
254
|
+
// If optimized prompt is suspiciously short or looks like it failed, use original
|
|
255
|
+
if (cleanedPrompt.length < userPrompt.length * 0.3) {
|
|
256
|
+
logger.warn('Prompt optimize agent: Optimized prompt too short, using original');
|
|
257
|
+
return userPrompt;
|
|
258
|
+
}
|
|
259
|
+
return cleanedPrompt;
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
logger.error('Prompt optimize agent: Failed to optimize prompt', error);
|
|
263
|
+
return userPrompt;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Export singleton instance
|
|
268
|
+
export const promptOptimizeAgent = new PromptOptimizeAgent();
|