plugin-ai-api 1.0.1 → 1.0.3

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 (48) hide show
  1. package/dist/client/23.e96ecf13e6072dce.js +10 -0
  2. package/dist/client/{824c2f7487ee05fd.js → 503.29bcdb426b01e715.js} +10 -1
  3. package/dist/client/index.js +10 -1
  4. package/dist/externalVersion.js +19 -8
  5. package/dist/index.js +9 -0
  6. package/dist/locale/en-US.json +10 -10
  7. package/dist/locale/zh-CN.json +10 -10
  8. package/dist/server/collections/ai-api-config.js +9 -0
  9. package/dist/server/collections/ai-api-role-permissions.js +62 -0
  10. package/dist/server/index.js +9 -0
  11. package/dist/server/middleware/rate-limit.js +9 -0
  12. package/dist/server/middleware/role-permission.js +66 -0
  13. package/dist/server/plugin.js +15 -1
  14. package/dist/server/resource/ai-api-config.js +10 -1
  15. package/dist/server/routes/agent-completions.js +39 -5
  16. package/dist/server/routes/auth.js +11 -12
  17. package/dist/server/routes/chat-completions.js +27 -4
  18. package/dist/server/routes/completions.js +16 -4
  19. package/dist/server/routes/embeddings.js +10 -3
  20. package/dist/server/routes/models.js +11 -8
  21. package/dist/server/routes/router.js +37 -5
  22. package/dist/server/utils/openai-format.js +9 -0
  23. package/dist/server/utils/rate-limiter.js +9 -0
  24. package/dist/server/utils/resolve-service.js +9 -0
  25. package/dist/swagger.js +345 -0
  26. package/package.json +14 -13
  27. package/dist/client/AiApiConfigPage.d.ts +0 -3
  28. package/dist/client/index.d.ts +0 -1
  29. package/dist/client/locale.d.ts +0 -10
  30. package/dist/client/models/index.d.ts +0 -10
  31. package/dist/client/plugin.d.ts +0 -6
  32. package/dist/index.d.ts +0 -2
  33. package/dist/server/collections/ai-api-config.d.ts +0 -2
  34. package/dist/server/index.d.ts +0 -1
  35. package/dist/server/middleware/rate-limit.d.ts +0 -18
  36. package/dist/server/plugin.d.ts +0 -18
  37. package/dist/server/resource/ai-api-config.d.ts +0 -10
  38. package/dist/server/routes/agent-completions.d.ts +0 -34
  39. package/dist/server/routes/auth.d.ts +0 -8
  40. package/dist/server/routes/chat-completions.d.ts +0 -9
  41. package/dist/server/routes/completions.d.ts +0 -10
  42. package/dist/server/routes/embeddings.d.ts +0 -22
  43. package/dist/server/routes/models.d.ts +0 -21
  44. package/dist/server/routes/router.d.ts +0 -25
  45. package/dist/server/utils/openai-format.d.ts +0 -99
  46. package/dist/server/utils/rate-limiter.d.ts +0 -35
  47. package/dist/server/utils/resolve-service.d.ts +0 -19
  48. package/nocobase-plugin-ai-api-2.0.20.tgz +0 -0
