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.
Files changed (128) hide show
  1. package/README.md +51 -12
  2. package/dist/client/185.c47663fefaeb0e5b.js +10 -0
  3. package/dist/client/562.9012cfd1fa04303d.js +10 -0
  4. package/dist/client/685.b5b1e0a5b825d253.js +10 -0
  5. package/dist/client/97.1bc5103fd9d995a8.js +10 -0
  6. package/dist/client/index.js +1 -1
  7. package/dist/client-v2/185.b552dc91ec2371ba.js +10 -0
  8. package/dist/client-v2/562.db2984167250b1be.js +10 -0
  9. package/dist/client-v2/685.cf16e5b829e06f85.js +10 -0
  10. package/dist/client-v2/97.96da323832251796.js +10 -0
  11. package/dist/client-v2/index.js +1 -1
  12. package/dist/externalVersion.js +8 -8
  13. package/dist/locale/en-US.json +38 -1
  14. package/dist/locale/vi-VN.json +177 -138
  15. package/dist/locale/zh-CN.json +177 -138
  16. package/dist/server/billing.js +17 -9
  17. package/dist/server/collections/ai-api-model-metadata.js +26 -0
  18. package/dist/server/collections/ai-api-model-prices.js +8 -0
  19. package/dist/server/collections/ai-api-response-records.js +101 -0
  20. package/dist/server/collections/ai-api-usage-records.js +1 -0
  21. package/dist/server/collections/ai-api-virtual-models.js +68 -0
  22. package/dist/server/middleware/response-record-resource.js +66 -0
  23. package/dist/server/middleware/role-permission.js +43 -18
  24. package/dist/server/migrations/20260827000000-add-cache-input-price.js +66 -0
  25. package/dist/server/migrations/20260901000000-remove-default-group-members.js +60 -0
  26. package/dist/server/migrations/20260902000000-seed-default-role-permissions.js +55 -0
  27. package/dist/server/migrations/20260903000000-seed-sample-response-records.js +170 -0
  28. package/dist/server/plugin.js +66 -16
  29. package/dist/server/routes/auth.js +16 -0
  30. package/dist/server/routes/chat-completions.js +38 -6
  31. package/dist/server/routes/completions.js +16 -4
  32. package/dist/server/routes/embeddings.js +34 -5
  33. package/dist/server/routes/models.js +29 -0
  34. package/dist/server/routes/responses.js +530 -0
  35. package/dist/server/routes/router.js +65 -10
  36. package/dist/server/usage.js +26 -4
  37. package/dist/server/utils/direct-llm-context.js +10 -9
  38. package/dist/server/utils/resolve-service.js +24 -0
  39. package/dist/server/utils/response-store.js +138 -0
  40. package/dist/server/utils/responses-format.js +686 -0
  41. package/dist/server/utils/responses-stream.js +330 -0
  42. package/dist/server/utils/virtual-models.js +238 -0
  43. package/dist/server/validation.js +45 -2
  44. package/dist/swagger.js +137 -0
  45. package/package.json +34 -32
  46. package/src/__tests__/locale.test.ts +43 -0
  47. package/src/client/__tests__/settings-registration.test.tsx +1 -0
  48. package/src/client/index.tsx +10 -10
  49. package/src/client/models/index.ts +12 -12
  50. package/src/client/plugin.tsx +9 -1
  51. package/src/client-v2/__tests__/settings-registration.test.tsx +1 -0
  52. package/src/client-v2/pages/ModelMetadataPage.tsx +44 -0
  53. package/src/client-v2/pages/ModelPricingPage.tsx +15 -0
  54. package/src/client-v2/pages/ModelRoutingPage.tsx +238 -0
  55. package/src/client-v2/pages/UsageGroupsPage.tsx +75 -38
  56. package/src/client-v2/plugin.tsx +8 -0
  57. package/src/index.ts +11 -11
  58. package/src/locale/en-US.json +38 -1
  59. package/src/locale/vi-VN.json +177 -138
  60. package/src/locale/zh-CN.json +177 -138
  61. package/src/server/__tests__/billing-quota.test.ts +8 -2
  62. package/src/server/__tests__/billing.test.ts +13 -0
  63. package/src/server/__tests__/embeddings.test.ts +184 -0
  64. package/src/server/__tests__/models.test.ts +21 -1
  65. package/src/server/__tests__/response-record-resource.test.ts +50 -0
  66. package/src/server/__tests__/response-store-integration.test.ts +341 -0
  67. package/src/server/__tests__/response-store.test.ts +195 -0
  68. package/src/server/__tests__/responses-contract.test.ts +469 -0
  69. package/src/server/__tests__/responses-format.test.ts +299 -0
  70. package/src/server/__tests__/responses-router.test.ts +182 -0
  71. package/src/server/__tests__/responses-streaming.test.ts +368 -0
  72. package/src/server/__tests__/responses.test.ts +462 -0
  73. package/src/server/__tests__/role-permission.test.ts +139 -0
  74. package/src/server/__tests__/seed-role-permission.test.ts +88 -0
  75. package/src/server/__tests__/types/responses-sdk.types.test-d.ts +23 -0
  76. package/src/server/__tests__/usage-groups.test.ts +96 -0
  77. package/src/server/__tests__/usage-route.test.ts +1 -0
  78. package/src/server/__tests__/usage.test.ts +15 -0
  79. package/src/server/__tests__/validation.test.ts +66 -7
  80. package/src/server/__tests__/virtual-model-routing.test.ts +589 -0
  81. package/src/server/billing.ts +29 -6
  82. package/src/server/collections/ai-api-model-metadata.ts +26 -0
  83. package/src/server/collections/ai-api-model-prices.ts +8 -0
  84. package/src/server/collections/ai-api-response-records.ts +77 -0
  85. package/src/server/collections/ai-api-usage-records.ts +1 -0
  86. package/src/server/collections/ai-api-virtual-models.ts +58 -0
  87. package/src/server/middleware/response-record-resource.ts +44 -0
  88. package/src/server/middleware/role-permission.ts +69 -35
  89. package/src/server/migrations/20260827000000-add-cache-input-price.ts +49 -0
  90. package/src/server/migrations/20260901000000-remove-default-group-members.ts +56 -0
  91. package/src/server/migrations/20260902000000-seed-default-role-permissions.ts +46 -0
  92. package/src/server/migrations/20260903000000-seed-sample-response-records.ts +162 -0
  93. package/src/server/plugin.ts +84 -20
  94. package/src/server/resource/ai-api-config.ts +2 -1
  95. package/src/server/routes/agent-completions.ts +3 -0
  96. package/src/server/routes/auth.ts +21 -1
  97. package/src/server/routes/chat-completions.ts +34 -10
  98. package/src/server/routes/completions.ts +16 -4
  99. package/src/server/routes/embeddings.ts +42 -6
  100. package/src/server/routes/models.ts +34 -0
  101. package/src/server/routes/responses.ts +640 -0
  102. package/src/server/routes/router.ts +81 -12
  103. package/src/server/services/__tests__/file-processor.test.ts +1 -0
  104. package/src/server/usage.ts +30 -2
  105. package/src/server/utils/app-observability.ts +1 -1
  106. package/src/server/utils/direct-llm-context.ts +23 -12
  107. package/src/server/utils/openai-format.ts +1 -0
  108. package/src/server/utils/resolve-service.ts +39 -1
  109. package/src/server/utils/response-store.ts +148 -0
  110. package/src/server/utils/responses-format.ts +974 -0
  111. package/src/server/utils/responses-stream.ts +384 -0
  112. package/src/server/utils/virtual-models.ts +320 -0
  113. package/src/server/validation.ts +50 -0
  114. package/src/swagger.ts +139 -0
  115. package/dist/client/562.44b16aad4718b4c7.js +0 -10
  116. package/dist/client/685.ae483e17b6b49c98.js +0 -10
  117. package/dist/client/97.9b6b2d2b01a4c060.js +0 -10
  118. package/dist/client-v2/562.45d5c504433be38b.js +0 -10
  119. package/dist/client-v2/685.1030370b309b7d4b.js +0 -10
  120. package/dist/client-v2/97.36a42eff36bb3d8a.js +0 -10
  121. package/dist/server/collections/ai-api-user-permissions.js +0 -67
  122. package/dist/server/collections/ai-api-user-quota-buckets.js +0 -54
  123. package/dist/server/collections/ai-api-user-quota-policies.js +0 -63
  124. package/dist/server/resource/ai-api-usage-groups.js +0 -168
  125. package/src/server/collections/ai-api-user-permissions.ts +0 -46
  126. package/src/server/collections/ai-api-user-quota-buckets.ts +0 -24
  127. package/src/server/collections/ai-api-user-quota-policies.ts +0 -33
  128. package/src/server/resource/ai-api-usage-groups.ts +0 -171
@@ -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
- roleName: state.currentRole || ((_b = state.currentRoles) == null ? void 0 : _b[0]) || "unknown",
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 contextWindow = positiveInteger(getValue(row, "contextWindow"));
234
- const maxCompletionTokens = positiveInteger(getValue(row, "maxCompletionTokens"));
235
- if (!contextWindow || !maxCompletionTokens) {
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
+ });