manifest 5.28.2 → 5.28.4

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.
Files changed (44) hide show
  1. package/dist/backend/app.module.js +1 -0
  2. package/dist/backend/common/filters/spa-fallback.filter.js +7 -4
  3. package/dist/backend/common/utils/anthropic-model-id.js +36 -0
  4. package/dist/backend/database/migrations/1773600000000-AddOverrideProvider.js +18 -0
  5. package/dist/backend/entities/tier-assignment.entity.js +5 -0
  6. package/dist/backend/model-prices/model-name-normalizer.js +11 -0
  7. package/dist/backend/model-prices/model-prices.module.js +11 -2
  8. package/dist/backend/model-prices/model-prices.service.js +1 -0
  9. package/dist/backend/model-prices/model-pricing-cache.service.js +26 -6
  10. package/dist/backend/routing/dto/routing.dto.js +7 -0
  11. package/dist/backend/routing/model-discovery/model-discovery.service.js +12 -3
  12. package/dist/backend/routing/model-discovery/model-fallback.js +12 -3
  13. package/dist/backend/routing/model-discovery/provider-model-registry.service.js +84 -0
  14. package/dist/backend/routing/proxy/provider-client.js +68 -0
  15. package/dist/backend/routing/proxy/proxy.service.js +119 -15
  16. package/dist/backend/routing/resolve.service.js +6 -3
  17. package/dist/backend/routing/routing-invalidation.service.js +1 -0
  18. package/dist/backend/routing/routing.controller.js +1 -1
  19. package/dist/backend/routing/routing.service.js +14 -3
  20. package/dist/backend/routing/tier-auto-assign.service.js +1 -0
  21. package/dist/index.js +2 -2
  22. package/dist/index.js.map +3 -3
  23. package/dist/local-mode.js +2 -2
  24. package/dist/openclaw.plugin.json +1 -1
  25. package/openclaw.plugin.json +1 -1
  26. package/package.json +1 -1
  27. package/public/assets/{Account-BAE8eRcJ.js → Account-BCGnPZpy.js} +1 -1
  28. package/public/assets/{Limits-CqQQ_3K0.js → Limits-oxYjJ-dc.js} +1 -1
  29. package/public/assets/{Login-QaFynG2U.js → Login-CO8qeCzE.js} +1 -1
  30. package/public/assets/{MessageLog-DQphxL5q.js → MessageLog-Dgo_LIpr.js} +1 -1
  31. package/public/assets/{ModelPrices-C0TBirFC.js → ModelPrices-C65v0VGg.js} +1 -1
  32. package/public/assets/{Overview-NS_yOL6z.js → Overview-BxMRtfGQ.js} +1 -1
  33. package/public/assets/{Register-D9NcEgwm.js → Register-B23msCLK.js} +1 -1
  34. package/public/assets/{ResetPassword-BopGZ9WZ.js → ResetPassword-DxYU1h7s.js} +1 -1
  35. package/public/assets/Routing-DsotPZ7f.js +3 -0
  36. package/public/assets/{Settings-DPQF7HQI.js → Settings-R67t6_xt.js} +1 -1
  37. package/public/assets/{SocialButtons-D9y2Be-U.js → SocialButtons-2KTxe6fx.js} +1 -1
  38. package/public/assets/index-C5s5w1mG.js +2 -0
  39. package/public/assets/{model-display-D6Z8peXf.js → model-display-uKwr3P-q.js} +1 -1
  40. package/public/assets/{overview-DWB5XRs3.js → overview-Bfro9DjY.js} +1 -1
  41. package/public/index.html +1 -1
  42. package/skills/manifest/SKILL.md +4 -4
  43. package/public/assets/Routing-Cai7oclw.js +0 -3
  44. package/public/assets/index-D0vjJLh-.js +0 -2
