@hermespilot/link 0.7.3 → 0.7.4-beta.0

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.
@@ -2002,7 +2002,7 @@ var DEFAULT_HERMES_API_SERVER_HOST = "127.0.0.1";
2002
2002
  var DEFAULT_HERMES_API_SERVER_PORT = 8642;
2003
2003
  var PROFILE_API_SERVER_PORT_START = DEFAULT_HERMES_API_SERVER_PORT + 1;
2004
2004
  var PROFILE_API_SERVER_PORT_END = DEFAULT_HERMES_API_SERVER_PORT + 999;
2005
- var MODEL_CONFIG_RESTART_HINT = "\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002\u5EFA\u8BAE\u91CD\u8F7D Hermes Gateway\uFF0C\u6B63\u5728\u8FD0\u884C\u4E2D\u7684 Run \u4E0D\u4F1A\u88AB\u4E2D\u65AD\uFF0C\u65B0\u7684 Run \u4F1A\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\u3002";
2005
+ var MODEL_CONFIG_APPLIED_HINT = "\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002\u65B0\u7684 Run \u4F1A\u76F4\u63A5\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\uFF0C\u65E0\u9700\u91CD\u8F7D Hermes Gateway\u3002";
2006
2006
  var MODEL_DEFAULTS_APPLIED_HINT = "\u9ED8\u8BA4\u6A21\u578B\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002\u65B0\u7684 Run \u4F1A\u76F4\u63A5\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\uFF0C\u65E0\u9700\u91CD\u8F7D Hermes Gateway\u3002";
2007
2007
  var PROFILE_PERMISSIONS_RESTART_HINT = "\u6743\u9650\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002\u540E\u7EED\u4EE5\u8BE5 Profile \u53D1\u8D77\u7684\u65B0 Run \u4F1A\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\uFF1B\u5982\u679C\u8BE5 Profile \u7684 Gateway \u5DF2\u7ECF\u5728\u8FD0\u884C\uFF0C\u9700\u8981\u91CD\u8F7D\u5BF9\u5E94 Gateway\u3002";
2008
2008
  var PROFILE_TOOL_CONFIG_RESTART_HINT = "\u5DE5\u5177\u540E\u7AEF\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002\u540E\u7EED\u4EE5\u8BE5 Profile \u53D1\u8D77\u7684\u65B0 Run \u4F1A\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\uFF1B\u5982\u679C\u8BE5 Profile \u7684 Gateway \u5DF2\u7ECF\u5728\u8FD0\u884C\uFF0C\u9700\u8981\u91CD\u8F7D\u5BF9\u5E94 Gateway\u3002";
