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.
- package/dist/backend/app.module.js +1 -0
- package/dist/backend/common/filters/spa-fallback.filter.js +7 -4
- package/dist/backend/common/utils/anthropic-model-id.js +36 -0
- package/dist/backend/database/migrations/1773600000000-AddOverrideProvider.js +18 -0
- package/dist/backend/entities/tier-assignment.entity.js +5 -0
- package/dist/backend/model-prices/model-name-normalizer.js +11 -0
- package/dist/backend/model-prices/model-prices.module.js +11 -2
- package/dist/backend/model-prices/model-prices.service.js +1 -0
- package/dist/backend/model-prices/model-pricing-cache.service.js +26 -6
- package/dist/backend/routing/dto/routing.dto.js +7 -0
- package/dist/backend/routing/model-discovery/model-discovery.service.js +12 -3
- package/dist/backend/routing/model-discovery/model-fallback.js +12 -3
- package/dist/backend/routing/model-discovery/provider-model-registry.service.js +84 -0
- package/dist/backend/routing/proxy/provider-client.js +68 -0
- package/dist/backend/routing/proxy/proxy.service.js +119 -15
- package/dist/backend/routing/resolve.service.js +6 -3
- package/dist/backend/routing/routing-invalidation.service.js +1 -0
- package/dist/backend/routing/routing.controller.js +1 -1
- package/dist/backend/routing/routing.service.js +14 -3
- package/dist/backend/routing/tier-auto-assign.service.js +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +3 -3
- package/dist/local-mode.js +2 -2
- package/dist/openclaw.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/public/assets/{Account-BAE8eRcJ.js → Account-BCGnPZpy.js} +1 -1
- package/public/assets/{Limits-CqQQ_3K0.js → Limits-oxYjJ-dc.js} +1 -1
- package/public/assets/{Login-QaFynG2U.js → Login-CO8qeCzE.js} +1 -1
- package/public/assets/{MessageLog-DQphxL5q.js → MessageLog-Dgo_LIpr.js} +1 -1
- package/public/assets/{ModelPrices-C0TBirFC.js → ModelPrices-C65v0VGg.js} +1 -1
- package/public/assets/{Overview-NS_yOL6z.js → Overview-BxMRtfGQ.js} +1 -1
- package/public/assets/{Register-D9NcEgwm.js → Register-B23msCLK.js} +1 -1
- package/public/assets/{ResetPassword-BopGZ9WZ.js → ResetPassword-DxYU1h7s.js} +1 -1
- package/public/assets/Routing-DsotPZ7f.js +3 -0
- package/public/assets/{Settings-DPQF7HQI.js → Settings-R67t6_xt.js} +1 -1
- package/public/assets/{SocialButtons-D9y2Be-U.js → SocialButtons-2KTxe6fx.js} +1 -1
- package/public/assets/index-C5s5w1mG.js +2 -0
- package/public/assets/{model-display-D6Z8peXf.js → model-display-uKwr3P-q.js} +1 -1
- package/public/assets/{overview-DWB5XRs3.js → overview-Bfro9DjY.js} +1 -1
- package/public/index.html +1 -1
- package/skills/manifest/SKILL.md +4 -4
- package/public/assets/Routing-Cai7oclw.js +0 -3
- 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
|
-
|
|
19
|
+
indexContent;
|
|
19
20
|
constructor() {
|
|
20
21
|
const frontendDir = (0, frontend_path_1.resolveFrontendDir)();
|
|
21
|
-
this.
|
|
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.
|
|
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.
|
|
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: [
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|