@@ -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([["23"],{928:function(e,t,r){r.r(t),r.d(t,{AiApiRolePermissions:function(){return f}});var n=r(156),l=r.n(n),a=r(772),o=r(721);function i(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 u(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 c(e){return function(){var t=this,r=arguments;return new Promise(function(n,l){var a=e.apply(t,r);function o(e){u(a,n,l,o,i,"next",e)}function i(e){u(a,n,l,o,i,"throw",e)}o(void 0)})}}function s(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 p(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r,n,l=null==e?null:"undefined"!=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 i(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 i(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 d(e,t){var r,n,l,a,o={label:0,sent:function(){if(1&l[0])throw l[1];return l[1]},trys:[],ops:[]};return a={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function i(a){return function(i){var u=[a,i];if(r)throw TypeError("Generator is already executing.");for(;o;)try{if(r=1,n&&(l=2&u[0]?n.return:u[0]?n.throw||((l=n.return)&&l.call(n),0):n.next)&&!(l=l.call(n,u[1])).done)return l;switch(n=0,l&&(u=[2&u[0],l.value]),u[0]){case 0:case 1:l=u;break;case 4:return o.label++,{value:u[1],done:!1};case 5:o.label++,n=u[1],u=[0];continue;case 7:u=o.ops.pop(),o.trys.pop();continue;default:if(!(l=(l=o.trys).length>0&&l[l.length-1])&&(6===u[0]||2===u[0])){o=0;continue}if(3===u[0]&&(!l||u[1]>l[0]&&u[1]<l[3])){o.label=u[1];break}if(6===u[0]&&o.label<l[1]){o.label=l[1],l=u;break}if(l&&o.label<l[2]){o.label=l[2],o.ops.push(u);break}l[2]&&o.ops.pop(),o.trys.pop();continue}u=t.call(e,o)}catch(e){u=[6,e],n=0}finally{r=l=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}var m=o.Typography.Text;function f(e){var t,r,i=e.role,u=(0,a.useAPIClient)(),f=p((0,n.useState)(!0),2),y=f[0],b=f[1],h=p((0,n.useState)(!1),2),v=h[0],w=h[1],E=p((0,n.useState)([]),2),g=E[0],A=E[1],O=p((0,n.useState)(null),2),S=O[0],P=O[1],j=null==i?void 0:i.name;(0,n.useEffect)(function(){j&&k()},[j]);var k=(t=c(function(){var e,t,r,n,l,a,o;return d(this,function(i){switch(i.label){case 0:b(!0),i.label=1;case 1:return i.trys.push([1,3,4,5]),[4,Promise.all([u.request({url:"aiApiRolePermissions",params:{filter:{roleName:j},paginate:!1}}),u.request({url:"aiEmployees:list",params:{paginate:!1}})])];case 2:return l=(n=p.apply(void 0,[i.sent(),2]))[0],a=n[1],P((o=null==l||null==(t=l.data)||null==(e=t.data)?void 0:e[0])?{id:o.id,roleName:o.roleName,enabled:!!o.enabled,allowAllEmployees:!1!==o.allowAllEmployees,allowedEmployees:o.allowedEmployees||[]}:{roleName:j,enabled:!1,allowAllEmployees:!0,allowedEmployees:[]}),A(((null==a||null==(r=a.data)?void 0:r.data)||[]).map(function(e){return{username:e.username,nickname:e.nickname||e.username}})),[3,5];case 3:return console.error("Failed to load AI API role permissions:",i.sent()),[3,5];case 4:return b(!1),[7];case 5:return[2]}})}),function(){return t.apply(this,arguments)}),I=(r=c(function(e){var t,r,n,l;return d(this,function(a){switch(a.label){case 0:if(!S)return[2];P(t=s({},S,e)),w(!0),a.label=1;case 1:if(a.trys.push([1,6,7,8]),!t.id)return[3,3];return[4,u.request({url:"aiApiRolePermissions/".concat(t.id),method:"PUT",data:t})];case 2:return a.sent(),[3,5];case 3:return[4,u.request({url:"aiApiRolePermissions",method:"POST",data:t})];case 4:var o,i;(null==(l=null==(n=a.sent())||null==(r=n.data)?void 0:r.data)?void 0:l.id)&&P((o=s({},t),i=i={id:l.id},Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):(function(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r.push.apply(r,n)}return r})(Object(i)).forEach(function(e){Object.defineProperty(o,e,Object.getOwnPropertyDescriptor(i,e))}),o)),a.label=5;case 5:return[3,8];case 6:return console.error("Failed to save AI API role permissions:",a.sent()),P(S),[3,8];case 7:return w(!1),[7];case 8:return[2]}})}),function(e){return r.apply(this,arguments)});return y?l().createElement(o.Spin,null):l().createElement(o.Card,{bordered:!1},l().createElement(o.Space,{direction:"vertical",style:{width:"100%"},size:"middle"},l().createElement(o.Space,null,l().createElement(o.Switch,{checked:!!(null==S?void 0:S.enabled),loading:v,onChange:function(e){return I({enabled:e})}}),l().createElement(m,{strong:!0},"Allow this role to use the AI API")),(null==S?void 0:S.enabled)&&l().createElement(l().Fragment,null,l().createElement(o.Divider,{style:{margin:"8px 0"}}),l().createElement(o.Space,null,l().createElement(o.Switch,{checked:!!(null==S?void 0:S.allowAllEmployees),loading:v,onChange:function(e){return I({allowAllEmployees:e})}}),l().createElement(m,null,"Allow all AI Employees")),!(null==S?void 0:S.allowAllEmployees)&&l().createElement("div",null,l().createElement(m,{type:"secondary",style:{display:"block",marginBottom:8}},"Select which AI Employees this role may use:"),l().createElement(o.Select,{mode:"multiple",allowClear:!0,style:{width:"100%",maxWidth:480},placeholder:"Select allowed AI Employees",value:(null==S?void 0:S.allowedEmployees)||[],options:g.map(function(e){return{label:"".concat(e.nickname," (").concat(e.username,")"),value:e.username}}),onChange:function(e){return I({allowedEmployees:e})},disabled:v})))))}}}]);
@@ -1 +1,10 @@
1
- "use strict";(self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).push([["503"],{968:function(e,t,n){n.r(t),n.d(t,{AiApiConfigPage:function(){return y}});var a=n(156),r=n.n(a),l=n(721),o=n(772);function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,a=Array(t);n<t;n++)a[n]=e[n];return a}function c(e,t,n,a,r,l,o){try{var i=e[l](o),c=i.value}catch(e){n(e);return}i.done?t(c):Promise.resolve(c).then(a,r)}function u(e){return function(){var t=this,n=arguments;return new Promise(function(a,r){var l=e.apply(t,n);function o(e){c(l,a,r,o,i,"next",e)}function i(e){c(l,a,r,o,i,"throw",e)}o(void 0)})}}function s(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n,a,r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var l=[],o=!0,i=!1;try{for(r=r.call(e);!(o=(n=r.next()).done)&&(l.push(n.value),!t||l.length!==t);o=!0);}catch(e){i=!0,a=e}finally{try{o||null==r.return||r.return()}finally{if(i)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(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 m(e,t){var n,a,r,l,o={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return l={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(l[Symbol.iterator]=function(){return this}),l;function i(l){return function(i){var c=[l,i];if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,a&&(r=2&c[0]?a.return:c[0]?a.throw||((r=a.return)&&r.call(a),0):a.next)&&!(r=r.call(a,c[1])).done)return r;switch(a=0,r&&(c=[2&c[0],r.value]),c[0]){case 0:case 1:r=c;break;case 4:return o.label++,{value:c[1],done:!1};case 5:o.label++,a=c[1],c=[0];continue;case 7:c=o.ops.pop(),o.trys.pop();continue;default:if(!(r=(r=o.trys).length>0&&r[r.length-1])&&(6===c[0]||2===c[0])){o=0;continue}if(3===c[0]&&(!r||c[1]>r[0]&&c[1]<r[3])){o.label=c[1];break}if(6===c[0]&&o.label<r[1]){o.label=r[1],r=c;break}if(r&&o.label<r[2]){o.label=r[2],o.ops.push(c);break}r[2]&&o.ops.pop(),o.trys.pop();continue}c=t.call(e,o)}catch(e){c=[6,e],a=0}finally{n=r=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}var p=l.Typography.Title,d=l.Typography.Text,f=l.Typography.Paragraph;function y(){var e,t,n=(0,o.useAPIClient)(),i=s(l.Form.useForm(),1)[0],c=s((0,a.useState)(!0),2),y=c[0],v=c[1],h=s((0,a.useState)(!1),2),b=h[0],g=h[1],E=s((0,a.useState)([]),2),A=E[0],S=E[1],w=s((0,a.useState)([]),2),I=w[0],L=w[1],k=s((0,a.useState)(""),2),C=k[0],P=k[1];(0,a.useEffect)(function(){x(),P("".concat(window.location.origin,"/api/ai-llm/v1"))},[]);var x=(e=u(function(){var e,t,a,r,l,o,c;return m(this,function(u){switch(u.label){case 0:v(!0),u.label=1;case 1:return u.trys.push([1,5,6,7]),[4,n.request({url:"aiApiConfig:get"})];case 2:return(l=null==(r=u.sent())||null==(e=r.data)?void 0:e.data)&&i.setFieldsValue({mode:l.mode||"llm",defaultAiEmployee:l.defaultAiEmployee||void 0,defaultLlmService:l.defaultLlmService||void 0,enabledLlmServices:l.enabledLlmServices||[],rateLimitPerMinute:l.rateLimitPerMinute||60}),[4,n.request({url:"aiEmployees:list",params:{paginate:!1}})];case 3:return S(((null==(o=u.sent())||null==(t=o.data)?void 0:t.data)||[]).map(function(e){return{username:e.username,nickname:e.nickname||e.username}})),[4,n.request({url:"ai:listLLMServices"})];case 4:return L(((null==(c=u.sent())||null==(a=c.data)?void 0:a.data)||[]).map(function(e){return{name:e.name,title:e.title||e.name,provider:e.provider,enabled:!1!==e.enabled}})),[3,7];case 5:return console.error("Failed to load config:",u.sent()),[3,7];case 6:return v(!1),[7];case 7:return[2]}})}),function(){return e.apply(this,arguments)}),M=(t=u(function(){var e;return m(this,function(t){switch(t.label){case 0:g(!0),t.label=1;case 1:return t.trys.push([1,3,4,5]),e=i.getFieldsValue(),[4,n.request({url:"aiApiConfig:save",method:"post",data:e})];case 2:return t.sent(),l.message.success("Configuration saved"),[3,5];case 3:return t.sent(),l.message.error("Failed to save configuration"),[3,5];case 4:return g(!1),[7];case 5:return[2]}})}),function(){return t.apply(this,arguments)});return y?r().createElement("div",{style:{display:"flex",justifyContent:"center",padding:60}},r().createElement(l.Spin,{size:"large"})):r().createElement("div",{style:{maxWidth:800,margin:"0 auto",padding:"24px 0"}},r().createElement(p,{level:3},"AI API Gateway Configuration"),r().createElement(f,{type:"secondary"},"Configure the OpenAI-compatible API endpoint. External applications can connect using the base URL and a NocoBase API key."),r().createElement(l.Card,{style:{marginBottom:24}},r().createElement(l.Alert,{type:"info",showIcon:!0,message:"API Endpoint",description:r().createElement(l.Space,{direction:"vertical",size:4},r().createElement(d,null,"Base URL: ",r().createElement(d,{code:!0,copyable:!0},C)),r().createElement(d,{type:"secondary"},"Use this as the base URL in any OpenAI-compatible client (Cursor, Continue.dev, n8n, etc.)")),style:{marginBottom:16}}),r().createElement(l.Alert,{type:"warning",showIcon:!0,message:"Authentication",description:r().createElement(d,null,"Clients must include a NocoBase API key as a Bearer token:",r().createElement("br",null),r().createElement(d,{code:!0},"Authorization: Bearer ","<your-nocobase-api-key>"),r().createElement("br",null),r().createElement(d,{type:"secondary"},"API keys can be created in Settings → API keys."))})),r().createElement(l.Card,{title:"Configuration"},r().createElement(l.Form,{form:i,layout:"vertical",onFinish:M,initialValues:{mode:"llm"}},r().createElement(l.Form.Item,{name:"mode",label:"API Mode",tooltip:"LLM Proxy: Direct access to the LLM model. Agent: Full AI Employee with tools, knowledge base, and agent capabilities."},r().createElement(l.Radio.Group,null,r().createElement(l.Radio.Button,{value:"llm"},"LLM Proxy"),r().createElement(l.Radio.Button,{value:"agent"},"AI Employee Agent"))),r().createElement(l.Form.Item,{name:"defaultAiEmployee",label:"Default AI Employee",tooltip:"The AI Employee whose system prompt will be injected into chat completions (when client doesn't provide a system message)"},r().createElement(l.Select,{allowClear:!0,placeholder:"Select an AI Employee (optional)",options:A.map(function(e){return{label:"".concat(e.nickname," (").concat(e.username,")"),value:e.username}})})),r().createElement(l.Form.Item,{name:"defaultLlmService",label:"Default LLM Service",tooltip:"The LLM service used when clients send only a model name (e.g. 'gpt-4o') without a service prefix. This is the main service that powers the API.",rules:[{required:!0,message:"Please select a default LLM service"}]},r().createElement(l.Select,{allowClear:!0,placeholder:"Select an LLM Service",options:I.map(function(e){return{label:"".concat(e.title," (").concat(e.provider,")"),value:e.name}})})),r().createElement(l.Form.Item,{name:"enabledLlmServices",label:"Enabled LLM Services",tooltip:"Only these services will be exposed via the API. Leave empty to expose all enabled services."},r().createElement(l.Select,{mode:"multiple",allowClear:!0,placeholder:"All enabled services (default)",options:I.map(function(e){return{label:"".concat(e.title," (").concat(e.provider,")"),value:e.name}})})),r().createElement(l.Form.Item,{name:"rateLimitPerMinute",label:"Rate Limit (requests/minute)",tooltip:"Maximum API requests per user per minute. Set 0 for unlimited."},r().createElement(l.InputNumber,{min:0,max:1e4,style:{width:200}})),r().createElement(l.Form.Item,null,r().createElement(l.Button,{type:"primary",htmlType:"submit",loading:b},"Save Configuration")))),r().createElement(l.Card,{title:"Quick Start",style:{marginTop:24}},r().createElement(p,{level:5},"cURL Example"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},"curl ".concat(C,'/chat/completions \\\n -H "Authorization: Bearer <your-api-key>" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "model": "<service-name>/<model-id>",\n "messages": [\n {"role": "user", "content": "Hello!"}\n ]\n }\''))),r().createElement(p,{level:5},"Python (OpenAI SDK)"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},'from openai import OpenAI\n\nclient = OpenAI(\n base_url="'.concat(C,'",\n api_key="<your-nocobase-api-key>"\n)\n\nresponse = client.chat.completions.create(\n model="<service-name>/<model-id>",\n messages=[{"role": "user", "content": "Hello!"}]\n)'))),r().createElement(p,{level:5},"List Available Models"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},"curl ".concat(C,'/models \\\n -H "Authorization: Bearer <your-api-key>"')))))}t.default=y}}]);
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([["503"],{968:function(e,t,n){n.r(t),n.d(t,{AiApiConfigPage:function(){return y}});var a=n(156),r=n.n(a),l=n(721),o=n(772);function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,a=Array(t);n<t;n++)a[n]=e[n];return a}function c(e,t,n,a,r,l,o){try{var i=e[l](o),c=i.value}catch(e){n(e);return}i.done?t(c):Promise.resolve(c).then(a,r)}function u(e){return function(){var t=this,n=arguments;return new Promise(function(a,r){var l=e.apply(t,n);function o(e){c(l,a,r,o,i,"next",e)}function i(e){c(l,a,r,o,i,"throw",e)}o(void 0)})}}function s(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n,a,r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var l=[],o=!0,i=!1;try{for(r=r.call(e);!(o=(n=r.next()).done)&&(l.push(n.value),!t||l.length!==t);o=!0);}catch(e){i=!0,a=e}finally{try{o||null==r.return||r.return()}finally{if(i)throw a}}return l}}(e,t)||function(e,t){if(e){if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(n);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(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 m(e,t){var n,a,r,l,o={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return l={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(l[Symbol.iterator]=function(){return this}),l;function i(l){return function(i){var c=[l,i];if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,a&&(r=2&c[0]?a.return:c[0]?a.throw||((r=a.return)&&r.call(a),0):a.next)&&!(r=r.call(a,c[1])).done)return r;switch(a=0,r&&(c=[2&c[0],r.value]),c[0]){case 0:case 1:r=c;break;case 4:return o.label++,{value:c[1],done:!1};case 5:o.label++,a=c[1],c=[0];continue;case 7:c=o.ops.pop(),o.trys.pop();continue;default:if(!(r=(r=o.trys).length>0&&r[r.length-1])&&(6===c[0]||2===c[0])){o=0;continue}if(3===c[0]&&(!r||c[1]>r[0]&&c[1]<r[3])){o.label=c[1];break}if(6===c[0]&&o.label<r[1]){o.label=r[1],r=c;break}if(r&&o.label<r[2]){o.label=r[2],o.ops.push(c);break}r[2]&&o.ops.pop(),o.trys.pop();continue}c=t.call(e,o)}catch(e){c=[6,e],a=0}finally{n=r=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}var p=l.Typography.Title,d=l.Typography.Text,f=l.Typography.Paragraph;function y(){var e,t,n=(0,o.useAPIClient)(),i=s(l.Form.useForm(),1)[0],c=s((0,a.useState)(!0),2),y=c[0],v=c[1],h=s((0,a.useState)(!1),2),b=h[0],g=h[1],E=s((0,a.useState)([]),2),A=E[0],S=E[1],w=s((0,a.useState)([]),2),I=w[0],L=w[1],k=s((0,a.useState)(""),2),C=k[0],P=k[1];(0,a.useEffect)(function(){x(),P("".concat(window.location.origin,"/api/ai-llm/v1"))},[]);var x=(e=u(function(){var e,t,a,r,l,o,c;return m(this,function(u){switch(u.label){case 0:v(!0),u.label=1;case 1:return u.trys.push([1,5,6,7]),[4,n.request({url:"aiApiConfig:get"})];case 2:return(l=null==(r=u.sent())||null==(e=r.data)?void 0:e.data)&&i.setFieldsValue({mode:l.mode||"llm",defaultAiEmployee:l.defaultAiEmployee||void 0,defaultLlmService:l.defaultLlmService||void 0,enabledLlmServices:l.enabledLlmServices||[],rateLimitPerMinute:l.rateLimitPerMinute||60}),[4,n.request({url:"aiEmployees:list",params:{paginate:!1}})];case 3:return S(((null==(o=u.sent())||null==(t=o.data)?void 0:t.data)||[]).map(function(e){return{username:e.username,nickname:e.nickname||e.username}})),[4,n.request({url:"ai:listLLMServices"})];case 4:return L(((null==(c=u.sent())||null==(a=c.data)?void 0:a.data)||[]).map(function(e){return{name:e.name,title:e.title||e.name,provider:e.provider,enabled:!1!==e.enabled}})),[3,7];case 5:return console.error("Failed to load config:",u.sent()),[3,7];case 6:return v(!1),[7];case 7:return[2]}})}),function(){return e.apply(this,arguments)}),M=(t=u(function(){var e;return m(this,function(t){switch(t.label){case 0:g(!0),t.label=1;case 1:return t.trys.push([1,3,4,5]),e=i.getFieldsValue(),[4,n.request({url:"aiApiConfig:save",method:"post",data:e})];case 2:return t.sent(),l.message.success("Configuration saved"),[3,5];case 3:return t.sent(),l.message.error("Failed to save configuration"),[3,5];case 4:return g(!1),[7];case 5:return[2]}})}),function(){return t.apply(this,arguments)});return y?r().createElement("div",{style:{display:"flex",justifyContent:"center",padding:60}},r().createElement(l.Spin,{size:"large"})):r().createElement("div",{style:{maxWidth:800,margin:"0 auto",padding:"24px 0"}},r().createElement(p,{level:3},"AI API Gateway Configuration"),r().createElement(f,{type:"secondary"},"Configure the OpenAI-compatible API endpoint. External applications can connect using the base URL and a NocoBase API key."),r().createElement(l.Card,{style:{marginBottom:24}},r().createElement(l.Alert,{type:"info",showIcon:!0,message:"API Endpoint",description:r().createElement(l.Space,{direction:"vertical",size:4},r().createElement(d,null,"Base URL:"," ",r().createElement(d,{code:!0,copyable:!0},C)),r().createElement(d,{type:"secondary"},"Use this as the base URL in any OpenAI-compatible client (Cursor, Continue.dev, n8n, etc.)")),style:{marginBottom:16}}),r().createElement(l.Alert,{type:"warning",showIcon:!0,message:"Authentication",description:r().createElement(d,null,"Clients must include a NocoBase API key as a Bearer token:",r().createElement("br",null),r().createElement(d,{code:!0},"Authorization: Bearer ","<your-nocobase-api-key>"),r().createElement("br",null),r().createElement(d,{type:"secondary"},"API keys can be created in Settings → API keys."))})),r().createElement(l.Card,{title:"Configuration"},r().createElement(l.Form,{form:i,layout:"vertical",onFinish:M,initialValues:{mode:"llm"}},r().createElement(l.Form.Item,{name:"mode",label:"API Mode",tooltip:"LLM Proxy: Direct access to the LLM model. Agent: Full AI Employee with tools, knowledge base, and agent capabilities."},r().createElement(l.Radio.Group,null,r().createElement(l.Radio.Button,{value:"llm"},"LLM Proxy"),r().createElement(l.Radio.Button,{value:"agent"},"AI Employee Agent"))),r().createElement(l.Form.Item,{name:"defaultAiEmployee",label:"Default AI Employee",tooltip:"The AI Employee whose system prompt will be injected into chat completions (when client doesn't provide a system message)"},r().createElement(l.Select,{allowClear:!0,placeholder:"Select an AI Employee (optional)",options:A.map(function(e){return{label:"".concat(e.nickname," (").concat(e.username,")"),value:e.username}})})),r().createElement(l.Form.Item,{name:"defaultLlmService",label:"Default LLM Service",tooltip:"The LLM service used when clients send only a model name (e.g. 'gpt-4o') without a service prefix. This is the main service that powers the API.",rules:[{required:!0,message:"Please select a default LLM service"}]},r().createElement(l.Select,{allowClear:!0,placeholder:"Select an LLM Service",options:I.map(function(e){return{label:"".concat(e.title," (").concat(e.provider,")"),value:e.name}})})),r().createElement(l.Form.Item,{name:"enabledLlmServices",label:"Enabled LLM Services",tooltip:"Only these services will be exposed via the API. Leave empty to expose all enabled services."},r().createElement(l.Select,{mode:"multiple",allowClear:!0,placeholder:"All enabled services (default)",options:I.map(function(e){return{label:"".concat(e.title," (").concat(e.provider,")"),value:e.name}})})),r().createElement(l.Form.Item,{name:"rateLimitPerMinute",label:"Rate Limit (requests/minute)",tooltip:"Maximum API requests per user per minute. Set 0 for unlimited."},r().createElement(l.InputNumber,{min:0,max:1e4,style:{width:200}})),r().createElement(l.Form.Item,null,r().createElement(l.Button,{type:"primary",htmlType:"submit",loading:b},"Save Configuration")))),r().createElement(l.Card,{title:"Quick Start",style:{marginTop:24}},r().createElement(p,{level:5},"cURL Example"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},"curl ".concat(C,'/chat/completions \\\n -H "Authorization: Bearer <your-api-key>" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "model": "<service-name>/<model-id>",\n "messages": [\n {"role": "user", "content": "Hello!"}\n ]\n }\''))),r().createElement(p,{level:5},"Python (OpenAI SDK)"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},'from openai import OpenAI\n\nclient = OpenAI(\n base_url="'.concat(C,'",\n api_key="<your-nocobase-api-key>"\n)\n\nresponse = client.chat.completions.create(\n model="<service-name>/<model-id>",\n messages=[{"role": "user", "content": "Hello!"}]\n)'))),r().createElement(p,{level:5},"List Available Models"),r().createElement(f,null,r().createElement("pre",{style:{background:"#1a1a2e",color:"#e0e0e0",padding:16,borderRadius:8,fontSize:13,overflow:"auto"}},"curl ".concat(C,'/models \\\n -H "Authorization: Bearer <your-api-key>"')))))}t.default=y}}]);
@@ -1 +1,10 @@
1
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("@nocobase/client"),require("react"),require("antd")):"function"==typeof define&&define.amd?define("plugin-ai-api",["@nocobase/client","react","antd"],t):"object"==typeof exports?exports["plugin-ai-api"]=t(require("@nocobase/client"),require("react"),require("antd")):e["plugin-ai-api"]=t(e["@nocobase/client"],e.react,e.antd)}(self,function(e,t,n){return function(){var r,o,i,u,a,c,f={581:function(e){e.exports=function(e,t){return"undefined"!=typeof __deoptimization_sideEffect__&&__deoptimization_sideEffect__(e,t),t}},772:function(t){"use strict";t.exports=e},721:function(e){"use strict";e.exports=n},156:function(e){"use strict";e.exports=t}},l={};function p(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={exports:{}};return f[e](n,n.exports,p),n.exports}p.m=f,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"824c2f7487ee05fd.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)},r={},o="plugin-ai-api:",p.l=function(e,t,n,i){if(r[e])return void r[e].push(t);if(void 0!==n)for(var u,a,c=document.getElementsByTagName("script"),f=0;f<c.length;f++){var l=c[f];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){u=l;break}}u||(a=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,p.nc&&u.setAttribute("nonce",p.nc),u.setAttribute("data-webpack",o+n),u.src=e),r[e]=[t];var s=function(t,n){u.onerror=u.onload=null,clearTimeout(d);var o=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(function(e){return e(n)}),t)return t(n)},d=setTimeout(s.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=s.bind(null,u.onerror),u.onload=s.bind(null,u.onload),a&&document.head.appendChild(u)},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(i=window.__webpack_public_path__||"/").endsWith("/")||(i+="/"),p.p=i+"static/plugins/plugin-ai-api/dist/client/",u={909:0},p.f.j=function(e,t){var n=p.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 o=p.p+p.u(e),i=Error();p.l(o,function(t){if(p.o(u,e)&&(0!==(n=u[e])&&(u[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",i.name="ChunkLoadError",i.type=r,i.request=o,n[1](i)}},"chunk-"+e,e)}},a=function(e,t){var n,r,o=t[0],i=t[1],a=t[2],c=0;if(o.some(function(e){return 0!==u[e]})){for(n in i)p.o(i,n)&&(p.m[n]=i[n]);a&&a(p)}for(e&&e(t);c<o.length;c++)r=o[c],p.o(u,r)&&u[r]&&u[r][0](),u[r]=0},(c=self.webpackChunkplugin_ai_api=self.webpackChunkplugin_ai_api||[]).forEach(a.bind(null,0)),c.push=a.bind(null,c.push.bind(c));var s={};return!function(){"use strict";p.r(s),p.d(s,{default:function(){return d}});var e=p(772),t=p(156),n=p.n(t),r=p(581);function o(e,t,n,r,o,i,u){try{var a=e[i](u),c=a.value}catch(e){n(e);return}a.done?t(c):Promise.resolve(c).then(r,o)}function i(e,t,n){return(i=f()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var o=new(Function.bind.apply(e,r));return n&&a(o,n.prototype),o}).apply(null,arguments)}function u(e){return(u=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function a(e,t){return(a=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function c(e){var t="function"==typeof Map?new Map:void 0;return(c=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 i(e,arguments,u(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),a(n,e)})(e)}function f(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(f=function(){return!!e})()}var l=n().lazy(function(){return r("imported_c1deu3_component",p.e("503").then(p.bind(p,968)))}),d=function(e){var t;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function n(){var e,t,r,o,i;if(!(this instanceof n))throw TypeError("Cannot call a class as a function");return t=n,r=arguments,t=u(t),o=e=function(e,t){var n;if(t&&("object"==((n=t)&&"undefined"!=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,f()?Reflect.construct(t,r||[],u(this).constructor):t.apply(this,r)),i=function(e){return e},"t"in o?Object.defineProperty(o,"t",{value:i,enumerable:!0,configurable:!0,writable:!0}):o.t=i,e}return n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,writable:!0,configurable:!0}}),e&&a(n,e),t=[{key:"load",value:function(){var e,t=this;return(e=function(){return function(e,t){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){var c=[i,a];if(n)throw TypeError("Generator is already executing.");for(;u;)try{if(n=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return u.label++,{value:c[1],done:!1};case 5:u.label++,r=c[1],c=[0];continue;case 7:c=u.ops.pop(),u.trys.pop();continue;default:if(!(o=(o=u.trys).length>0&&o[o.length-1])&&(6===c[0]||2===c[0])){u=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){u.label=c[1];break}if(6===c[0]&&u.label<o[1]){u.label=o[1],o=c;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(c);break}o[2]&&u.ops.pop(),u.trys.pop();continue}c=t.call(e,u)}catch(e){c=[6,e],r=0}finally{n=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}(this,function(e){return t.app.pluginSettingsManager.add("ai-api",{icon:"ApiOutlined",title:t.t("AI API Gateway"),aclSnippet:"pm.plugin-ai-api.configuration"}),t.app.pluginSettingsManager.add("ai-api.config",{title:t.t("Configuration"),Component:l}),[2]})},function(){var t=this,n=arguments;return new Promise(function(r,i){var u=e.apply(t,n);function a(e){o(u,r,i,a,c,"next",e)}function c(e){o(u,r,i,a,c,"throw",e)}a(void 0)})})()}}],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)}}(n.prototype,t),n}(c(e.Plugin))}(),s}()});
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
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("@nocobase/plugin-acl/client"),require("@nocobase/client"),require("react"),require("antd")):"function"==typeof define&&define.amd?define("plugin-ai-api",["@nocobase/plugin-acl/client","@nocobase/client","react","antd"],t):"object"==typeof exports?exports["plugin-ai-api"]=t(require("@nocobase/plugin-acl/client"),require("@nocobase/client"),require("react"),require("antd")):e["plugin-ai-api"]=t(e["@nocobase/plugin-acl/client"],e["@nocobase/client"],e.react,e.antd)}(self,function(e,t,n,r){return function(){var o,i,u,a,c,l,f={581:function(e){e.exports=function(e,t){return"undefined"!=typeof __deoptimization_sideEffect__&&__deoptimization_sideEffect__(e,t),t}},772:function(e){"use strict";e.exports=t},465:function(t){"use strict";t.exports=e},721:function(e){"use strict";e.exports=r},156:function(e){"use strict";e.exports=n}},p={};function s(e){var t=p[e];if(void 0!==t)return t.exports;var n=p[e]={exports:{}};return f[e](n,n.exports,s),n.exports}s.m=f,s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,{a:t}),t},s.d=function(e,t){for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},s.f={},s.e=function(e){return Promise.all(Object.keys(s.f).reduce(function(t,n){return s.f[n](e,t),t},[]))},s.u=function(e){return""+e+"."+({23:"e96ecf13e6072dce",503:"29bcdb426b01e715"})[e]+".js"},s.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o={},i="plugin-ai-api:",s.l=function(e,t,n,r){if(o[e])return void o[e].push(t);if(void 0!==n)for(var u,a,c=document.getElementsByTagName("script"),l=0;l<c.length;l++){var f=c[l];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==i+n){u=f;break}}u||(a=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,s.nc&&u.setAttribute("nonce",s.nc),u.setAttribute("data-webpack",i+n),u.src=e),o[e]=[t];var p=function(t,n){u.onerror=u.onload=null,clearTimeout(d);var r=o[e];if(delete o[e],u.parentNode&&u.parentNode.removeChild(u),r&&r.forEach(function(e){return e(n)}),t)return t(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=p.bind(null,u.onerror),u.onload=p.bind(null,u.onload),a&&document.head.appendChild(u)},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(u=window.__webpack_public_path__||"/").endsWith("/")||(u+="/"),s.p=u+"static/plugins/plugin-ai-api/dist/client/",a={909:0},s.f.j=function(e,t){var n=s.o(a,e)?a[e]:void 0;if(0!==n)if(n)t.push(n[2]);else{var r=new Promise(function(t,r){n=a[e]=[t,r]});t.push(n[2]=r);var o=s.p+s.u(e),i=Error();s.l(o,function(t){if(s.o(a,e)&&(0!==(n=a[e])&&(a[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",i.name="ChunkLoadError",i.type=r,i.request=o,n[1](i)}},"chunk-"+e,e)}},c=function(e,t){var n,r,o=t[0],i=t[1],u=t[2],c=0;if(o.some(function(e){return 0!==a[e]})){for(n in i)s.o(i,n)&&(s.m[n]=i[n]);u&&u(s)}for(e&&e(t);c<o.length;c++)r=o[c],s.o(a,r)&&a[r]&&a[r][0](),a[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 d={};return!function(){"use strict";s.r(d),s.d(d,{default:function(){return h}});var e=s(772),t=s(465),n=s.n(t),r=s(156),o=s.n(r),i=s(581);function u(e,t,n,r,o,i,u){try{var a=e[i](u),c=a.value}catch(e){n(e);return}a.done?t(c):Promise.resolve(c).then(r,o)}function a(e,t,n){return(a=p()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var o=new(Function.bind.apply(e,r));return n&&l(o,n.prototype),o}).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 f(e){var t="function"==typeof Map?new Map:void 0;return(f=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 a(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 p(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(p=function(){return!!e})()}var b=o().lazy(function(){return i("imported_c1deu3_component",s.e("503").then(s.bind(s,968)))}),y=(0,e.lazy)(function(){return i("imported_-1fpkg57_component",s.e("23").then(s.bind(s,928)))},"AiApiRolePermissions").AiApiRolePermissions,h=function(e){var t;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function r(){var e,t,n,o,i;if(!(this instanceof r))throw TypeError("Cannot call a class as a function");return t=r,n=arguments,t=c(t),o=e=function(e,t){var n;if(t&&("object"==((n=t)&&"undefined"!=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,p()?Reflect.construct(t,n||[],c(this).constructor):t.apply(this,n)),i=function(e){return e},"t"in o?Object.defineProperty(o,"t",{value:i,enumerable:!0,configurable:!0,writable:!0}):o.t=i,e}return r.prototype=Object.create(e&&e.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),e&&l(r,e),t=[{key:"load",value:function(){var e,t=this;return(e=function(){var e;return function(e,t){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){var c=[i,a];if(n)throw TypeError("Generator is already executing.");for(;u;)try{if(n=1,r&&(o=2&c[0]?r.return:c[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,c[1])).done)return o;switch(r=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return u.label++,{value:c[1],done:!1};case 5:u.label++,r=c[1],c=[0];continue;case 7:c=u.ops.pop(),u.trys.pop();continue;default:if(!(o=(o=u.trys).length>0&&o[o.length-1])&&(6===c[0]||2===c[0])){u=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){u.label=c[1];break}if(6===c[0]&&u.label<o[1]){u.label=o[1],o=c;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(c);break}o[2]&&u.ops.pop(),u.trys.pop();continue}c=t.call(e,u)}catch(e){c=[6,e],r=0}finally{n=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}}}(this,function(r){return t.app.pluginSettingsManager.add("ai-api",{icon:"ApiOutlined",title:t.t("AI API Gateway"),aclSnippet:"pm.plugin-ai-api.configuration"}),t.app.pluginSettingsManager.add("ai-api.config",{title:t.t("Configuration"),Component:b}),(null==(e=t.app.pm.get(n()))?void 0:e.settingsUI)&&e.settingsUI.addPermissionsTab(function(e){e.t;var t=e.TabLayout,n=e.activeRole;return{key:"aiApi",label:"AI API",sort:25,children:o().createElement(t,null,o().createElement(y,{role:n}))}}),[2]})},function(){var t=this,n=arguments;return new Promise(function(r,o){var i=e.apply(t,n);function a(e){u(i,r,o,a,c,"next",e)}function c(e){u(i,r,o,a,c,"throw",e)}a(void 0)})})()}}],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)}}(r.prototype,t),r}(f(e.Plugin))}(),d}()});
@@ -1,11 +1,22 @@
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
+
1
10
  module.exports = {
2
- "react": "18.3.1",
11
+ "react": "18.2.0",
3
12
  "antd": "5.24.2",
4
- "@nocobase/client": "2.0.20",
5
- "@nocobase/flow-engine": "2.0.20",
6
- "@nocobase/server": "2.0.20",
7
- "@nocobase/database": "2.0.20",
8
- "@nocobase/actions": "2.0.20",
9
- "@nocobase/resourcer": "2.0.20",
10
- "@nocobase/plugin-ai": "2.0.20"
13
+ "@nocobase/client": "2.0.38",
14
+ "@nocobase/flow-engine": "2.0.38",
15
+ "@nocobase/plugin-acl": "2.0.38",
16
+ "@nocobase/server": "2.0.38",
17
+ "dayjs": "1.11.13",
18
+ "@nocobase/database": "2.0.38",
19
+ "@nocobase/actions": "2.0.38",
20
+ "@nocobase/resourcer": "2.0.38",
21
+ "@nocobase/plugin-ai": "2.0.38"
11
22
  };
package/dist/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __create = Object.create;
2
11
  var __defProp = Object.defineProperty;
3
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,10 +1,10 @@
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
- "Failed to save configuration": "Failed to save configuration"
10
- }
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
+ "Failed to save configuration": "Failed to save configuration"
10
+ }
@@ -1,10 +1,10 @@
1
- {
2
- "AI API Gateway": "AI API 网关",
3
- "Configuration": "配置",
4
- "Default AI Employee": "默认 AI 员工",
5
- "Enabled LLM Services": "已启用 LLM 服务",
6
- "Rate Limit": "速率限制",
7
- "Save Configuration": "保存配置",
8
- "Configuration saved": "配置已保存",
9
- "Failed to save configuration": "保存配置失败"
10
- }
1
+ {
2
+ "AI API Gateway": "AI API 网关",
3
+ "Configuration": "配置",
4
+ "Default AI Employee": "默认 AI 员工",
5
+ "Enabled LLM Services": "已启用 LLM 服务",
6
+ "Rate Limit": "速率限制",
7
+ "Save Configuration": "保存配置",
8
+ "Configuration saved": "配置已保存",
9
+ "Failed to save configuration": "保存配置失败"
10
+ }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __defProp = Object.defineProperty;
2
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -0,0 +1,62 @@
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_role_permissions_exports = {};
28
+ __export(ai_api_role_permissions_exports, {
29
+ default: () => ai_api_role_permissions_default
30
+ });
31
+ module.exports = __toCommonJS(ai_api_role_permissions_exports);
32
+ var import_database = require("@nocobase/database");
33
+ var ai_api_role_permissions_default = (0, import_database.defineCollection)({
34
+ name: "aiApiRolePermissions",
35
+ autoGenId: true,
36
+ fields: [
37
+ {
38
+ name: "roleName",
39
+ type: "string",
40
+ unique: true,
41
+ comment: "Role name (links to roles.name)"
42
+ },
43
+ {
44
+ name: "enabled",
45
+ type: "boolean",
46
+ defaultValue: false,
47
+ comment: "Whether this role can use the AI API at all"
48
+ },
49
+ {
50
+ name: "allowAllEmployees",
51
+ type: "boolean",
52
+ defaultValue: true,
53
+ comment: "If true, the role may use any AI Employee. If false, only those in allowedEmployees."
54
+ },
55
+ {
56
+ name: "allowedEmployees",
57
+ type: "json",
58
+ defaultValue: [],
59
+ comment: "Array of AI Employee usernames this role is allowed to use (when allowAllEmployees=false)"
60
+ }
61
+ ]
62
+ });
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __create = Object.create;
2
11
  var __defProp = Object.defineProperty;
3
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __defProp = Object.defineProperty;
2
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var role_permission_exports = {};
28
+ __export(role_permission_exports, {
29
+ checkEmployeeAccess: () => checkEmployeeAccess,
30
+ checkRolePermission: () => checkRolePermission
31
+ });
32
+ module.exports = __toCommonJS(role_permission_exports);
33
+ var import_openai_format = require("../utils/openai-format");
34
+ async function checkRolePermission(ctx) {
35
+ var _a;
36
+ const roleName = ((_a = ctx.state.currentRoles) == null ? void 0 : _a[0]) || "member";
37
+ if (roleName === "root" || roleName === "admin") {
38
+ return true;
39
+ }
40
+ const record = await ctx.db.getRepository("aiApiRolePermissions").findOne({
41
+ filter: { roleName }
42
+ });
43
+ if (!(record == null ? void 0 : record.enabled)) {
44
+ ctx.status = 403;
45
+ ctx.body = (0, import_openai_format.toOpenAIError)(
46
+ 403,
47
+ `Role '${roleName}' is not authorized to use the AI API. An admin must enable access in Settings \u2192 Users & Permissions \u2192 [Role] \u2192 AI API.`,
48
+ "permission_denied",
49
+ "role_not_permitted"
50
+ );
51
+ return false;
52
+ }
53
+ ctx.state.aiApiRolePermission = record;
54
+ return true;
55
+ }
56
+ function checkEmployeeAccess(ctx, employeeUsername) {
57
+ const perm = ctx.state.aiApiRolePermission;
58
+ if (!perm) return true;
59
+ if (perm.allowAllEmployees) return true;
60
+ return (perm.allowedEmployees || []).includes(employeeUsername);
61
+ }
62
+ // Annotate the CommonJS export names for ESM import in node:
63
+ 0 && (module.exports = {
64
+ checkEmployeeAccess,
65
+ checkRolePermission
66
+ });
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __create = Object.create;
2
11
  var __defProp = Object.defineProperty;
3
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -35,6 +44,11 @@ var import_server = require("@nocobase/server");
35
44
  var import_router = require("./routes/router");
36
45
  var import_ai_api_config = __toESM(require("./resource/ai-api-config"));
37
46
  var import_rate_limiter = require("./utils/rate-limiter");
47
+ var import_dayjs = __toESM(require("dayjs"));
48
+ var import_utc = __toESM(require("dayjs/plugin/utc"));
49
+ var import_timezone = __toESM(require("dayjs/plugin/timezone"));
50
+ import_dayjs.default.extend(import_utc.default);
51
+ import_dayjs.default.extend(import_timezone.default);
38
52
  class PluginAiApiServer extends import_server.Plugin {
39
53
  /**
40
54
  * Singleton rate limiter — lives for the entire plugin lifetime, shared across all requests.
@@ -51,7 +65,7 @@ class PluginAiApiServer extends import_server.Plugin {
51
65
  this.app.resourceManager.define(import_ai_api_config.default);
52
66
  this.app.acl.registerSnippet({
53
67
  name: `pm.${this.name}.configuration`,
54
- actions: ["aiApiConfig:*"]
68
+ actions: ["aiApiConfig:*", "aiApiRolePermissions:*"]
55
69
  });
56
70
  this.gcInterval = setInterval(() => this.rateLimiter.gc(), 5 * 60 * 1e3);
57
71
  this.gcInterval.unref();
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __defProp = Object.defineProperty;
2
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -41,7 +50,7 @@ const aiApiConfigResource = {
41
50
  await next();
42
51
  },
43
52
  async save(ctx, next) {
44
- const values = ctx.action.params.values || {};
53
+ const values = ctx.action.params.values || ctx.request.body || {};
45
54
  const repo = ctx.db.getRepository("aiApiConfig");
46
55
  let config = await repo.findOne();
47
56
  if (!config) {
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __create = Object.create;
2
11
  var __defProp = Object.defineProperty;
3
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -32,6 +41,7 @@ __export(agent_completions_exports, {
32
41
  module.exports = __toCommonJS(agent_completions_exports);
33
42
  var import_openai_format = require("../utils/openai-format");
34
43
  var import_resolve_service = require("../utils/resolve-service");
44
+ var import_role_permission = require("../middleware/role-permission");
35
45
  async function handleAgentCompletions(ctx, plugin) {
36
46
  var _a;
37
47
  const body = ctx.request.body;
@@ -56,7 +66,8 @@ async function handleAgentCompletions(ctx, plugin) {
56
66
  return;
57
67
  }
58
68
  const config = await ctx.db.getRepository("aiApiConfig").findOne();
59
- if (!(config == null ? void 0 : config.defaultAiEmployee)) {
69
+ const defaultAiEmployee = config ? config.get("defaultAiEmployee") || config.defaultAiEmployee : null;
70
+ if (!defaultAiEmployee) {
60
71
  ctx.status = 400;
61
72
  ctx.body = (0, import_openai_format.toOpenAIError)(
62
73
  400,
@@ -83,7 +94,17 @@ async function handleAgentCompletions(ctx, plugin) {
83
94
  ctx.body = (0, import_openai_format.toOpenAIError)(404, "LLM service is disabled", "invalid_request_error", "model_not_found");
84
95
  return;
85
96
  }
86
- const employeeUsername = config.defaultAiEmployee;
97
+ const employeeUsername = defaultAiEmployee;
98
+ if (!(0, import_role_permission.checkEmployeeAccess)(ctx, employeeUsername)) {
99
+ ctx.status = 403;
100
+ ctx.body = (0, import_openai_format.toOpenAIError)(
101
+ 403,
102
+ `Role is not permitted to use AI Employee '${employeeUsername}'. An admin must grant access in Settings \u2192 Users & Permissions \u2192 [Role] \u2192 AI API.`,
103
+ "permission_denied",
104
+ "employee_not_permitted"
105
+ );
106
+ return;
107
+ }
87
108
  const wantStream = body.stream === true;
88
109
  try {
89
110
  const employeeRecord = await ctx.db.getRepository("aiEmployees").findOne({
@@ -122,20 +143,33 @@ async function handleAgentCompletions(ctx, plugin) {
122
143
  try {
123
144
  const mod = await import(
124
145
  /* webpackIgnore: true */
125
- "@nocobase/plugin-ai/src/server/ai-employees/ai-employee"
146
+ "@nocobase/plugin-ai/dist/server/ai-employees/ai-employee.js"
126
147
  );
127
148
  AIEmployee = mod.AIEmployee;
128
149
  } catch (importErr) {
129
- ctx.log.error("AI API: Failed to import AIEmployee from plugin-ai. Ensure plugin-ai is installed and built.", importErr);
150
+ ctx.log.error(
151
+ "AI API: Failed to import AIEmployee from plugin-ai. Ensure plugin-ai is installed and built.",
152
+ importErr
153
+ );
130
154
  ctx.status = 500;
131
155
  ctx.body = (0, import_openai_format.toOpenAIError)(500, "Agent mode unavailable: plugin-ai not found or not built", "server_error");
132
156
  return;
133
157
  }
134
158
  if (!AIEmployee) {
135
159
  ctx.status = 500;
136
- ctx.body = (0, import_openai_format.toOpenAIError)(500, "Agent mode unavailable: AIEmployee class not exported by plugin-ai", "server_error");
160
+ ctx.body = (0, import_openai_format.toOpenAIError)(
161
+ 500,
162
+ "Agent mode unavailable: AIEmployee class not exported by plugin-ai",
163
+ "server_error"
164
+ );
137
165
  return;
138
166
  }
167
+ if (!ctx.get("x-timezone")) {
168
+ ctx.req.headers["x-timezone"] = "UTC";
169
+ }
170
+ if (!ctx.get("x-locale")) {
171
+ ctx.req.headers["x-locale"] = "en-US";
172
+ }
139
173
  if (wantStream) {
140
174
  ctx.set({
141
175
  "Content-Type": "text/event-stream",
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __defProp = Object.defineProperty;
2
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -58,22 +67,12 @@ async function authenticateBearer(ctx) {
58
67
  decoded = await jwt.decode(token);
59
68
  } catch (e) {
60
69
  ctx.status = 401;
61
- ctx.body = (0, import_openai_format.toOpenAIError)(
62
- 401,
63
- "Incorrect API key provided",
64
- "invalid_request_error",
65
- "invalid_api_key"
66
- );
70
+ ctx.body = (0, import_openai_format.toOpenAIError)(401, "Incorrect API key provided", "invalid_request_error", "invalid_api_key");
67
71
  return false;
68
72
  }
69
73
  if (!decoded || !decoded.userId) {
70
74
  ctx.status = 401;
71
- ctx.body = (0, import_openai_format.toOpenAIError)(
72
- 401,
73
- "Invalid or expired API key",
74
- "invalid_request_error",
75
- "invalid_api_key"
76
- );
75
+ ctx.body = (0, import_openai_format.toOpenAIError)(401, "Invalid or expired API key", "invalid_request_error", "invalid_api_key");
77
76
  return false;
78
77
  }
79
78
  const user = await ctx.db.getRepository("users").findOne({
@@ -1,3 +1,12 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
1
10
  var __defProp = Object.defineProperty;
2
11
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -22,6 +31,7 @@ __export(chat_completions_exports, {
22
31
  module.exports = __toCommonJS(chat_completions_exports);
23
32
  var import_openai_format = require("../utils/openai-format");
24
33
  var import_resolve_service = require("../utils/resolve-service");
34
+ var import_role_permission = require("../middleware/role-permission");
25
35
  async function handleChatCompletions(ctx, plugin) {
26
36
  var _a;
27
37
  const body = ctx.request.body;
@@ -67,16 +77,19 @@ async function handleChatCompletions(ctx, plugin) {
67
77
  }
68
78
  if (service.enabled === false) {
69
79
  ctx.status = 404;
70
- ctx.body = (0, import_openai_format.toOpenAIError)(404, `LLM service '${service.title || service.name}' is disabled`, "invalid_request_error", "model_not_found");
80
+ ctx.body = (0, import_openai_format.toOpenAIError)(
81
+ 404,
82
+ `LLM service '${service.title || service.name}' is disabled`,
83
+ "invalid_request_error",
84
+ "model_not_found"
85
+ );
71
86
  return;
72
87
  }
73
88
  const config = await ctx.db.getRepository("aiApiConfig").findOne();
74
89
  if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
75
90
  const serviceName = service.name;
76
91
  const serviceTitle = service.title;
77
- const isAllowed = config.enabledLlmServices.some(
78
- (s) => s === serviceName || s === serviceTitle
79
- );
92
+ const isAllowed = config.enabledLlmServices.some((s) => s === serviceName || s === serviceTitle);
80
93
  if (!isAllowed) {
81
94
  ctx.status = 403;
82
95
  ctx.body = (0, import_openai_format.toOpenAIError)(
@@ -112,6 +125,16 @@ async function handleChatCompletions(ctx, plugin) {
112
125
  });
113
126
  let systemPrompt = "";
114
127
  if (config == null ? void 0 : config.defaultAiEmployee) {
128
+ if (!(0, import_role_permission.checkEmployeeAccess)(ctx, config.defaultAiEmployee)) {
129
+ ctx.status = 403;
130
+ ctx.body = (0, import_openai_format.toOpenAIError)(
131
+ 403,
132
+ `Role is not permitted to use AI Employee '${config.defaultAiEmployee}'. An admin must grant access in Settings \u2192 Users & Permissions \u2192 [Role] \u2192 AI API.`,
133
+ "permission_denied",
134
+ "employee_not_permitted"
135
+ );
136
+ return;
137
+ }
115
138
  const employee = await ctx.db.getRepository("aiEmployees").findOne({
116
139
  filter: { username: config.defaultAiEmployee }
117
140
  });