plugin-ai-api 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/client/97.1bc5103fd9d995a8.js +10 -0
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/97.96da323832251796.js +10 -0
  4. package/dist/client-v2/index.js +1 -1
  5. package/dist/externalVersion.js +8 -8
  6. package/dist/locale/en-US.json +139 -138
  7. package/dist/locale/vi-VN.json +139 -138
  8. package/dist/locale/zh-CN.json +139 -138
  9. package/dist/server/billing.js +17 -9
  10. package/dist/server/collections/ai-api-model-prices.js +8 -0
  11. package/dist/server/collections/ai-api-usage-records.js +1 -0
  12. package/dist/server/migrations/20260827000000-add-cache-input-price.js +66 -0
  13. package/dist/server/routes/auth.js +16 -0
  14. package/dist/server/routes/embeddings.js +11 -1
  15. package/dist/server/usage.js +1 -0
  16. package/dist/server/utils/direct-llm-context.js +9 -8
  17. package/dist/server/validation.js +1 -0
  18. package/package.json +1 -1
  19. package/src/client/index.tsx +10 -10
  20. package/src/client/models/index.ts +12 -12
  21. package/src/client-v2/pages/ModelPricingPage.tsx +15 -0
  22. package/src/index.ts +11 -11
  23. package/src/locale/en-US.json +139 -138
  24. package/src/locale/vi-VN.json +139 -138
  25. package/src/locale/zh-CN.json +139 -138
  26. package/src/server/__tests__/billing-quota.test.ts +8 -2
  27. package/src/server/__tests__/billing.test.ts +13 -0
  28. package/src/server/__tests__/usage.test.ts +1 -0
  29. package/src/server/billing.ts +29 -6
  30. package/src/server/collections/ai-api-model-prices.ts +8 -0
  31. package/src/server/collections/ai-api-usage-records.ts +1 -0
  32. package/src/server/migrations/20260827000000-add-cache-input-price.ts +49 -0
  33. package/src/server/routes/auth.ts +21 -1
  34. package/src/server/routes/embeddings.ts +17 -3
  35. package/src/server/usage.ts +1 -0
  36. package/src/server/utils/direct-llm-context.ts +21 -11
  37. package/src/server/validation.ts +1 -0
  38. package/dist/client/97.9b6b2d2b01a4c060.js +0 -10
  39. package/dist/client-v2/97.36a42eff36bb3d8a.js +0 -10
