Skip to content

Commit 4dda346

Browse files
authored
fix(routing): filter internal Azure routing models from GitHub Copilot (#1596)
* fix(routing): filter internal Azure routing models from GitHub Copilot provider The Copilot API returns internal `accounts/msft/routers/*` entries that are Azure load-balancing infrastructure, not user-facing chat models. Add a PROVIDER_NON_CHAT filter for copilot and apply filterNonChatModels at cache-read time so previously-cached entries are also excluded. * chore: add changeset
1 parent 0c8a327 commit 4dda346

File tree

5 files changed

+49
-3
lines changed

5 files changed

+49
-3
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"manifest": patch
3+
---
4+
5+
Filter internal Azure routing models from GitHub Copilot provider

packages/backend/src/model-discovery/model-discovery.service.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,25 @@ describe('ModelDiscoveryService', () => {
523523
expect(result[0].contextWindow).toBe(128000);
524524
});
525525

526+
it('should filter out non-chat models from cached results', async () => {
527+
const providers = [
528+
makeProvider({
529+
provider: 'copilot',
530+
cached_models: [
531+
makeModel({ id: 'copilot/claude-opus-4.7', provider: 'copilot' }),
532+
makeModel({ id: 'copilot/accounts/msft/routers/f185i3v4', provider: 'copilot' }),
533+
makeModel({ id: 'copilot/accounts/msft/routers/fmfeto88', provider: 'copilot' }),
534+
],
535+
}),
536+
];
537+
providerRepo.find.mockResolvedValue(providers);
538+
customProviderRepo.find.mockResolvedValue([]);
539+
540+
const result = await service.getModelsForAgent('agent-1');
541+
expect(result).toHaveLength(1);
542+
expect(result[0].id).toBe('copilot/claude-opus-4.7');
543+
});
544+
526545
it('should deduplicate custom provider models by composite key', async () => {
527546
providerRepo.find.mockResolvedValue([]);
528547
customProviderRepo.find.mockResolvedValue([

packages/backend/src/model-discovery/model-discovery.service.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
33
import { Repository } from 'typeorm';
44
import { UserProvider } from '../entities/user-provider.entity';
55
import { CustomProvider } from '../entities/custom-provider.entity';
6-
import { ProviderModelFetcherService } from './provider-model-fetcher.service';
6+
import { ProviderModelFetcherService, filterNonChatModels } from './provider-model-fetcher.service';
77
import { ProviderModelRegistryService } from './provider-model-registry.service';
88
import { DiscoveredModel } from './model-fetcher';
99
import { decrypt, getEncryptionSecret } from '../common/utils/crypto.util';
@@ -208,8 +208,9 @@ export class ModelDiscoveryService {
208208

209209
for (const p of providers) {
210210
if (p.provider.startsWith('custom:')) continue;
211-
const cached = p.cached_models;
212-
if (!Array.isArray(cached)) continue;
211+
const rawCached = p.cached_models;
212+
if (!Array.isArray(rawCached)) continue;
213+
const cached = filterNonChatModels(rawCached, p.provider.toLowerCase());
213214
const providerAuthType = p.auth_type === 'subscription' ? 'subscription' : 'api_key';
214215
for (const m of cached) {
215216
const effectiveAuthType = m.authType ?? providerAuthType;

packages/backend/src/model-discovery/provider-model-fetcher.service.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,26 @@ describe('ProviderModelFetcherService', () => {
13471347
const result = await service.fetch('copilot', 'tid=token');
13481348
expect(result).toEqual([]);
13491349
});
1350+
1351+
it('should filter out internal Azure routing models', async () => {
1352+
fetchSpy.mockResolvedValue({
1353+
ok: true,
1354+
json: async () => ({
1355+
data: [
1356+
{ id: 'claude-opus-4.7' },
1357+
{ id: 'gpt-4o' },
1358+
{ id: 'accounts/msft/routers/f185i3v4' },
1359+
{ id: 'accounts/msft/routers/fmfeto88' },
1360+
{ id: 'accounts/msft/routers/gdjv4v2v' },
1361+
],
1362+
}),
1363+
});
1364+
1365+
const result = await service.fetch('copilot', 'tid=token');
1366+
expect(result).toHaveLength(2);
1367+
expect(result[0].id).toBe('copilot/claude-opus-4.7');
1368+
expect(result[1].id).toBe('copilot/gpt-4o');
1369+
});
13501370
});
13511371

13521372
/* ── OpenAI subscription routing ── */

packages/backend/src/model-discovery/provider-model-fetcher.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const PROVIDER_NON_CHAT: Record<string, RegExp> = {
116116
mistral:
117117
/(?:^mistral-ocr|moderation|voxtral-.*-(?:transcribe|realtime)|^labs-|^mistral-vibe-cli)/i,
118118
xai: /(?:imagine|multi-agent)/i,
119+
copilot: /accounts\/[^/]+\/routers\//i,
119120
};
120121

121122
/**

0 commit comments

Comments
 (0)