mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 00:44:31 +00:00
fix(models): antigravity opus 4.6 availability follow-up (#12845)
* fix(models): antigravity opus 4.6 availability follow-up * chore(format): apply updated oxfmt config to models files * fix(models): retain zai glm-5 forward-compat fallback after extraction * chore(format): apply updated oxfmt config * fix(models): fail fast on unknown auth login provider --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
|
||||
import type { ModelRegistry } from "../../agents/pi-model-discovery.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { ModelRow } from "./list.types.js";
|
||||
import { resolveOpenClawAgentDir } from "../../agents/agent-paths.js";
|
||||
@@ -9,9 +10,14 @@ import {
|
||||
resolveAwsSdkEnvVarName,
|
||||
resolveEnvApiKey,
|
||||
} from "../../agents/model-auth.js";
|
||||
import { resolveForwardCompatModel } from "../../agents/model-forward-compat.js";
|
||||
import { ensureOpenClawModelsJson } from "../../agents/models-config.js";
|
||||
import { ensurePiAuthJsonFromAuthProfiles } from "../../agents/pi-auth-json.js";
|
||||
import { discoverAuthStorage, discoverModels } from "../../agents/pi-model-discovery.js";
|
||||
import {
|
||||
formatErrorWithStack,
|
||||
MODEL_AVAILABILITY_UNAVAILABLE_CODE,
|
||||
shouldFallbackToAuthHeuristics,
|
||||
} from "./list.errors.js";
|
||||
import { modelKey } from "./shared.js";
|
||||
|
||||
const isLocalBaseUrl = (baseUrl: string) => {
|
||||
@@ -30,7 +36,14 @@ const isLocalBaseUrl = (baseUrl: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const hasAuthForProvider = (provider: string, cfg: OpenClawConfig, authStore: AuthProfileStore) => {
|
||||
const hasAuthForProvider = (
|
||||
provider: string,
|
||||
cfg?: OpenClawConfig,
|
||||
authStore?: AuthProfileStore,
|
||||
) => {
|
||||
if (!cfg || !authStore) {
|
||||
return false;
|
||||
}
|
||||
if (listProfilesForProvider(authStore, provider).length > 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -46,16 +59,150 @@ const hasAuthForProvider = (provider: string, cfg: OpenClawConfig, authStore: Au
|
||||
return false;
|
||||
};
|
||||
|
||||
function createAvailabilityUnavailableError(message: string): Error {
|
||||
const err = new Error(message);
|
||||
(err as { code?: string }).code = MODEL_AVAILABILITY_UNAVAILABLE_CODE;
|
||||
return err;
|
||||
}
|
||||
|
||||
function normalizeAvailabilityError(err: unknown): Error {
|
||||
if (shouldFallbackToAuthHeuristics(err) && err instanceof Error) {
|
||||
return err;
|
||||
}
|
||||
return createAvailabilityUnavailableError(
|
||||
`Model availability unavailable: getAvailable() failed.\n${formatErrorWithStack(err)}`,
|
||||
);
|
||||
}
|
||||
|
||||
function validateAvailableModels(availableModels: unknown): Model<Api>[] {
|
||||
if (!Array.isArray(availableModels)) {
|
||||
throw createAvailabilityUnavailableError(
|
||||
"Model availability unavailable: getAvailable() returned a non-array value.",
|
||||
);
|
||||
}
|
||||
|
||||
for (const model of availableModels) {
|
||||
if (
|
||||
!model ||
|
||||
typeof model !== "object" ||
|
||||
typeof (model as { provider?: unknown }).provider !== "string" ||
|
||||
typeof (model as { id?: unknown }).id !== "string"
|
||||
) {
|
||||
throw createAvailabilityUnavailableError(
|
||||
"Model availability unavailable: getAvailable() returned invalid model entries.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return availableModels as Model<Api>[];
|
||||
}
|
||||
|
||||
function loadAvailableModels(registry: ModelRegistry): Model<Api>[] {
|
||||
let availableModels: unknown;
|
||||
try {
|
||||
availableModels = registry.getAvailable();
|
||||
} catch (err) {
|
||||
throw normalizeAvailabilityError(err);
|
||||
}
|
||||
try {
|
||||
return validateAvailableModels(availableModels);
|
||||
} catch (err) {
|
||||
throw normalizeAvailabilityError(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadModelRegistry(cfg: OpenClawConfig) {
|
||||
await ensureOpenClawModelsJson(cfg);
|
||||
const agentDir = resolveOpenClawAgentDir();
|
||||
await ensurePiAuthJsonFromAuthProfiles(agentDir);
|
||||
const authStorage = discoverAuthStorage(agentDir);
|
||||
const registry = discoverModels(authStorage, agentDir);
|
||||
const models = registry.getAll();
|
||||
const availableModels = registry.getAvailable();
|
||||
const availableKeys = new Set(availableModels.map((model) => modelKey(model.provider, model.id)));
|
||||
return { registry, models, availableKeys };
|
||||
const appended = appendAntigravityForwardCompatModels(registry.getAll(), registry);
|
||||
const models = appended.models;
|
||||
const synthesizedForwardCompat = appended.synthesizedForwardCompat;
|
||||
let availableKeys: Set<string> | undefined;
|
||||
let availabilityErrorMessage: string | undefined;
|
||||
|
||||
try {
|
||||
const availableModels = loadAvailableModels(registry);
|
||||
availableKeys = new Set(availableModels.map((model) => modelKey(model.provider, model.id)));
|
||||
for (const synthesized of synthesizedForwardCompat) {
|
||||
if (hasAvailableTemplate(availableKeys, synthesized.templatePrefixes)) {
|
||||
availableKeys.add(synthesized.key);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (!shouldFallbackToAuthHeuristics(err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Some providers can report model-level availability as unavailable.
|
||||
// Fall back to provider-level auth heuristics when availability is undefined.
|
||||
availableKeys = undefined;
|
||||
if (!availabilityErrorMessage) {
|
||||
availabilityErrorMessage = formatErrorWithStack(err);
|
||||
}
|
||||
}
|
||||
return { registry, models, availableKeys, availabilityErrorMessage };
|
||||
}
|
||||
|
||||
type SynthesizedForwardCompat = {
|
||||
key: string;
|
||||
templatePrefixes: string[];
|
||||
};
|
||||
|
||||
function appendAntigravityForwardCompatModels(
|
||||
models: Model<Api>[],
|
||||
modelRegistry: ModelRegistry,
|
||||
): { models: Model<Api>[]; synthesizedForwardCompat: SynthesizedForwardCompat[] } {
|
||||
const candidates = [
|
||||
{
|
||||
id: "claude-opus-4-6-thinking",
|
||||
templatePrefixes: [
|
||||
"google-antigravity/claude-opus-4-5-thinking",
|
||||
"google-antigravity/claude-opus-4.5-thinking",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "claude-opus-4-6",
|
||||
templatePrefixes: [
|
||||
"google-antigravity/claude-opus-4-5",
|
||||
"google-antigravity/claude-opus-4.5",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const nextModels = [...models];
|
||||
const synthesizedForwardCompat: SynthesizedForwardCompat[] = [];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const key = modelKey("google-antigravity", candidate.id);
|
||||
const hasForwardCompat = nextModels.some((model) => modelKey(model.provider, model.id) === key);
|
||||
if (hasForwardCompat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fallback = resolveForwardCompatModel("google-antigravity", candidate.id, modelRegistry);
|
||||
if (!fallback) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextModels.push(fallback);
|
||||
synthesizedForwardCompat.push({
|
||||
key,
|
||||
templatePrefixes: candidate.templatePrefixes,
|
||||
});
|
||||
}
|
||||
|
||||
return { models: nextModels, synthesizedForwardCompat };
|
||||
}
|
||||
|
||||
function hasAvailableTemplate(availableKeys: Set<string>, templatePrefixes: string[]): boolean {
|
||||
for (const key of availableKeys) {
|
||||
if (templatePrefixes.some((prefix) => key.startsWith(prefix))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function toModelRow(params: {
|
||||
@@ -83,10 +230,14 @@ export function toModelRow(params: {
|
||||
|
||||
const input = model.input.join("+") || "text";
|
||||
const local = isLocalBaseUrl(model.baseUrl);
|
||||
// Prefer model-level registry availability when present.
|
||||
// Fall back to provider-level auth heuristics only if registry availability isn't available.
|
||||
const available =
|
||||
cfg && authStore
|
||||
? hasAuthForProvider(model.provider, cfg, authStore)
|
||||
: (availableKeys?.has(modelKey(model.provider, model.id)) ?? false);
|
||||
availableKeys !== undefined
|
||||
? availableKeys.has(modelKey(model.provider, model.id))
|
||||
: cfg && authStore
|
||||
? hasAuthForProvider(model.provider, cfg, authStore)
|
||||
: false;
|
||||
const aliasTags = aliases.length > 0 ? [`alias:${aliases.join(",")}`] : [];
|
||||
const mergedTags = new Set(tags);
|
||||
if (aliasTags.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user