plugin-ai-api 1.0.21 → 1.0.23

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 (46) hide show
  1. package/dist/client/123.e6fe04c856ce6417.js +10 -0
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/123.05f1f649923f93eb.js +10 -0
  4. package/dist/client-v2/index.js +1 -1
  5. package/dist/constants.js +5 -2
  6. package/dist/locale/en-US.json +12 -1
  7. package/dist/locale/vi-VN.json +12 -1
  8. package/dist/locale/zh-CN.json +12 -1
  9. package/dist/server/collections/ai-api-user-permissions.js +67 -0
  10. package/dist/server/plugin.js +32 -0
  11. package/dist/server/resource/ai-api-user-permissions.js +75 -0
  12. package/dist/server/routes/agent-completions.js +5 -0
  13. package/dist/server/routes/chat-completions.js +29 -16
  14. package/dist/server/routes/completions.js +33 -20
  15. package/dist/server/routes/embeddings.js +6 -14
  16. package/dist/server/routes/models.js +24 -0
  17. package/dist/server/utils/openai-format.js +17 -3
  18. package/dist/server/utils/user-permissions.js +160 -0
  19. package/dist/swagger.js +4 -3
  20. package/package.json +2 -2
  21. package/src/client/__tests__/settings-registration.test.tsx +69 -0
  22. package/src/client/plugin.tsx +14 -3
  23. package/src/client-v2/__tests__/settings-registration.test.tsx +33 -4
  24. package/src/client-v2/pages/UserPermissionsPage.tsx +322 -0
  25. package/src/client-v2/plugin.tsx +12 -3
  26. package/src/constants.ts +7 -0
  27. package/src/locale/en-US.json +12 -1
  28. package/src/locale/vi-VN.json +12 -1
  29. package/src/locale/zh-CN.json +12 -1
  30. package/src/server/__tests__/models.test.ts +44 -2
  31. package/src/server/__tests__/openai-format.test.ts +52 -1
  32. package/src/server/__tests__/permission-sync.test.ts +109 -0
  33. package/src/server/__tests__/usage-route.test.ts +213 -0
  34. package/src/server/__tests__/user-permissions-resource.test.ts +66 -0
  35. package/src/server/__tests__/user-permissions.test.ts +284 -0
  36. package/src/server/collections/ai-api-user-permissions.ts +46 -0
  37. package/src/server/plugin.ts +42 -1
  38. package/src/server/resource/ai-api-user-permissions.ts +76 -0
  39. package/src/server/routes/agent-completions.ts +7 -0
  40. package/src/server/routes/chat-completions.ts +32 -16
  41. package/src/server/routes/completions.ts +40 -18
  42. package/src/server/routes/embeddings.ts +10 -15
  43. package/src/server/routes/models.ts +28 -0
  44. package/src/server/utils/openai-format.ts +26 -0
  45. package/src/server/utils/user-permissions.ts +218 -0
  46. package/src/swagger.ts +9 -3
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ "use strict";(self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).push([["123"],{46:function(e,t,r){r.r(t),r.d(t,{default:function(){return p}});var n=r(155),l=r.n(n),a=r(59),o=r(694),i=r(650),u=r(630);function s(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 c(e,t,r,n,l,a,o){try{var i=e[a](o),u=i.value}catch(e){r(e);return}i.done?t(u):Promise.resolve(u).then(n,l)}function d(e){return function(){var t=this,r=arguments;return new Promise(function(n,l){var a=e.apply(t,r);function o(e){c(a,n,l,o,i,"next",e)}function i(e){c(a,n,l,o,i,"throw",e)}o(void 0)})}}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,l=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=l){var a=[],o=!0,i=!1;try{for(l=l.call(e);!(o=(r=l.next()).done)&&(a.push(r.value),!t||a.length!==t);o=!0);}catch(e){i=!0,n=e}finally{try{o||null==l.return||l.return()}finally{if(i)throw n}}return a}}(e,t)||function(e,t){if(e){if("string"==typeof e)return s(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 s(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 f(e,t){var r,n,l,a={label:0,sent:function(){if(1&l[0])throw l[1];return l[1]},trys:[],ops:[]},o=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),i=Object.defineProperty;return i(o,"next",{value:u(0)}),i(o,"throw",{value:u(1)}),i(o,"return",{value:u(2)}),"function"==typeof Symbol&&i(o,Symbol.iterator,{value:function(){return this}}),o;function u(i){return function(u){var s=[i,u];if(r)throw TypeError("Generator is already executing.");for(;o&&(o=0,s[0]&&(a=0)),a;)try{if(r=1,n&&(l=2&s[0]?n.return:s[0]?n.throw||((l=n.return)&&l.call(n),0):n.next)&&!(l=l.call(n,s[1])).done)return l;switch(n=0,l&&(s=[2&s[0],l.value]),s[0]){case 0:case 1:l=s;break;case 4:return a.label++,{value:s[1],done:!1};case 5:a.label++,n=s[1],s=[0];continue;case 7:s=a.ops.pop(),a.trys.pop();continue;default:if(!(l=(l=a.trys).length>0&&l[l.length-1])&&(6===s[0]||2===s[0])){a=0;continue}if(3===s[0]&&(!l||s[1]>l[0]&&s[1]<l[3])){a.label=s[1];break}if(6===s[0]&&a.label<l[1]){a.label=l[1],l=s;break}if(l&&a.label<l[2]){a.label=l[2],a.ops.push(s);break}l[2]&&a.ops.pop(),a.trys.pop();continue}s=t.call(e,a)}catch(e){s=[6,e],n=0}finally{r=l=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}}}function p(){var e=(0,o.useFlowContext)(),t=(0,i.k)(),r=m(a.Form.useForm(),1)[0],s=a.Form.useWatch("allowedLlmServices",r),c=a.Form.useWatch("allowAllModels",r),p=m((0,n.useState)([]),2),v=p[0],h=p[1],b=m((0,n.useState)(0),2),y=b[0],w=b[1],S=m((0,n.useState)(1),2),g=S[0],E=S[1],k=m((0,n.useState)([]),2),A=k[0],M=k[1],I=m((0,n.useState)(""),2),F=I[0],C=I[1],U=m((0,n.useState)([]),2),x=U[0],L=U[1],P=m((0,n.useState)(!1),2),T=P[0],O=P[1],j=m((0,n.useState)(!1),2),z=j[0],q=j[1],B=m((0,n.useState)(),2),D=B[0],N=B[1],_=m((0,n.useState)(!1),2),G=_[0],V=_[1],W=(0,n.useCallback)(function(){return d(function(){var t,r,n,l,o,i,s;return f(this,function(c){switch(c.label){case 0:O(!0),c.label=1;case 1:return c.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiUserPermissions:list",method:"get",params:{page:g,pageSize:20,appends:["user"],sort:"-updatedAt"}}),e.api.request({url:"ai:listAllEnabledModels",method:"get"})])];case 2:return o=(l=m.apply(void 0,[c.sent(),2]))[0],i=l[1],h((0,u.m)(o,[])),w(null!=(t=null==o||null==(n=o.data)||null==(r=n.meta)?void 0:r.count)?t:0),L((0,u.m)(i,[])),[3,5];case 3:return s=c.sent(),a.message.error((0,u.g)(s)),[3,5];case 4:return O(!1),[7];case 5:return[2]}})})()},[e.api,g]);(0,n.useEffect)(function(){W()},[W]);var J=(0,n.useCallback)(function(t){return d(function(){var r,n;return f(this,function(l){switch(l.label){case 0:return l.trys.push([0,2,,3]),[4,e.api.request({url:"aiApiUserPermissions:listUsers",method:"get",params:{keyword:t,pageSize:50,excludeGranted:!D}})];case 1:return r=l.sent(),M((0,u.m)(r,[])),[3,3];case 2:return n=l.sent(),a.message.error((0,u.g)(n)),[3,3];case 3:return[2]}})})()},[e.api,D]);(0,n.useEffect)(function(){if(G){var e=setTimeout(function(){return J(F)},300);return function(){return clearTimeout(e)}}},[G,F,J]);var K=(0,n.useMemo)(function(){return x.map(function(e){return{label:e.llmServiceTitle||e.llmService,value:e.llmService}})},[x]),$=(0,n.useMemo)(function(){var e=new Set(s||[]);return x.filter(function(t){return e.has(t.llmService)}).flatMap(function(e){return(e.enabledModels||[]).map(function(t){return{label:"".concat(e.llmServiceTitle||e.llmService," / ").concat(t.label||t.value),value:"".concat(e.llmService,"/").concat(t.value)}})})},[x,s]),H=function(e){N(e),C(""),M(e.user?[e.user]:[]),r.setFieldsValue({userId:e.userId,enabled:e.enabled,allowAllModels:e.allowAllModels,allowedLlmServices:e.allowedLlmServices||[],allowedModels:e.allowedModels||[]}),V(!0)},Q=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:"")},R=function(e){var t;return(null==(t=K.find(function(t){return t.value===e}))?void 0:t.label)||e},X=[{title:t("User"),key:"user",width:180,render:function(e,t){return Q(t.user)||String(t.userId)}},{title:t("Allowed LLM services"),dataIndex:"allowedLlmServices",key:"allowedLlmServices",render:function(e){return(null==e?void 0:e.length)?l().createElement(a.Space,{size:[0,4],wrap:!0},e.map(function(e){return l().createElement(a.Tag,{key:e},R(e))})):l().createElement(a.Tag,{color:"red"},t("No service allowed"))}},{title:t("Allowed models"),key:"allowedModels",width:220,render:function(e,r){return r.allowAllModels?l().createElement(a.Tag,{color:"blue"},t("All models of allowed services")):l().createElement(a.Space,{size:[0,4],wrap:!0},(r.allowedModels||[]).map(function(e){return l().createElement(a.Tag,{key:e},e)}))}},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return l().createElement(a.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",width:150,fixed:"right",render:function(r,n){return l().createElement(a.Space,{size:0},l().createElement(a.Button,{type:"link",onClick:function(){return H(n)}},t("Edit")),l().createElement(a.Popconfirm,{title:t("Delete this permission?"),onConfirm:function(){return d(function(){var r;return f(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiUserPermissions:destroy/".concat(n.id),method:"post"})];case 1:return l.sent(),a.message.success(t("Deleted successfully")),[4,W()];case 2:return l.sent(),[3,4];case 3:return r=l.sent(),a.message.error((0,u.g)(r)),[3,4];case 4:return[2]}})})()}},l().createElement(a.Button,{type:"link",danger:!0},t("Delete"))))}}];return l().createElement(a.Card,{title:t("User LLM permissions"),extra:l().createElement(a.Button,{type:"primary",onClick:function(){N(void 0),C(""),r.setFieldsValue({enabled:!0,allowedLlmServices:[],allowAllModels:!0,allowedModels:[]}),V(!0)}},t("Add permission"))},l().createElement(a.Alert,{type:"info",showIcon:!0,style:{marginBottom:16},message:t("Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.")}),l().createElement(a.Table,{rowKey:"id",columns:X,dataSource:v,loading:T,scroll:{x:1e3},pagination:{current:g,pageSize:20,total:y,showSizeChanger:!1,onChange:E}}),l().createElement(a.Modal,{title:D?t("Edit permission"):t("Add permission"),open:G,onCancel:function(){return V(!1)},onOk:function(){return d(function(){var n,l;return f(this,function(o){switch(o.label){case 0:return[4,r.validateFields()];case 1:n=o.sent(),q(!0),o.label=2;case 2:return o.trys.push([2,5,6,7]),[4,e.api.request({url:D?"aiApiUserPermissions:update/".concat(D.id):"aiApiUserPermissions:create",method:"post",data:n})];case 3:return o.sent(),a.message.success(t("Saved successfully")),V(!1),[4,W()];case 4:return o.sent(),[3,7];case 5:return l=o.sent(),a.message.error((0,u.g)(l)),[3,7];case 6:return q(!1),[7];case 7:return[2]}})})()},confirmLoading:z,destroyOnClose:!0},l().createElement(a.Form,{form:r,layout:"vertical",preserve:!1},l().createElement(a.Form.Item,{name:"userId",label:t("User"),rules:[{required:!0}]},l().createElement(a.Select,{disabled:!!D,showSearch:!0,filterOption:!1,onSearch:C,notFoundContent:null,options:A.map(function(e){return{label:Q(e),value:e.id}})})),l().createElement(a.Form.Item,{name:"allowedLlmServices",label:t("Allowed LLM services"),extra:t("Only services also enabled in the general configuration take effect.")},l().createElement(a.Select,{mode:"multiple",allowClear:!0,showSearch:!0,optionFilterProp:"label",options:K})),l().createElement(a.Form.Item,{name:"allowAllModels",label:t("Allow all models"),valuePropName:"checked"},l().createElement(a.Switch,null)),!1===c&&l().createElement(a.Form.Item,{name:"allowedModels",label:t("Allowed models")},l().createElement(a.Select,{mode:"multiple",allowClear:!0,showSearch:!0,optionFilterProp:"label",options:$})),l().createElement(a.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},l().createElement(a.Switch,null)))))}},650:function(e,t,r){r.d(t,{k:function(){return a}});var n=r(694),l=JSON.parse('{"UU":"plugin-ai-api"}');function a(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[l.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 l(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 l},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/client"),require("@nocobase/plugin-acl/client"),require("@nocobase/flow-engine")):"function"==typeof define&&define.amd?define("plugin-ai-api",["@nocobase/client-v2","dayjs","react","antd","@nocobase/client","@nocobase/plugin-acl/client","@nocobase/flow-engine"],t):"object"==typeof exports?exports["plugin-ai-api"]=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/client"),require("@nocobase/plugin-acl/client"),require("@nocobase/flow-engine")):e["plugin-ai-api"]=t(e["@nocobase/client-v2"],e.dayjs,e.react,e.antd,e["@nocobase/client"],e["@nocobase/plugin-acl/client"],e["@nocobase/flow-engine"])}(self,function(e,t,n,r,i,o,a){return function(){"use strict";var u,c,l,p={342:function(e){e.exports=i},485:function(t){t.exports=e},694:function(e){e.exports=a},79:function(e){e.exports=o},59:function(e){e.exports=r},185:function(e){e.exports=t},155:function(e){e.exports=n}},s={};function f(e){var t=s[e];if(void 0!==t)return t.exports;var n=s[e]={exports:{}};return p[e](n,n.exports,f),n.exports}f.m=p,f.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(t,{a:t}),t},f.d=function(e,t){for(var n in t)f.o(t,n)&&!f.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},f.f={},f.e=function(e){return Promise.all(Object.keys(f.f).reduce(function(t,n){return f.f[n](e,t),t},[]))},f.u=function(e){return""+e+"."+({286:"01c0e3c5fff3cccb",302:"fc3a3491b4ec2dfd",562:"17a0a299d2e5152c",757:"a01403fb7a1bea01",902:"92e1daaf1ab16ebf",97:"72979a11a067a7c9"})[e]+".js"},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},d={},f.l=function(e,t,n,r){if(d[e])return void d[e].push(t);if(void 0!==n)for(var i,o,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:"+n){i=c;break}}i||(o=!0,(i=document.createElement("script")).timeout=120,f.nc&&i.setAttribute("nonce",f.nc),i.setAttribute("data-rspack","plugin-ai-api:"+n),i.src=e),d[e]=[t];var l=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var r=d[e];if(delete d[e],i.parentNode&&i.parentNode.removeChild(i),r&&r.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(l.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=l.bind(null,i.onerror),i.onload=l.bind(null,i.onload),o&&document.head.appendChild(i)},f.r=function(e){"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.g.importScripts&&(b=f.g.location+"");var d,b,g=f.g.document;if(!b&&g&&(g.currentScript&&"SCRIPT"===g.currentScript.tagName.toUpperCase()&&(b=g.currentScript.src),!b)){var h=g.getElementsByTagName("script");if(h.length)for(var y=h.length-1;y>-1&&(!b||!/^http(s?):/.test(b));)b=h[y--].src}if(!b)throw Error("Automatic publicPath is not supported in this browser");f.p=b.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),u={889:0},f.f.j=function(e,t){var n=f.o(u,e)?u[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=u[e]=[t,r]});t.push(n[2]=r);var i=f.p+f.u(e),o=Error();f.l(i,function(t){if(f.o(u,e)&&(0!==(n=u[e])&&(u[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;o.message="Loading chunk "+e+" failed.\n("+r+": "+i+")",o.name="ChunkLoadError",o.type=r,o.request=i,n[1](o)}},"chunk-"+e,e)}},c=function(e,t){var n,r,i=t[0],o=t[1],a=t[2],c=0;if(i.some(function(e){return 0!==u[e]})){for(n in o)f.o(o,n)&&(f.m[n]=o[n]);a&&a(f)}for(e&&e(t);c<i.length;c++)r=i[c],f.o(u,r)&&u[r]&&u[r][0](),u[r]=0},(l=self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).forEach(c.bind(null,0)),l.push=c.bind(null,l.push.bind(l));var v={};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/")>=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/")}if(!e){var i=window.__nocobase_modern_client_prefix__||"v",o="/"+(i=String(i).replace(/^\/+|\/+$/g,"")||"v")+"/";if(!(e=window.__nocobase_public_path__||"")&&window.location&&window.location.pathname){var a=window.location.pathname||"/",u=a.indexOf(o);e=u>=0?a.slice(0,u+1):"/"}e&&(e=e.replace(RegExp("/"+i+"/?$"),"/")),e||(e="/"),"/"!==e.charAt(e.length-1)&&(e+="/"),e+="static/plugins/plugin-ai-api/dist/client/"}f.p=e}(),!function(){f.r(v),f.d(v,{default:function(){return w}});var e=f(342),t=f(79),n=f.n(t),r="pm.plugin-ai-api.configuration",i=f(155),o=f.n(i);function a(e,t,n,r,i,o,a){try{var u=e[o](a),c=u.value}catch(e){n(e);return}u.done?t(c):Promise.resolve(c).then(r,i)}function u(e,t,n){return(u=s()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&l(i,n.prototype),i}).apply(null,arguments)}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function p(e){var t="function"==typeof Map?new Map:void 0;return(p=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 u(e,arguments,c(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),l(n,e)})(e)}function s(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(s=function(){return!!e})()}var d=o().lazy(function(){return f.e("302").then(f.bind(f,581))}),b=o().lazy(function(){return f.e("97").then(f.bind(f,760))}),g=o().lazy(function(){return f.e("562").then(f.bind(f,641))}),h=o().lazy(function(){return f.e("902").then(f.bind(f,77))}),y=o().lazy(function(){return f.e("757").then(f.bind(f,364))}),m=(0,e.lazy)(function(){return f.e("286").then(f.bind(f,421))},"AiApiRolePermissions").AiApiRolePermissions,w=function(e){var t;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=c(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,s()?Reflect.construct(e,t||[],c(this).constructor):e.apply(this,t))}return i.prototype=Object.create(e&&e.prototype,{constructor:{value:i,writable:!0,configurable:!0}}),e&&l(i,e),t=[{key:"load",value:function(){var e;return(e=function(){var e;return function(e,t){var n,r,i,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[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]&&(o=0)),o;)try{if(n=1,r&&(i=2&l[0]?r.return:l[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,l[1])).done)return i;switch(r=0,i&&(l=[2&l[0],i.value]),l[0]){case 0:case 1:i=l;break;case 4:return o.label++,{value:l[1],done:!1};case 5:o.label++,r=l[1],l=[0];continue;case 7:l=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===l[0]||2===l[0])){o=0;continue}if(3===l[0]&&(!i||l[1]>i[0]&&l[1]<i[3])){o.label=l[1];break}if(6===l[0]&&o.label<i[1]){o.label=i[1],i=l;break}if(i&&o.label<i[2]){o.label=i[2],o.ops.push(l);break}i[2]&&o.ops.pop(),o.trys.pop();continue}l=t.call(e,o)}catch(e){l=[6,e],r=0}finally{n=i=0}if(5&l[0])throw l[1];return{value:l[0]?l[1]:void 0,done:!0}}}}(this,function(t){return this.app.pluginSettingsManager.add("ai-api",{icon:"ApiOutlined",title:this.t("AI API Gateway"),aclSnippet:r}),this.app.pluginSettingsManager.add("ai-api.config",{title:this.t("Configuration"),Component:d,aclSnippet:r,sort:1}),this.app.pluginSettingsManager.add("ai-api.model-pricing",{title:this.t("Model pricing"),Component:b,aclSnippet:r,sort:2}),this.app.pluginSettingsManager.add("ai-api.model-metadata",{title:this.t("Model metadata"),Component:g,aclSnippet:r,sort:3}),this.app.pluginSettingsManager.add("ai-api.user-quotas",{title:this.t("User quotas"),Component:h,aclSnippet:r,sort:4}),this.app.pluginSettingsManager.add("ai-api.usage",{title:this.t("Usage"),Component:y,aclSnippet:r,sort:5}),(null==(e=this.app.pm.get(n()))?void 0:e.settingsUI)&&e.settingsUI.addPermissionsTab(function(e){var t=e.t,n=e.TabLayout,r=e.activeRole;return{key:"aiApi",label:t("AI API",{ns:["plugin-ai-api","client"]}),sort:25,children:o().createElement(n,null,o().createElement(m,{role:r}))}}),[2]})},function(){var t=this,n=arguments;return new Promise(function(r,i){var o=e.apply(t,n);function u(e){a(o,r,i,u,c,"next",e)}function c(e){a(o,r,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,t),i}(p(e.Plugin))}(),v}()});
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/client"),require("@nocobase/plugin-acl/client"),require("@nocobase/flow-engine")):"function"==typeof define&&define.amd?define("plugin-ai-api",["@nocobase/client-v2","dayjs","react","antd","@nocobase/client","@nocobase/plugin-acl/client","@nocobase/flow-engine"],t):"object"==typeof exports?exports["plugin-ai-api"]=t(require("@nocobase/client-v2"),require("dayjs"),require("react"),require("antd"),require("@nocobase/client"),require("@nocobase/plugin-acl/client"),require("@nocobase/flow-engine")):e["plugin-ai-api"]=t(e["@nocobase/client-v2"],e.dayjs,e.react,e.antd,e["@nocobase/client"],e["@nocobase/plugin-acl/client"],e["@nocobase/flow-engine"])}(self,function(e,t,n,r,i,o,a){return function(){"use strict";var u,c,l,p={342:function(e){e.exports=i},485:function(t){t.exports=e},694:function(e){e.exports=a},79:function(e){e.exports=o},59:function(e){e.exports=r},185:function(e){e.exports=t},155:function(e){e.exports=n}},s={};function f(e){var t=s[e];if(void 0!==t)return t.exports;var n=s[e]={exports:{}};return p[e](n,n.exports,f),n.exports}f.m=p,f.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(t,{a:t}),t},f.d=function(e,t){for(var n in t)f.o(t,n)&&!f.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},f.f={},f.e=function(e){return Promise.all(Object.keys(f.f).reduce(function(t,n){return f.f[n](e,t),t},[]))},f.u=function(e){return""+e+"."+({123:"e6fe04c856ce6417",286:"01c0e3c5fff3cccb",302:"fc3a3491b4ec2dfd",562:"17a0a299d2e5152c",757:"a01403fb7a1bea01",902:"92e1daaf1ab16ebf",97:"72979a11a067a7c9"})[e]+".js"},f.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),f.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},d={},f.l=function(e,t,n,r){if(d[e])return void d[e].push(t);if(void 0!==n)for(var i,o,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:"+n){i=c;break}}i||(o=!0,(i=document.createElement("script")).timeout=120,f.nc&&i.setAttribute("nonce",f.nc),i.setAttribute("data-rspack","plugin-ai-api:"+n),i.src=e),d[e]=[t];var l=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var r=d[e];if(delete d[e],i.parentNode&&i.parentNode.removeChild(i),r&&r.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(l.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=l.bind(null,i.onerror),i.onload=l.bind(null,i.onload),o&&document.head.appendChild(i)},f.r=function(e){"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.g.importScripts&&(b=f.g.location+"");var d,b,g=f.g.document;if(!b&&g&&(g.currentScript&&"SCRIPT"===g.currentScript.tagName.toUpperCase()&&(b=g.currentScript.src),!b)){var h=g.getElementsByTagName("script");if(h.length)for(var y=h.length-1;y>-1&&(!b||!/^http(s?):/.test(b));)b=h[y--].src}if(!b)throw Error("Automatic publicPath is not supported in this browser");f.p=b.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),u={889:0},f.f.j=function(e,t){var n=f.o(u,e)?u[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=u[e]=[t,r]});t.push(n[2]=r);var i=f.p+f.u(e),o=Error();f.l(i,function(t){if(f.o(u,e)&&(0!==(n=u[e])&&(u[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;o.message="Loading chunk "+e+" failed.\n("+r+": "+i+")",o.name="ChunkLoadError",o.type=r,o.request=i,n[1](o)}},"chunk-"+e,e)}},c=function(e,t){var n,r,i=t[0],o=t[1],a=t[2],c=0;if(i.some(function(e){return 0!==u[e]})){for(n in o)f.o(o,n)&&(f.m[n]=o[n]);a&&a(f)}for(e&&e(t);c<i.length;c++)r=i[c],f.o(u,r)&&u[r]&&u[r][0](),u[r]=0},(l=self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).forEach(c.bind(null,0)),l.push=c.bind(null,l.push.bind(l));var v={};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/")>=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/")}if(!e){var i=window.__nocobase_modern_client_prefix__||"v",o="/"+(i=String(i).replace(/^\/+|\/+$/g,"")||"v")+"/";if(!(e=window.__nocobase_public_path__||"")&&window.location&&window.location.pathname){var a=window.location.pathname||"/",u=a.indexOf(o);e=u>=0?a.slice(0,u+1):"/"}e&&(e=e.replace(RegExp("/"+i+"/?$"),"/")),e||(e="/"),"/"!==e.charAt(e.length-1)&&(e+="/"),e+="static/plugins/plugin-ai-api/dist/client/"}f.p=e}(),!function(){f.r(v),f.d(v,{default:function(){return _}});var e=f(342),t=f(79),n=f.n(t),r="pm.plugin-ai-api.configuration",i=f(155),o=f.n(i);function a(e,t,n,r,i,o,a){try{var u=e[o](a),c=u.value}catch(e){n(e);return}u.done?t(c):Promise.resolve(c).then(r,i)}function u(e,t,n){return(u=s()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&l(i,n.prototype),i}).apply(null,arguments)}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function p(e){var t="function"==typeof Map?new Map:void 0;return(p=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 u(e,arguments,c(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),l(n,e)})(e)}function s(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(s=function(){return!!e})()}var d=o().lazy(function(){return f.e("302").then(f.bind(f,581))}),b=o().lazy(function(){return f.e("97").then(f.bind(f,760))}),g=o().lazy(function(){return f.e("562").then(f.bind(f,641))}),h=o().lazy(function(){return f.e("123").then(f.bind(f,46))}),y=o().lazy(function(){return f.e("902").then(f.bind(f,77))}),m=o().lazy(function(){return f.e("757").then(f.bind(f,364))}),w=(0,e.lazy)(function(){return f.e("286").then(f.bind(f,421))},"AiApiRolePermissions").AiApiRolePermissions,_=function(e){var t;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=c(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,s()?Reflect.construct(e,t||[],c(this).constructor):e.apply(this,t))}return i.prototype=Object.create(e&&e.prototype,{constructor:{value:i,writable:!0,configurable:!0}}),e&&l(i,e),t=[{key:"load",value:function(){var e;return(e=function(){var e;return function(e,t){var n,r,i,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[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]&&(o=0)),o;)try{if(n=1,r&&(i=2&l[0]?r.return:l[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,l[1])).done)return i;switch(r=0,i&&(l=[2&l[0],i.value]),l[0]){case 0:case 1:i=l;break;case 4:return o.label++,{value:l[1],done:!1};case 5:o.label++,r=l[1],l=[0];continue;case 7:l=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===l[0]||2===l[0])){o=0;continue}if(3===l[0]&&(!i||l[1]>i[0]&&l[1]<i[3])){o.label=l[1];break}if(6===l[0]&&o.label<i[1]){o.label=i[1],i=l;break}if(i&&o.label<i[2]){o.label=i[2],o.ops.push(l);break}i[2]&&o.ops.pop(),o.trys.pop();continue}l=t.call(e,o)}catch(e){l=[6,e],r=0}finally{n=i=0}if(5&l[0])throw l[1];return{value:l[0]?l[1]:void 0,done:!0}}}}(this,function(t){return this.app.pluginSettingsManager.add("ai-api",{icon:"ApiOutlined",title:this.t("AI API Gateway"),aclSnippet:r}),this.app.pluginSettingsManager.add("ai-api.config",{title:this.t("Configuration"),Component:d,aclSnippet:r,sort:1}),this.app.pluginSettingsManager.add("ai-api.model-pricing",{title:this.t("Model pricing"),Component:b,aclSnippet:r,sort:2}),this.app.pluginSettingsManager.add("ai-api.model-metadata",{title:this.t("Model metadata"),Component:g,aclSnippet:r,sort:3}),this.app.pluginSettingsManager.add("ai-api.user-permissions",{title:this.t("User LLM permissions"),Component:h,aclSnippet:"pm.plugin-ai-api.user-permissions",sort:4}),this.app.pluginSettingsManager.add("ai-api.user-quotas",{title:this.t("User quotas"),Component:y,aclSnippet:r,sort:5}),this.app.pluginSettingsManager.add("ai-api.usage",{title:this.t("Usage"),Component:m,aclSnippet:r,sort:6}),(null==(e=this.app.pm.get(n()))?void 0:e.settingsUI)&&e.settingsUI.addPermissionsTab(function(e){var t=e.t,n=e.TabLayout,r=e.activeRole;return{key:"aiApi",label:t("AI API",{ns:["plugin-ai-api","client"]}),sort:25,children:o().createElement(n,null,o().createElement(w,{role:r}))}}),[2]})},function(){var t=this,n=arguments;return new Promise(function(r,i){var o=e.apply(t,n);function u(e){a(o,r,i,u,c,"next",e)}function c(e){a(o,r,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,t),i}(p(e.Plugin))}(),v}()});
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ "use strict";(self.webpackChunkplugin_ai_api_client_v2=self.webpackChunkplugin_ai_api_client_v2||[]).push([["123"],{46:function(e,t,r){r.r(t),r.d(t,{default:function(){return p}});var n=r(155),l=r.n(n),a=r(59),o=r(694),i=r(650),u=r(630);function s(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 c(e,t,r,n,l,a,o){try{var i=e[a](o),u=i.value}catch(e){r(e);return}i.done?t(u):Promise.resolve(u).then(n,l)}function d(e){return function(){var t=this,r=arguments;return new Promise(function(n,l){var a=e.apply(t,r);function o(e){c(a,n,l,o,i,"next",e)}function i(e){c(a,n,l,o,i,"throw",e)}o(void 0)})}}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,l=null==e?null:"u">typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=l){var a=[],o=!0,i=!1;try{for(l=l.call(e);!(o=(r=l.next()).done)&&(a.push(r.value),!t||a.length!==t);o=!0);}catch(e){i=!0,n=e}finally{try{o||null==l.return||l.return()}finally{if(i)throw n}}return a}}(e,t)||function(e,t){if(e){if("string"==typeof e)return s(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 s(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 f(e,t){var r,n,l,a={label:0,sent:function(){if(1&l[0])throw l[1];return l[1]},trys:[],ops:[]},o=Object.create(("function"==typeof Iterator?Iterator:Object).prototype),i=Object.defineProperty;return i(o,"next",{value:u(0)}),i(o,"throw",{value:u(1)}),i(o,"return",{value:u(2)}),"function"==typeof Symbol&&i(o,Symbol.iterator,{value:function(){return this}}),o;function u(i){return function(u){var s=[i,u];if(r)throw TypeError("Generator is already executing.");for(;o&&(o=0,s[0]&&(a=0)),a;)try{if(r=1,n&&(l=2&s[0]?n.return:s[0]?n.throw||((l=n.return)&&l.call(n),0):n.next)&&!(l=l.call(n,s[1])).done)return l;switch(n=0,l&&(s=[2&s[0],l.value]),s[0]){case 0:case 1:l=s;break;case 4:return a.label++,{value:s[1],done:!1};case 5:a.label++,n=s[1],s=[0];continue;case 7:s=a.ops.pop(),a.trys.pop();continue;default:if(!(l=(l=a.trys).length>0&&l[l.length-1])&&(6===s[0]||2===s[0])){a=0;continue}if(3===s[0]&&(!l||s[1]>l[0]&&s[1]<l[3])){a.label=s[1];break}if(6===s[0]&&a.label<l[1]){a.label=l[1],l=s;break}if(l&&a.label<l[2]){a.label=l[2],a.ops.push(s);break}l[2]&&a.ops.pop(),a.trys.pop();continue}s=t.call(e,a)}catch(e){s=[6,e],n=0}finally{r=l=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}}}function p(){var e=(0,o.useFlowContext)(),t=(0,i.k)(),r=m(a.Form.useForm(),1)[0],s=a.Form.useWatch("allowedLlmServices",r),c=a.Form.useWatch("allowAllModels",r),p=m((0,n.useState)([]),2),v=p[0],h=p[1],b=m((0,n.useState)(0),2),y=b[0],w=b[1],S=m((0,n.useState)(1),2),g=S[0],E=S[1],k=m((0,n.useState)([]),2),A=k[0],M=k[1],I=m((0,n.useState)(""),2),F=I[0],C=I[1],U=m((0,n.useState)([]),2),x=U[0],L=U[1],P=m((0,n.useState)(!1),2),T=P[0],O=P[1],_=m((0,n.useState)(!1),2),j=_[0],z=_[1],q=m((0,n.useState)(),2),B=q[0],D=q[1],N=m((0,n.useState)(!1),2),G=N[0],V=N[1],W=(0,n.useCallback)(function(){return d(function(){var t,r,n,l,o,i,s;return f(this,function(c){switch(c.label){case 0:O(!0),c.label=1;case 1:return c.trys.push([1,3,4,5]),[4,Promise.all([e.api.request({url:"aiApiUserPermissions:list",method:"get",params:{page:g,pageSize:20,appends:["user"],sort:"-updatedAt"}}),e.api.request({url:"ai:listAllEnabledModels",method:"get"})])];case 2:return o=(l=m.apply(void 0,[c.sent(),2]))[0],i=l[1],h((0,u.m)(o,[])),w(null!=(t=null==o||null==(n=o.data)||null==(r=n.meta)?void 0:r.count)?t:0),L((0,u.m)(i,[])),[3,5];case 3:return s=c.sent(),a.message.error((0,u.g)(s)),[3,5];case 4:return O(!1),[7];case 5:return[2]}})})()},[e.api,g]);(0,n.useEffect)(function(){W()},[W]);var J=(0,n.useCallback)(function(t){return d(function(){var r,n;return f(this,function(l){switch(l.label){case 0:return l.trys.push([0,2,,3]),[4,e.api.request({url:"aiApiUserPermissions:listUsers",method:"get",params:{keyword:t,pageSize:50,excludeGranted:!B}})];case 1:return r=l.sent(),M((0,u.m)(r,[])),[3,3];case 2:return n=l.sent(),a.message.error((0,u.g)(n)),[3,3];case 3:return[2]}})})()},[e.api,B]);(0,n.useEffect)(function(){if(G){var e=setTimeout(function(){return J(F)},300);return function(){return clearTimeout(e)}}},[G,F,J]);var K=(0,n.useMemo)(function(){return x.map(function(e){return{label:e.llmServiceTitle||e.llmService,value:e.llmService}})},[x]),$=(0,n.useMemo)(function(){var e=new Set(s||[]);return x.filter(function(t){return e.has(t.llmService)}).flatMap(function(e){return(e.enabledModels||[]).map(function(t){return{label:"".concat(e.llmServiceTitle||e.llmService," / ").concat(t.label||t.value),value:"".concat(e.llmService,"/").concat(t.value)}})})},[x,s]),H=function(e){D(e),C(""),M(e.user?[e.user]:[]),r.setFieldsValue({userId:e.userId,enabled:e.enabled,allowAllModels:e.allowAllModels,allowedLlmServices:e.allowedLlmServices||[],allowedModels:e.allowedModels||[]}),V(!0)},Q=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:"")},R=function(e){var t;return(null==(t=K.find(function(t){return t.value===e}))?void 0:t.label)||e},X=[{title:t("User"),key:"user",width:180,render:function(e,t){return Q(t.user)||String(t.userId)}},{title:t("Allowed LLM services"),dataIndex:"allowedLlmServices",key:"allowedLlmServices",render:function(e){return(null==e?void 0:e.length)?l().createElement(a.Space,{size:[0,4],wrap:!0},e.map(function(e){return l().createElement(a.Tag,{key:e},R(e))})):l().createElement(a.Tag,{color:"red"},t("No service allowed"))}},{title:t("Allowed models"),key:"allowedModels",width:220,render:function(e,r){return r.allowAllModels?l().createElement(a.Tag,{color:"blue"},t("All models of allowed services")):l().createElement(a.Space,{size:[0,4],wrap:!0},(r.allowedModels||[]).map(function(e){return l().createElement(a.Tag,{key:e},e)}))}},{title:t("Status"),dataIndex:"enabled",key:"enabled",width:100,render:function(e){return l().createElement(a.Tag,{color:e?"green":"default"},e?t("Enabled"):t("Disabled"))}},{title:t("Actions"),key:"actions",width:150,fixed:"right",render:function(r,n){return l().createElement(a.Space,{size:0},l().createElement(a.Button,{type:"link",onClick:function(){return H(n)}},t("Edit")),l().createElement(a.Popconfirm,{title:t("Delete this permission?"),onConfirm:function(){return d(function(){var r;return f(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,e.api.request({url:"aiApiUserPermissions:destroy/".concat(n.id),method:"post"})];case 1:return l.sent(),a.message.success(t("Deleted successfully")),[4,W()];case 2:return l.sent(),[3,4];case 3:return r=l.sent(),a.message.error((0,u.g)(r)),[3,4];case 4:return[2]}})})()}},l().createElement(a.Button,{type:"link",danger:!0},t("Delete"))))}}];return l().createElement(a.Card,{title:t("User LLM permissions"),extra:l().createElement(a.Button,{type:"primary",onClick:function(){D(void 0),C(""),r.setFieldsValue({enabled:!0,allowedLlmServices:[],allowAllModels:!0,allowedModels:[]}),V(!0)}},t("Add permission"))},l().createElement(a.Alert,{type:"info",showIcon:!0,style:{marginBottom:16},message:t("Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.")}),l().createElement(a.Table,{rowKey:"id",columns:X,dataSource:v,loading:T,scroll:{x:1e3},pagination:{current:g,pageSize:20,total:y,showSizeChanger:!1,onChange:E}}),l().createElement(a.Modal,{title:B?t("Edit permission"):t("Add permission"),open:G,onCancel:function(){return V(!1)},onOk:function(){return d(function(){var n,l;return f(this,function(o){switch(o.label){case 0:return[4,r.validateFields()];case 1:n=o.sent(),z(!0),o.label=2;case 2:return o.trys.push([2,5,6,7]),[4,e.api.request({url:B?"aiApiUserPermissions:update/".concat(B.id):"aiApiUserPermissions:create",method:"post",data:n})];case 3:return o.sent(),a.message.success(t("Saved successfully")),V(!1),[4,W()];case 4:return o.sent(),[3,7];case 5:return l=o.sent(),a.message.error((0,u.g)(l)),[3,7];case 6:return z(!1),[7];case 7:return[2]}})})()},confirmLoading:j,destroyOnClose:!0},l().createElement(a.Form,{form:r,layout:"vertical",preserve:!1},l().createElement(a.Form.Item,{name:"userId",label:t("User"),rules:[{required:!0}]},l().createElement(a.Select,{disabled:!!B,showSearch:!0,filterOption:!1,onSearch:C,notFoundContent:null,options:A.map(function(e){return{label:Q(e),value:e.id}})})),l().createElement(a.Form.Item,{name:"allowedLlmServices",label:t("Allowed LLM services"),extra:t("Only services also enabled in the general configuration take effect.")},l().createElement(a.Select,{mode:"multiple",allowClear:!0,showSearch:!0,optionFilterProp:"label",options:K})),l().createElement(a.Form.Item,{name:"allowAllModels",label:t("Allow all models"),valuePropName:"checked"},l().createElement(a.Switch,null)),!1===c&&l().createElement(a.Form.Item,{name:"allowedModels",label:t("Allowed models")},l().createElement(a.Select,{mode:"multiple",allowClear:!0,showSearch:!0,optionFilterProp:"label",options:$})),l().createElement(a.Form.Item,{name:"enabled",label:t("Enabled"),valuePropName:"checked"},l().createElement(a.Switch,null)))))}},650:function(e,t,r){r.d(t,{k:function(){return a}});var n=r(694),l=JSON.parse('{"UU":"plugin-ai-api"}');function a(){var e=(0,n.useFlowEngine)();return function(t){return e.context.t(t,{ns:[l.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 l(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 l},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:"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}()});
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,i){return function(){"use strict";var o,a,u,c={485:function(t){t.exports=e},694:function(e){e.exports=i},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+"."+({123:"05f1f649923f93eb",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 i,o,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){i=c;break}}i||(o=!0,(i=document.createElement("script")).timeout=120,p.nc&&i.setAttribute("nonce",p.nc),i.setAttribute("data-rspack","plugin-ai-api/client-v2:"+n),i.src=e),s[e]=[t];var l=function(t,n){i.onerror=i.onload=null,clearTimeout(f);var r=s[e];if(delete s[e],i.parentNode&&i.parentNode.removeChild(i),r&&r.forEach(function(e){return e(n)}),t)return t(n)},f=setTimeout(l.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=l.bind(null,i.onerror),i.onload=l.bind(null,i.onload),o&&document.head.appendChild(i)},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(/\/[^\/]+$/,"/"),o={889:0},p.f.j=function(e,t){var n=p.o(o,e)?o[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=o[e]=[t,r]});t.push(n[2]=r);var i=p.p+p.u(e),a=Error();p.l(i,function(t){if(p.o(o,e)&&(0!==(n=o[e])&&(o[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;a.message="Loading chunk "+e+" failed.\n("+r+": "+i+")",a.name="ChunkLoadError",a.type=r,a.request=i,n[1](a)}},"chunk-"+e,e)}},a=function(e,t){var n,r,i=t[0],a=t[1],u=t[2],c=0;if(i.some(function(e){return 0!==o[e]})){for(n in a)p.o(a,n)&&(p.m[n]=a[n]);u&&u(p)}for(e&&e(t);c<i.length;c++)r=i[c],p.o(o,r)&&o[r]&&o[r][0](),o[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 i=window.__nocobase_modern_client_prefix__||"v",o="/"+(i=String(i).replace(/^\/+|\/+$/g,"")||"v")+"/";if(!(e=window.__nocobase_public_path__||"")&&window.location&&window.location.pathname){var a=window.location.pathname||"/",u=a.indexOf(o);e=u>=0?a.slice(0,u+1):"/"}e&&(e=e.replace(RegExp("/"+i+"/?$"),"/")),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,i,o,a){try{var u=e[o](a),c=u.value}catch(e){n(e);return}u.done?t(c):Promise.resolve(c).then(r,i)}function r(e,t,n){return(r=u()?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 i(e){return(i=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 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,i(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),o(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=i(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||[],i(this).constructor):e.apply(this,t))}return a.prototype=Object.create(e&&e.prototype,{constructor:{value:a,writable:!0,configurable:!0}}),e&&o(a,e),r=[{key:"load",value:function(){var e;return(e=function(){var e,n,r;return function(e,t){var n,r,i,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[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]&&(o=0)),o;)try{if(n=1,r&&(i=2&l[0]?r.return:l[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,l[1])).done)return i;switch(r=0,i&&(l=[2&l[0],i.value]),l[0]){case 0:case 1:i=l;break;case 4:return o.label++,{value:l[1],done:!1};case 5:o.label++,r=l[1],l=[0];continue;case 7:l=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===l[0]||2===l[0])){o=0;continue}if(3===l[0]&&(!i||l[1]>i[0]&&l[1]<i[3])){o.label=l[1];break}if(6===l[0]&&o.label<i[1]){o.label=i[1],i=l;break}if(i&&o.label<i[2]){o.label=i[2],o.ops.push(l);break}i[2]&&o.ops.pop(),o.trys.pop();continue}l=t.call(e,o)}catch(e){l=[6,e],r=0}finally{n=i=0}if(5&l[0])throw l[1];return{value:l[0]?l[1]:void 0,done:!0}}}}(this,function(i){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-permissions",title:this.t("User LLM permissions"),aclSnippet:"pm.plugin-ai-api.user-permissions",sort:4,componentLoader:function(){return p.e("123").then(p.bind(p,46))}}),this.pluginSettingsManager.addPageTabItem({menuKey:"ai-api",key:"user-quotas",title:this.t("User quotas"),aclSnippet:t,sort:5,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:6,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(i,o){var a=e.apply(t,r);function u(e){n(a,i,o,u,c,"next",e)}function c(e){n(a,i,o,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}()});
package/dist/constants.js CHANGED
@@ -26,11 +26,14 @@ var __copyProps = (to, from, except, desc) => {
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
  var constants_exports = {};
28
28
  __export(constants_exports, {
29
- AI_API_ACL_SNIPPET: () => AI_API_ACL_SNIPPET
29
+ AI_API_ACL_SNIPPET: () => AI_API_ACL_SNIPPET,
30
+ AI_API_USER_PERMISSIONS_SNIPPET: () => AI_API_USER_PERMISSIONS_SNIPPET
30
31
  });
31
32
  module.exports = __toCommonJS(constants_exports);
32
33
  const AI_API_ACL_SNIPPET = "pm.plugin-ai-api.configuration";
34
+ const AI_API_USER_PERMISSIONS_SNIPPET = "pm.plugin-ai-api.user-permissions";
33
35
  // Annotate the CommonJS export names for ESM import in node:
34
36
  0 && (module.exports = {
35
- AI_API_ACL_SNIPPET
37
+ AI_API_ACL_SNIPPET,
38
+ AI_API_USER_PERMISSIONS_SNIPPET
36
39
  });
@@ -101,5 +101,16 @@
101
101
  "Select which AI Employees this role may use:": "Select which AI Employees this role may use:",
102
102
  "Select allowed AI Employees": "Select allowed AI Employees",
103
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."
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.",
105
+ "User LLM permissions": "User LLM permissions",
106
+ "Add permission": "Add permission",
107
+ "Edit permission": "Edit permission",
108
+ "Delete this permission?": "Delete this permission?",
109
+ "Allowed LLM services": "Allowed LLM services",
110
+ "Allow all models": "Allow all models",
111
+ "Allowed models": "Allowed models",
112
+ "No service allowed": "No service allowed",
113
+ "All models of allowed services": "All models of allowed services",
114
+ "Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.": "Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.",
115
+ "Only services also enabled in the general configuration take effect.": "Only services also enabled in the general configuration take effect."
105
116
  }
@@ -101,5 +101,16 @@
101
101
  "Select which AI Employees this role may use:": "Chọn AI Employee mà vai trò này được dùng:",
102
102
  "Select allowed AI Employees": "Chọn AI Employee được phép",
103
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."
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.",
105
+ "User LLM permissions": "Phân quyền LLM theo người dùng",
106
+ "Add permission": "Thêm phân quyền",
107
+ "Edit permission": "Sửa phân quyền",
108
+ "Delete this permission?": "Xoá phân quyền này?",
109
+ "Allowed LLM services": "Dịch vụ LLM được phép",
110
+ "Allow all models": "Cho phép tất cả model",
111
+ "Allowed models": "Model được phép",
112
+ "No service allowed": "Không được phép dịch vụ nào",
113
+ "All models of allowed services": "Tất cả model của các dịch vụ được phép",
114
+ "Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.": "Người dùng có trong danh sách này chỉ được dùng các dịch vụ được chọn bên dưới. Người dùng không có bản ghi sẽ dùng theo cấu hình chung.",
115
+ "Only services also enabled in the general configuration take effect.": "Chỉ những dịch vụ đồng thời được bật trong cấu hình chung mới có hiệu lực."
105
116
  }
@@ -101,5 +101,16 @@
101
101
  "Select which AI Employees this role may use:": "选择此角色可使用的 AI 员工:",
102
102
  "Select allowed AI Employees": "选择允许的 AI 员工",
103
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%。"
104
+ "Raise this to accept inline base64 images. Base64 adds about 33% to the original file size.": "调高此值以接收内联 base64 图片。base64 编码会使体积增加约 33%。",
105
+ "User LLM permissions": "用户 LLM 权限",
106
+ "Add permission": "添加权限",
107
+ "Edit permission": "编辑权限",
108
+ "Delete this permission?": "确定删除此权限?",
109
+ "Allowed LLM services": "允许的 LLM 服务",
110
+ "Allow all models": "允许所有模型",
111
+ "Allowed models": "允许的模型",
112
+ "No service allowed": "未允许任何服务",
113
+ "All models of allowed services": "允许服务下的所有模型",
114
+ "Users listed here are limited to the services selected below. Users without a record fall back to the general configuration.": "此处列出的用户仅能使用下方所选的服务;没有记录的用户按通用配置处理。",
115
+ "Only services also enabled in the general configuration take effect.": "仅当服务同时在通用配置中启用时才会生效。"
105
116
  }
@@ -0,0 +1,67 @@
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_user_permissions_exports = {};
28
+ __export(ai_api_user_permissions_exports, {
29
+ default: () => ai_api_user_permissions_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_user_permissions_exports);
32
+ var import_database = require("@nocobase/database");
33
+ var ai_api_user_permissions_default = (0, import_database.defineCollection)({
34
+ name: "aiApiUserPermissions",
35
+ autoGenId: true,
36
+ fields: [
37
+ { name: "userId", type: "bigInt", allowNull: false, index: true },
38
+ {
39
+ name: "user",
40
+ type: "belongsTo",
41
+ target: "users",
42
+ targetKey: "id",
43
+ foreignKey: "userId",
44
+ constraints: false
45
+ },
46
+ { name: "enabled", type: "boolean", defaultValue: true, index: true },
47
+ {
48
+ name: "allowedLlmServices",
49
+ type: "json",
50
+ defaultValue: [],
51
+ comment: "LLM service names/titles this user may use. Empty means the user is denied every service."
52
+ },
53
+ { name: "allowAllModels", type: "boolean", defaultValue: true },
54
+ {
55
+ name: "allowedModels",
56
+ type: "json",
57
+ defaultValue: [],
58
+ comment: 'Array of "serviceName/modelId" this user may use (when allowAllModels=false)'
59
+ }
60
+ ],
61
+ indexes: [
62
+ {
63
+ fields: ["userId"],
64
+ unique: true
65
+ }
66
+ ]
67
+ });
@@ -44,8 +44,10 @@ var import_server = require("@nocobase/server");
44
44
  var import_router = require("./routes/router");
45
45
  var import_ai_api_config = __toESM(require("./resource/ai-api-config"));
46
46
  var import_ai_api_usage_monitor = __toESM(require("./resource/ai-api-usage-monitor"));
47
+ var import_ai_api_user_permissions = __toESM(require("./resource/ai-api-user-permissions"));
47
48
  var import_rate_limiter = require("./utils/rate-limiter");
48
49
  var import_role_permission = require("./middleware/role-permission");
50
+ var import_user_permissions = require("./utils/user-permissions");
49
51
  var import_validation = require("./validation");
50
52
  var import_constants = require("../constants");
51
53
  var import_dayjs = __toESM(require("dayjs"));
@@ -84,12 +86,19 @@ class PluginAiApiServer extends import_server.Plugin {
84
86
  this.app.use((0, import_router.createAiLlmRouter)(this), { after: "idp-oauth-resource-auth", before: "resourcer" });
85
87
  this.app.resourceManager.define(import_ai_api_config.default);
86
88
  this.app.resourceManager.define(import_ai_api_usage_monitor.default);
89
+ this.app.resourceManager.define(import_ai_api_user_permissions.default);
87
90
  this.app.db.on("aiApiRolePermissions.afterSave", (model) => {
88
91
  (0, import_role_permission.invalidateRolePermissionCache)(model.get("roleName"));
89
92
  });
90
93
  this.app.db.on("aiApiRolePermissions.afterDestroy", (model) => {
91
94
  (0, import_role_permission.invalidateRolePermissionCache)(model.get("roleName"));
92
95
  });
96
+ this.app.db.on("aiApiUserPermissions.afterSave", (model, options) => {
97
+ this.revokeUserPermissions(model.get("userId"), options == null ? void 0 : options.transaction);
98
+ });
99
+ this.app.db.on("aiApiUserPermissions.afterDestroy", (model, options) => {
100
+ this.revokeUserPermissions(model.get("userId"), options == null ? void 0 : options.transaction);
101
+ });
93
102
  this.app.acl.registerSnippet({
94
103
  name: import_constants.AI_API_ACL_SNIPPET,
95
104
  actions: [
@@ -105,9 +114,32 @@ class PluginAiApiServer extends import_server.Plugin {
105
114
  "aiApiUsageMonitor:summary"
106
115
  ]
107
116
  });
117
+ this.app.acl.registerSnippet({
118
+ name: import_constants.AI_API_USER_PERMISSIONS_SNIPPET,
119
+ actions: ["aiApiUserPermissions:*"]
120
+ });
108
121
  this.gcInterval = setInterval(() => this.rateLimiter.gc(), 5 * 60 * 1e3);
109
122
  this.gcInterval.unref();
110
123
  }
124
+ /**
125
+ * Drop a user's cached LLM scope on every node.
126
+ *
127
+ * The local call is not redundant: syncMessageManager hardcodes skipSelf, so the publishing
128
+ * node never receives its own message. Passing the transaction defers the broadcast until
129
+ * the write commits, so other nodes cannot re-read the old row and re-cache it.
130
+ */
131
+ revokeUserPermissions(userId, transaction) {
132
+ (0, import_user_permissions.invalidateUserPermissionCache)(userId);
133
+ this.sendSyncMessage({ type: "invalidateUserPermissions", userId }, { transaction });
134
+ }
135
+ /**
136
+ * Received only on the *other* nodes (skipSelf), so this must not re-broadcast.
137
+ */
138
+ async handleSyncMessage(message) {
139
+ if ((message == null ? void 0 : message.type) === "invalidateUserPermissions") {
140
+ (0, import_user_permissions.invalidateUserPermissionCache)(message.userId);
141
+ }
142
+ }
111
143
  async install() {
112
144
  const existing = await this.db.getRepository("aiApiConfig").findOne();
113
145
  if (!existing) {
@@ -0,0 +1,75 @@
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_user_permissions_exports = {};
28
+ __export(ai_api_user_permissions_exports, {
29
+ default: () => ai_api_user_permissions_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_user_permissions_exports);
32
+ const MAX_PAGE_SIZE = 100;
33
+ const aiApiUserPermissionsResource = {
34
+ name: "aiApiUserPermissions",
35
+ actions: {
36
+ async listUsers(ctx, next) {
37
+ const params = ctx.action.params || {};
38
+ const keyword = typeof params.keyword === "string" ? params.keyword.trim() : "";
39
+ const page = Math.max(1, Number(params.page) || 1);
40
+ const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, Number(params.pageSize) || 50));
41
+ const filter = keyword ? {
42
+ $or: [
43
+ { nickname: { $includes: keyword } },
44
+ { username: { $includes: keyword } },
45
+ { email: { $includes: keyword } }
46
+ ]
47
+ } : {};
48
+ if (params.excludeGranted) {
49
+ const granted = await ctx.db.getRepository("aiApiUserPermissions").find({ fields: ["userId"] });
50
+ const ids = granted.map((row) => row.get("userId")).filter((id) => id !== null && id !== void 0);
51
+ if (ids.length) filter.id = { $notIn: ids };
52
+ }
53
+ const [rows, count] = await ctx.db.getRepository("users").findAndCount({
54
+ filter,
55
+ fields: ["id", "nickname", "username", "email"],
56
+ sort: ["nickname", "id"],
57
+ offset: (page - 1) * pageSize,
58
+ limit: pageSize
59
+ });
60
+ ctx.body = {
61
+ rows: rows.map((row) => ({
62
+ id: row.get("id"),
63
+ nickname: row.get("nickname"),
64
+ username: row.get("username"),
65
+ email: row.get("email")
66
+ })),
67
+ count,
68
+ page,
69
+ pageSize
70
+ };
71
+ await next();
72
+ }
73
+ }
74
+ };
75
+ var ai_api_user_permissions_default = aiApiUserPermissionsResource;
@@ -32,6 +32,7 @@ module.exports = __toCommonJS(agent_completions_exports);
32
32
  var import_openai_format = require("../utils/openai-format");
33
33
  var import_resolve_service = require("../utils/resolve-service");
34
34
  var import_role_permission = require("../middleware/role-permission");
35
+ var import_user_permissions = require("../utils/user-permissions");
35
36
  var import_streaming = require("../utils/streaming");
36
37
  var import_ai_employee_runtime = require("../utils/ai-employee-runtime");
37
38
  var import_usage = require("../usage");
@@ -88,6 +89,10 @@ async function handleAgentCompletions(ctx, plugin) {
88
89
  ctx.body = (0, import_openai_format.toOpenAIError)(404, "LLM service is disabled", "invalid_request_error", "model_not_found");
89
90
  return;
90
91
  }
92
+ const globalEnabledServices = config ? config.get("enabledLlmServices") || config.enabledLlmServices : [];
93
+ if (!await (0, import_user_permissions.enforceModelAccess)(ctx, globalEnabledServices, service, modelId)) {
94
+ return;
95
+ }
91
96
  const employeeUsername = defaultAiEmployee;
92
97
  if (!(0, import_role_permission.checkEmployeeAccess)(ctx, employeeUsername)) {
93
98
  ctx.status = 403;
@@ -38,11 +38,11 @@ var import_openai_format = require("../utils/openai-format");
38
38
  var import_resolve_service = require("../utils/resolve-service");
39
39
  var import_streaming = require("../utils/streaming");
40
40
  var import_role_permission = require("../middleware/role-permission");
41
+ var import_user_permissions = require("../utils/user-permissions");
41
42
  var import_usage = require("../usage");
42
43
  var import_billing = require("../billing");
43
44
  var import_app_observability = require("../utils/app-observability");
44
45
  async function handleChatCompletions(ctx, plugin) {
45
- var _a;
46
46
  const body = ctx.request.body;
47
47
  if (!(body == null ? void 0 : body.model)) {
48
48
  ctx.status = 400;
@@ -117,20 +117,8 @@ async function handleChatCompletions(ctx, plugin) {
117
117
  return;
118
118
  }
119
119
  const config = await ctx.db.getRepository("aiApiConfig").findOne();
120
- if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
121
- const serviceName = service.name;
122
- const serviceTitle = service.title;
123
- const isAllowed = config.enabledLlmServices.some((s) => s === serviceName || s === serviceTitle);
124
- if (!isAllowed) {
125
- ctx.status = 403;
126
- ctx.body = (0, import_openai_format.toOpenAIError)(
127
- 403,
128
- `LLM service '${service.title || service.name}' is not enabled for API access`,
129
- "invalid_request_error",
130
- "model_not_available"
131
- );
132
- return;
133
- }
120
+ if (!await (0, import_user_permissions.enforceModelAccess)(ctx, config == null ? void 0 : config.enabledLlmServices, service, modelId)) {
121
+ return;
134
122
  }
135
123
  const providerMeta = aiPlugin.aiManager.llmProviders.get(service.provider);
136
124
  if (!providerMeta) {
@@ -140,6 +128,13 @@ async function handleChatCompletions(ctx, plugin) {
140
128
  }
141
129
  await (0, import_billing.prepareLlmBilling)(ctx, resolved);
142
130
  const providerRequestParameters = getProviderRequestParameters(body);
131
+ if (stream) {
132
+ const streamOptions = isRecord(body.stream_options) ? body.stream_options : {};
133
+ providerRequestParameters.stream_options = {
134
+ ...streamOptions,
135
+ include_usage: true
136
+ };
137
+ }
143
138
  const modelOptions = {
144
139
  model: modelId,
145
140
  llmService: service.name
@@ -312,7 +307,13 @@ async function handleStreamingCompletion(ctx, chatModel, messages, completionId,
312
307
  finishReason = "tool_calls";
313
308
  await (0, import_streaming.writeResponse)(
314
309
  ctx,
315
- (0, import_openai_format.formatSSE)((0, import_openai_format.toOpenAIStreamChunk)({ id: completionId, model: modelName, delta: { tool_calls: toolCallChunks } }))
310
+ (0, import_openai_format.formatSSE)(
311
+ (0, import_openai_format.toOpenAIStreamChunk)({
312
+ id: completionId,
313
+ model: modelName,
314
+ delta: { tool_calls: toolCallChunks }
315
+ })
316
+ )
316
317
  );
317
318
  }
318
319
  if (chunk.usage_metadata) {
@@ -331,6 +332,18 @@ async function handleStreamingCompletion(ctx, chatModel, messages, completionId,
331
332
  })
332
333
  )
333
334
  );
335
+ if (usage) {
336
+ await (0, import_streaming.writeResponse)(
337
+ ctx,
338
+ (0, import_openai_format.formatSSE)(
339
+ (0, import_openai_format.toOpenAIUsageChunk)({
340
+ id: completionId,
341
+ model: modelName,
342
+ usage
343
+ })
344
+ )
345
+ );
346
+ }
334
347
  await (0, import_streaming.writeResponse)(ctx, (0, import_openai_format.formatSSEDone)());
335
348
  (0, import_usage.setAiApiUsageResult)(ctx, usage, { gatewayResponseId: completionId, providerRequestId });
336
349
  ctx.state.aiApiStreamResult = { succeeded: true, id: completionId };