@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 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
- - 5-minute availability cache with fallback
95
- - Error retry with alternative LLM
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
- try {
52
- const modulePath = `${LIB_URL}${provider}-api.js`;
53
- const module = await import(modulePath);
54
-
55
- const jsonMode = mode === 'orchestrate-json';
56
- const orchestrateFn = provider === 'gpt'
57
- ? module.vibeGptOrchestrate
58
- : module.vibeGeminiOrchestrate;
59
- const result = await orchestrateFn(cleanPrompt, systemPrompt, { jsonMode });
60
- const label = provider === 'gpt' ? 'GPT-5.2' : 'Gemini-3';
61
- console.log(`${label} 응답: ${result}`);
62
- } catch (e) {
63
- console.log(`[${provider.toUpperCase()}] Error: ${e.message}`);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.4.66",
3
+ "version": "2.4.67",
4
4
  "description": "Vibe - Claude Code exclusive SPEC-driven AI coding framework with 35+ integrated tools",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",