manifest 5.27.0 → 5.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/routing/minimax-oauth.controller.js +80 -0
- package/dist/backend/routing/minimax-oauth.service.js +306 -0
- package/dist/backend/routing/model-discovery/model-discovery.service.js +11 -9
- package/dist/backend/routing/model-discovery/model-fallback.js +13 -3
- package/dist/backend/routing/model-discovery/provider-model-fetcher.service.js +24 -2
- package/dist/backend/routing/openai-oauth.types.js +21 -0
- package/dist/backend/routing/provider-base-url.js +25 -0
- package/dist/backend/routing/proxy/chatgpt-adapter.js +25 -5
- package/dist/backend/routing/proxy/provider-client.js +23 -0
- package/dist/backend/routing/proxy/provider-endpoints.js +25 -1
- package/dist/backend/routing/proxy/proxy.service.js +37 -12
- package/dist/backend/routing/routing.module.js +5 -0
- package/dist/backend/routing/routing.service.js +8 -7
- package/dist/index.js +2 -2
- package/dist/local-mode.js +1 -1
- package/dist/openclaw.plugin.json +1 -1
- package/dist/subscription.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/public/assets/Account-CgHKYcxX.js +1 -0
- package/public/assets/{Limits-DMhjJmfn.js → Limits-DtI10mW2.js} +1 -1
- package/public/assets/Login-DhEMJAnc.js +1 -0
- package/public/assets/{MessageLog-ouDtsrVk.js → MessageLog-D-PrXoJr.js} +1 -1
- package/public/assets/{ModelPrices-CLb817O4.js → ModelPrices-xY-lQ6DR.js} +1 -1
- package/public/assets/{Overview-BkBN-yAJ.js → Overview-B6kaNMxS.js} +1 -1
- package/public/assets/ProviderIcon-C1sEdkWq.js +1 -0
- package/public/assets/{Register-BTimdbbI.js → Register-BbHU04Jz.js} +1 -1
- package/public/assets/{ResetPassword-VA6EMO3z.js → ResetPassword-USsGc2kx.js} +1 -1
- package/public/assets/Routing-BpgdSvv8.js +3 -0
- package/public/assets/{Settings-BO_Zw9re.js → Settings-CtTGuerh.js} +1 -1
- package/public/assets/{SocialButtons-BYMeXs5O.js → SocialButtons-s6oYDylB.js} +1 -1
- package/public/assets/{index-iVuNO4tR.css → index-BZMbFWG-.css} +1 -1
- package/public/assets/{index-B4etj0id.js → index-CpEcUZbW.js} +2 -2
- package/public/assets/{model-display-BFHsHhiv.js → model-display-CcUHbbaH.js} +1 -1
- package/public/assets/{overview-K8ENzzN2.js → overview-DqBss4Dv.js} +1 -1
- package/public/index.html +2 -2
- package/subscription-capabilities/index.d.ts +1 -0
- package/subscription-capabilities/index.js +19 -0
- package/public/assets/Account-VXfD0W30.js +0 -1
- package/public/assets/Login-GEA0cpss.js +0 -1
- package/public/assets/ProviderIcon-DuNcnqvr.js +0 -1
- package/public/assets/Routing-DfCzJ6V-.js +0 -3
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.MinimaxOauthController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const current_user_decorator_1 = require("../auth/current-user.decorator");
|
|
18
|
+
const minimax_oauth_service_1 = require("./minimax-oauth.service");
|
|
19
|
+
const resolve_agent_service_1 = require("./resolve-agent.service");
|
|
20
|
+
let MinimaxOauthController = class MinimaxOauthController {
|
|
21
|
+
oauthService;
|
|
22
|
+
resolveAgent;
|
|
23
|
+
constructor(oauthService, resolveAgent) {
|
|
24
|
+
this.oauthService = oauthService;
|
|
25
|
+
this.resolveAgent = resolveAgent;
|
|
26
|
+
}
|
|
27
|
+
async start(agentName, region, user) {
|
|
28
|
+
if (!agentName) {
|
|
29
|
+
throw new common_1.HttpException('agentName query parameter is required', common_1.HttpStatus.BAD_REQUEST);
|
|
30
|
+
}
|
|
31
|
+
if (region && !(0, minimax_oauth_service_1.isMinimaxRegion)(region)) {
|
|
32
|
+
throw new common_1.HttpException('region query parameter must be one of: global, cn', common_1.HttpStatus.BAD_REQUEST);
|
|
33
|
+
}
|
|
34
|
+
const agent = await this.resolveAgent.resolve(user.id, agentName);
|
|
35
|
+
const selectedRegion = region && (0, minimax_oauth_service_1.isMinimaxRegion)(region) ? region : 'global';
|
|
36
|
+
try {
|
|
37
|
+
return await this.oauthService.startAuthorization(agent.id, user.id, selectedRegion);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const message = err instanceof Error ? err.message : 'Failed to start MiniMax OAuth';
|
|
41
|
+
throw new common_1.HttpException(message, common_1.HttpStatus.SERVICE_UNAVAILABLE);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async poll(flowId, user) {
|
|
45
|
+
if (!flowId) {
|
|
46
|
+
throw new common_1.HttpException('flowId query parameter is required', common_1.HttpStatus.BAD_REQUEST);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await this.oauthService.pollAuthorization(flowId, user.id);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : 'Failed to poll MiniMax OAuth';
|
|
53
|
+
throw new common_1.HttpException(message, common_1.HttpStatus.SERVICE_UNAVAILABLE);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.MinimaxOauthController = MinimaxOauthController;
|
|
58
|
+
__decorate([
|
|
59
|
+
(0, common_1.Post)('start'),
|
|
60
|
+
__param(0, (0, common_1.Query)('agentName')),
|
|
61
|
+
__param(1, (0, common_1.Query)('region')),
|
|
62
|
+
__param(2, (0, current_user_decorator_1.CurrentUser)()),
|
|
63
|
+
__metadata("design:type", Function),
|
|
64
|
+
__metadata("design:paramtypes", [String, Object, Object]),
|
|
65
|
+
__metadata("design:returntype", Promise)
|
|
66
|
+
], MinimaxOauthController.prototype, "start", null);
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, common_1.Get)('poll'),
|
|
69
|
+
__param(0, (0, common_1.Query)('flowId')),
|
|
70
|
+
__param(1, (0, current_user_decorator_1.CurrentUser)()),
|
|
71
|
+
__metadata("design:type", Function),
|
|
72
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
73
|
+
__metadata("design:returntype", Promise)
|
|
74
|
+
], MinimaxOauthController.prototype, "poll", null);
|
|
75
|
+
exports.MinimaxOauthController = MinimaxOauthController = __decorate([
|
|
76
|
+
(0, common_1.Controller)('api/v1/oauth/minimax'),
|
|
77
|
+
__metadata("design:paramtypes", [minimax_oauth_service_1.MinimaxOauthService,
|
|
78
|
+
resolve_agent_service_1.ResolveAgentService])
|
|
79
|
+
], MinimaxOauthController);
|
|
80
|
+
//# sourceMappingURL=minimax-oauth.controller.js.map
|
|
@@ -0,0 +1,306 @@
|
|
|
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 MinimaxOauthService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.MINIMAX_DEFAULT_RESOURCE_URL = exports.MinimaxOauthService = void 0;
|
|
14
|
+
exports.isMinimaxRegion = isMinimaxRegion;
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const config_1 = require("@nestjs/config");
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
18
|
+
const routing_service_1 = require("./routing.service");
|
|
19
|
+
const model_discovery_service_1 = require("./model-discovery/model-discovery.service");
|
|
20
|
+
const provider_base_url_1 = require("./provider-base-url");
|
|
21
|
+
const MINIMAX_BASE_URLS = {
|
|
22
|
+
global: 'https://api.minimax.io',
|
|
23
|
+
cn: 'https://api.minimaxi.com',
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_REGION = 'global';
|
|
26
|
+
const DEFAULT_BASE_URL = MINIMAX_BASE_URLS[DEFAULT_REGION];
|
|
27
|
+
const DEFAULT_CLIENT_ID = '78257093-7e40-4613-99e0-527b14b39113';
|
|
28
|
+
const DEFAULT_RESOURCE_URL = `${DEFAULT_BASE_URL}/anthropic`;
|
|
29
|
+
exports.MINIMAX_DEFAULT_RESOURCE_URL = DEFAULT_RESOURCE_URL;
|
|
30
|
+
const SCOPE = 'group_id profile model.completion';
|
|
31
|
+
const USER_CODE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:user_code';
|
|
32
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
33
|
+
const ABSOLUTE_TIME_THRESHOLD_MS = 10_000_000_000;
|
|
34
|
+
function isMinimaxRegion(value) {
|
|
35
|
+
return value === 'global' || value === 'cn';
|
|
36
|
+
}
|
|
37
|
+
function getMinimaxBaseUrl(region = DEFAULT_REGION) {
|
|
38
|
+
return MINIMAX_BASE_URLS[region];
|
|
39
|
+
}
|
|
40
|
+
function buildMinimaxCodeUrl(baseUrl) {
|
|
41
|
+
return `${baseUrl}/oauth/code`;
|
|
42
|
+
}
|
|
43
|
+
function buildMinimaxTokenUrl(baseUrl) {
|
|
44
|
+
return `${baseUrl}/oauth/token`;
|
|
45
|
+
}
|
|
46
|
+
function buildMinimaxResourceUrl(baseUrl) {
|
|
47
|
+
return `${baseUrl}/anthropic`;
|
|
48
|
+
}
|
|
49
|
+
function getMinimaxResourceUrl(resourceUrl) {
|
|
50
|
+
if (!resourceUrl)
|
|
51
|
+
return null;
|
|
52
|
+
return (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(resourceUrl);
|
|
53
|
+
}
|
|
54
|
+
function getMinimaxOauthBaseUrl(resourceUrl) {
|
|
55
|
+
const normalized = getMinimaxResourceUrl(resourceUrl);
|
|
56
|
+
if (!normalized)
|
|
57
|
+
return DEFAULT_BASE_URL;
|
|
58
|
+
return new URL(normalized).origin;
|
|
59
|
+
}
|
|
60
|
+
function toAbsoluteExpiryTimestamp(expiredIn) {
|
|
61
|
+
if (expiredIn > ABSOLUTE_TIME_THRESHOLD_MS)
|
|
62
|
+
return expiredIn;
|
|
63
|
+
return Date.now() + expiredIn * 1000;
|
|
64
|
+
}
|
|
65
|
+
function toPollIntervalMs(interval) {
|
|
66
|
+
if (!interval || interval <= 0)
|
|
67
|
+
return DEFAULT_POLL_INTERVAL_MS;
|
|
68
|
+
return interval >= 1000 ? interval : interval * 1000;
|
|
69
|
+
}
|
|
70
|
+
function isOAuthTokenBlob(value) {
|
|
71
|
+
if (!value || typeof value !== 'object')
|
|
72
|
+
return false;
|
|
73
|
+
const blob = value;
|
|
74
|
+
return (typeof blob.t === 'string' &&
|
|
75
|
+
typeof blob.r === 'string' &&
|
|
76
|
+
typeof blob.e === 'number' &&
|
|
77
|
+
Number.isFinite(blob.e) &&
|
|
78
|
+
(blob.u === undefined || typeof blob.u === 'string'));
|
|
79
|
+
}
|
|
80
|
+
let MinimaxOauthService = MinimaxOauthService_1 = class MinimaxOauthService {
|
|
81
|
+
routingService;
|
|
82
|
+
configService;
|
|
83
|
+
discoveryService;
|
|
84
|
+
logger = new common_1.Logger(MinimaxOauthService_1.name);
|
|
85
|
+
pending = new Map();
|
|
86
|
+
clientId;
|
|
87
|
+
constructor(routingService, configService, discoveryService) {
|
|
88
|
+
this.routingService = routingService;
|
|
89
|
+
this.configService = configService;
|
|
90
|
+
this.discoveryService = discoveryService;
|
|
91
|
+
this.clientId = this.configService.get('MINIMAX_OAUTH_CLIENT_ID') ?? DEFAULT_CLIENT_ID;
|
|
92
|
+
}
|
|
93
|
+
async startAuthorization(agentId, userId, region = DEFAULT_REGION) {
|
|
94
|
+
this.cleanupExpired();
|
|
95
|
+
const verifier = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
96
|
+
const challenge = (0, crypto_1.createHash)('sha256').update(verifier).digest('base64url');
|
|
97
|
+
const flowId = (0, crypto_1.randomBytes)(16).toString('base64url');
|
|
98
|
+
const baseUrl = getMinimaxBaseUrl(region);
|
|
99
|
+
const resourceUrl = buildMinimaxResourceUrl(baseUrl);
|
|
100
|
+
const response = await fetch(buildMinimaxCodeUrl(baseUrl), {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: {
|
|
103
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
104
|
+
Accept: 'application/json',
|
|
105
|
+
'x-request-id': (0, crypto_1.randomUUID)(),
|
|
106
|
+
},
|
|
107
|
+
body: new URLSearchParams({
|
|
108
|
+
response_type: 'code',
|
|
109
|
+
client_id: this.clientId,
|
|
110
|
+
scope: SCOPE,
|
|
111
|
+
code_challenge: challenge,
|
|
112
|
+
code_challenge_method: 'S256',
|
|
113
|
+
state: flowId,
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const text = await response.text();
|
|
118
|
+
this.logger.error(`MiniMax OAuth code request failed: ${text}`);
|
|
119
|
+
throw new Error('Failed to start MiniMax OAuth');
|
|
120
|
+
}
|
|
121
|
+
const payload = (await response.json());
|
|
122
|
+
if (!payload.user_code || !payload.verification_uri || !payload.expired_in) {
|
|
123
|
+
throw new Error(payload.error ?? 'MiniMax OAuth returned an incomplete authorization payload.');
|
|
124
|
+
}
|
|
125
|
+
if (payload.state !== flowId) {
|
|
126
|
+
throw new Error('MiniMax OAuth state mismatch');
|
|
127
|
+
}
|
|
128
|
+
const pollIntervalMs = toPollIntervalMs(payload.interval);
|
|
129
|
+
const expiresAt = toAbsoluteExpiryTimestamp(payload.expired_in);
|
|
130
|
+
this.pending.set(flowId, {
|
|
131
|
+
verifier,
|
|
132
|
+
userCode: payload.user_code,
|
|
133
|
+
agentId,
|
|
134
|
+
userId,
|
|
135
|
+
baseUrl,
|
|
136
|
+
resourceUrl,
|
|
137
|
+
expiresAt,
|
|
138
|
+
pollIntervalMs,
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
flowId,
|
|
142
|
+
userCode: payload.user_code,
|
|
143
|
+
verificationUri: payload.verification_uri,
|
|
144
|
+
expiresAt,
|
|
145
|
+
pollIntervalMs,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async pollAuthorization(flowId, userId) {
|
|
149
|
+
this.cleanupExpired();
|
|
150
|
+
const pending = this.pending.get(flowId);
|
|
151
|
+
if (!pending) {
|
|
152
|
+
return { status: 'error', message: 'MiniMax login expired. Start again.' };
|
|
153
|
+
}
|
|
154
|
+
if (pending.userId !== userId) {
|
|
155
|
+
return { status: 'error', message: 'MiniMax login session does not match the current user.' };
|
|
156
|
+
}
|
|
157
|
+
if (Date.now() >= pending.expiresAt) {
|
|
158
|
+
this.pending.delete(flowId);
|
|
159
|
+
return { status: 'error', message: 'MiniMax login expired. Start again.' };
|
|
160
|
+
}
|
|
161
|
+
const response = await fetch(buildMinimaxTokenUrl(pending.baseUrl), {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: {
|
|
164
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
165
|
+
Accept: 'application/json',
|
|
166
|
+
},
|
|
167
|
+
body: new URLSearchParams({
|
|
168
|
+
grant_type: USER_CODE_GRANT_TYPE,
|
|
169
|
+
client_id: this.clientId,
|
|
170
|
+
user_code: pending.userCode,
|
|
171
|
+
code_verifier: pending.verifier,
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
const text = await response.text();
|
|
175
|
+
const payload = this.parseTokenResponse(text);
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
const message = payload?.base_resp?.status_msg ?? text ?? 'MiniMax OAuth failed';
|
|
178
|
+
this.pending.delete(flowId);
|
|
179
|
+
return { status: 'error', message };
|
|
180
|
+
}
|
|
181
|
+
if (!payload) {
|
|
182
|
+
this.pending.delete(flowId);
|
|
183
|
+
return { status: 'error', message: 'MiniMax OAuth returned an invalid token response.' };
|
|
184
|
+
}
|
|
185
|
+
if (payload.status === 'error') {
|
|
186
|
+
this.pending.delete(flowId);
|
|
187
|
+
return { status: 'error', message: 'MiniMax OAuth failed. Please try again later.' };
|
|
188
|
+
}
|
|
189
|
+
if (payload.status !== 'success') {
|
|
190
|
+
return {
|
|
191
|
+
status: 'pending',
|
|
192
|
+
message: 'Waiting for MiniMax approval…',
|
|
193
|
+
pollIntervalMs: pending.pollIntervalMs,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (!payload.access_token || !payload.refresh_token || !payload.expired_in) {
|
|
197
|
+
this.pending.delete(flowId);
|
|
198
|
+
return { status: 'error', message: 'MiniMax OAuth returned an incomplete token payload.' };
|
|
199
|
+
}
|
|
200
|
+
this.pending.delete(flowId);
|
|
201
|
+
const resourceUrl = getMinimaxResourceUrl(payload.resource_url) ?? pending.resourceUrl;
|
|
202
|
+
const blob = {
|
|
203
|
+
t: payload.access_token,
|
|
204
|
+
r: payload.refresh_token,
|
|
205
|
+
e: toAbsoluteExpiryTimestamp(payload.expired_in),
|
|
206
|
+
u: resourceUrl,
|
|
207
|
+
};
|
|
208
|
+
const { provider: savedProvider } = await this.routingService.upsertProvider(pending.agentId, pending.userId, 'minimax', JSON.stringify(blob), 'subscription');
|
|
209
|
+
try {
|
|
210
|
+
await this.discoveryService.discoverModels(savedProvider);
|
|
211
|
+
await this.routingService.recalculateTiers(pending.agentId);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
this.logger.warn(`Model discovery after MiniMax OAuth failed: ${err}`);
|
|
215
|
+
}
|
|
216
|
+
this.logger.log(`MiniMax OAuth token stored for agent=${pending.agentId}`);
|
|
217
|
+
return { status: 'success' };
|
|
218
|
+
}
|
|
219
|
+
async refreshAccessToken(refreshToken, resourceUrl) {
|
|
220
|
+
const baseUrl = getMinimaxOauthBaseUrl(resourceUrl);
|
|
221
|
+
const response = await fetch(buildMinimaxTokenUrl(baseUrl), {
|
|
222
|
+
method: 'POST',
|
|
223
|
+
headers: {
|
|
224
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
225
|
+
Accept: 'application/json',
|
|
226
|
+
},
|
|
227
|
+
body: new URLSearchParams({
|
|
228
|
+
grant_type: 'refresh_token',
|
|
229
|
+
refresh_token: refreshToken,
|
|
230
|
+
client_id: this.clientId,
|
|
231
|
+
}),
|
|
232
|
+
});
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
const text = await response.text();
|
|
235
|
+
this.logger.error(`MiniMax token refresh failed: ${text}`);
|
|
236
|
+
throw new Error('Token refresh failed');
|
|
237
|
+
}
|
|
238
|
+
const payload = (await response.json());
|
|
239
|
+
if (!payload.access_token || !payload.expired_in) {
|
|
240
|
+
throw new Error('MiniMax token refresh returned an incomplete payload');
|
|
241
|
+
}
|
|
242
|
+
const nextResourceUrl = getMinimaxResourceUrl(payload.resource_url) ??
|
|
243
|
+
getMinimaxResourceUrl(resourceUrl) ??
|
|
244
|
+
buildMinimaxResourceUrl(baseUrl);
|
|
245
|
+
return {
|
|
246
|
+
t: payload.access_token,
|
|
247
|
+
r: payload.refresh_token || refreshToken,
|
|
248
|
+
e: toAbsoluteExpiryTimestamp(payload.expired_in),
|
|
249
|
+
...(nextResourceUrl ? { u: nextResourceUrl } : {}),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async unwrapToken(rawValue, agentId, userId) {
|
|
253
|
+
let blob;
|
|
254
|
+
try {
|
|
255
|
+
const parsed = JSON.parse(rawValue);
|
|
256
|
+
if (!isOAuthTokenBlob(parsed))
|
|
257
|
+
return null;
|
|
258
|
+
blob = parsed;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
if (!blob.t || !blob.r || !blob.e)
|
|
264
|
+
return null;
|
|
265
|
+
if (Date.now() < blob.e - 60_000)
|
|
266
|
+
return blob;
|
|
267
|
+
try {
|
|
268
|
+
const refreshed = await this.refreshAccessToken(blob.r, blob.u);
|
|
269
|
+
await this.routingService.upsertProvider(agentId, userId, 'minimax', JSON.stringify(refreshed), 'subscription');
|
|
270
|
+
this.logger.log(`MiniMax OAuth token refreshed for agent=${agentId}`);
|
|
271
|
+
return refreshed;
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
this.logger.error(`Failed to refresh MiniMax token: ${err}`);
|
|
275
|
+
return blob;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
getPendingCount() {
|
|
279
|
+
return this.pending.size;
|
|
280
|
+
}
|
|
281
|
+
cleanupExpired() {
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
for (const [flowId, pending] of this.pending.entries()) {
|
|
284
|
+
if (pending.expiresAt <= now)
|
|
285
|
+
this.pending.delete(flowId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
parseTokenResponse(text) {
|
|
289
|
+
if (!text)
|
|
290
|
+
return null;
|
|
291
|
+
try {
|
|
292
|
+
return JSON.parse(text);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
exports.MinimaxOauthService = MinimaxOauthService;
|
|
300
|
+
exports.MinimaxOauthService = MinimaxOauthService = MinimaxOauthService_1 = __decorate([
|
|
301
|
+
(0, common_1.Injectable)(),
|
|
302
|
+
__metadata("design:paramtypes", [routing_service_1.RoutingService,
|
|
303
|
+
config_1.ConfigService,
|
|
304
|
+
model_discovery_service_1.ModelDiscoveryService])
|
|
305
|
+
], MinimaxOauthService);
|
|
306
|
+
//# sourceMappingURL=minimax-oauth.service.js.map
|
|
@@ -23,6 +23,7 @@ const provider_model_fetcher_service_1 = require("./provider-model-fetcher.servi
|
|
|
23
23
|
const crypto_util_1 = require("../../common/utils/crypto.util");
|
|
24
24
|
const quality_score_util_1 = require("../../database/quality-score.util");
|
|
25
25
|
const pricing_sync_service_1 = require("../../database/pricing-sync.service");
|
|
26
|
+
const openai_oauth_types_1 = require("../openai-oauth.types");
|
|
26
27
|
const model_fallback_1 = require("./model-fallback");
|
|
27
28
|
const customProviderKey = (id) => `custom:${id}`;
|
|
28
29
|
const customModelKey = (id, modelName) => `custom:${id}/${modelName}`;
|
|
@@ -40,6 +41,7 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
|
|
|
40
41
|
}
|
|
41
42
|
async discoverModels(provider) {
|
|
42
43
|
let apiKey = '';
|
|
44
|
+
let endpointOverride;
|
|
43
45
|
if (provider.api_key_encrypted) {
|
|
44
46
|
try {
|
|
45
47
|
apiKey = (0, crypto_util_1.decrypt)(provider.api_key_encrypted, (0, crypto_util_1.getEncryptionSecret)());
|
|
@@ -49,17 +51,17 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
|
|
|
49
51
|
return [];
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
if (provider.auth_type === 'subscription' &&
|
|
53
|
-
provider.provider.toLowerCase()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (blob.t) {
|
|
54
|
+
if (provider.auth_type === 'subscription' && apiKey) {
|
|
55
|
+
const lowerProvider = provider.provider.toLowerCase();
|
|
56
|
+
if (lowerProvider === 'openai' || lowerProvider === 'minimax') {
|
|
57
|
+
const blob = (0, openai_oauth_types_1.parseOAuthTokenBlob)(apiKey);
|
|
58
|
+
if (blob?.t) {
|
|
58
59
|
apiKey = blob.t;
|
|
60
|
+
if (lowerProvider === 'minimax' && blob.u) {
|
|
61
|
+
endpointOverride = blob.u;
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
catch {
|
|
62
|
-
}
|
|
63
65
|
}
|
|
64
66
|
let raw;
|
|
65
67
|
if (provider.auth_type === 'subscription' && !apiKey) {
|
|
@@ -69,7 +71,7 @@ let ModelDiscoveryService = ModelDiscoveryService_1 = class ModelDiscoveryServic
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
else {
|
|
72
|
-
raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type);
|
|
74
|
+
raw = await this.fetcher.fetch(provider.provider, apiKey, provider.auth_type, endpointOverride);
|
|
73
75
|
if (raw.length === 0) {
|
|
74
76
|
raw = (0, model_fallback_1.buildFallbackModels)(this.pricingSync, provider.provider);
|
|
75
77
|
if (raw.length > 0) {
|
|
@@ -87,6 +87,7 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
|
|
|
87
87
|
const knownPrefixes = (0, subscription_capabilities_1.getSubscriptionKnownModels)(providerId);
|
|
88
88
|
if (!knownPrefixes)
|
|
89
89
|
return [];
|
|
90
|
+
const normalizedKnownPrefixes = knownPrefixes.map((modelId) => modelId.toLowerCase());
|
|
90
91
|
const capabilities = (0, subscription_capabilities_1.getSubscriptionCapabilities)(providerId);
|
|
91
92
|
const models = [];
|
|
92
93
|
const seen = new Set();
|
|
@@ -96,8 +97,9 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
|
|
|
96
97
|
if (!fullId.startsWith(`${orPrefix}/`))
|
|
97
98
|
continue;
|
|
98
99
|
const modelId = fullId.substring(orPrefix.length + 1);
|
|
99
|
-
if (!
|
|
100
|
+
if (!normalizedKnownPrefixes.some((p) => modelId.toLowerCase().startsWith(p))) {
|
|
100
101
|
continue;
|
|
102
|
+
}
|
|
101
103
|
if (seen.has(modelId))
|
|
102
104
|
continue;
|
|
103
105
|
seen.add(modelId);
|
|
@@ -120,7 +122,11 @@ function buildSubscriptionFallbackModels(pricingSync, providerId) {
|
|
|
120
122
|
}
|
|
121
123
|
const defaultCtx = capabilities?.maxContextWindow ?? 200000;
|
|
122
124
|
for (const modelId of knownPrefixes) {
|
|
123
|
-
const
|
|
125
|
+
const lowerModelId = modelId.toLowerCase();
|
|
126
|
+
const covered = models.some((m) => {
|
|
127
|
+
const lowerDiscovered = m.id.toLowerCase();
|
|
128
|
+
return lowerDiscovered === lowerModelId || lowerDiscovered.startsWith(`${lowerModelId}-`);
|
|
129
|
+
});
|
|
124
130
|
if (covered)
|
|
125
131
|
continue;
|
|
126
132
|
models.push({
|
|
@@ -144,7 +150,11 @@ function supplementWithKnownModels(raw, providerId) {
|
|
|
144
150
|
const capabilities = (0, subscription_capabilities_1.getSubscriptionCapabilities)(providerId);
|
|
145
151
|
const defaultCtx = capabilities?.maxContextWindow ?? 200000;
|
|
146
152
|
for (const modelId of knownModels) {
|
|
147
|
-
const
|
|
153
|
+
const lowerModelId = modelId.toLowerCase();
|
|
154
|
+
const covered = raw.some((m) => {
|
|
155
|
+
const lowerDiscovered = m.id.toLowerCase();
|
|
156
|
+
return lowerDiscovered === lowerModelId || lowerDiscovered.startsWith(`${lowerModelId}-`);
|
|
157
|
+
});
|
|
148
158
|
if (covered)
|
|
149
159
|
continue;
|
|
150
160
|
raw.push({
|
|
@@ -10,10 +10,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.ProviderModelFetcherService = exports.PROVIDER_CONFIGS = void 0;
|
|
11
11
|
const common_1 = require("@nestjs/common");
|
|
12
12
|
const ollama_1 = require("../../common/constants/ollama");
|
|
13
|
+
const provider_base_url_1 = require("../provider-base-url");
|
|
13
14
|
const FETCH_TIMEOUT_MS = 5000;
|
|
14
15
|
const DEFAULT_CONTEXT_WINDOW = 128000;
|
|
15
16
|
const ANTHROPIC_DEFAULT_CONTEXT = 200000;
|
|
16
17
|
const GEMINI_DEFAULT_CONTEXT = 1000000;
|
|
18
|
+
const MINIMAX_SUBSCRIPTION_MODELS_URL = 'https://api.minimax.io/anthropic/v1/models?limit=100';
|
|
17
19
|
function parseOpenAI(body, provider) {
|
|
18
20
|
const data = body?.data;
|
|
19
21
|
if (!Array.isArray(data))
|
|
@@ -212,6 +214,14 @@ exports.PROVIDER_CONFIGS = {
|
|
|
212
214
|
buildHeaders: bearerHeaders,
|
|
213
215
|
parse: parseOpenAI,
|
|
214
216
|
},
|
|
217
|
+
'minimax-subscription': {
|
|
218
|
+
endpoint: MINIMAX_SUBSCRIPTION_MODELS_URL,
|
|
219
|
+
buildHeaders: (key) => ({
|
|
220
|
+
Authorization: `Bearer ${key}`,
|
|
221
|
+
'anthropic-version': '2023-06-01',
|
|
222
|
+
}),
|
|
223
|
+
parse: parseAnthropic,
|
|
224
|
+
},
|
|
215
225
|
qwen: {
|
|
216
226
|
endpoint: 'https://dashscope.aliyuncs.com/compatible-mode/v1/models',
|
|
217
227
|
buildHeaders: bearerHeaders,
|
|
@@ -257,17 +267,29 @@ exports.PROVIDER_CONFIGS = {
|
|
|
257
267
|
};
|
|
258
268
|
let ProviderModelFetcherService = ProviderModelFetcherService_1 = class ProviderModelFetcherService {
|
|
259
269
|
logger = new common_1.Logger(ProviderModelFetcherService_1.name);
|
|
260
|
-
async fetch(providerId, apiKey, authType) {
|
|
270
|
+
async fetch(providerId, apiKey, authType, endpointOverride) {
|
|
261
271
|
let configKey = providerId.toLowerCase();
|
|
262
272
|
if (configKey === 'openai' && authType === 'subscription') {
|
|
263
273
|
configKey = 'openai-subscription';
|
|
264
274
|
}
|
|
275
|
+
else if (configKey === 'minimax' && authType === 'subscription') {
|
|
276
|
+
configKey = 'minimax-subscription';
|
|
277
|
+
}
|
|
265
278
|
const config = exports.PROVIDER_CONFIGS[configKey];
|
|
266
279
|
if (!config) {
|
|
267
280
|
this.logger.warn(`No fetcher config for provider: ${providerId}`);
|
|
268
281
|
return [];
|
|
269
282
|
}
|
|
270
|
-
|
|
283
|
+
let url = typeof config.endpoint === 'function' ? config.endpoint(apiKey) : config.endpoint;
|
|
284
|
+
if (endpointOverride && configKey === 'minimax-subscription') {
|
|
285
|
+
const minimaxBaseUrl = (0, provider_base_url_1.normalizeMinimaxSubscriptionBaseUrl)(endpointOverride);
|
|
286
|
+
if (minimaxBaseUrl) {
|
|
287
|
+
url = `${minimaxBaseUrl}/v1/models?limit=100`;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
this.logger.warn('Ignoring invalid MiniMax subscription endpoint override');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
271
293
|
const headers = config.buildHeaders(apiKey, authType);
|
|
272
294
|
try {
|
|
273
295
|
const controller = new AbortController();
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseOAuthTokenBlob = parseOAuthTokenBlob;
|
|
3
4
|
exports.oauthDoneHtml = oauthDoneHtml;
|
|
5
|
+
function parseOAuthTokenBlob(rawValue) {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(rawValue);
|
|
8
|
+
if (typeof parsed?.t !== 'string' ||
|
|
9
|
+
typeof parsed?.r !== 'string' ||
|
|
10
|
+
typeof parsed?.e !== 'number' ||
|
|
11
|
+
(parsed.u !== undefined && typeof parsed.u !== 'string')) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
t: parsed.t,
|
|
16
|
+
r: parsed.r,
|
|
17
|
+
e: parsed.e,
|
|
18
|
+
...(parsed.u !== undefined ? { u: parsed.u } : {}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
4
25
|
function oauthDoneHtml(success) {
|
|
5
26
|
const message = success ? 'manifest-oauth-success' : 'manifest-oauth-error';
|
|
6
27
|
const text = success
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeProviderBaseUrl = normalizeProviderBaseUrl;
|
|
4
|
+
exports.normalizeMinimaxSubscriptionBaseUrl = normalizeMinimaxSubscriptionBaseUrl;
|
|
5
|
+
function normalizeProviderBaseUrl(baseUrl) {
|
|
6
|
+
return baseUrl.replace(/\/+$/, '').replace(/\/v1$/, '');
|
|
7
|
+
}
|
|
8
|
+
const MINIMAX_SUBSCRIPTION_BASE_URLS = new Set([
|
|
9
|
+
'https://api.minimax.io/anthropic',
|
|
10
|
+
'https://api.minimaxi.com/anthropic',
|
|
11
|
+
]);
|
|
12
|
+
function normalizeMinimaxSubscriptionBaseUrl(baseUrl) {
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(baseUrl);
|
|
15
|
+
if (url.protocol !== 'https:' || url.username || url.password) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const normalized = normalizeProviderBaseUrl(`${url.origin}${url.pathname}`);
|
|
19
|
+
return MINIMAX_SUBSCRIPTION_BASE_URLS.has(normalized) ? normalized : null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=provider-base-url.js.map
|
|
@@ -4,21 +4,19 @@ exports.toResponsesRequest = toResponsesRequest;
|
|
|
4
4
|
exports.fromResponsesResponse = fromResponsesResponse;
|
|
5
5
|
exports.transformResponsesStreamChunk = transformResponsesStreamChunk;
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
|
+
const DEFAULT_INSTRUCTIONS = 'You are a helpful assistant.';
|
|
7
8
|
function toResponsesRequest(body, model) {
|
|
8
9
|
const messages = (body.messages ?? []);
|
|
9
|
-
const systemMsg = messages.find((m) => m.role === 'system');
|
|
10
10
|
const input = messages
|
|
11
|
-
.filter((m) => m.role !== 'system')
|
|
11
|
+
.filter((m) => m.role !== 'system' && m.role !== 'developer')
|
|
12
12
|
.map((m) => ({ role: m.role, content: convertContent(m.content, m.role) }));
|
|
13
13
|
const request = {
|
|
14
14
|
model,
|
|
15
15
|
input,
|
|
16
16
|
stream: body.stream !== false,
|
|
17
17
|
store: false,
|
|
18
|
+
instructions: extractInstructions(messages),
|
|
18
19
|
};
|
|
19
|
-
if (systemMsg && typeof systemMsg.content === 'string') {
|
|
20
|
-
request.instructions = systemMsg.content;
|
|
21
|
-
}
|
|
22
20
|
return request;
|
|
23
21
|
}
|
|
24
22
|
function fromResponsesResponse(data, model) {
|
|
@@ -114,6 +112,28 @@ function convertContent(content, role) {
|
|
|
114
112
|
return part;
|
|
115
113
|
});
|
|
116
114
|
}
|
|
115
|
+
function extractInstructions(messages) {
|
|
116
|
+
const parts = [];
|
|
117
|
+
for (const message of messages) {
|
|
118
|
+
if (message.role !== 'system' && message.role !== 'developer')
|
|
119
|
+
continue;
|
|
120
|
+
parts.push(...extractTextParts(message.content));
|
|
121
|
+
}
|
|
122
|
+
const instructions = parts
|
|
123
|
+
.map((part) => part.trim())
|
|
124
|
+
.filter(Boolean)
|
|
125
|
+
.join('\n\n');
|
|
126
|
+
return instructions || DEFAULT_INSTRUCTIONS;
|
|
127
|
+
}
|
|
128
|
+
function extractTextParts(content) {
|
|
129
|
+
if (typeof content === 'string')
|
|
130
|
+
return [content];
|
|
131
|
+
if (!Array.isArray(content))
|
|
132
|
+
return [];
|
|
133
|
+
return content
|
|
134
|
+
.filter((part) => part.type === 'text' && typeof part.text === 'string')
|
|
135
|
+
.map((part) => part.text);
|
|
136
|
+
}
|
|
117
137
|
function safeParse(str) {
|
|
118
138
|
try {
|
|
119
139
|
return JSON.parse(str);
|