Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
| `pr_number` | ✅ | — | Pull request number to review |
| `api_key` | ✅ | — | API key for the provider. For `github-models`: use `GITHUB_TOKEN`. For `copilot`: use a personal PAT with Copilot subscription |
| `provider` | | `github-models` | AI provider shorthand (see [Providers](#providers)) |
| `model` | | `gpt-4o` | Model name |
| `model` | | `openai/gpt-4o-mini` | Model name |
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
| `title` | | `AI Code Review` | Title shown in the PR comment header |
| `api_url` | | — | Full endpoint URL — overrides provider + api_type defaults |
| `api_type` | | `chat` | API format: `chat`, `responses`, or `messages` |
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ inputs:
provider:
description: "AI provider shorthand: copilot, github-models, openai, anthropic, openrouter, ollama, xai, zai, google, azure, aws, custom — sets api_url and api_type defaults automatically (azure/aws require api_url). Use github-models to authenticate with GITHUB_TOKEN without needing a personal PAT."
required: false
default: "github-models"
default: "github-azure-models"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Default provider in the action definition doesn't match what the documentation promises

The action's default provider is set to github-azure-models (action.yml:29) but the README documents the default as github-models, so users relying on the documented default will unknowingly hit a different API endpoint.

Impact: Users following the Quick Start guide without specifying a provider will connect to the Azure-hosted endpoint instead of the documented GitHub-hosted endpoint, which may behave differently or require different permissions.

Inconsistency between action.yml and README

The README at line 41 states:

| `provider` | | `github-models` | AI provider shorthand (see [Providers](#providers)) |

But action.yml:29 sets:

default: "github-azure-models"

The github-azure-models provider resolves to https://models.inference.ai.azure.com while github-models resolves to https://models.github.ai/inference. These are different endpoints. Additionally, github-azure-models is not listed in the README's Providers table at all, so users have no documentation for the actual default behavior.

Prompt for agents
The action.yml default provider is set to 'github-azure-models' but the README documents the default as 'github-models'. Either update action.yml line 29 to use 'github-models' as the default (matching the README), or update the README to document 'github-azure-models' as the default and add it to the Providers table. The choice depends on which endpoint is preferred for the default zero-config experience.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

api_url:
description: "Full API endpoint URL (e.g. https://api.anthropic.com/v1/messages). Overrides provider + api_type defaults when set."
required: false
Expand Down
12,258 changes: 11,947 additions & 311 deletions dist/index.js

Large diffs are not rendered by default.

374 changes: 374 additions & 0 deletions dist/licenses.txt

Large diffs are not rendered by default.

214 changes: 211 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
"@vercel/ncc": "^0.44.0",
"typescript": "^5.0.0",
"vitest": "^4.1.9"
},
"dependencies": {
"@azure-rest/ai-inference": "^1.0.0-beta.6",
"@azure/core-auth": "^1.10.1"
}
}
4 changes: 2 additions & 2 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe("PROVIDER_BASES", () => {
);
});
it("github-models base URL", () => {
expect(PROVIDER_BASES["github-models"]).toBe("https://models.inference.ai.azure.com");
expect(PROVIDER_BASES["github-models"]).toBe("https://models.github.ai/inference");
});
it("azure/aws/custom have no base URL", () => {
expect(PROVIDER_BASES.azure).toBeUndefined();
Expand Down Expand Up @@ -125,7 +125,7 @@ describe("resolveApiUrl", () => {
});
it("constructs GitHub Models chat URL", () => {
expect(resolveApiUrl("github-models", "chat")).toBe(
"https://models.inference.ai.azure.com/chat/completions"
"https://models.github.ai/inference/chat/completions"
);
});
it("uses override URL when provided, ignoring provider+type", () => {
Expand Down
8 changes: 7 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type ApiType = "chat" | "responses" | "messages" | "text";
export type Provider =
| "copilot"
| "github-models"
| "github-azure-models"
| "openai"
| "anthropic"
| "openrouter"
Expand All @@ -21,7 +22,8 @@ export const API_TYPE_PATHS: Record<ApiType, string> = {
};

export const PROVIDER_BASES: Partial<Record<Provider, string>> = {
"github-models": "https://models.inference.ai.azure.com",
"github-azure-models": "https://models.inference.ai.azure.com",
"github-models": "https://models.github.ai/inference",
copilot: "https://api.githubcopilot.com",
openai: "https://api.openai.com/v1",
anthropic: "https://api.anthropic.com/v1",
Expand Down Expand Up @@ -67,6 +69,10 @@ export function isAzureProvider(provider: Provider | undefined, url: string): bo
return provider === "azure" || url.includes(".openai.azure.com");
}

export function isGithubModelsProvider(provider: Provider | undefined, url: string): boolean {
return provider === "github-models" || url.includes(".inference.ai.azure.com");
}
Comment on lines +72 to +74

@devin-ai-integration devin-ai-integration Bot Jul 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 GitHub Models URL detection checks the old domain instead of the new one

The URL-based detection for the GitHub Models provider checks for the old domain .inference.ai.azure.com (url.includes(".inference.ai.azure.com") at src/config.ts:71) even though the base URL was changed to models.github.ai/inference, so custom-provider users pointing at the new endpoint will not be routed to the correct SDK path.

Impact: Users who set provider: custom with a models.github.ai URL will not get the GitHub Models SDK integration.

Mechanism: URL pattern mismatch after base URL migration

The PROVIDER_BASES["github-models"] was changed from https://models.inference.ai.azure.com to https://models.github.ai/inference at src/config.ts:24, but the newly added isGithubModelsProvider function at src/config.ts:70-72 still checks for the old domain pattern .inference.ai.azure.com. The URL-based fallback will never match the new URL. It should check for models.github.ai instead.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


export function requiresApiUrl(provider: Provider | undefined): boolean {
return REQUIRES_API_URL.includes(provider as Provider);
}
33 changes: 33 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
isCopilotUrl,
isAzureProvider,
requiresApiUrl,
isGithubModelsProvider,
} from "./config";
import ModelClient, { isUnexpected } from "@azure-rest/ai-inference";
import { AzureKeyCredential } from "@azure/core-auth";

// ดึงค่าจาก Environment Variables (รองรับทั้ง GitHub Actions inputs และ env vars ตรง)
const GH_PAT = process.env.INPUT_GH_PAT ?? process.env.GH_PAT;
Expand Down Expand Up @@ -130,6 +133,9 @@ async function askAI(diffText: string): Promise<string> {
const systemPrompt = `You are an expert Code Reviewer. Review the code and respond in ${LANGUAGE}.`;
const userPrompt = `Please review the following code diff and suggest improvements. Also point out any security vulnerabilities if present. Respond in ${LANGUAGE}.\n\n${diffText}`;
console.log(`Using api_type: ${API_TYPE} (${API_URL})`);
if (isGithubModelsProvider(PROVIDER, API_URL) && API_TYPE === "chat") {
return askGitHubModels(systemPrompt, userPrompt);
}
if (API_TYPE === "chat" || API_TYPE === "text") {
return askOpenAICompat(systemPrompt, userPrompt);
}
Expand All @@ -142,6 +148,33 @@ async function askAI(diffText: string): Promise<string> {
return askOpenAICompat(systemPrompt, userPrompt);
}

// 2b. GitHub Models — ใช้ @azure-rest/ai-inference SDK อย่างเป็นทางการ
async function askGitHubModels(systemPrompt: string, userPrompt: string): Promise<string> {
const endpoint = API_URL.replace(/\/chat\/completions\/?$/, "");
const token = GH_PAT ?? API_KEY;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 GitHub Models authentication ignores the explicit API key and always uses the general-purpose token

The authentication token for GitHub Models is selected with wrong priority (GH_PAT ?? API_KEY at src/index.ts:155), so the explicitly provided API key is ignored whenever the general GitHub PAT is set — which is always, since it is a required input.

Impact: Users who provide a dedicated API key for GitHub Models will silently authenticate with the wrong credential, potentially causing authorization failures.

Mechanism: GH_PAT takes precedence over the resolved API_KEY

GH_PAT is the gh_pat input (required, always set). API_KEY at src/index.ts:41 is resolved as EXPLICIT_API_KEY ?? GH_PAT ?? "", meaning it already incorporates the explicit api_key input with GH_PAT as fallback. By writing GH_PAT ?? API_KEY, the code always picks GH_PAT (since it's always truthy for a required input), ignoring any explicitly provided api_key. All other API functions (askOpenAICompat, askAnthropic, askResponses) correctly use API_KEY. The fix should be API_KEY (or API_KEY ?? GH_PAT if a different priority is intended).

Suggested change
const token = GH_PAT ?? API_KEY;
const token = API_KEY || GH_PAT || "";
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

const client = ModelClient(endpoint, new AzureKeyCredential(token));

const response = await client.path("/chat/completions").post({
body: {
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt },
],
model: MODEL,
max_tokens: MAX_TOKENS,
},
});

if (isUnexpected(response)) {
const errText = JSON.stringify(response.body.error ?? response.body);
console.error(`GitHub Models API error: ${response.status}`);
console.error(`Response: ${errText.slice(0, 500)}`);
return `⚠️ GitHub Models API returned status ${response.status}. Response: ${errText.slice(0, 200)}`;
}

return response.body.choices?.[0]?.message?.content ?? "";
}

async function askOpenAICompat(systemPrompt: string, userPrompt: string): Promise<string> {
const isChat = API_TYPE !== "text";

Expand Down
Loading