Skip to content

fix(api-proxy): fetch models from BYOK custom providers and fix models_url in reflect#2699

Merged
lpcox merged 4 commits intomainfrom
copilot/fix-api-proxy-bad-request
May 7, 2026
Merged

fix(api-proxy): fetch models from BYOK custom providers and fix models_url in reflect#2699
lpcox merged 4 commits intomainfrom
copilot/fix-api-proxy-bad-request

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 7, 2026

In BYOK mode with a custom provider (e.g. OpenRouter), cachedModels.copilot was never populated and models_url in the reflect response always pointed to /models (ignoring COPILOT_API_BASE_PATH). This caused awf-reflect: models fetch returned 400 errors from the gh-aw framework and made arbitrary model names like minimax/minimax-m2.5:free invisible to the model resolver.

Changes

containers/api-proxy/providers/copilot.js

  • getModelsFetchConfig() — For standard api.githubcopilot.com, behavior is unchanged (only COPILOT_GITHUB_TOKEN accepted, returns null for BYOK-only). For custom targets, now returns a fetch config using the BYOK auth token at https://${rawTarget}${basePath}/models, populating the model cache at startup.

  • getReflectionInfo()models_url now includes the base path prefix. For OpenRouter (COPILOT_API_BASE_PATH=/api/v1), the reflected URL becomes http://api-proxy:10002/api/v1/models instead of http://api-proxy:10002/models.

  • Extracted a modelsPath closure variable shared by both methods to avoid duplication.

Example: with COPILOT_PROVIDER_BASE_URL=https://openrouter.ai/api/v1 and COPILOT_API_KEY=<key>:

// Before: returned null → cachedModels.copilot never populated
// After:
adapter.getModelsFetchConfig()
// → { url: 'https://openrouter.ai/api/v1/models', opts: { headers: { Authorization: '******' } }, cacheKey: 'copilot' }

adapter.getReflectionInfo().models_url
// → 'http://api-proxy:10002/api/v1/models'  (was '/models')

containers/api-proxy/server.test.js

