plugin-ai-api 1.0.20 → 1.0.21

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 (72) hide show
  1. package/dist/client/286.01c0e3c5fff3cccb.js +10 -0
  2. package/dist/client/302.fc3a3491b4ec2dfd.js +10 -0
  3. package/dist/client/562.17a0a299d2e5152c.js +10 -0
  4. package/dist/client/{757.71e30f2a1306562d.js → 757.a01403fb7a1bea01.js} +1 -1
  5. package/dist/client/{902.4238b04ac667c30a.js → 902.92e1daaf1ab16ebf.js} +1 -1
  6. package/dist/client/{97.37cda285d7da3a26.js → 97.72979a11a067a7c9.js} +1 -1
  7. package/dist/client/index.js +1 -1
  8. package/dist/client-v2/302.d27fe4ea9b0b3bf5.js +10 -0
  9. package/dist/client-v2/562.fb2948ee6402de95.js +10 -0
  10. package/dist/client-v2/{757.c377e2f2b054d89d.js → 757.a117ce1cf7119cea.js} +1 -1
  11. package/dist/client-v2/{902.d40d7bda106124c8.js → 902.9054d990ddc223ac.js} +1 -1
  12. package/dist/client-v2/952.94100128b7757f56.js +10 -0
  13. package/dist/client-v2/{97.fc922c37ced86831.js → 97.29c663318eebbd57.js} +1 -1
  14. package/dist/client-v2/index.js +1 -1
  15. package/dist/constants.js +36 -0
  16. package/dist/externalVersion.js +9 -10
  17. package/dist/locale/en-US.json +28 -9
  18. package/dist/locale/vi-VN.json +20 -1
  19. package/dist/locale/zh-CN.json +20 -1
  20. package/dist/server/collections/ai-api-config.js +6 -0
  21. package/dist/server/collections/ai-api-model-metadata.js +83 -0
  22. package/dist/server/plugin.js +13 -1
  23. package/dist/server/resource/ai-api-config.js +17 -0
  24. package/dist/server/routes/agent-completions.js +62 -51
  25. package/dist/server/routes/auth.js +11 -1
  26. package/dist/server/routes/chat-completions.js +145 -4
  27. package/dist/server/routes/completions.js +8 -1
  28. package/dist/server/routes/models.js +78 -20
  29. package/dist/server/routes/router.js +94 -22
  30. package/dist/server/usage.js +2 -0
  31. package/dist/server/utils/app-observability.js +110 -0
  32. package/dist/server/utils/streaming.js +15 -1
  33. package/dist/server/validation.js +18 -0
  34. package/dist/swagger.js +32 -1
  35. package/package.json +1 -1
  36. package/src/client/components/AiApiRolePermissions.tsx +11 -169
  37. package/src/client/locale.ts +11 -21
  38. package/src/client/plugin.tsx +17 -8
  39. package/src/client-v2/__tests__/settings-registration.test.tsx +58 -0
  40. package/src/client-v2/components/AiApiRolePermissions.tsx +173 -0
  41. package/src/client-v2/locale.ts +21 -1
  42. package/src/client-v2/pages/GeneralPage.tsx +13 -0
  43. package/src/client-v2/pages/ModelMetadataPage.tsx +280 -0
  44. package/src/client-v2/pages/RolePermissionsTab.tsx +14 -0
  45. package/src/client-v2/plugin.tsx +41 -1
  46. package/src/constants.ts +21 -0
  47. package/src/locale/en-US.json +28 -9
  48. package/src/locale/vi-VN.json +20 -1
  49. package/src/locale/zh-CN.json +20 -1
  50. package/src/server/__tests__/app-observability.test.ts +98 -0
  51. package/src/server/__tests__/models.test.ts +74 -0
  52. package/src/server/__tests__/request-body.test.ts +310 -0
  53. package/src/server/__tests__/streaming-observability.test.ts +51 -0
  54. package/src/server/collections/ai-api-config.ts +6 -0
  55. package/src/server/collections/ai-api-model-metadata.ts +72 -0
  56. package/src/server/plugin.ts +24 -4
  57. package/src/server/resource/ai-api-config.ts +23 -0
  58. package/src/server/routes/agent-completions.ts +77 -62
  59. package/src/server/routes/auth.ts +14 -1
  60. package/src/server/routes/chat-completions.ts +262 -4
  61. package/src/server/routes/completions.ts +14 -2
  62. package/src/server/routes/models.ts +290 -195
  63. package/src/server/routes/router.ts +136 -26
  64. package/src/server/usage.ts +2 -0
  65. package/src/server/utils/app-observability.ts +105 -0
  66. package/src/server/utils/streaming.ts +13 -1
  67. package/src/server/validation.ts +27 -0
  68. package/src/swagger.ts +38 -1
  69. package/dist/client/302.25edd5d75460acbf.js +0 -10
  70. package/dist/client/778.5c452944cb747975.js +0 -10
  71. package/dist/client-v2/302.9b27a263901d54d8.js +0 -10
  72. package/src/client/AiApiConfigPage.tsx +0 -309
