mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(copilot): keep Gemini working without live discovery (#131143)
Declare the Gemini Chat Completions transport and request compatibility in the owning model manifest. Remove the private metadata decoration loop so static catalog selection and forward-compatible synthesis use the same declared contract.
This commit is contained in:
committed by
GitHub
parent
98ba35ab6f
commit
937a31c4a4
@@ -289,8 +289,9 @@ configured default model is never replaced.
|
||||
<Accordion title="Transport selection">
|
||||
Claude model IDs use the Anthropic Messages transport automatically.
|
||||
Gemini models use the OpenAI Chat Completions transport; GPT and o-series
|
||||
models keep the OpenAI Responses transport. OpenClaw selects the correct
|
||||
transport based on the model ref.
|
||||
models keep the OpenAI Responses transport. The bundled static catalog
|
||||
includes these transports and request compatibility settings, so Gemini
|
||||
keeps using Chat Completions when live discovery is disabled or unavailable.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Thinking levels">
|
||||
|
||||
@@ -26,10 +26,6 @@ const manifestModels = buildManifestModelProviderConfig({
|
||||
providerId: "github-copilot",
|
||||
catalog: manifestCatalog,
|
||||
}).models;
|
||||
for (const model of manifestModels) {
|
||||
model.api = resolveCopilotTransportApi(model.id);
|
||||
model.compat = { ...resolveCopilotModelCompat(model.id), ...model.compat };
|
||||
}
|
||||
|
||||
const STATIC_MODEL_OVERRIDES = new Map<string, Partial<ModelDefinitionConfig>>([
|
||||
...manifestModels.map((model) => [model.id, model] as const),
|
||||
|
||||
@@ -97,6 +97,13 @@
|
||||
{
|
||||
"id": "gemini-3.6-flash",
|
||||
"name": "Gemini 3.6 Flash",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
"maxTokensField": "max_tokens"
|
||||
},
|
||||
"input": ["text", "image"],
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
@@ -105,6 +112,13 @@
|
||||
{
|
||||
"id": "gemini-3.1-pro-preview",
|
||||
"name": "Gemini 3.1 Pro Preview",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
"maxTokensField": "max_tokens"
|
||||
},
|
||||
"input": ["text", "image"],
|
||||
"contextWindow": 1048576,
|
||||
"maxTokens": 65536,
|
||||
@@ -113,6 +127,13 @@
|
||||
{
|
||||
"id": "gemini-3.5-flash",
|
||||
"name": "Gemini 3.5 Flash",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
"maxTokensField": "max_tokens"
|
||||
},
|
||||
"status": "deprecated",
|
||||
"replacedBy": "gemini-3.6-flash",
|
||||
"input": ["text", "image"],
|
||||
@@ -123,6 +144,13 @@
|
||||
{
|
||||
"id": "gemini-2.5-pro",
|
||||
"name": "Gemini 2.5 Pro",
|
||||
"api": "openai-completions",
|
||||
"compat": {
|
||||
"supportsStore": false,
|
||||
"supportsDeveloperRole": false,
|
||||
"supportsUsageInStreaming": false,
|
||||
"maxTokensField": "max_tokens"
|
||||
},
|
||||
"status": "deprecated",
|
||||
"replacedBy": "gemini-3.1-pro-preview",
|
||||
"input": ["text", "image"],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Provider runtime contract helpers define reusable runtime tests for provider plugins.
|
||||
import { normalizeModelCatalog } from "@openclaw/model-catalog-core/model-catalog-normalize";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createPluginMetadataSnapshot } from "../../config/plugin-auto-enable.test-helpers.js";
|
||||
import type { ProviderRuntimeModel } from "../plugin-entry.js";
|
||||
import { registerProviderPlugin, requireRegisteredProvider } from "../plugin-test-runtime.js";
|
||||
import { buildManifestModelProviderConfig } from "../provider-catalog-shared.js";
|
||||
@@ -283,6 +285,95 @@ export function describeGithubCopilotProviderRuntimeContract(
|
||||
]);
|
||||
const createManifestModel = createManifestModelFactory("github-copilot", manifestCatalog);
|
||||
|
||||
it.each([
|
||||
["gemini-3.6-flash", "openai-completions", false],
|
||||
["gemini-3.1-pro-preview", "openai-completions", false],
|
||||
["gemini-3.5-flash", "openai-completions", false],
|
||||
["gemini-2.5-pro", "openai-completions", false],
|
||||
["gpt-5.6-sol", "openai-responses", false],
|
||||
["claude-sonnet-5", "anthropic-messages", false],
|
||||
["gemini-3.6-flash", "openai-completions", true],
|
||||
] as const)(
|
||||
"routes static %s through %s with discovery enabled=%s",
|
||||
async (modelId, api, discoveryEnabled) => {
|
||||
const { createBundledStaticCatalogModelResolver } =
|
||||
await import("../../agents/embedded-agent-runner/model.static-catalog.js");
|
||||
const config = {
|
||||
models: { catalogRefresh: { enabled: false } },
|
||||
plugins: {
|
||||
entries: {
|
||||
"github-copilot": { config: { discovery: { enabled: discoveryEnabled } } },
|
||||
},
|
||||
},
|
||||
};
|
||||
const metadataSnapshot = createPluginMetadataSnapshot({
|
||||
config,
|
||||
manifestRegistry: {
|
||||
diagnostics: [],
|
||||
plugins: [
|
||||
{
|
||||
id: "github-copilot",
|
||||
origin: "bundled",
|
||||
providers: ["github-copilot"],
|
||||
channels: [],
|
||||
cliBackends: [],
|
||||
skills: [],
|
||||
hooks: [],
|
||||
rootDir: "/fixtures/github-copilot",
|
||||
source: "/fixtures/github-copilot/index.js",
|
||||
manifestPath: "/fixtures/github-copilot/openclaw.plugin.json",
|
||||
modelCatalog: normalizeModelCatalog(
|
||||
{
|
||||
providers: { "github-copilot": manifestCatalog },
|
||||
discovery: { "github-copilot": "runtime" },
|
||||
},
|
||||
{ ownedProviders: new Set(["github-copilot"]) },
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const resolveModel = createBundledStaticCatalogModelResolver({
|
||||
cfg: config,
|
||||
env: {},
|
||||
metadataSnapshot,
|
||||
includeRuntimeDiscovery: true,
|
||||
});
|
||||
const model = resolveModel({ provider: "github-copilot", modelId });
|
||||
expect(model?.api).toBe(api);
|
||||
if (api === "openai-completions") {
|
||||
expect(model?.compat).toMatchObject({
|
||||
supportsStore: false,
|
||||
supportsDeveloperRole: false,
|
||||
supportsUsageInStreaming: false,
|
||||
maxTokensField: "max_tokens",
|
||||
});
|
||||
}
|
||||
const provider = requireProviderContractProvider("github-copilot");
|
||||
expect(
|
||||
provider.preferRuntimeResolvedModel?.({
|
||||
config,
|
||||
provider: "github-copilot",
|
||||
modelId,
|
||||
}),
|
||||
).toBe(discoveryEnabled);
|
||||
// A missing prepared live row also occurs when enabled discovery is unavailable.
|
||||
expect(
|
||||
provider.resolveDynamicModel?.({
|
||||
config,
|
||||
provider: "github-copilot",
|
||||
modelId,
|
||||
modelRegistry: {
|
||||
find: () => model,
|
||||
getAll: () => (model ? [model] : []),
|
||||
getAvailable: () => (model ? [model] : []),
|
||||
hasConfiguredAuth: () => false,
|
||||
},
|
||||
}),
|
||||
).toBeUndefined();
|
||||
},
|
||||
);
|
||||
|
||||
it("owns Copilot-specific forward-compat fallbacks", () => {
|
||||
const provider = requireProviderContractProvider("github-copilot");
|
||||
const expected = createManifestModel("gpt-5.4");
|
||||
|
||||
Reference in New Issue
Block a user