@@ -86,9 +86,15 @@ function formatUnits(value, scale) {
86
86
  function divideRounded(value, divisor) {
87
87
  return (value + divisor / 2n) / divisor;
88
88
  }
89
- function calculateCostUnits(inputTokens, outputTokens, price) {
89
+ function calculateCostUnits(inputTokens, outputTokens, promptCacheTokens, price) {
90
+ const cachedInputTokens = Math.min(Math.max(promptCacheTokens, 0), inputTokens);
91
+ const uncachedInputTokens = inputTokens - cachedInputTokens;
90
92
  const input = divideRounded(
91
- BigInt(inputTokens) * decimalUnits(price.inputPricePerMillionTokens, PRICE_SCALE),
93
+ BigInt(uncachedInputTokens) * decimalUnits(price.inputPricePerMillionTokens, PRICE_SCALE),
94
+ PRICE_TO_COST_DIVISOR
95
+ );
96
+ const cacheInput = divideRounded(
97
+ BigInt(cachedInputTokens) * decimalUnits(price.cacheInputPricePerMillionTokens, PRICE_SCALE),
92
98
  PRICE_TO_COST_DIVISOR
93
99
  );
94
100
  const output = divideRounded(
@@ -96,10 +102,10 @@ function calculateCostUnits(inputTokens, outputTokens, price) {
96
102
  PRICE_TO_COST_DIVISOR
97
103
  );
98
104
  const fixed = divideRounded(decimalUnits(price.fixedCostPerRequest, PRICE_SCALE), 100n);
99
- return input + output + fixed;
105
+ return input + cacheInput + output + fixed;
100
106
  }
101
- function calculateLlmCost(inputTokens, outputTokens, price) {
102
- return formatUnits(calculateCostUnits(inputTokens, outputTokens, price), COST_SCALE);
107
+ function calculateLlmCost(inputTokens, outputTokens, price, promptCacheTokens = 0) {
108
+ return formatUnits(calculateCostUnits(inputTokens, outputTokens, promptCacheTokens, price), COST_SCALE);
103
109
  }
104
110
  function normalizePositiveInteger(value, fallback) {
105
111
  const parsed = Number(value);
@@ -145,6 +151,7 @@ async function findPrice(ctx, service, modelId) {
145
151
  id: valueOf(price, "id"),
146
152
  currency: valueOf(price, "currency"),
147
153
  inputPricePerMillionTokens: decimalString(valueOf(price, "inputPricePerMillionTokens"), PRICE_SCALE),
154
+ cacheInputPricePerMillionTokens: decimalString(valueOf(price, "cacheInputPricePerMillionTokens"), PRICE_SCALE),
148
155
  outputPricePerMillionTokens: decimalString(valueOf(price, "outputPricePerMillionTokens"), PRICE_SCALE),
149
156
  fixedCostPerRequest: decimalString(valueOf(price, "fixedCostPerRequest"), PRICE_SCALE)
150
157
  };
@@ -252,7 +259,7 @@ function usageNumbers(usage) {
252
259
  };
253
260
  }
254
261
  async function finalizeLlmBilling(ctx, providerUsage, succeeded) {
255
- var _a, _b, _c, _d, _e, _f;
262
+ var _a, _b, _c, _d, _e, _f, _g;
256
263
  const billing = stateOf(ctx).aiApiLlmBilling;
257
264
  if (!billing) return {};
258
265
  let numbers = usageNumbers(providerUsage);
@@ -269,7 +276,7 @@ async function finalizeLlmBilling(ctx, providerUsage, succeeded) {
269
276
  } else {
270
277
  costStatus = billing.price ? "usage_unavailable" : "unpriced";
271
278
  }
272
- const cost = numbers && billing.price ? calculateLlmCost(numbers.input, numbers.output, billing.price) : void 0;
279
+ const cost = numbers && billing.price ? calculateLlmCost(numbers.input, numbers.output, billing.price, (providerUsage == null ? void 0 : providerUsage.prompt_cache_tokens) ?? 0) : void 0;
273
280
  const reservation = billing.reservation;
274
281
  if (reservation) {
275
282
  const Bucket = ctx.db.getModel("aiApiGroupQuotaBuckets");
@@ -315,8 +322,9 @@ async function finalizeLlmBilling(ctx, providerUsage, succeeded) {
315
322
  groupId: reservation == null ? void 0 : reservation.groupId,
316
323
  quotaMode: reservation == null ? void 0 : reservation.quotaMode,
317
324
  inputPricePerMillionTokens: (_d = billing.price) == null ? void 0 : _d.inputPricePerMillionTokens,
318
- outputPricePerMillionTokens: (_e = billing.price) == null ? void 0 : _e.outputPricePerMillionTokens,
319
- fixedCostPerRequest: (_f = billing.price) == null ? void 0 : _f.fixedCostPerRequest
325
+ cacheInputPricePerMillionTokens: (_e = billing.price) == null ? void 0 : _e.cacheInputPricePerMillionTokens,
326
+ outputPricePerMillionTokens: (_f = billing.price) == null ? void 0 : _f.outputPricePerMillionTokens,
327
+ fixedCostPerRequest: (_g = billing.price) == null ? void 0 : _g.fixedCostPerRequest
320
328
  };
321
329
  }
322
330
  // Annotate the CommonJS export names for ESM import in node:
@@ -39,6 +39,14 @@ var ai_api_model_prices_default = (0, import_database.defineCollection)({
39
39
  { name: "model", type: "string", allowNull: false, index: true },
40
40
  { name: "currency", type: "string", allowNull: false, defaultValue: "USD" },
41
41
  { name: "inputPricePerMillionTokens", type: "decimal", precision: 20, scale: 10, allowNull: false },
42
+ {
43
+ name: "cacheInputPricePerMillionTokens",
44
+ type: "decimal",
45
+ precision: 20,
46
+ scale: 10,
47
+ allowNull: false,
48
+ defaultValue: 0
49
+ },
42
50
  { name: "outputPricePerMillionTokens", type: "decimal", precision: 20, scale: 10, allowNull: false },
43
51
  { name: "fixedCostPerRequest", type: "decimal", precision: 20, scale: 10, allowNull: false, defaultValue: 0 },
44
52
  { name: "effectiveFrom", type: "datetimeTz", allowNull: false, index: true },
@@ -70,6 +70,7 @@ var ai_api_usage_records_default = (0, import_database.defineCollection)({
70
70
  { name: "quotaPolicyId", type: "bigInt", allowNull: true, index: true },
71
71
  { name: "groupId", type: "bigInt", allowNull: true, index: true },
72
72
  { name: "inputPricePerMillionTokens", type: "decimal", allowNull: true, precision: 20, scale: 10 },
73
+ { name: "cacheInputPricePerMillionTokens", type: "decimal", allowNull: true, precision: 20, scale: 10 },
73
74
  { name: "outputPricePerMillionTokens", type: "decimal", allowNull: true, precision: 20, scale: 10 },
74
75
  { name: "fixedCostPerRequest", type: "decimal", allowNull: true, precision: 20, scale: 10 },
75
76
  { name: "providerRequestId", type: "string", allowNull: true },
@@ -0,0 +1,66 @@
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 add_cache_input_price_exports = {};
28
+ __export(add_cache_input_price_exports, {
29
+ default: () => AddCacheInputPriceToModelPrices
30
+ });
31
+ module.exports = __toCommonJS(add_cache_input_price_exports);
32
+ var import_server = require("@nocobase/server");
33
+ class AddCacheInputPriceToModelPrices extends import_server.Migration {
34
+ on = "beforeLoad";
35
+ async up() {
36
+ await this.addDecimalColumn("aiApiModelPrices", "cacheInputPricePerMillionTokens", false, 0);
37
+ await this.addDecimalColumn("aiApiUsageRecords", "cacheInputPricePerMillionTokens", true);
38
+ }
39
+ async down() {
40
+ await this.removeColumn("aiApiUsageRecords", "cacheInputPricePerMillionTokens");
41
+ await this.removeColumn("aiApiModelPrices", "cacheInputPricePerMillionTokens");
42
+ }
43
+ async addDecimalColumn(collectionName, columnName, allowNull, defaultValue) {
44
+ const collection = this.db.getCollection(collectionName);
45
+ if (!collection || !await collection.existsInDb()) return;
46
+ const tableName = collection.getTableNameWithSchema();
47
+ if (await this.columnExists(tableName, columnName)) return;
48
+ await this.queryInterface.addColumn(tableName, columnName, {
49
+ type: "DECIMAL(20,10)",
50
+ allowNull,
51
+ ...defaultValue === void 0 ? {} : { defaultValue }
52
+ });
53
+ }
54
+ async removeColumn(collectionName, columnName) {
55
+ const collection = this.db.getCollection(collectionName);
56
+ if (!collection || !await collection.existsInDb()) return;
57
+ const tableName = collection.getTableNameWithSchema();
58
+ if (await this.columnExists(tableName, columnName)) {
59
+ await this.queryInterface.removeColumn(tableName, columnName);
60
+ }
61
+ }
62
+ async columnExists(tableName, columnName) {
63
+ const columns = await this.queryInterface.describeTable(tableName);
64
+ return Object.prototype.hasOwnProperty.call(columns, columnName);
65
+ }
66
+ }
@@ -91,6 +91,22 @@ async function authenticateBearer(ctx) {
91
91
  ctx.body = (0, import_openai_format.toOpenAIError)(401, "Invalid or expired API key", "invalid_request_error", "invalid_api_key");
92
92
  return false;
93
93
  }
94
+ if (jwt.blacklist) {
95
+ let blocked = false;
96
+ try {
97
+ blocked = await jwt.blacklist.has(decoded.jti ?? token);
98
+ } catch (err) {
99
+ ctx.log.error("AI API token blacklist check failed:", err);
100
+ ctx.status = 401;
101
+ ctx.body = (0, import_openai_format.toOpenAIError)(401, "Unable to verify API key status", "server_error");
102
+ return false;
103
+ }
104
+ if (blocked) {
105
+ ctx.status = 401;
106
+ ctx.body = (0, import_openai_format.toOpenAIError)(401, "API key has been revoked", "invalid_request_error", "invalid_api_key");
107
+ return false;
108
+ }
109
+ }
94
110
  if (!decoded.roleName) {
95
111
  ctx.status = 401;
96
112
  ctx.body = (0, import_openai_format.toOpenAIError)(
@@ -34,6 +34,7 @@ var import_resolve_service = require("../utils/resolve-service");
34
34
  var import_user_permissions = require("../utils/user-permissions");
35
35
  var import_request_cache = require("../utils/request-cache");
36
36
  var import_usage = require("../usage");
37
+ var import_billing = require("../billing");
37
38
  async function handleEmbeddings(ctx, plugin) {
38
39
  const body = ctx.request.body;
39
40
  if (!(body == null ? void 0 : body.model)) {
@@ -93,6 +94,7 @@ async function handleEmbeddings(ctx, plugin) {
93
94
  return;
94
95
  }
95
96
  const { service, modelId } = resolved;
97
+ await (0, import_billing.prepareLlmBilling)(ctx, resolved);
96
98
  if (service.enabled === false) {
97
99
  ctx.status = 404;
98
100
  ctx.body = (0, import_openai_format.toOpenAIError)(
@@ -144,10 +146,18 @@ async function handleEmbeddings(ctx, plugin) {
144
146
  // The specific embedding model
145
147
  });
146
148
  const embeddingModel = embeddingProvider.createEmbedding();
149
+ (0, import_billing.markLlmProviderAttempted)(ctx);
147
150
  const vectors = await embeddingModel.embedDocuments(inputs);
151
+ const estimatedInputTokens = Math.ceil(inputs.reduce((sum, s) => sum + s.length, 0) / 4);
152
+ const usage = {
153
+ prompt_tokens: estimatedInputTokens,
154
+ completion_tokens: 0,
155
+ total_tokens: estimatedInputTokens
156
+ };
157
+ (0, import_usage.setAiApiUsageResult)(ctx, usage);
158
+ await (0, import_billing.finalizeLlmBilling)(ctx, usage, true);
148
159
  ctx.status = 200;
149
160
  ctx.set("Content-Type", "application/json");
150
- (0, import_usage.setAiApiUsageUnavailable)(ctx);
151
161
  ctx.body = (0, import_openai_format.toOpenAIEmbeddingsResponse)({
152
162
  model: body.model,
153
163
  embeddings: vectors,
@@ -176,6 +176,7 @@ async function finishUsageRecord(ctx, id, startedAt, status) {
176
176
  quotaPolicyId: billing.quotaPolicyId ?? null,
177
177
  groupId: billing.groupId ?? null,
178
178
  inputPricePerMillionTokens: billing.inputPricePerMillionTokens ?? null,
179
+ cacheInputPricePerMillionTokens: billing.cacheInputPricePerMillionTokens ?? null,
179
180
  outputPricePerMillionTokens: billing.outputPricePerMillionTokens ?? null,
180
181
  fixedCostPerRequest: billing.fixedCostPerRequest ?? null,
181
182
  providerRequestId: usageResult.providerRequestId ?? null,
@@ -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 } : {} };
@@ -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
@@ -55,6 +55,7 @@ function requireNonNegativeIntegerOrNull(value, field) {
55
55
  }
56
56
  async function validateModelPrice(db, model) {
57
57
  requireNonNegativeDecimal(model.get("inputPricePerMillionTokens"), "inputPricePerMillionTokens");
58
+ requireNonNegativeDecimal(model.get("cacheInputPricePerMillionTokens") ?? 0, "cacheInputPricePerMillionTokens");
58
59
  requireNonNegativeDecimal(model.get("outputPricePerMillionTokens"), "outputPricePerMillionTokens");
59
60
  requireNonNegativeDecimal(model.get("fixedCostPerRequest") ?? 0, "fixedCostPerRequest");
60
61
  const effectiveFrom = new Date(String(model.get("effectiveFrom")));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-ai-api",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "main": "dist/server/index.js",
5
5
  "dependencies": {},
6
6
  "peerDependencies": {
@@ -1,10 +1,10 @@
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
- export { default } from './plugin';
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
+ export { default } from './plugin';
@@ -1,12 +1,12 @@
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
- import { ModelConstructor } from '@nocobase/flow-engine';
11
-
12
- export default {} as Record<string, ModelConstructor>;
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
+ import { ModelConstructor } from '@nocobase/flow-engine';
11
+
12
+ export default {} as Record<string, ModelConstructor>;
@@ -28,6 +28,7 @@ interface ModelPrice {
28
28
  model: string;
29
29
  currency: string;
30
30
  inputPricePerMillionTokens: string;
31
+ cacheInputPricePerMillionTokens: string;
31
32
  outputPricePerMillionTokens: string;
32
33
  fixedCostPerRequest: string;
33
34
  effectiveFrom: string;
@@ -95,6 +96,7 @@ export default function ModelPricingPage() {
95
96
  form.setFieldsValue({
96
97
  currency: 'USD',
97
98
  inputPricePerMillionTokens: '0',
99
+ cacheInputPricePerMillionTokens: '0',
98
100
  outputPricePerMillionTokens: '0',
99
101
  fixedCostPerRequest: '0',
100
102
  effectiveFrom: dayjs(),
@@ -182,6 +184,12 @@ export default function ModelPricingPage() {
182
184
  { title: t('LLM service'), dataIndex: 'llmService', key: 'llmService', width: 180 },
183
185
  { title: t('Model'), dataIndex: 'model', key: 'model', width: 180 },
184
186
  { title: t('Input price / 1M'), dataIndex: 'inputPricePerMillionTokens', key: 'inputPrice', width: 150 },
187
+ {
188
+ title: t('Cache input price / 1M'),
189
+ dataIndex: 'cacheInputPricePerMillionTokens',
190
+ key: 'cacheInputPrice',
191
+ width: 170,
192
+ },
185
193
  { title: t('Output price / 1M'), dataIndex: 'outputPricePerMillionTokens', key: 'outputPrice', width: 150 },
186
194
  { title: t('Fixed request cost'), dataIndex: 'fixedCostPerRequest', key: 'fixedCost', width: 150 },
187
195
  { title: t('Currency'), dataIndex: 'currency', key: 'currency', width: 90 },
@@ -260,6 +268,13 @@ export default function ModelPricingPage() {
260
268
  <Form.Item name="inputPricePerMillionTokens" label={t('Input price / 1M')} rules={[{ required: true }]}>
261
269
  <InputNumber min={0} stringMode style={{ width: '100%' }} />
262
270
  </Form.Item>
271
+ <Form.Item
272
+ name="cacheInputPricePerMillionTokens"
273
+ label={t('Cache input price / 1M')}
274
+ rules={[{ required: true }]}
275
+ >
276
+ <InputNumber min={0} stringMode style={{ width: '100%' }} />
277
+ </Form.Item>
263
278
  <Form.Item name="outputPricePerMillionTokens" label={t('Output price / 1M')} rules={[{ required: true }]}>
264
279
  <InputNumber min={0} stringMode style={{ width: '100%' }} />
265
280
  </Form.Item>
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
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
- export * from './server';
11
- export { default } from './server';
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
+ export * from './server';
11
+ export { default } from './server';