manifest 5.34.0 → 5.35.1

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 (29) hide show
  1. package/dist/backend/database/models-dev-sync.service.js +204 -0
  2. package/dist/backend/model-discovery/model-discovery.service.js +44 -7
  3. package/dist/backend/model-discovery/model-fallback.js +20 -0
  4. package/dist/backend/model-discovery/provider-model-fetcher.service.js +20 -3
  5. package/dist/backend/model-prices/model-prices.module.js +8 -1
  6. package/dist/backend/model-prices/model-pricing-cache.service.js +48 -3
  7. package/dist/backend/routing/proxy/provider-client-converters.js +10 -0
  8. package/dist/openclaw.plugin.json +1 -1
  9. package/openclaw.plugin.json +1 -1
  10. package/package.json +1 -1
  11. package/public/assets/{Account-Bmij8ZrS.js → Account-CDKYuc8Y.js} +1 -1
  12. package/public/assets/{Limits-CJay2wDy.js → Limits-DpYfj80Z.js} +1 -1
  13. package/public/assets/{Login-B6PWrEGq.js → Login-DBUKUcpO.js} +1 -1
  14. package/public/assets/{MessageLog-Cb_ucG8H.js → MessageLog-DB_ZGtRS.js} +1 -1
  15. package/public/assets/ModelPrices-CsTVSmEf.js +1 -0
  16. package/public/assets/{Overview-DY7f_oAi.js → Overview-WPFffGwu.js} +1 -1
  17. package/public/assets/{Register-CbGDtQku.js → Register-BPUSuyhr.js} +1 -1
  18. package/public/assets/{ResetPassword-DFQ1lTig.js → ResetPassword-DbYikLII.js} +1 -1
  19. package/public/assets/{Routing-Kn93Tqlz.js → Routing-CqkpJ5K7.js} +1 -1
  20. package/public/assets/{Settings-BZw5xW8c.js → Settings-CPrWA4SD.js} +1 -1
  21. package/public/assets/{SocialButtons-DEIg2R3v.js → SocialButtons-Bz_LKRqa.js} +1 -1
  22. package/public/assets/{index-BCCvBxnZ.js → index-6zwEk5De.js} +2 -2
  23. package/public/assets/{index-D6OW2yDC.css → index-DHEiPwfc.css} +1 -1
  24. package/public/assets/{model-display-C23X2Y_y.js → model-display-bwKW0oKN.js} +1 -1
  25. package/public/assets/{overview-DGoLsOFJ.js → overview-CsP0GEBQ.js} +1 -1
  26. package/public/assets/{routing-C26EmiVE.js → routing-C7G-3vs6.js} +1 -1
  27. package/public/assets/{routing-utils-DGg0JMgE.js → routing-utils-TY9NvF2y.js} +1 -1
  28. package/public/index.html +2 -2
  29. package/public/assets/ModelPrices-CK2tG0zg.js +0 -1