Added tests covering: adapter-based fetchStartupModels with BYOK + custom target, getModelsFetchConfig() for all auth/target combinations, and getReflectionInfo() base-path-aware models_url.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.example.com
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/.bin/jest server.test.js (dns block)
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node /opt/hostedtoolcache/node/20.20.2/x64/bin/node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/jest-worker/build/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Fix API proxy rejection of mismatched BYOK models fix(api-proxy): fetch models from BYOK custom providers and fix models_url in reflect May 7, 2026
Copilot AI requested a review from lpcox May 7, 2026 18:19
Copilot finished work on behalf of lpcox May 7, 2026 18:19
@lpcox lpcox marked this pull request as ready for review May 7, 2026 18:29
@lpcox lpcox requested a review from Mossaka as a code owner May 7, 2026 18:29
Copilot AI review requested due to automatic review settings May 7, 2026 18:29
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 87.48% 87.55% 📈 +0.07%
Statements 87.44% 87.51% 📈 +0.07%
Functions 82.66% 82.66% ➡️ +0.00%
Branches 79.65% 79.69% 📈 +0.04%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/container-lifecycle.ts 87.1% → 88.2% (+1.14%) 87.5% → 88.6% (+1.11%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes Copilot BYOK + custom provider startup model discovery in the api-proxy sidecar, and corrects the reflected models_url to include COPILOT_API_BASE_PATH so downstream tooling can fetch models from the right endpoint.

Changes:

  • Updated Copilot provider adapter to fetch /models at startup for custom (BYOK) targets and to reflect a base-path-aware models_url.
  • Added Jest coverage for Copilot adapter model-fetch configuration, base-path-aware reflection, and adapter-driven fetchStartupModels caching behavior.
Show a summary per file
File Description
containers/api-proxy/providers/copilot.js Adds base-path-aware models_url and enables startup model fetch for BYOK custom targets via adapter config.
containers/api-proxy/server.test.js Adds tests for adapter-based startup model fetching and Copilot adapter getModelsFetchConfig() / getReflectionInfo() behavior.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 4

// Pre-computed models path used by getModelsFetchConfig and getReflectionInfo.
// For BYOK/custom providers the base path prefix is included (e.g. /api/v1/models
// for COPILOT_PROVIDER_BASE_URL=https://openrouter.ai/api/v1).
const modelsPath = basePath ? `${basePath}/models` : '/models';
Comment on lines 244 to 276
getModelsFetchConfig() {
// Only COPILOT_GITHUB_TOKEN is accepted by the /models endpoint
if (!githubToken) return null;
if (!authToken) return null;

// Standard Copilot API (api.githubcopilot.com):
// The /models endpoint only accepts GitHub OAuth tokens (COPILOT_GITHUB_TOKEN).
// Skip startup model fetch when only a BYOK API key is configured.
if (rawTarget === 'api.githubcopilot.com') {
if (!githubToken) return null;
return {
url: `https://${rawTarget}/models`,
opts: {
method: 'GET',
headers: {
'Authorization': `Bearer ${githubToken}`,
'Copilot-Integration-Id': integrationId,
},
},
cacheKey: 'copilot',
};
}

// BYOK / custom provider (e.g. OpenRouter):
// Fetch models using the BYOK auth token so that arbitrary model names
// (e.g. "minimax/minimax-m2.5:free") are cached and visible in the reflect
// response. The pre-computed modelsPath already includes the base path prefix.
return {
url: `https://${rawTarget}/models`,
url: `https://${rawTarget}${modelsPath}`,
opts: {
method: 'GET',
headers: {
'Authorization': `Bearer ${githubToken}`,
'Copilot-Integration-Id': integrationId,
'Authorization': `Bearer ${authToken}`,
},
},
expect(config.opts.headers['Authorization']).toBe('Bearer sk-or-key');
expect(config.cacheKey).toBe('copilot');
});

Comment on lines +1570 to +1595
it('getModelsFetchConfig uses /models directly when basePath is not configured', () => {
// When no basePath is set, /models is used directly (no prefix)
const adapter = createCopilotAdapter({
COPILOT_API_KEY: 'sk-custom-key',
COPILOT_API_TARGET: 'custom.llm.example.com',
});
const config = adapter.getModelsFetchConfig();
expect(config).not.toBeNull();
expect(config.url).toBe('https://custom.llm.example.com/models');
});

it('getReflectionInfo includes /models for standard Copilot API (no base path)', () => {
const adapter = createCopilotAdapter({ COPILOT_GITHUB_TOKEN: 'ghu_token' });
const info = adapter.getReflectionInfo();
expect(info.models_url).toBe('http://api-proxy:10002/models');
});

it('getReflectionInfo includes base path in models_url for BYOK providers', () => {
const adapter = createCopilotAdapter({
COPILOT_API_KEY: 'sk-or-key',
COPILOT_API_TARGET: 'openrouter.ai',
COPILOT_API_BASE_PATH: '/api/v1',
});
const info = adapter.getReflectionInfo();
expect(info.models_url).toBe('http://api-proxy:10002/api/v1/models');
});
@github-actions github-actions Bot mentioned this pull request May 7, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 7, 2026

@copilot address the review feedback

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 7, 2026

@copilot address the review feedback

All three review issues addressed in commit 3441ba7:

  1. basePath='/'//models: Fixed by computing modelsPath as (basePath && basePath !== '/') ? \${basePath}/models` : '/models', so a bare /` base path is treated as no prefix.

  2. GitHub token leaking to third-party providers: The custom-target branch in getModelsFetchConfig() now uses apiKey (COPILOT_API_KEY) explicitly instead of authToken. If only a GitHub token is set with no BYOK key, the fetch is skipped (returns null) rather than forwarding the OAuth token to a third party.

  3. New tests added:

    • Both tokens + custom target → Authorization uses apiKey, not githubToken
    • Only GitHub token + custom target → getModelsFetchConfig() returns null
    • COPILOT_API_BASE_PATH='/' → both fetch URL and models_url produce /models not //models

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.example.com
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/.bin/jest server.test.js (dns block)
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node /opt/hostedtoolcache/node/20.20.2/x64/bin/node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/jest-worker/build/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of lpcox May 7, 2026 19:08
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🔬 Smoke Test Results

Test Result
GitHub MCP connectivity
GitHub.com HTTP ✅ (template vars not expanded; MCP reads confirmed)
File write/read ⚠️ (file path not provided — template vars unexpanded)

PR: fix(api-proxy): fetch models from BYOK custom providers and fix models_url in reflect
Author: @Copilot | Assignees: @lpcox, @Copilot

Overall: PASS (core connectivity verified)

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🔥 Smoke Test: Copilot BYOK

Test Result
GitHub MCP (list merged PRs) ✅ "chore: upgrade agentic workflows to gh-aw v0.71.5"
GitHub.com connectivity ⚠️ Pre-step data not expanded
File write/read ⚠️ Pre-step data not expanded
BYOK inference (this response)

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com

PR by @Copilot · Assignees: @lpcox, @Copilot

Overall: PASS (core BYOK path verified ✅)

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Smoke Test Results

✅ GitHub MCP: Last 2 merged PRs retrieved

✅ Playwright: Navigated to github.com, title verified
✅ File Writing: Test file created successfully
✅ Bash Tool: File verified with cat

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Smoke Test

✅ GitHub PR review: chore: upgrade agentic workflows to gh-aw v0.71.5; refactor: eliminate duplicate code patterns
❌ safeinputs-gh query: unavailable; fallback titles fix(api-proxy): fetch models from BYOK custom providers and fix models_url in reflect; feat: replace Playwright MCP container with pre-installed @playwright/cli in agent image
✅ Playwright: title contains GitHub
❌ Tavily: tool list unavailable
✅ File write/bash: verified
✅ Discussion comment: posted
✅ Build: npm ci && npm run build
Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #2699 · ● 482.2K ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Chroot Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3 ❌ NO
Node.js v24.14.1 v20.20.2 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: FAILED — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Smoke Test Results

  • Redis PING: ❌ timeout/no response
  • PostgreSQL pg_isready: ❌ no response (host.docker.internal:5432 - no response)
  • PostgreSQL SELECT 1: ❌ skipped (pg_isready failed)

Overall: FAILhost.docker.internal is not reachable from this runner environment. Service containers may not be configured or the host alias is unavailable.

🔌 Service connectivity validated by Smoke Services

@lpcox lpcox merged commit feddd88 into main May 7, 2026
65 of 68 checks passed
@lpcox lpcox deleted the copilot/fix-api-proxy-bad-request branch May 7, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API proxy rejects BYOK models that don't match configured model routing patterns

3 participants