@@ -2386,6 +2386,7 @@ async function listHermesModelConfigs(profileName = "default", configPath = reso
2386
2386
  configPath,
2387
2387
  defaultModel,
2388
2388
  defaultReasoningEffort,
2389
+ imageInputMode: readProfileImageInputMode(config),
2389
2390
  compressionModelId,
2390
2391
  compressionModel: resolveCompressionModel(config, models),
2391
2392
  models
@@ -2396,7 +2397,7 @@ async function listHermesModelConfigCatalog(input) {
2396
2397
  const targetModels = targetProfileName ? await listHermesModelConfigs(targetProfileName).then((result) => result.models).catch(() => []) : [];
2397
2398
  const targetKeys = new Set(
2398
2399
  targetModels.map(
2399
- (model) => modelConfigKey(model.provider, model.baseUrl, model.id)
2400
+ (model) => modelConfigKey(model.provider, model.baseUrl, model.id, model.apiMode)
2400
2401
  )
2401
2402
  );
2402
2403
  const items = /* @__PURE__ */ new Map();
@@ -2410,7 +2411,12 @@ async function listHermesModelConfigCatalog(input) {
2410
2411
  continue;
2411
2412
  }
2412
2413
  for (const model of listed.models) {
2413
- const key = modelConfigKey(model.provider, model.baseUrl, model.id);
2414
+ const key = modelConfigKey(
2415
+ model.provider,
2416
+ model.baseUrl,
2417
+ model.id,
2418
+ model.apiMode
2419
+ );
2414
2420
  const existing = items.get(key);
2415
2421
  if (existing) {
2416
2422
  if (!existing.sourceProfiles.some(
@@ -2449,6 +2455,7 @@ async function listHermesModelConfigCatalog(input) {
2449
2455
  ...model.reasoningEffort ? { reasoningEffort: model.reasoningEffort } : {},
2450
2456
  reasoningSupport: model.reasoningSupport,
2451
2457
  supportedReasoningEfforts: model.supportedReasoningEfforts,
2458
+ supportsVision: model.supportsVision,
2452
2459
  sourceProfiles: [
2453
2460
  {
2454
2461
  name: profileName,
@@ -2480,12 +2487,24 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
2480
2487
  });
2481
2488
  const { document, config, existingRaw } = await readHermesConfigDocument(targetConfigPath);
2482
2489
  const targetEnv = await readHermesEnvFile(targetProfileName);
2483
- const customProviders = ensureCustomProvidersList(config);
2484
- const targetEntryIndex = findCustomProviderIndexByEndpoint(customProviders, {
2490
+ const providers = ensureProvidersRecordWithLegacyMigration(config);
2491
+ const targetProviderKey = findProviderConfigKeyByVisibleEndpoint(providers, {
2492
+ providerName: source.model.providerName,
2493
+ baseUrl: source.model.baseUrl,
2494
+ apiMode: source.model.apiMode
2495
+ }) ?? findProviderConfigKeyByEndpoint(providers, {
2485
2496
  provider: source.model.provider,
2486
- baseUrl: source.model.baseUrl
2487
- });
2488
- const entry = targetEntryIndex >= 0 ? toRecord(customProviders[targetEntryIndex]) : {};
2497
+ baseUrl: source.model.baseUrl,
2498
+ apiMode: source.model.apiMode
2499
+ }) ?? uniqueProviderConfigKey(
2500
+ providerConfigKeyBase(
2501
+ source.model.provider,
2502
+ source.model.providerName,
2503
+ source.model.baseUrl
2504
+ ),
2505
+ providers
2506
+ );
2507
+ const entry = toRecord(providers[targetProviderKey]);
2489
2508
  const entryHadModels = readEntryModelIds(entry).length > 0;
2490
2509
  const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
2491
2510
  const existingInlineApiKey = readInlineApiKey(readString2(entry.api_key));
@@ -2498,14 +2517,26 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
2498
2517
  if (sourceApiKey && keyEnv && !existingInlineApiKey && (!existingKeyEnv || !targetEnv[keyEnv]?.trim())) {
2499
2518
  await writeHermesEnvValue(targetProfileName, keyEnv, sourceApiKey);
2500
2519
  }
2501
- entry.name = readString2(entry.name) ?? readString2(entry.provider_name) ?? source.model.providerName;
2502
- entry.provider_key = source.model.provider;
2503
- entry.base_url = source.model.baseUrl;
2520
+ writeProviderEndpointConfig(
2521
+ entry,
2522
+ {
2523
+ id: source.model.id,
2524
+ provider: targetProviderKey,
2525
+ providerName: readString2(entry.name) ?? readString2(entry.provider_name) ?? source.model.providerName,
2526
+ baseUrl: source.model.baseUrl,
2527
+ apiMode: source.model.apiMode,
2528
+ contextLength: source.model.contextLength,
2529
+ keyEnv,
2530
+ setDefault: input.setDefault,
2531
+ ...source.model.reasoningEffort ? { reasoningEffort: source.model.reasoningEffort } : {},
2532
+ supportsVision: source.model.supportsVision
2533
+ },
2534
+ keyEnv
2535
+ );
2536
+ ensureEntryModelConfig(entry, source.model.id, source.model.id);
2504
2537
  if (!entryHadModels) {
2505
- entry.model = source.model.id;
2538
+ entry.default_model = source.model.id;
2506
2539
  }
2507
- entry.api_mode = source.model.apiMode;
2508
- addEntryModel(entry, source.model.id);
2509
2540
  if (source.model.contextLength) {
2510
2541
  writeEntryModelContextLength(
2511
2542
  entry,
@@ -2515,43 +2546,39 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
2515
2546
  } else if (!entryHadModels) {
2516
2547
  delete entry.context_length;
2517
2548
  }
2518
- if (keyEnv) {
2519
- entry.key_env = keyEnv;
2549
+ if (!keyEnv && !existingInlineApiKey) {
2520
2550
  delete entry.api_key;
2521
- } else {
2522
- delete entry.key_env;
2523
- if (!existingInlineApiKey) {
2524
- delete entry.api_key;
2525
- }
2526
2551
  }
2527
2552
  writeEntryModelReasoningEffort(
2528
2553
  entry,
2529
2554
  source.model.id,
2530
2555
  source.model.reasoningEffort
2531
2556
  );
2532
- if (targetEntryIndex >= 0) {
2533
- customProviders[targetEntryIndex] = entry;
2534
- } else {
2535
- customProviders.push(entry);
2536
- }
2557
+ writeEntryModelSupportsVision(
2558
+ entry,
2559
+ source.model.id,
2560
+ source.model.supportsVision
2561
+ );
2562
+ providers[targetProviderKey] = entry;
2537
2563
  const modelConfig = ensureRecord(config, "model");
2538
2564
  const currentDefaultConfig = readModelConfig(modelConfig);
2539
2565
  const currentDefaultReasoningEffort = readProfileReasoningEffort(config);
2540
2566
  if (input.setDefault || !currentDefaultConfig.model) {
2541
2567
  if (input.setDefault && currentDefaultConfig.model && currentDefaultConfig.model !== source.model.id) {
2542
- retainModelDefaultAsCustomProvider(customProviders, {
2568
+ retainModelDefaultAsProvider(providers, {
2543
2569
  ...currentDefaultConfig,
2544
2570
  ...currentDefaultReasoningEffort ? { reasoningEffort: currentDefaultReasoningEffort } : {}
2545
2571
  });
2546
2572
  }
2547
2573
  writeDefaultModelConfig(modelConfig, {
2548
2574
  id: source.model.id,
2549
- provider: source.model.provider,
2575
+ provider: targetProviderKey,
2550
2576
  baseUrl: source.model.baseUrl,
2551
2577
  apiMode: source.model.apiMode,
2552
2578
  contextLength: source.model.contextLength,
2553
2579
  keyEnv
2554
2580
  });
2581
+ writeDefaultModelSupportsVision(modelConfig, source.model.supportsVision);
2555
2582
  if (source.model.reasoningEffort) {
2556
2583
  writeProfileReasoningEffort(config, source.model.reasoningEffort);
2557
2584
  }
@@ -2563,9 +2590,17 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
2563
2590
  existingRaw
2564
2591
  });
2565
2592
  const listed = await listHermesModelConfigs(targetProfileName, targetConfigPath);
2566
- const importedModel = listed.models.find(
2567
- (model) => model.id === source.model.id && model.provider === source.model.provider && model.baseUrl === source.model.baseUrl
2568
- ) ?? listed.models.find((model) => model.id === source.model.id);
2593
+ const importedModel = findManagedModel(listed.models, {
2594
+ id: source.model.id,
2595
+ provider: targetProviderKey,
2596
+ baseUrl: source.model.baseUrl,
2597
+ apiMode: source.model.apiMode
2598
+ }) ?? findManagedModelByVisibleIdentity(listed.models, {
2599
+ id: source.model.id,
2600
+ providerName: source.model.providerName,
2601
+ baseUrl: source.model.baseUrl,
2602
+ apiMode: source.model.apiMode
2603
+ });
2569
2604
  if (!importedModel) {
2570
2605
  throw new Error("imported model is missing from config");
2571
2606
  }
@@ -2574,44 +2609,47 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
2574
2609
  model: importedModel,
2575
2610
  sourceProfileName,
2576
2611
  backupPath,
2577
- requiresGatewayReload: true,
2578
- restartHint: MODEL_CONFIG_RESTART_HINT
2612
+ requiresGatewayReload: false,
2613
+ restartHint: MODEL_CONFIG_APPLIED_HINT
2579
2614
  };
2580
2615
  }
2581
2616
  async function saveHermesModelConfig(input, profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
2582
2617
  const normalized = normalizeModelConfigInput(input);
2583
2618
  const shouldUpdateReasoningEffort = input.reasoningEffort !== void 0;
2584
2619
  const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
2585
- const customProviders = ensureCustomProvidersList(config);
2620
+ const providers = ensureProvidersRecordWithLegacyMigration(config);
2586
2621
  const originalModelId = input.originalModelId?.trim() || normalized.id;
2587
- const index = findCustomProviderIndex(customProviders, originalModelId);
2588
- const entry = index >= 0 ? toRecord(customProviders[index]) : {};
2622
+ const originalProvider = input.originalProvider?.trim();
2623
+ const originalBaseUrl = input.originalBaseUrl?.trim();
2624
+ const originalApiMode = input.originalApiMode?.trim();
2625
+ const existingProviderKey = findProviderConfigKeyByModelIdentity(providers, {
2626
+ id: originalModelId,
2627
+ provider: originalProvider,
2628
+ baseUrl: originalBaseUrl,
2629
+ apiMode: originalApiMode
2630
+ }) ?? (originalProvider && originalBaseUrl ? findProviderConfigKeyByEndpoint(providers, {
2631
+ provider: originalProvider,
2632
+ baseUrl: originalBaseUrl,
2633
+ apiMode: originalApiMode
2634
+ }) : null) ?? findProviderConfigKeyByVisibleEndpoint(providers, normalized) ?? findProviderConfigKeyByEndpoint(providers, normalized);
2635
+ const providerKey = existingProviderKey ?? uniqueProviderConfigKey(
2636
+ providerConfigKeyBase(
2637
+ normalized.provider === "custom" ? void 0 : normalized.provider,
2638
+ normalized.providerName,
2639
+ normalized.baseUrl
2640
+ ),
2641
+ providers
2642
+ );
2643
+ const entry = toRecord(providers[providerKey]);
2589
2644
  const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
2590
2645
  const keyEnv = normalized.keyEnv ?? existingKeyEnv ?? (normalized.apiKey ? buildApiKeyEnvName(normalized.providerName, normalized.id) : void 0);
2591
2646
  if (normalized.apiKey && keyEnv) {
2592
2647
  await writeHermesEnvValue(profileName, keyEnv, normalized.apiKey);
2593
2648
  }
2594
- entry.name = normalized.providerName;
2595
- entry.provider_key = normalized.provider;
2596
- entry.base_url = normalized.baseUrl;
2597
- entry.model = normalized.id;
2598
- if (normalized.apiMode) {
2599
- entry.api_mode = normalized.apiMode;
2600
- } else {
2601
- delete entry.api_mode;
2602
- }
2603
- if (normalized.contextLength) {
2604
- entry.context_length = normalized.contextLength;
2605
- } else {
2606
- delete entry.context_length;
2607
- }
2608
- if (keyEnv) {
2609
- entry.key_env = keyEnv;
2610
- delete entry.api_key;
2611
- } else {
2612
- delete entry.key_env;
2613
- }
2614
- updateEntryModels(entry, originalModelId, normalized.id);
2649
+ writeProviderEndpointConfig(entry, normalized, keyEnv);
2650
+ ensureEntryModelConfig(entry, originalModelId, normalized.id);
2651
+ writeEntryModelContextLength(entry, normalized.id, normalized.contextLength);
2652
+ writeEntryModelSupportsVision(entry, normalized.id, input.supportsVision);
2615
2653
  if (shouldUpdateReasoningEffort) {
2616
2654
  writeEntryModelReasoningEffort(
2617
2655
  entry,
@@ -2619,18 +2657,14 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
2619
2657
  normalized.reasoningEffort
2620
2658
  );
2621
2659
  }
2622
- if (index >= 0) {
2623
- customProviders[index] = entry;
2624
- } else {
2625
- customProviders.push(entry);
2626
- }
2660
+ providers[providerKey] = entry;
2627
2661
  const modelConfig = ensureRecord(config, "model");
2628
2662
  const currentDefaultConfig = readModelConfig(modelConfig);
2629
2663
  const currentDefault = currentDefaultConfig.model;
2630
2664
  const currentDefaultReasoningEffort = readProfileReasoningEffort(config);
2631
2665
  if (normalized.setDefault || !currentDefault || currentDefault === originalModelId) {
2632
2666
  if (normalized.setDefault && currentDefault && currentDefault !== normalized.id && currentDefault !== originalModelId) {
2633
- retainModelDefaultAsCustomProvider(customProviders, {
2667
+ retainModelDefaultAsProvider(providers, {
2634
2668
  ...currentDefaultConfig,
2635
2669
  ...currentDefaultReasoningEffort ? { reasoningEffort: currentDefaultReasoningEffort } : {}
2636
2670
  });
@@ -2639,9 +2673,13 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
2639
2673
  const defaultApiKey = normalized.apiKey ?? (!defaultKeyEnv && currentDefault === originalModelId ? currentDefaultConfig.apiKey : void 0);
2640
2674
  writeDefaultModelConfig(modelConfig, {
2641
2675
  ...normalized,
2676
+ provider: providerKey,
2642
2677
  apiKey: defaultApiKey,
2643
2678
  keyEnv: defaultKeyEnv
2644
2679
  });
2680
+ if (input.supportsVision !== void 0) {
2681
+ writeDefaultModelSupportsVision(modelConfig, input.supportsVision);
2682
+ }
2645
2683
  if (shouldUpdateReasoningEffort && normalized.reasoningEffort) {
2646
2684
  writeProfileReasoningEffort(config, normalized.reasoningEffort);
2647
2685
  }
@@ -2653,7 +2691,22 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
2653
2691
  existingRaw
2654
2692
  });
2655
2693
  const listed = await listHermesModelConfigs(profileName, configPath);
2656
- const savedModel = listed.models.find((model) => model.id === normalized.id);
2694
+ const normalizedApiMode = inferApiMode(
2695
+ providerKey,
2696
+ normalized.baseUrl,
2697
+ normalized.apiMode
2698
+ );
2699
+ const savedModel = findManagedModel(listed.models, {
2700
+ id: normalized.id,
2701
+ provider: providerKey,
2702
+ baseUrl: normalized.baseUrl,
2703
+ apiMode: normalizedApiMode
2704
+ }) ?? findManagedModelByVisibleIdentity(listed.models, {
2705
+ id: normalized.id,
2706
+ providerName: normalized.providerName,
2707
+ baseUrl: normalized.baseUrl,
2708
+ apiMode: normalizedApiMode
2709
+ });
2657
2710
  if (!savedModel) {
2658
2711
  throw new Error("saved model is missing from config");
2659
2712
  }
@@ -2661,12 +2714,18 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
2661
2714
  ...listed,
2662
2715
  model: savedModel,
2663
2716
  backupPath,
2664
- requiresGatewayReload: true,
2665
- restartHint: MODEL_CONFIG_RESTART_HINT
2717
+ requiresGatewayReload: false,
2718
+ restartHint: MODEL_CONFIG_APPLIED_HINT
2666
2719
  };
2667
2720
  }
2668
- async function deleteHermesModelConfig(modelId, profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
2669
- const id = modelId.trim();
2721
+ async function deleteHermesModelConfig(input, profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
2722
+ const deleteInput = typeof input === "string" ? { id: input } : {
2723
+ id: input.id,
2724
+ provider: input.provider,
2725
+ baseUrl: input.baseUrl,
2726
+ apiMode: input.apiMode
2727
+ };
2728
+ const id = deleteInput.id.trim();
2670
2729
  if (!id) {
2671
2730
  throw new Error("model id is required");
2672
2731
  }
@@ -2680,7 +2739,7 @@ async function deleteHermesModelConfig(modelId, profileName = "default", configP
2680
2739
  readProfileReasoningEffort(config),
2681
2740
  authBackedProviders
2682
2741
  );
2683
- const deletingModel = findManagedModelById(existingModels, id);
2742
+ const deletingModel = findManagedModel(existingModels, deleteInput) ?? (hasModelIdentitySelector(deleteInput) ? void 0 : findManagedModelById(existingModels, id));
2684
2743
  if (!deletingModel) {
2685
2744
  throw new Error(`model "${id}" is not configured`);
2686
2745
  }
@@ -2694,9 +2753,19 @@ async function deleteHermesModelConfig(modelId, profileName = "default", configP
2694
2753
  "\u81F3\u5C11\u9700\u8981\u4FDD\u7559\u4E00\u4E2A\u6A21\u578B\uFF0C\u907F\u514D Hermes Agent \u6CA1\u6709\u53EF\u7528\u9ED8\u8BA4\u6A21\u578B\u3002"
2695
2754
  );
2696
2755
  }
2697
- const customProviders = ensureCustomProvidersList(config);
2698
- const nextProviders = customProviders.map((entry) => removeModelFromCustomProvider(toRecord(entry), id)).filter((entry) => entry !== null);
2699
- config.custom_providers = nextProviders;
2756
+ const providers = ensureProvidersRecordWithLegacyMigration(config);
2757
+ for (const [key, rawEntry] of Object.entries(providers)) {
2758
+ const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
2759
+ if (!entry || !providerEntryMatchesManagedModel(entry, deletingModel)) {
2760
+ continue;
2761
+ }
2762
+ const nextEntry = removeModelFromProvider(toRecord(rawEntry), id);
2763
+ if (nextEntry) {
2764
+ providers[key] = nextEntry;
2765
+ } else {
2766
+ delete providers[key];
2767
+ }
2768
+ }
2700
2769
  const modelConfig = ensureRecord(config, "model");
2701
2770
  const currentDefault = readModelConfig(modelConfig).model;
2702
2771
  if (currentDefault === id) {
@@ -2716,6 +2785,7 @@ async function deleteHermesModelConfig(modelId, profileName = "default", configP
2716
2785
  contextLength: nextDefault.contextLength,
2717
2786
  keyEnv: nextDefault.keyEnv
2718
2787
  });
2788
+ writeDefaultModelSupportsVision(modelConfig, nextDefault.supportsVision);
2719
2789
  if (nextDefault.reasoningEffort) {
2720
2790
  writeProfileReasoningEffort(config, nextDefault.reasoningEffort);
2721
2791
  }
@@ -2729,6 +2799,7 @@ async function deleteHermesModelConfig(modelId, profileName = "default", configP
2729
2799
  delete modelConfig.key_env;
2730
2800
  delete modelConfig.api_mode;
2731
2801
  delete modelConfig.context_length;
2802
+ delete modelConfig.supports_vision;
2732
2803
  }
2733
2804
  }
2734
2805
  const backupPath = await writeHermesConfigDocument({
@@ -2741,22 +2812,26 @@ async function deleteHermesModelConfig(modelId, profileName = "default", configP
2741
2812
  return {
2742
2813
  ...listed,
2743
2814
  backupPath,
2744
- requiresGatewayReload: true,
2745
- restartHint: MODEL_CONFIG_RESTART_HINT
2815
+ requiresGatewayReload: false,
2816
+ restartHint: MODEL_CONFIG_APPLIED_HINT
2746
2817
  };
2747
2818
  }
2748
2819
  async function saveHermesModelDefaults(input, profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
2749
2820
  const taskModelId = input.taskModelId?.trim();
2750
2821
  const compressionModelId = input.compressionModelId?.trim();
2822
+ const imageInputMode = input.imageInputMode ? normalizeImageInputMode(input.imageInputMode) : void 0;
2751
2823
  const reasoningEffort = input.reasoningEffort ? normalizeReasoningEffort(input.reasoningEffort) : void 0;
2752
2824
  if (input.reasoningEffort && !reasoningEffort) {
2753
2825
  throw new Error(
2754
2826
  "reasoningEffort must be none, minimal, low, medium, high or xhigh"
2755
2827
  );
2756
2828
  }
2757
- if (!taskModelId && !compressionModelId && !reasoningEffort) {
2829
+ if (input.imageInputMode && !imageInputMode) {
2830
+ throw new Error("imageInputMode must be auto, native or text");
2831
+ }
2832
+ if (!taskModelId && !compressionModelId && !reasoningEffort && !imageInputMode) {
2758
2833
  throw new Error(
2759
- "taskModelId, compressionModelId or reasoningEffort is required"
2834
+ "taskModelId, compressionModelId, reasoningEffort or imageInputMode is required"
2760
2835
  );
2761
2836
  }
2762
2837
  const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
@@ -2773,17 +2848,18 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
2773
2848
  const selected = findManagedModel(models, {
2774
2849
  id: taskModelId,
2775
2850
  provider: input.taskModelProvider,
2776
- baseUrl: input.taskModelBaseUrl
2851
+ baseUrl: input.taskModelBaseUrl,
2852
+ apiMode: input.taskModelApiMode
2777
2853
  });
2778
2854
  if (!selected) {
2779
2855
  throw new Error(`model "${taskModelId}" is not configured`);
2780
2856
  }
2781
- const customProviders = ensureCustomProvidersList(config);
2857
+ const providers = ensureProvidersRecordWithLegacyMigration(config);
2782
2858
  const modelConfig = ensureRecord(config, "model");
2783
2859
  const currentDefaultConfig = readModelConfig(modelConfig);
2784
2860
  const currentDefaultReasoningEffort = readProfileReasoningEffort(config);
2785
2861
  if (currentDefaultConfig.model && currentDefaultConfig.model !== selected.id) {
2786
- retainModelDefaultAsCustomProvider(customProviders, {
2862
+ retainModelDefaultAsProvider(providers, {
2787
2863
  ...currentDefaultConfig,
2788
2864
  ...currentDefaultReasoningEffort ? { reasoningEffort: currentDefaultReasoningEffort } : {}
2789
2865
  });
@@ -2796,6 +2872,7 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
2796
2872
  contextLength: selected.contextLength,
2797
2873
  keyEnv: selected.keyEnv
2798
2874
  });
2875
+ writeDefaultModelSupportsVision(modelConfig, selected.supportsVision);
2799
2876
  if (selected.reasoningEffort) {
2800
2877
  writeProfileReasoningEffort(config, selected.reasoningEffort);
2801
2878
  }
@@ -2803,6 +2880,9 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
2803
2880
  if (reasoningEffort) {
2804
2881
  writeProfileReasoningEffort(config, reasoningEffort);
2805
2882
  }
2883
+ if (imageInputMode) {
2884
+ writeProfileImageInputMode(config, imageInputMode);
2885
+ }
2806
2886
  if (compressionModelId) {
2807
2887
  const models = readManagedModelConfigs(
2808
2888
  config,
@@ -2814,7 +2894,8 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
2814
2894
  const selected = findManagedModel(models, {
2815
2895
  id: compressionModelId,
2816
2896
  provider: input.compressionModelProvider,
2817
- baseUrl: input.compressionModelBaseUrl
2897
+ baseUrl: input.compressionModelBaseUrl,
2898
+ apiMode: input.compressionModelApiMode
2818
2899
  });
2819
2900
  if (!selected) {
2820
2901
  throw new Error(`model "${compressionModelId}" is not configured`);
@@ -2992,6 +3073,15 @@ async function saveHermesProfileToolConfig(profileName, toolKey, input, configPa
2992
3073
  "PARALLEL_API_KEY"
2993
3074
  ]);
2994
3075
  break;
3076
+ case "vision":
3077
+ configTouched = applyVisionToolConfig(config, input.values);
3078
+ await writeToolConfigEnvValues(profileName, input.values, [
3079
+ "OPENROUTER_API_KEY",
3080
+ "OPENAI_API_KEY",
3081
+ "ANTHROPIC_API_KEY",
3082
+ "AUXILIARY_VISION_API_KEY"
3083
+ ]);
3084
+ break;
2995
3085
  case "image_gen":
2996
3086
  configTouched = applyImageGenToolConfig(config, input.values);
2997
3087
  await writeToolConfigEnvValues(profileName, input.values, [
@@ -3307,12 +3397,411 @@ async function writeHermesConfigDocument(input) {
3307
3397
  );
3308
3398
  return backupPath;
3309
3399
  }
3400
+ function readCompatibleModelProviderEntries(config) {
3401
+ const entries = [];
3402
+ const seenProviderKeys = /* @__PURE__ */ new Set();
3403
+ const seenEndpointModels = /* @__PURE__ */ new Set();
3404
+ const seenVisibleModels = /* @__PURE__ */ new Set();
3405
+ const append = (entry) => {
3406
+ const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3407
+ const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? "Custom Provider";
3408
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
3409
+ const apiMode = inferApiMode(provider, baseUrl, readString2(entry.api_mode));
3410
+ const providerKey = provider.trim().toLowerCase();
3411
+ const dedupeProviderKey = providerKey && providerKey !== "custom";
3412
+ const modelIds = readEntryModelIds(entry);
3413
+ const endpointModels = modelIds.map(
3414
+ (modelId) => modelConfigKey(provider, baseUrl, modelId, apiMode)
3415
+ );
3416
+ const visibleModels = modelIds.map(
3417
+ (modelId) => modelVisibleKey({ providerName, baseUrl, id: modelId, apiMode })
3418
+ );
3419
+ if (dedupeProviderKey && seenProviderKeys.has(providerKey)) {
3420
+ return;
3421
+ }
3422
+ if (endpointModels.length > 0 && endpointModels.every((key) => seenEndpointModels.has(key))) {
3423
+ return;
3424
+ }
3425
+ if (visibleModels.length > 0 && visibleModels.every((key) => seenVisibleModels.has(key))) {
3426
+ return;
3427
+ }
3428
+ entries.push(entry);
3429
+ if (dedupeProviderKey) {
3430
+ seenProviderKeys.add(providerKey);
3431
+ }
3432
+ for (const key of endpointModels) {
3433
+ seenEndpointModels.add(key);
3434
+ }
3435
+ for (const key of visibleModels) {
3436
+ seenVisibleModels.add(key);
3437
+ }
3438
+ };
3439
+ const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
3440
+ for (const rawEntry of customProviders) {
3441
+ append(toRecord(rawEntry));
3442
+ }
3443
+ const providers = toRecord(config.providers);
3444
+ for (const [key, rawEntry] of Object.entries(providers)) {
3445
+ const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
3446
+ if (entry) {
3447
+ append(entry);
3448
+ }
3449
+ }
3450
+ return entries;
3451
+ }
3452
+ function providerConfigToLegacyEntry(providerKey, entry) {
3453
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api);
3454
+ if (!baseUrl) {
3455
+ return null;
3456
+ }
3457
+ const normalized = {
3458
+ ...entry,
3459
+ name: readString2(entry.name) ?? providerKey,
3460
+ provider_key: providerKey,
3461
+ base_url: baseUrl
3462
+ };
3463
+ const apiMode = readString2(entry.api_mode) ?? readString2(entry.transport);
3464
+ if (apiMode) {
3465
+ normalized.api_mode = apiMode;
3466
+ }
3467
+ const defaultModel = readString2(entry.model) ?? readString2(entry.default_model);
3468
+ if (defaultModel) {
3469
+ normalized.model = defaultModel;
3470
+ }
3471
+ return normalized;
3472
+ }
3473
+ function ensureProvidersRecordWithLegacyMigration(config) {
3474
+ const providers = ensureRecord(config, "providers");
3475
+ const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
3476
+ for (const rawEntry of customProviders) {
3477
+ const entry = toRecord(rawEntry);
3478
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api);
3479
+ if (!baseUrl) {
3480
+ continue;
3481
+ }
3482
+ const providerKey = findProviderConfigKeyForLegacyMigration(providers, entry, baseUrl) ?? buildProviderConfigKey(entry, providers);
3483
+ const nextConfig = legacyEntryToProviderConfig(entry, baseUrl);
3484
+ providers[providerKey] = providers[providerKey] ? mergeProviderConfig(toRecord(providers[providerKey]), nextConfig) : nextConfig;
3485
+ }
3486
+ delete config.custom_providers;
3487
+ const providerAliases = mergeDuplicateProviderConfigs(providers);
3488
+ rewriteProviderReferences(config, providerAliases);
3489
+ return providers;
3490
+ }
3491
+ function mergeDuplicateProviderConfigs(providers) {
3492
+ const groupKeys = /* @__PURE__ */ new Map();
3493
+ const aliases = /* @__PURE__ */ new Map();
3494
+ for (const [key, rawEntry] of Object.entries(providers)) {
3495
+ const entry = toRecord(rawEntry);
3496
+ const legacyEntry = providerConfigToLegacyEntry(key, entry);
3497
+ if (!legacyEntry) {
3498
+ continue;
3499
+ }
3500
+ const groupKey = providerConfigGroupKey(legacyEntry);
3501
+ const existingKey = groupKeys.get(groupKey);
3502
+ if (!existingKey) {
3503
+ groupKeys.set(groupKey, key);
3504
+ providers[key] = entry;
3505
+ continue;
3506
+ }
3507
+ providers[existingKey] = mergeProviderConfig(
3508
+ toRecord(providers[existingKey]),
3509
+ entry
3510
+ );
3511
+ delete providers[key];
3512
+ aliases.set(key, existingKey);
3513
+ }
3514
+ return aliases;
3515
+ }
3516
+ function rewriteProviderReferences(config, aliases) {
3517
+ if (aliases.size === 0) {
3518
+ return;
3519
+ }
3520
+ const rewriteProvider = (record) => {
3521
+ const provider = readString2(record.provider);
3522
+ if (provider && aliases.has(provider)) {
3523
+ record.provider = aliases.get(provider);
3524
+ }
3525
+ };
3526
+ rewriteProvider(toRecord(config.model));
3527
+ const auxiliary = toRecord(config.auxiliary);
3528
+ rewriteProvider(toRecord(auxiliary.compression));
3529
+ rewriteProvider(toRecord(config.fallback_model));
3530
+ if (Array.isArray(config.fallback_model)) {
3531
+ for (const entry of config.fallback_model) {
3532
+ rewriteProvider(toRecord(entry));
3533
+ }
3534
+ }
3535
+ if (Array.isArray(config.fallback_providers)) {
3536
+ for (const entry of config.fallback_providers) {
3537
+ rewriteProvider(toRecord(entry));
3538
+ }
3539
+ }
3540
+ }
3541
+ function findProviderConfigKeyForLegacyMigration(providers, entry, baseUrl) {
3542
+ const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3543
+ const apiMode = readString2(entry.api_mode) ?? readString2(entry.transport);
3544
+ if (provider !== "custom" && Object.prototype.hasOwnProperty.call(providers, provider)) {
3545
+ const existingEntry = providerConfigToLegacyEntry(
3546
+ provider,
3547
+ toRecord(providers[provider])
3548
+ );
3549
+ if (existingEntry) {
3550
+ const existingBaseUrl = readString2(existingEntry.base_url) ?? readString2(existingEntry.url) ?? readString2(existingEntry.api) ?? "";
3551
+ const existingApiMode = inferApiMode(
3552
+ provider,
3553
+ existingBaseUrl,
3554
+ readString2(existingEntry.api_mode)
3555
+ );
3556
+ if (normalizeBaseUrl(existingBaseUrl) === normalizeBaseUrl(baseUrl) && existingApiMode === inferApiMode(provider, baseUrl, apiMode) && providerCredentialIdentity(existingEntry) === providerCredentialIdentity(entry)) {
3557
+ return provider;
3558
+ }
3559
+ }
3560
+ }
3561
+ return findProviderConfigKeyByEndpoint(providers, {
3562
+ provider,
3563
+ baseUrl,
3564
+ apiMode
3565
+ }) ?? findProviderConfigKeyByVisibleEndpoint(providers, {
3566
+ providerName: readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? provider,
3567
+ baseUrl,
3568
+ apiMode,
3569
+ keyEnv: readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key))
3570
+ });
3571
+ }
3572
+ function mergeProviderConfig(existing, next) {
3573
+ const merged = { ...existing };
3574
+ for (const [key, value] of Object.entries(next)) {
3575
+ if (key === "models") {
3576
+ continue;
3577
+ }
3578
+ if (merged[key] === void 0 || merged[key] === null) {
3579
+ merged[key] = value;
3580
+ }
3581
+ }
3582
+ const models = mergeEntryModelsMap(existing, next);
3583
+ if (Object.keys(models).length > 0) {
3584
+ merged.models = models;
3585
+ }
3586
+ const defaultModel = readString2(existing.default_model) ?? readString2(existing.model) ?? readString2(next.default_model) ?? readString2(next.model);
3587
+ if (defaultModel) {
3588
+ merged.default_model = defaultModel;
3589
+ delete merged.model;
3590
+ }
3591
+ return merged;
3592
+ }
3593
+ function mergeEntryModelsMap(existing, next) {
3594
+ const merged = { ...normalizeEntryModelsMap(existing) };
3595
+ for (const [id, rawNextModelConfig] of Object.entries(
3596
+ normalizeEntryModelsMap(next)
3597
+ )) {
3598
+ const existingModelConfig = toRecord(merged[id]);
3599
+ const nextModelConfig = toRecord(rawNextModelConfig);
3600
+ merged[id] = { ...nextModelConfig, ...existingModelConfig };
3601
+ }
3602
+ return merged;
3603
+ }
3604
+ function legacyEntryToProviderConfig(entry, baseUrl) {
3605
+ const next = {
3606
+ name: readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? "Custom Provider",
3607
+ api: baseUrl
3608
+ };
3609
+ const apiKey = readString2(entry.api_key);
3610
+ const keyEnv = readString2(entry.key_env) ?? parseEnvReference(apiKey);
3611
+ if (keyEnv) {
3612
+ next.key_env = keyEnv;
3613
+ } else if (apiKey) {
3614
+ next.api_key = apiKey;
3615
+ }
3616
+ const transport = readString2(entry.api_mode) ?? readString2(entry.transport);
3617
+ if (transport) {
3618
+ next.transport = transport;
3619
+ }
3620
+ const defaultModel = readString2(entry.model) ?? readString2(entry.default_model);
3621
+ if (defaultModel) {
3622
+ next.default_model = defaultModel;
3623
+ }
3624
+ const models = normalizeEntryModelsMap(entry);
3625
+ if (Object.keys(models).length > 0) {
3626
+ next.models = models;
3627
+ }
3628
+ const contextLength = readPositiveInteger(entry.context_length);
3629
+ if (contextLength) {
3630
+ next.context_length = contextLength;
3631
+ }
3632
+ const reasoningEffort = normalizeReasoningEffort(
3633
+ entry.reasoning_effort ?? entry.reasoningEffort
3634
+ );
3635
+ if (reasoningEffort) {
3636
+ next.reasoning_effort = reasoningEffort;
3637
+ }
3638
+ const supportsVision = readCapabilityBoolean(
3639
+ entry.supports_vision ?? entry.supportsVision
3640
+ );
3641
+ if (supportsVision !== void 0) {
3642
+ next.supports_vision = supportsVision;
3643
+ }
3644
+ return next;
3645
+ }
3646
+ function normalizeEntryModelsMap(entry) {
3647
+ const models = entry.models;
3648
+ if (typeof models === "object" && models !== null && !Array.isArray(models)) {
3649
+ return { ...models };
3650
+ }
3651
+ return Object.fromEntries(readEntryModelIds(entry).map((id) => [id, {}]));
3652
+ }
3653
+ function buildProviderConfigKey(entry, existing) {
3654
+ const rawProviderKey = readString2(entry.provider_key);
3655
+ const rawName = readString2(entry.name) ?? readString2(entry.provider_name);
3656
+ const rawBaseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api);
3657
+ const base = providerConfigKeyBase(
3658
+ rawProviderKey && rawProviderKey !== "custom" ? rawProviderKey : void 0,
3659
+ rawName,
3660
+ rawBaseUrl
3661
+ );
3662
+ return uniqueProviderConfigKey(base, existing);
3663
+ }
3664
+ function providerConfigKeyBase(provider, providerName, baseUrl) {
3665
+ const fromProvider = provider?.trim();
3666
+ const fromName = providerName?.trim();
3667
+ const host = baseUrl ? UriHostname.safe(baseUrl) : "";
3668
+ return slugifyProviderKey(fromProvider || fromName || host || "provider");
3669
+ }
3670
+ function uniqueProviderConfigKey(base, existing) {
3671
+ let key = base || "provider";
3672
+ let index = 2;
3673
+ while (Object.prototype.hasOwnProperty.call(existing, key)) {
3674
+ key = `${base}-${index}`;
3675
+ index += 1;
3676
+ }
3677
+ return key;
3678
+ }
3679
+ function slugifyProviderKey(value) {
3680
+ const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "");
3681
+ return slug || "provider";
3682
+ }
3683
+ function findProviderConfigKeyByModelIdentity(providers, input) {
3684
+ const provider = input.provider?.trim();
3685
+ const baseUrl = input.baseUrl?.trim();
3686
+ const apiMode = input.apiMode?.trim();
3687
+ for (const [key, rawEntry] of Object.entries(providers)) {
3688
+ const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
3689
+ if (!entry || !readEntryModelIds(entry).includes(input.id)) {
3690
+ continue;
3691
+ }
3692
+ const entryProvider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3693
+ const entryBaseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
3694
+ const entryApiMode = inferApiMode(
3695
+ entryProvider,
3696
+ entryBaseUrl,
3697
+ readString2(entry.api_mode)
3698
+ );
3699
+ if (provider && provider !== key && provider !== entryProvider) {
3700
+ continue;
3701
+ }
3702
+ if (baseUrl !== void 0 && normalizeBaseUrl(baseUrl) !== normalizeBaseUrl(entryBaseUrl)) {
3703
+ continue;
3704
+ }
3705
+ if (apiMode && apiMode !== entryApiMode) {
3706
+ continue;
3707
+ }
3708
+ return key;
3709
+ }
3710
+ return null;
3711
+ }
3712
+ function findProviderConfigKeyByVisibleEndpoint(providers, endpoint) {
3713
+ const targetProviderName = endpoint.providerName?.trim().toLowerCase();
3714
+ if (!targetProviderName) {
3715
+ return null;
3716
+ }
3717
+ const targetBaseUrl = normalizeBaseUrl(endpoint.baseUrl);
3718
+ const targetApiMode = endpoint.apiMode?.trim();
3719
+ const targetKeyEnv = endpoint.keyEnv?.trim().toLowerCase();
3720
+ for (const [key, rawEntry] of Object.entries(providers)) {
3721
+ const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
3722
+ if (!entry) {
3723
+ continue;
3724
+ }
3725
+ const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3726
+ const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? key;
3727
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
3728
+ const apiMode = inferApiMode(provider, baseUrl, readString2(entry.api_mode));
3729
+ const keyEnv = (readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key)))?.toLowerCase();
3730
+ if (providerName.trim().toLowerCase() !== targetProviderName) {
3731
+ continue;
3732
+ }
3733
+ if (normalizeBaseUrl(baseUrl) !== targetBaseUrl) {
3734
+ continue;
3735
+ }
3736
+ if (targetApiMode && targetApiMode !== apiMode) {
3737
+ continue;
3738
+ }
3739
+ if (targetKeyEnv && targetKeyEnv !== keyEnv) {
3740
+ continue;
3741
+ }
3742
+ return key;
3743
+ }
3744
+ return null;
3745
+ }
3746
+ function findProviderConfigKeyByEndpoint(providers, endpoint) {
3747
+ const targetProvider = endpoint.provider.trim().toLowerCase();
3748
+ const targetBaseUrl = normalizeBaseUrl(endpoint.baseUrl);
3749
+ const targetApiMode = endpoint.apiMode?.trim();
3750
+ for (const [key, rawEntry] of Object.entries(providers)) {
3751
+ const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
3752
+ if (!entry) {
3753
+ continue;
3754
+ }
3755
+ const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3756
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
3757
+ const apiMode = inferApiMode(provider, baseUrl, readString2(entry.api_mode));
3758
+ if (targetApiMode && targetApiMode !== apiMode) {
3759
+ continue;
3760
+ }
3761
+ if ((key.toLowerCase() === targetProvider || provider.trim().toLowerCase() === targetProvider) && normalizeBaseUrl(baseUrl) === targetBaseUrl) {
3762
+ return key;
3763
+ }
3764
+ }
3765
+ return null;
3766
+ }
3767
+ function writeProviderEndpointConfig(entry, input, keyEnv) {
3768
+ entry.name = input.providerName;
3769
+ entry.api = input.baseUrl;
3770
+ delete entry.base_url;
3771
+ delete entry.url;
3772
+ entry.default_model = input.id;
3773
+ delete entry.model;
3774
+ entry.transport = inferApiMode(input.provider, input.baseUrl, input.apiMode);
3775
+ delete entry.api_mode;
3776
+ if (input.contextLength) {
3777
+ entry.context_length = input.contextLength;
3778
+ } else {
3779
+ delete entry.context_length;
3780
+ delete entry.contextLength;
3781
+ }
3782
+ if (keyEnv) {
3783
+ entry.key_env = keyEnv;
3784
+ delete entry.api_key;
3785
+ } else {
3786
+ delete entry.key_env;
3787
+ }
3788
+ }
3789
+ var UriHostname = class {
3790
+ static safe(value) {
3791
+ try {
3792
+ return new URL(value).hostname.replace(/\./gu, "-");
3793
+ } catch {
3794
+ return "";
3795
+ }
3796
+ }
3797
+ };
3310
3798
  function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffort, authBackedProviders = EMPTY_AUTH_BACKED_PROVIDER_STATE) {
3311
3799
  const models = [];
3312
3800
  const seen = /* @__PURE__ */ new Set();
3801
+ const seenVisibleModels = /* @__PURE__ */ new Map();
3313
3802
  const seenEndpoint = /* @__PURE__ */ new Map();
3314
3803
  const modelConfig = readModelConfig(config.model);
3315
- const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
3804
+ const customProviders = readCompatibleModelProviderEntries(config);
3316
3805
  for (const rawEntry of customProviders) {
3317
3806
  const entry = toRecord(rawEntry);
3318
3807
  const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? "Custom Provider";
@@ -3329,21 +3818,30 @@ function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffo
3329
3818
  authBackedProviders
3330
3819
  );
3331
3820
  for (const id of readEntryModelIds(entry)) {
3332
- const key = modelConfigKey(provider, baseUrl, id);
3821
+ const key = modelConfigKey(provider, baseUrl, id, apiMode);
3333
3822
  if (seen.has(key)) {
3334
3823
  continue;
3335
3824
  }
3336
3825
  seen.add(key);
3337
- const endpointKey = modelEndpointKey(baseUrl, id);
3826
+ const endpointKey = modelEndpointKey(baseUrl, id, apiMode);
3338
3827
  const modelContextLength = readEntryModelContextLength(entry, id) ?? contextLength;
3339
3828
  const reasoningEffort = readEntryModelReasoningEffort(entry, id);
3829
+ const supportsVision = readEntryModelSupportsVision(entry, id);
3340
3830
  const reasoningMetadata = modelReasoningMetadata({
3341
3831
  provider,
3342
3832
  baseUrl,
3343
3833
  modelId: id,
3344
3834
  apiMode
3345
3835
  });
3346
- models.push({
3836
+ const isDefault = matchesDefaultModelConfig({
3837
+ id,
3838
+ provider,
3839
+ baseUrl,
3840
+ apiMode,
3841
+ defaultModel,
3842
+ modelConfig
3843
+ });
3844
+ const index = upsertManagedModelConfig(models, seenVisibleModels, {
3347
3845
  id,
3348
3846
  provider,
3349
3847
  providerName,
@@ -3354,32 +3852,40 @@ function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffo
3354
3852
  ...keyEnv ? { keyEnv } : {},
3355
3853
  credentialConfigured: credentialMetadata.credentialState === "configured",
3356
3854
  ...credentialMetadata,
3357
- isDefault: id === defaultModel,
3855
+ isDefault,
3358
3856
  ...reasoningEffort ? { reasoningEffort } : {},
3359
- ...reasoningMetadata
3857
+ ...reasoningMetadata,
3858
+ supportsVision
3360
3859
  });
3361
- seenEndpoint.set(endpointKey, models.length - 1);
3860
+ seenEndpoint.set(endpointKey, index);
3362
3861
  }
3363
3862
  }
3364
3863
  if (defaultModel) {
3365
3864
  const provider = modelConfig.provider ?? "default";
3366
3865
  const authDefinition = AUTH_BACKED_MODEL_PROVIDER_BY_KEY.get(provider);
3367
3866
  const baseUrl = modelConfig.baseUrl ?? authDefinition?.baseUrl ?? "";
3867
+ const defaultApiMode = inferApiMode(provider, baseUrl, modelConfig.apiMode);
3368
3868
  const endpointMatchIndex = seenEndpoint.get(
3369
- modelEndpointKey(baseUrl, defaultModel)
3869
+ modelEndpointKey(baseUrl, defaultModel, defaultApiMode)
3370
3870
  );
3371
3871
  if (endpointMatchIndex !== void 0) {
3372
3872
  const existing = models[endpointMatchIndex];
3373
3873
  models[endpointMatchIndex] = {
3374
3874
  ...existing,
3375
- apiMode: modelConfig.apiMode ? inferApiMode(provider, baseUrl, modelConfig.apiMode) : existing.apiMode,
3875
+ apiMode: defaultApiMode,
3376
3876
  contextLength: existing.contextLength ?? modelConfig.contextLength,
3377
3877
  keyEnv: existing.keyEnv ?? modelConfig.keyEnv,
3378
3878
  isDefault: true,
3379
- reasoningEffort: existing.reasoningEffort ?? defaultReasoningEffort ?? void 0
3879
+ reasoningEffort: existing.reasoningEffort ?? defaultReasoningEffort ?? void 0,
3880
+ supportsVision: existing.supportsVision ?? modelConfig.supportsVision ?? null
3380
3881
  };
3381
3882
  } else {
3382
- const key = modelConfigKey(provider, baseUrl, defaultModel);
3883
+ const key = modelConfigKey(
3884
+ provider,
3885
+ baseUrl,
3886
+ defaultModel,
3887
+ defaultApiMode
3888
+ );
3383
3889
  if (!seen.has(key)) {
3384
3890
  const credentialMetadata = readModelCredentialMetadata(
3385
3891
  modelConfig,
@@ -3388,8 +3894,9 @@ function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffo
3388
3894
  authBackedProviders
3389
3895
  );
3390
3896
  const reasoningEffort = modelConfig.reasoningEffort ?? defaultReasoningEffort;
3391
- const apiMode = inferApiMode(provider, baseUrl, modelConfig.apiMode);
3392
- models.unshift({
3897
+ const supportsVision = modelConfig.supportsVision ?? null;
3898
+ const apiMode = defaultApiMode;
3899
+ const index = upsertManagedModelConfig(models, seenVisibleModels, {
3393
3900
  id: defaultModel,
3394
3901
  provider,
3395
3902
  providerName: authDefinition?.providerName ?? provider,
@@ -3407,18 +3914,21 @@ function readManagedModelConfigs(config, env, defaultModel, defaultReasoningEffo
3407
3914
  baseUrl,
3408
3915
  modelId: defaultModel,
3409
3916
  apiMode
3410
- })
3917
+ }),
3918
+ supportsVision
3411
3919
  });
3412
3920
  seen.add(key);
3413
- seenEndpoint.set(modelEndpointKey(baseUrl, defaultModel), 0);
3921
+ seenEndpoint.set(modelEndpointKey(baseUrl, defaultModel, apiMode), index);
3414
3922
  }
3415
3923
  }
3416
3924
  }
3417
3925
  appendAuthBackedModelConfigs({
3418
3926
  models,
3419
3927
  seen,
3928
+ seenVisibleModels,
3420
3929
  seenEndpoint,
3421
3930
  defaultModel,
3931
+ modelConfig,
3422
3932
  defaultProvider: modelConfig.provider ?? null,
3423
3933
  defaultReasoningEffort,
3424
3934
  authBackedProviders
@@ -3437,46 +3947,132 @@ function appendAuthBackedModelConfigs(input) {
3437
3947
  definition.provider
3438
3948
  ) ? "external_auth" : "auth_store";
3439
3949
  for (const id of definition.modelIds) {
3440
- const key = modelConfigKey(definition.provider, baseUrl, id);
3441
- if (input.seen.has(key)) {
3442
- continue;
3443
- }
3444
- input.seen.add(key);
3445
3950
  const apiMode = inferApiMode(
3446
3951
  definition.provider,
3447
3952
  baseUrl,
3448
3953
  definition.apiMode
3449
3954
  );
3450
- const isDefault = id === input.defaultModel && definition.provider === input.defaultProvider;
3451
- input.models.push({
3955
+ const key = modelConfigKey(definition.provider, baseUrl, id, apiMode);
3956
+ if (input.seen.has(key)) {
3957
+ continue;
3958
+ }
3959
+ input.seen.add(key);
3960
+ const isDefault = matchesDefaultModelConfig({
3452
3961
  id,
3453
3962
  provider: definition.provider,
3454
- providerName: definition.providerName,
3455
- source: "auth_store",
3456
3963
  baseUrl,
3457
3964
  apiMode,
3458
- credentialConfigured: true,
3459
- credentialState: "configured",
3460
- credentialSource,
3461
- authType: definition.authType,
3462
- editable: false,
3463
- isReadOnly: true,
3464
- isDefault,
3465
- ...isDefault && input.defaultReasoningEffort ? { reasoningEffort: input.defaultReasoningEffort } : {},
3466
- ...modelReasoningMetadata({
3965
+ defaultModel: input.defaultModel,
3966
+ modelConfig: input.modelConfig
3967
+ });
3968
+ const index = upsertManagedModelConfig(
3969
+ input.models,
3970
+ input.seenVisibleModels,
3971
+ {
3972
+ id,
3467
3973
  provider: definition.provider,
3974
+ providerName: definition.providerName,
3975
+ source: "auth_store",
3468
3976
  baseUrl,
3469
- modelId: id,
3470
- apiMode
3471
- })
3472
- });
3473
- input.seenEndpoint.set(
3474
- modelEndpointKey(baseUrl, id),
3475
- input.models.length - 1
3977
+ apiMode,
3978
+ credentialConfigured: true,
3979
+ credentialState: "configured",
3980
+ credentialSource,
3981
+ authType: definition.authType,
3982
+ editable: false,
3983
+ isReadOnly: true,
3984
+ isDefault,
3985
+ ...isDefault && input.defaultReasoningEffort ? { reasoningEffort: input.defaultReasoningEffort } : {},
3986
+ ...modelReasoningMetadata({
3987
+ provider: definition.provider,
3988
+ baseUrl,
3989
+ modelId: id,
3990
+ apiMode
3991
+ }),
3992
+ supportsVision: null
3993
+ }
3476
3994
  );
3995
+ input.seenEndpoint.set(modelEndpointKey(baseUrl, id, apiMode), index);
3477
3996
  }
3478
3997
  }
3479
3998
  }
3999
+ function modelVisibleKey(model) {
4000
+ return [
4001
+ model.providerName.trim(),
4002
+ normalizeBaseUrl(model.baseUrl),
4003
+ model.id.trim(),
4004
+ model.apiMode.trim()
4005
+ ].map((value) => value.toLowerCase()).join("\n");
4006
+ }
4007
+ function upsertManagedModelConfig(models, seenVisibleModels, next) {
4008
+ const key = modelVisibleKey(next);
4009
+ const existingIndex = seenVisibleModels.get(key);
4010
+ if (existingIndex === void 0) {
4011
+ models.push(next);
4012
+ const index = models.length - 1;
4013
+ seenVisibleModels.set(key, index);
4014
+ return index;
4015
+ }
4016
+ models[existingIndex] = mergeManagedModelConfig(
4017
+ models[existingIndex],
4018
+ next
4019
+ );
4020
+ return existingIndex;
4021
+ }
4022
+ function mergeManagedModelConfig(existing, next) {
4023
+ const primary = managedModelPreferenceScore(next) > managedModelPreferenceScore(existing) ? next : existing;
4024
+ const secondary = primary === next ? existing : next;
4025
+ const credentialState = primary.credentialState === "configured" || secondary.credentialState !== "configured" ? primary.credentialState : secondary.credentialState;
4026
+ return {
4027
+ ...primary,
4028
+ contextLength: primary.contextLength ?? secondary.contextLength,
4029
+ keyEnv: primary.keyEnv ?? secondary.keyEnv,
4030
+ credentialConfigured: primary.credentialConfigured || secondary.credentialConfigured,
4031
+ credentialState,
4032
+ credentialSource: primary.credentialState === "configured" || secondary.credentialState !== "configured" ? primary.credentialSource : secondary.credentialSource,
4033
+ isDefault: primary.isDefault || secondary.isDefault,
4034
+ reasoningEffort: primary.reasoningEffort ?? secondary.reasoningEffort,
4035
+ reasoningSupport: primary.reasoningSupport === "unknown" ? secondary.reasoningSupport : primary.reasoningSupport,
4036
+ supportedReasoningEfforts: primary.supportedReasoningEfforts ?? secondary.supportedReasoningEfforts,
4037
+ supportsVision: primary.supportsVision ?? secondary.supportsVision
4038
+ };
4039
+ }
4040
+ function managedModelPreferenceScore(model) {
4041
+ return (model.isDefault ? 1e3 : 0) + (model.credentialState === "configured" ? 200 : 0) + (model.credentialState === "missing" ? 50 : 0) + (model.source === "auth_store" ? 100 : 0) + (model.credentialSource !== "unknown" ? 40 : 0) + (model.isReadOnly ? 10 : 0);
4042
+ }
4043
+ function matchesDefaultModelConfig(input) {
4044
+ if (!input.defaultModel || input.id !== input.defaultModel) {
4045
+ return false;
4046
+ }
4047
+ const defaultApiMode = input.modelConfig.apiMode?.trim();
4048
+ if (defaultApiMode && input.apiMode !== defaultApiMode) {
4049
+ return false;
4050
+ }
4051
+ const defaultBaseUrl = input.modelConfig.baseUrl;
4052
+ if (defaultBaseUrl?.trim()) {
4053
+ return normalizeBaseUrl(input.baseUrl) === normalizeBaseUrl(defaultBaseUrl);
4054
+ }
4055
+ const defaultProvider = input.modelConfig.provider?.trim();
4056
+ if (defaultProvider) {
4057
+ return input.provider === defaultProvider;
4058
+ }
4059
+ return true;
4060
+ }
4061
+ function providerEntryMatchesManagedModel(entry, model) {
4062
+ const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? "Custom Provider";
4063
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
4064
+ const apiMode = inferApiMode(
4065
+ readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom",
4066
+ baseUrl,
4067
+ readString2(entry.api_mode)
4068
+ );
4069
+ return modelVisibleKey({
4070
+ providerName,
4071
+ baseUrl,
4072
+ id: model.id,
4073
+ apiMode
4074
+ }) === modelVisibleKey(model);
4075
+ }
3480
4076
  function readEntryCredentialMetadata(entry, env, provider, authBackedProviders) {
3481
4077
  const apiKey = readString2(entry.api_key);
3482
4078
  const keyEnv = readString2(entry.key_env) ?? parseEnvReference(apiKey);
@@ -3673,6 +4269,7 @@ function findManagedModelById(models, id) {
3673
4269
  function findManagedModel(models, input) {
3674
4270
  const provider = input.provider?.trim();
3675
4271
  const baseUrl = input.baseUrl?.trim();
4272
+ const apiMode = input.apiMode?.trim();
3676
4273
  return models.find((model) => {
3677
4274
  if (model.id !== input.id) {
3678
4275
  return false;
@@ -3683,9 +4280,21 @@ function findManagedModel(models, input) {
3683
4280
  if (baseUrl !== void 0 && normalizeBaseUrl(model.baseUrl) !== normalizeBaseUrl(baseUrl)) {
3684
4281
  return false;
3685
4282
  }
4283
+ if (apiMode && model.apiMode !== apiMode) {
4284
+ return false;
4285
+ }
3686
4286
  return true;
3687
4287
  });
3688
4288
  }
4289
+ function findManagedModelByVisibleIdentity(models, input) {
4290
+ const key = modelVisibleKey(input);
4291
+ return models.find((model) => modelVisibleKey(model) === key);
4292
+ }
4293
+ function hasModelIdentitySelector(input) {
4294
+ return Boolean(
4295
+ input.provider?.trim() || input.baseUrl?.trim() || input.apiMode?.trim()
4296
+ );
4297
+ }
3689
4298
  function writeAuxiliaryCompressionModelConfig(config, model, env) {
3690
4299
  const auxiliary = ensureRecord(config, "auxiliary");
3691
4300
  const compression = ensureRecord(auxiliary, "compression");
@@ -3722,8 +4331,7 @@ function resolveManagedModelApiKey(config, env, model) {
3722
4331
  return defaultKey;
3723
4332
  }
3724
4333
  }
3725
- const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
3726
- for (const rawEntry of customProviders) {
4334
+ for (const rawEntry of readCompatibleModelProviderEntries(config)) {
3727
4335
  const entry = toRecord(rawEntry);
3728
4336
  const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
3729
4337
  const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
@@ -3774,53 +4382,59 @@ function normalizeModelConfigInput(input) {
3774
4382
  ...reasoningEffort ? { reasoningEffort } : {}
3775
4383
  };
3776
4384
  }
3777
- function ensureCustomProvidersList(config) {
3778
- const existing = config.custom_providers;
3779
- if (Array.isArray(existing)) {
3780
- return existing;
3781
- }
3782
- const next = [];
3783
- config.custom_providers = next;
3784
- return next;
3785
- }
3786
- function retainModelDefaultAsCustomProvider(entries, model) {
4385
+ function retainModelDefaultAsProvider(providers, model) {
3787
4386
  const id = model.model?.trim();
3788
4387
  if (!id) {
3789
4388
  return;
3790
4389
  }
3791
4390
  const provider = model.provider?.trim() || "default";
3792
4391
  const baseUrl = model.baseUrl?.trim() || "";
3793
- const key = modelConfigKey(provider, baseUrl, id);
3794
- const exists = entries.some((entry2) => {
3795
- const record = toRecord(entry2);
3796
- const entryProvider = readString2(record.provider_key) ?? readString2(record.provider) ?? "custom";
3797
- const entryBaseUrl = readString2(record.base_url) ?? readString2(record.url) ?? readString2(record.api) ?? "";
3798
- return readEntryModelIds(record).some(
3799
- (modelId) => modelConfigKey(entryProvider, entryBaseUrl, modelId) === key
3800
- );
3801
- });
4392
+ const apiMode = inferApiMode(provider, baseUrl, model.apiMode);
4393
+ const key = modelConfigKey(provider, baseUrl, id, apiMode);
4394
+ const exists = readCompatibleModelProviderEntries({ providers }).some(
4395
+ (entry2) => {
4396
+ const record = toRecord(entry2);
4397
+ const entryProvider = readString2(record.provider_key) ?? readString2(record.provider) ?? "custom";
4398
+ const entryBaseUrl = readString2(record.base_url) ?? readString2(record.url) ?? readString2(record.api) ?? "";
4399
+ const entryApiMode = inferApiMode(
4400
+ entryProvider,
4401
+ entryBaseUrl,
4402
+ readString2(record.api_mode)
4403
+ );
4404
+ return readEntryModelIds(record).some(
4405
+ (modelId) => modelConfigKey(entryProvider, entryBaseUrl, modelId, entryApiMode) === key
4406
+ );
4407
+ }
4408
+ );
3802
4409
  if (exists) {
3803
4410
  return;
3804
4411
  }
4412
+ const providerKey = uniqueProviderConfigKey(
4413
+ providerConfigKeyBase(provider, provider, baseUrl),
4414
+ providers
4415
+ );
3805
4416
  const entry = {
3806
4417
  name: provider,
3807
- provider_key: provider,
3808
- base_url: baseUrl,
3809
- api_mode: inferApiMode(provider, baseUrl, model.apiMode),
3810
- model: id
4418
+ api: baseUrl,
4419
+ transport: inferApiMode(provider, baseUrl, model.apiMode),
4420
+ default_model: id,
4421
+ models: { [id]: {} }
3811
4422
  };
3812
4423
  if (model.contextLength) {
3813
4424
  entry.context_length = model.contextLength;
3814
4425
  }
3815
4426
  if (model.reasoningEffort) {
3816
- entry.reasoning_effort = model.reasoningEffort;
4427
+ writeEntryModelReasoningEffort(entry, id, model.reasoningEffort);
4428
+ }
4429
+ if (model.supportsVision !== void 0) {
4430
+ writeEntryModelSupportsVision(entry, id, model.supportsVision);
3817
4431
  }
3818
4432
  if (model.keyEnv) {
3819
4433
  entry.key_env = model.keyEnv;
3820
4434
  } else if (model.apiKey) {
3821
4435
  entry.api_key = model.apiKey;
3822
4436
  }
3823
- entries.push(entry);
4437
+ providers[providerKey] = entry;
3824
4438
  }
3825
4439
  function writeDefaultModelConfig(modelConfig, model) {
3826
4440
  modelConfig.default = model.id;
@@ -3847,71 +4461,15 @@ function writeDefaultModelConfig(modelConfig, model) {
3847
4461
  delete modelConfig.context_length;
3848
4462
  }
3849
4463
  }
3850
- function findCustomProviderIndex(entries, modelId) {
3851
- return entries.findIndex(
3852
- (entry) => readEntryModelIds(toRecord(entry)).includes(modelId)
3853
- );
3854
- }
3855
- function findCustomProviderIndexByEndpoint(entries, endpoint) {
3856
- return entries.findIndex((entry) => {
3857
- const record = toRecord(entry);
3858
- const provider = readString2(record.provider_key) ?? readString2(record.provider) ?? "custom";
3859
- const baseUrl = readString2(record.base_url) ?? readString2(record.url) ?? readString2(record.api) ?? "";
3860
- return provider.trim().toLowerCase() === endpoint.provider.trim().toLowerCase() && normalizeBaseUrl(baseUrl) === normalizeBaseUrl(endpoint.baseUrl);
3861
- });
3862
- }
3863
- function updateEntryModels(entry, originalModelId, nextModelId) {
3864
- const models = entry.models;
3865
- if (Array.isArray(models)) {
3866
- const next = models.map((value) => value === originalModelId ? nextModelId : value).filter(
3867
- (value) => typeof value === "string" && value.trim().length > 0
3868
- );
3869
- if (!next.includes(nextModelId)) {
3870
- next.unshift(nextModelId);
3871
- }
3872
- entry.models = Array.from(new Set(next));
4464
+ function writeDefaultModelSupportsVision(modelConfig, supportsVision) {
4465
+ if (supportsVision === void 0 || supportsVision === null) {
4466
+ delete modelConfig.supports_vision;
4467
+ delete modelConfig.supportsVision;
3873
4468
  return;
3874
4469
  }
3875
- if (typeof models === "object" && models !== null && originalModelId !== nextModelId && originalModelId in models) {
3876
- const record = models;
3877
- record[nextModelId] = record[originalModelId];
3878
- delete record[originalModelId];
3879
- }
4470
+ modelConfig.supports_vision = supportsVision;
3880
4471
  }
3881
- function addEntryModel(entry, modelId) {
3882
- const id = modelId.trim();
3883
- if (!id) {
3884
- return;
3885
- }
3886
- const models = entry.models;
3887
- if (Array.isArray(models)) {
3888
- entry.models = Object.fromEntries(
3889
- Array.from(
3890
- /* @__PURE__ */ new Set([
3891
- ...models.filter(
3892
- (value) => typeof value === "string" && value.trim().length > 0
3893
- ),
3894
- id
3895
- ])
3896
- ).map((model) => [model, {}])
3897
- );
3898
- return;
3899
- }
3900
- if (typeof models === "object" && models !== null) {
3901
- const record = models;
3902
- record[id] = toRecord(record[id]);
3903
- return;
3904
- }
3905
- if (readString2(entry.model) !== id && readString2(entry.default_model) !== id) {
3906
- entry.models = Object.fromEntries(
3907
- Array.from(/* @__PURE__ */ new Set([...readEntryModelIds(entry), id])).map((model) => [
3908
- model,
3909
- {}
3910
- ])
3911
- );
3912
- }
3913
- }
3914
- function removeModelFromCustomProvider(entry, modelId) {
4472
+ function removeModelFromProvider(entry, modelId) {
3915
4473
  if (readString2(entry.model) === modelId || readString2(entry.default_model) === modelId) {
3916
4474
  delete entry.model;
3917
4475
  delete entry.default_model;
@@ -3924,7 +4482,7 @@ function removeModelFromCustomProvider(entry, modelId) {
3924
4482
  }
3925
4483
  const remainingModels = readEntryModelIds(entry);
3926
4484
  if (remainingModels.length > 0) {
3927
- entry.model = readString2(entry.model) ?? remainingModels[0];
4485
+ entry.default_model = readString2(entry.default_model) ?? remainingModels[0];
3928
4486
  return entry;
3929
4487
  }
3930
4488
  return null;
@@ -4017,6 +4575,63 @@ function writeEntryModelReasoningEffort(entry, modelId, reasoningEffort) {
4017
4575
  delete entry.reasoningEffort;
4018
4576
  }
4019
4577
  }
4578
+ function readEntryModelSupportsVision(entry, modelId) {
4579
+ const models = entry.models;
4580
+ if (typeof models === "object" && models !== null && !Array.isArray(models)) {
4581
+ const modelConfig = toRecord(models[modelId]);
4582
+ const modelValue = readCapabilityBoolean(
4583
+ modelConfig.supports_vision ?? modelConfig.supportsVision
4584
+ );
4585
+ if (modelValue !== void 0) {
4586
+ return modelValue;
4587
+ }
4588
+ }
4589
+ const entryValue = readCapabilityBoolean(
4590
+ entry.supports_vision ?? entry.supportsVision
4591
+ );
4592
+ return entryValue ?? null;
4593
+ }
4594
+ function writeEntryModelSupportsVision(entry, modelId, supportsVision) {
4595
+ if (supportsVision === void 0) {
4596
+ return;
4597
+ }
4598
+ const modelConfig = ensureEntryModelConfig(entry, modelId, modelId);
4599
+ if (supportsVision === null) {
4600
+ delete modelConfig.supports_vision;
4601
+ delete modelConfig.supportsVision;
4602
+ } else {
4603
+ modelConfig.supports_vision = supportsVision;
4604
+ }
4605
+ }
4606
+ function readCapabilityBoolean(value) {
4607
+ if (typeof value === "boolean") {
4608
+ return value;
4609
+ }
4610
+ if (typeof value !== "string") {
4611
+ return void 0;
4612
+ }
4613
+ const normalized = value.trim().toLowerCase();
4614
+ if (["true", "1", "yes", "y", "on"].includes(normalized)) {
4615
+ return true;
4616
+ }
4617
+ if (["false", "0", "no", "n", "off"].includes(normalized)) {
4618
+ return false;
4619
+ }
4620
+ return void 0;
4621
+ }
4622
+ function ensureEntryModelConfig(entry, originalModelId, nextModelId) {
4623
+ const modelMap = normalizeEntryModelsMap(entry);
4624
+ if (originalModelId !== nextModelId && Object.prototype.hasOwnProperty.call(modelMap, originalModelId) && !Object.prototype.hasOwnProperty.call(modelMap, nextModelId)) {
4625
+ modelMap[nextModelId] = modelMap[originalModelId];
4626
+ }
4627
+ if (originalModelId !== nextModelId) {
4628
+ delete modelMap[originalModelId];
4629
+ }
4630
+ const modelConfig = toRecord(modelMap[nextModelId]);
4631
+ modelMap[nextModelId] = modelConfig;
4632
+ entry.models = modelMap;
4633
+ return modelConfig;
4634
+ }
4020
4635
  async function readHermesModelConfigForImport(input) {
4021
4636
  const { config } = await readHermesConfigDocument(
4022
4637
  resolveHermesConfigPath(input.profileName)
@@ -4064,8 +4679,7 @@ function readInlineApiKeyForModel(config, model) {
4064
4679
  return key;
4065
4680
  }
4066
4681
  }
4067
- const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
4068
- for (const rawEntry of customProviders) {
4682
+ for (const rawEntry of readCompatibleModelProviderEntries(config)) {
4069
4683
  const entry = toRecord(rawEntry);
4070
4684
  const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
4071
4685
  const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
@@ -4095,11 +4709,35 @@ function compareCatalogItems(left, right) {
4095
4709
  }
4096
4710
  return left.id.localeCompare(right.id);
4097
4711
  }
4098
- function modelConfigKey(provider, baseUrl, modelId) {
4099
- return [provider, normalizeBaseUrl(baseUrl), modelId].join("\n").toLowerCase();
4712
+ function modelConfigKey(provider, baseUrl, modelId, apiMode) {
4713
+ return [provider, normalizeBaseUrl(baseUrl), modelId, apiMode].join("\n").toLowerCase();
4714
+ }
4715
+ function modelEndpointKey(baseUrl, modelId, apiMode) {
4716
+ return [normalizeBaseUrl(baseUrl), modelId, apiMode].join("\n").toLowerCase();
4100
4717
  }
4101
- function modelEndpointKey(baseUrl, modelId) {
4102
- return [normalizeBaseUrl(baseUrl), modelId].join("\n").toLowerCase();
4718
+ function providerConfigGroupKey(entry) {
4719
+ const provider = readString2(entry.provider_key) ?? readString2(entry.provider) ?? "custom";
4720
+ const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? "";
4721
+ const baseUrl = readString2(entry.base_url) ?? readString2(entry.url) ?? readString2(entry.api) ?? "";
4722
+ const apiMode = inferApiMode(provider, baseUrl, readString2(entry.api_mode));
4723
+ return [
4724
+ providerName.trim().toLowerCase(),
4725
+ normalizeBaseUrl(baseUrl).toLowerCase(),
4726
+ apiMode.trim().toLowerCase(),
4727
+ providerCredentialIdentity(entry)
4728
+ ].join("\n");
4729
+ }
4730
+ function providerCredentialIdentity(entry) {
4731
+ const apiKey = readString2(entry.api_key);
4732
+ const keyEnv = readString2(entry.key_env) ?? parseEnvReference(apiKey);
4733
+ if (keyEnv) {
4734
+ return `env:${keyEnv.trim().toLowerCase()}`;
4735
+ }
4736
+ const inlineApiKey = readInlineApiKey(apiKey);
4737
+ if (inlineApiKey) {
4738
+ return `inline:${inlineApiKey}`;
4739
+ }
4740
+ return "";
4103
4741
  }
4104
4742
  function normalizeBaseUrl(baseUrl) {
4105
4743
  return baseUrl.trim().replace(/\/+$/u, "");
@@ -4206,9 +4844,18 @@ function readModelConfig(value) {
4206
4844
  contextLength,
4207
4845
  reasoningEffort: normalizeReasoningEffort(
4208
4846
  model.reasoning_effort ?? model.reasoningEffort
4209
- )
4847
+ ),
4848
+ supportsVision: readCapabilityBoolean(model.supports_vision ?? model.supportsVision) ?? null
4210
4849
  };
4211
4850
  }
4851
+ function readProfileImageInputMode(config) {
4852
+ const agent = toRecord(config.agent);
4853
+ return normalizeImageInputMode(agent.image_input_mode) ?? "auto";
4854
+ }
4855
+ function writeProfileImageInputMode(config, imageInputMode) {
4856
+ const agent = ensureRecord(config, "agent");
4857
+ agent.image_input_mode = imageInputMode;
4858
+ }
4212
4859
  function readProfileReasoningEffort(config) {
4213
4860
  const agent = toRecord(config.agent);
4214
4861
  return normalizeReasoningEffort(agent.reasoning_effort ?? agent.reasoningEffort) ?? null;
@@ -4224,6 +4871,13 @@ function normalizeReasoningEffort(value) {
4224
4871
  const normalized = value.trim().toLowerCase();
4225
4872
  return REASONING_EFFORTS.includes(normalized) ? normalized : void 0;
4226
4873
  }
4874
+ function normalizeImageInputMode(value) {
4875
+ if (typeof value !== "string") {
4876
+ return void 0;
4877
+ }
4878
+ const normalized = value.trim().toLowerCase();
4879
+ return normalized === "auto" || normalized === "native" || normalized === "text" ? normalized : void 0;
4880
+ }
4227
4881
  function readPositiveInteger(value) {
4228
4882
  if (typeof value === "number" && Number.isFinite(value) && value > 0) {
4229
4883
  return Math.floor(value);
@@ -4293,6 +4947,11 @@ function readToolsetConfigState(key, config, env) {
4293
4947
  requiresConfig: true,
4294
4948
  configured: isWebToolConfigured(config, env)
4295
4949
  };
4950
+ case "vision":
4951
+ return {
4952
+ requiresConfig: true,
4953
+ configured: isVisionToolConfigured(config, env)
4954
+ };
4296
4955
  case "image_gen":
4297
4956
  return {
4298
4957
  requiresConfig: true,
@@ -4348,6 +5007,34 @@ function isWebToolConfigured(config, env) {
4348
5007
  }
4349
5008
  return isEnvValueConfigured(env.FIRECRAWL_API_KEY) || isEnvValueConfigured(env.FIRECRAWL_API_URL) || isEnvValueConfigured(env.TAVILY_API_KEY) || isEnvValueConfigured(env.EXA_API_KEY) || isEnvValueConfigured(env.PARALLEL_API_KEY);
4350
5009
  }
5010
+ function isVisionToolConfigured(config, env) {
5011
+ const auxiliary = toRecord(config.auxiliary);
5012
+ const vision = toRecord(auxiliary.vision);
5013
+ const provider = readString2(vision.provider)?.toLowerCase() ?? "auto";
5014
+ const baseUrl = readString2(vision.base_url);
5015
+ const apiKey = readString2(vision.api_key);
5016
+ const keyEnv = parseEnvReference(apiKey);
5017
+ const resolvedApiKey = resolveConfiguredApiKey(apiKey, keyEnv, env);
5018
+ if (provider === "auto" && !baseUrl) {
5019
+ return true;
5020
+ }
5021
+ if (provider === "nous") {
5022
+ return true;
5023
+ }
5024
+ if (provider === "openrouter") {
5025
+ return isEnvValueConfigured(env.OPENROUTER_API_KEY);
5026
+ }
5027
+ if (provider === "openai") {
5028
+ return isEnvValueConfigured(env.OPENAI_API_KEY) || isEnvValueConfigured(resolvedApiKey);
5029
+ }
5030
+ if (provider === "anthropic") {
5031
+ return isEnvValueConfigured(env.ANTHROPIC_API_KEY);
5032
+ }
5033
+ if (provider === "custom" || baseUrl) {
5034
+ return isEnvValueConfigured(resolvedApiKey);
5035
+ }
5036
+ return true;
5037
+ }
4351
5038
  function isImageGenToolConfigured(config, env) {
4352
5039
  const imageGen = toRecord(config.image_gen);
4353
5040
  const provider = readString2(imageGen.provider)?.toLowerCase() ?? "fal";
@@ -4474,7 +5161,7 @@ function readString2(value) {
4474
5161
  }
4475
5162
  function normalizeProfileToolConfigKey(value) {
4476
5163
  const normalized = value.trim();
4477
- if (normalized === "web" || normalized === "image_gen" || normalized === "stt" || normalized === "tts" || normalized === "messaging" || normalized === "homeassistant" || normalized === "rl") {
5164
+ if (normalized === "web" || normalized === "vision" || normalized === "image_gen" || normalized === "stt" || normalized === "tts" || normalized === "messaging" || normalized === "homeassistant" || normalized === "rl") {
4478
5165
  return normalized;
4479
5166
  }
4480
5167
  throw new Error(`unsupported tool config "${value}"`);
@@ -4501,6 +5188,25 @@ function profileToolConfigFromSources(profileName, configPath, toolKey, config,
4501
5188
  configured.FIRECRAWL_API_URL = isEnvValueConfigured(env.FIRECRAWL_API_URL);
4502
5189
  break;
4503
5190
  }
5191
+ case "vision": {
5192
+ const auxiliary = toRecord(config.auxiliary);
5193
+ const section = toRecord(auxiliary.vision);
5194
+ values.provider = readString2(section.provider) ?? "auto";
5195
+ values.model = readString2(section.model) ?? env.AUXILIARY_VISION_MODEL?.trim() ?? "";
5196
+ values.baseUrl = readString2(section.base_url) ?? env.AUXILIARY_VISION_BASE_URL?.trim() ?? "";
5197
+ values.apiMode = readString2(section.api_mode) ?? "";
5198
+ configured.OPENROUTER_API_KEY = isEnvValueConfigured(env.OPENROUTER_API_KEY);
5199
+ configured.OPENAI_API_KEY = isEnvValueConfigured(env.OPENAI_API_KEY);
5200
+ configured.ANTHROPIC_API_KEY = isEnvValueConfigured(env.ANTHROPIC_API_KEY);
5201
+ configured.AUXILIARY_VISION_API_KEY = isEnvValueConfigured(env.AUXILIARY_VISION_API_KEY) || isEnvValueConfigured(
5202
+ resolveConfiguredApiKey(
5203
+ readString2(section.api_key),
5204
+ parseEnvReference(readString2(section.api_key)),
5205
+ env
5206
+ )
5207
+ );
5208
+ break;
5209
+ }
4504
5210
  case "image_gen": {
4505
5211
  const section = toRecord(config.image_gen);
4506
5212
  values.provider = readString2(section.provider) ?? "fal";
@@ -4669,6 +5375,71 @@ function applyImageGenToolConfig(config, values) {
4669
5375
  }
4670
5376
  return changed;
4671
5377
  }
5378
+ function applyVisionToolConfig(config, values) {
5379
+ let changed = false;
5380
+ const auxiliary = ensureRecord(config, "auxiliary");
5381
+ const section = ensureRecord(auxiliary, "vision");
5382
+ const provider = readToolConfigString(values.provider);
5383
+ if (provider !== void 0) {
5384
+ if (![
5385
+ "auto",
5386
+ "openrouter",
5387
+ "nous",
5388
+ "openai",
5389
+ "anthropic",
5390
+ "custom"
5391
+ ].includes(provider)) {
5392
+ throw new Error("auxiliary.vision.provider is not supported");
5393
+ }
5394
+ section.provider = provider;
5395
+ changed = true;
5396
+ if (provider === "auto") {
5397
+ delete section.base_url;
5398
+ delete section.api_key;
5399
+ delete section.api_mode;
5400
+ }
5401
+ }
5402
+ if (Object.prototype.hasOwnProperty.call(values, "model")) {
5403
+ const model = readToolConfigString(values.model);
5404
+ if (model) {
5405
+ section.model = model;
5406
+ } else {
5407
+ delete section.model;
5408
+ }
5409
+ changed = true;
5410
+ }
5411
+ if (Object.prototype.hasOwnProperty.call(values, "baseUrl")) {
5412
+ const baseUrl = readToolConfigString(values.baseUrl);
5413
+ if (baseUrl) {
5414
+ section.base_url = normalizeToolConfigHttpUrl(
5415
+ baseUrl,
5416
+ "auxiliary.vision.base_url"
5417
+ );
5418
+ section.api_key = "${AUXILIARY_VISION_API_KEY}";
5419
+ } else {
5420
+ delete section.base_url;
5421
+ if (readString2(section.api_key) === "${AUXILIARY_VISION_API_KEY}") {
5422
+ delete section.api_key;
5423
+ }
5424
+ }
5425
+ changed = true;
5426
+ }
5427
+ if (Object.prototype.hasOwnProperty.call(values, "apiMode")) {
5428
+ const apiMode = readToolConfigString(values.apiMode);
5429
+ if (apiMode) {
5430
+ section.api_mode = apiMode;
5431
+ } else {
5432
+ delete section.api_mode;
5433
+ }
5434
+ changed = true;
5435
+ }
5436
+ if (readToolConfigString(values.AUXILIARY_VISION_API_KEY) && readString2(section.base_url)) {
5437
+ section.api_key = "${AUXILIARY_VISION_API_KEY}";
5438
+ changed = true;
5439
+ }
5440
+ deleteEmptyNestedToolSection(auxiliary, "vision");
5441
+ return changed;
5442
+ }
4672
5443
  function applyOptionalNestedToolString(parent, sectionKey, targetKey, values, inputKeys) {
4673
5444
  const inputKey = inputKeys.find(
4674
5445
  (key) => Object.prototype.hasOwnProperty.call(values, key)
@@ -5621,7 +6392,7 @@ function isConversationMissingError(error) {
5621
6392
  }
5622
6393
 
5623
6394
  // src/constants.ts
5624
- var LINK_VERSION = "0.7.3";
6395
+ var LINK_VERSION = "0.7.4-beta.0";
5625
6396
  var LINK_COMMAND = "hermeslink";
5626
6397
  var LINK_DEFAULT_PORT = 52379;
5627
6398
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -9210,7 +9981,8 @@ var ConversationCommandHandlers = class {
9210
9981
  {
9211
9982
  taskModelId: configuredModel.id,
9212
9983
  taskModelProvider: configuredModel.provider,
9213
- taskModelBaseUrl: configuredModel.baseUrl
9984
+ taskModelBaseUrl: configuredModel.baseUrl,
9985
+ taskModelApiMode: configuredModel.apiMode
9214
9986
  },
9215
9987
  runtime.profileName
9216
9988
  );
@@ -16315,6 +17087,13 @@ async function resolveHermesPythonRuntime() {
16315
17087
  const hermesBin = await resolveExecutablePath2(resolveHermesBin());
16316
17088
  let shebangRuntime = null;
16317
17089
  if (hermesBin) {
17090
+ const launcherTarget = await readHermesLauncherTarget(hermesBin);
17091
+ if (launcherTarget) {
17092
+ const launcherRuntime = await resolveHermesEntrypointRuntime(launcherTarget);
17093
+ if (launcherRuntime) {
17094
+ return launcherRuntime;
17095
+ }
17096
+ }
16318
17097
  const installRoot = await findHermesSourceRoot(hermesBin);
16319
17098
  const shebang = await readShebang(hermesBin);
16320
17099
  const command = shebangToPythonCommand(shebang);
@@ -16435,6 +17214,42 @@ async function findDevHermesAgentSource() {
16435
17214
  }
16436
17215
  return null;
16437
17216
  }
17217
+ async function readHermesLauncherTarget(filePath) {
17218
+ const raw = await readFile11(filePath, "utf8").catch(() => "");
17219
+ for (const line of raw.split(/\r?\n/u)) {
17220
+ const quoted = /^\s*exec\s+(["'])(?<target>.+?)\1\s+(?:"\$@"|'\$@')/.exec(
17221
+ line
17222
+ );
17223
+ const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
17224
+ if (!rawTarget || !path18.isAbsolute(rawTarget)) {
17225
+ continue;
17226
+ }
17227
+ if (await isExecutableFile(rawTarget)) {
17228
+ return rawTarget;
17229
+ }
17230
+ }
17231
+ return null;
17232
+ }
17233
+ async function resolveHermesEntrypointRuntime(entrypointPath) {
17234
+ const installRoot = await findHermesSourceRoot(entrypointPath);
17235
+ const shebang = await readShebang(entrypointPath);
17236
+ const command = shebangToPythonCommand(shebang);
17237
+ if (command) {
17238
+ return {
17239
+ ...command,
17240
+ sourceRoot: await findHermesSourceRoot(command.command) ?? installRoot
17241
+ };
17242
+ }
17243
+ const installPython = installRoot ? await findHermesVenvPython(installRoot) : null;
17244
+ if (installPython) {
17245
+ return {
17246
+ command: installPython,
17247
+ args: [],
17248
+ sourceRoot: installRoot
17249
+ };
17250
+ }
17251
+ return null;
17252
+ }
16438
17253
  async function findHermesSourceRoot(executablePath) {
16439
17254
  let cursor = path18.dirname(path18.resolve(executablePath));
16440
17255
  for (let index = 0; index < 6; index += 1) {
@@ -21553,7 +22368,8 @@ var ConversationService = class {
21553
22368
  {
21554
22369
  taskModelId: configuredModel.id,
21555
22370
  taskModelProvider: configuredModel.provider,
21556
- taskModelBaseUrl: configuredModel.baseUrl
22371
+ taskModelBaseUrl: configuredModel.baseUrl,
22372
+ taskModelApiMode: configuredModel.apiMode
21557
22373
  },
21558
22374
  currentRuntime.profile.name
21559
22375
  );
@@ -24314,16 +25130,13 @@ function registerModelConfigRoutes(router, options) {
24314
25130
  router.delete("/api/v1/model-configs", async (ctx) => {
24315
25131
  await authenticateRequest(ctx, paths);
24316
25132
  const body = await readJsonBody(ctx.req);
24317
- const modelId = readString17(body, "model_id") ?? readString17(body, "modelId");
24318
- if (!modelId) {
24319
- throw new LinkHttpError(400, "model_id_required", "model_id is required");
24320
- }
25133
+ const input = readModelDeleteInput(body);
24321
25134
  try {
24322
- const result = await deleteHermesModelConfig(modelId);
24323
- ctx.body = await reloadGatewayAfterModelConfigChange(result, {
25135
+ const result = await deleteHermesModelConfig(input);
25136
+ ctx.body = shouldReloadGatewayAfterModelConfigChange(body) ? await reloadGatewayAfterModelConfigChange(result, {
24324
25137
  paths,
24325
25138
  logger
24326
- });
25139
+ }) : markModelConfigAppliedWithoutGatewayReload(result);
24327
25140
  } catch (error) {
24328
25141
  throw toModelConfigHttpError(error);
24329
25142
  }
@@ -24386,17 +25199,14 @@ function registerModelConfigRoutes(router, options) {
24386
25199
  await authenticateRequest(ctx, paths);
24387
25200
  await getHermesProfileStatus(ctx.params.name, paths);
24388
25201
  const body = await readJsonBody(ctx.req);
24389
- const modelId = readString17(body, "model_id") ?? readString17(body, "modelId");
24390
- if (!modelId) {
24391
- throw new LinkHttpError(400, "model_id_required", "model_id is required");
24392
- }
25202
+ const input = readModelDeleteInput(body);
24393
25203
  try {
24394
- const result = await deleteHermesModelConfig(modelId, ctx.params.name);
24395
- ctx.body = await reloadGatewayAfterProfileModelConfigChange(result, {
25204
+ const result = await deleteHermesModelConfig(input, ctx.params.name);
25205
+ ctx.body = shouldReloadGatewayAfterModelConfigChange(body) ? await reloadGatewayAfterProfileModelConfigChange(result, {
24396
25206
  paths,
24397
25207
  logger,
24398
25208
  profileName: ctx.params.name
24399
- });
25209
+ }) : markModelConfigAppliedWithoutGatewayReload(result);
24400
25210
  } catch (error) {
24401
25211
  throw toModelConfigHttpError(error);
24402
25212
  }
@@ -24416,6 +25226,9 @@ function readModelConfigInput(body) {
24416
25226
  return {
24417
25227
  id,
24418
25228
  originalModelId: readString17(body, "original_model_id") ?? readString17(body, "originalModelId") ?? readString17(body, "original_id") ?? void 0,
25229
+ originalProvider: readString17(body, "original_provider") ?? readString17(body, "originalProvider") ?? readString17(body, "original_provider_key") ?? readString17(body, "originalProviderKey") ?? void 0,
25230
+ originalBaseUrl: readString17(body, "original_base_url") ?? readString17(body, "originalBaseUrl") ?? void 0,
25231
+ originalApiMode: readString17(body, "original_api_mode") ?? readString17(body, "originalApiMode") ?? void 0,
24419
25232
  provider,
24420
25233
  providerName: readString17(body, "provider_name") ?? readString17(body, "providerName") ?? void 0,
24421
25234
  baseUrl,
@@ -24426,7 +25239,10 @@ function readModelConfigInput(body) {
24426
25239
  ),
24427
25240
  keyEnv: readString17(body, "key_env") ?? readString17(body, "keyEnv") ?? void 0,
24428
25241
  setDefault: readBoolean3(body.set_default ?? body.setDefault),
24429
- reasoningEffort: readString17(body, "reasoning_effort") ?? readString17(body, "reasoningEffort") ?? void 0
25242
+ reasoningEffort: readString17(body, "reasoning_effort") ?? readString17(body, "reasoningEffort") ?? void 0,
25243
+ supportsVision: readNullableBoolean(
25244
+ body.supports_vision ?? body.supportsVision
25245
+ )
24430
25246
  };
24431
25247
  }
24432
25248
  function readModelDefaultsInput(body) {
@@ -24434,12 +25250,46 @@ function readModelDefaultsInput(body) {
24434
25250
  taskModelId: readString17(body, "task_model_id") ?? readString17(body, "taskModelId") ?? readString17(body, "default_model_id") ?? readString17(body, "defaultModelId") ?? void 0,
24435
25251
  taskModelProvider: readString17(body, "task_model_provider") ?? readString17(body, "taskModelProvider") ?? readString17(body, "default_model_provider") ?? readString17(body, "defaultModelProvider") ?? void 0,
24436
25252
  taskModelBaseUrl: readString17(body, "task_model_base_url") ?? readString17(body, "taskModelBaseUrl") ?? readString17(body, "default_model_base_url") ?? readString17(body, "defaultModelBaseUrl") ?? void 0,
25253
+ taskModelApiMode: readString17(body, "task_model_api_mode") ?? readString17(body, "taskModelApiMode") ?? readString17(body, "default_model_api_mode") ?? readString17(body, "defaultModelApiMode") ?? void 0,
24437
25254
  compressionModelId: readString17(body, "compression_model_id") ?? readString17(body, "compressionModelId") ?? void 0,
24438
25255
  compressionModelProvider: readString17(body, "compression_model_provider") ?? readString17(body, "compressionModelProvider") ?? void 0,
24439
25256
  compressionModelBaseUrl: readString17(body, "compression_model_base_url") ?? readString17(body, "compressionModelBaseUrl") ?? void 0,
24440
- reasoningEffort: readString17(body, "reasoning_effort") ?? readString17(body, "reasoningEffort") ?? readString17(body, "default_reasoning_effort") ?? readString17(body, "defaultReasoningEffort") ?? void 0
25257
+ compressionModelApiMode: readString17(body, "compression_model_api_mode") ?? readString17(body, "compressionModelApiMode") ?? void 0,
25258
+ reasoningEffort: readString17(body, "reasoning_effort") ?? readString17(body, "reasoningEffort") ?? readString17(body, "default_reasoning_effort") ?? readString17(body, "defaultReasoningEffort") ?? void 0,
25259
+ imageInputMode: readString17(body, "image_input_mode") ?? readString17(body, "imageInputMode") ?? void 0
24441
25260
  };
24442
25261
  }
25262
+ function readModelDeleteInput(body) {
25263
+ const id = readString17(body, "model_id") ?? readString17(body, "modelId");
25264
+ if (!id) {
25265
+ throw new LinkHttpError(400, "model_id_required", "model_id is required");
25266
+ }
25267
+ return {
25268
+ id,
25269
+ provider: readString17(body, "provider") ?? readString17(body, "provider_key") ?? readString17(body, "providerKey") ?? void 0,
25270
+ baseUrl: readString17(body, "base_url") ?? readString17(body, "baseUrl") ?? void 0,
25271
+ apiMode: readString17(body, "api_mode") ?? readString17(body, "apiMode") ?? void 0
25272
+ };
25273
+ }
25274
+ function readNullableBoolean(value) {
25275
+ if (value === null) {
25276
+ return null;
25277
+ }
25278
+ if (typeof value === "boolean") {
25279
+ return value;
25280
+ }
25281
+ if (typeof value !== "string") {
25282
+ return void 0;
25283
+ }
25284
+ const normalized = value.trim().toLowerCase();
25285
+ if (["true", "1", "yes", "on"].includes(normalized)) {
25286
+ return true;
25287
+ }
25288
+ if (["false", "0", "no", "off"].includes(normalized)) {
25289
+ return false;
25290
+ }
25291
+ return void 0;
25292
+ }
24443
25293
  function readModelConfigImportInput(body) {
24444
25294
  const sourceProfileName = readString17(body, "source_profile") ?? readString17(body, "sourceProfile") ?? readString17(body, "source_profile_name") ?? readString17(body, "sourceProfileName");
24445
25295
  const modelId = readString17(body, "model_id") ?? readString17(body, "modelId") ?? readString17(body, "id");
@@ -24461,7 +25311,7 @@ function readModelConfigImportInput(body) {
24461
25311
  }
24462
25312
  function shouldReloadGatewayAfterModelConfigChange(body) {
24463
25313
  const explicit = readBoolean3(body.reload_gateway ?? body.reloadGateway) ?? (readBoolean3(body.skip_gateway_reload ?? body.skipGatewayReload) === true ? false : void 0);
24464
- return explicit ?? true;
25314
+ return explicit ?? false;
24465
25315
  }
24466
25316
  function markModelConfigAppliedWithoutGatewayReload(result) {
24467
25317
  return {