@su-record/vibe 2.4.66 → 2.4.67
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/README.md +3 -2
- package/hooks/scripts/llm-orchestrate.js +107 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,8 +91,9 @@ Enable maximum performance with `ultrawork` or `ulw`:
|
|
|
91
91
|
|
|
92
92
|
**Smart Routing features:**
|
|
93
93
|
- Automatic LLM selection based on task type
|
|
94
|
-
-
|
|
95
|
-
-
|
|
94
|
+
- Exponential backoff retry (3 attempts, 2s → 4s → 8s)
|
|
95
|
+
- Auto fallback chain: GPT ↔ Gemini (if primary fails)
|
|
96
|
+
- 5-minute availability cache
|
|
96
97
|
- Web search via Gemini (Google Search grounding)
|
|
97
98
|
|
|
98
99
|
**API usage:**
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* mode: orchestrate | orchestrate-json
|
|
7
7
|
* systemPrompt: (optional) custom system prompt for orchestrate mode
|
|
8
8
|
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Exponential backoff retry (3 attempts)
|
|
11
|
+
* - Auto fallback: gemini → gpt, gpt → gemini
|
|
12
|
+
* - Overload/rate-limit detection
|
|
13
|
+
*
|
|
9
14
|
* Input: JSON from stdin with { prompt: string }
|
|
10
15
|
*/
|
|
11
16
|
import { getLibBaseUrl } from './utils.js';
|
|
@@ -16,6 +21,86 @@ const provider = process.argv[2] || 'gemini';
|
|
|
16
21
|
const mode = process.argv[3] || 'orchestrate';
|
|
17
22
|
const systemPrompt = process.argv[4] || 'You are a helpful assistant.';
|
|
18
23
|
|
|
24
|
+
// Retry configuration
|
|
25
|
+
const MAX_RETRIES = 3;
|
|
26
|
+
const INITIAL_DELAY_MS = 2000;
|
|
27
|
+
|
|
28
|
+
// Errors that should skip retry and go to fallback immediately
|
|
29
|
+
const SKIP_RETRY_PATTERNS = [
|
|
30
|
+
/rate.?limit/i,
|
|
31
|
+
/quota/i,
|
|
32
|
+
/unauthorized/i,
|
|
33
|
+
/forbidden/i,
|
|
34
|
+
/401/,
|
|
35
|
+
/403/,
|
|
36
|
+
/429/,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
// Errors that should trigger retry with backoff
|
|
40
|
+
const RETRY_PATTERNS = [
|
|
41
|
+
/overload/i,
|
|
42
|
+
/503/,
|
|
43
|
+
/5\d\d/,
|
|
44
|
+
/network/i,
|
|
45
|
+
/timeout/i,
|
|
46
|
+
/ECONNRESET/i,
|
|
47
|
+
/ETIMEDOUT/i,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
function shouldSkipRetry(errorMsg) {
|
|
51
|
+
return SKIP_RETRY_PATTERNS.some(pattern => pattern.test(errorMsg));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function shouldRetry(errorMsg) {
|
|
55
|
+
return RETRY_PATTERNS.some(pattern => pattern.test(errorMsg));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sleep(ms) {
|
|
59
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function callProvider(providerName, prompt, sysPrompt, jsonMode) {
|
|
63
|
+
const modulePath = `${LIB_URL}${providerName}-api.js`;
|
|
64
|
+
const module = await import(modulePath);
|
|
65
|
+
|
|
66
|
+
const orchestrateFn = providerName === 'gpt'
|
|
67
|
+
? module.vibeGptOrchestrate
|
|
68
|
+
: module.vibeGeminiOrchestrate;
|
|
69
|
+
|
|
70
|
+
return await orchestrateFn(prompt, sysPrompt, { jsonMode });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function callWithRetry(providerName, prompt, sysPrompt, jsonMode) {
|
|
74
|
+
let lastError;
|
|
75
|
+
|
|
76
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
77
|
+
try {
|
|
78
|
+
return { success: true, result: await callProvider(providerName, prompt, sysPrompt, jsonMode) };
|
|
79
|
+
} catch (e) {
|
|
80
|
+
lastError = e;
|
|
81
|
+
const errorMsg = e.message || String(e);
|
|
82
|
+
|
|
83
|
+
// Skip retry for auth/quota errors - go to fallback immediately
|
|
84
|
+
if (shouldSkipRetry(errorMsg)) {
|
|
85
|
+
return { success: false, error: errorMsg, skipToFallback: true };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Retry with backoff for transient errors
|
|
89
|
+
if (shouldRetry(errorMsg) && attempt < MAX_RETRIES) {
|
|
90
|
+
const delay = INITIAL_DELAY_MS * Math.pow(2, attempt - 1);
|
|
91
|
+
console.error(`[${providerName.toUpperCase()}] Retry ${attempt}/${MAX_RETRIES} after ${delay}ms...`);
|
|
92
|
+
await sleep(delay);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Unknown error or max retries reached
|
|
97
|
+
return { success: false, error: errorMsg, skipToFallback: false };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { success: false, error: lastError?.message || 'Max retries exceeded', skipToFallback: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
19
104
|
async function main() {
|
|
20
105
|
let prompt;
|
|
21
106
|
|
|
@@ -47,20 +132,29 @@ async function main() {
|
|
|
47
132
|
gemini: /^(gemini[-.\s]|제미나이-|vibe-gemini-)\s*/i,
|
|
48
133
|
};
|
|
49
134
|
const cleanPrompt = prompt.replace(prefixPatterns[provider] || /^/, '').trim();
|
|
135
|
+
const jsonMode = mode === 'orchestrate-json';
|
|
136
|
+
|
|
137
|
+
// Provider chain: primary → fallback
|
|
138
|
+
const fallbackProvider = provider === 'gpt' ? 'gemini' : 'gpt';
|
|
139
|
+
const providerChain = [provider, fallbackProvider];
|
|
50
140
|
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
141
|
+
for (const currentProvider of providerChain) {
|
|
142
|
+
const label = currentProvider === 'gpt' ? 'GPT-5.2' : 'Gemini-3';
|
|
143
|
+
const result = await callWithRetry(currentProvider, cleanPrompt, systemPrompt, jsonMode);
|
|
144
|
+
|
|
145
|
+
if (result.success) {
|
|
146
|
+
console.log(`${label} 응답: ${result.result}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Log failure and try fallback
|
|
151
|
+
if (currentProvider !== providerChain[providerChain.length - 1]) {
|
|
152
|
+
const fallbackLabel = fallbackProvider === 'gpt' ? 'GPT' : 'Gemini';
|
|
153
|
+
console.error(`[${currentProvider.toUpperCase()}] Failed: ${result.error}. Falling back to ${fallbackLabel}...`);
|
|
154
|
+
} else {
|
|
155
|
+
// All providers failed
|
|
156
|
+
console.log(`[LLM] Error: All providers failed. Last error: ${result.error}`);
|
|
157
|
+
}
|
|
64
158
|
}
|
|
65
159
|
}
|
|
66
160
|
|