mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 13:23:43 +00:00
fix: preserve Google Gemini 3 cron thinking
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
clawsweeper
parent
6bd430ee35
commit
0cfd99f5ee
@@ -4,25 +4,15 @@ import type {
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { createGoogleThinkingStreamWrapper, isGoogleGemini3ProModel } from "./thinking-api.js";
|
||||
import { resolveGoogleThinkingProfile } from "./provider-policy.js";
|
||||
import { createGoogleThinkingStreamWrapper } from "./thinking-api.js";
|
||||
|
||||
export const GOOGLE_GEMINI_PROVIDER_HOOKS = {
|
||||
...buildProviderReplayFamilyHooks({
|
||||
family: "google-gemini",
|
||||
}),
|
||||
...buildProviderToolCompatFamilyHooks("gemini"),
|
||||
resolveThinkingProfile: ({ modelId }: ProviderDefaultThinkingPolicyContext) =>
|
||||
({
|
||||
levels: isGoogleGemini3ProModel(modelId)
|
||||
? [{ id: "off" }, { id: "low" }, { id: "adaptive" }, { id: "high" }]
|
||||
: [
|
||||
{ id: "off" },
|
||||
{ id: "minimal" },
|
||||
{ id: "low" },
|
||||
{ id: "medium" },
|
||||
{ id: "adaptive" },
|
||||
{ id: "high" },
|
||||
],
|
||||
}) satisfies ProviderThinkingProfile,
|
||||
resolveThinkingProfile: (context: ProviderDefaultThinkingPolicyContext) =>
|
||||
resolveGoogleThinkingProfile(context) satisfies ProviderThinkingProfile | undefined,
|
||||
wrapStreamFn: createGoogleThinkingStreamWrapper,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { normalizeConfig } from "./provider-policy-api.js";
|
||||
import { normalizeConfig, resolveThinkingProfile } from "./provider-policy-api.js";
|
||||
|
||||
describe("google provider policy public artifact", () => {
|
||||
it("normalizes Google provider config without loading the full provider plugin", () => {
|
||||
@@ -129,4 +129,21 @@ describe("google provider policy public artifact", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves Gemini 3 thinking levels when catalog reasoning metadata is stale", () => {
|
||||
expect(
|
||||
resolveThinkingProfile({
|
||||
provider: "google",
|
||||
modelId: "gemini-3-flash-preview",
|
||||
reasoning: false,
|
||||
})?.levels,
|
||||
).toEqual([
|
||||
{ id: "off" },
|
||||
{ id: "minimal" },
|
||||
{ id: "low" },
|
||||
{ id: "medium" },
|
||||
{ id: "adaptive" },
|
||||
{ id: "high" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { ProviderDefaultThinkingPolicyContext } from "openclaw/plugin-sdk/core";
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
|
||||
import { normalizeGoogleProviderConfig } from "./provider-policy.js";
|
||||
import { normalizeGoogleProviderConfig, resolveGoogleThinkingProfile } from "./provider-policy.js";
|
||||
|
||||
export function normalizeConfig(params: { provider: string; providerConfig: ModelProviderConfig }) {
|
||||
return normalizeGoogleProviderConfig(params.provider, params.providerConfig);
|
||||
}
|
||||
|
||||
export function resolveThinkingProfile(context: ProviderDefaultThinkingPolicyContext) {
|
||||
return resolveGoogleThinkingProfile(context);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type {
|
||||
ProviderDefaultThinkingPolicyContext,
|
||||
ProviderThinkingProfile,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types";
|
||||
import { normalizeAntigravityModelId, normalizeGoogleModelId } from "./model-id.js";
|
||||
import { isGoogleGemini3ProModel, isGoogleGemini3ThinkingLevelModel } from "./thinking-api.js";
|
||||
|
||||
type GoogleApiCarrier = {
|
||||
api?: string | null;
|
||||
@@ -174,3 +179,27 @@ export function normalizeGoogleProviderConfig(
|
||||
|
||||
return nextProvider;
|
||||
}
|
||||
|
||||
export function resolveGoogleThinkingProfile({
|
||||
modelId,
|
||||
reasoning,
|
||||
}: ProviderDefaultThinkingPolicyContext): ProviderThinkingProfile | undefined {
|
||||
const isGemini3ThinkingModel = isGoogleGemini3ThinkingLevelModel(modelId);
|
||||
if (reasoning === false && !isGemini3ThinkingModel) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
levels: isGoogleGemini3ProModel(modelId)
|
||||
? [{ id: "off" }, { id: "low" }, { id: "adaptive" }, { id: "high" }]
|
||||
: [
|
||||
{ id: "off" },
|
||||
{ id: "minimal" },
|
||||
{ id: "low" },
|
||||
{ id: "medium" },
|
||||
{ id: "adaptive" },
|
||||
{ id: "high" },
|
||||
],
|
||||
preserveWhenCatalogReasoningFalse: isGemini3ThinkingModel || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -192,6 +192,38 @@ describe("listThinkingLevels", () => {
|
||||
).toBe("off");
|
||||
});
|
||||
|
||||
it("preserves provider-authoritative thinking profiles over stale catalog reasoning", () => {
|
||||
providerRuntimeMocks.resolveProviderThinkingProfile.mockReturnValue({
|
||||
levels: [{ id: "off" }, { id: "minimal" }, { id: "low" }, { id: "medium" }],
|
||||
preserveWhenCatalogReasoningFalse: true,
|
||||
});
|
||||
const catalog = [
|
||||
{
|
||||
provider: "google",
|
||||
id: "gemini-3-flash-preview",
|
||||
name: "Gemini 3 Flash Preview",
|
||||
reasoning: false,
|
||||
},
|
||||
];
|
||||
|
||||
expect(
|
||||
isThinkingLevelSupported({
|
||||
provider: "google",
|
||||
model: "gemini-3-flash-preview",
|
||||
level: "low",
|
||||
catalog,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
resolveSupportedThinkingLevel({
|
||||
provider: "google",
|
||||
model: "gemini-3-flash-preview",
|
||||
level: "low",
|
||||
catalog,
|
||||
}),
|
||||
).toBe("low");
|
||||
});
|
||||
|
||||
it("passes catalog reasoning into provider thinking profiles for support checks", () => {
|
||||
providerRuntimeMocks.resolveProviderThinkingProfile.mockImplementation(({ context }) => ({
|
||||
levels:
|
||||
|
||||
@@ -166,19 +166,22 @@ export function resolveThinkingProfile(params: {
|
||||
modelId: context.modelId,
|
||||
reasoning: context.reasoning,
|
||||
};
|
||||
if (context.reasoning === false) {
|
||||
return buildOffOnlyThinkingProfile();
|
||||
}
|
||||
const pluginProfile = resolveProviderThinkingProfile({
|
||||
provider: context.normalizedProvider,
|
||||
context: providerContext,
|
||||
});
|
||||
if (pluginProfile) {
|
||||
const normalized = normalizeThinkingProfile(pluginProfile);
|
||||
if (normalized.levels.length > 0) {
|
||||
if (
|
||||
normalized.levels.length > 0 &&
|
||||
(context.reasoning !== false || pluginProfile.preserveWhenCatalogReasoningFalse === true)
|
||||
) {
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
if (context.reasoning === false) {
|
||||
return buildOffOnlyThinkingProfile();
|
||||
}
|
||||
|
||||
const defaultLevel = resolveProviderDefaultThinkingLevel({
|
||||
provider: context.normalizedProvider,
|
||||
|
||||
@@ -49,4 +49,10 @@ export type ProviderThinkingLevel = {
|
||||
export type ProviderThinkingProfile = {
|
||||
levels: ProviderThinkingLevel[] | ReadonlyArray<ProviderThinkingLevel>;
|
||||
defaultLevel?: ProviderThinkingLevelId | null;
|
||||
/**
|
||||
* Some bundled providers have model-specific thinking contracts that are more
|
||||
* current than cached generic catalog metadata. Keep this opt-in so
|
||||
* `reasoning: false` remains authoritative for ordinary catalog entries.
|
||||
*/
|
||||
preserveWhenCatalogReasoningFalse?: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user