yymaxapi 1.0.89 → 1.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/yymaxapi.js CHANGED
@@ -134,7 +134,7 @@ const DEFAULT_API_CONFIG = {
134
134
  "codex": {
135
135
  "urlSuffix": "/codex",
136
136
  "api": "openai-completions",
137
- "contextWindow": 1050000,
137
+ "contextWindow": 1000000,
138
138
  "maxTokens": 128000,
139
139
  "providerName": "yunyi"
140
140
  }
@@ -881,6 +881,7 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
881
881
  if (!normalizedUrl.endsWith('/v1')) normalizedUrl += '/v1';
882
882
  try {
883
883
  let existing = '';
884
+ const escapedProviderKey = providerKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
884
885
  if (fs.existsSync(configPath)) {
885
886
  existing = fs.readFileSync(configPath, 'utf8');
886
887
  // 移除旧的 maxapi section(provider block)
@@ -889,13 +890,14 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
889
890
  // 移除旧的 maxapi top-level block
890
891
  const topRe = new RegExp(`${topMarker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${topMarkerEnd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n?`, 'g');
891
892
  existing = existing.replace(topRe, '');
892
- // 兼容旧版:移除散落的 model/model_provider、旧 openclaw-relay、旧 yunyi opencode 标记块
893
- existing = existing.replace(/^model\s*=\s*"[^"]*"\s*$/gm, '');
894
- existing = existing.replace(/^model_provider\s*=\s*"[^"]*"\s*$/gm, '');
893
+ // 兼容旧版:移除历史遗留的未标记 yunyi-codex provider、旧 openclaw-relay、旧 yunyi opencode 标记块
894
+ existing = existing.replace(new RegExp(`\\[model_providers\\.${escapedProviderKey}\\]\\n(?:(?!\\[)[^\\n]*\\n?)*`, 'g'), '');
895
895
  existing = existing.replace(/\[model_providers\.openclaw-relay\]\n(?:(?!\[)[^\n]*\n?)*/g, '');
896
896
  existing = existing.replace(/# >>> yunyi opencode >>>[\s\S]*?# <<< yunyi opencode <<<\n?/g, '');
897
897
  existing = existing.replace(/\n{3,}/g, '\n\n').trim();
898
898
  }
899
+ const hasExistingTopLevelModelConfig = /^model\s*=\s*"[^"]*"\s*$/m.test(existing)
900
+ || /^model_provider\s*=\s*"[^"]*"\s*$/m.test(existing);
899
901
 
900
902
  // 顶层设置(MUST 在所有 [section] 之前)
901
903
  const topBlock = [
@@ -919,7 +921,9 @@ function writeCodexConfig(baseUrl, apiKey, modelId = 'gpt-5.4') {
919
921
  // 组装:顶层设置 → 原有内容 → provider section
920
922
  let content;
921
923
  if (existing) {
922
- content = `${topBlock}\n\n${existing}\n\n${providerBlock}\n`;
924
+ content = hasExistingTopLevelModelConfig
925
+ ? `${existing}\n\n${providerBlock}\n`
926
+ : `${topBlock}\n\n${existing}\n\n${providerBlock}\n`;
923
927
  } else {
924
928
  content = `${topBlock}\n\n${providerBlock}\n`;
925
929
  }
@@ -1933,6 +1937,10 @@ const YYMAXAPI_OPENCLAW_GPT_PRIMARY = `${YYMAXAPI_OPENCLAW_GPT_PROVIDER}/gpt-5.4
1933
1937
  const YYMAXAPI_OPENCLAW_GPT_FALLBACK = `${YYMAXAPI_OPENCLAW_GPT_PROVIDER}/gpt-5.3-codex`;
1934
1938
  const YYMAXAPI_MANAGED_MAIN_NAMES = new Set(['Claude', 'claude', 'yunyi-claude', 'claude-yunyi']);
1935
1939
  const YYMAXAPI_MANAGED_GPT_NAMES = new Set(['GPT', 'Codex', 'gpt', 'yunyi-gpt', 'yunyi-codex']);
1940
+ const YYMAXAPI_MANAGED_MULTIMODAL_MODELS = {
1941
+ claude: new Set(['claude-sonnet-4-6', 'claude-opus-4-6']),
1942
+ codex: new Set(['gpt-5.4'])
1943
+ };
1936
1944
 
1937
1945
  function isYymaxapiOpenClawBuild() {
1938
1946
  return isLikelyYunyiBaseUrl(ENDPOINTS[0]?.url || '')
@@ -1982,14 +1990,21 @@ function resolveManagedYunyiModelName(type, modelId, fallbackName) {
1982
1990
  return catalog.find(model => model.id === modelId)?.name || fallbackName;
1983
1991
  }
1984
1992
 
1993
+ function getManagedYunyiModelInput(type, modelId) {
1994
+ return YYMAXAPI_MANAGED_MULTIMODAL_MODELS[type]?.has(modelId)
1995
+ ? ['text', 'image']
1996
+ : ['text'];
1997
+ }
1998
+
1985
1999
  function getManagedYunyiProviderModels(type) {
1986
2000
  const apiConfig = type === 'claude'
1987
2001
  ? { contextWindow: API_CONFIG.claude?.contextWindow || 200000, maxTokens: API_CONFIG.claude?.maxTokens || 8192 }
1988
- : { contextWindow: API_CONFIG.codex?.contextWindow || 1050000, maxTokens: API_CONFIG.codex?.maxTokens || 128000 };
2002
+ : { contextWindow: API_CONFIG.codex?.contextWindow || 1000000, maxTokens: API_CONFIG.codex?.maxTokens || 128000 };
1989
2003
 
1990
2004
  return getManagedYunyiModelCatalog(type).map(model => ({
1991
2005
  id: model.id,
1992
2006
  name: resolveManagedYunyiModelName(type, model.id, model.name),
2007
+ input: getManagedYunyiModelInput(type, model.id),
1993
2008
  contextWindow: apiConfig.contextWindow,
1994
2009
  maxTokens: apiConfig.maxTokens
1995
2010
  }));
@@ -2389,6 +2404,39 @@ function shouldManageYunyiDefaults(config) {
2389
2404
  return fallbacks.some(isManagedYunyiModelKey);
2390
2405
  }
2391
2406
 
2407
+ function shouldManageYunyiImageDefaults(config) {
2408
+ const imageModelState = normalizeDefaultModelSelection(config?.agents?.defaults?.imageModel, config);
2409
+ const primary = typeof imageModelState.primary === 'string' ? imageModelState.primary.trim() : '';
2410
+ const fallbacks = Array.isArray(imageModelState.fallbacks) ? imageModelState.fallbacks : [];
2411
+ if (!primary) return true;
2412
+ if (isManagedYunyiModelKey(primary)) return true;
2413
+ return fallbacks.some(isManagedYunyiModelKey);
2414
+ }
2415
+
2416
+ function normalizeManagedYunyiImageModel(modelState = {}, options = {}) {
2417
+ const currentPrimary = String(modelState.primary || '').trim();
2418
+ const canPreserve = !options.force && currentPrimary && isManagedYunyiModelKey(currentPrimary);
2419
+ const primary = canPreserve
2420
+ ? canonicalizeManagedYunyiModelKey(currentPrimary)
2421
+ : YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY;
2422
+
2423
+ let fallbacks = Array.isArray(modelState.fallbacks) ? modelState.fallbacks : [];
2424
+ fallbacks = [...new Set(
2425
+ fallbacks
2426
+ .map(item => {
2427
+ const text = String(item || '').trim();
2428
+ return isManagedYunyiModelKey(text) ? canonicalizeManagedYunyiModelKey(text) : text;
2429
+ })
2430
+ .filter(item => item && item !== primary)
2431
+ )];
2432
+
2433
+ if (fallbacks.length === 0) {
2434
+ fallbacks = [isManagedYunyiClaudeModelKey(primary) ? YYMAXAPI_OPENCLAW_GPT_PRIMARY : YYMAXAPI_OPENCLAW_CLAUDE_PRIMARY];
2435
+ }
2436
+
2437
+ return { primary, fallbacks };
2438
+ }
2439
+
2392
2440
  function applyManagedYunyiOpenClawLayout(config, options = {}) {
2393
2441
  if (!isYymaxapiOpenClawBuild()) {
2394
2442
  return { changed: false, applied: false, claudeAgentId: null, preservedMain: false };
@@ -2491,6 +2539,15 @@ function applyManagedYunyiOpenClawLayout(config, options = {}) {
2491
2539
  }
2492
2540
  }
2493
2541
 
2542
+ if (shouldManageYunyiImageDefaults(config)) {
2543
+ const currentImageModel = normalizeDefaultModelSelection(config.agents.defaults.imageModel, config);
2544
+ const nextImageModel = normalizeManagedYunyiImageModel(currentImageModel, options);
2545
+ if (JSON.stringify(currentImageModel) !== JSON.stringify(nextImageModel)) {
2546
+ config.agents.defaults.imageModel = nextImageModel;
2547
+ changed = true;
2548
+ }
2549
+ }
2550
+
2494
2551
  return { changed, applied: true, claudeAgentId, preservedMain };
2495
2552
  }
2496
2553
 
@@ -43,7 +43,7 @@
43
43
  "codex": {
44
44
  "urlSuffix": "/codex",
45
45
  "api": "openai-completions",
46
- "contextWindow": 1050000,
46
+ "contextWindow": 1000000,
47
47
  "maxTokens": 128000,
48
48
  "providerName": "yunyi"
49
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yymaxapi",
3
- "version": "1.0.89",
3
+ "version": "1.0.90",
4
4
  "description": "跨平台 OpenClaw/Clawdbot 配置管理工具 - 管理中转地址、模型切换、API Keys、测速优化",
5
5
  "main": "bin/yymaxapi.js",
6
6
  "bin": {