plugin-ai-api 1.0.23 → 1.0.24

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.
@@ -0,0 +1,216 @@
1
+ import type { Context } from '@nocobase/actions';
2
+
3
+ export type ContextOverflowBehavior = 'reject' | 'truncate';
4
+
5
+ export interface OpenAIMessage {
6
+ role: string;
7
+ content?: unknown;
8
+ tool_calls?: unknown;
9
+ tool_call_id?: string;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ interface ModelMetadata {
14
+ contextWindow: number;
15
+ maxCompletionTokens: number;
16
+ }
17
+
18
+ interface ContextPreparationOptions {
19
+ serviceName: string;
20
+ modelId: string;
21
+ messages: OpenAIMessage[];
22
+ tools?: unknown;
23
+ maxCompletionTokens?: unknown;
24
+ maxTokens?: unknown;
25
+ }
26
+
27
+ export interface PreparedDirectLlmContext {
28
+ messages: OpenAIMessage[];
29
+ estimatedInputTokens: number;
30
+ inputTokenBudget: number;
31
+ reservedOutputTokens: number;
32
+ truncated: boolean;
33
+ }
34
+
35
+ export class DirectLlmContextError extends Error {
36
+ constructor(
37
+ readonly code:
38
+ | 'context_length_exceeded'
39
+ | 'context_estimation_unsupported'
40
+ | 'max_completion_tokens_exceeds_model_limit'
41
+ | 'model_context_metadata_not_configured',
42
+ message: string,
43
+ ) {
44
+ super(message);
45
+ this.name = 'DirectLlmContextError';
46
+ }
47
+ }
48
+
49
+ function getValue<T>(
50
+ record: { get?: (key: string) => unknown; [key: string]: unknown } | null,
51
+ key: string,
52
+ ): T | undefined {
53
+ return (record?.get?.(key) as T | undefined) ?? (record?.[key] as T | undefined);
54
+ }
55
+
56
+ function positiveInteger(value: unknown): number | undefined {
57
+ const parsed = Number(value);
58
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
59
+ }
60
+
61
+ function hasImageContent(value: unknown): boolean {
62
+ return (
63
+ Array.isArray(value) &&
64
+ value.some(
65
+ (block) => typeof block === 'object' && block !== null && (block as { type?: unknown }).type === 'image_url',
66
+ )
67
+ );
68
+ }
69
+
70
+ function estimateValueTokens(value: unknown): number {
71
+ if (value === undefined) return 0;
72
+ return Math.ceil(Buffer.byteLength(JSON.stringify(value), 'utf8') / 3);
73
+ }
74
+
75
+ function estimateMessagesTokens(messages: OpenAIMessage[]): number {
76
+ return messages.reduce((total, message) => total + estimateValueTokens(message) + 4, 0);
77
+ }
78
+
79
+ function containsUnsupportedContent(messages: OpenAIMessage[]): boolean {
80
+ return messages.some((message) => hasImageContent(message.content));
81
+ }
82
+
83
+ function isInstruction(message: OpenAIMessage): boolean {
84
+ return message.role === 'system' || message.role === 'developer';
85
+ }
86
+
87
+ function splitTurns(messages: OpenAIMessage[]): { fixed: OpenAIMessage[]; turns: OpenAIMessage[][] } {
88
+ const fixed: OpenAIMessage[] = [];
89
+ const turns: OpenAIMessage[][] = [];
90
+ let currentTurn: OpenAIMessage[] | undefined;
91
+
92
+ for (const message of messages) {
93
+ if (isInstruction(message)) {
94
+ fixed.push(message);
95
+ continue;
96
+ }
97
+ if (message.role === 'user' || !currentTurn) {
98
+ currentTurn = [message];
99
+ turns.push(currentTurn);
100
+ continue;
101
+ }
102
+ currentTurn.push(message);
103
+ }
104
+
105
+ return { fixed, turns };
106
+ }
107
+
108
+ function messagesWithTurns(messages: OpenAIMessage[], turns: OpenAIMessage[][]): OpenAIMessage[] {
109
+ const retainedMessages = new Set(turns.flat());
110
+ return messages.filter((message) => isInstruction(message) || retainedMessages.has(message));
111
+ }
112
+
113
+ async function loadModelMetadata(ctx: Context, serviceName: string, modelId: string): Promise<ModelMetadata> {
114
+ const row = await ctx.db.getRepository('aiApiModelMetadata').findOne({
115
+ filter: { llmService: serviceName, model: modelId, enabled: true },
116
+ });
117
+ const contextWindow = positiveInteger(getValue<unknown>(row, 'contextWindow'));
118
+ const maxCompletionTokens = positiveInteger(getValue<unknown>(row, 'maxCompletionTokens'));
119
+ if (!contextWindow || !maxCompletionTokens) {
120
+ throw new DirectLlmContextError(
121
+ 'model_context_metadata_not_configured',
122
+ `Context metadata is not configured for '${serviceName}/${modelId}'. Configure context window and max completion tokens.`,
123
+ );
124
+ }
125
+ return { contextWindow, maxCompletionTokens };
126
+ }
127
+
128
+ async function resolveOverflowBehavior(ctx: Context): Promise<ContextOverflowBehavior> {
129
+ const userId = ctx.state.currentUser?.id;
130
+ if (userId === null || userId === undefined) return 'reject';
131
+ const policy = await ctx.db.getRepository('aiApiUserQuotaPolicies').findOne({
132
+ filter: { userId, enabled: true },
133
+ });
134
+ return getValue<unknown>(policy, 'contextOverflowBehavior') === 'truncate' ? 'truncate' : 'reject';
135
+ }
136
+
137
+ function resolveReservedOutputTokens(options: ContextPreparationOptions, metadata: ModelMetadata): number {
138
+ const requested = positiveInteger(options.maxCompletionTokens ?? options.maxTokens);
139
+ if (requested && requested > metadata.maxCompletionTokens) {
140
+ throw new DirectLlmContextError(
141
+ 'max_completion_tokens_exceeds_model_limit',
142
+ `Requested max completion tokens (${requested}) exceeds the model limit (${metadata.maxCompletionTokens}).`,
143
+ );
144
+ }
145
+ return requested ?? metadata.maxCompletionTokens;
146
+ }
147
+
148
+ export async function prepareDirectLlmContext(
149
+ ctx: Context,
150
+ options: ContextPreparationOptions,
151
+ ): Promise<PreparedDirectLlmContext> {
152
+ if (containsUnsupportedContent(options.messages)) {
153
+ throw new DirectLlmContextError(
154
+ 'context_estimation_unsupported',
155
+ 'Context enforcement does not support image_url content without a model-specific vision token estimator.',
156
+ );
157
+ }
158
+
159
+ const [metadata, behavior] = await Promise.all([
160
+ loadModelMetadata(ctx, options.serviceName, options.modelId),
161
+ resolveOverflowBehavior(ctx),
162
+ ]);
163
+ const reservedOutputTokens = resolveReservedOutputTokens(options, metadata);
164
+ const inputTokenBudget = metadata.contextWindow - reservedOutputTokens;
165
+ if (inputTokenBudget <= 0) {
166
+ throw new DirectLlmContextError(
167
+ 'context_length_exceeded',
168
+ `The model context window (${metadata.contextWindow}) leaves no input capacity after reserving ${reservedOutputTokens} output tokens.`,
169
+ );
170
+ }
171
+
172
+ const fixedOverheadTokens = estimateValueTokens(options.tools) + (options.tools === undefined ? 0 : 4);
173
+ const originalEstimate = estimateMessagesTokens(options.messages) + fixedOverheadTokens;
174
+ if (originalEstimate <= inputTokenBudget) {
175
+ return {
176
+ messages: options.messages,
177
+ estimatedInputTokens: originalEstimate,
178
+ inputTokenBudget,
179
+ reservedOutputTokens,
180
+ truncated: false,
181
+ };
182
+ }
183
+
184
+ if (behavior === 'reject') {
185
+ throw new DirectLlmContextError(
186
+ 'context_length_exceeded',
187
+ `Estimated input tokens (${originalEstimate}) exceed the allowed input budget (${inputTokenBudget}).`,
188
+ );
189
+ }
190
+
191
+ const { turns } = splitTurns(options.messages);
192
+ let remainingTurns = turns;
193
+ let messages = messagesWithTurns(options.messages, remainingTurns);
194
+ let estimatedInputTokens = estimateMessagesTokens(messages) + fixedOverheadTokens;
195
+
196
+ while (remainingTurns.length > 1 && estimatedInputTokens > inputTokenBudget) {
197
+ remainingTurns = remainingTurns.slice(1);
198
+ messages = messagesWithTurns(options.messages, remainingTurns);
199
+ estimatedInputTokens = estimateMessagesTokens(messages) + fixedOverheadTokens;
200
+ }
201
+
202
+ if (estimatedInputTokens > inputTokenBudget) {
203
+ throw new DirectLlmContextError(
204
+ 'context_length_exceeded',
205
+ `The fixed instructions, tools, and newest conversation turn require ${estimatedInputTokens} input tokens, exceeding the allowed budget (${inputTokenBudget}).`,
206
+ );
207
+ }
208
+
209
+ return {
210
+ messages,
211
+ estimatedInputTokens,
212
+ inputTokenBudget,
213
+ reservedOutputTokens,
214
+ truncated: messages.length !== options.messages.length,
215
+ };
216
+ }
@@ -76,6 +76,9 @@ export function validateQuotaPolicy(model: Model): void {
76
76
  if (!['allow', 'use_reserved'].includes(String(model.get('missingUsageBehavior')))) {
77
77
  throw new Error('missingUsageBehavior must be allow or use_reserved.');
78
78
  }
79
+ if (!['reject', 'truncate'].includes(String(model.get('contextOverflowBehavior') ?? 'reject'))) {
80
+ throw new Error('contextOverflowBehavior must be reject or truncate.');
81
+ }
79
82
  try {
80
83
  dayjs().tz(String(model.get('timezone') || 'UTC'));
81
84
  } catch {
@@ -1,10 +0,0 @@
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
- "use strict";(self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).push([["902"],{77:function(e,t,r){r.r(t),r.d(t,{default:function(){return f}});var n=r(155),a=r.n(n),l=r(59),i=r(694),o=r(650),u=r(630);function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}function s(e,t,r,n,a,l,i){try{var o=e[l](i),u=o.value}catch(e){r(e);return}o.done?t(u):Promise.resolve(u).then(n,a)}function m(e){return function(){var t=this,r=arguments;return new Promise(function(n,a){var l=e.apply(t,r);function i(e){s(l,n,a,i,o,"next",e)}function o(e){s(l,n,a,i,o,"throw",e)}i(void 0)})}}function d(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,a=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=a){var l=[],i=!0,o=!1;try{for(a=a.call(e);!(i=(r=a.next()).done)&&(l.push(r.value),!t||l.length!==t);i=!0);}catch(e){o=!0,n=e}finally{try{i||null==a.return||a.return()}finally{if(o)throw n}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return c(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);if("Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r)return Array.from(r);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return c(e,t)}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function p(e,t){var r,n,a,l={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]},i=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),o=Object.defineProperty;return o(i,"next",{value:u(0)}),o(i,"throw",{value:u(1)}),o(i,"return",{value:u(2)}),"function"==typeof Symbol&&o(i,Symbol.iterator,{value:function(){return this}}),i;function u(o){return function(u){var c=[o,u];if(r)throw TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(l=0)),l;)try{if(r=1,n&&(a=2&c[0]?n.return:c[0]?n.throw||((a=n.return)&&a.call(n),0):n.next)&&!(a=a.call(n,c[1])).done)return a;switch(n=0,a&&(c=[2&c[0],a.value]),c[0]){case 0:case 1:a=c;break;case 4:return l.label++,{value:c[1],done:!1};case 5:l.label++,n=c[1],c=[0];continue;case 7:c=l.ops.pop(),l.trys.pop();continue;default:if(!(a=(a=l.trys).length>0&&a[a.length-1])&&(6===c[0]||2===c[0])){l=0;continue}if(3===c[0]&&(!a||c[1]>a[0]&&c[1]<a[3])){l.label=c[1];break}if(6===c[0]&&l.label<a[1]){l.label=a[1],a=c;break}if(a&&l.label<a[2]){l.label=a[2],l.ops.push(c);break}a[2]&&l.ops.pop(),l.trys.pop();continue}c=t.call(e,l)}catch(e){c=[6,e],n=0}finally{r=a=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}function f(){var e=(0,i.useFlowContext)(),t=(0,o.k)(),r=d(l.Form.useForm(),1)[0],c=d((0,n.useState)([]),2),s=c[0],f=c[1],y=d((0,n.useState)([]),2),b=y[0],h=y[1],v=d((0,n.useState)(!1),2),g=v[0],E=v[1],w=d((0,n.useState)(!1),2),S=w[0],k=w[1],I=d((0,n.useState)(),2),U=I[0],q=I[1],F=d((0,n.useState)(!1),2),T=F[0],x=F[1],C=(0,n.useCallback)(function(){return m(function(){var t,r,n,a;return p(this,function(i){switch(i.label){case 0:E(!0),i.label=1;case 1:return i.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiUserQuotaPolicies:list",method:"get",params:{pageSize:200,appends:["user"],sort:"-updatedAt"}}),e.api.request({url:"users:list",method:"get",params:{pageSize:200}})])];case 2:return r=(t=d.apply(void 0,[i.sent(),2]))[0],n=t[1],f((0,u.m)(r,[])),h((0,u.m)(n,[])),[3,5];case 3:return a=i.sent(),l.message.error((0,u.g)(a)),[3,5];case 4:return E(!1),[7];case 5:return[2]}})})()},[e.api]);(0,n.useEffect)(function(){C()},[C]);var A=function(e){q(e),r.setFieldsValue(e),x(!0)},P=function(e){var t;return(null==e?void 0:e.nickname)||(null==e?void 0:e.username)||(null==e?void 0:e.email)||String(null!=(t=null==e?void 0:e.id)?t:"")},j=[{title:t("User"),key:"user",width:180,render:function(e,t){return P(t.user)||String(t.userId)}},{title:t("Period"),dataIndex:"periodType",key:"periodType",width:100},{title:t("Request limit"),dataIndex:"requestLimit",key:"requestLimit",width:130,render:function(e){return null!=e?e:t("Unlimited")}},{title:t("Token limit"),dataIndex:"totalTokenLimit",key:"totalTokenLimit",width:130,render:function(e){return null!=e?e:t("Unlimited")}},{title:t("Cost limit"),dataIndex:"costLimit",key:"costLimit",width:120,render:function(e,r){return null==e?t("Unlimited"):"".concat(e," ").concat(r.currency)}},{title:t("Timezone"),dataIndex:"timezone",key:"timezone",width:140},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return a().createElement(l.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",width:150,fixed:"right",render:function(r,n){return a().createElement(l.Space,{size:0},a().createElement(l.Button,{type:"link",onClick:function(){return A(n)}},t("Edit")),a().createElement(l.Popconfirm,{title:t("Delete this quota?"),onConfirm:function(){return m(function(){var r;return p(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiUserQuotaPolicies:destroy/".concat(n.id),method:"post"})];case 1:return a.sent(),l.message.success(t("Deleted successfully")),[4,C()];case 2:return a.sent(),[3,4];case 3:return r=a.sent(),l.message.error((0,u.g)(r)),[3,4];case 4:return[2]}})})()}},a().createElement(l.Button,{type:"link",danger:!0},t("Delete"))))}}];return a().createElement(l.Card,{title:t("User quotas"),extra:a().createElement(l.Button,{type:"primary",onClick:function(){q(void 0),r.setFieldsValue({enabled:!0,periodType:"monthly",timezone:"UTC",currency:"USD",rejectUnpricedModel:!0,missingUsageBehavior:"use_reserved"}),x(!0)}},t("Add quota"))},a().createElement(l.Table,{rowKey:"id",columns:j,dataSource:s,loading:g,scroll:{x:1100}}),a().createElement(l.Modal,{title:U?t("Edit quota"):t("Add quota"),open:T,onCancel:function(){return x(!1)},onOk:function(){return m(function(){var n,a;return p(this,function(i){switch(i.label){case 0:return[4,r.validateFields()];case 1:n=i.sent(),k(!0),i.label=2;case 2:return i.trys.push([2,5,6,7]),[4,e.api.request({url:U?"aiApiUserQuotaPolicies:update/".concat(U.id):"aiApiUserQuotaPolicies:create",method:"post",data:n})];case 3:return i.sent(),l.message.success(t("Saved successfully")),x(!1),[4,C()];case 4:return i.sent(),[3,7];case 5:return a=i.sent(),l.message.error((0,u.g)(a)),[3,7];case 6:return k(!1),[7];case 7:return[2]}})})()},confirmLoading:S,destroyOnClose:!0},a().createElement(l.Form,{form:r,layout:"vertical",preserve:!1},a().createElement(l.Form.Item,{name:"userId",label:t("User"),rules:[{required:!0}]},a().createElement(l.Select,{disabled:!!U,showSearch:!0,optionFilterProp:"label",options:b.map(function(e){return{label:P(e),value:e.id}})})),a().createElement(l.Form.Item,{name:"periodType",label:t("Period"),rules:[{required:!0}]},a().createElement(l.Select,{options:[{label:t("Daily"),value:"daily"},{label:t("Monthly"),value:"monthly"}]})),a().createElement(l.Form.Item,{name:"timezone",label:t("Timezone"),rules:[{required:!0}]},a().createElement(l.Input,{placeholder:"UTC"})),a().createElement(l.Form.Item,{name:"requestLimit",label:t("Request limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"totalTokenLimit",label:t("Token limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"costLimit",label:t("Cost limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"currency",label:t("Currency"),rules:[{required:!0}]},a().createElement(l.Input,null)),a().createElement(l.Form.Item,{name:"rejectUnpricedModel",label:t("Reject unpriced models"),valuePropName:"checked"},a().createElement(l.Switch,null)),a().createElement(l.Form.Item,{name:"missingUsageBehavior",label:t("Missing usage behavior"),rules:[{required:!0}]},a().createElement(l.Select,{options:[{label:t("Use reserved estimate"),value:"use_reserved"},{label:t("Allow without token charge"),value:"allow"}]})),a().createElement(l.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},a().createElement(l.Switch,null)))))}},650:function(e,t,r){r.d(t,{k:function(){return l}});var n=r(694),a=JSON.parse('{"UU":"plugin-ai-api"}');function l(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[a.UU,"client"]})}}},630:function(e,t,r){function n(e,t){var r,n;return e&&(void 0===e?"undefined":e&&"u">typeof Symbol&&e.constructor===Symbol?"symbol":typeof e)=="object"?null!=(r=null==(n=e.data)?void 0:n.data)?r:t:t}function a(e){var t;return(null!=(t=Error)&&"u">typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t)?e.message:String(e)}r.d(t,{g:function(){return a},m:function(){return n}})}}]);
@@ -1,10 +0,0 @@
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
- "use strict";(self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).push([["902"],{77:function(e,t,r){r.r(t),r.d(t,{default:function(){return f}});var n=r(155),a=r.n(n),l=r(59),i=r(694),o=r(650),u=r(630);function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}function s(e,t,r,n,a,l,i){try{var o=e[l](i),u=o.value}catch(e){r(e);return}o.done?t(u):Promise.resolve(u).then(n,a)}function m(e){return function(){var t=this,r=arguments;return new Promise(function(n,a){var l=e.apply(t,r);function i(e){s(l,n,a,i,o,"next",e)}function o(e){s(l,n,a,i,o,"throw",e)}i(void 0)})}}function d(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,a=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=a){var l=[],i=!0,o=!1;try{for(a=a.call(e);!(i=(r=a.next()).done)&&(l.push(r.value),!t||l.length!==t);i=!0);}catch(e){o=!0,n=e}finally{try{i||null==a.return||a.return()}finally{if(o)throw n}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return c(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);if("Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r)return Array.from(r);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return c(e,t)}}(e,t)||function(){throw TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function p(e,t){var r,n,a,l={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]},i=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),o=Object.defineProperty;return o(i,"next",{value:u(0)}),o(i,"throw",{value:u(1)}),o(i,"return",{value:u(2)}),"function"==typeof Symbol&&o(i,Symbol.iterator,{value:function(){return this}}),i;function u(o){return function(u){var c=[o,u];if(r)throw TypeError("Generator is already executing.");for(;i&&(i=0,c[0]&&(l=0)),l;)try{if(r=1,n&&(a=2&c[0]?n.return:c[0]?n.throw||((a=n.return)&&a.call(n),0):n.next)&&!(a=a.call(n,c[1])).done)return a;switch(n=0,a&&(c=[2&c[0],a.value]),c[0]){case 0:case 1:a=c;break;case 4:return l.label++,{value:c[1],done:!1};case 5:l.label++,n=c[1],c=[0];continue;case 7:c=l.ops.pop(),l.trys.pop();continue;default:if(!(a=(a=l.trys).length>0&&a[a.length-1])&&(6===c[0]||2===c[0])){l=0;continue}if(3===c[0]&&(!a||c[1]>a[0]&&c[1]<a[3])){l.label=c[1];break}if(6===c[0]&&l.label<a[1]){l.label=a[1],a=c;break}if(a&&l.label<a[2]){l.label=a[2],l.ops.push(c);break}a[2]&&l.ops.pop(),l.trys.pop();continue}c=t.call(e,l)}catch(e){c=[6,e],n=0}finally{r=a=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}function f(){var e=(0,i.useFlowContext)(),t=(0,o.k)(),r=d(l.Form.useForm(),1)[0],c=d((0,n.useState)([]),2),s=c[0],f=c[1],y=d((0,n.useState)([]),2),b=y[0],h=y[1],v=d((0,n.useState)(!1),2),g=v[0],E=v[1],w=d((0,n.useState)(!1),2),S=w[0],k=w[1],I=d((0,n.useState)(),2),U=I[0],q=I[1],F=d((0,n.useState)(!1),2),T=F[0],x=F[1],C=(0,n.useCallback)(function(){return m(function(){var t,r,n,a;return p(this,function(i){switch(i.label){case 0:E(!0),i.label=1;case 1:return i.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiUserQuotaPolicies:list",method:"get",params:{pageSize:200,appends:["user"],sort:"-updatedAt"}}),e.api.request({url:"users:list",method:"get",params:{pageSize:200}})])];case 2:return r=(t=d.apply(void 0,[i.sent(),2]))[0],n=t[1],f((0,u.m)(r,[])),h((0,u.m)(n,[])),[3,5];case 3:return a=i.sent(),l.message.error((0,u.g)(a)),[3,5];case 4:return E(!1),[7];case 5:return[2]}})})()},[e.api]);(0,n.useEffect)(function(){C()},[C]);var A=function(e){q(e),r.setFieldsValue(e),x(!0)},P=function(e){var t;return(null==e?void 0:e.nickname)||(null==e?void 0:e.username)||(null==e?void 0:e.email)||String(null!=(t=null==e?void 0:e.id)?t:"")},j=[{title:t("User"),key:"user",width:180,render:function(e,t){return P(t.user)||String(t.userId)}},{title:t("Period"),dataIndex:"periodType",key:"periodType",width:100},{title:t("Request limit"),dataIndex:"requestLimit",key:"requestLimit",width:130,render:function(e){return null!=e?e:t("Unlimited")}},{title:t("Token limit"),dataIndex:"totalTokenLimit",key:"totalTokenLimit",width:130,render:function(e){return null!=e?e:t("Unlimited")}},{title:t("Cost limit"),dataIndex:"costLimit",key:"costLimit",width:120,render:function(e,r){return null==e?t("Unlimited"):"".concat(e," ").concat(r.currency)}},{title:t("Timezone"),dataIndex:"timezone",key:"timezone",width:140},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return a().createElement(l.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",width:150,fixed:"right",render:function(r,n){return a().createElement(l.Space,{size:0},a().createElement(l.Button,{type:"link",onClick:function(){return A(n)}},t("Edit")),a().createElement(l.Popconfirm,{title:t("Delete this quota?"),onConfirm:function(){return m(function(){var r;return p(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiUserQuotaPolicies:destroy/".concat(n.id),method:"post"})];case 1:return a.sent(),l.message.success(t("Deleted successfully")),[4,C()];case 2:return a.sent(),[3,4];case 3:return r=a.sent(),l.message.error((0,u.g)(r)),[3,4];case 4:return[2]}})})()}},a().createElement(l.Button,{type:"link",danger:!0},t("Delete"))))}}];return a().createElement(l.Card,{title:t("User quotas"),extra:a().createElement(l.Button,{type:"primary",onClick:function(){q(void 0),r.setFieldsValue({enabled:!0,periodType:"monthly",timezone:"UTC",currency:"USD",rejectUnpricedModel:!0,missingUsageBehavior:"use_reserved"}),x(!0)}},t("Add quota"))},a().createElement(l.Table,{rowKey:"id",columns:j,dataSource:s,loading:g,scroll:{x:1100}}),a().createElement(l.Modal,{title:U?t("Edit quota"):t("Add quota"),open:T,onCancel:function(){return x(!1)},onOk:function(){return m(function(){var n,a;return p(this,function(i){switch(i.label){case 0:return[4,r.validateFields()];case 1:n=i.sent(),k(!0),i.label=2;case 2:return i.trys.push([2,5,6,7]),[4,e.api.request({url:U?"aiApiUserQuotaPolicies:update/".concat(U.id):"aiApiUserQuotaPolicies:create",method:"post",data:n})];case 3:return i.sent(),l.message.success(t("Saved successfully")),x(!1),[4,C()];case 4:return i.sent(),[3,7];case 5:return a=i.sent(),l.message.error((0,u.g)(a)),[3,7];case 6:return k(!1),[7];case 7:return[2]}})})()},confirmLoading:S,destroyOnClose:!0},a().createElement(l.Form,{form:r,layout:"vertical",preserve:!1},a().createElement(l.Form.Item,{name:"userId",label:t("User"),rules:[{required:!0}]},a().createElement(l.Select,{disabled:!!U,showSearch:!0,optionFilterProp:"label",options:b.map(function(e){return{label:P(e),value:e.id}})})),a().createElement(l.Form.Item,{name:"periodType",label:t("Period"),rules:[{required:!0}]},a().createElement(l.Select,{options:[{label:t("Daily"),value:"daily"},{label:t("Monthly"),value:"monthly"}]})),a().createElement(l.Form.Item,{name:"timezone",label:t("Timezone"),rules:[{required:!0}]},a().createElement(l.Input,{placeholder:"UTC"})),a().createElement(l.Form.Item,{name:"requestLimit",label:t("Request limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"totalTokenLimit",label:t("Token limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"costLimit",label:t("Cost limit")},a().createElement(l.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),a().createElement(l.Form.Item,{name:"currency",label:t("Currency"),rules:[{required:!0}]},a().createElement(l.Input,null)),a().createElement(l.Form.Item,{name:"rejectUnpricedModel",label:t("Reject unpriced models"),valuePropName:"checked"},a().createElement(l.Switch,null)),a().createElement(l.Form.Item,{name:"missingUsageBehavior",label:t("Missing usage behavior"),rules:[{required:!0}]},a().createElement(l.Select,{options:[{label:t("Use reserved estimate"),value:"use_reserved"},{label:t("Allow without token charge"),value:"allow"}]})),a().createElement(l.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},a().createElement(l.Switch,null)))))}},650:function(e,t,r){r.d(t,{k:function(){return l}});var n=r(694),a=JSON.parse('{"UU":"plugin-ai-api"}');function l(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[a.UU,"client"]})}}},630:function(e,t,r){function n(e,t){var r,n;return e&&(void 0===e?"undefined":e&&"u">typeof Symbol&&e.constructor===Symbol?"symbol":typeof e)=="object"?null!=(r=null==(n=e.data)?void 0:n.data)?r:t:t}function a(e){var t;return(null!=(t=Error)&&"u">typeof Symbol&&t[Symbol.hasInstance]?!!t[Symbol.hasInstance](e):e instanceof t)?e.message:String(e)}r.d(t,{g:function(){return a},m:function(){return n}})}}]);