fix: ensure maxTokens for anthropic-messages providers in openclaw.json (#1042)
This commit is contained in:
@@ -5,6 +5,7 @@ import type { ProviderConfig } from '../../utils/secure-storage';
|
||||
import { getAllProviders, getApiKey, getDefaultProvider, getProvider } from '../../utils/secure-storage';
|
||||
import { getProviderConfig, getProviderDefaultModel } from '../../utils/provider-registry';
|
||||
import {
|
||||
ensureAnthropicMessagesModelMaxTokens,
|
||||
ensureOpenClawProviderAgentRuntimePins,
|
||||
pruneInvalidApiProviderEntries,
|
||||
removeProviderFromOpenClaw,
|
||||
@@ -626,6 +627,17 @@ export async function syncDefaultProviderToRuntime(
|
||||
logger.warn('[provider-runtime] Failed to pin embedded agent runtime for provider entries before switch:', err);
|
||||
}
|
||||
|
||||
try {
|
||||
const healed = await ensureAnthropicMessagesModelMaxTokens();
|
||||
if (healed.length > 0) {
|
||||
logger.warn(
|
||||
`[provider-runtime] Ensured anthropic-messages maxTokens for models.providers entries before switch: ${healed.join(', ')}`,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn('[provider-runtime] Failed to ensure anthropic-messages maxTokens before switch:', err);
|
||||
}
|
||||
|
||||
const ock = await resolveRuntimeProviderKey(provider);
|
||||
const providerKey = await getApiKey(providerId);
|
||||
const fallbackModels = await getProviderFallbackModelRefs(provider);
|
||||
|
||||
@@ -244,6 +244,17 @@ export const PROVIDER_DEFINITIONS: ProviderDefinition[] = [
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'MINIMAX_API_KEY',
|
||||
models: [
|
||||
{
|
||||
id: 'MiniMax-M2.7',
|
||||
name: 'MiniMax M2.7',
|
||||
reasoning: false,
|
||||
input: ['text'],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 204800,
|
||||
maxTokens: 131072,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -268,6 +279,17 @@ export const PROVIDER_DEFINITIONS: ProviderDefinition[] = [
|
||||
baseUrl: 'https://api.minimaxi.com/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'MINIMAX_CN_API_KEY',
|
||||
models: [
|
||||
{
|
||||
id: 'MiniMax-M2.7',
|
||||
name: 'MiniMax M2.7',
|
||||
reasoning: false,
|
||||
input: ['text'],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 204800,
|
||||
maxTokens: 131072,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1235,6 +1235,182 @@ function mergeProviderModels(
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenClaw 2026.5+ requires a positive `maxTokens` on each model (and can
|
||||
* fall back to provider-level `maxTokens`) when `api` is `anthropic-messages`.
|
||||
* ClawX-written entries historically only included `{ id, name }`.
|
||||
*
|
||||
* Generic Anthropic-compatible providers should not be capped at 8k by
|
||||
* default: OpenClaw's native Anthropic transport caps default requests at 32k
|
||||
* (`min(model.maxTokens, 32000)`), while high-output providers such as MiniMax
|
||||
* M2.7 advertise a larger catalog limit.
|
||||
*/
|
||||
export const ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS = 32768;
|
||||
export const MINIMAX_M27_MAX_TOKENS = 131072;
|
||||
|
||||
function resolvePositiveMaxTokens(value: unknown): number | undefined {
|
||||
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const floored = Math.floor(value);
|
||||
return floored > 0 ? floored : undefined;
|
||||
}
|
||||
|
||||
function isMiniMaxM27AnthropicEntry(
|
||||
providerKey: string | undefined,
|
||||
entry: Record<string, unknown> | undefined,
|
||||
model: Record<string, unknown> | undefined,
|
||||
): boolean {
|
||||
const normalizedProvider = (providerKey || '').toLowerCase();
|
||||
if (normalizedProvider === 'minimax' || normalizedProvider.startsWith('minimax-portal')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const baseUrl = typeof entry?.baseUrl === 'string' ? entry.baseUrl.toLowerCase() : '';
|
||||
if (baseUrl.includes('api.minimax.io') || baseUrl.includes('api.minimaxi.com')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const modelId = typeof model?.id === 'string' ? model.id.toLowerCase() : '';
|
||||
return modelId === 'minimax-m2.7' || modelId === 'minimax-m2.7-highspeed';
|
||||
}
|
||||
|
||||
function resolveAnthropicMessagesDefaultMaxTokens(
|
||||
providerKey?: string,
|
||||
entry?: Record<string, unknown>,
|
||||
model?: Record<string, unknown>,
|
||||
): number {
|
||||
if (isMiniMaxM27AnthropicEntry(providerKey, entry, model)) {
|
||||
return MINIMAX_M27_MAX_TOKENS;
|
||||
}
|
||||
return ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS;
|
||||
}
|
||||
|
||||
function ensureAnthropicMessagesModelEntry(
|
||||
model: Record<string, unknown>,
|
||||
providerKey?: string,
|
||||
entry?: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const resolved = resolvePositiveMaxTokens(model.maxTokens);
|
||||
if (resolved !== undefined) {
|
||||
if (model.maxTokens === resolved) {
|
||||
return model;
|
||||
}
|
||||
return { ...model, maxTokens: resolved };
|
||||
}
|
||||
return { ...model, maxTokens: resolveAnthropicMessagesDefaultMaxTokens(providerKey, entry, model) };
|
||||
}
|
||||
|
||||
function resolveAnthropicMessagesProviderDefaultMaxTokens(
|
||||
providerKey: string | undefined,
|
||||
entry: Record<string, unknown>,
|
||||
): number {
|
||||
if (Array.isArray(entry.models)) {
|
||||
const modelDefaults = entry.models
|
||||
.filter(isPlainRecord)
|
||||
.map((model) => resolveAnthropicMessagesDefaultMaxTokens(providerKey, entry, model));
|
||||
if (modelDefaults.length > 0) {
|
||||
return Math.max(...modelDefaults);
|
||||
}
|
||||
}
|
||||
return resolveAnthropicMessagesDefaultMaxTokens(providerKey, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure `models.providers.*` entries using `anthropic-messages` include the
|
||||
* token limits OpenClaw's transport layer requires. Returns whether `entry`
|
||||
* was modified.
|
||||
*/
|
||||
function ensureAnthropicMessagesProviderDefaults(
|
||||
entry: Record<string, unknown>,
|
||||
providerKey?: string,
|
||||
): boolean {
|
||||
if (entry.api !== 'anthropic-messages') {
|
||||
return false;
|
||||
}
|
||||
|
||||
let modified = false;
|
||||
|
||||
if (resolvePositiveMaxTokens(entry.maxTokens) === undefined) {
|
||||
entry.maxTokens = resolveAnthropicMessagesProviderDefaultMaxTokens(providerKey, entry);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (Array.isArray(entry.models)) {
|
||||
const nextModels = (entry.models as Array<Record<string, unknown>>).map((model) => {
|
||||
if (!isPlainRecord(model)) {
|
||||
return model;
|
||||
}
|
||||
const next = ensureAnthropicMessagesModelEntry(model, providerKey, entry);
|
||||
if (next !== model) {
|
||||
modified = true;
|
||||
}
|
||||
return next;
|
||||
});
|
||||
entry.models = nextModels;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
function healAnthropicMessagesMaxTokensInConfig(config: Record<string, unknown>): boolean {
|
||||
const models = (config.models || {}) as Record<string, unknown>;
|
||||
const providers = (models.providers || {}) as Record<string, unknown>;
|
||||
let modified = false;
|
||||
|
||||
for (const [providerKey, entry] of Object.entries(providers)) {
|
||||
if (!isPlainRecord(entry)) {
|
||||
continue;
|
||||
}
|
||||
if (ensureAnthropicMessagesProviderDefaults(entry, providerKey)) {
|
||||
providers[providerKey] = entry;
|
||||
modified = true;
|
||||
console.log(
|
||||
`[openclaw-auth] Ensured anthropic-messages maxTokens defaults for models.providers.${providerKey}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
models.providers = providers;
|
||||
config.models = models;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Self-heal helper: walk `models.providers.*` and ensure every
|
||||
* `anthropic-messages` entry (and its model rows) has a positive `maxTokens`.
|
||||
*/
|
||||
export async function ensureAnthropicMessagesModelMaxTokens(): Promise<string[]> {
|
||||
const healed: string[] = [];
|
||||
await withConfigLock(async () => {
|
||||
const config = await readOpenClawJson();
|
||||
const models = (config.models || {}) as Record<string, unknown>;
|
||||
const providers = (models.providers || {}) as Record<string, unknown>;
|
||||
let modified = false;
|
||||
|
||||
for (const [providerKey, entry] of Object.entries(providers)) {
|
||||
if (!isPlainRecord(entry)) {
|
||||
continue;
|
||||
}
|
||||
if (ensureAnthropicMessagesProviderDefaults(entry, providerKey)) {
|
||||
providers[providerKey] = entry;
|
||||
healed.push(providerKey);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
models.providers = providers;
|
||||
config.models = models;
|
||||
await writeOpenClawJson(config);
|
||||
}
|
||||
});
|
||||
return healed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of OpenClaw `models.providers.*` keys that must be pinned to a specific
|
||||
* embedded agent harness so that OpenClaw's auto-routing policy does not
|
||||
@@ -1294,13 +1470,20 @@ function upsertOpenClawProviderEntry(
|
||||
? ((getProviderConfig(provider)?.models ?? []).map((m) => ({ ...m })) as Array<Record<string, unknown>>)
|
||||
: [];
|
||||
const runtimeModels = (options.modelIds ?? []).map((id) => ({ id, name: id }));
|
||||
let mergedModels = mergeProviderModels(registryModels, existingModels, runtimeModels);
|
||||
if (options.api === 'anthropic-messages') {
|
||||
mergedModels = mergedModels.map((model) => ensureAnthropicMessagesModelEntry(model, provider, existingProvider));
|
||||
}
|
||||
|
||||
const nextProvider: Record<string, unknown> = {
|
||||
...existingProvider,
|
||||
baseUrl: options.baseUrl,
|
||||
api: options.api,
|
||||
models: mergeProviderModels(registryModels, existingModels, runtimeModels),
|
||||
models: mergedModels,
|
||||
};
|
||||
if (options.api === 'anthropic-messages') {
|
||||
ensureAnthropicMessagesProviderDefaults(nextProvider, provider);
|
||||
}
|
||||
if (options.apiKeyEnv) nextProvider.apiKey = options.apiKeyEnv;
|
||||
if (options.headers !== undefined) {
|
||||
if (Object.keys(options.headers).length > 0) {
|
||||
@@ -1883,7 +2066,13 @@ export async function batchSyncConfigFields(token: string): Promise<void> {
|
||||
type AgentModelProviderEntry = {
|
||||
baseUrl?: string;
|
||||
api?: string;
|
||||
models?: Array<{ id: string; name: string; cost?: PiAiModelCostRates }>;
|
||||
models?: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
cost?: PiAiModelCostRates;
|
||||
maxTokens?: number;
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
apiKey?: string;
|
||||
/** When true, pi-ai sends Authorization: Bearer instead of x-api-key */
|
||||
authHeader?: boolean;
|
||||
@@ -1930,6 +2119,7 @@ async function updateModelsJsonProviderEntriesForAgents(
|
||||
if (mergedModels.length > 0) existing.models = mergedModels;
|
||||
if (entry.apiKey !== undefined) existing.apiKey = entry.apiKey;
|
||||
if (entry.authHeader !== undefined) existing.authHeader = entry.authHeader;
|
||||
ensureAnthropicMessagesProviderDefaults(existing, providerType);
|
||||
|
||||
providers[providerType] = existing;
|
||||
data.providers = providers;
|
||||
@@ -2652,6 +2842,10 @@ export async function sanitizeOpenClawConfig(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (healAnthropicMessagesMaxTokensInConfig(config)) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
await writeOpenClawJson(config);
|
||||
console.log('[sanitize] openclaw.json sanitized successfully');
|
||||
|
||||
@@ -1330,6 +1330,208 @@ describe('assertValidApiProtocol guard at write sites', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('anthropic-messages maxTokens', () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.restoreAllMocks();
|
||||
await rm(testHome, { recursive: true, force: true });
|
||||
await rm(testUserData, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('adds maxTokens when syncProviderConfigToOpenClaw writes anthropic-messages providers', async () => {
|
||||
await writeOpenClawJson({ models: { providers: {} } });
|
||||
|
||||
const { syncProviderConfigToOpenClaw, MINIMAX_M27_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await syncProviderConfigToOpenClaw('minimax-portal', 'MiniMax-M2.7', {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'minimax-oauth',
|
||||
});
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const provider = (result.models as Record<string, unknown>).providers as Record<string, unknown>;
|
||||
const entry = provider['minimax-portal'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models).toHaveLength(1);
|
||||
expect(models[0]?.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
});
|
||||
|
||||
it('adds maxTokens for custom providers using anthropic-messages', async () => {
|
||||
await writeOpenClawJson({ models: { providers: {} } });
|
||||
|
||||
const { syncProviderConfigToOpenClaw, ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await syncProviderConfigToOpenClaw('custom-a1b2c3d4', 'my-claude-proxy', {
|
||||
baseUrl: 'https://example.com/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'CUSTOM_API_KEY',
|
||||
});
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const provider = (result.models as Record<string, unknown>).providers as Record<string, unknown>;
|
||||
const entry = provider['custom-a1b2c3d4'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS);
|
||||
expect(models[0]?.maxTokens).toBe(ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS);
|
||||
});
|
||||
|
||||
it('does not inject maxTokens for openai-completions providers', async () => {
|
||||
await writeOpenClawJson({ models: { providers: {} } });
|
||||
|
||||
const { syncProviderConfigToOpenClaw } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await syncProviderConfigToOpenClaw('custom-a1b2c3d4', 'gpt-proxy', {
|
||||
baseUrl: 'https://example.com/v1',
|
||||
api: 'openai-completions',
|
||||
apiKeyEnv: 'CUSTOM_API_KEY',
|
||||
});
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const provider = (result.models as Record<string, unknown>).providers as Record<string, unknown>;
|
||||
const entry = provider['custom-a1b2c3d4'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBeUndefined();
|
||||
expect(models[0]?.maxTokens).toBeUndefined();
|
||||
});
|
||||
|
||||
it('heals legacy anthropic-messages entries missing maxTokens', async () => {
|
||||
await writeOpenClawJson({
|
||||
models: {
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
models: [{ id: 'MiniMax-M2.7', name: 'MiniMax-M2.7' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { ensureAnthropicMessagesModelMaxTokens, MINIMAX_M27_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
const healed = await ensureAnthropicMessagesModelMaxTokens();
|
||||
|
||||
expect(healed).toEqual(['minimax-portal']);
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const entry = ((result.models as Record<string, unknown>).providers as Record<string, unknown>)['minimax-portal'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models[0]?.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
});
|
||||
|
||||
it('preserves a valid user-configured maxTokens value', async () => {
|
||||
await writeOpenClawJson({
|
||||
models: {
|
||||
providers: {
|
||||
'custom-a1b2c3d4': {
|
||||
baseUrl: 'https://example.com/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
maxTokens: 4096,
|
||||
models: [{ id: 'claude-proxy', name: 'claude-proxy', maxTokens: 12288 }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { ensureAnthropicMessagesModelMaxTokens } = await import('@electron/utils/openclaw-auth');
|
||||
const healed = await ensureAnthropicMessagesModelMaxTokens();
|
||||
|
||||
expect(healed).toEqual([]);
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const entry = ((result.models as Record<string, unknown>).providers as Record<string, unknown>)['custom-a1b2c3d4'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(4096);
|
||||
expect(models[0]?.maxTokens).toBe(12288);
|
||||
});
|
||||
|
||||
it('repairs invalid zero maxTokens during sanitizeOpenClawConfig', async () => {
|
||||
await writeOpenClawJson({
|
||||
models: {
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
models: [{ id: 'MiniMax-M2.7', name: 'MiniMax-M2.7', maxTokens: 0 }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { sanitizeOpenClawConfig, MINIMAX_M27_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
await sanitizeOpenClawConfig();
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const entry = ((result.models as Record<string, unknown>).providers as Record<string, unknown>)['minimax-portal'] as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models[0]?.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
});
|
||||
|
||||
it('adds maxTokens to agent models.json for anthropic-messages providers', async () => {
|
||||
await writeOpenClawJson({ agents: { list: [{ id: 'main', name: 'Main' }] } });
|
||||
|
||||
const { updateAgentModelProvider, MINIMAX_M27_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await updateAgentModelProvider('minimax-portal', {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
authHeader: true,
|
||||
apiKey: 'minimax-oauth',
|
||||
models: [{ id: 'MiniMax-M2.7', name: 'MiniMax-M2.7', cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } }],
|
||||
});
|
||||
|
||||
const content = await readFile(join(testHome, '.openclaw', 'agents', 'main', 'agent', 'models.json'), 'utf8');
|
||||
const result = JSON.parse(content) as Record<string, unknown>;
|
||||
const providers = result.providers as Record<string, Record<string, unknown>>;
|
||||
const entry = providers['minimax-portal'];
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models[0]?.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models[0]?.cost).toEqual({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
||||
});
|
||||
|
||||
it('repairs legacy agent models.json anthropic-messages entries during update', async () => {
|
||||
await writeOpenClawJson({ agents: { list: [{ id: 'main', name: 'Main' }] } });
|
||||
const agentDir = join(testHome, '.openclaw', 'agents', 'main', 'agent');
|
||||
await mkdir(agentDir, { recursive: true });
|
||||
await writeFile(join(agentDir, 'models.json'), JSON.stringify({
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
models: [{ id: 'MiniMax-M2.7', name: 'MiniMax-M2.7', maxTokens: 0 }],
|
||||
},
|
||||
},
|
||||
}, null, 2), 'utf8');
|
||||
|
||||
const { updateAgentModelProvider, MINIMAX_M27_MAX_TOKENS } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await updateAgentModelProvider('minimax-portal', {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
models: [{ id: 'MiniMax-M2.7', name: 'MiniMax-M2.7', cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } }],
|
||||
});
|
||||
|
||||
const content = await readFile(join(agentDir, 'models.json'), 'utf8');
|
||||
const result = JSON.parse(content) as Record<string, unknown>;
|
||||
const entry = ((result.providers as Record<string, unknown>)['minimax-portal']) as Record<string, unknown>;
|
||||
const models = entry.models as Array<Record<string, unknown>>;
|
||||
|
||||
expect(entry.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
expect(models[0]?.maxTokens).toBe(MINIMAX_M27_MAX_TOKENS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pruneInvalidApiProviderEntries', () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
|
||||
Reference in New Issue
Block a user