@@ -0,0 +1,204 @@
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 ModelsDevSyncService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ModelsDevSyncService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const schedule_1 = require("@nestjs/schedule");
16
+ const providers_1 = require("../common/constants/providers");
17
+ const PROVIDER_ID_MAP = {
18
+ anthropic: 'anthropic',
19
+ openai: 'openai',
20
+ gemini: 'google',
21
+ deepseek: 'deepseek',
22
+ mistral: 'mistral',
23
+ xai: 'xai',
24
+ minimax: 'minimax',
25
+ moonshot: 'moonshotai',
26
+ qwen: 'alibaba',
27
+ zai: 'zai',
28
+ copilot: 'github-copilot',
29
+ };
30
+ const SUPPORTED_PROVIDERS = new Set(Object.keys(PROVIDER_ID_MAP));
31
+ function resolveProviderId(providerId) {
32
+ const lower = providerId.toLowerCase();
33
+ if (SUPPORTED_PROVIDERS.has(lower))
34
+ return lower;
35
+ const entry = providers_1.PROVIDER_BY_ID_OR_ALIAS.get(lower);
36
+ return entry?.id ?? lower;
37
+ }
38
+ const MODELS_DEV_API = 'https://models.dev/api.json';
39
+ const FETCH_TIMEOUT_MS = 10000;
40
+ const VERSION_SUFFIX_RE = /-\d{3}$/;
41
+ const DATE_SUFFIX_RE = /-\d{4}-?\d{2}-?\d{2}$/;
42
+ const SHORT_DATE_SUFFIX_RE = /-\d{4}$/;
43
+ const LATEST_SUFFIX = '-latest';
44
+ const REASONING_SUFFIX_RE = /-(reasoning|non-reasoning)$/;
45
+ let ModelsDevSyncService = ModelsDevSyncService_1 = class ModelsDevSyncService {
46
+ logger = new common_1.Logger(ModelsDevSyncService_1.name);
47
+ cache = new Map();
48
+ lastFetchedAt = null;
49
+ async onModuleInit() {
50
+ try {
51
+ await this.refreshCache();
52
+ }
53
+ catch (err) {
54
+ this.logger.error(`Startup models.dev cache refresh failed: ${err}`);
55
+ }
56
+ }
57
+ async refreshCache() {
58
+ this.logger.log('Refreshing models.dev cache...');
59
+ const raw = await this.fetchModelsDevData();
60
+ if (!raw)
61
+ return 0;
62
+ const newCache = new Map();
63
+ let totalModels = 0;
64
+ for (const [ourId, modelsDevId] of Object.entries(PROVIDER_ID_MAP)) {
65
+ const provider = raw[modelsDevId];
66
+ if (!provider?.models)
67
+ continue;
68
+ const modelMap = new Map();
69
+ for (const [modelId, model] of Object.entries(provider.models)) {
70
+ if (!this.isChatCompatible(model))
71
+ continue;
72
+ const entry = this.parseModel(modelId, model);
73
+ modelMap.set(modelId, entry);
74
+ totalModels++;
75
+ }
76
+ if (modelMap.size > 0) {
77
+ newCache.set(ourId, modelMap);
78
+ }
79
+ }
80
+ this.cache = newCache;
81
+ this.lastFetchedAt = new Date();
82
+ this.logger.log(`models.dev cache loaded: ${newCache.size} providers, ${totalModels} models`);
83
+ return totalModels;
84
+ }
85
+ lookupModel(providerId, modelId) {
86
+ const providerModels = this.cache.get(resolveProviderId(providerId));
87
+ if (!providerModels)
88
+ return null;
89
+ const exact = providerModels.get(modelId);
90
+ if (exact)
91
+ return exact;
92
+ const noVersion = modelId.replace(VERSION_SUFFIX_RE, '');
93
+ if (noVersion !== modelId) {
94
+ const found = providerModels.get(noVersion);
95
+ if (found)
96
+ return found;
97
+ }
98
+ const noDate = modelId.replace(DATE_SUFFIX_RE, '');
99
+ if (noDate !== modelId) {
100
+ const found = providerModels.get(noDate);
101
+ if (found)
102
+ return found;
103
+ }
104
+ if (!modelId.endsWith(LATEST_SUFFIX)) {
105
+ const withLatest = providerModels.get(modelId + LATEST_SUFFIX);
106
+ if (withLatest)
107
+ return withLatest;
108
+ }
109
+ if (noDate !== modelId && !noDate.endsWith(LATEST_SUFFIX)) {
110
+ const found = providerModels.get(noDate + LATEST_SUFFIX);
111
+ if (found)
112
+ return found;
113
+ }
114
+ const noReasoning = modelId.replace(REASONING_SUFFIX_RE, '');
115
+ if (noReasoning !== modelId) {
116
+ const found = providerModels.get(noReasoning);
117
+ if (found)
118
+ return found;
119
+ }
120
+ const noShortDate = modelId.replace(SHORT_DATE_SUFFIX_RE, '');
121
+ if (noShortDate !== modelId) {
122
+ const found = providerModels.get(noShortDate);
123
+ if (found)
124
+ return found;
125
+ if (!noShortDate.endsWith(LATEST_SUFFIX)) {
126
+ const withLatest = providerModels.get(noShortDate + LATEST_SUFFIX);
127
+ if (withLatest)
128
+ return withLatest;
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+ getModelsForProvider(providerId) {
134
+ const providerModels = this.cache.get(resolveProviderId(providerId));
135
+ if (!providerModels)
136
+ return [];
137
+ return [...providerModels.values()];
138
+ }
139
+ isProviderSupported(providerId) {
140
+ return SUPPORTED_PROVIDERS.has(resolveProviderId(providerId));
141
+ }
142
+ getLastFetchedAt() {
143
+ return this.lastFetchedAt;
144
+ }
145
+ parseModel(modelId, raw) {
146
+ const inputPerMillion = raw.cost?.input ?? null;
147
+ const outputPerMillion = raw.cost?.output ?? null;
148
+ const cacheReadPerMillion = raw.cost?.cache_read ?? null;
149
+ const cacheWritePerMillion = raw.cost?.cache_write ?? null;
150
+ return {
151
+ id: modelId,
152
+ name: raw.name || modelId,
153
+ family: raw.family,
154
+ reasoning: raw.reasoning ?? false,
155
+ toolCall: raw.tool_call ?? false,
156
+ structuredOutput: raw.structured_output ?? false,
157
+ contextWindow: raw.limit?.context,
158
+ maxOutputTokens: raw.limit?.output,
159
+ inputPricePerToken: inputPerMillion !== null ? inputPerMillion / 1_000_000 : null,
160
+ outputPricePerToken: outputPerMillion !== null ? outputPerMillion / 1_000_000 : null,
161
+ cacheReadPricePerToken: cacheReadPerMillion !== null ? cacheReadPerMillion / 1_000_000 : null,
162
+ cacheWritePricePerToken: cacheWritePerMillion !== null ? cacheWritePerMillion / 1_000_000 : null,
163
+ };
164
+ }
165
+ isChatCompatible(model) {
166
+ const inputMods = model.modalities?.input?.map((m) => m.toLowerCase());
167
+ if (inputMods && inputMods.length > 0 && !inputMods.includes('text')) {
168
+ return false;
169
+ }
170
+ const outputMods = model.modalities?.output?.map((m) => m.toLowerCase());
171
+ if (outputMods && outputMods.length > 0) {
172
+ return outputMods.every((m) => m === 'text');
173
+ }
174
+ return true;
175
+ }
176
+ async fetchModelsDevData() {
177
+ try {
178
+ const controller = new AbortController();
179
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
180
+ const res = await fetch(MODELS_DEV_API, { signal: controller.signal });
181
+ clearTimeout(timeout);
182
+ if (!res.ok) {
183
+ this.logger.error(`models.dev API returned ${res.status}`);
184
+ return null;
185
+ }
186
+ return (await res.json());
187
+ }
188
+ catch (err) {
189
+ this.logger.error(`Failed to fetch models.dev data: ${err}`);
190
+ return null;
191
+ }
192
+ }
193
+ };
194
+ exports.ModelsDevSyncService = ModelsDevSyncService;
195
+ __decorate([
196
+ (0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_DAY_AT_4AM),
197
+ __metadata("design:type", Function),
198
+ __metadata("design:paramtypes", []),
199
+ __metadata("design:returntype", Promise)
200
+ ], ModelsDevSyncService.prototype, "refreshCache", null);
201
+ exports.ModelsDevSyncService = ModelsDevSyncService = ModelsDevSyncService_1 = __decorate([
202
+ (0, common_1.Injectable)()
203
+ ], ModelsDevSyncService);
204
+ //# sourceMappingURL=models-dev-sync.service.js.map
@@ -24,6 +24,7 @@ const provider_model_registry_service_1 = require("./provider-model-registry.ser
24
24
  const crypto_util_1 = require("../common/utils/crypto.util");
25
25
  const quality_score_util_1 = require("../database/quality-score.util");
26
26
  const pricing_sync_service_1 = require("../database/pricing-sync.service");
27
+ const models_dev_sync_service_1 = require("../database/models-dev-sync.service");
27
28
  const openai_oauth_types_1 = require("../routing/oauth/openai-oauth.types");
28
29
  const qwen_region_1 = require("../routing/qwen-region");
29
30
  const copilot_token_service_1 = require("../routing/proxy/copilot-token.service");
@@ -39,14 +40,16 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
39
40
  customProviderRepo;
40
41
  fetcher;
41
42
  pricingSync;
43
+ modelsDevSync;
42
44
  modelRegistry;
43
45
  copilotTokenService;
44
46
  logger = new common_1.Logger(ModelDiscoveryService_1.name);
45
- constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelRegistry, copilotTokenService) {
47
+ constructor(providerRepo, customProviderRepo, fetcher, pricingSync, modelsDevSync, modelRegistry, copilotTokenService) {
46
48
  this.providerRepo = providerRepo;
47
49
  this.customProviderRepo = customProviderRepo;
48
50
  this.fetcher = fetcher;
49
51
  this.pricingSync = pricingSync;
52
+ this.modelsDevSync = modelsDevSync;
50
53
  this.modelRegistry = modelRegistry;
51
54
  this.copilotTokenService = copilotTokenService;
52
55
  }
@@ -98,11 +101,17 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
98
101
  if (raw.length > 0 && this.modelRegistry) {
99
102
  this.modelRegistry.registerModels(provider.provider, raw.map((m) => m.id));
100
103
  }
104
+ if (raw.length === 0) {
105
+ raw = (0, model_fallback_1.buildModelsDevFallback)(this.modelsDevSync, provider.provider);
106
+ if (raw.length > 0) {
107
+ this.logger.log(`Native API returned 0 models for ${provider.provider} — using ${raw.length} models from models.dev`);
108
+ }
109
+ }
101
110
  if (raw.length === 0 && !isQwenProvider(provider.provider)) {
102
111
  const confirmed = this.modelRegistry?.getConfirmedModels(provider.provider) ?? null;
103
112
  raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider, confirmed);
104
113
  if (raw.length > 0) {
105
- this.logger.log(`Native API returned 0 models for ${provider.provider} — using ${raw.length} models from pricing data`);
114
+ this.logger.log(`No models.dev data for ${provider.provider} — using ${raw.length} models from OpenRouter`);
106
115
  }
107
116
  }
108
117
  }
@@ -193,8 +202,22 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
193
202
  return all.find((m) => m.id === modelName);
194
203
  }
195
204
  enrichModel(model, providerId) {
196
- if (model.inputPricePerToken !== null && model.inputPricePerToken > 0) {
197
- return this.computeScore(model);
205
+ if (model.inputPricePerToken !== null && model.inputPricePerToken >= 0) {
206
+ return this.computeScore(this.applyCapabilities(model, providerId));
207
+ }
208
+ if (this.modelsDevSync) {
209
+ const mdEntry = this.modelsDevSync.lookupModel(providerId, model.id);
210
+ if (mdEntry && mdEntry.inputPricePerToken !== null) {
211
+ return this.computeScore({
212
+ ...model,
213
+ inputPricePerToken: mdEntry.inputPricePerToken,
214
+ outputPricePerToken: mdEntry.outputPricePerToken,
215
+ contextWindow: mdEntry.contextWindow ?? model.contextWindow,
216
+ displayName: mdEntry.name || model.displayName,
217
+ capabilityReasoning: mdEntry.reasoning ?? model.capabilityReasoning,
218
+ capabilityCode: mdEntry.toolCall ?? model.capabilityCode,
219
+ });
220
+ }
198
221
  }
199
222
  if (this.pricingSync) {
200
223
  const orPrefix = (0, model_fallback_1.findOpenRouterPrefix)(providerId);
@@ -223,6 +246,18 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
223
246
  }
224
247
  return this.computeScore(model);
225
248
  }
249
+ applyCapabilities(model, providerId) {
250
+ if (!this.modelsDevSync)
251
+ return model;
252
+ const mdEntry = this.modelsDevSync.lookupModel(providerId, model.id);
253
+ if (!mdEntry)
254
+ return model;
255
+ return {
256
+ ...model,
257
+ capabilityReasoning: mdEntry.reasoning ?? model.capabilityReasoning,
258
+ capabilityCode: mdEntry.toolCall ?? model.capabilityCode,
259
+ };
260
+ }
226
261
  computeScore(model) {
227
262
  const score = (0, quality_score_util_1.computeQualityScore)({
228
263
  model_name: model.id,
@@ -243,11 +278,13 @@ exports.ModelDiscoveryService = ModelDiscoveryService = ModelDiscoveryService_1
243
278
  __param(3, (0, common_1.Optional)()),
244
279
  __param(3, (0, common_1.Inject)(pricing_sync_service_1.PricingSyncService)),
245
280
  __param(4, (0, common_1.Optional)()),
246
- __param(4, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
281
+ __param(4, (0, common_1.Inject)(models_dev_sync_service_1.ModelsDevSyncService)),
247
282
  __param(5, (0, common_1.Optional)()),
248
- __param(5, (0, common_1.Inject)(copilot_token_service_1.CopilotTokenService)),
283
+ __param(5, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
284
+ __param(6, (0, common_1.Optional)()),
285
+ __param(6, (0, common_1.Inject)(copilot_token_service_1.CopilotTokenService)),
249
286
  __metadata("design:paramtypes", [typeorm_2.Repository,
250
287
  typeorm_2.Repository,
251
- provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object, Object])
288
+ provider_model_fetcher_service_1.ProviderModelFetcherService, Object, Object, Object, Object])
252
289
  ], ModelDiscoveryService);
253
290
  //# sourceMappingURL=model-discovery.service.js.map
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findOpenRouterPrefix = findOpenRouterPrefix;
4
4
  exports.lookupWithVariants = lookupWithVariants;
5
+ exports.buildModelsDevFallback = buildModelsDevFallback;
5
6
  exports.buildFallbackModels = buildFallbackModels;
6
7
  exports.buildSubscriptionFallbackModels = buildSubscriptionFallbackModels;
7
8
  exports.supplementWithKnownModels = supplementWithKnownModels;
@@ -58,8 +59,27 @@ function lookupWithVariants(pricingSync, prefix, modelId) {
58
59
  return noDateDotResult;
59
60
  }
60
61
  }
62
+ const freeResult = pricingSync.lookupPricing(`${prefix}/${modelId}:free`);
63
+ if (freeResult)
64
+ return freeResult;
61
65
  return null;
62
66
  }
67
+ function buildModelsDevFallback(modelsDevSync, providerId) {
68
+ if (!modelsDevSync)
69
+ return [];
70
+ const entries = modelsDevSync.getModelsForProvider(providerId);
71
+ return entries.map((e) => ({
72
+ id: e.id,
73
+ displayName: e.name || e.id,
74
+ provider: providerId,
75
+ contextWindow: e.contextWindow ?? 128000,
76
+ inputPricePerToken: e.inputPricePerToken,
77
+ outputPricePerToken: e.outputPricePerToken,
78
+ capabilityReasoning: e.reasoning ?? false,
79
+ capabilityCode: e.toolCall ?? false,
80
+ qualityScore: 3,
81
+ }));
82
+ }
63
83
  function buildFallbackModels(pricingSync, providerId, confirmedModels) {
64
84
  if (!pricingSync)
65
85
  return [];
@@ -48,6 +48,15 @@ const parseOpenAI = createModelParser({
48
48
  getId: (entry) => entry.id,
49
49
  getDisplayName: (_entry, id) => id,
50
50
  });
51
+ const OPENAI_NON_CHAT_RE = /(?:embed|tts|whisper|dall-e|moderation|davinci|babbage|^text-|audio|realtime|-transcribe|^sora|^gpt-3\.5-turbo-instruct)/i;
52
+ const OPENAI_RESPONSES_ONLY_RE = /(?:-codex(?!-mini-latest)|^gpt-5[^/]*-pro(?:-|$))/i;
53
+ function parseOpenAIChatOnly(body, provider) {
54
+ return parseOpenAI(body, provider).filter((m) => !OPENAI_NON_CHAT_RE.test(m.id) && !OPENAI_RESPONSES_ONLY_RE.test(m.id));
55
+ }
56
+ const MISTRAL_NON_CHAT_RE = /(?:^mistral-ocr|embed)/i;
57
+ function parseMistralChatOnly(body, provider) {
58
+ return parseOpenAI(body, provider).filter((m) => !MISTRAL_NON_CHAT_RE.test(m.id));
59
+ }
51
60
  function bearerHeaders(key) {
52
61
  return { Authorization: `Bearer ${key}` };
53
62
  }
@@ -58,11 +67,12 @@ const parseAnthropic = createModelParser({
58
67
  getDisplayName: (entry, id) => entry.display_name || id,
59
68
  contextWindow: ANTHROPIC_DEFAULT_CONTEXT,
60
69
  });
70
+ const GEMINI_VERSION_SUFFIX_RE = /-\d{3}$/;
61
71
  function parseGemini(body, provider) {
62
72
  const models = body?.models;
63
73
  if (!Array.isArray(models))
64
74
  return [];
65
- return models
75
+ const parsed = models
66
76
  .filter((m) => {
67
77
  const entry = m;
68
78
  if (typeof entry.name !== 'string')
@@ -85,6 +95,13 @@ function parseGemini(body, provider) {
85
95
  qualityScore: 3,
86
96
  };
87
97
  });
98
+ const ids = new Set(parsed.map((m) => m.id));
99
+ return parsed.filter((m) => {
100
+ if (!GEMINI_VERSION_SUFFIX_RE.test(m.id))
101
+ return true;
102
+ const alias = m.id.replace(GEMINI_VERSION_SUFFIX_RE, '');
103
+ return !ids.has(alias);
104
+ });
88
105
  }
89
106
  function parseOpenRouter(body, provider) {
90
107
  const data = body?.data;
@@ -149,7 +166,7 @@ exports.PROVIDER_CONFIGS = {
149
166
  openai: {
150
167
  endpoint: 'https://api.openai.com/v1/models',
151
168
  buildHeaders: bearerHeaders,
152
- parse: parseOpenAI,
169
+ parse: parseOpenAIChatOnly,
153
170
  },
154
171
  'openai-subscription': {
155
172
  endpoint: 'https://chatgpt.com/backend-api/codex/models?client_version=0.99.0',
@@ -169,7 +186,7 @@ exports.PROVIDER_CONFIGS = {
169
186
  mistral: {
170
187
  endpoint: 'https://api.mistral.ai/v1/models',
171
188
  buildHeaders: bearerHeaders,
172
- parse: parseOpenAI,
189
+ parse: parseMistralChatOnly,
173
190
  },
174
191
  moonshot: {
175
192
  endpoint: 'https://api.moonshot.ai/v1/models',
@@ -13,6 +13,7 @@ const model_prices_controller_1 = require("./model-prices.controller");
13
13
  const model_prices_service_1 = require("./model-prices.service");
14
14
  const model_pricing_cache_service_1 = require("./model-pricing-cache.service");
15
15
  const pricing_sync_service_1 = require("../database/pricing-sync.service");
16
+ const models_dev_sync_service_1 = require("../database/models-dev-sync.service");
16
17
  const provider_model_registry_service_1 = require("../model-discovery/provider-model-registry.service");
17
18
  const user_provider_entity_1 = require("../entities/user-provider.entity");
18
19
  let ModelPricesModule = class ModelPricesModule {
@@ -26,9 +27,15 @@ exports.ModelPricesModule = ModelPricesModule = __decorate([
26
27
  model_prices_service_1.ModelPricesService,
27
28
  model_pricing_cache_service_1.ModelPricingCacheService,
28
29
  pricing_sync_service_1.PricingSyncService,
30
+ models_dev_sync_service_1.ModelsDevSyncService,
31
+ provider_model_registry_service_1.ProviderModelRegistryService,
32
+ ],
33
+ exports: [
34
+ model_pricing_cache_service_1.ModelPricingCacheService,
35
+ pricing_sync_service_1.PricingSyncService,
36
+ models_dev_sync_service_1.ModelsDevSyncService,
29
37
  provider_model_registry_service_1.ProviderModelRegistryService,
30
38
  ],
31
- exports: [model_pricing_cache_service_1.ModelPricingCacheService, pricing_sync_service_1.PricingSyncService, provider_model_registry_service_1.ProviderModelRegistryService],
32
39
  })
33
40
  ], ModelPricesModule);
34
41
  //# sourceMappingURL=model-prices.module.js.map
@@ -17,16 +17,19 @@ exports.ModelPricingCacheService = void 0;
17
17
  const common_1 = require("@nestjs/common");
18
18
  const model_name_normalizer_1 = require("./model-name-normalizer");
19
19
  const pricing_sync_service_1 = require("../database/pricing-sync.service");
20
+ const models_dev_sync_service_1 = require("../database/models-dev-sync.service");
20
21
  const providers_1 = require("../common/constants/providers");
21
22
  const provider_model_registry_service_1 = require("../model-discovery/provider-model-registry.service");
22
23
  let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCacheService {
23
24
  pricingSync;
25
+ modelsDevSync;
24
26
  modelRegistry;
25
27
  logger = new common_1.Logger(ModelPricingCacheService_1.name);
26
28
  cache = new Map();
27
29
  aliasMap = new Map();
28
- constructor(pricingSync, modelRegistry) {
30
+ constructor(pricingSync, modelsDevSync, modelRegistry) {
29
31
  this.pricingSync = pricingSync;
32
+ this.modelsDevSync = modelsDevSync;
30
33
  this.modelRegistry = modelRegistry;
31
34
  }
32
35
  async onApplicationBootstrap() {
@@ -44,12 +47,14 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
44
47
  output_price_per_token: entry.output,
45
48
  display_name: entry.displayName ?? null,
46
49
  validated: this.resolveValidated(providerId, canonical),
50
+ source: 'openrouter',
47
51
  };
48
52
  this.cache.set(fullId, pricingEntry);
49
53
  if (canonical !== fullId && !this.cache.has(canonical)) {
50
54
  this.cache.set(canonical, pricingEntry);
51
55
  }
52
56
  }
57
+ this.loadModelsDevEntries();
53
58
  this.aliasMap = (0, model_name_normalizer_1.buildAliasMap)([...this.cache.keys()]);
54
59
  this.logger.log(`Loaded ${this.cache.size} pricing entries`);
55
60
  }
@@ -92,6 +97,38 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
92
97
  }
93
98
  return { provider: 'OpenRouter', canonical: openRouterId, providerId: null };
94
99
  }
100
+ loadModelsDevEntries() {
101
+ if (!this.modelsDevSync)
102
+ return;
103
+ let count = 0;
104
+ for (const [providerId, registryEntry] of providers_1.PROVIDER_BY_ID) {
105
+ const models = this.modelsDevSync.getModelsForProvider(providerId);
106
+ for (const model of models) {
107
+ if (model.inputPricePerToken === null)
108
+ continue;
109
+ const pricingEntry = {
110
+ model_name: model.id,
111
+ provider: registryEntry.displayName,
112
+ input_price_per_token: model.inputPricePerToken,
113
+ output_price_per_token: model.outputPricePerToken,
114
+ display_name: model.name || null,
115
+ validated: this.resolveValidatedForModelsDev(providerId, model.id),
116
+ source: 'models.dev',
117
+ };
118
+ this.cache.set(model.id, pricingEntry);
119
+ for (const prefix of registryEntry.openRouterPrefixes) {
120
+ const prefixedKey = `${prefix}/${model.id}`;
121
+ if (this.cache.has(prefixedKey)) {
122
+ this.cache.set(prefixedKey, { ...pricingEntry, model_name: prefixedKey });
123
+ }
124
+ }
125
+ count++;
126
+ }
127
+ }
128
+ if (count > 0) {
129
+ this.logger.log(`Overlaid ${count} models.dev pricing entries`);
130
+ }
131
+ }
95
132
  resolveValidated(providerId, canonical) {
96
133
  if (!this.modelRegistry || !providerId)
97
134
  return undefined;
@@ -100,12 +137,20 @@ let ModelPricingCacheService = ModelPricingCacheService_1 = class ModelPricingCa
100
137
  const result = this.modelRegistry.isModelConfirmed(canonicalProviderId, canonical);
101
138
  return result ?? undefined;
102
139
  }
140
+ resolveValidatedForModelsDev(providerId, modelId) {
141
+ if (!this.modelRegistry)
142
+ return undefined;
143
+ const result = this.modelRegistry.isModelConfirmed(providerId, modelId);
144
+ return result ?? undefined;
145
+ }
103
146
  };
104
147
  exports.ModelPricingCacheService = ModelPricingCacheService;
105
148
  exports.ModelPricingCacheService = ModelPricingCacheService = ModelPricingCacheService_1 = __decorate([
106
149
  (0, common_1.Injectable)(),
107
150
  __param(1, (0, common_1.Optional)()),
108
- __param(1, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
109
- __metadata("design:paramtypes", [pricing_sync_service_1.PricingSyncService, Object])
151
+ __param(1, (0, common_1.Inject)(models_dev_sync_service_1.ModelsDevSyncService)),
152
+ __param(2, (0, common_1.Optional)()),
153
+ __param(2, (0, common_1.Inject)(provider_model_registry_service_1.ProviderModelRegistryService)),
154
+ __metadata("design:paramtypes", [pricing_sync_service_1.PricingSyncService, Object, Object])
110
155
  ], ModelPricingCacheService);
111
156
  //# sourceMappingURL=model-pricing-cache.service.js.map
@@ -49,6 +49,7 @@ const OPENAI_ONLY_FIELDS = new Set([
49
49
  const PASSTHROUGH_PROVIDERS = new Set(['openai', 'openrouter']);
50
50
  const MISTRAL_TOOL_CALL_ID_REGEX = /^[A-Za-z0-9]{9}$/;
51
51
  const DEEPSEEK_MAX_TOKENS_LIMIT = 8192;
52
+ const OPENAI_MAX_COMPLETION_TOKENS_RE = /^(o\d|gpt-5)/i;
52
53
  function supportsReasoningContent(endpointKey, model) {
53
54
  if (endpointKey === 'deepseek')
54
55
  return true;
@@ -153,6 +154,11 @@ function normalizeDeepSeekMaxTokens(body) {
153
154
  }
154
155
  function sanitizeOpenAiBody(body, endpointKey, model) {
155
156
  const passthroughTopLevel = PASSTHROUGH_PROVIDERS.has(endpointKey);
157
+ const bareForRegex = model.includes('/') ? model.substring(model.indexOf('/') + 1) : model;
158
+ const convertMaxTokens = endpointKey === 'openai' &&
159
+ OPENAI_MAX_COMPLETION_TOKENS_RE.test(bareForRegex) &&
160
+ 'max_tokens' in body &&
161
+ !('max_completion_tokens' in body);
156
162
  const cleaned = {};
157
163
  for (const [key, value] of Object.entries(body)) {
158
164
  if (key === 'messages') {
@@ -160,6 +166,10 @@ function sanitizeOpenAiBody(body, endpointKey, model) {
160
166
  continue;
161
167
  }
162
168
  if (passthroughTopLevel) {
169
+ if (convertMaxTokens && key === 'max_tokens') {
170
+ cleaned['max_completion_tokens'] = value;
171
+ continue;
172
+ }
163
173
  cleaned[key] = value;
164
174
  continue;
165
175
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "manifest",
3
3
  "name": "Manifest Self-Hosted LLM Router",
4
- "version": "5.34.0",
4
+ "version": "5.35.1",
5
5
  "description": "Run the Manifest LLM router locally with SQLite. Zero-config dashboard included.",
6
6
  "author": "MNFST Inc.",
7
7
  "homepage": "https://manifest.build",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "manifest",
3
3
  "name": "Manifest Self-Hosted LLM Router",
4
- "version": "5.34.0",
4
+ "version": "5.35.1",
5
5
  "description": "Run the Manifest LLM router locally with SQLite. Zero-config dashboard included.",
6
6
  "author": "MNFST Inc.",
7
7
  "homepage": "https://manifest.build",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manifest",
3
- "version": "5.34.0",
3
+ "version": "5.35.1",
4
4
  "description": "Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- import{b as H,a as b,e as R,i as c,g as d,T as V,M as W,h,m as Z,S as M,t as l,d as F}from"./vendor-K8fEFSq9.js";import{s as G,u as J,v as K,d as L,w as E}from"./index-BCCvBxnZ.js";import"./auth-B7LxODhZ.js";var O=l("<h3 class=settings-section__title>Profile information"),Q=l('<div class=settings-card><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Display name</span><span class=settings-card__label-desc>Name shown throughout the dashboard.</span></div><div class=settings-card__control><input class=settings-card__input type=text aria-label="Display name"readonly></div></div><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Email</span><span class=settings-card__label-desc>Used for account notifications and limit alerts.</span></div><div class=settings-card__control><input class=settings-card__input type=email aria-label=Email readonly></div></div><div class=settings-card__footer><span style=font-size:var(--font-size-xs);color:hsl(var(--muted-foreground))>Profile information is managed through your authentication provider.'),X=l("<h3 class=settings-section__title>Workspace"),ee=l("<div class=settings-card><div class=settings-card__body><p class=settings-card__desc>Your unique workspace identifier. You may need this for support requests or advanced integrations.</p><div class=settings-card__id-row><code class=settings-card__id-value></code><button class=settings-card__copy-btn title=Copy>"),te=l("<h3 class=settings-section__title>Profile"),se=l('<div class=settings-card><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Display name</span><span class=settings-card__label-desc>Name shown throughout the dashboard.</span></div><div class=settings-card__control><input class=settings-card__input type=text aria-label="Display name"placeholder="Local User">'),ie=l('<div class=account-modal><div class=account-modal__inner><button class=account-modal__back><svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="m15 18-6-6 6-6"></path></svg>Back</button><div class=page-header><div><h1>Account Preferences</h1><span class=breadcrumb>Your profile, workspace details, and display preferences</span></div></div><h3 class=settings-section__title>Appearance</h3><div class=settings-card><div class=settings-card__body><p class=settings-card__desc>Choose how Manifest looks for you.</p><div class=theme-picker><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><circle cx=12 cy=12 r=4></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg>Light</button><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg>Dark</button><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><rect x=2 y=3 width=20 height=14 rx=2></rect><path d="M8 21h8"></path><path d="M12 17v4"></path></svg>System'),ae=l('<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><polyline points="20 6 9 17 4 12">'),le=l('<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><rect x=9 y=9 width=13 height=13 rx=2></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">');const ce=()=>{const N=H(),p=G.useSession(),[T,w]=b(!1),[_,g]=b("system"),[u,y]=b(""),B=()=>p()?.data?.user?.name??"",D=()=>p()?.data?.user?.email??"",x=()=>p()?.data?.user?.id??"";R(()=>{J(),y(K()||"Local User");const t=localStorage.getItem("theme");g(t==="dark"||t==="light"?t:"system")});const m=t=>{if(g(t),t==="system"){localStorage.removeItem("theme");const o=window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.classList.toggle("dark",o)}else localStorage.setItem("theme",t),document.documentElement.classList.toggle("dark",t==="dark")},j=()=>{navigator.clipboard.writeText(x()),w(!0),setTimeout(()=>w(!1),2e3)};return(()=>{var t=ie(),o=t.firstChild,C=o.firstChild,I=C.nextSibling,v=I.nextSibling,P=v.nextSibling,A=P.firstChild,U=A.firstChild,Y=U.nextSibling,k=Y.firstChild,f=k.nextSibling,S=f.nextSibling;return c(t,d(V,{children:"Account Preferences - Manifest"}),o),c(t,d(W,{name:"description",content:"Manage your profile, workspace, and theme preferences."}),o),C.$$click=()=>N(-1),c(o,d(M,{get when(){return!L()},get children(){return[O(),(()=>{var e=Q(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild,a=s.nextSibling,$=a.firstChild,q=$.nextSibling,z=q.firstChild;return h(()=>i.value=B()),h(()=>z.value=D()),e})(),X(),(()=>{var e=ee(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild,a=i.nextSibling;return c(i,x),a.$$click=j,c(a,(()=>{var $=Z(()=>!!T());return()=>$()?ae():le()})()),e})()]}}),v),c(o,d(M,{get when(){return L()},get children(){return[te(),(()=>{var e=se(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild;return i.$$keydown=a=>{a.key==="Enter"&&(E(u()),a.currentTarget.blur())},i.addEventListener("blur",()=>E(u())),i.$$input=a=>y(a.currentTarget.value),h(()=>i.value=u()),e})()]}}),v),k.$$click=()=>m("light"),f.$$click=()=>m("dark"),S.$$click=()=>m("system"),h(e=>{var s=_()==="light",r=_()==="dark",n=_()==="system";return s!==e.e&&k.classList.toggle("theme-picker__option--active",e.e=s),r!==e.t&&f.classList.toggle("theme-picker__option--active",e.t=r),n!==e.a&&S.classList.toggle("theme-picker__option--active",e.a=n),e},{e:void 0,t:void 0,a:void 0}),t})()};F(["click","input","keydown"]);export{ce as default};
1
+ import{b as H,a as b,e as R,i as c,g as d,T as V,M as W,h,m as Z,S as M,t as l,d as F}from"./vendor-K8fEFSq9.js";import{s as G,u as J,v as K,d as L,w as E}from"./index-6zwEk5De.js";import"./auth-B7LxODhZ.js";var O=l("<h3 class=settings-section__title>Profile information"),Q=l('<div class=settings-card><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Display name</span><span class=settings-card__label-desc>Name shown throughout the dashboard.</span></div><div class=settings-card__control><input class=settings-card__input type=text aria-label="Display name"readonly></div></div><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Email</span><span class=settings-card__label-desc>Used for account notifications and limit alerts.</span></div><div class=settings-card__control><input class=settings-card__input type=email aria-label=Email readonly></div></div><div class=settings-card__footer><span style=font-size:var(--font-size-xs);color:hsl(var(--muted-foreground))>Profile information is managed through your authentication provider.'),X=l("<h3 class=settings-section__title>Workspace"),ee=l("<div class=settings-card><div class=settings-card__body><p class=settings-card__desc>Your unique workspace identifier. You may need this for support requests or advanced integrations.</p><div class=settings-card__id-row><code class=settings-card__id-value></code><button class=settings-card__copy-btn title=Copy>"),te=l("<h3 class=settings-section__title>Profile"),se=l('<div class=settings-card><div class=settings-card__row><div class=settings-card__label><span class=settings-card__label-title>Display name</span><span class=settings-card__label-desc>Name shown throughout the dashboard.</span></div><div class=settings-card__control><input class=settings-card__input type=text aria-label="Display name"placeholder="Local User">'),ie=l('<div class=account-modal><div class=account-modal__inner><button class=account-modal__back><svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="m15 18-6-6 6-6"></path></svg>Back</button><div class=page-header><div><h1>Account Preferences</h1><span class=breadcrumb>Your profile, workspace details, and display preferences</span></div></div><h3 class=settings-section__title>Appearance</h3><div class=settings-card><div class=settings-card__body><p class=settings-card__desc>Choose how Manifest looks for you.</p><div class=theme-picker><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><circle cx=12 cy=12 r=4></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg>Light</button><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg>Dark</button><button class=theme-picker__option><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><rect x=2 y=3 width=20 height=14 rx=2></rect><path d="M8 21h8"></path><path d="M12 17v4"></path></svg>System'),ae=l('<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><polyline points="20 6 9 17 4 12">'),le=l('<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><rect x=9 y=9 width=13 height=13 rx=2></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">');const ce=()=>{const N=H(),p=G.useSession(),[T,w]=b(!1),[_,g]=b("system"),[u,y]=b(""),B=()=>p()?.data?.user?.name??"",D=()=>p()?.data?.user?.email??"",x=()=>p()?.data?.user?.id??"";R(()=>{J(),y(K()||"Local User");const t=localStorage.getItem("theme");g(t==="dark"||t==="light"?t:"system")});const m=t=>{if(g(t),t==="system"){localStorage.removeItem("theme");const o=window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.classList.toggle("dark",o)}else localStorage.setItem("theme",t),document.documentElement.classList.toggle("dark",t==="dark")},j=()=>{navigator.clipboard.writeText(x()),w(!0),setTimeout(()=>w(!1),2e3)};return(()=>{var t=ie(),o=t.firstChild,C=o.firstChild,I=C.nextSibling,v=I.nextSibling,P=v.nextSibling,A=P.firstChild,U=A.firstChild,Y=U.nextSibling,k=Y.firstChild,f=k.nextSibling,S=f.nextSibling;return c(t,d(V,{children:"Account Preferences - Manifest"}),o),c(t,d(W,{name:"description",content:"Manage your profile, workspace, and theme preferences."}),o),C.$$click=()=>N(-1),c(o,d(M,{get when(){return!L()},get children(){return[O(),(()=>{var e=Q(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild,a=s.nextSibling,$=a.firstChild,q=$.nextSibling,z=q.firstChild;return h(()=>i.value=B()),h(()=>z.value=D()),e})(),X(),(()=>{var e=ee(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild,a=i.nextSibling;return c(i,x),a.$$click=j,c(a,(()=>{var $=Z(()=>!!T());return()=>$()?ae():le()})()),e})()]}}),v),c(o,d(M,{get when(){return L()},get children(){return[te(),(()=>{var e=se(),s=e.firstChild,r=s.firstChild,n=r.nextSibling,i=n.firstChild;return i.$$keydown=a=>{a.key==="Enter"&&(E(u()),a.currentTarget.blur())},i.addEventListener("blur",()=>E(u())),i.$$input=a=>y(a.currentTarget.value),h(()=>i.value=u()),e})()]}}),v),k.$$click=()=>m("light"),f.$$click=()=>m("dark"),S.$$click=()=>m("system"),h(e=>{var s=_()==="light",r=_()==="dark",n=_()==="system";return s!==e.e&&k.classList.toggle("theme-picker__option--active",e.e=s),r!==e.t&&f.classList.toggle("theme-picker__option--active",e.t=r),n!==e.a&&S.classList.toggle("theme-picker__option--active",e.a=n),e},{e:void 0,t:void 0,a:void 0}),t})()};F(["click","input","keydown"]);export{ce as default};