@@ -42,6 +42,7 @@ const serveStaticImports = frontendPath
42
42
  ? [
43
43
  serve_static_1.ServeStaticModule.forRoot({
44
44
  rootPath: frontendPath,
45
+ renderPath: '/__serve_static_never_match',
45
46
  exclude: ['/api/{*path}', '/otlp/{*path}', '/v1/{*path}'],
46
47
  serveStaticOptions: {
47
48
  maxAge: ONE_YEAR_S * 1000,
@@ -12,27 +12,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.SpaFallbackFilter = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
14
  const path_1 = require("path");
15
+ const fs_1 = require("fs");
15
16
  const frontend_path_1 = require("../utils/frontend-path");
16
17
  const API_PREFIXES = ['/api/', '/otlp/', '/v1/'];
17
18
  let SpaFallbackFilter = class SpaFallbackFilter {
18
- indexPath;
19
+ indexContent;
19
20
  constructor() {
20
21
  const frontendDir = (0, frontend_path_1.resolveFrontendDir)();
21
- this.indexPath = frontendDir ? (0, path_1.join)(frontendDir, 'index.html') : null;
22
+ this.indexContent = frontendDir ? (0, fs_1.readFileSync)((0, path_1.join)(frontendDir, 'index.html'), 'utf-8') : null;
22
23
  }
23
24
  catch(exception, host) {
24
25
  const ctx = host.switchToHttp();
25
26
  const req = ctx.getRequest();
26
27
  const res = ctx.getResponse();
27
28
  if (req.method !== 'GET' ||
28
- !this.indexPath ||
29
+ !this.indexContent ||
29
30
  API_PREFIXES.some((p) => req.originalUrl.startsWith(p))) {
30
31
  const response = exception.getResponse();
31
32
  const status = exception.getStatus();
32
33
  res.status(status).json(response);
33
34
  return;
34
35
  }
35
- res.sendFile(this.indexPath);
36
+ res.setHeader('Content-Type', 'text/html');
37
+ res.setHeader('Cache-Control', 'no-cache');
38
+ res.status(200).send(this.indexContent);
36
39
  }
37
40
  };
38
41
  exports.SpaFallbackFilter = SpaFallbackFilter;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeAnthropicShortModelId = normalizeAnthropicShortModelId;
4
+ exports.buildAnthropicShortModelIdVariants = buildAnthropicShortModelIdVariants;
5
+ const ANTHROPIC_PREFIX = 'anthropic/';
6
+ const SHORT_ANTHROPIC_MODEL_RE = /^claude-(opus|sonnet|haiku)-/i;
7
+ const DOTTED_MINOR_RE = /-(\d+)\.(\d{1,2})(?=$|-\d{8}$)/g;
8
+ const DASHED_MINOR_RE = /-(\d+)-(\d{1,2})(?=$|-\d{8}$)/g;
9
+ function splitAnthropicPrefix(model) {
10
+ if (model.startsWith(ANTHROPIC_PREFIX)) {
11
+ return {
12
+ prefix: ANTHROPIC_PREFIX,
13
+ bare: model.slice(ANTHROPIC_PREFIX.length),
14
+ };
15
+ }
16
+ return { prefix: '', bare: model };
17
+ }
18
+ function supportsShortAnthropicMinorVersion(model) {
19
+ return SHORT_ANTHROPIC_MODEL_RE.test(model);
20
+ }
21
+ function normalizeAnthropicShortModelId(model) {
22
+ const { prefix, bare } = splitAnthropicPrefix(model);
23
+ if (!supportsShortAnthropicMinorVersion(bare))
24
+ return model;
25
+ return `${prefix}${bare.replace(DOTTED_MINOR_RE, '-$1-$2')}`;
26
+ }
27
+ function buildAnthropicShortModelIdVariants(model) {
28
+ const normalized = normalizeAnthropicShortModelId(model);
29
+ const { prefix, bare } = splitAnthropicPrefix(normalized);
30
+ if (!supportsShortAnthropicMinorVersion(bare))
31
+ return [model];
32
+ const variants = new Set([model, normalized]);
33
+ variants.add(`${prefix}${bare.replace(DASHED_MINOR_RE, '-$1.$2')}`);
34
+ return [...variants];
35
+ }
36
+ //# sourceMappingURL=anthropic-model-id.js.map
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddOverrideProvider1773600000000 = void 0;
4
+ const typeorm_1 = require("typeorm");
5
+ class AddOverrideProvider1773600000000 {
6
+ async up(queryRunner) {
7
+ await queryRunner.addColumn('tier_assignments', new typeorm_1.TableColumn({
8
+ name: 'override_provider',
9
+ type: 'varchar',
10
+ isNullable: true,
11
+ }));
12
+ }
13
+ async down(queryRunner) {
14
+ await queryRunner.dropColumn('tier_assignments', 'override_provider');
15
+ }
16
+ }
17
+ exports.AddOverrideProvider1773600000000 = AddOverrideProvider1773600000000;
18
+ //# sourceMappingURL=1773600000000-AddOverrideProvider.js.map
@@ -18,6 +18,7 @@ let TierAssignment = class TierAssignment {
18
18
  agent_id;
19
19
  tier;
20
20
  override_model;
21
+ override_provider;
21
22
  override_auth_type;
22
23
  auto_assigned_model;
23
24
  fallback_models;
@@ -44,6 +45,10 @@ __decorate([
44
45
  (0, typeorm_1.Column)('varchar', { nullable: true }),
45
46
  __metadata("design:type", Object)
46
47
  ], TierAssignment.prototype, "override_model", void 0);
48
+ __decorate([
49
+ (0, typeorm_1.Column)('varchar', { nullable: true }),
50
+ __metadata("design:type", Object)
51
+ ], TierAssignment.prototype, "override_provider", void 0);
47
52
  __decorate([
48
53
  (0, typeorm_1.Column)('varchar', { nullable: true }),
49
54
  __metadata("design:type", Object)
@@ -5,6 +5,7 @@ exports.stripDateSuffix = stripDateSuffix;
5
5
  exports.buildAliasMap = buildAliasMap;
6
6
  exports.normalizeDots = normalizeDots;
7
7
  exports.resolveModelName = resolveModelName;
8
+ const anthropic_model_id_1 = require("../common/utils/anthropic-model-id");
8
9
  const KNOWN_ALIASES = [
9
10
  ['claude-opus-4', 'claude-opus-4-6'],
10
11
  ['claude-sonnet-4.5', 'claude-sonnet-4-5-20250929'],
@@ -61,10 +62,20 @@ function buildAliasMap(canonicalNames) {
61
62
  const map = new Map();
62
63
  for (const name of canonicalNames) {
63
64
  map.set(name, name);
65
+ for (const variant of (0, anthropic_model_id_1.buildAnthropicShortModelIdVariants)(name)) {
66
+ if (!map.has(variant)) {
67
+ map.set(variant, name);
68
+ }
69
+ }
64
70
  const bare = stripProviderPrefix(name);
65
71
  if (bare !== name && !map.has(bare)) {
66
72
  map.set(bare, name);
67
73
  }
74
+ for (const variant of (0, anthropic_model_id_1.buildAnthropicShortModelIdVariants)(bare)) {
75
+ if (!map.has(variant)) {
76
+ map.set(variant, name);
77
+ }
78
+ }
68
79
  }
69
80
  for (const [alias, canonical] of KNOWN_ALIASES) {
70
81
  if (map.has(alias))
@@ -8,18 +8,27 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.ModelPricesModule = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
+ const typeorm_1 = require("@nestjs/typeorm");
11
12
  const model_prices_controller_1 = require("./model-prices.controller");
12
13
  const model_prices_service_1 = require("./model-prices.service");
13
14
  const model_pricing_cache_service_1 = require("./model-pricing-cache.service");
14
15
  const pricing_sync_service_1 = require("../database/pricing-sync.service");
16
+ const provider_model_registry_service_1 = require("../routing/model-discovery/provider-model-registry.service");
17
+ const user_provider_entity_1 = require("../entities/user-provider.entity");
15
18
  let ModelPricesModule = class ModelPricesModule {
16
19
  };
17
20
  exports.ModelPricesModule = ModelPricesModule;
18
21
  exports.ModelPricesModule = ModelPricesModule = __decorate([
19
22
  (0, common_1.Module)({
23
+ imports: [typeorm_1.TypeOrmModule.forFeature([user_provider_entity_1.UserProvider])],
20
24
  controllers: [model_prices_controller_1.ModelPricesController],
21
- providers: [model_prices_service_1.ModelPricesService, model_pricing_cache_service_1.ModelPricingCacheService, pricing_sync_service_1.PricingSyncService],
22
- exports: [model_pricing_cache_service_1.ModelPricingCacheService, pricing_sync_service_1.PricingSyncService],
25
+ providers: [
26
+ model_prices_service_1.ModelPricesService,
27
+ model_pricing_cache_service_1.ModelPricingCacheService,
28
+ pricing_sync_service_1.PricingSyncService,
29
+ provider_model_registry_service_1.ProviderModelRegistryService,
30
+ ],
31
+ exports: [model_pricing_cache_service_1.ModelPricingCacheService, pricing_sync_service_1.PricingSyncService, provider_model_registry_service_1.ProviderModelRegistryService],
23
32
  })
24
33
  ], ModelPricesModule);
25
34
  //# sourceMappingURL=model-prices.module.js.map
@@ -30,6 +30,7 @@ let ModelPricesService = class ModelPricesService {
30
30
  input_price_per_million: r.input_price_per_token != null ? Number(r.input_price_per_token) * 1_000_000 : null,
31
31
  output_price_per_million: r.output_price_per_token != null ? Number(r.output_price_per_token) * 1_000_000 : null,
32
32
  display_name: r.display_name || null,
33
+ validated: r.validated,
33
34
  })),
34
35
  lastSyncedAt,
35
36
  };
@@ -8,6 +8,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
11
14
  var ModelPricingCacheService_1;
12
15
  Object.defineProperty(exports, "__esModule", { value: true });
13
16
  exports.ModelPricingCacheService = void 0;
@@ -15,13 +18,16 @@ const common_1 = require("@nestjs/common");
15
18
  const model_name_normalizer_1 = require("./model-name-normalizer");
16
19
  const pricing_sync_service_1 = require("../database/pricing-sync.service");
17
20
  const providers_1 = require("../common/constants/providers");
21
+ const provider_model_registry_service_1 = require("../routing/model-discovery/provider-model-registry.service");
18
22
  let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCacheService {
19
23
  pricingSync;
24
+ modelRegistry;
20
25
  logger = new common_1.Logger(ModelPricingCacheService_1.name);
21
26
  cache = new Map();
22
27
  aliasMap = new Map();
23
- constructor(pricingSync) {
28
+ constructor(pricingSync, modelRegistry) {
24
29
  this.pricingSync = pricingSync;
30
+ this.modelRegistry = modelRegistry;
25
31
  }
26
32
  async onApplicationBootstrap() {
27
33
  await this.reload();
@@ -30,14 +36,16 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
30
36
  this.cache.clear();
31
37
  const orCache = this.pricingSync.getAll();
32
38
  for (const [fullId, entry] of orCache) {
33
- const { provider, canonical } = this.resolveProviderAndName(fullId);
39
+ const { provider, canonical, providerId } = this.resolveProviderAndName(fullId);
34
40
  const displayName = entry.displayName ?? null;
41
+ const validated = this.resolveValidated(providerId, canonical);
35
42
  this.cache.set(fullId, {
36
43
  model_name: fullId,
37
44
  provider,
38
45
  input_price_per_token: entry.input,
39
46
  output_price_per_token: entry.output,
40
47
  display_name: displayName,
48
+ validated,
41
49
  });
42
50
  if (canonical !== fullId && !this.cache.has(canonical)) {
43
51
  this.cache.set(canonical, {
@@ -46,6 +54,7 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
46
54
  input_price_per_token: entry.input,
47
55
  output_price_per_token: entry.output,
48
56
  display_name: displayName,
57
+ validated,
49
58
  });
50
59
  }
51
60
  }
@@ -74,11 +83,11 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
74
83
  }
75
84
  resolveProviderAndName(openRouterId) {
76
85
  if (openRouterId.startsWith('openrouter/')) {
77
- return { provider: 'OpenRouter', canonical: openRouterId };
86
+ return { provider: 'OpenRouter', canonical: openRouterId, providerId: null };
78
87
  }
79
88
  const slashIdx = openRouterId.indexOf('/');
80
89
  if (slashIdx <= 0) {
81
- return { provider: 'OpenRouter', canonical: openRouterId };
90
+ return { provider: 'OpenRouter', canonical: openRouterId, providerId: null };
82
91
  }
83
92
  const prefix = openRouterId.substring(0, slashIdx);
84
93
  const displayName = providers_1.OPENROUTER_PREFIX_TO_PROVIDER.get(prefix);
@@ -86,14 +95,25 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
86
95
  return {
87
96
  provider: displayName,
88
97
  canonical: openRouterId.substring(slashIdx + 1),
98
+ providerId: prefix,
89
99
  };
90
100
  }
91
- return { provider: 'OpenRouter', canonical: openRouterId };
101
+ return { provider: 'OpenRouter', canonical: openRouterId, providerId: null };
102
+ }
103
+ resolveValidated(providerId, canonical) {
104
+ if (!this.modelRegistry || !providerId)
105
+ return undefined;
106
+ const entry = providers_1.PROVIDER_BY_ID_OR_ALIAS.get(providerId);
107
+ const canonicalProviderId = entry?.id ?? providerId;
108
+ const result = this.modelRegistry.isModelConfirmed(canonicalProviderId, canonical);
109
+ return result ?? undefined;
92
110
  }
93
111
  };
94
112
  exports.ModelPricingCacheService = ModelPricingCacheService;
95
113
  exports.ModelPricingCacheService = ModelPricingCacheService = ModelPricingCacheService_1 = __decorate([
96
114
  (0, common_1.Injectable)(),
97
- __metadata("design:paramtypes", [pricing_sync_service_1.PricingSyncService])
115
+ __param(1, (0, common_1.Optional)()),
116
+ __param(1, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
117
+ __metadata("design:paramtypes", [pricing_sync_service_1.PricingSyncService, Object])
98
118
  ], ModelPricingCacheService);
99
119
  //# sourceMappingURL=model-pricing-cache.service.js.map
@@ -88,6 +88,7 @@ __decorate([
88
88
  ], RemoveProviderQueryDto.prototype, "authType", void 0);
89
89
  class SetOverrideDto {
90
90
  model;
91
+ provider;
91
92
  authType;
92
93
  }
93
94
  exports.SetOverrideDto = SetOverrideDto;
@@ -96,6 +97,12 @@ __decorate([
96
97
  (0, class_validator_1.IsNotEmpty)(),
97
98
  __metadata("design:type", String)
98
99
  ], SetOverrideDto.prototype, "model", void 0);
100
+ __decorate([
101
+ (0, class_validator_1.IsOptional)(),
102
+ (0, class_validator_1.IsString)(),
103
+ (0, class_validator_1.IsNotEmpty)(),
104
+ __metadata("design:type", String)
105
+ ], SetOverrideDto.prototype, "provider", void 0);
99
106
  __decorate([
100
107
  (0, class_validator_1.IsOptional)(),
101
108
  (0, class_validator_1.IsIn)(VALID_AUTH_TYPES),
@@ -20,6 +20,7 @@ const typeorm_2 = require("typeorm");
20
20
  const user_provider_entity_1 = require("../../entities/user-provider.entity");
21
21
  const custom_provider_entity_1 = require("../../entities/custom-provider.entity");
22
22
  const provider_model_fetcher_service_1 = require("./provider-model-fetcher.service");
23
+ const provider_model_registry_service_1 = require("./provider-model-registry.service");
23
24
  const crypto_util_1 = require("../../common/utils/crypto.util");
24
25
  const quality_score_util_1 = require("../../database/quality-score.util");
25
26
  const pricing_sync_service_1 = require("../../database/pricing-sync.service");
@@ -32,12 +33,14 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
32
33
  customProviderRepo;
33
34
  fetcher;
34
35
  pricingSync;
36
+ modelRegistry;
35
37
  logger = new common_1.Logger(ModelDiscoveryService_1.name);
36
- constructor(providerRepo, customProviderRepo, fetcher, pricingSync) {
38
+ constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelRegistry) {
37
39
  this.providerRepo = providerRepo;
38
40
  this.customProviderRepo = customProviderRepo;
39
41
  this.fetcher = fetcher;
40
42
  this.pricingSync = pricingSync;
43
+ this.modelRegistry = modelRegistry;
41
44
  }
42
45
  async discoverModels(provider) {
43
46
  let apiKey = '';
@@ -72,8 +75,12 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
72
75
  }
73
76
  else {
74
77
  raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type, endpointOverride);
78
+ if (raw.length > 0 && this.modelRegistry) {
79
+ this.modelRegistry.registerModels(provider.provider, raw.map((m) => m.id));
80
+ }
75
81
  if (raw.length === 0) {
76
- raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider);
82
+ const confirmed = this.modelRegistry?.getConfirmedModels(provider.provider) ?? null;
83
+ raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider, confirmed);
77
84
  if (raw.length > 0) {
78
85
  this.logger.log(`Native API returned 0 models for ${provider.provider} — using ${raw.length} models from pricing data`);
79
86
  }
@@ -215,8 +222,10 @@ exports.ModelDiscoveryService = ModelDiscoveryService = ModelDiscoveryService_1
215
222
  __param(1, (0, typeorm_1.InjectRepository)(custom_provider_entity_1.CustomProvider)),
216
223
  __param(3, (0, common_1.Optional)()),
217
224
  __param(3, (0, common_1.Inject)(pricing_sync_service_1.PricingSyncService)),
225
+ __param(4, (0, common_1.Optional)()),
226
+ __param(4, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
218
227
  __metadata("design:paramtypes", [typeorm_2.Repository,
219
228
  typeorm_2.Repository,
220
- provider_model_fetcher_service_1.ProviderModelFetcherService, Object])
229
+ provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object])
221
230
  ], ModelDiscoveryService);
222
231
  //# sourceMappingURL=model-discovery.service.js.map
@@ -7,6 +7,12 @@ exports.buildSubscriptionFallbackModels = buildSubscriptionFallbackModels;
7
7
  exports.supplementWithKnownModels = supplementWithKnownModels;
8
8
  const providers_1 = require("../../common/constants/providers");
9
9
  const subscription_capabilities_1 = require("../../../../subscription-capabilities");
10
+ const anthropic_model_id_1 = require("../../common/utils/anthropic-model-id");
11
+ function normalizeProviderModelId(providerId, modelId) {
12
+ return providerId.toLowerCase() === 'anthropic'
13
+ ? (0, anthropic_model_id_1.normalizeAnthropicShortModelId)(modelId)
14
+ : modelId;
15
+ }
10
16
  function findOpenRouterPrefix(providerId) {
11
17
  const lower = providerId.toLowerCase();
12
18
  if (providers_1.OPENROUTER_PREFIX_TO_PROVIDER.has(lower))
@@ -54,20 +60,23 @@ function lookupWithVariants(pricingSync, prefix, modelId) {
54
60
  }
55
61
  return null;
56
62
  }
57
- function buildFallbackModels(pricingSync, providerId) {
63
+ function buildFallbackModels(pricingSync, providerId, confirmedModels) {
58
64
  if (!pricingSync)
59
65
  return [];
60
66
  const models = [];
61
67
  const seen = new Set();
68
+ const hasConfirmed = confirmedModels != null && confirmedModels.size > 0;
62
69
  const orPrefix = findOpenRouterPrefix(providerId);
63
70
  if (!orPrefix)
64
71
  return [];
65
72
  for (const [fullId, entry] of pricingSync.getAll()) {
66
73
  if (!fullId.startsWith(`${orPrefix}/`))
67
74
  continue;
68
- const modelId = fullId.substring(orPrefix.length + 1);
75
+ const modelId = normalizeProviderModelId(providerId, fullId.substring(orPrefix.length + 1));
69
76
  if (seen.has(modelId))
70
77
  continue;
78
+ if (hasConfirmed && !confirmedModels.has(modelId.toLowerCase()))
79
+ continue;
71
80
  seen.add(modelId);
72
81
  models.push({
73
82
  id: modelId,
@@ -96,7 +105,7 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
96
105
  for (const [fullId, entry] of pricingSync.getAll()) {
97
106
  if (!fullId.startsWith(`${orPrefix}/`))
98
107
  continue;
99
- const modelId = fullId.substring(orPrefix.length + 1);
108
+ const modelId = normalizeProviderModelId(providerId, fullId.substring(orPrefix.length + 1));
100
109
  if (!normalizedKnownPrefixes.some((p) => modelId.toLowerCase().startsWith(p))) {
101
110
  continue;
102
111
  }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var ProviderModelRegistryService_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ProviderModelRegistryService = void 0;
17
+ const common_1 = require("@nestjs/common");
18
+ const typeorm_1 = require("@nestjs/typeorm");
19
+ const typeorm_2 = require("typeorm");
20
+ const user_provider_entity_1 = require("../../entities/user-provider.entity");
21
+ let ProviderModelRegistryService = ProviderModelRegistryService_1 = class ProviderModelRegistryService {
22
+ providerRepo;
23
+ logger = new common_1.Logger(ProviderModelRegistryService_1.name);
24
+ registry = new Map();
25
+ constructor(providerRepo) {
26
+ this.providerRepo = providerRepo;
27
+ }
28
+ async onApplicationBootstrap() {
29
+ await this.loadFromCache();
30
+ }
31
+ registerModels(providerId, modelIds) {
32
+ const key = providerId.toLowerCase();
33
+ const existing = this.registry.get(key);
34
+ if (existing) {
35
+ for (const id of modelIds)
36
+ existing.add(id.toLowerCase());
37
+ }
38
+ else {
39
+ this.registry.set(key, new Set(modelIds.map((id) => id.toLowerCase())));
40
+ }
41
+ }
42
+ getConfirmedModels(providerId) {
43
+ return this.registry.get(providerId.toLowerCase()) ?? null;
44
+ }
45
+ isModelConfirmed(providerId, modelId) {
46
+ const confirmed = this.registry.get(providerId.toLowerCase());
47
+ if (!confirmed)
48
+ return null;
49
+ return confirmed.has(modelId.toLowerCase());
50
+ }
51
+ async loadFromCache() {
52
+ try {
53
+ const providers = await this.providerRepo
54
+ .createQueryBuilder('p')
55
+ .select(['p.provider', 'p.cached_models'])
56
+ .where('p.cached_models IS NOT NULL')
57
+ .getMany();
58
+ let totalModels = 0;
59
+ for (const p of providers) {
60
+ if (!Array.isArray(p.cached_models))
61
+ continue;
62
+ const ids = p.cached_models.map((m) => m.id).filter(Boolean);
63
+ if (ids.length > 0) {
64
+ this.registerModels(p.provider, ids);
65
+ totalModels += ids.length;
66
+ }
67
+ }
68
+ const providerCount = this.registry.size;
69
+ if (providerCount > 0) {
70
+ this.logger.log(`Provider model registry loaded: ${providerCount} providers, ${totalModels} model entries`);
71
+ }
72
+ }
73
+ catch (err) {
74
+ this.logger.warn(`Failed to load provider model registry from cache: ${err}`);
75
+ }
76
+ }
77
+ };
78
+ exports.ProviderModelRegistryService = ProviderModelRegistryService;
79
+ exports.ProviderModelRegistryService = ProviderModelRegistryService = ProviderModelRegistryService_1 = __decorate([
80
+ (0, common_1.Injectable)(),
81
+ __param(0, (0, typeorm_1.InjectRepository)(user_provider_entity_1.UserProvider)),
82
+ __metadata("design:paramtypes", [typeorm_2.Repository])
83
+ ], ProviderModelRegistryService);
84
+ //# sourceMappingURL=provider-model-registry.service.js.map
@@ -25,6 +25,7 @@ const OPENAI_ONLY_FIELDS = new Set([
25
25
  'reasoning_effort',
26
26
  ]);
27
27
  const PASSTHROUGH_PROVIDERS = new Set(['openai', 'openrouter']);
28
+ const MISTRAL_TOOL_CALL_ID_REGEX = /^[A-Za-z0-9]{9}$/;
28
29
  const DEEPSEEK_MAX_TOKENS_LIMIT = 8192;
29
30
  function supportsReasoningContent(endpointKey, model) {
30
31
  if (endpointKey === 'deepseek')
@@ -37,6 +38,60 @@ function sanitizeOpenAiMessages(messages, endpointKey, model) {
37
38
  if (!Array.isArray(messages))
38
39
  return messages;
39
40
  const preserveReasoningContent = supportsReasoningContent(endpointKey, model);
41
+ const isMistral = endpointKey === 'mistral';
42
+ const mistralIdMap = new Map();
43
+ const reservedMistralIds = new Set();
44
+ let generatedMistralIdCounter = 0;
45
+ const reserveMistralToolCallId = (toolCallId) => {
46
+ if (!isMistral || typeof toolCallId !== 'string')
47
+ return;
48
+ if (MISTRAL_TOOL_CALL_ID_REGEX.test(toolCallId)) {
49
+ reservedMistralIds.add(toolCallId);
50
+ }
51
+ };
52
+ if (isMistral) {
53
+ for (const message of messages) {
54
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
55
+ continue;
56
+ }
57
+ const rawMessage = message;
58
+ if (Array.isArray(rawMessage.tool_calls)) {
59
+ for (const toolCall of rawMessage.tool_calls) {
60
+ if (!toolCall || typeof toolCall !== 'object' || Array.isArray(toolCall)) {
61
+ continue;
62
+ }
63
+ reserveMistralToolCallId(toolCall.id);
64
+ }
65
+ }
66
+ if ('tool_call_id' in rawMessage) {
67
+ reserveMistralToolCallId(rawMessage.tool_call_id);
68
+ }
69
+ }
70
+ }
71
+ const nextGeneratedMistralId = () => {
72
+ do {
73
+ generatedMistralIdCounter += 1;
74
+ const candidate = `tc${generatedMistralIdCounter.toString(36).padStart(7, '0')}`;
75
+ if (!reservedMistralIds.has(candidate))
76
+ return candidate;
77
+ } while (true);
78
+ };
79
+ const normalizeMistralToolCallId = (toolCallId) => {
80
+ if (!isMistral || typeof toolCallId !== 'string')
81
+ return toolCallId;
82
+ const existing = mistralIdMap.get(toolCallId);
83
+ if (existing)
84
+ return existing;
85
+ if (MISTRAL_TOOL_CALL_ID_REGEX.test(toolCallId)) {
86
+ mistralIdMap.set(toolCallId, toolCallId);
87
+ reservedMistralIds.add(toolCallId);
88
+ return toolCallId;
89
+ }
90
+ const rewritten = nextGeneratedMistralId();
91
+ mistralIdMap.set(toolCallId, rewritten);
92
+ reservedMistralIds.add(rewritten);
93
+ return rewritten;
94
+ };
40
95
  return messages.map((message) => {
41
96
  if (!message || typeof message !== 'object' || Array.isArray(message)) {
42
97
  return message;
@@ -45,6 +100,19 @@ function sanitizeOpenAiMessages(messages, endpointKey, model) {
45
100
  if (!preserveReasoningContent) {
46
101
  delete cleaned.reasoning_content;
47
102
  }
103
+ if (isMistral && Array.isArray(cleaned.tool_calls)) {
104
+ cleaned.tool_calls = cleaned.tool_calls.map((toolCall) => {
105
+ if (!toolCall || typeof toolCall !== 'object' || Array.isArray(toolCall)) {
106
+ return toolCall;
107
+ }
108
+ const cleanedToolCall = { ...toolCall };
109
+ cleanedToolCall.id = normalizeMistralToolCallId(cleanedToolCall.id);
110
+ return cleanedToolCall;
111
+ });
112
+ }
113
+ if (isMistral && 'tool_call_id' in cleaned) {
114
+ cleaned.tool_call_id = normalizeMistralToolCallId(cleaned.tool_call_id);
115
+ }
48
116
  return cleaned;
49
117
  });
50
118
  }