Fix GitHub Copilot completions path
- commit
- 1b17517ac2ce6f340f8779d6dc6d6bf5b6f771f2
- author
- patriceckhart · · 2026-07-12 19:29 UTC
- parent
- 37f8c5c
- signature
- unsigned
- refs
- tag: v0.2.80
3 files · +32 -11
M packages/provider/github_copilot.go +5 -4
| @@ -190,9 +190,10 @@ func NewGithubCopilotClient(pat string) Client { | |||
| 190 | 190 | // Initial baseURL is a sane default; copilotRefreshTransport rewrites | |
| 191 | 191 | // the host on every request based on the freshly-issued token. | |
| 192 | 192 | return &openaiClient{ | |
| 193 | - | apiKey: pat, // unused at the wire level (transport overrides Auth) but kept for parity | |
| 194 | - | baseURL: "https://api.individual.githubcopilot.com", | |
| 195 | - | name: "github-copilot", | |
| 196 | - | http: httpClient, | |
| 193 | + | apiKey: pat, // unused at the wire level (transport overrides Auth) but kept for parity | |
| 194 | + | baseURL: "https://api.individual.githubcopilot.com", | |
| 195 | + | chatCompletionsPath: "/chat/completions", | |
| 196 | + | name: "github-copilot", | |
| 197 | + | http: httpClient, | |
| 197 | 198 | } | |
| 198 | 199 | } | |
| @@ -190,9 +190,10 @@ func NewGithubCopilotClient(pat string) Client { | |||||
| 190 | // Initial baseURL is a sane default; copilotRefreshTransport rewrites | 190 | // Initial baseURL is a sane default; copilotRefreshTransport rewrites | ||
| 191 | // the host on every request based on the freshly-issued token. | 191 | // the host on every request based on the freshly-issued token. | ||
| 192 | return &openaiClient{ | 192 | return &openaiClient{ | ||
| 193 | - | apiKey: pat, // unused at the wire level (transport overrides Auth) but kept for parity | 193 | + | apiKey: pat, // unused at the wire level (transport overrides Auth) but kept for parity |
| 194 | - | baseURL: "https://api.individual.githubcopilot.com", | 194 | + | baseURL: "https://api.individual.githubcopilot.com", |
| 195 | - | name: "github-copilot", | 195 | + | chatCompletionsPath: "/chat/completions", |
| 196 | - | http: httpClient, | 196 | + | name: "github-copilot", |
| 197 | + | http: httpClient, | |||
| 197 | } | 198 | } | ||
| 198 | } | 199 | } | ||
M packages/provider/openai.go +15 -7
| @@ -35,12 +35,13 @@ func chatCompletionsURL(baseURL string) string { | |||
| 35 | 35 | } | |
| 36 | 36 | ||
| 37 | 37 | type openaiClient struct { | |
| 38 | - | apiKey string | |
| 39 | - | baseURL string | |
| 40 | - | name string | |
| 41 | - | oauth bool // when true, apiKey actually holds an OAuth access token | |
| 42 | - | headers map[string]string | |
| 43 | - | http *http.Client | |
| 38 | + | apiKey string | |
| 39 | + | baseURL string | |
| 40 | + | chatCompletionsPath string | |
| 41 | + | name string | |
| 42 | + | oauth bool // when true, apiKey actually holds an OAuth access token | |
| 43 | + | headers map[string]string | |
| 44 | + | http *http.Client | |
| 44 | 45 | } | |
| 45 | 46 | ||
| 46 | 47 | // NewOpenAI creates an OpenAI client using an API key. baseURL may be empty. | |
| @@ -431,8 +432,15 @@ func buildOAIContentBlocks(blocks []Content, isError bool) []interface{} { | |||
| 431 | 432 | ||
| 432 | 433 | // ---- streaming ---- | |
| 433 | 434 | ||
| 435 | + | func (c *openaiClient) chatCompletionsURL() string { | |
| 436 | + | if c.chatCompletionsPath != "" { | |
| 437 | + | return strings.TrimRight(c.baseURL, "/") + "/" + strings.TrimLeft(c.chatCompletionsPath, "/") | |
| 438 | + | } | |
| 439 | + | return chatCompletionsURL(c.baseURL) | |
| 440 | + | } | |
| 441 | + | ||
| 434 | 442 | func (c *openaiClient) Stream(ctx context.Context, req Request) (<-chan Event, error) { | |
| 435 | - | endpoint := chatCompletionsURL(c.baseURL) | |
| 443 | + | endpoint := c.chatCompletionsURL() | |
| 436 | 444 | wire, err := c.buildRequest(req) | |
| 437 | 445 | if err != nil { | |
| 438 | 446 | return nil, err | |
| @@ -35,12 +35,13 @@ func chatCompletionsURL(baseURL string) string { | |||||
| 35 | } | 35 | } | ||
| 36 | 36 | ||||
| 37 | type openaiClient struct { | 37 | type openaiClient struct { | ||
| 38 | - | apiKey string | 38 | + | apiKey string |
| 39 | - | baseURL string | 39 | + | baseURL string |
| 40 | - | name string | 40 | + | chatCompletionsPath string |
| 41 | - | oauth bool // when true, apiKey actually holds an OAuth access token | 41 | + | name string |
| 42 | - | headers map[string]string | 42 | + | oauth bool // when true, apiKey actually holds an OAuth access token |
| 43 | - | http *http.Client | 43 | + | headers map[string]string |
| 44 | + | http *http.Client | |||
| 44 | } | 45 | } | ||
| 45 | 46 | ||||
| 46 | // NewOpenAI creates an OpenAI client using an API key. baseURL may be empty. | 47 | // NewOpenAI creates an OpenAI client using an API key. baseURL may be empty. | ||
| @@ -431,8 +432,15 @@ func buildOAIContentBlocks(blocks []Content, isError bool) []interface{} { | |||||
| 431 | 432 | ||||
| 432 | // ---- streaming ---- | 433 | // ---- streaming ---- | ||
| 433 | 434 | ||||
| 435 | + | func (c *openaiClient) chatCompletionsURL() string { | |||
| 436 | + | if c.chatCompletionsPath != "" { | |||
| 437 | + | return strings.TrimRight(c.baseURL, "/") + "/" + strings.TrimLeft(c.chatCompletionsPath, "/") | |||
| 438 | + | } | |||
| 439 | + | return chatCompletionsURL(c.baseURL) | |||
| 440 | + | } | |||
| 441 | + | ||||
| 434 | func (c *openaiClient) Stream(ctx context.Context, req Request) (<-chan Event, error) { | 442 | func (c *openaiClient) Stream(ctx context.Context, req Request) (<-chan Event, error) { | ||
| 435 | - | endpoint := chatCompletionsURL(c.baseURL) | 443 | + | endpoint := c.chatCompletionsURL() |
| 436 | wire, err := c.buildRequest(req) | 444 | wire, err := c.buildRequest(req) | ||
| 437 | if err != nil { | 445 | if err != nil { | ||
| 438 | return nil, err | 446 | return nil, err | ||
M packages/provider/openai_url_test.go +12 -0
| @@ -38,3 +38,15 @@ func TestChatCompletionsURL(t *testing.T) { | |||
| 38 | 38 | }) | |
| 39 | 39 | } | |
| 40 | 40 | } | |
| 41 | + | ||
| 42 | + | func TestGithubCopilotChatCompletionsURL(t *testing.T) { | |
| 43 | + | client, ok := NewGithubCopilotClient("test-token").(*openaiClient) | |
| 44 | + | if !ok { | |
| 45 | + | t.Fatal("NewGithubCopilotClient did not return an openaiClient") | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | const want = "https://api.individual.githubcopilot.com/chat/completions" | |
| 49 | + | if got := client.chatCompletionsURL(); got != want { | |
| 50 | + | t.Errorf("GitHub Copilot chat completions URL = %q, want %q", got, want) | |
| 51 | + | } | |
| 52 | + | } | |
| @@ -38,3 +38,15 @@ func TestChatCompletionsURL(t *testing.T) { | |||||
| 38 | }) | 38 | }) | ||
| 39 | } | 39 | } | ||
| 40 | } | 40 | } | ||
| 41 | + | ||||
| 42 | + | func TestGithubCopilotChatCompletionsURL(t *testing.T) { | |||
| 43 | + | client, ok := NewGithubCopilotClient("test-token").(*openaiClient) | |||
| 44 | + | if !ok { | |||
| 45 | + | t.Fatal("NewGithubCopilotClient did not return an openaiClient") | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | const want = "https://api.individual.githubcopilot.com/chat/completions" | |||
| 49 | + | if got := client.chatCompletionsURL(); got != want { | |||
| 50 | + | t.Errorf("GitHub Copilot chat completions URL = %q, want %q", got, want) | |||
| 51 | + | } | |||
| 52 | + | } | |||