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
@@ -1,138 +1,177 @@
1
- {
2
- "AI API Gateway": "AI API 网关",
3
- "Configuration": "配置",
4
- "Default AI Employee": "默认 AI 员工",
5
- "Enabled LLM Services": "已启用 LLM 服务",
6
- "Rate Limit": "速率限制",
7
- "Save Configuration": "保存配置",
8
- "Configuration saved": "配置已保存",
9
- "Failed to save configuration": "保存配置失败",
10
- "API mode": "API 模式",
11
- "Direct LLM": "直接 LLM",
12
- "AI Employee agent": "AI 员工代理",
13
- "Default LLM service": "默认 LLM 服务",
14
- "Enable user quotas": "启用用户配额",
15
- "Default reserved output tokens": "默认预留输出令牌数",
16
- "Refresh": "刷新",
17
- "Model pricing": "模型定价",
18
- "User quotas": "用户配额",
19
- "Usage": "用量",
20
- "LLM service": "LLM 服务",
21
- "Model": "模型",
22
- "Input price / 1M": "输入价格 / 百万令牌",
23
- "Output price / 1M": "输出价格 / 百万令牌",
24
- "Fixed request cost": "每次请求固定费用",
25
- "Currency": "货币",
26
- "Status": "状态",
27
- "Enabled": "已启用",
28
- "Disabled": "已禁用",
29
- "Actions": "操作",
30
- "Edit": "编辑",
31
- "Delete": "删除",
32
- "Delete this price?": "删除此价格?",
33
- "Add price": "添加价格",
34
- "Edit price": "编辑价格",
35
- "Effective from": "生效时间",
36
- "Effective to": "失效时间",
37
- "Notes": "备注",
38
- "Saved successfully": "保存成功",
39
- "Deleted successfully": "删除成功",
40
- "User": "用户",
41
- "Period": "周期",
42
- "Request limit": "请求限制",
43
- "Token limit": "令牌限制",
44
- "Cost limit": "费用限制",
45
- "Timezone": "时区",
46
- "Unlimited": "无限制",
47
- "Add quota": "添加配额",
48
- "Edit quota": "编辑配额",
49
- "Delete this quota?": "删除此配额?",
50
- "Daily": "每日",
51
- "Monthly": "每月",
52
- "Reject unpriced models": "拒绝未定价模型",
53
- "Missing usage behavior": "缺少用量时的行为",
54
- "Use reserved estimate": "使用预留估算",
55
- "Allow without token charge": "允许且不计令牌",
56
- "Context overflow behavior": "上下文超限处理",
57
- "Reject request": "拒绝请求",
58
- "Truncate oldest conversation turns": "截断最早的对话轮次",
59
- "Started at": "开始时间",
60
- "Requested model": "请求模型",
61
- "Resolved service": "解析后的服务",
62
- "Resolved model": "解析后的模型",
63
- "Input tokens": "输入令牌",
64
- "Output tokens": "输出令牌",
65
- "Total tokens": "总令牌",
66
- "Prompt cache tokens": "提示缓存令牌",
67
- "Cost": "费用",
68
- "Cost status": "费用状态",
69
- "Request ID": "请求 ID",
70
- "Failed to load models": "加载模型失败",
71
- "Select a model": "选择模型",
72
- "Select an AI Employee": "选择 AI 员工",
73
- "Usage guide": "使用指南",
74
- "OpenAI-compatible endpoint": "OpenAI 兼容端点",
75
- "Base URL": "基础 URL",
76
- "Use a NocoBase API key as the Bearer token.": "使用 NocoBase API 密钥作为 Bearer Token。",
77
- "List available models": "列出可用模型",
78
- "Send a chat completion": "发送聊天补全请求",
79
- "Usage filters": "用量筛选",
80
- "Time range": "时间范围",
81
- "User ID": "用户 ID",
82
- "Succeeded": "成功",
83
- "Failed": "失败",
84
- "Started": "已开始",
85
- "Apply filters": "应用筛选",
86
- "Reset": "重置",
87
- "Requests": "请求数",
88
- "Total cost": "总费用",
89
- "Usage records": "用量记录",
90
- "Model metadata": "模型元数据",
91
- "Add override": "添加覆盖",
92
- "Edit override": "编辑覆盖",
93
- "Delete this override?": "确认删除此覆盖?",
94
- "Context window": "上下文窗口",
95
- "Max completion tokens": "最大输出 token 数",
96
- "Owned by": "所属方",
97
- "Display name": "显示名称",
98
- "Description": "描述",
99
- "Leave empty to not override": "留空则不覆盖",
100
- "Total input + output token capacity reported to clients.": "返回给客户端的输入+输出 token 总容量。",
101
- "Maximum output tokens reported to clients.": "返回给客户端的最大输出 token 数。",
102
- "Initial system prompt": "初始系统提示词",
103
- "Prepended as the first system message, before any system prompt sent by the client. If the client sends no system prompt, this becomes the system prompt sent to the provider.": "作为第一条 system 消息插入到客户端发送的任何 system 提示词之前。如果客户端未发送 system 提示词,此提示词将作为发送给提供商的 system 提示词。",
104
- "AI API": "AI API",
105
- "Allow this role to use the AI API": "允许此角色使用 AI API",
106
- "Allow all AI Employees": "允许所有 AI 员工",
107
- "Select which AI Employees this role may use:": "选择此角色可使用的 AI 员工:",
108
- "Select allowed AI Employees": "选择允许的 AI 员工",
109
- "Max request body size (MB)": "请求体大小上限(MB)",
110
- "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "调高此值以接收内联 base64 图片。base64 编码会使体积增加约 33%。",
111
- "Usage groups": "用量组",
112
- "Add group": "添加分组",
113
- "Edit group": "编辑分组",
114
- "Delete this group?": "删除此分组?",
115
- "Mode": "模式",
116
- "Share": "共享",
117
- "Per user": "按用户",
118
- "Rate limit per minute": "每分钟速率限制",
119
- "Members": "成员",
120
- "Add member": "添加成员",
121
- "Member added": "成员已添加",
122
- "Member removed": "成员已移除",
123
- "Remove member?": "移除成员?",
124
- "Remove": "移除",
125
- "Search group by user": "按用户搜索分组",
126
- "User belongs to": "用户属于",
127
- "User not found": "未找到用户",
128
- "Default": "默认",
129
- "Allowed LLM services": "允许的 LLM 服务",
130
- "Allow all models": "允许所有模型",
131
- "Allowed models": "允许的模型",
132
- "Model access": "模型访问",
133
- "All models": "所有模型",
134
- "All services": "所有服务",
135
- "No models": "未允许任何模型",
136
- "Leave empty to allow every service enabled in the general configuration.": "留空则允许通用配置中已启用的所有服务。",
137
- "Users who do not belong to any other group automatically use this default group — no need to add members.": "不属于其他分组的用户会自动使用此默认分组,无需手动添加成员。"
138
- }
1
+ {
2
+ "AI API Gateway": "AI API 网关",
3
+ "Configuration": "配置",
4
+ "Default AI Employee": "默认 AI 员工",
5
+ "Enabled LLM Services": "已启用 LLM 服务",
6
+ "Rate Limit": "速率限制",
7
+ "Save Configuration": "保存配置",
8
+ "Configuration saved": "配置已保存",
9
+ "Failed to save configuration": "保存配置失败",
10
+ "API mode": "API 模式",
11
+ "Direct LLM": "直接 LLM",
12
+ "AI Employee agent": "AI 员工代理",
13
+ "Default LLM service": "默认 LLM 服务",
14
+ "Enable user quotas": "启用用户配额",
15
+ "Default reserved output tokens": "默认预留输出令牌数",
16
+ "Refresh": "刷新",
17
+ "Model pricing": "模型定价",
18
+ "User quotas": "用户配额",
19
+ "Usage": "用量",
20
+ "LLM service": "LLM 服务",
21
+ "Model": "模型",
22
+ "Input price / 1M": "输入价格 / 百万令牌",
23
+ "Cache input price / 1M": "缓存输入价格 / 百万令牌",
24
+ "Output price / 1M": "输出价格 / 百万令牌",
25
+ "Fixed request cost": "每次请求固定费用",
26
+ "Currency": "货币",
27
+ "Status": "状态",
28
+ "Enabled": "已启用",
29
+ "Disabled": "已禁用",
30
+ "Actions": "操作",
31
+ "Edit": "编辑",
32
+ "Delete": "删除",
33
+ "Delete this price?": "删除此价格?",
34
+ "Add price": "添加价格",
35
+ "Edit price": "编辑价格",
36
+ "Effective from": "生效时间",
37
+ "Effective to": "失效时间",
38
+ "Notes": "备注",
39
+ "Saved successfully": "保存成功",
40
+ "Deleted successfully": "删除成功",
41
+ "User": "用户",
42
+ "Period": "周期",
43
+ "Request limit": "请求限制",
44
+ "Token limit": "令牌限制",
45
+ "Cost limit": "费用限制",
46
+ "Timezone": "时区",
47
+ "Unlimited": "无限制",
48
+ "Add quota": "添加配额",
49
+ "Edit quota": "编辑配额",
50
+ "Delete this quota?": "删除此配额?",
51
+ "Daily": "每日",
52
+ "Monthly": "每月",
53
+ "Reject unpriced models": "拒绝未定价模型",
54
+ "Missing usage behavior": "缺少用量时的行为",
55
+ "Use reserved estimate": "使用预留估算",
56
+ "Allow without token charge": "允许且不计令牌",
57
+ "Context overflow behavior": "上下文超限处理",
58
+ "Reject request": "拒绝请求",
59
+ "Truncate oldest conversation turns": "截断最早的对话轮次",
60
+ "Started at": "开始时间",
61
+ "Requested model": "请求模型",
62
+ "Resolved service": "解析后的服务",
63
+ "Resolved model": "解析后的模型",
64
+ "Input tokens": "输入令牌",
65
+ "Output tokens": "输出令牌",
66
+ "Total tokens": "总令牌",
67
+ "Prompt cache tokens": "提示缓存令牌",
68
+ "Cost": "费用",
69
+ "Cost status": "费用状态",
70
+ "Request ID": "请求 ID",
71
+ "Failed to load models": "加载模型失败",
72
+ "Select a model": "选择模型",
73
+ "Select an AI Employee": "选择 AI 员工",
74
+ "Usage guide": "使用指南",
75
+ "OpenAI-compatible endpoint": "OpenAI 兼容端点",
76
+ "Base URL": "基础 URL",
77
+ "Use a NocoBase API key as the Bearer token.": "使用 NocoBase API 密钥作为 Bearer Token。",
78
+ "List available models": "列出可用模型",
79
+ "Send a chat completion": "发送聊天补全请求",
80
+ "Usage filters": "用量筛选",
81
+ "Time range": "时间范围",
82
+ "User ID": "用户 ID",
83
+ "Succeeded": "成功",
84
+ "Failed": "失败",
85
+ "Started": "已开始",
86
+ "Apply filters": "应用筛选",
87
+ "Reset": "重置",
88
+ "Requests": "请求数",
89
+ "Total cost": "总费用",
90
+ "Usage records": "用量记录",
91
+ "Model metadata": "模型元数据",
92
+ "Add override": "添加覆盖",
93
+ "Edit override": "编辑覆盖",
94
+ "Delete this override?": "确认删除此覆盖?",
95
+ "Context window": "上下文窗口",
96
+ "Max completion tokens": "最大输出 token 数",
97
+ "Owned by": "所属方",
98
+ "Display name": "显示名称",
99
+ "Description": "描述",
100
+ "Leave empty to not override": "留空则不覆盖",
101
+ "Total input + output token capacity reported to clients.": "返回给客户端的输入+输出 token 总容量。",
102
+ "Maximum output tokens reported to clients.": "返回给客户端的最大输出 token 数。",
103
+ "Initial system prompt": "初始系统提示词",
104
+ "Prepended as the first system message, before any system prompt sent by the client. If the client sends no system prompt, this becomes the system prompt sent to the provider.": "作为第一条 system 消息插入到客户端发送的任何 system 提示词之前。如果客户端未发送 system 提示词,此提示词将作为发送给提供商的 system 提示词。",
105
+ "AI API": "AI API",
106
+ "Allow this role to use the AI API": "允许此角色使用 AI API",
107
+ "Allow all AI Employees": "允许所有 AI 员工",
108
+ "Select which AI Employees this role may use:": "选择此角色可使用的 AI 员工:",
109
+ "Select allowed AI Employees": "选择允许的 AI 员工",
110
+ "Max request body size (MB)": "请求体大小上限(MB)",
111
+ "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "调高此值以接收内联 base64 图片。base64 编码会使体积增加约 33%。",
112
+ "Usage groups": "用量组",
113
+ "Add group": "添加分组",
114
+ "Edit group": "编辑分组",
115
+ "Delete this group?": "删除此分组?",
116
+ "Mode": "模式",
117
+ "Share": "共享",
118
+ "Per user": "按用户",
119
+ "Rate limit per minute": "每分钟速率限制",
120
+ "Members": "成员",
121
+ "Add member": "添加成员",
122
+ "Member added": "成员已添加",
123
+ "Member removed": "成员已移除",
124
+ "Remove member?": "移除成员?",
125
+ "Remove": "移除",
126
+ "Search group by user": "按用户搜索分组",
127
+ "User belongs to": "用户属于",
128
+ "User not found": "未找到用户",
129
+ "Default": "默认",
130
+ "Allowed LLM services": "允许的 LLM 服务",
131
+ "Allow all models": "允许所有模型",
132
+ "Allowed models": "允许的模型",
133
+ "Model access": "模型访问",
134
+ "All models": "所有模型",
135
+ "All services": "所有服务",
136
+ "No models": "未允许任何模型",
137
+ "Leave empty to allow every service enabled in the general configuration.": "留空则允许通用配置中已启用的所有服务。",
138
+ "Users who do not belong to any other group automatically use this default group — no need to add members.": "不属于其他分组的用户会自动使用此默认分组,无需手动添加成员。",
139
+ "Membership": "默认成员",
140
+ "Model routing": "模型路由",
141
+ "Model routing (virtual models)": "模型路由(虚拟模型)",
142
+ "Alias": "别名",
143
+ "Fallback model": "兜底模型",
144
+ "A fallback model is required": "必须填写兜底模型",
145
+ "Used when no capability bucket candidate is usable and the fallback is permitted for the caller.": "当能力桶中没有可用模型且调用者有权使用回退模型时使用。",
146
+ "Vision models (in order)": "视觉模型(按顺序)",
147
+ "Requests with an image or file block use the first permitted model here.": "包含图片或文件块的请求会使用此处第一个有权限的模型。",
148
+ "Tool-calling models (in order)": "工具调用模型(按顺序)",
149
+ "Requests with tools/tool_choice use the first permitted model here.": "包含 tools/tool_choice 的请求会使用此处第一个有权限的模型。",
150
+ "Reasoning models (in order)": "推理模型(按顺序)",
151
+ "Requests with an explicit reasoning or reasoning_effort parameter use the first permitted model here.": "显式包含 reasoning 或 reasoning_effort 参数的请求会使用此处第一个有权限的模型。",
152
+ "Cheap models (in order)": "低成本模型(按顺序)",
153
+ "Optional. When set, cheap-eligible requests use the first permitted model here.": "可选。设置后,低复杂度请求会使用此处第一个有权限的模型。",
154
+ "General models (in order)": "通用模型(按顺序)",
155
+ "Default bucket when no capability rule matched. Leave empty to derive from all enabled models ordered by Model metadata sortOrder.": "没有任何能力规则命中时的默认桶。留空则按模型元数据 sortOrder 对所有已启用模型排序后派生。",
156
+ "Type service/model and press Enter — earlier entries are preferred": "输入 service/模型 后回车——越靠前优先级越高",
157
+ "An empty bucket is derived automatically from Model metadata (capability flags + sortOrder).": "留空的桶会自动从“模型元数据”页的能力标志 + sortOrder 派生。",
158
+ "Save": "保存",
159
+ "Supports vision": "支持视觉输入",
160
+ "Used by virtual-model routing for image/file requests.": "用于对包含图片或文件的请求进行虚拟模型路由。",
161
+ "Supports tool calling": "支持工具调用",
162
+ "Used by virtual-model routing for requests with tools/tool_choice.": "用于对包含 tools/tool_choice 的请求进行虚拟模型路由。",
163
+ "Reasoning tier": "推理层级",
164
+ "cheap | general | reasoning. Used by virtual-model routing buckets.": "cheap | general | reasoning。用于虚拟模型路由桶。",
165
+ "Cheap": "低成本",
166
+ "General": "通用",
167
+ "Reasoning": "推理",
168
+ "Routing priority": "路由优先级",
169
+ "Ascending — lower is preferred when a bucket is derived from metadata.": "升序 — 从元数据派生路由桶时,数值越小优先级越高。",
170
+ "Select models — the order shown is the routing priority": "选择模型 — 显示顺序即路由优先级",
171
+ "Select a fallback model": "选择兜底模型",
172
+ "Name": "名称",
173
+ "Rate limit/min": "每分钟限制",
174
+
175
+
176
+ "unavailable": "(不可用)"
177
+ }
@@ -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:
@@ -72,6 +72,32 @@ var ai_api_model_metadata_default = (0, import_database.defineCollection)({
72
72
  allowNull: true,
73
73
  comment: "Initial system prompt prepended as the first system message of every request for this model. Never replaces the client system prompt."
74
74
  },
75
+ {
76
+ name: "supportsVision",
77
+ type: "boolean",
78
+ defaultValue: false,
79
+ index: true,
80
+ comment: "Whether the model accepts image content blocks. Used by virtual-model routing."
81
+ },
82
+ {
83
+ name: "supportsToolCalling",
84
+ type: "boolean",
85
+ defaultValue: true,
86
+ index: true,
87
+ comment: "Whether the model accepts tools / tool_choice. Used by virtual-model routing."
88
+ },
89
+ {
90
+ name: "reasoningTier",
91
+ type: "string",
92
+ defaultValue: "general",
93
+ comment: "Cost/capability tier for routing: cheap | general | reasoning."
94
+ },
95
+ {
96
+ name: "sortOrder",
97
+ type: "integer",
98
+ defaultValue: 0,
99
+ comment: "Ascending priority used when deriving a routing bucket from metadata (lower = preferred)."
100
+ },
75
101
  {
76
102
  name: "enabled",
77
103
  type: "boolean",
@@ -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 },
@@ -0,0 +1,101 @@
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 ai_api_response_records_exports = {};
28
+ __export(ai_api_response_records_exports, {
29
+ default: () => ai_api_response_records_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_response_records_exports);
32
+ var import_database = require("@nocobase/database");
33
+ var ai_api_response_records_default = (0, import_database.defineCollection)({
34
+ name: "aiApiResponseRecords",
35
+ dataCategory: "runtime",
36
+ hidden: true,
37
+ autoGenId: true,
38
+ fields: [
39
+ {
40
+ name: "responseId",
41
+ type: "string",
42
+ unique: true,
43
+ index: true,
44
+ comment: "Public response ID (resp_xxx) exposed to clients"
45
+ },
46
+ {
47
+ name: "userId",
48
+ type: "bigInt",
49
+ allowNull: false,
50
+ index: true,
51
+ comment: "Owner of this response chain"
52
+ },
53
+ {
54
+ name: "user",
55
+ type: "belongsTo",
56
+ target: "users",
57
+ targetKey: "id",
58
+ foreignKey: "userId",
59
+ constraints: false
60
+ },
61
+ {
62
+ name: "model",
63
+ type: "string",
64
+ allowNull: false,
65
+ index: true,
66
+ comment: "Model used for this response (service/model format)"
67
+ },
68
+ {
69
+ name: "input",
70
+ type: "jsonb",
71
+ allowNull: false,
72
+ comment: "Original Responses API input (string or array)"
73
+ },
74
+ {
75
+ name: "output",
76
+ type: "jsonb",
77
+ allowNull: false,
78
+ comment: "Complete ResponseObject as returned to the client"
79
+ },
80
+ {
81
+ name: "previousResponseId",
82
+ type: "string",
83
+ allowNull: true,
84
+ index: true,
85
+ comment: "Links to the previous response in this conversation chain"
86
+ },
87
+ {
88
+ name: "metadata",
89
+ type: "jsonb",
90
+ allowNull: true,
91
+ comment: "Client-provided metadata from the request"
92
+ },
93
+ {
94
+ name: "expiresAt",
95
+ type: "datetimeTz",
96
+ allowNull: false,
97
+ index: true,
98
+ comment: "Auto-delete after 30 days (OpenAI retention policy)"
99
+ }
100
+ ]
101
+ });
@@ -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,68 @@
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 ai_api_virtual_models_exports = {};
28
+ __export(ai_api_virtual_models_exports, {
29
+ default: () => ai_api_virtual_models_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_virtual_models_exports);
32
+ var import_database = require("@nocobase/database");
33
+ var ai_api_virtual_models_default = (0, import_database.defineCollection)({
34
+ name: "aiApiVirtualModels",
35
+ autoGenId: true,
36
+ fields: [
37
+ {
38
+ name: "name",
39
+ type: "string",
40
+ unique: true,
41
+ allowNull: false,
42
+ comment: 'Alias the client passes as the model id, e.g. "auto".'
43
+ },
44
+ {
45
+ name: "mode",
46
+ type: "string",
47
+ defaultValue: "chat",
48
+ comment: "Endpoint family this alias serves: chat | embedding."
49
+ },
50
+ {
51
+ name: "fallbackModel",
52
+ type: "string",
53
+ allowNull: false,
54
+ comment: 'Concrete "service/modelId" used when no capability bucket candidate is usable and fallback is permitted.'
55
+ },
56
+ {
57
+ name: "visionModels",
58
+ type: "json",
59
+ defaultValue: [],
60
+ comment: 'Ordered "service/modelId" list for vision requests.'
61
+ },
62
+ { name: "toolModels", type: "json", defaultValue: [], comment: "Ordered list for tool-calling requests." },
63
+ { name: "reasoningModels", type: "json", defaultValue: [], comment: "Ordered list for reasoning-tier requests." },
64
+ { name: "cheapModels", type: "json", defaultValue: [], comment: "Ordered list for cheap-tier requests." },
65
+ { name: "generalModels", type: "json", defaultValue: [], comment: "Ordered list for general requests." },
66
+ { name: "enabled", type: "boolean", defaultValue: true, index: true }
67
+ ]
68
+ });