diff --git a/.github/workflows/open_code_review.yml b/.github/workflows/open_code_review.yml index 7921a55..f582c3f 100644 --- a/.github/workflows/open_code_review.yml +++ b/.github/workflows/open_code_review.yml @@ -4,7 +4,9 @@ on: workflow_call: secrets: DEEPSEEK_API_KEY: - required: true + required: false + QWEN_API_KEY: + required: false # A newer authorized /review command supersedes a review that is still running. # Other comments get unique groups, so they cannot cancel a PR review. @@ -35,46 +37,95 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: + - name: Acknowledge review command + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'eyes', + }); + - name: Resolve model and pull request refs id: pr uses: actions/github-script@v7 with: script: | - const command = context.payload.comment.body.trim(); - const match = /^\/review(?: +([A-Za-z0-9-]+))?$/.exec(command); + const providers = { + deepseek: { + url: 'https://api.deepseek.com/chat/completions', + secret: 'DEEPSEEK_API_KEY', + }, + qwen: { + url: 'https://dashscope-us.aliyuncs.com/compatible-mode/v1/chat/completions', + secret: 'QWEN_API_KEY', + }, + }; + const models = { - pro: 'deepseek-v4-pro', - flash: 'deepseek-flash', - 'deepseek-v4-pro': 'deepseek-v4-pro', - 'deepseek-flash': 'deepseek-flash', + pro: { id: 'deepseek-v4-pro', provider: 'deepseek' }, + flash: { id: 'deepseek-flash', provider: 'deepseek' }, + max: { id: 'qwen3.8-max', provider: 'qwen' }, + // DeepSeek + 'deepseek-pro': { id: 'deepseek-v4-pro', provider: 'deepseek' }, + 'deepseek-flash': { id: 'deepseek-flash', provider: 'deepseek' }, + // Qwen + 'qwen-max': { id: 'qwen3.8-max', provider: 'qwen' }, + 'qwen-flash': { id: 'qwen3.8-flash', provider: 'qwen' }, + 'qwen-plus': { id: 'qwen3.7-plus', provider: 'qwen' }, }; - const model = match && (match[1] ? models[match[1]] : models.pro); - if (!model) { - core.setFailed('Use /review [pro|flash|deepseek-v4-pro|deepseek-flash].'); - return; - } - core.setOutput('model', model); - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - core.setOutput('base_ref', pr.base.ref); - core.setOutput('head_sha', pr.head.sha); + try { + const command = context.payload.comment.body.trim(); + const match = /^\/review(?: +([A-Za-z0-9._-]+))?$/i.exec(command); + const requestedModel = match && (match[1] || 'pro').toLowerCase(); + const model = requestedModel && models[requestedModel]; + if (!model) { + throw new Error( + `Unsupported model. Use /review MODEL. Supported models: ${Object.keys(models).join(', ')}.` + ); + } - - name: Acknowledge review command + const provider = providers[model.provider]; + if (!provider) { + throw new Error(`Provider configuration is missing for ${model.provider}.`); + } + + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + core.setOutput('model', model.id); + core.setOutput('llm_url', provider.url); + core.setOutput('llm_auth_secret', provider.secret); + core.setOutput('base_ref', pr.base.ref); + core.setOutput('head_sha', pr.head.sha); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + core.setOutput('error', message); + core.setFailed(message); + } + + - name: Report model resolution error + if: ${{ failure() && steps.pr.outcome == 'failure' }} uses: actions/github-script@v7 - continue-on-error: true + env: + ERROR_MESSAGE: ${{ steps.pr.outputs.error || 'Failed to resolve the review model or pull request.' }} with: script: | - await github.rest.reactions.createForIssueComment({ + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'eyes', + issue_number: context.issue.number, + body: `OpenCodeReview could not start: ${process.env.ERROR_MESSAGE}`, }); + # The shared rules are checked out from this trusted repository, never # from the pull request branch being reviewed. - name: Check out shared Nim rules @@ -111,8 +162,8 @@ jobs: - name: Review with OpenCodeReview uses: alibaba/open-code-review@main with: - llm_url: https://api.deepseek.com/chat/completions - llm_auth_token: ${{ secrets.DEEPSEEK_API_KEY }} + llm_url: ${{ steps.pr.outputs.llm_url }} + llm_auth_token: ${{ secrets[steps.pr.outputs.llm_auth_secret] }} llm_model: ${{ steps.pr.outputs.model }} llm_use_anthropic: 'false' base_ref: ${{ steps.pr.outputs.base_ref }}