From 1485decdbb29b954c5c13254faada99d0199781f Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Thu, 21 May 2026 19:40:41 +0200 Subject: [PATCH] backend: fix copilot API paths Copilot API has no /v1 prefix. Add pathPrefix field to OpenAIBackend (defaults to /v1) and apply extraHeaders in fetchModels. Copilot sets pathPrefix to / so it hits /models and /chat/completions directly. --- pkg/backend/copilot.go | 9 +++++---- pkg/backend/openai.go | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/pkg/backend/copilot.go b/pkg/backend/copilot.go index 4802820..0876175 100644 --- a/pkg/backend/copilot.go +++ b/pkg/backend/copilot.go @@ -10,10 +10,11 @@ import ( func NewCopilot(token string) (*OpenAIBackend, error) { u, _ := url.Parse("https://api.githubcopilot.com") return &OpenAIBackend{ - name: "copilot", - baseURL: u, - apiKey: token, - client: &http.Client{}, + name: "copilot", + baseURL: u, + apiKey: token, + client: &http.Client{}, + pathPrefix: "/", extraHeaders: map[string]string{ "Openai-Intent": "conversation-edits", "User-Agent": "opencode/local", diff --git a/pkg/backend/openai.go b/pkg/backend/openai.go index f58aee7..7b444dd 100644 --- a/pkg/backend/openai.go +++ b/pkg/backend/openai.go @@ -23,6 +23,7 @@ type OpenAIBackend struct { model string client *http.Client extraHeaders map[string]string // optional; applied after Authorization + pathPrefix string // API path prefix (default: /v1); set to "/" for no prefix ctxLength int // cached; 0 = not yet fetched ctxModel string // model the cached value is for cachedModels []openAIModelInfo // cached model list @@ -54,10 +55,17 @@ func (b *OpenAIBackend) fetchModels(ctx context.Context) []openAIModelInfo { if len(b.cachedModels) > 0 { return b.cachedModels } - req, _ := http.NewRequestWithContext(ctx, "GET", b.baseURL.JoinPath("/v1/models").String(), nil) + prefix := b.pathPrefix + if prefix == "" { + prefix = "/v1" + } + req, _ := http.NewRequestWithContext(ctx, "GET", b.baseURL.JoinPath(prefix, "models").String(), nil) if b.apiKey != "" { req.Header.Set("Authorization", "Bearer "+b.apiKey) } + for k, v := range b.extraHeaders { + req.Header.Set(k, v) + } resp, err := b.client.Do(req) if err != nil || resp.StatusCode != 200 { if resp != nil { @@ -493,7 +501,11 @@ func (b *OpenAIBackend) ChatStream(ctx context.Context, messages []Message, tool data, _ := json.Marshal(req) - httpReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, b.baseURL.JoinPath("/v1/chat/completions").String(), bytes.NewReader(data)) + pp := b.pathPrefix + if pp == "" { + pp = "/v1" + } + httpReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, b.baseURL.JoinPath(pp, "chat/completions").String(), bytes.NewReader(data)) httpReq.Header.Set("Content-Type", "application/json") if b.apiKey != "" { httpReq.Header.Set("Authorization", "Bearer "+b.apiKey)