plugin-ai-api 1.1.0 → 1.1.2
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/README.md +51 -12
- package/dist/client/185.c47663fefaeb0e5b.js +10 -0
- package/dist/client/562.9012cfd1fa04303d.js +10 -0
- package/dist/client/685.b5b1e0a5b825d253.js +10 -0
- package/dist/client/97.1bc5103fd9d995a8.js +10 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/185.b552dc91ec2371ba.js +10 -0
- package/dist/client-v2/562.db2984167250b1be.js +10 -0
- package/dist/client-v2/685.cf16e5b829e06f85.js +10 -0
- package/dist/client-v2/97.96da323832251796.js +10 -0
- package/dist/client-v2/index.js +1 -1
- package/dist/externalVersion.js +8 -8
- package/dist/locale/en-US.json +38 -1
- package/dist/locale/vi-VN.json +177 -138
- package/dist/locale/zh-CN.json +177 -138
- package/dist/server/billing.js +17 -9
- package/dist/server/collections/ai-api-model-metadata.js +26 -0
- package/dist/server/collections/ai-api-model-prices.js +8 -0
- package/dist/server/collections/ai-api-response-records.js +101 -0
- package/dist/server/collections/ai-api-usage-records.js +1 -0
- package/dist/server/collections/ai-api-virtual-models.js +68 -0
- package/dist/server/middleware/response-record-resource.js +66 -0
- package/dist/server/middleware/role-permission.js +43 -18
- package/dist/server/migrations/20260827000000-add-cache-input-price.js +66 -0
- package/dist/server/migrations/20260901000000-remove-default-group-members.js +60 -0
- package/dist/server/migrations/20260902000000-seed-default-role-permissions.js +55 -0
- package/dist/server/migrations/20260903000000-seed-sample-response-records.js +170 -0
- package/dist/server/plugin.js +66 -16
- package/dist/server/routes/auth.js +16 -0
- package/dist/server/routes/chat-completions.js +38 -6
- package/dist/server/routes/completions.js +16 -4
- package/dist/server/routes/embeddings.js +34 -5
- package/dist/server/routes/models.js +29 -0
- package/dist/server/routes/responses.js +530 -0
- package/dist/server/routes/router.js +65 -10
- package/dist/server/usage.js +26 -4
- package/dist/server/utils/direct-llm-context.js +10 -9
- package/dist/server/utils/resolve-service.js +24 -0
- package/dist/server/utils/response-store.js +138 -0
- package/dist/server/utils/responses-format.js +686 -0
- package/dist/server/utils/responses-stream.js +330 -0
- package/dist/server/utils/virtual-models.js +238 -0
- package/dist/server/validation.js +45 -2
- package/dist/swagger.js +137 -0
- package/package.json +34 -32
- package/src/__tests__/locale.test.ts +43 -0
- package/src/client/__tests__/settings-registration.test.tsx +1 -0
- package/src/client/index.tsx +10 -10
- package/src/client/models/index.ts +12 -12
- package/src/client/plugin.tsx +9 -1
- package/src/client-v2/__tests__/settings-registration.test.tsx +1 -0
- package/src/client-v2/pages/ModelMetadataPage.tsx +44 -0
- package/src/client-v2/pages/ModelPricingPage.tsx +15 -0
- package/src/client-v2/pages/ModelRoutingPage.tsx +238 -0
- package/src/client-v2/pages/UsageGroupsPage.tsx +75 -38
- package/src/client-v2/plugin.tsx +8 -0
- package/src/index.ts +11 -11
- package/src/locale/en-US.json +38 -1
- package/src/locale/vi-VN.json +177 -138
- package/src/locale/zh-CN.json +177 -138
- package/src/server/__tests__/billing-quota.test.ts +8 -2
- package/src/server/__tests__/billing.test.ts +13 -0
- package/src/server/__tests__/embeddings.test.ts +184 -0
- package/src/server/__tests__/models.test.ts +21 -1
- package/src/server/__tests__/response-record-resource.test.ts +50 -0
- package/src/server/__tests__/response-store-integration.test.ts +341 -0
- package/src/server/__tests__/response-store.test.ts +195 -0
- package/src/server/__tests__/responses-contract.test.ts +469 -0
- package/src/server/__tests__/responses-format.test.ts +299 -0
- package/src/server/__tests__/responses-router.test.ts +182 -0
- package/src/server/__tests__/responses-streaming.test.ts +368 -0
- package/src/server/__tests__/responses.test.ts +462 -0
- package/src/server/__tests__/role-permission.test.ts +139 -0
- package/src/server/__tests__/seed-role-permission.test.ts +88 -0
- package/src/server/__tests__/types/responses-sdk.types.test-d.ts +23 -0
- package/src/server/__tests__/usage-groups.test.ts +96 -0
- package/src/server/__tests__/usage-route.test.ts +1 -0
- package/src/server/__tests__/usage.test.ts +15 -0
- package/src/server/__tests__/validation.test.ts +66 -7
- package/src/server/__tests__/virtual-model-routing.test.ts +589 -0
- package/src/server/billing.ts +29 -6
- package/src/server/collections/ai-api-model-metadata.ts +26 -0
- package/src/server/collections/ai-api-model-prices.ts +8 -0
- package/src/server/collections/ai-api-response-records.ts +77 -0
- package/src/server/collections/ai-api-usage-records.ts +1 -0
- package/src/server/collections/ai-api-virtual-models.ts +58 -0
- package/src/server/middleware/response-record-resource.ts +44 -0
- package/src/server/middleware/role-permission.ts +69 -35
- package/src/server/migrations/20260827000000-add-cache-input-price.ts +49 -0
- package/src/server/migrations/20260901000000-remove-default-group-members.ts +56 -0
- package/src/server/migrations/20260902000000-seed-default-role-permissions.ts +46 -0
- package/src/server/migrations/20260903000000-seed-sample-response-records.ts +162 -0
- package/src/server/plugin.ts +84 -20
- package/src/server/resource/ai-api-config.ts +2 -1
- package/src/server/routes/agent-completions.ts +3 -0
- package/src/server/routes/auth.ts +21 -1
- package/src/server/routes/chat-completions.ts +34 -10
- package/src/server/routes/completions.ts +16 -4
- package/src/server/routes/embeddings.ts +42 -6
- package/src/server/routes/models.ts +34 -0
- package/src/server/routes/responses.ts +640 -0
- package/src/server/routes/router.ts +81 -12
- package/src/server/services/__tests__/file-processor.test.ts +1 -0
- package/src/server/usage.ts +30 -2
- package/src/server/utils/app-observability.ts +1 -1
- package/src/server/utils/direct-llm-context.ts +23 -12
- package/src/server/utils/openai-format.ts +1 -0
- package/src/server/utils/resolve-service.ts +39 -1
- package/src/server/utils/response-store.ts +148 -0
- package/src/server/utils/responses-format.ts +974 -0
- package/src/server/utils/responses-stream.ts +384 -0
- package/src/server/utils/virtual-models.ts +320 -0
- package/src/server/validation.ts +50 -0
- package/src/swagger.ts +139 -0
- package/dist/client/562.44b16aad4718b4c7.js +0 -10
- package/dist/client/685.ae483e17b6b49c98.js +0 -10
- package/dist/client/97.9b6b2d2b01a4c060.js +0 -10
- package/dist/client-v2/562.45d5c504433be38b.js +0 -10
- package/dist/client-v2/685.1030370b309b7d4b.js +0 -10
- package/dist/client-v2/97.36a42eff36bb3d8a.js +0 -10
- package/dist/server/collections/ai-api-user-permissions.js +0 -67
- package/dist/server/collections/ai-api-user-quota-buckets.js +0 -54
- package/dist/server/collections/ai-api-user-quota-policies.js +0 -63
- package/dist/server/resource/ai-api-usage-groups.js +0 -168
- package/src/server/collections/ai-api-user-permissions.ts +0 -46
- package/src/server/collections/ai-api-user-quota-buckets.ts +0 -24
- package/src/server/collections/ai-api-user-quota-policies.ts +0 -33
- package/src/server/resource/ai-api-usage-groups.ts +0 -171
package/dist/server/usage.js
CHANGED
|
@@ -60,6 +60,20 @@ function extractPromptCacheTokens(source) {
|
|
|
60
60
|
if (typeof source.cache_read === "number") return source.cache_read;
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
|
+
function extractReasoningTokens(source) {
|
|
64
|
+
const detailCandidates = [
|
|
65
|
+
source.output_token_details,
|
|
66
|
+
source.output_tokens_details,
|
|
67
|
+
source.completion_tokens_details
|
|
68
|
+
];
|
|
69
|
+
for (const details of detailCandidates) {
|
|
70
|
+
if (!details || typeof details !== "object") continue;
|
|
71
|
+
const record = details;
|
|
72
|
+
const value = normalizeTokenCount(record.reasoning ?? record.reasoning_tokens);
|
|
73
|
+
if (value !== null) return value;
|
|
74
|
+
}
|
|
75
|
+
return normalizeTokenCount(source.reasoning_tokens ?? source.reasoningTokens);
|
|
76
|
+
}
|
|
63
77
|
function normalizeUsage(value) {
|
|
64
78
|
if (!value || typeof value !== "object") return void 0;
|
|
65
79
|
const source = value;
|
|
@@ -77,7 +91,8 @@ function normalizeUsage(value) {
|
|
|
77
91
|
// Keep normalizeUsage idempotent: streaming routes normalize the chunk
|
|
78
92
|
// usage once and hand the result to setAiApiUsageResult, which normalizes
|
|
79
93
|
// again — an already-extracted prompt_cache_tokens must survive that pass.
|
|
80
|
-
prompt_cache_tokens: extractPromptCacheTokens(source) ?? normalizeTokenCount(source.prompt_cache_tokens)
|
|
94
|
+
prompt_cache_tokens: extractPromptCacheTokens(source) ?? normalizeTokenCount(source.prompt_cache_tokens),
|
|
95
|
+
reasoning_tokens: extractReasoningTokens(source) ?? normalizeTokenCount(source.reasoning_tokens)
|
|
81
96
|
};
|
|
82
97
|
}
|
|
83
98
|
function setAiApiUsageResult(ctx, value, metadata = {}, responseMetadata) {
|
|
@@ -126,7 +141,9 @@ async function startUsageRecord(ctx, requestId, endpoint, model, streaming, mode
|
|
|
126
141
|
values: {
|
|
127
142
|
requestId,
|
|
128
143
|
userId,
|
|
129
|
-
|
|
144
|
+
// When several roles granted access (union semantics), record all of them so the
|
|
145
|
+
// audit trail shows the full set, not just the first role in the list.
|
|
146
|
+
roleName: ((_b = state.currentRoles) == null ? void 0 : _b.length) ? state.currentRoles.join(",") : state.currentRole || "unknown",
|
|
130
147
|
authType: state.aiApiAuthType || (oauth ? "oidc" : "unknown"),
|
|
131
148
|
oauthClientId: oauth == null ? void 0 : oauth.clientId,
|
|
132
149
|
oauthSubject: oauth == null ? void 0 : oauth.subject,
|
|
@@ -141,7 +158,7 @@ async function startUsageRecord(ctx, requestId, endpoint, model, streaming, mode
|
|
|
141
158
|
messageCount: messages == null ? void 0 : messages.length,
|
|
142
159
|
promptCount,
|
|
143
160
|
embeddingInputCount,
|
|
144
|
-
requestedMaxTokens: body.max_completion_tokens ?? body.max_tokens
|
|
161
|
+
requestedMaxTokens: body.max_output_tokens ?? body.max_completion_tokens ?? body.max_tokens
|
|
145
162
|
}
|
|
146
163
|
}
|
|
147
164
|
});
|
|
@@ -176,6 +193,7 @@ async function finishUsageRecord(ctx, id, startedAt, status) {
|
|
|
176
193
|
quotaPolicyId: billing.quotaPolicyId ?? null,
|
|
177
194
|
groupId: billing.groupId ?? null,
|
|
178
195
|
inputPricePerMillionTokens: billing.inputPricePerMillionTokens ?? null,
|
|
196
|
+
cacheInputPricePerMillionTokens: billing.cacheInputPricePerMillionTokens ?? null,
|
|
179
197
|
outputPricePerMillionTokens: billing.outputPricePerMillionTokens ?? null,
|
|
180
198
|
fixedCostPerRequest: billing.fixedCostPerRequest ?? null,
|
|
181
199
|
providerRequestId: usageResult.providerRequestId ?? null,
|
|
@@ -183,7 +201,11 @@ async function finishUsageRecord(ctx, id, startedAt, status) {
|
|
|
183
201
|
durationMs: Date.now() - startedAt,
|
|
184
202
|
responseMetadata: {
|
|
185
203
|
usageSource: usageResult.source,
|
|
186
|
-
...gatewayResponseId ? { gatewayResponseId } : {}
|
|
204
|
+
...gatewayResponseId ? { gatewayResponseId } : {},
|
|
205
|
+
// When the request went through a virtual alias, keep both the alias and why
|
|
206
|
+
// it routed there; resolvedModel (from billing state) already holds the concrete model.
|
|
207
|
+
...state.aiApiVirtualModel ? { virtualModel: state.aiApiVirtualModel } : {},
|
|
208
|
+
...state.aiApiRoutingReason ? { routingReason: state.aiApiRoutingReason } : {}
|
|
187
209
|
}
|
|
188
210
|
};
|
|
189
211
|
await ctx.db.getRepository("aiApiUsageRecords").update({ filterByTk: id, values });
|
|
@@ -26,6 +26,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
var direct_llm_context_exports = {};
|
|
28
28
|
__export(direct_llm_context_exports, {
|
|
29
|
+
DEFAULT_CONTEXT_WINDOW: () => DEFAULT_CONTEXT_WINDOW,
|
|
30
|
+
DEFAULT_MAX_COMPLETION_TOKENS: () => DEFAULT_MAX_COMPLETION_TOKENS,
|
|
29
31
|
DirectLlmContextError: () => DirectLlmContextError,
|
|
30
32
|
parseImageDimensions: () => parseImageDimensions,
|
|
31
33
|
prepareDirectLlmContext: () => prepareDirectLlmContext
|
|
@@ -226,18 +228,15 @@ function messagesWithTurns(messages, turns) {
|
|
|
226
228
|
const retainedMessages = new Set(turns.flat());
|
|
227
229
|
return messages.filter((message) => isInstruction(message) || retainedMessages.has(message));
|
|
228
230
|
}
|
|
231
|
+
const DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
232
|
+
const DEFAULT_MAX_COMPLETION_TOKENS = 8192;
|
|
229
233
|
async function loadModelMetadata(ctx, serviceName, modelId) {
|
|
230
234
|
const row = await ctx.db.getRepository("aiApiModelMetadata").findOne({
|
|
231
235
|
filter: { llmService: serviceName, model: modelId, enabled: true }
|
|
232
236
|
});
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
throw new DirectLlmContextError(
|
|
237
|
-
"model_context_metadata_not_configured",
|
|
238
|
-
`Context metadata is not configured for '${serviceName}/${modelId}'. Configure context window and max completion tokens.`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
237
|
+
const config = await (0, import_request_cache.getAiApiConfig)(ctx);
|
|
238
|
+
const contextWindow = positiveInteger(getValue(row, "contextWindow")) ?? positiveInteger(getValue(config, "defaultContextWindow")) ?? DEFAULT_CONTEXT_WINDOW;
|
|
239
|
+
const maxCompletionTokens = positiveInteger(getValue(row, "maxCompletionTokens")) ?? positiveInteger(getValue(config, "defaultMaxCompletionTokens")) ?? DEFAULT_MAX_COMPLETION_TOKENS;
|
|
241
240
|
const systemPromptValue = getValue(row, "systemPrompt");
|
|
242
241
|
const systemPrompt = typeof systemPromptValue === "string" ? systemPromptValue.trim() : "";
|
|
243
242
|
return { contextWindow, maxCompletionTokens, ...systemPrompt ? { systemPrompt } : {} };
|
|
@@ -262,7 +261,7 @@ function resolveReservedOutputTokens(options, metadata) {
|
|
|
262
261
|
async function prepareDirectLlmContext(ctx, options) {
|
|
263
262
|
const [metadata, behavior] = await Promise.all([
|
|
264
263
|
loadModelMetadata(ctx, options.serviceName, options.modelId),
|
|
265
|
-
resolveOverflowBehavior(ctx)
|
|
264
|
+
options.overflowBehavior ? Promise.resolve(options.overflowBehavior) : resolveOverflowBehavior(ctx)
|
|
266
265
|
]);
|
|
267
266
|
const reservedOutputTokens = resolveReservedOutputTokens(options, metadata);
|
|
268
267
|
const inputTokenBudget = metadata.contextWindow - reservedOutputTokens;
|
|
@@ -315,6 +314,8 @@ async function prepareDirectLlmContext(ctx, options) {
|
|
|
315
314
|
}
|
|
316
315
|
// Annotate the CommonJS export names for ESM import in node:
|
|
317
316
|
0 && (module.exports = {
|
|
317
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
318
|
+
DEFAULT_MAX_COMPLETION_TOKENS,
|
|
318
319
|
DirectLlmContextError,
|
|
319
320
|
parseImageDimensions,
|
|
320
321
|
prepareDirectLlmContext
|
|
@@ -27,6 +27,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
27
27
|
var resolve_service_exports = {};
|
|
28
28
|
__export(resolve_service_exports, {
|
|
29
29
|
resolveLlmService: () => resolveLlmService,
|
|
30
|
+
resolveModelReference: () => resolveModelReference,
|
|
30
31
|
resolveModelString: () => resolveModelString
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(resolve_service_exports);
|
|
@@ -74,8 +75,31 @@ async function resolveModelString(ctx, modelString) {
|
|
|
74
75
|
}
|
|
75
76
|
return null;
|
|
76
77
|
}
|
|
78
|
+
async function resolveModelReference(ctx, reference) {
|
|
79
|
+
const repo = ctx.db.getRepository("llmServices");
|
|
80
|
+
const slashPositions = [];
|
|
81
|
+
for (let i = 0; i < reference.length; i++) {
|
|
82
|
+
if (reference[i] === "/") {
|
|
83
|
+
slashPositions.push(i);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const pos of slashPositions) {
|
|
87
|
+
const serviceKey = reference.substring(0, pos);
|
|
88
|
+
const modelId = reference.substring(pos + 1);
|
|
89
|
+
if (!serviceKey || !modelId) continue;
|
|
90
|
+
let service = await repo.findOne({ filter: { name: serviceKey } });
|
|
91
|
+
if (!service) {
|
|
92
|
+
service = await repo.findOne({ filter: { title: serviceKey } });
|
|
93
|
+
}
|
|
94
|
+
if (service) {
|
|
95
|
+
return { service, modelId };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
77
100
|
// Annotate the CommonJS export names for ESM import in node:
|
|
78
101
|
0 && (module.exports = {
|
|
79
102
|
resolveLlmService,
|
|
103
|
+
resolveModelReference,
|
|
80
104
|
resolveModelString
|
|
81
105
|
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var response_store_exports = {};
|
|
28
|
+
__export(response_store_exports, {
|
|
29
|
+
RESPONSE_RETENTION_MS: () => RESPONSE_RETENTION_MS,
|
|
30
|
+
cleanupExpiredResponseRecords: () => cleanupExpiredResponseRecords,
|
|
31
|
+
deleteResponseRecord: () => deleteResponseRecord,
|
|
32
|
+
getResponseRecord: () => getResponseRecord,
|
|
33
|
+
loadConversationChain: () => loadConversationChain,
|
|
34
|
+
storeResponseRecord: () => storeResponseRecord
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(response_store_exports);
|
|
37
|
+
var import_responses_format = require("./responses-format");
|
|
38
|
+
const RETENTION_DAYS = 30;
|
|
39
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
40
|
+
const RESPONSE_RETENTION_MS = RETENTION_DAYS * MS_PER_DAY;
|
|
41
|
+
function valueOf(model, key) {
|
|
42
|
+
if (!model) return void 0;
|
|
43
|
+
if (typeof model.get === "function") {
|
|
44
|
+
return model.get(key);
|
|
45
|
+
}
|
|
46
|
+
return model[key];
|
|
47
|
+
}
|
|
48
|
+
function isExpired(value) {
|
|
49
|
+
if (value === void 0 || value === null || value === "") return true;
|
|
50
|
+
const timestamp = value instanceof Date ? value.getTime() : new Date(String(value)).getTime();
|
|
51
|
+
return !Number.isFinite(timestamp) || timestamp <= Date.now();
|
|
52
|
+
}
|
|
53
|
+
async function storeResponseRecord(ctx, response, requestBody, userId) {
|
|
54
|
+
if (requestBody.store === false) return;
|
|
55
|
+
await ctx.db.getRepository("aiApiResponseRecords").create({
|
|
56
|
+
values: {
|
|
57
|
+
responseId: response.id,
|
|
58
|
+
userId,
|
|
59
|
+
model: response.model,
|
|
60
|
+
input: requestBody.input,
|
|
61
|
+
output: response,
|
|
62
|
+
previousResponseId: response.previous_response_id,
|
|
63
|
+
metadata: response.metadata,
|
|
64
|
+
expiresAt: new Date(Date.now() + RESPONSE_RETENTION_MS)
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function getResponseRecord(ctx, responseId, userId) {
|
|
69
|
+
const row = await ctx.db.getRepository("aiApiResponseRecords").findOne({
|
|
70
|
+
filter: { responseId, userId }
|
|
71
|
+
});
|
|
72
|
+
if (!row || isExpired(valueOf(row, "expiresAt"))) return null;
|
|
73
|
+
return {
|
|
74
|
+
id: valueOf(row, "id"),
|
|
75
|
+
responseId: valueOf(row, "responseId"),
|
|
76
|
+
userId: valueOf(row, "userId"),
|
|
77
|
+
model: valueOf(row, "model"),
|
|
78
|
+
input: valueOf(row, "input"),
|
|
79
|
+
output: valueOf(row, "output"),
|
|
80
|
+
previousResponseId: valueOf(row, "previousResponseId") ?? void 0,
|
|
81
|
+
metadata: valueOf(row, "metadata"),
|
|
82
|
+
expiresAt: valueOf(row, "expiresAt")
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function responseOutputToMessages(output) {
|
|
86
|
+
return (0, import_responses_format.responsesInputToMessages)(output.output);
|
|
87
|
+
}
|
|
88
|
+
async function loadConversationChain(ctx, responseId, userId, maxDepth = 100) {
|
|
89
|
+
const records = [];
|
|
90
|
+
const visited = /* @__PURE__ */ new Set();
|
|
91
|
+
let currentId = responseId;
|
|
92
|
+
while (currentId) {
|
|
93
|
+
if (records.length >= maxDepth || visited.has(currentId)) return null;
|
|
94
|
+
visited.add(currentId);
|
|
95
|
+
const record = await getResponseRecord(ctx, currentId, userId);
|
|
96
|
+
if (!record) return null;
|
|
97
|
+
records.unshift(record);
|
|
98
|
+
currentId = record.previousResponseId;
|
|
99
|
+
}
|
|
100
|
+
return records.flatMap((record) => [
|
|
101
|
+
...(0, import_responses_format.responsesInputToMessages)(record.input),
|
|
102
|
+
...responseOutputToMessages(record.output)
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
async function cleanupExpiredResponseRecords(ctx) {
|
|
106
|
+
const repo = ctx.db.getRepository("aiApiResponseRecords");
|
|
107
|
+
const now = /* @__PURE__ */ new Date();
|
|
108
|
+
let deleted = 0;
|
|
109
|
+
for (; ; ) {
|
|
110
|
+
const expired = await repo.find({
|
|
111
|
+
filter: { expiresAt: { $lt: now } },
|
|
112
|
+
fields: ["id"],
|
|
113
|
+
limit: 1e3,
|
|
114
|
+
sort: "id"
|
|
115
|
+
});
|
|
116
|
+
const ids = expired.map((record) => record.get("id"));
|
|
117
|
+
if (ids.length === 0) return deleted;
|
|
118
|
+
const count = await repo.destroy({ filterByTk: ids, individualHooks: false });
|
|
119
|
+
if (typeof count === "number") deleted += count;
|
|
120
|
+
else deleted += ids.length;
|
|
121
|
+
if (count === 0) return deleted;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function deleteResponseRecord(ctx, responseId, userId) {
|
|
125
|
+
const record = await getResponseRecord(ctx, responseId, userId);
|
|
126
|
+
if (!record) return false;
|
|
127
|
+
await ctx.db.getRepository("aiApiResponseRecords").destroy({ filterByTk: record.id });
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
131
|
+
0 && (module.exports = {
|
|
132
|
+
RESPONSE_RETENTION_MS,
|
|
133
|
+
cleanupExpiredResponseRecords,
|
|
134
|
+
deleteResponseRecord,
|
|
135
|
+
getResponseRecord,
|
|
136
|
+
loadConversationChain,
|
|
137
|
+
storeResponseRecord
|
|
138
|
+
});
|