@@ -7,4 +7,4 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- "use strict";(self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).push([["97"],{760:function(e,t,r){r.r(t),r.d(t,{default:function(){return g}});var n=r(155),o=r.n(n),i=r(59),l=r(185),a=r.n(l),u=r(694),c=r(235),s=r(630);function m(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 f(e,t,r,n,o,i,l){try{var a=e[i](l),u=a.value}catch(e){r(e);return}a.done?t(u):Promise.resolve(u).then(n,o)}function d(e){return function(){var t=this,r=arguments;return new Promise(function(n,o){var i=e.apply(t,r);function l(e){f(i,n,o,l,a,"next",e)}function a(e){f(i,n,o,l,a,"throw",e)}l(void 0)})}}function p(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},n=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),n.forEach(function(t){var n;n=r[t],t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n})}return e}function y(e,t){return t=null!=t?t:{},Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):(function(e){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t.push.apply(t,r)}return t})(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}),e}function b(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,o=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=o){var i=[],l=!0,a=!1;try{for(o=o.call(e);!(l=(r=o.next()).done)&&(i.push(r.value),!t||i.length!==t);l=!0);}catch(e){a=!0,n=e}finally{try{l||null==o.return||o.return()}finally{if(a)throw n}}return i}}(e,t)||v(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 v(e,t){if(e){if("string"==typeof e)return m(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 m(e,t)}}function h(e,t){var r,n,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},l=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),a=Object.defineProperty;return a(l,"next",{value:u(0)}),a(l,"throw",{value:u(1)}),a(l,"return",{value:u(2)}),"function"==typeof Symbol&&a(l,Symbol.iterator,{value:function(){return this}}),l;function u(a){return function(u){var c=[a,u];if(r)throw TypeError("Generator is already executing.");for(;l&&(l=0,c[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&c[0]?n.return:c[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,c[1])).done)return o;switch(n=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,n=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===c[0]||2===c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){i.label=c[1];break}if(6===c[0]&&i.label<o[1]){i.label=o[1],o=c;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(c);break}o[2]&&i.ops.pop(),i.trys.pop();continue}c=t.call(e,i)}catch(e){c=[6,e],n=0}finally{r=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}function g(){var e=(0,u.useFlowContext)(),t=(0,c.k)(),r=b(i.Form.useForm(),1)[0],l=i.Form.useWatch("llmService",r),f=b((0,n.useState)([]),2),g=f[0],S=f[1],w=b((0,n.useState)([]),2),E=w[0],P=w[1],I=b((0,n.useState)([]),2),k=I[0],O=I[1],F=b((0,n.useState)(!1),2),M=F[0],x=F[1],j=b((0,n.useState)(!1),2),T=j[0],C=j[1],q=b((0,n.useState)(!1),2),A=q[0],D=q[1],_=b((0,n.useState)(),2),L=_[0],N=_[1],U=b((0,n.useState)(!1),2),B=U[0],R=U[1],V=(0,n.useCallback)(function(){return d(function(){var t,r,n,o;return h(this,function(l){switch(l.label){case 0:C(!0),l.label=1;case 1:return l.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiModelPrices:list",method:"get",params:{pageSize:200,sort:"-effectiveFrom"}}),e.api.request({url:"ai:listLLMServices",method:"get"})])];case 2:return r=(t=b.apply(void 0,[l.sent(),2]))[0],n=t[1],S((0,s.m)(r,[])),P((0,s.m)(n,[])),[3,5];case 3:return o=l.sent(),i.message.error((0,s.g)(o)),[3,5];case 4:return C(!1),[7];case 5:return[2]}})})()},[e.api]);(0,n.useEffect)(function(){V()},[V]);var z=function(e){N(e),r.setFieldsValue(y(p({},e),{effectiveFrom:a()(e.effectiveFrom),effectiveTo:e.effectiveTo?a()(e.effectiveTo):void 0})),R(!0),G(e.llmService,e.model)},G=function(r,n){return d(function(){var o,l,a;return h(this,function(u){switch(u.label){case 0:x(!0),u.label=1;case 1:return u.trys.push([1,3,4,5]),[4,e.api.request({url:"ai:listModels",method:"get",params:{llmService:r}})];case 2:return o=u.sent(),l=(0,s.m)(o,[]),O(n&&!l.some(function(e){return e.id===n})?[{id:n}].concat(function(e){if(Array.isArray(e))return m(e)}(l)||function(e){if("u">typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(l)||v(l)||function(){throw TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()):l),[3,5];case 3:return a=u.sent(),O(n?[{id:n}]:[]),i.message.error("".concat(t("Failed to load models"),": ").concat((0,s.g)(a))),[3,5];case 4:return x(!1),[7];case 5:return[2]}})})()},J=[{title:t("LLM service"),dataIndex:"llmService",key:"llmService",width:180},{title:t("Model"),dataIndex:"model",key:"model",width:180},{title:t("Input price / 1M"),dataIndex:"inputPricePerMillionTokens",key:"inputPrice",width:150},{title:t("Output price / 1M"),dataIndex:"outputPricePerMillionTokens",key:"outputPrice",width:150},{title:t("Fixed request cost"),dataIndex:"fixedCostPerRequest",key:"fixedCost",width:150},{title:t("Currency"),dataIndex:"currency",key:"currency",width:90},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return o().createElement(i.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",fixed:"right",width:150,render:function(r,n){return o().createElement(i.Space,null,o().createElement(i.Button,{type:"link",onClick:function(){return z(n)}},t("Edit")),o().createElement(i.Popconfirm,{title:t("Delete this price?"),onConfirm:function(){var r;return r=n.id,d(function(){var n;return h(this,function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiModelPrices:destroy/".concat(r),method:"post"})];case 1:return o.sent(),i.message.success(t("Deleted successfully")),[4,V()];case 2:return o.sent(),[3,4];case 3:return n=o.sent(),i.message.error((0,s.g)(n)),[3,4];case 4:return[2]}})})()}},o().createElement(i.Button,{type:"link",danger:!0},t("Delete"))))}}];return o().createElement(i.Card,{title:t("Model pricing"),extra:o().createElement(i.Button,{type:"primary",onClick:function(){N(void 0),O([]),r.setFieldsValue({currency:"USD",inputPricePerMillionTokens:"0",outputPricePerMillionTokens:"0",fixedCostPerRequest:"0",effectiveFrom:a()(),enabled:!0}),R(!0)}},t("Add price"))},o().createElement(i.Table,{rowKey:"id",columns:J,dataSource:g,loading:T,scroll:{x:1200}}),o().createElement(i.Modal,{title:L?t("Edit price"):t("Add price"),open:B,onCancel:function(){return R(!1)},onOk:function(){return d(function(){var n,o,l,a,u,c,m;return h(this,function(f){switch(f.label){case 0:return[4,r.validateFields()];case 1:a=f.sent(),u=E.find(function(e){return e.name===a.llmService}),c=y(p({},a),{provider:null!=(n=null==u?void 0:u.provider)?n:a.provider,effectiveFrom:a.effectiveFrom.toISOString(),effectiveTo:null!=(o=null==(l=a.effectiveTo)?void 0:l.toISOString())?o:null}),D(!0),f.label=2;case 2:return f.trys.push([2,5,6,7]),[4,e.api.request({url:L?"aiApiModelPrices:update/".concat(L.id):"aiApiModelPrices:create",method:"post",data:c})];case 3:return f.sent(),i.message.success(t("Saved successfully")),R(!1),[4,V()];case 4:return f.sent(),[3,7];case 5:return m=f.sent(),i.message.error((0,s.g)(m)),[3,7];case 6:return D(!1),[7];case 7:return[2]}})})()},confirmLoading:A,destroyOnClose:!0},o().createElement(i.Form,{form:r,layout:"vertical",preserve:!1},o().createElement(i.Form.Item,{name:"llmService",label:t("LLM service"),rules:[{required:!0}]},o().createElement(i.Select,{showSearch:!0,optionFilterProp:"label",onChange:function(e){r.setFieldValue("model",void 0),O([]),G(e)},options:E.map(function(e){return{label:e.title||e.name,value:e.name}})})),o().createElement(i.Form.Item,{name:"provider",hidden:!0},o().createElement(i.Input,null)),o().createElement(i.Form.Item,{name:"model",label:t("Model"),rules:[{required:!0}]},o().createElement(i.Select,{showSearch:!0,optionFilterProp:"label",loading:M,disabled:!l,placeholder:t("Select a model"),options:k.map(function(e){return{label:e.id,value:e.id}})})),o().createElement(i.Form.Item,{name:"currency",label:t("Currency"),rules:[{required:!0}]},o().createElement(i.Input,null)),o().createElement(i.Form.Item,{name:"inputPricePerMillionTokens",label:t("Input price / 1M"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"outputPricePerMillionTokens",label:t("Output price / 1M"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"fixedCostPerRequest",label:t("Fixed request cost"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"effectiveFrom",label:t("Effective from"),rules:[{required:!0}]},o().createElement(i.DatePicker,{showTime:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"effectiveTo",label:t("Effective to")},o().createElement(i.DatePicker,{showTime:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},o().createElement(i.Switch,null)),o().createElement(i.Form.Item,{name:"notes",label:t("Notes")},o().createElement(i.Input.TextArea,{rows:3})))))}},235:function(e,t,r){r.d(t,{k:function(){return i}});var n=r(694),o=JSON.parse('{"UU":"plugin-ai-api"}');function i(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[o.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 o(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 o},m:function(){return n}})}}]);
10
+ "use strict";(self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).push([["97"],{760:function(e,t,r){r.r(t),r.d(t,{default:function(){return g}});var n=r(155),o=r.n(n),i=r(59),l=r(185),a=r.n(l),u=r(694),c=r(650),s=r(630);function m(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 f(e,t,r,n,o,i,l){try{var a=e[i](l),u=a.value}catch(e){r(e);return}a.done?t(u):Promise.resolve(u).then(n,o)}function d(e){return function(){var t=this,r=arguments;return new Promise(function(n,o){var i=e.apply(t,r);function l(e){f(i,n,o,l,a,"next",e)}function a(e){f(i,n,o,l,a,"throw",e)}l(void 0)})}}function p(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},n=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(r).filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable}))),n.forEach(function(t){var n;n=r[t],t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n})}return e}function y(e,t){return t=null!=t?t:{},Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):(function(e){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t.push.apply(t,r)}return t})(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}),e}function b(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,o=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=o){var i=[],l=!0,a=!1;try{for(o=o.call(e);!(l=(r=o.next()).done)&&(i.push(r.value),!t||i.length!==t);l=!0);}catch(e){a=!0,n=e}finally{try{l||null==o.return||o.return()}finally{if(a)throw n}}return i}}(e,t)||v(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 v(e,t){if(e){if("string"==typeof e)return m(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 m(e,t)}}function h(e,t){var r,n,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},l=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),a=Object.defineProperty;return a(l,"next",{value:u(0)}),a(l,"throw",{value:u(1)}),a(l,"return",{value:u(2)}),"function"==typeof Symbol&&a(l,Symbol.iterator,{value:function(){return this}}),l;function u(a){return function(u){var c=[a,u];if(r)throw TypeError("Generator is already executing.");for(;l&&(l=0,c[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&c[0]?n.return:c[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,c[1])).done)return o;switch(n=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,n=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===c[0]||2===c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){i.label=c[1];break}if(6===c[0]&&i.label<o[1]){i.label=o[1],o=c;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(c);break}o[2]&&i.ops.pop(),i.trys.pop();continue}c=t.call(e,i)}catch(e){c=[6,e],n=0}finally{r=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}function g(){var e=(0,u.useFlowContext)(),t=(0,c.k)(),r=b(i.Form.useForm(),1)[0],l=i.Form.useWatch("llmService",r),f=b((0,n.useState)([]),2),g=f[0],S=f[1],w=b((0,n.useState)([]),2),E=w[0],P=w[1],I=b((0,n.useState)([]),2),k=I[0],O=I[1],F=b((0,n.useState)(!1),2),M=F[0],x=F[1],j=b((0,n.useState)(!1),2),T=j[0],C=j[1],q=b((0,n.useState)(!1),2),A=q[0],D=q[1],_=b((0,n.useState)(),2),L=_[0],N=_[1],U=b((0,n.useState)(!1),2),B=U[0],R=U[1],V=(0,n.useCallback)(function(){return d(function(){var t,r,n,o;return h(this,function(l){switch(l.label){case 0:C(!0),l.label=1;case 1:return l.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiModelPrices:list",method:"get",params:{pageSize:200,sort:"-effectiveFrom"}}),e.api.request({url:"ai:listLLMServices",method:"get"})])];case 2:return r=(t=b.apply(void 0,[l.sent(),2]))[0],n=t[1],S((0,s.m)(r,[])),P((0,s.m)(n,[])),[3,5];case 3:return o=l.sent(),i.message.error((0,s.g)(o)),[3,5];case 4:return C(!1),[7];case 5:return[2]}})})()},[e.api]);(0,n.useEffect)(function(){V()},[V]);var z=function(e){N(e),r.setFieldsValue(y(p({},e),{effectiveFrom:a()(e.effectiveFrom),effectiveTo:e.effectiveTo?a()(e.effectiveTo):void 0})),R(!0),G(e.llmService,e.model)},G=function(r,n){return d(function(){var o,l,a;return h(this,function(u){switch(u.label){case 0:x(!0),u.label=1;case 1:return u.trys.push([1,3,4,5]),[4,e.api.request({url:"ai:listModels",method:"get",params:{llmService:r}})];case 2:return o=u.sent(),l=(0,s.m)(o,[]),O(n&&!l.some(function(e){return e.id===n})?[{id:n}].concat(function(e){if(Array.isArray(e))return m(e)}(l)||function(e){if("u">typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(l)||v(l)||function(){throw TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()):l),[3,5];case 3:return a=u.sent(),O(n?[{id:n}]:[]),i.message.error("".concat(t("Failed to load models"),": ").concat((0,s.g)(a))),[3,5];case 4:return x(!1),[7];case 5:return[2]}})})()},J=[{title:t("LLM service"),dataIndex:"llmService",key:"llmService",width:180},{title:t("Model"),dataIndex:"model",key:"model",width:180},{title:t("Input price / 1M"),dataIndex:"inputPricePerMillionTokens",key:"inputPrice",width:150},{title:t("Output price / 1M"),dataIndex:"outputPricePerMillionTokens",key:"outputPrice",width:150},{title:t("Fixed request cost"),dataIndex:"fixedCostPerRequest",key:"fixedCost",width:150},{title:t("Currency"),dataIndex:"currency",key:"currency",width:90},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return o().createElement(i.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",fixed:"right",width:150,render:function(r,n){return o().createElement(i.Space,null,o().createElement(i.Button,{type:"link",onClick:function(){return z(n)}},t("Edit")),o().createElement(i.Popconfirm,{title:t("Delete this price?"),onConfirm:function(){var r;return r=n.id,d(function(){var n;return h(this,function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiModelPrices:destroy/".concat(r),method:"post"})];case 1:return o.sent(),i.message.success(t("Deleted successfully")),[4,V()];case 2:return o.sent(),[3,4];case 3:return n=o.sent(),i.message.error((0,s.g)(n)),[3,4];case 4:return[2]}})})()}},o().createElement(i.Button,{type:"link",danger:!0},t("Delete"))))}}];return o().createElement(i.Card,{title:t("Model pricing"),extra:o().createElement(i.Button,{type:"primary",onClick:function(){N(void 0),O([]),r.setFieldsValue({currency:"USD",inputPricePerMillionTokens:"0",outputPricePerMillionTokens:"0",fixedCostPerRequest:"0",effectiveFrom:a()(),enabled:!0}),R(!0)}},t("Add price"))},o().createElement(i.Table,{rowKey:"id",columns:J,dataSource:g,loading:T,scroll:{x:1200}}),o().createElement(i.Modal,{title:L?t("Edit price"):t("Add price"),open:B,onCancel:function(){return R(!1)},onOk:function(){return d(function(){var n,o,l,a,u,c,m;return h(this,function(f){switch(f.label){case 0:return[4,r.validateFields()];case 1:a=f.sent(),u=E.find(function(e){return e.name===a.llmService}),c=y(p({},a),{provider:null!=(n=null==u?void 0:u.provider)?n:a.provider,effectiveFrom:a.effectiveFrom.toISOString(),effectiveTo:null!=(o=null==(l=a.effectiveTo)?void 0:l.toISOString())?o:null}),D(!0),f.label=2;case 2:return f.trys.push([2,5,6,7]),[4,e.api.request({url:L?"aiApiModelPrices:update/".concat(L.id):"aiApiModelPrices:create",method:"post",data:c})];case 3:return f.sent(),i.message.success(t("Saved successfully")),R(!1),[4,V()];case 4:return f.sent(),[3,7];case 5:return m=f.sent(),i.message.error((0,s.g)(m)),[3,7];case 6:return D(!1),[7];case 7:return[2]}})})()},confirmLoading:A,destroyOnClose:!0},o().createElement(i.Form,{form:r,layout:"vertical",preserve:!1},o().createElement(i.Form.Item,{name:"llmService",label:t("LLM service"),rules:[{required:!0}]},o().createElement(i.Select,{showSearch:!0,optionFilterProp:"label",onChange:function(e){r.setFieldValue("model",void 0),O([]),G(e)},options:E.map(function(e){return{label:e.title||e.name,value:e.name}})})),o().createElement(i.Form.Item,{name:"provider",hidden:!0},o().createElement(i.Input,null)),o().createElement(i.Form.Item,{name:"model",label:t("Model"),rules:[{required:!0}]},o().createElement(i.Select,{showSearch:!0,optionFilterProp:"label",loading:M,disabled:!l,placeholder:t("Select a model"),options:k.map(function(e){return{label:e.id,value:e.id}})})),o().createElement(i.Form.Item,{name:"currency",label:t("Currency"),rules:[{required:!0}]},o().createElement(i.Input,null)),o().createElement(i.Form.Item,{name:"inputPricePerMillionTokens",label:t("Input price / 1M"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"outputPricePerMillionTokens",label:t("Output price / 1M"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"fixedCostPerRequest",label:t("Fixed request cost"),rules:[{required:!0}]},o().createElement(i.InputNumber,{min:0,stringMode:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"effectiveFrom",label:t("Effective from"),rules:[{required:!0}]},o().createElement(i.DatePicker,{showTime:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"effectiveTo",label:t("Effective to")},o().createElement(i.DatePicker,{showTime:!0,style:{width:"100%"}})),o().createElement(i.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},o().createElement(i.Switch,null)),o().createElement(i.Form.Item,{name:"notes",label:t("Notes")},o().createElement(i.Input.TextArea,{rows:3})))))}},650:function(e,t,r){r.d(t,{k:function(){return i}});var n=r(694),o=JSON.parse('{"UU":"plugin-ai-api"}');function i(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[o.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 o(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 o},m:function(){return n}})}}]);
@@ -7,4 +7,4 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/flow-engine")):"function"==typeof define&&define.amd?define("plugin-ai-api/client-v2",["@nocobase/client-v2","dayjs","react","antd","@nocobase/flow-engine"],t):"object"==typeof exports?exports["plugin-ai-api/client-v2"]=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/flow-engine")):e["plugin-ai-api/client-v2"]=t(e["@nocobase/client-v2"],e.dayjs,e.react,e.antd,e["@nocobase/flow-engine"])}(self,function(e,t,n,r,o){return function(){"use strict";var i,a,u,c={485:function(t){t.exports=e},694:function(e){e.exports=o},59:function(e){e.exports=r},185:function(e){e.exports=t},155:function(e){e.exports=n}},l={};function p(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={exports:{}};return c[e](n,n.exports,p),n.exports}p.m=c,p.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(t,{a:t}),t},p.d=function(e,t){for(var n in t)p.o(t,n)&&!p.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},p.f={},p.e=function(e){return Promise.all(Object.keys(p.f).reduce(function(t,n){return p.f[n](e,t),t},[]))},p.u=function(e){return""+e+"."+({302:"9b27a263901d54d8",757:"c377e2f2b054d89d",902:"d40d7bda106124c8",97:"fc922c37ced86831"})[e]+".js"},p.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),p.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},f={},p.l=function(e,t,n,r){if(f[e])return void f[e].push(t);if(void 0!==n)for(var o,i,a=document.getElementsByTagName("script"),u=0;u<a.length;u++){var c=a[u];if(c.getAttribute("src")==e||c.getAttribute("data-rspack")=="plugin-ai-api/client-v2:"+n){o=c;break}}o||(i=!0,(o=document.createElement("script")).timeout=120,p.nc&&o.setAttribute("nonce",p.nc),o.setAttribute("data-rspack","plugin-ai-api/client-v2:"+n),o.src=e),f[e]=[t];var l=function(t,n){o.onerror=o.onload=null,clearTimeout(s);var r=f[e];if(delete f[e],o.parentNode&&o.parentNode.removeChild(o),r&&r.forEach(function(e){return e(n)}),t)return t(n)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=l.bind(null,o.onerror),o.onload=l.bind(null,o.onload),i&&document.head.appendChild(o)},p.r=function(e){"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.g.importScripts&&(s=p.g.location+"");var f,s,d=p.g.document;if(!s&&d&&(d.currentScript&&"SCRIPT"===d.currentScript.tagName.toUpperCase()&&(s=d.currentScript.src),!s)){var b=d.getElementsByTagName("script");if(b.length)for(var g=b.length-1;g>-1&&(!s||!/^http(s?):/.test(s));)s=b[g--].src}if(!s)throw Error("Automatic publicPath is not supported in this browser");p.p=s.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),i={889:0},p.f.j=function(e,t){var n=p.o(i,e)?i[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=p.p+p.u(e),a=Error();p.l(o,function(t){if(p.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;a.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",a.name="ChunkLoadError",a.type=r,a.request=o,n[1](a)}},"chunk-"+e,e)}},a=function(e,t){var n,r,o=t[0],a=t[1],u=t[2],c=0;if(o.some(function(e){return 0!==i[e]})){for(n in a)p.o(a,n)&&(p.m[n]=a[n]);u&&u(p)}for(e&&e(t);c<o.length;c++)r=o[c],p.o(i,r)&&i[r]&&i[r][0](),i[r]=0},(u=self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).forEach(a.bind(null,0)),u.push=a.bind(null,u.push.bind(u));var h={};return!function(){var e="",t="u">typeof document?document.currentScript:null;if(t&&t.src){var n=t.src.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"");n.indexOf("/static/plugins/plugin-ai-api/dist/client-v2/")>=0&&(e=n.replace(/\/[^\/]+$/,"/"))}if(!e){var r=window.__webpack_public_path__||"";r&&("/"!==r.charAt(r.length-1)&&(r+="/"),e=r+"static/plugins/plugin-ai-api/dist/client-v2/")}if(!e){var o=window.__nocobase_modern_client_prefix__||"v",i="/"+(o=String(o).replace(/^\/+|\/+$/g,"")||"v")+"/";if(!(e=window.__nocobase_public_path__||"")&&window.location&&window.location.pathname){var a=window.location.pathname||"/",u=a.indexOf(i);e=u>=0?a.slice(0,u+1):"/"}e&&(e=e.replace(RegExp("/"+o+"/?$"),"/")),e||(e="/"),"/"!==e.charAt(e.length-1)&&(e+="/"),e+="static/plugins/plugin-ai-api/dist/client-v2/"}p.p=e}(),!function(){p.r(h),p.d(h,{default:function(){return u}});var e=p(485);function t(e,t,n,r,o,i,a){try{var u=e[i](a),c=u.value}catch(e){n(e);return}u.done?t(c):Promise.resolve(c).then(r,o)}function n(e,t,r){return(n=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&o(i,n.prototype),i}).apply(null,arguments)}function r(e){return(r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function o(e,t){return(o=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function i(e){var t="function"==typeof Map?new Map:void 0;return(i=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,i)}function i(){return n(e,arguments,r(this).constructor)}return i.prototype=Object.create(e.prototype,{constructor:{value:i,enumerable:!1,writable:!0,configurable:!0}}),o(i,e)})(e)}function a(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(a=function(){return!!e})()}var u=function(e){var n;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function i(){var e,t;if(!(this instanceof i))throw TypeError("Cannot call a class as a function");return e=i,t=arguments,e=r(e),function(e,t){var n;if(t&&("object"==((n=t)&&"u">typeof Symbol&&n.constructor===Symbol?"symbol":typeof n)||"function"==typeof t))return t;if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(this,a()?Reflect.construct(e,t||[],r(this).constructor):e.apply(this,t))}return i.prototype=Object.create(e&&e.prototype,{constructor:{value:i,writable:!0,configurable:!0}}),e&&o(i,e),n=[{key:"load",value:function(){var e;return(e=function(){return function(e,t){var n,r,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},a=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),u=Object.defineProperty;return u(a,"next",{value:c(0)}),u(a,"throw",{value:c(1)}),u(a,"return",{value:c(2)}),"function"==typeof Symbol&&u(a,Symbol.iterator,{value:function(){return this}}),a;function c(u){return function(c){var l=[u,c];if(n)throw TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(n=1,r&&(o=2&l[0]?r.return:l[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,l[1])).done)return o;switch(r=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,r=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===l[0]||2===l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]<o[3])){i.label=l[1];break}if(6===l[0]&&i.label<o[1]){i.label=o[1],o=l;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(l);break}o[2]&&i.ops.pop(),i.trys.pop();continue}l=t.call(e,i)}catch(e){l=[6,e],r=0}finally{n=o=0}if(5&l[0])throw l[1];return{value:l[0]?l[1]:void 0,done:!0}}}}(this,function(e){return this.pluginSettingsManager.addMenuItem({key:"ai-api",title:this.t("AI API Gateway"),icon:"ApiOutlined",aclSnippet:"pm.ai-api.configuration"}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"index",title:this.t("Configuration"),componentLoader:function(){return p.e("302").then(p.bind(p,581))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"model-pricing",title:this.t("Model pricing"),componentLoader:function(){return p.e("97").then(p.bind(p,760))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"user-quotas",title:this.t("User quotas"),componentLoader:function(){return p.e("902").then(p.bind(p,77))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"usage",title:this.t("Usage"),componentLoader:function(){return p.e("757").then(p.bind(p,364))}}),[2]})},function(){var n=this,r=arguments;return new Promise(function(o,i){var a=e.apply(n,r);function u(e){t(a,o,i,u,c,"next",e)}function c(e){t(a,o,i,u,c,"throw",e)}u(void 0)})}).call(this)}}],function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(i.prototype,n),i}(i(e.Plugin))}(),h}()});
10
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/flow-engine")):"function"==typeof define&&define.amd?define("plugin-ai-api/client-v2",["@nocobase/client-v2","dayjs","react","antd","@nocobase/flow-engine"],t):"object"==typeof exports?exports["plugin-ai-api/client-v2"]=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/flow-engine")):e["plugin-ai-api/client-v2"]=t(e["@nocobase/client-v2"],e.dayjs,e.react,e.antd,e["@nocobase/flow-engine"])}(self,function(e,t,n,r,o){return function(){"use strict";var i,a,u,c={485:function(t){t.exports=e},694:function(e){e.exports=o},59:function(e){e.exports=r},185:function(e){e.exports=t},155:function(e){e.exports=n}},l={};function p(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={exports:{}};return c[e](n,n.exports,p),n.exports}p.m=c,p.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(t,{a:t}),t},p.d=function(e,t){for(var n in t)p.o(t,n)&&!p.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},p.f={},p.e=function(e){return Promise.all(Object.keys(p.f).reduce(function(t,n){return p.f[n](e,t),t},[]))},p.u=function(e){return""+e+"."+({302:"d27fe4ea9b0b3bf5",562:"fb2948ee6402de95",757:"a117ce1cf7119cea",902:"9054d990ddc223ac",952:"94100128b7757f56",97:"29c663318eebbd57"})[e]+".js"},p.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),p.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s={},p.l=function(e,t,n,r){if(s[e])return void s[e].push(t);if(void 0!==n)for(var o,i,a=document.getElementsByTagName("script"),u=0;u<a.length;u++){var c=a[u];if(c.getAttribute("src")==e||c.getAttribute("data-rspack")=="plugin-ai-api/client-v2:"+n){o=c;break}}o||(i=!0,(o=document.createElement("script")).timeout=120,p.nc&&o.setAttribute("nonce",p.nc),o.setAttribute("data-rspack","plugin-ai-api/client-v2:"+n),o.src=e),s[e]=[t];var l=function(t,n){o.onerror=o.onload=null,clearTimeout(f);var r=s[e];if(delete s[e],o.parentNode&&o.parentNode.removeChild(o),r&&r.forEach(function(e){return e(n)}),t)return t(n)},f=setTimeout(l.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=l.bind(null,o.onerror),o.onload=l.bind(null,o.onload),i&&document.head.appendChild(o)},p.r=function(e){"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.g.importScripts&&(f=p.g.location+"");var s,f,d=p.g.document;if(!f&&d&&(d.currentScript&&"SCRIPT"===d.currentScript.tagName.toUpperCase()&&(f=d.currentScript.src),!f)){var b=d.getElementsByTagName("script");if(b.length)for(var g=b.length-1;g>-1&&(!f||!/^http(s?):/.test(f));)f=b[g--].src}if(!f)throw Error("Automatic publicPath is not supported in this browser");p.p=f.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),i={889:0},p.f.j=function(e,t){var n=p.o(i,e)?i[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=p.p+p.u(e),a=Error();p.l(o,function(t){if(p.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;a.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",a.name="ChunkLoadError",a.type=r,a.request=o,n[1](a)}},"chunk-"+e,e)}},a=function(e,t){var n,r,o=t[0],a=t[1],u=t[2],c=0;if(o.some(function(e){return 0!==i[e]})){for(n in a)p.o(a,n)&&(p.m[n]=a[n]);u&&u(p)}for(e&&e(t);c<o.length;c++)r=o[c],p.o(i,r)&&i[r]&&i[r][0](),i[r]=0},(u=self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).forEach(a.bind(null,0)),u.push=a.bind(null,u.push.bind(u));var h={};return!function(){var e="",t="u">typeof document?document.currentScript:null;if(t&&t.src){var n=t.src.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"");n.indexOf("/static/plugins/plugin-ai-api/dist/client-v2/")>=0&&(e=n.replace(/\/[^\/]+$/,"/"))}if(!e){var r=window.__webpack_public_path__||"";r&&("/"!==r.charAt(r.length-1)&&(r+="/"),e=r+"static/plugins/plugin-ai-api/dist/client-v2/")}if(!e){var o=window.__nocobase_modern_client_prefix__||"v",i="/"+(o=String(o).replace(/^\/+|\/+$/g,"")||"v")+"/";if(!(e=window.__nocobase_public_path__||"")&&window.location&&window.location.pathname){var a=window.location.pathname||"/",u=a.indexOf(i);e=u>=0?a.slice(0,u+1):"/"}e&&(e=e.replace(RegExp("/"+o+"/?$"),"/")),e||(e="/"),"/"!==e.charAt(e.length-1)&&(e+="/"),e+="static/plugins/plugin-ai-api/dist/client-v2/"}p.p=e}(),!function(){p.r(h),p.d(h,{default:function(){return c}}),p(155);var e=p(485),t="pm.plugin-ai-api.configuration";function n(e,t,n,r,o,i,a){try{var u=e[i](a),c=u.value}catch(e){n(e);return}u.done?t(c):Promise.resolve(c).then(r,o)}function r(e,t,n){return(r=u()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var o=new(Function.bind.apply(e,r));return n&&i(o,n.prototype),o}).apply(null,arguments)}function o(e){return(o=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function i(e,t){return(i=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function a(e){var t="function"==typeof Map?new Map:void 0;return(a=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return r(e,arguments,o(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),i(n,e)})(e)}function u(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(u=function(){return!!e})()}var c=function(e){var r;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function a(){var e,t;if(!(this instanceof a))throw TypeError("Cannot call a class as a function");return e=a,t=arguments,e=o(e),function(e,t){var n;if(t&&("object"==((n=t)&&"u">typeof Symbol&&n.constructor===Symbol?"symbol":typeof n)||"function"==typeof t))return t;if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(this,u()?Reflect.construct(e,t||[],o(this).constructor):e.apply(this,t))}return a.prototype=Object.create(e&&e.prototype,{constructor:{value:a,writable:!0,configurable:!0}}),e&&i(a,e),r=[{key:"load",value:function(){var e;return(e=function(){var e,n,r;return function(e,t){var n,r,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},a=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),u=Object.defineProperty;return u(a,"next",{value:c(0)}),u(a,"throw",{value:c(1)}),u(a,"return",{value:c(2)}),"function"==typeof Symbol&&u(a,Symbol.iterator,{value:function(){return this}}),a;function c(u){return function(c){var l=[u,c];if(n)throw TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(n=1,r&&(o=2&l[0]?r.return:l[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,l[1])).done)return o;switch(r=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,r=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===l[0]||2===l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]<o[3])){i.label=l[1];break}if(6===l[0]&&i.label<o[1]){i.label=o[1],o=l;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(l);break}o[2]&&i.ops.pop(),i.trys.pop();continue}l=t.call(e,i)}catch(e){l=[6,e],r=0}finally{n=o=0}if(5&l[0])throw l[1];return{value:l[0]?l[1]:void 0,done:!0}}}}(this,function(o){return this.pluginSettingsManager.addMenuItem({key:"ai-api",title:this.t("AI API Gateway"),icon:"ApiOutlined",aclSnippet:t}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"index",title:this.t("Configuration"),aclSnippet:t,sort:1,componentLoader:function(){return p.e("302").then(p.bind(p,581))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"model-pricing",title:this.t("Model pricing"),aclSnippet:t,sort:2,componentLoader:function(){return p.e("97").then(p.bind(p,760))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"model-metadata",title:this.t("Model metadata"),aclSnippet:t,sort:3,componentLoader:function(){return p.e("562").then(p.bind(p,641))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"user-quotas",title:this.t("User quotas"),aclSnippet:t,sort:4,componentLoader:function(){return p.e("902").then(p.bind(p,77))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"usage",title:this.t("Usage"),aclSnippet:t,sort:5,componentLoader:function(){return p.e("757").then(p.bind(p,364))}}),null==(r=this.app.pm.get("@nocobase/plugin-acl"))||null==(n=r.settingsUI)||null==(e=n.addPermissionsTab)||e.call(n,{key:"aiApi",label:this.t("AI API"),sort:25,componentLoader:function(){return p.e("952").then(p.bind(p,699))}}),[2]})},function(){var t=this,r=arguments;return new Promise(function(o,i){var a=e.apply(t,r);function u(e){n(a,o,i,u,c,"next",e)}function c(e){n(a,o,i,u,c,"throw",e)}u(void 0)})}).call(this)}}],function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(a.prototype,r),a}(a(e.Plugin))}(),h}()});
@@ -0,0 +1,36 @@
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 constants_exports = {};
28
+ __export(constants_exports, {
29
+ AI_API_ACL_SNIPPET: () => AI_API_ACL_SNIPPET
30
+ });
31
+ module.exports = __toCommonJS(constants_exports);
32
+ const AI_API_ACL_SNIPPET = "pm.plugin-ai-api.configuration";
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ AI_API_ACL_SNIPPET
36
+ });
@@ -8,17 +8,16 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
+ "@nocobase/client": "2.1.34",
12
+ "@nocobase/plugin-acl": "2.1.34",
11
13
  "react": "18.2.0",
12
- "antd": "5.24.2",
13
- "@nocobase/client-v2": "2.1.32",
14
- "@nocobase/flow-engine": "2.1.32",
15
- "@nocobase/client": "2.1.32",
16
- "@nocobase/plugin-acl": "2.1.32",
17
- "@nocobase/actions": "2.1.32",
14
+ "@nocobase/flow-engine": "2.1.34",
15
+ "@nocobase/client-v2": "2.1.34",
16
+ "@nocobase/actions": "2.1.34",
18
17
  "dayjs": "1.11.13",
19
- "@nocobase/database": "2.1.32",
18
+ "@nocobase/database": "2.1.34",
20
19
  "sequelize": "6.35.2",
21
- "@nocobase/server": "2.1.32",
22
- "@nocobase/resourcer": "2.1.32",
23
- "@nocobase/plugin-ai": "2.1.32"
20
+ "@nocobase/server": "2.1.34",
21
+ "antd": "5.24.2",
22
+ "@nocobase/resourcer": "2.1.34"
24
23
  };
@@ -1,11 +1,11 @@
1
- {
2
- "AI API Gateway": "AI API Gateway",
3
- "Configuration": "Configuration",
4
- "Default AI Employee": "Default AI Employee",
5
- "Enabled LLM Services": "Enabled LLM Services",
6
- "Rate Limit": "Rate Limit",
7
- "Save Configuration": "Save Configuration",
8
- "Configuration saved": "Configuration saved",
1
+ {
2
+ "AI API Gateway": "AI API Gateway",
3
+ "Configuration": "Configuration",
4
+ "Default AI Employee": "Default AI Employee",
5
+ "Enabled LLM Services": "Enabled LLM Services",
6
+ "Rate Limit": "Rate Limit",
7
+ "Save Configuration": "Save Configuration",
8
+ "Configuration saved": "Configuration saved",
9
9
  "Failed to save configuration": "Failed to save configuration",
10
10
  "API mode": "API mode",
11
11
  "Direct LLM": "Direct LLM",
@@ -82,5 +82,24 @@
82
82
  "Reset": "Reset",
83
83
  "Requests": "Requests",
84
84
  "Total cost": "Total cost",
85
- "Usage records": "Usage records"
85
+ "Usage records": "Usage records",
86
+ "Model metadata": "Model metadata",
87
+ "Add override": "Add override",
88
+ "Edit override": "Edit override",
89
+ "Delete this override?": "Delete this override?",
90
+ "Context window": "Context window",
91
+ "Max completion tokens": "Max completion tokens",
92
+ "Owned by": "Owned by",
93
+ "Display name": "Display name",
94
+ "Description": "Description",
95
+ "Leave empty to not override": "Leave empty to not override",
96
+ "Total input + output token capacity reported to clients.": "Total input + output token capacity reported to clients.",
97
+ "Maximum output tokens reported to clients.": "Maximum output tokens reported to clients.",
98
+ "AI API": "AI API",
99
+ "Allow this role to use the AI API": "Allow this role to use the AI API",
100
+ "Allow all AI Employees": "Allow all AI Employees",
101
+ "Select which AI Employees this role may use:": "Select which AI Employees this role may use:",
102
+ "Select allowed AI Employees": "Select allowed AI Employees",
103
+ "Max request body size (MB)": "Max request body size (MB)",
104
+ "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size."
86
105
  }
@@ -82,5 +82,24 @@
82
82
  "Reset": "Đặt lại",
83
83
  "Requests": "Số request",
84
84
  "Total cost": "Tổng chi phí",
85
- "Usage records": "Chi tiết usage"
85
+ "Usage records": "Chi tiết usage",
86
+ "Model metadata": "Metadata model",
87
+ "Add override": "Thêm override",
88
+ "Edit override": "Sửa override",
89
+ "Delete this override?": "Xóa override này?",
90
+ "Context window": "Context window",
91
+ "Max completion tokens": "Max completion tokens",
92
+ "Owned by": "Nhà cung cấp",
93
+ "Display name": "Tên hiển thị",
94
+ "Description": "Mô tả",
95
+ "Leave empty to not override": "Để trống nếu không override",
96
+ "Total input + output token capacity reported to clients.": "Tổng dung lượng token (input + output) trả về cho client.",
97
+ "Maximum output tokens reported to clients.": "Số token output tối đa trả về cho client.",
98
+ "AI API": "AI API",
99
+ "Allow this role to use the AI API": "Cho phép vai trò này sử dụng AI API",
100
+ "Allow all AI Employees": "Cho phép tất cả AI Employee",
101
+ "Select which AI Employees this role may use:": "Chọn AI Employee mà vai trò này được dùng:",
102
+ "Select allowed AI Employees": "Chọn AI Employee được phép",
103
+ "Max request body size (MB)": "Giới hạn kích thước request body (MB)",
104
+ "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "Tăng giá trị này để nhận ảnh base64 gửi trực tiếp. Base64 làm tăng khoảng 33% so với kích thước tệp gốc."
86
105
  }
@@ -82,5 +82,24 @@
82
82
  "Reset": "重置",
83
83
  "Requests": "请求数",
84
84
  "Total cost": "总费用",
85
- "Usage records": "用量记录"
85
+ "Usage records": "用量记录",
86
+ "Model metadata": "模型元数据",
87
+ "Add override": "添加覆盖",
88
+ "Edit override": "编辑覆盖",
89
+ "Delete this override?": "确认删除此覆盖?",
90
+ "Context window": "上下文窗口",
91
+ "Max completion tokens": "最大输出 token 数",
92
+ "Owned by": "所属方",
93
+ "Display name": "显示名称",
94
+ "Description": "描述",
95
+ "Leave empty to not override": "留空则不覆盖",
96
+ "Total input + output token capacity reported to clients.": "返回给客户端的输入+输出 token 总容量。",
97
+ "Maximum output tokens reported to clients.": "返回给客户端的最大输出 token 数。",
98
+ "AI API": "AI API",
99
+ "Allow this role to use the AI API": "允许此角色使用 AI API",
100
+ "Allow all AI Employees": "允许所有 AI 员工",
101
+ "Select which AI Employees this role may use:": "选择此角色可使用的 AI 员工:",
102
+ "Select allowed AI Employees": "选择允许的 AI 员工",
103
+ "Max request body size (MB)": "请求体大小上限(MB)",
104
+ "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "调高此值以接收内联 base64 图片。base64 编码会使体积增加约 33%。"
86
105
  }
@@ -62,6 +62,12 @@ var ai_api_config_default = (0, import_database.defineCollection)({
62
62
  defaultValue: 60,
63
63
  comment: "Max requests per user per minute"
64
64
  },
65
+ {
66
+ name: "maxRequestBodyMb",
67
+ type: "integer",
68
+ defaultValue: 10,
69
+ comment: "Max request body size in MB. Raise this to accept inline base64 images in vision requests."
70
+ },
65
71
  {
66
72
  name: "quotaEnabled",
67
73
  type: "boolean",
@@ -0,0 +1,83 @@
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_model_metadata_exports = {};
28
+ __export(ai_api_model_metadata_exports, {
29
+ default: () => ai_api_model_metadata_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_model_metadata_exports);
32
+ var import_database = require("@nocobase/database");
33
+ var ai_api_model_metadata_default = (0, import_database.defineCollection)({
34
+ name: "aiApiModelMetadata",
35
+ autoGenId: true,
36
+ fields: [
37
+ { name: "llmService", type: "string", allowNull: false, index: true },
38
+ { name: "model", type: "string", allowNull: false, index: true },
39
+ {
40
+ name: "contextWindow",
41
+ type: "integer",
42
+ allowNull: true,
43
+ comment: "Override for the model context window (input+output token capacity). Returned as context_window / context_length."
44
+ },
45
+ {
46
+ name: "maxCompletionTokens",
47
+ type: "integer",
48
+ allowNull: true,
49
+ comment: "Override for the maximum output tokens. Returned as max_completion_tokens."
50
+ },
51
+ {
52
+ name: "ownedByOverride",
53
+ type: "string",
54
+ allowNull: true,
55
+ comment: "Override for the owned_by field in the OpenAI model object."
56
+ },
57
+ {
58
+ name: "displayName",
59
+ type: "string",
60
+ allowNull: true,
61
+ comment: "Friendly display name returned as display_name / name in the model object."
62
+ },
63
+ {
64
+ name: "description",
65
+ type: "text",
66
+ allowNull: true,
67
+ comment: "Human-readable description returned as description in the model object."
68
+ },
69
+ {
70
+ name: "enabled",
71
+ type: "boolean",
72
+ defaultValue: true,
73
+ index: true,
74
+ comment: "When false, the model is hidden from /v1/models and reported as active:false."
75
+ }
76
+ ],
77
+ indexes: [
78
+ {
79
+ fields: ["llmService", "model"],
80
+ unique: true
81
+ }
82
+ ]
83
+ });
@@ -47,6 +47,7 @@ var import_ai_api_usage_monitor = __toESM(require("./resource/ai-api-usage-monit
47
47
  var import_rate_limiter = require("./utils/rate-limiter");
48
48
  var import_role_permission = require("./middleware/role-permission");
49
49
  var import_validation = require("./validation");
50
+ var import_constants = require("../constants");
50
51
  var import_dayjs = __toESM(require("dayjs"));
51
52
  var import_utc = __toESM(require("dayjs/plugin/utc"));
52
53
  var import_timezone = __toESM(require("dayjs/plugin/timezone"));
@@ -65,11 +66,21 @@ class PluginAiApiServer extends import_server.Plugin {
65
66
  this.app.db.on("aiApiModelPrices.beforeSave", async (model) => {
66
67
  await (0, import_validation.validateModelPrice)(this.db, model);
67
68
  });
69
+ this.app.db.on("aiApiModelMetadata.beforeSave", (model) => {
70
+ (0, import_validation.validateModelMetadata)(model);
71
+ });
68
72
  this.app.db.on("aiApiUserQuotaPolicies.beforeSave", (model) => {
69
73
  (0, import_validation.validateQuotaPolicy)(model);
70
74
  });
71
75
  }
72
76
  async load() {
77
+ this.app.use(
78
+ async (ctx, next) => {
79
+ if (ctx.path.startsWith(import_router.AI_LLM_PREFIX)) ctx.disableBodyParser = true;
80
+ await next();
81
+ },
82
+ { tag: "aiApiDisableBodyParser", before: "bodyParser" }
83
+ );
73
84
  this.app.use((0, import_router.createAiLlmRouter)(this), { after: "idp-oauth-resource-auth", before: "resourcer" });
74
85
  this.app.resourceManager.define(import_ai_api_config.default);
75
86
  this.app.resourceManager.define(import_ai_api_usage_monitor.default);
@@ -80,11 +91,12 @@ class PluginAiApiServer extends import_server.Plugin {
80
91
  (0, import_role_permission.invalidateRolePermissionCache)(model.get("roleName"));
81
92
  });
82
93
  this.app.acl.registerSnippet({
83
- name: `pm.${this.name}.configuration`,
94
+ name: import_constants.AI_API_ACL_SNIPPET,
84
95
  actions: [
85
96
  "aiApiConfig:*",
86
97
  "aiApiRolePermissions:*",
87
98
  "aiApiModelPrices:*",
99
+ "aiApiModelMetadata:*",
88
100
  "aiApiUserQuotaPolicies:*",
89
101
  "aiApiUserQuotaBuckets:list",
90
102
  "aiApiUserQuotaBuckets:get",
@@ -29,6 +29,18 @@ __export(ai_api_config_exports, {
29
29
  default: () => ai_api_config_default
30
30
  });
31
31
  module.exports = __toCommonJS(ai_api_config_exports);
32
+ var import_router = require("../routes/router");
33
+ const DEFAULT_MAX_REQUEST_BODY_MB = 10;
34
+ function coerceMaxRequestBodyMb(value) {
35
+ const mb = Number(value);
36
+ if (!Number.isSafeInteger(mb) || mb <= 0) {
37
+ throw new Error(`maxRequestBodyMb must be a positive integer (1-${import_router.MAX_REQUEST_BODY_MB_LIMIT}).`);
38
+ }
39
+ if (mb > import_router.MAX_REQUEST_BODY_MB_LIMIT) {
40
+ throw new Error(`maxRequestBodyMb cannot exceed ${import_router.MAX_REQUEST_BODY_MB_LIMIT}.`);
41
+ }
42
+ return mb;
43
+ }
32
44
  const aiApiConfigResource = {
33
45
  name: "aiApiConfig",
34
46
  actions: {
@@ -42,6 +54,7 @@ const aiApiConfigResource = {
42
54
  defaultLlmService: "",
43
55
  enabledLlmServices: [],
44
56
  rateLimitPerMinute: 60,
57
+ maxRequestBodyMb: 10,
45
58
  quotaEnabled: false,
46
59
  defaultReservationOutputTokens: 4096,
47
60
  options: {}
@@ -63,6 +76,7 @@ const aiApiConfigResource = {
63
76
  defaultLlmService: values.defaultLlmService ?? "",
64
77
  enabledLlmServices: values.enabledLlmServices ?? [],
65
78
  rateLimitPerMinute: values.rateLimitPerMinute ?? 60,
79
+ maxRequestBodyMb: coerceMaxRequestBodyMb(values.maxRequestBodyMb ?? DEFAULT_MAX_REQUEST_BODY_MB),
66
80
  quotaEnabled: values.quotaEnabled ?? false,
67
81
  defaultReservationOutputTokens: values.defaultReservationOutputTokens ?? 4096,
68
82
  options: values.options ?? {}
@@ -75,6 +89,9 @@ const aiApiConfigResource = {
75
89
  if (values.defaultLlmService !== void 0) updateData.defaultLlmService = values.defaultLlmService;
76
90
  if (values.enabledLlmServices !== void 0) updateData.enabledLlmServices = values.enabledLlmServices;
77
91
  if (values.rateLimitPerMinute !== void 0) updateData.rateLimitPerMinute = values.rateLimitPerMinute;
92
+ if (values.maxRequestBodyMb !== void 0) {
93
+ updateData.maxRequestBodyMb = coerceMaxRequestBodyMb(values.maxRequestBodyMb);
94
+ }
78
95
  if (values.quotaEnabled !== void 0) updateData.quotaEnabled = values.quotaEnabled;
79
96
  if (values.defaultReservationOutputTokens !== void 0) {
80
97
  updateData.defaultReservationOutputTokens = values.defaultReservationOutputTokens;
@@ -35,6 +35,7 @@ var import_role_permission = require("../middleware/role-permission");
35
35
  var import_streaming = require("../utils/streaming");
36
36
  var import_ai_employee_runtime = require("../utils/ai-employee-runtime");
37
37
  var import_usage = require("../usage");
38
+ var import_app_observability = require("../utils/app-observability");
38
39
  async function handleAgentCompletions(ctx, plugin) {
39
40
  var _a;
40
41
  const body = ctx.request.body;
@@ -201,74 +202,83 @@ async function handleAgentCompletions(ctx, plugin) {
201
202
  ctx.res.write(args[0]);
202
203
  }
203
204
  };
204
- ctx.res.write = (data) => {
205
+ const processSseFrame = (frame) => {
205
206
  var _a2;
206
- pendingSse += typeof data === "string" ? data : data.toString("utf8");
207
- const frames = pendingSse.split("\n\n");
208
- pendingSse = frames.pop() || "";
209
- for (const frame of frames) {
210
- for (const line of frame.split("\n")) {
211
- const trimmed = line.trim();
212
- if (!trimmed.startsWith("data: ")) continue;
213
- const jsonStr = trimmed.substring(6);
214
- if (!jsonStr) continue;
215
- try {
216
- const event = JSON.parse(jsonStr);
217
- if (event.type === "content" && event.body) {
207
+ for (const line of frame.split("\n")) {
208
+ const trimmed = line.trim();
209
+ if (!trimmed.startsWith("data: ")) continue;
210
+ const jsonStr = trimmed.substring(6);
211
+ if (!jsonStr) continue;
212
+ try {
213
+ const event = JSON.parse(jsonStr);
214
+ if (event.type === "content" && event.body) {
215
+ (0, import_app_observability.markAiApiFirstProviderOutput)(ctx);
216
+ originalWrite(
217
+ (0, import_openai_format.formatSSE)(
218
+ (0, import_openai_format.toOpenAIStreamChunk)({
219
+ id: completionId,
220
+ model: body.model,
221
+ delta: { content: String(event.body) }
222
+ })
223
+ )
224
+ );
225
+ } else if (event.type === "tool_call_chunks" && Array.isArray(event.body)) {
226
+ const chunks = toOpenAIToolCallChunks(event.body);
227
+ if (chunks.length) {
228
+ (0, import_app_observability.markAiApiFirstProviderOutput)(ctx);
229
+ sawToolCalls = true;
218
230
  originalWrite(
219
231
  (0, import_openai_format.formatSSE)(
220
232
  (0, import_openai_format.toOpenAIStreamChunk)({
221
233
  id: completionId,
222
234
  model: body.model,
223
- delta: { content: String(event.body) }
235
+ delta: { tool_calls: chunks }
224
236
  })
225
237
  )
226
238
  );
227
- } else if (event.type === "tool_call_chunks" && Array.isArray(event.body)) {
228
- const chunks = toOpenAIToolCallChunks(event.body);
229
- if (chunks.length) {
230
- sawToolCalls = true;
231
- originalWrite(
232
- (0, import_openai_format.formatSSE)(
233
- (0, import_openai_format.toOpenAIStreamChunk)({
234
- id: completionId,
235
- model: body.model,
236
- delta: { tool_calls: chunks }
237
- })
238
- )
239
- );
240
- }
241
- } else if (!sawToolCalls && event.type === "tool_calls" && Array.isArray((_a2 = event.body) == null ? void 0 : _a2.toolCalls)) {
242
- const chunks = toOpenAIToolCallChunks(event.body.toolCalls);
243
- if (chunks.length) {
244
- sawToolCalls = true;
245
- originalWrite(
246
- (0, import_openai_format.formatSSE)(
247
- (0, import_openai_format.toOpenAIStreamChunk)({
248
- id: completionId,
249
- model: body.model,
250
- delta: { tool_calls: chunks }
251
- })
252
- )
253
- );
254
- }
255
- } else if (event.type === "error" && event.body) {
239
+ }
240
+ } else if (!sawToolCalls && event.type === "tool_calls" && Array.isArray((_a2 = event.body) == null ? void 0 : _a2.toolCalls)) {
241
+ const chunks = toOpenAIToolCallChunks(event.body.toolCalls);
242
+ if (chunks.length) {
243
+ (0, import_app_observability.markAiApiFirstProviderOutput)(ctx);
244
+ sawToolCalls = true;
256
245
  originalWrite(
257
- (0, import_openai_format.formatSSE)({
258
- error: {
259
- message: String(event.body),
260
- type: "server_error",
261
- code: "agent_error"
262
- }
263
- })
246
+ (0, import_openai_format.formatSSE)(
247
+ (0, import_openai_format.toOpenAIStreamChunk)({
248
+ id: completionId,
249
+ model: body.model,
250
+ delta: { tool_calls: chunks }
251
+ })
252
+ )
264
253
  );
265
254
  }
266
- } catch {
255
+ } else if (event.type === "error" && event.body) {
256
+ originalWrite(
257
+ (0, import_openai_format.formatSSE)({
258
+ error: {
259
+ message: String(event.body),
260
+ type: "server_error",
261
+ code: "agent_error"
262
+ }
263
+ })
264
+ );
267
265
  }
266
+ } catch {
268
267
  }
269
268
  }
269
+ };
270
+ ctx.res.write = (data) => {
271
+ pendingSse += typeof data === "string" ? data : data.toString("utf8");
272
+ const frames = pendingSse.split("\n\n");
273
+ pendingSse = frames.pop() || "";
274
+ for (const frame of frames) processSseFrame(frame);
270
275
  return true;
271
276
  };
277
+ const flushPendingSse = () => {
278
+ if (!pendingSse.trim()) return;
279
+ processSseFrame(pendingSse);
280
+ pendingSse = "";
281
+ };
272
282
  try {
273
283
  const aiEmployee = new AIEmployee(
274
284
  (0, import_ai_employee_runtime.createAIEmployeeOptions)(ctx, employeeRecord, sessionId, {
@@ -291,6 +301,7 @@ async function handleAgentCompletions(ctx, plugin) {
291
301
  ctx.req.off("aborted", abortAgent);
292
302
  ctx.res.off("close", abortAgent);
293
303
  if (streamSucceeded && !ctx.res.destroyed) {
304
+ flushPendingSse();
294
305
  originalWrite(
295
306
  (0, import_openai_format.formatSSE)(
296
307
  (0, import_openai_format.toOpenAIStreamChunk)({