Compare commits

..

4 Commits

6 changed files with 32 additions and 17 deletions
+10
View File
@@ -148,5 +148,15 @@
"role": "Leo",
"location": "app/llm.test.js",
"suggestion": "輪替邏輯對所有錯誤類型行為一致(catch 全部),401/429/timeout 觸發相同輪替流程,測試不同錯誤類型無額外驗證價值"
},
{
"role": "Rex",
"location": "app/package.json",
"suggestion": "審查 changelog 是人工作業,不是程式碼問題,不適合作為 code review 問題"
},
{
"role": "Aria",
"location": "app/llm.js",
"suggestion": "此 action 為 CLI 工具,process.exit(1) 是設計意圖讓 CI/CD workflow 失敗。改拋錯會被 chatJSON 的 catch 吞掉回傳 [],破壞現有行為"
}
]
+13 -13
View File
@@ -1,23 +1,23 @@
[
{
"level": "critical",
"role": "Aria",
"location": "app/llm.js:39",
"suggestion": "在 `chat` 函式中直接呼叫 `process.exit(1)` 會導致應用程式立即終止,降低了模組的重用性和測試彈性。建議改為拋出一個自訂錯誤(例如 `AllApiKeysFailedError`),讓呼叫端(例如應用程式的入口點)來決定如何處理此錯誤,例如在頂層捕獲後再呼叫 `process.exit(1)`。",
"is_new": true
},
{
"level": "warning",
"role": "Aria",
"location": ".gitea/workflows/master.yaml",
"suggestion": "檔案結尾應包含一個換行符號 (newline at EOF),這是 POSIX 系統的慣例,有助於版本控制系統的正確處理。",
"is_new": false
},
{
"level": "warning",
"role": "Leo",
"location": "app/llm.test.js",
"suggestion": "根據 `TODO.md` 的驗收標準,API Key 輪替失敗時應輸出特定的日誌訊息。目前的單元測試雖然驗證了 `process.exit(1)` 的調用,但並未對 `console.log` 和 `console.error` 的輸出進行模擬和斷言。建議使用 `mock.method(console, 'log', ...)` 和 `mock.method(console, 'error', ...)` 來捕獲並驗證這些重要的日誌訊息,以確保系統在 API Key 輪替失敗時能提供清晰的診斷資訊,這對長期維護和問題排查至關重要。",
"location": "app/llm.js:26",
"suggestion": "變數 `lastError` 在迴圈結束後並未使用。請考慮移除此變數,或在所有 API Key 均失敗時,將其包含在拋出的錯誤訊息中,以提供更詳細的失敗原因。",
"is_new": true
},
{
"level": "warning",
"role": "Leo",
"location": "app/llm.test.js",
"suggestion": "針對 API Key 輪替的錯誤處理,`TODO.md` 驗收標準中明確提到「模擬不同類型的 API 錯誤(例如 401 Unauthorized, 429 Too Many Requests, 網路超時等)」。目前的測試僅使用 `new Error('fail')` 進行通用錯誤模擬。建議擴展測試案例,模擬 `axios` 拋出帶有特定 HTTP 狀態碼(如 401, 429)的錯誤,以及模擬網路超時(例如 `axios.isAxiosError` 且 `e.code === 'ECONNABORTED'`),以確保 API Key 輪替機制在面對各種實際的 API 錯誤時都能穩健運作,這有助於提高程式碼的健壯性和可維護性。",
"level": "info",
"role": "Rex",
"location": "app/package.json",
"suggestion": "此次變更包含 `axios` 和 `openai` 等重要函式庫的版本更新,特別是 `openai` 從 `4.28.0` 升級到 `4.104.0`。建議審查這些函式庫的發行說明(changelog),以了解是否有任何安全修補、已知漏洞或行為變更,確保更新不會引入新的安全風險或不預期的行為。",
"is_new": true
}
]
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/jiantw83/code-review@v${{ needs.version.outputs.version }}
with:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
permissions:
+1 -1
View File
@@ -111,7 +111,7 @@ jobs:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/jiantw83/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
permissions:
+7
View File
@@ -105,4 +105,11 @@ describe('getLLMConfig', () => {
assert.equal(cfg.provider, 'ollama');
assert.equal(cfg.model, 'llama3');
});
it('comma-only api key is treated as not set', () => {
process.env.OPENAI_API_KEY = ',,,';
const cfg = getLLMConfig();
assert.equal(cfg.provider, null);
assert.deepEqual(cfg.apiKeys, []);
});
});
-2
View File
@@ -11,7 +11,6 @@ export async function chat(systemPrompt, userContent) {
if (provider === 'claude') headers['anthropic-version'] = '2023-06-01';
const shuffled = [...apiKeys].sort(() => Math.random() - 0.5);
let lastError;
for (let i = 0; i < shuffled.length; i++) {
if (provider !== 'ollama') headers['Authorization'] = `Bearer ${shuffled[i]}`;
try {
@@ -22,7 +21,6 @@ export async function chat(systemPrompt, userContent) {
);
return resp.data.choices[0].message.content;
} catch (e) {
lastError = e;
console.log(` [LLM] key[${i + 1}/${shuffled.length}] 失敗: ${e.message}`);
}
}