feat(ai-code-review): 改用 AI 助理 CLI 執行審查
This commit is contained in:
+11
-53
@@ -6,7 +6,6 @@ import {
|
||||
GITEA_REPOSITORY,
|
||||
PR_NUMBER,
|
||||
getInsecureHttpsAgent,
|
||||
getOpenCodeHttpsAgent,
|
||||
getLLMConfig,
|
||||
} from './config.js';
|
||||
import { verifyRemoteAccess } from './git.js';
|
||||
@@ -30,33 +29,6 @@ const api = (path) => `${GITEA_SERVER_URL.replace(/\/$/, '')}/api/v1${path}`;
|
||||
* @returns {{Authorization: string, 'Content-Type': string}} 可直接交給 axios 的 headers 物件。
|
||||
*/
|
||||
const giteaHeaders = (token) => ({ Authorization: `token ${token}`, 'Content-Type': 'application/json' });
|
||||
/**
|
||||
* 將模型字串解析為 OpenCode 的 providerID 與 modelID。
|
||||
*
|
||||
* 若 model 含 `/` 則以斜線拆分為 provider/model;否則 provider 取
|
||||
* 環境變數 OPENCODE_PROVIDER(預設 `google`),model 即原字串。
|
||||
* @param {string} model - 模型識別字串,例如 `google/gemini-2.0` 或 `gemini-2.0`。
|
||||
* @returns {{providerID: string, modelID: string}} 解析後的 provider 與 model 識別碼。
|
||||
* @remarks 讀取 process.env.OPENCODE_PROVIDER;split 上限為 2 段,多餘段落會被忽略。
|
||||
*/
|
||||
const opencodeModelConfig = (model) => {
|
||||
const [providerID, modelID] = model.includes('/') ? model.split('/', 2) : [process.env.OPENCODE_PROVIDER || 'google', model];
|
||||
return { providerID, modelID };
|
||||
};
|
||||
/**
|
||||
* 組出呼叫 OpenCode server 用的 axios 請求設定。
|
||||
*
|
||||
* 固定 30 秒逾時,並套用 config.js 的 getOpenCodeHttpsAgent() 作為 httpsAgent。
|
||||
* @param {object} headers - 要套用的 HTTP headers 物件。
|
||||
* @returns {{headers: object, timeout: number, httpsAgent: import('https').Agent}} axios 設定物件。
|
||||
* @remarks 每次呼叫都會執行 getOpenCodeHttpsAgent() 取得 agent。
|
||||
*/
|
||||
const opencodeAxiosOptions = (headers) => ({
|
||||
headers,
|
||||
timeout: 30000,
|
||||
httpsAgent: getOpenCodeHttpsAgent(),
|
||||
});
|
||||
|
||||
/**
|
||||
* 將(axios)錯誤格式化為易讀的訊息字串。
|
||||
*
|
||||
@@ -124,37 +96,23 @@ export async function verifyCommentToken(token = GITEA_COMMENT_TOKEN) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 驗證 LLM(OpenCode server)設定可用。
|
||||
* 驗證 LLM(AI 助理 CLI)設定可用。
|
||||
*
|
||||
* 依序確認:已設定 provider、有 base URL、health 端點可連線、OpenCode 已設定
|
||||
* 對應 provider 且其列出指定 model。任一不符回傳對應錯誤;錯誤被攔截不會 throw。
|
||||
* 確認目前環境可偵測到支援的 CLI,且已解析出 model。實際模型可用性由 CLI
|
||||
* 在正式呼叫時回報;preflight 不主動送 prompt,避免額外消耗額度。
|
||||
* @returns {Promise<{ok: true, provider: string}|{ok: false, provider?: string, error: string}>}
|
||||
* 通過時含 provider;未設定 provider 的失敗分支不含 provider 欄位。
|
||||
* @remarks 設定來源為 config.js 的 getLLMConfig();provider/model 鍵的比對使用 opencodeModelConfig 解析後的 providerID/modelID。
|
||||
* @remarks 設定來源為 config.js 的 getLLMConfig()。
|
||||
*/
|
||||
export async function verifyLLM() {
|
||||
const { provider, baseURL, model } = getLLMConfig();
|
||||
if (!provider) return { ok: false, error: '未設定 OpenCode server,請設定 OPENCODE_BASE_URL' };
|
||||
if (!baseURL) return { ok: false, provider, error: `${provider} 缺少 base URL` };
|
||||
|
||||
const base = baseURL.replace(/\/$/, '');
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
|
||||
const { providerID, modelID } = opencodeModelConfig(model);
|
||||
try {
|
||||
await axios.get(`${base}/global/health`, opencodeAxiosOptions(headers));
|
||||
const providers = await axios.get(`${base}/config/providers`, opencodeAxiosOptions(headers));
|
||||
const configuredProvider = providers.data.providers?.find(p => p.id === providerID);
|
||||
if (!configuredProvider) return { ok: false, provider, error: `OpenCode server 未設定 provider=${providerID}` };
|
||||
if (!configuredProvider.models?.[modelID]) return { ok: false, provider, error: `OpenCode server provider=${providerID} 未列出 model=${modelID}` };
|
||||
return { ok: true, provider };
|
||||
} catch (e) {
|
||||
return { ok: false, provider, error: `OpenCode server 驗證失敗: ${e.message}` };
|
||||
}
|
||||
const { provider, command, model } = getLLMConfig();
|
||||
if (!provider || !command) return { ok: false, error: '未偵測到可用 AI 助理 CLI,請安裝 codex、claude 或 opencode' };
|
||||
if (!model) return { ok: false, provider, error: '未設定 MODEL' };
|
||||
return { ok: true, provider, command, model };
|
||||
}
|
||||
|
||||
/**
|
||||
* 執行所有前置驗證(Step2):環境變數、Gitea token、comment token、git 遠端、LLM。
|
||||
* 執行所有前置驗證(Step2):環境變數、Gitea token、comment token、git 遠端、LLM CLI。
|
||||
*
|
||||
* 全程唯讀,不發布任何 comment;任一檢查失敗即記錄錯誤並回傳 false。
|
||||
* 各檢查可經 deps 注入覆寫,方便單元測試。
|
||||
@@ -164,7 +122,7 @@ export async function verifyLLM() {
|
||||
* @param {Function} [deps.verifyToken=verifyGiteaToken] - Gitea token / repo 讀取驗證。
|
||||
* @param {Function} [deps.verifyComment=verifyCommentToken] - comment token 驗證。
|
||||
* @param {Function} [deps.verifyRemote=verifyRemoteAccess] - git 遠端(ls-remote)認證驗證。
|
||||
* @param {Function} [deps.verifyLLMFn=verifyLLM] - LLM(OpenCode)連線驗證。
|
||||
* @param {Function} [deps.verifyLLMFn=verifyLLM] - LLM(AI 助理 CLI)驗證。
|
||||
* @returns {Promise<boolean>} 全部通過為 true,任一失敗為 false。
|
||||
* @remarks 透過 log.js 輸出 step/ok/line/error/result 記錄;不會 throw(前提是注入的檢查函式皆自行攔截錯誤)。
|
||||
*/
|
||||
@@ -212,7 +170,7 @@ export async function runPreflight(workspace = process.env.GITHUB_WORKSPACE || '
|
||||
error(`LLM 驗證失敗: ${llm.error}`);
|
||||
return false;
|
||||
}
|
||||
ok(`LLM provider=${llm.provider} 連線正常`);
|
||||
ok(`LLM provider=${llm.provider} CLI 可用`);
|
||||
|
||||
result(true, '前置驗證通過');
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user