fix(providers): restored copilot business vision
Honor GitHub Copilot /models vision support on Business and Enterprise endpoints and remove the stale snapcompact non-personal-host block. Fixes #4779
This commit is contained in:
@@ -526,10 +526,7 @@ describe("github copilot vision endpoint policy", () => {
|
||||
enterpriseUrl: "ghe.example.com",
|
||||
});
|
||||
|
||||
it("strips vision when discovery resolves to the business endpoint, even though upstream reports it", async () => {
|
||||
// `api.business.githubcopilot.com` responds `400 vision is not supported`
|
||||
// on image inputs (issue #3387), so the catalog MUST ignore the upstream's
|
||||
// `supports.vision = true` flag for non-personal hosts.
|
||||
it("keeps vision when discovery resolves to the business endpoint and upstream reports it", async () => {
|
||||
const { models } = await discoverCopilotModels(
|
||||
{
|
||||
data: [
|
||||
@@ -548,10 +545,10 @@ describe("github copilot vision endpoint policy", () => {
|
||||
);
|
||||
const model = models.find(candidate => candidate.id === "claude-sonnet-4.6");
|
||||
expect(model?.baseUrl).toBe("https://api.business.githubcopilot.com");
|
||||
expect(model?.input).toEqual(["text"]);
|
||||
expect(model?.input).toEqual(["text", "image"]);
|
||||
});
|
||||
|
||||
it("strips vision when discovery resolves to an enterprise host", async () => {
|
||||
it("keeps vision when discovery resolves to an enterprise host and upstream reports it", async () => {
|
||||
const { models } = await discoverCopilotModels(
|
||||
{
|
||||
data: [
|
||||
@@ -570,7 +567,42 @@ describe("github copilot vision endpoint policy", () => {
|
||||
);
|
||||
const model = models.find(candidate => candidate.id === "claude-sonnet-4.6");
|
||||
expect(model?.baseUrl).toBe("https://copilot-api.ghe.example.com");
|
||||
expect(model?.input).toEqual(["text"]);
|
||||
expect(model?.input).toEqual(["text", "image"]);
|
||||
});
|
||||
|
||||
it("maps explicit upstream vision false to text-only on non-personal Copilot endpoints", async () => {
|
||||
for (const endpoint of [
|
||||
{
|
||||
apiKey: businessApiKey,
|
||||
baseUrl: "https://api.business.githubcopilot.com",
|
||||
token: "ghu_business_token",
|
||||
},
|
||||
{
|
||||
apiKey: enterpriseApiKey,
|
||||
baseUrl: "https://copilot-api.ghe.example.com",
|
||||
token: "ghu_enterprise_token",
|
||||
},
|
||||
]) {
|
||||
const { models } = await discoverCopilotModels(
|
||||
{
|
||||
data: [
|
||||
tieredCopilotEntry({
|
||||
id: "claude-sonnet-4.6",
|
||||
name: "Claude Sonnet 4.6",
|
||||
window: 200_000,
|
||||
maxOutput: 32_000,
|
||||
vision: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
endpoint.apiKey,
|
||||
endpoint.baseUrl,
|
||||
endpoint.token,
|
||||
);
|
||||
const model = models.find(candidate => candidate.id === "claude-sonnet-4.6");
|
||||
expect(model?.baseUrl).toBe(endpoint.baseUrl);
|
||||
expect(model?.input).toEqual(["text"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps vision on the canonical personal Copilot endpoint", async () => {
|
||||
@@ -590,11 +622,11 @@ describe("github copilot vision endpoint policy", () => {
|
||||
expect(model?.input).toEqual(["text", "image"]);
|
||||
});
|
||||
|
||||
it("downgrades the merged Model to text-only when business discovery overrides a vision-capable bundled reference", async () => {
|
||||
// Bundled `claude-sonnet-4.6` ships with `input=['text','image']` and the
|
||||
// canonical baseUrl. Discovery against the business host hands back a
|
||||
// dynamic entry with the business baseUrl; the merge MUST honour the
|
||||
// dynamic side's text-only capability instead of OR-upgrading.
|
||||
it("keeps the merged Model image-capable when business discovery confirms a vision-capable bundled reference", async () => {
|
||||
// Bundled `claude-sonnet-4.6` ships with `input=['text','image']`.
|
||||
// Discovery against the business host confirms the same upstream vision
|
||||
// capability; the full manager merge must preserve image input instead
|
||||
// of downgrading solely because the baseUrl is non-personal.
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-ai-copilot-vision-"));
|
||||
try {
|
||||
const fetchMock = vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
|
||||
@@ -629,7 +661,7 @@ describe("github copilot vision endpoint policy", () => {
|
||||
const { models } = await manager.refresh("online");
|
||||
const model = models.find(candidate => candidate.id === "claude-sonnet-4.6");
|
||||
expect(model?.baseUrl).toBe("https://api.business.githubcopilot.com");
|
||||
expect(model?.input).toEqual(["text"]);
|
||||
expect(model?.input).toEqual(["text", "image"]);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user