Skip to content

Commit 3abcf38

Browse files
committed
fix: use GitHub Models API for embeddings instead of Copilot API
- Change base URL from api.githubcopilot.com to models.github.ai - Update endpoint to /inference/embeddings - Use GitHub API headers (Accept: application/vnd.github+json) - Use OAuth token directly instead of Copilot token exchange - Prefix model with openai/ as required by GitHub Models API - Remove unused imports and token persistence logic
1 parent 4fad0b5 commit 3abcf38

File tree

2 files changed

+12
-64
lines changed

2 files changed

+12
-64
lines changed

src/embeddings/detector.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,11 @@ function getGitHubCopilotCredentials(): ProviderCredentials | null {
114114
return null;
115115
}
116116

117+
// Use GitHub Models API for embeddings (models.github.ai)
118+
// Enterprise uses different URL pattern
117119
const baseUrl = (copilotAuth as OpenCodeAuthOAuth).enterpriseUrl
118120
? `https://copilot-api.${(copilotAuth as OpenCodeAuthOAuth).enterpriseUrl!.replace(/^https?:\/\//, "").replace(/\/$/, "")}`
119-
: "https://api.githubcopilot.com";
121+
: "https://models.github.ai";
120122

121123
return {
122124
provider: "github-copilot",

src/embeddings/provider.ts

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { EmbeddingModelInfo } from "../config/schema.js";
22
import { ProviderCredentials } from "./detector.js";
3-
import { readFileSync, writeFileSync } from "fs";
4-
import * as path from "path";
5-
import * as os from "os";
63

74
export interface EmbeddingResult {
85
embedding: number[];
@@ -38,69 +35,17 @@ export function createEmbeddingProvider(
3835
}
3936
}
4037

41-
const COPILOT_HEADERS = {
42-
"User-Agent": "GitHubCopilotChat/0.35.0",
43-
"Editor-Version": "vscode/1.107.0",
44-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
45-
"Copilot-Integration-Id": "vscode-chat",
46-
"Openai-Intent": "conversation-edits",
47-
};
48-
4938
class GitHubCopilotEmbeddingProvider implements EmbeddingProviderInterface {
50-
private accessToken: string;
51-
private tokenExpires: number;
52-
5339
constructor(
5440
private credentials: ProviderCredentials,
5541
private modelInfo: EmbeddingModelInfo
56-
) {
57-
this.accessToken = credentials.accessToken || "";
58-
this.tokenExpires = credentials.tokenExpires || 0;
59-
}
60-
61-
private async ensureValidToken(): Promise<string> {
62-
if (this.accessToken && this.tokenExpires > Date.now()) {
63-
return this.accessToken;
64-
}
42+
) {}
6543

44+
private getToken(): string {
6645
if (!this.credentials.refreshToken) {
67-
throw new Error("No refresh token available for GitHub Copilot");
68-
}
69-
70-
const response = await fetch("https://api.github.com/copilot_internal/v2/token", {
71-
headers: {
72-
Accept: "application/json",
73-
Authorization: `Bearer ${this.credentials.refreshToken}`,
74-
...COPILOT_HEADERS,
75-
},
76-
});
77-
78-
if (!response.ok) {
79-
throw new Error(`Token refresh failed: ${response.status}`);
80-
}
81-
82-
const tokenData = await response.json() as { token: string; expires_at: number };
83-
this.accessToken = tokenData.token;
84-
this.tokenExpires = tokenData.expires_at * 1000 - 5 * 60 * 1000;
85-
86-
this.persistToken(tokenData.token, this.tokenExpires);
87-
88-
return this.accessToken;
89-
}
90-
91-
private persistToken(token: string, expires: number): void {
92-
try {
93-
const authPath = path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
94-
const authData = JSON.parse(readFileSync(authPath, "utf-8"));
95-
96-
if (authData["github-copilot"]) {
97-
authData["github-copilot"].access = token;
98-
authData["github-copilot"].expires = expires;
99-
writeFileSync(authPath, JSON.stringify(authData, null, 2));
100-
}
101-
} catch {
102-
// Ignore token cache write errors
46+
throw new Error("No OAuth token available for GitHub");
10347
}
48+
return this.credentials.refreshToken;
10449
}
10550

10651
async embed(text: string): Promise<EmbeddingResult> {
@@ -112,17 +57,18 @@ class GitHubCopilotEmbeddingProvider implements EmbeddingProviderInterface {
11257
}
11358

11459
async embedBatch(texts: string[]): Promise<EmbeddingBatchResult> {
115-
const token = await this.ensureValidToken();
60+
const token = this.getToken();
11661

117-
const response = await fetch(`${this.credentials.baseUrl}/embeddings`, {
62+
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
11863
method: "POST",
11964
headers: {
12065
Authorization: `Bearer ${token}`,
12166
"Content-Type": "application/json",
122-
...COPILOT_HEADERS,
67+
Accept: "application/vnd.github+json",
68+
"X-GitHub-Api-Version": "2022-11-28",
12369
},
12470
body: JSON.stringify({
125-
model: this.modelInfo.model,
71+
model: `openai/${this.modelInfo.model}`,
12672
input: texts,
12773
}),
12874
});

0 commit comments

Comments
 (0)