├── .env.postgres ├── .env.sample ├── .env.sqlite ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── types │ ├── auth.d.ts │ ├── email.d.ts │ ├── io.d.ts │ ├── neo.d.ts │ ├── odata.d.ts │ ├── request.d.ts │ ├── types.d.ts │ └── yao.d.ts ├── LICENSE ├── aigcs ├── summary.yml ├── title.yml └── translate.yml ├── apis ├── ai.http.yao ├── chat.http.yao ├── file.http.yao ├── neo.http.yao ├── proxy.http.json ├── test.http.yao ├── utils.http.json └── v1.http.yao ├── app.json ├── assistants └── neo │ ├── package.yao │ ├── prompts.yml │ └── src │ └── index.ts ├── charts └── ai │ └── chat.chart.json ├── components ├── components │ └── view │ │ ├── chat.less │ │ └── chat.tsx ├── package.json ├── pnpm-lock.yaml ├── readme.md ├── rollup.build.css ├── rollup.build.ts ├── rollup.common.ts ├── rollup.dev.ts ├── tsconfig.json └── typings │ └── index.d.ts ├── connectors ├── bailian-ds-r1-70b.conn.yao ├── copilot-tencent.conn.yao ├── doubao.conn.yao ├── gpt-3_5-turbo.conn.yao ├── text-embedding-ada-002.conn.yao ├── text-embedding-doubao.conn.yao └── text-embedding-llamacpp.conn.yao ├── dashboards └── ai.dash.json ├── db └── dummy ├── docs ├── ai-convesation.png ├── api paramters-zh_CN.md ├── api paramters.md ├── stop_sequence.md └── yao-xgen-neo-assistant.png ├── flows ├── chat │ └── prompt_template.flow.json └── utils │ └── export.flow.json ├── forms ├── admin │ ├── menu.form.json │ └── user.form.json ├── ai │ ├── chatlog.form.json │ ├── model.form.json │ ├── permission.form.json │ └── setting.form.json ├── chat │ ├── chat.form.json │ ├── conversation.form.json │ ├── conversation_simple.form.json │ ├── message.form.json │ ├── message_simple.form.json │ ├── prompttemplate.form.json │ └── sensitive_word.form.json └── doc │ ├── file.form.yao │ ├── file_view.form.yao │ ├── vector.form.yao │ └── vector_view.form.yao ├── front-end ├── .gitignore ├── .vscode │ └── extensions.json ├── README.md ├── deplay.sh ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── vite.svg ├── src │ ├── App.vue │ ├── assets │ │ ├── style.css │ │ └── talk.css │ ├── chatpgt.ts │ ├── main.ts │ ├── plugin.ts │ ├── router │ │ └── index.ts │ ├── views │ │ ├── Talk.vue │ │ ├── Talk2.vue │ │ ├── Talk3.vue │ │ └── Talk4.vue │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── icons ├── 404.png ├── app.icns ├── app.ico └── app.png ├── langs ├── en │ └── global.yml └── zh-cn │ └── global.yml ├── lists ├── doc │ └── vector.list.yao └── permission.list.json ├── logins ├── admin.login.json └── user.login.json ├── models ├── admin │ ├── menu.mod.json │ └── user.mod.json ├── ai │ ├── chatlog.mod.json │ ├── model.mod.json │ ├── permission.mod.json │ └── setting.mod.yao ├── chat │ ├── conversation.mod.yao │ ├── message.mod.yao │ ├── prompttemplate.mod.json │ └── sensitive_word.mod.json └── doc │ ├── file.mod.yao │ └── vector.mod.yao ├── neo └── neo.yml ├── package.json ├── plugins └── httpx │ ├── .env.sample │ ├── .gitignore │ ├── .vscode │ └── launch.json │ ├── Makefile │ ├── go.mod │ ├── go.sum │ ├── httpx │ ├── client.go │ ├── process.go │ └── types.go │ ├── main.go │ └── readme.md ├── public ├── assets │ ├── Talk-f9212dc8.js │ ├── Talk2-800b822a.js │ ├── index-63368c72.js │ ├── index-71ed385e.css │ └── talk-53fef37d.css ├── chat1 │ └── index.html ├── chat2 │ ├── index.html │ └── jquery.min.js ├── components │ ├── chat.less.js │ └── view │ │ └── chat.js ├── index.html ├── upload.html └── yao.js ├── readme.md ├── scripts ├── adapter │ ├── aardio.js │ └── tencent.js ├── ai │ ├── bot.js │ ├── bot.md │ ├── bot_headers.js │ ├── chatgpt.js │ ├── chatgpt_chat.js │ ├── chatgpt_complete.js │ ├── chatgpt_stream.js │ ├── chatweb_lib.js │ ├── dashboard.js │ ├── deepseek.js │ ├── gemini.js │ ├── md5.js │ ├── model.js │ ├── stat.js │ ├── tencent.js │ └── yuanbao.js ├── auth │ └── token.js ├── chat │ └── conversation.js ├── chatweb.js ├── doc │ ├── file.js │ ├── task.js │ ├── test │ │ ├── local.js │ │ ├── openai.js │ │ └── test.js │ └── vector.js ├── jsproxy.js ├── lib │ └── web.ts ├── loader │ ├── prompt_template.js │ └── sensitive_word.js ├── menu │ ├── menu.js │ └── version.js ├── neo.js ├── neo │ └── neo.ts ├── remote.js ├── security.js ├── stat.js ├── sub │ └── demo.js ├── test │ ├── chatgpt2.js │ ├── chatgpt3.js │ ├── demo.js │ └── test.js ├── text.js ├── utils │ ├── array.js │ ├── test.js │ └── user.js └── xgen │ ├── compute │ └── json.js │ └── file │ └── upload.js ├── stores └── cache.lru.json ├── studio └── init.js ├── tables ├── admin │ ├── menu.tab.json │ └── user.tab.json ├── ai │ ├── chatlog.tab.json │ ├── chatlog2.tab.json │ ├── model.tab.json │ ├── permission.tab.json │ └── setting.tab.json ├── chat │ ├── conversation.tab.json │ ├── conversation_simple.tab.json │ ├── message.tab.json │ ├── message_simple.tab.json │ ├── prompttemplate.tab.json │ └── sensitive_word.tab.json └── doc │ ├── file.tab.yao │ └── vector.tab.yao ├── tasks └── doc.task.yao ├── tsconfig.json └── yarn.lock /.env.postgres: -------------------------------------------------------------------------------- 1 | YAO_DB_DRIVER="postgres" 2 | YAO_DB_PRIMARY="postgres://user:password@127.0.0.1:5432/quant?sslmode=disable" 3 | YAO_ENV="development" 4 | YAO_HOST="0.0.0.0" 5 | YAO_LANG="zh-cn" 6 | YAO_LOG="logs/application.log" 7 | YAO_LOG_MODE="TEXT" 8 | YAO_PORT="5099" 9 | YAO_SESSION_FILE="db/.session" 10 | YAO_SESSION_STORE="file" 11 | YAO_STUDIO_PORT="5077" 12 | 13 | REMOTE_DEBUG_SERVER="http://localhost:8082/api/proxy/call" 14 | # YAO_DB_DRIVER=mysql 15 | # YAO_DB_PRIMARY="root:123456@tcp(172.18.3.234:33306)/yao_ai?charset=utf8mb4&parseTime=True&loc=Local" 16 | ACCESS_KEY="123456" 17 | 18 | 19 | #HTTP_RROXY="socks://127.0.0.1:10808" 20 | #HTTPS_PROXY="socks://127.0.0.1:10808" 21 | #本地调试服务器接口密钥 22 | YAO_API_ACCESS_KEY="11223365" 23 | OPENAI_KEY= 24 | YAO_CHAT_API_KEY=chatweb 25 | GOOGLE_GEMINI_API_HOST=https://generativelanguage.googleapis.com 26 | GOOGLE_GEMINI_API_ACCESS_KEY=AIzaSyAX- 27 | 28 | 29 | DEEPSEEK_KEY= 30 | 31 | DEEPSEKK_POW= 32 | 33 | DOUBAO_KEY= 34 | 35 | BAILIAN_KEY= 36 | 37 | BOT.HOST="https://bot.n.cn" 38 | BOT.Q= 39 | BOT.T= 40 | 41 | YUANBAO.HOST="https://yuanbao.tencent.com" 42 | YUANBAO.USER= 43 | YUANBAO.TOKEN= 44 | 45 | COPILOT.TENCENT_HOST="https://copilot.tencent.com/v2" 46 | COPILOT.TENCENT_TOKEN= -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | YAO_DB_DRIVER="sqlite3" 2 | YAO_DB_PRIMARY="db/yao.db" 3 | YAO_ENV="development" 4 | YAO_HOST="0.0.0.0" 5 | YAO_LANG="zh-cn" 6 | YAO_LOG="logs/application.log" 7 | YAO_LOG_MODE="TEXT" 8 | YAO_PORT="5099" 9 | YAO_SESSION_FILE="db/.session" 10 | YAO_SESSION_STORE="file" 11 | YAO_STUDIO_PORT="5077" 12 | 13 | REMOTE_DEBUG_SERVER="http://localhost:8082/api/proxy/call" 14 | # YAO_DB_DRIVER=mysql 15 | # YAO_DB_PRIMARY="root:123456@tcp(172.18.3.234:33306)/yao_ai?charset=utf8mb4&parseTime=True&loc=Local" 16 | ACCESS_KEY="123456" 17 | 18 | 19 | #HTTP_RROXY="socks://127.0.0.1:10808" 20 | #HTTPS_PROXY="socks://127.0.0.1:10808" 21 | #本地调试服务器接口密钥 22 | YAO_API_ACCESS_KEY="11223365" 23 | OPENAI_KEY= 24 | YAO_CHAT_API_KEY=chatweb 25 | GOOGLE_GEMINI_API_HOST=https://generativelanguage.googleapis.com 26 | GOOGLE_GEMINI_API_ACCESS_KEY=AIzaSyAX- -------------------------------------------------------------------------------- /.env.sqlite: -------------------------------------------------------------------------------- 1 | YAO_DB_DRIVER="sqlite3" 2 | YAO_DB_PRIMARY="db/yao.db" 3 | YAO_ENV="development" 4 | YAO_HOST="0.0.0.0" 5 | YAO_LANG="zh-cn" 6 | YAO_LOG="logs/application.log" 7 | YAO_LOG_MODE="TEXT" 8 | YAO_PORT="5099" 9 | YAO_SESSION_FILE="db/.session" 10 | YAO_SESSION_STORE="file" 11 | YAO_STUDIO_PORT="5077" 12 | 13 | REMOTE_DEBUG_SERVER="http://localhost:8082/api/proxy/call" 14 | # YAO_DB_DRIVER=mysql 15 | # YAO_DB_PRIMARY="root:123456@tcp(172.18.3.234:33306)/yao_ai?charset=utf8mb4&parseTime=True&loc=Local" 16 | ACCESS_KEY="123456" 17 | 18 | 19 | #HTTP_RROXY="socks://127.0.0.1:10808" 20 | #HTTPS_PROXY="socks://127.0.0.1:10808" 21 | #本地调试服务器接口密钥 22 | YAO_API_ACCESS_KEY="11223365" 23 | OPENAI_KEY=sk- 24 | OPENAI_KEY2=sess- 25 | YAO_CHAT_API_KEY=chatweb 26 | GOOGLE_GEMINI_API_HOST=https://generativelanguage.googleapis.com 27 | GOOGLE_GEMINI_API_ACCESS_KEY=AIzaSyAX- -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | db/*.db 3 | db/*.session 4 | temp.json 5 | node_modules 6 | data 7 | dist 8 | plugins/*.so 9 | .env 10 | venv 11 | public/web 12 | __pycache__ 13 | *.log 14 | plugins/*.dll 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${file}", 15 | "cwd": "${workspaceFolder}" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/types/auth.d.ts: -------------------------------------------------------------------------------- 1 | export interface PermissionFolder { 2 | operation: string[]; 3 | folderValue?: string; 4 | folder?: { 5 | label: string; 6 | value: string; 7 | chidren: PermissionFolder[]; 8 | }; 9 | } 10 | export interface PermissionRoute { 11 | operation: string[]; 12 | route: string; 13 | } 14 | export interface PermissionModel { 15 | operation: string[]; 16 | model: string; 17 | } 18 | 19 | export interface Permission { 20 | id: number; 21 | name: string; 22 | desc: string; 23 | parent: number; 24 | routes?: PermissionRoute[]; 25 | models?: PermissionModel[]; 26 | folders?: PermissionFolder[]; 27 | } 28 | 29 | export interface AuthMapObj { 30 | [key: string]: any; 31 | } 32 | 33 | export interface AuthObject { 34 | api: AuthRoute; 35 | menus: number[]; 36 | folder: AuthFolder; 37 | model: AuthModel; 38 | } 39 | export interface AuthFolder { 40 | folder_list: PermissionFolder[]; 41 | folder_with_method: AuthMapObj; 42 | method_with_folder: AuthMapObj; 43 | } 44 | export interface AuthModel { 45 | model_list: PermissionModel[]; 46 | model_with_method: AuthMapObj; 47 | method_with_model: AuthMapObj; 48 | } 49 | export interface AuthRoute { 50 | api_list: PermissionRoute[]; 51 | api_with_method: AuthMapObj; 52 | method_with_api: AuthMapObj; 53 | } 54 | -------------------------------------------------------------------------------- /.vscode/types/email.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Model=> app.email.account (邮件账号配置) 3 | * 4 | * Table=> app_email_account (邮件服务器账号配置表) 5 | */ 6 | export interface app_email_account { 7 | /**账户类型 */ 8 | category?: 'receive' | 'send'; 9 | /**服务器地址 */ 10 | server?: string; 11 | /**端口 */ 12 | port?: number; 13 | /**用户名 */ 14 | username: string; 15 | /**协议类型 */ 16 | type: 'smtp' | 'imap'; 17 | /**密码 */ 18 | password: string; 19 | } 20 | 21 | export interface EmailAddress { 22 | Name?: string; 23 | Address: string; 24 | } 25 | export interface EmailMessage { 26 | from: string; 27 | to: EmailAddress[]; 28 | cc?: EmailAddress[]; 29 | subject: string; 30 | body: string; 31 | /** attachment file path */ 32 | attachments?: string[]; 33 | } 34 | export interface EmailAccount { 35 | server: string; 36 | /** email server port */ 37 | port: number; 38 | username: string; 39 | password: string; 40 | /** stmp for send email,imap for receive email*/ 41 | type: 'stmp' | 'imap'; 42 | } 43 | /** 44 | * Email Message for email plugin 45 | */ 46 | export interface EmailConfig { 47 | /** email server config */ 48 | account: EmailAccount; 49 | /** messages for send */ 50 | messages?: EmailMessage[]; 51 | folder?: string; 52 | } 53 | 54 | /** 55 | * Model=> app.email.message (邮件信息) 56 | * 57 | * Table=> app_email_message (邮件发送与接收信息) 58 | */ 59 | export interface app_email_message { 60 | id: number; 61 | /**收发类型 */ 62 | type?: string; 63 | /**发件人 */ 64 | sender: string | object; 65 | /**收件人 */ 66 | receiver: string | object; 67 | /**抄送者 */ 68 | cc?: string | object; 69 | /**主题 */ 70 | subject: string; 71 | /**内容 */ 72 | content?: string; 73 | /**纯文本 */ 74 | plain_text?: string; 75 | /**状态 */ 76 | status?: 'sent' | 'received' | 'failed'; 77 | /**发送时间 */ 78 | send_at?: Date | string; 79 | /**接收时间 */ 80 | received_at?: Date | string; 81 | /**邮件发出时间 */ 82 | date?: string; 83 | /**邮件错误 */ 84 | error?: string; 85 | /**邮件唯一ID */ 86 | message_id?: string; 87 | /**用户唯一ID */ 88 | uid?: number; 89 | /**发送日志 */ 90 | message?: string; 91 | /**附件目录 */ 92 | attachment_folder?: string; 93 | /**附件列表 */ 94 | attachments?: string[]; 95 | /**附件明细 */ 96 | attachment_details?: AttachmentDetail[]; 97 | } 98 | export interface AttachmentDetail { 99 | content_type: string; 100 | saved_file_name: string; 101 | category: string; 102 | filename: string; 103 | encoding: string; 104 | download_url: string; 105 | } 106 | 107 | interface BodyPart { 108 | content_type: string; 109 | content_type_value: { [key: string]: string }; 110 | disposition: string; 111 | disposition_value: { [key: string]: string }; 112 | charset: string; 113 | file_name: string; 114 | file_size: number; 115 | saved_file_name: string; 116 | saved_file_path: string; 117 | encoding: string; 118 | content_id: string; 119 | centent: string; // Assuming 'centent' is correct, as in Go code 120 | attachment: string; 121 | } 122 | 123 | interface MailAddress { 124 | Name: string; 125 | Address: string; 126 | } 127 | 128 | export interface MessageReceived { 129 | from: MailAddress[]; 130 | to: MailAddress[]; 131 | /**主题 */ 132 | subject: string; 133 | body: BodyPart[]; 134 | attachments?: string[]; // Optional field 135 | date: string; // Use JavaScript's Date object for timestamps 136 | error: string; 137 | message_id: string; 138 | folder: string; 139 | uid: number; // For unsigned 32-bit int, TypeScript's 'number' will suffice 140 | } 141 | 142 | export interface EmailPluginResponse { 143 | code: number; 144 | message?: string; 145 | emails?: MessageReceived[]; 146 | } 147 | -------------------------------------------------------------------------------- /.vscode/types/io.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Namespace defining I/O-related types and interfaces. 3 | */ 4 | export declare namespace io { 5 | /** 6 | * Represents a generic writer interface. 7 | */ 8 | export type Writer = object; 9 | 10 | /** 11 | * Represents a generic reader interface. 12 | */ 13 | export type Reader = object; 14 | 15 | /** 16 | * Represents a combined reader and closer interface. 17 | */ 18 | export type ReadCloser = object; 19 | 20 | /** 21 | * Represents a combined writer and closer interface. 22 | */ 23 | export type WriteCloser = object; 24 | 25 | /** 26 | * Represents a response writer for handling output. 27 | */ 28 | export type ResponseWriter = object; 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/types/odata.d.ts: -------------------------------------------------------------------------------- 1 | import { OdataModel } from '@scripts/app/odata/lib/model'; 2 | import { YaoQuery } from '@yaoapps/types'; 3 | 4 | /** 5 | * odata query dsl 6 | */ 7 | export interface Qsl { 8 | /** yao query dsl */ 9 | qsl: YaoQuery.QueryDSL; 10 | /** output format */ 11 | format: string; //'json' | 'xml'; 12 | /** odata entityset */ 13 | entitySet: string; 14 | /**use the id get special record */ 15 | id?: string; 16 | model: OdataModel; 17 | /** count the entity */ 18 | isCount?: boolean; 19 | } 20 | 21 | /** 22 | * Model=> app.odata.view (Odata视图) 23 | * 24 | * Table=> app_odata_view 25 | */ 26 | export interface app_odata_view { 27 | /**名称 */ 28 | name: string; 29 | /**描述 */ 30 | label: string; 31 | /**长描述 */ 32 | description?: string; 33 | /**关联模型 */ 34 | model_id: string; 35 | /**关联表格 */ 36 | table_id?: string; 37 | /**可创建 */ 38 | creatable?: boolean; 39 | /**可更新 */ 40 | updatable?: boolean; 41 | /**可删除 */ 42 | deletable?: boolean; 43 | /**禁用 */ 44 | disabled?: boolean; 45 | } 46 | -------------------------------------------------------------------------------- /.vscode/types/request.d.ts: -------------------------------------------------------------------------------- 1 | export type QueryObjectIn = Record; 2 | export type HeaderObject = Record; 3 | -------------------------------------------------------------------------------- /.vscode/types/yao.d.ts: -------------------------------------------------------------------------------- 1 | // 不要直接使用export * from 的语法。 2 | import { 3 | Process, 4 | Exception, 5 | $L, 6 | FS, 7 | http, 8 | log, 9 | Query, 10 | Store, 11 | Studio, 12 | WebSocket, 13 | ssWrite 14 | } from '@yaoapps/client'; 15 | 16 | export * from './neo'; 17 | export * from './io'; 18 | 19 | export function Require(script: string) {} 20 | 21 | export { 22 | Process, 23 | Exception, 24 | $L, 25 | FS, 26 | http, 27 | log, 28 | Query, 29 | Store, 30 | Studio, 31 | WebSocket, 32 | ssWrite 33 | }; 34 | 35 | export class Job { 36 | /** 37 | * 38 | * 作业会使用golang 的协程进行调用执行 39 | * 40 | * 参数1是处理器的名称,剩余的是处理器的参数。 41 | * 42 | * @param processor 处理器的名称 43 | * @param args 处理器的参数 44 | */ 45 | constructor(processor: string, ...args: any[]) { 46 | throw new Exception(`Job Not support debug`); 47 | } 48 | /** 49 | * 状态检查回调函数 50 | * 51 | * 当作业在执行过程中会不断的检查作业状态,如果作业还没完成,就会不断的调用回调函数。 52 | * 53 | * 作业的状态回调方法Pending,方法的参数1必须是函数 54 | * 55 | * 可以在回调函数中使用 return false 打断状态检查。 56 | * @param f 57 | */ 58 | Pending(f: () => void) { 59 | throw new Exception(`Job Not support debug`); 60 | } 61 | /** 62 | * 读取作业完成后的数据 63 | */ 64 | Data() { 65 | throw new Exception(`Job Not support debug`); 66 | } 67 | /** 68 | * 取消作业 69 | */ 70 | Cancle() { 71 | throw new Exception(`Job Not support debug`); 72 | } 73 | } 74 | 75 | export const time = { 76 | After(ms: number, process: string, ...args: any[]) { 77 | this.Sleep(ms); 78 | Process(process, ...args); 79 | }, 80 | Sleep(delay: number) { 81 | const start = new Date().getTime(); 82 | while (new Date().getTime() - start < delay) { 83 | continue; 84 | } 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /aigcs/summary.yml: -------------------------------------------------------------------------------- 1 | # Get the summary of the given text. 2 | # yao run aigcs.summary 这是一只懒狗 3 | name: Summary 4 | connector: doubao 5 | prompts: 6 | - role: system 7 | content: | 8 | - Reply the summary of the content. 9 | - The maximum length of the summary is 200 characters. 10 | - Reply to me must be in the same language as the content. 11 | - Do not explain your answer, and do not use punctuation. 12 | - Do not add your own punctuation marks. 13 | 14 | optional: 15 | autopilot: true 16 | -------------------------------------------------------------------------------- /aigcs/title.yml: -------------------------------------------------------------------------------- 1 | # Get the title of the given text. 2 | # yao run aigcs.title 这是一只懒狗 3 | name: Title 4 | connector: doubao 5 | prompts: 6 | - role: system 7 | content: | 8 | - Reply the title of the content. 9 | - The maximum length of the title is 20 characters. 10 | - Reply to me must be in the same language as the content. 11 | - Do not explain your answer, and do not use punctuation. 12 | - Do not add your own punctuation marks. 13 | 14 | optional: 15 | autopilot: true 16 | -------------------------------------------------------------------------------- /aigcs/translate.yml: -------------------------------------------------------------------------------- 1 | # Translate to English 2 | # yao run aigcs.translate 你好 3 | name: Translate 4 | connector: doubao 5 | prompts: 6 | - role: system 7 | content: | 8 | - Translate the given question into English. 9 | - Do not explain your answer, and do not use punctuation. 10 | - Do not add your own punctuation marks. 11 | - eg: if I say "你好" reply me "Hello" only 12 | 13 | optional: 14 | autopilot: true 15 | -------------------------------------------------------------------------------- /apis/ai.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AI Chat API", 3 | "version": "1.0.0", 4 | "description": "User API", 5 | "group": "ai", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | "path": "/ask", 10 | "method": "GET", 11 | "guard": "-", 12 | "process": "scripts.ai.chatgpt.Callq", 13 | "in": [ 14 | "$query.q" 15 | ], 16 | "out": { 17 | "status": 200, 18 | "type": "application/json" 19 | } 20 | }, 21 | { 22 | "path": "/ask", 23 | "method": "POST", 24 | "guard": "-", 25 | "process": "scripts.ai.chatgpt.Call", 26 | "in": [ 27 | ":payload" 28 | ], 29 | "out": { 30 | "status": 200, 31 | "type": "application/json" 32 | } 33 | }, 34 | { 35 | // 以stream的方式返回数据 36 | "path": "/ask-stream", 37 | "method": "POST", 38 | "guard": "-", 39 | "process": "scripts.ai.bot.Call", // "scripts.ai.chatgpt_stream.Call", 40 | "in": [ 41 | ":payload" 42 | ], 43 | "out": { 44 | "status": 200, 45 | "type": "text/event-stream; charset=utf-8" 46 | } 47 | }, 48 | { 49 | "path": "/context", 50 | "method": "GET", 51 | "guard": "-", 52 | "process": "scripts.utils.test.test", 53 | "in": [ 54 | ":context" 55 | ], 56 | "out": { 57 | "status": 200, 58 | "type": "application/json" 59 | } 60 | }, 61 | { 62 | "path": "/templates", 63 | "method": "GET", 64 | "guard": "-", 65 | "process": "flows.chat.prompt_template", 66 | "in": [ 67 | "" 68 | ], 69 | "out": { 70 | "status": 200, 71 | "type": "application/json" 72 | } 73 | }, 74 | { 75 | "path": "/template/:id", 76 | "method": "GET", 77 | "guard": "-", 78 | "process": "models.chat.prompttemplate.find", 79 | "in": [ 80 | "$param.id", 81 | "" 82 | ], 83 | "out": { 84 | "status": 200, 85 | "type": "application/json", 86 | "body": "{{content}}" 87 | } 88 | }, 89 | { 90 | "path": "/setting", 91 | "method": "GET", 92 | "guard": "scripts.security.CheckAccessKey", 93 | "process": "scripts.ai.chatgpt.GetSetting", 94 | "in": [], 95 | "out": { 96 | "status": 200, 97 | "type": "application/json" 98 | } 99 | } 100 | ] 101 | } -------------------------------------------------------------------------------- /apis/chat.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AI Chat API", 3 | "version": "1.0.0", 4 | "description": "User API", 5 | "group": "chat", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | // 对应前端chat web 项目 10 | "path": "/chat-process", 11 | "method": "POST", 12 | "guard": "scripts.security.CheckChatKey", 13 | "process": "scripts.chatweb.chat_process", 14 | "in": [ 15 | ":payload" 16 | ], 17 | "out": { 18 | "status": 200, 19 | "type": "text/event-stream; charset=utf-8" 20 | } 21 | }, 22 | { 23 | "path": "/session", 24 | "method": "POST", 25 | "guard": "-", 26 | "process": "scripts.chatweb.session", 27 | "in": [], 28 | "out": { 29 | "status": 200, 30 | "type": "application/json" 31 | } 32 | }, 33 | { 34 | "path": "/verify", 35 | "method": "POST", 36 | "guard": "-", 37 | "process": "scripts.chatweb.verify", 38 | "in": [ 39 | ":payload" 40 | ], 41 | "out": { 42 | "status": 200, 43 | "type": "application/json" 44 | } 45 | }, 46 | { 47 | "path": "/config", 48 | "method": "POST", 49 | "guard": "scripts.security.CheckChatKey", 50 | "process": "scripts.chatweb.config", 51 | "in": [], 52 | "out": { 53 | "status": 200, 54 | "type": "application/json" 55 | } 56 | } 57 | ] 58 | } -------------------------------------------------------------------------------- /apis/file.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "File Upload API", 3 | "version": "1.0.0", 4 | "description": "File upload API", 5 | "group": "", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | // 上传文件 10 | "path": "/upload", 11 | "method": "POST", 12 | "guard": "bearer-jwt", 13 | "process": "scripts.doc.vector.uploadFile", 14 | "in": [ 15 | "$file.file" 16 | ], 17 | "out": { 18 | "status": 200, 19 | "type": "application/json; charset=utf-8" 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /apis/neo.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Neo Test", 3 | "version": "1.0.0", 4 | "guard": "-", 5 | "group": "neo", 6 | "paths": [ 7 | { 8 | // /api/neo/chat 9 | "path": "/chat", 10 | "method": "POST", 11 | "process": "scripts.neo.chat", 12 | "in": [ 13 | ":payload" 14 | ], 15 | "out": { 16 | "status": 200, 17 | "type": "application/json" 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /apis/proxy.http.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "代理yao的请求", 3 | "version": "1.0.0", 4 | "description": "调试本地yao js脚本", 5 | "group": "", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | "guard": "scripts.security.CheckAccessKey", 10 | "path": "/call", 11 | "method": "POST", 12 | "process": "scripts.jsproxy.Server", 13 | "in": [":payload"], 14 | "out": { 15 | "status": 200, 16 | "type": "application/json" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /apis/test.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AI Chat API", 3 | "version": "1.0.0", 4 | "description": "User API", 5 | "group": "", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | // 以stream的方式返回数据 10 | "path": "/ask-stream", 11 | "method": "POST", 12 | "guard": "-", 13 | "process": "scripts.ai.deepseek.Call", 14 | "in": [ 15 | ":payload" 16 | ], 17 | "out": { 18 | "status": 200, 19 | "type": "text/event-stream; charset=utf-8" 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /apis/utils.http.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "下载Excel", 3 | "version": "1.0.0", 4 | "description": "下载Excel", 5 | "group": "utils", 6 | "guard": "bearer-jwt", 7 | "paths": [ 8 | { 9 | "guard": "-", 10 | "path": "/export/:name", 11 | "method": "GET", 12 | "process": "flows.utils.export", 13 | "in": [ 14 | "$param.name" 15 | ], 16 | "out": { 17 | "status": 200, 18 | "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 19 | "headers": { 20 | "Content-Disposition": "attachment;filename=导出数据.xlsx" 21 | } 22 | } 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /apis/v1.http.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AI Chat API", 3 | "version": "1.0.0", 4 | "description": "User API", 5 | "group": "", 6 | "guard": "-", 7 | "paths": [ 8 | { 9 | // 以stream的方式返回数据 10 | // /api/v1/tencent/chat/completions 11 | "path": "/tencent/chat/completions", 12 | "method": "POST", 13 | "guard": "-", 14 | "process": "scripts.adapter.tencent.Completions", 15 | "in": [ 16 | ":payload" 17 | ], 18 | "out": { 19 | "status": 200, 20 | "type": "text/event-stream; charset=utf-8" 21 | } 22 | }, 23 | { 24 | // 以stream的方式返回数据 25 | // /api/v1/chat/completions 26 | "path": "/aardio/chat/completions", 27 | "method": "POST", 28 | "guard": "-", 29 | "process": "scripts.adapter.aardio.Completions", 30 | "in": [ 31 | ":payload" 32 | ], 33 | "out": { 34 | "status": 200, 35 | "type": "text/event-stream; charset=utf-8" 36 | } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "xgen": "1.0", 3 | "name": "::AI Chat Application", 4 | "short": "::Demo", 5 | "description": "::Chat With AI", 6 | "version": "0.10.3", 7 | "adminRoot": "admin", 8 | "setup": "", 9 | "menu": { 10 | "process": "scripts.menu.menu.main", 11 | "args": ["demo"] 12 | }, 13 | "moapi": { 14 | "secret": "$ENV.COPILOT_TENCENT_TOKEN", 15 | "mirrors": [ 16 | "$ENV.COPILOT_TENCENT_HOST" 17 | ] 18 | }, 19 | "optional": { 20 | "hideNotification": true, 21 | "hideSetting": false, 22 | "remoteCache": false, 23 | "neo": { 24 | "api": "/neo", 25 | "studio": false 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /assistants/neo/package.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neo-assistant", 3 | "type": "assistant", 4 | "description": "This is a chat assistant", 5 | "assistant_id": "neo", 6 | "mentionable": true, 7 | "automated": false, 8 | "readonly": false, 9 | "built_in": false, 10 | "sort": 0, 11 | "path": "chat", 12 | "connector": "copilot-tencent", 13 | "created_at": "2022-02-22T14:00:00.000Z", 14 | "tags": [ 15 | "test" 16 | ], 17 | "options": { 18 | "temperature": 0.7, 19 | "top_p": 1, 20 | "frequency_penalty": 0, 21 | "presence_penalty": 0 22 | }, 23 | "avatar": "https://cdn.pixabay.com/photo/2020/01/16/17/21/pantheon-4771206_1280.jpg", 24 | "prompts": "" 25 | } -------------------------------------------------------------------------------- /assistants/neo/prompts.yml: -------------------------------------------------------------------------------- 1 | # array of the prompts 2 | - role: system 3 | name: Translate 4 | content: 5 | -------------------------------------------------------------------------------- /charts/ai/chat.chart.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "聊天数据图表", 3 | "config": { 4 | "full": false 5 | }, 6 | "action": { 7 | "before:data": "scripts.ai.stat.BeforeData", 8 | "data": { 9 | "process": "scripts.ai.stat.Data", 10 | "default": [ 11 | "2022-09-20" 12 | ] 13 | }, 14 | "after:data": "scripts.ai.stat.AfterData" 15 | }, 16 | "layout": { 17 | "operation": { 18 | "actions": [ 19 | { 20 | "title": "官网文档", 21 | "icon": "icon-book", 22 | "action": { 23 | "Common.historyPush": { 24 | "pathname": "https://yaoapps.com/doc" 25 | } 26 | } 27 | } 28 | ] 29 | }, 30 | "filter": { 31 | "columns": [ 32 | { 33 | "name": "时间区间", 34 | "width": 6 35 | }, 36 | { 37 | "name": "类型", 38 | "width": 6 39 | } 40 | ] 41 | }, 42 | "chart": { 43 | "columns": [ 44 | { 45 | "name": "数量", 46 | "width": 6 47 | } 48 | ] 49 | } 50 | }, 51 | "fields": { 52 | "filter": { 53 | "时间区间": { 54 | "bind": "range", 55 | "edit": { 56 | "type": "RangePicker", 57 | "props": {} 58 | } 59 | }, 60 | "类型": { 61 | "bind": "range", 62 | "edit": { 63 | "type": "Input", 64 | "props": {} 65 | } 66 | } 67 | }, 68 | "chart": { 69 | "数量": { 70 | "bind": "count", 71 | "link": "/x/Table/ai.chatlog", 72 | "view": { 73 | "type": "Number", 74 | "props": { 75 | "unit": "条" 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /components/components/view/chat.less: -------------------------------------------------------------------------------- 1 | .box{ 2 | display: flex; 3 | background-color: aquamarine; 4 | } -------------------------------------------------------------------------------- /components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xgen-dev-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "Wendao", 7 | "license": "Apache-2.0", 8 | "scripts": { 9 | "dev": "rollup --c rollup.dev.ts --configPlugin swc3 -w", 10 | "build": "rollup --c rollup.build.ts --configPlugin swc3" 11 | }, 12 | "devDependencies": { 13 | "@rollup/plugin-node-resolve": "^15.0.1", 14 | "@swc/core": "^1.3.23", 15 | "@types/glob": "^8.1.0", 16 | "@types/node": "^18.11.18", 17 | "@types/react": "^18.0.26", 18 | "@types/react-dom": "^18.0.9", 19 | "glob": "^8.1.0", 20 | "rollup": "^3.7.5", 21 | "rollup-plugin-delete": "^2.0.0", 22 | "rollup-plugin-less": "^1.1.3", 23 | "rollup-plugin-postcss": "^4.0.2", 24 | "rollup-plugin-swc3": "^0.8.0" 25 | }, 26 | "dependencies": { 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /components/readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 远程插件 3 | 4 | 远程插件用于增强简单的显示控件与数据输入控件。 5 | 6 | 只适合增加一些没有远程加载的插件,比如增强显示。 7 | 8 | 不适合复杂的插件增强,特别是有大量第三方依赖库的。如果增加新的控件依赖,需要在xgen框架里注入库文件。 9 | 10 | 依赖库需要xgen主框加的配合,xgen本身只支持导入'react', 'react-dom', 'react/jsx-runtime',自调整版本增加了antd控件库,其它控件依赖需要调整xgen框架中的代码packages/xgen/utils/preset/system_modules.ts 11 | 12 | ## 列表类的控件有以下的传入值类型 13 | 14 | 15 | ```js 16 | //xgen/packages/xgen/components/base/PureTable/components/ViewContent.tsx 17 | 18 | //__value 绑定结构中__bind对应的值 19 | //__bind 绑定结构的字段名 20 | //__name 数据列的标签名称 21 | //__primary table配置主键,一般是id 22 | //onSave 保存单个字段值的回调函数,会调用api:/api/__yao/table/hero/save,传入记录的id与需要保存的键值 23 | 24 | const props_view_component = { 25 | ...field_detail.view.props, 26 | __namespace: namespace, 27 | __primary: primary, 28 | __bind: form_bind, 29 | __name: field_detail.name, 30 | __value: form_value, 31 | onSave 32 | } 33 | 34 | return 35 | ``` 36 | 37 | ## 输入类型的控件 38 | 39 | 输入类型的控件会放在andt的Form控件中,需要在输入控件外层再包装一层自定义的Item控件,Item控件会使用字段标识(name=__bind)与标签名(label=__name)绑定Form控件 40 | 41 | ```js 42 | const real_props = { 43 | label: hideLabel ? ( 44 | '' 45 | ) : ( 46 | 47 | 48 | 49 | ), 50 | name: __bind, 51 | noStyle: !__name 52 | } 53 | ``` 54 | 55 | 56 | 57 | 58 | ## 自定义控件列表 59 | 60 | + view/Rate 显示比分显示 61 | + edit/Rate 比分输入 62 | + edit/Input -------------------------------------------------------------------------------- /components/rollup.build.css: -------------------------------------------------------------------------------- 1 | .box { 2 | display: flex; 3 | background-color: aquamarine; 4 | } 5 | -------------------------------------------------------------------------------- /components/rollup.build.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup' 2 | import del from 'rollup-plugin-delete' 3 | import { defineRollupSwcMinifyOption, minify, swc } from 'rollup-plugin-swc3' 4 | 5 | import config, { plugins } from './rollup.common' 6 | 7 | export default defineConfig({ 8 | ...config, 9 | plugins: [ 10 | ...plugins, 11 | swc(), 12 | minify( 13 | defineRollupSwcMinifyOption({ 14 | compress: { drop_console: false } 15 | }) 16 | ), 17 | del({ targets: 'dist/*' }) 18 | ] 19 | }) 20 | -------------------------------------------------------------------------------- /components/rollup.common.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, InputPluginOption } from "rollup"; 2 | import less from 'rollup-plugin-less'; 3 | import postcss from 'rollup-plugin-postcss'; 4 | 5 | import { nodeResolve } from "@rollup/plugin-node-resolve"; 6 | import glob from "glob"; 7 | 8 | import path from "node:path"; 9 | import { fileURLToPath } from "node:url"; 10 | export const plugins = [ 11 | nodeResolve(),// 其他插件... 12 | less() 13 | ]; 14 | 15 | export default defineConfig({ 16 | input: Object.fromEntries( 17 | glob.sync("components/**/*.ts*").map((file: string) => [ 18 | // This remove `src/` as well as the file extension from each 19 | // file, so e.g. src/nested/foo.js becomes nested/foo 20 | path.relative( 21 | "components", 22 | file.slice(0, file.length - path.extname(file).length) 23 | ), 24 | // This expands the relative paths to absolute paths, so e.g. 25 | // src/nested/foo becomes /project/src/nested/foo.js 26 | fileURLToPath(new URL(file, import.meta.url)), 27 | ]) 28 | ), 29 | output: { 30 | dir: "../public/components", 31 | preserveModules: true, 32 | format: "systemjs", 33 | }, 34 | external: ["react", "react-dom", "react/jsx-runtime"], 35 | 36 | // When using tsyringe, this item needs to be set 37 | context: "false", 38 | }); 39 | -------------------------------------------------------------------------------- /components/rollup.dev.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup' 2 | import { swc } from 'rollup-plugin-swc3' 3 | 4 | import config, { plugins } from './rollup.common' 5 | 6 | export default defineConfig({ 7 | ...config, 8 | plugins: [...plugins, swc()] 9 | }) 10 | -------------------------------------------------------------------------------- /components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ESNext", 5 | "skipLibCheck": true, 6 | "lib": ["ESNEXT", "DOM"], 7 | "moduleResolution": "Node", 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "strict": true, 11 | "outDir": "../public/components", 12 | "baseUrl": ".", 13 | "esModuleInterop": true, 14 | "paths": { 15 | "@/*": ["./*"] 16 | }, 17 | "emitDecoratorMetadata": true, 18 | "experimentalDecorators": true, 19 | "jsx": "react-jsx" 20 | }, 21 | "include": ["./**/*.ts", "./**/*.tsx", "./typings/**/*.d.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /components/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.css"; 2 | declare module "*.sss"; 3 | declare module "*.less"; 4 | declare module "*.lsss"; 5 | declare module "*.png"; 6 | declare module "*.svg"; 7 | declare module "*.jpeg"; 8 | declare module "less-vars-to-js"; 9 | -------------------------------------------------------------------------------- /connectors/bailian-ds-r1-70b.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Model v3", 3 | "type": "openai", 4 | "options": { 5 | "model": "deepseek-r1-distill-llama-70b", 6 | "key": "$ENV.BAILIAN_KEY", 7 | "proxy": "https://dashscope.aliyuncs.com/compatible-mode/v1" 8 | } 9 | } -------------------------------------------------------------------------------- /connectors/copilot-tencent.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Model v3", 3 | "type": "openai", 4 | "options": { 5 | "model": "deepseek-r1", 6 | "key": "$ENV.COPILOT_TENCENT_TOKEN", 7 | "proxy": "$ENV.COPILOT_TENCENT_HOST" 8 | } 9 | } -------------------------------------------------------------------------------- /connectors/doubao.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Model v3", 3 | "type": "openai", 4 | "options": { 5 | "model": "ep-20250116132528-fngf6", 6 | "key": "$ENV.DOUBAO_KEY", 7 | "proxy": "https://ark.cn-beijing.volces.com/api/v3" 8 | } 9 | } -------------------------------------------------------------------------------- /connectors/gpt-3_5-turbo.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "1.0.0", 3 | "version": "1.0.0", 4 | "label": "Model gpt-3.5-turbo", 5 | "type": "openai", 6 | "options": { 7 | "model": "gpt-3.5-turbo", 8 | "key": "$ENV.OPENAI_KEY", 9 | "proxy": "$ENV.OPENAI_AIP_HOST" 10 | } 11 | } -------------------------------------------------------------------------------- /connectors/text-embedding-ada-002.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "LANG": "1.0.0", 3 | "VERSION": "1.0.0", 4 | "label": "text-embedding-ada-002", 5 | "type": "openai", 6 | "options": { 7 | "model": "text-embedding-ada-002", 8 | "key": "$ENV.OPENAI_KEY", 9 | "proxy": "$ENV.OPENAI_AIP_HOST" 10 | } 11 | } -------------------------------------------------------------------------------- /connectors/text-embedding-doubao.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "LANG": "1.0.0", 3 | "VERSION": "1.0.0", 4 | "label": "text-embedding-ada-002", 5 | "type": "openai", 6 | "options": { 7 | "model": "ep-20250205210443-n8qnk", 8 | "key": "$ENV.DOUBAO_KEY", 9 | "proxy": "https://ark.cn-beijing.volces.com/api/v3" 10 | } 11 | } -------------------------------------------------------------------------------- /connectors/text-embedding-llamacpp.conn.yao: -------------------------------------------------------------------------------- 1 | { 2 | "LANG": "1.0.0", 3 | "VERSION": "1.0.0", 4 | "label": "text-embedding-ada-002", 5 | "type": "openai", 6 | "options": { 7 | "model": "ep-20250205210443-n8qnk", 8 | "key": "$ENV.DOUBAO_KEY", 9 | "proxy": "http://localhost:8080/v1" 10 | } 11 | } -------------------------------------------------------------------------------- /dashboards/ai.dash.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "大屏", 3 | "action": { 4 | "data": { 5 | "process": "scripts.ai.dashboard.dataSource" 6 | } 7 | }, 8 | "layout": { 9 | "dashboard": { 10 | "columns": [ 11 | { 12 | "name": "列表", 13 | "width": 24 14 | }, 15 | { 16 | "name": "图表", 17 | "width": 12 18 | } 19 | ] 20 | } 21 | }, 22 | "fields": { 23 | "dashboard": { 24 | "列表": { 25 | "view": { 26 | "type": "base/Table", 27 | "props": { 28 | "model": "ai.chatlog", 29 | "onChangeEvent": true 30 | } 31 | } 32 | }, 33 | "图表": { 34 | "bind": "chat_count", 35 | "view": { 36 | "type": "chart/NumberChart", 37 | "props": { 38 | "height": 240, 39 | "prefix": "¥", 40 | "decimals": 2, 41 | "nameKey": "date", 42 | "valueKey": "value" 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /db/dummy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/db/dummy -------------------------------------------------------------------------------- /docs/ai-convesation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/docs/ai-convesation.png -------------------------------------------------------------------------------- /docs/stop_sequence.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2023-02-15T20:27:15 (UTC +08:00) 3 | tags: [] 4 | source: https://help.openai.com/en/articles/5072263-how-do-i-use-stop-sequences 5 | author: 6 | --- 7 | 8 | # 如何使用 Stop Sequences? |开放人工智能帮助中心 9 | 10 | > ## Excerpt 11 | > 12 | > 所有收藏 13 | 14 | --- 15 | 16 | [所有收藏](https://help.openai.com/en/) 17 | 18 | [OpenAI API](https://help.openai.com/en/collections/3675931-openai-api) 19 | 20 | [快速工程](https://help.openai.com/en/collections/3675931-openai-api#prompt-engineering) 21 | 22 | 如何使用 Stop Sequences? 23 | 24 | ## 如何使用 Stop Sequences? 25 | 26 | ![亚瑟·瑞恩头像](https://static.intercomassets.com/avatars/4418977/square_128/Screenshot_2023-01-25_at_11.28.45_AM-1674674951.png) 27 | 28 | 作者:Asher Ryan 29 | 一周前更新 30 | 31 | ## **Stop Sequences** 32 | 33 | Stop Sequences 用于使模型在所需点停止,例如句子或列表的结尾。通常,返回键可以很好地用作单行完成的 Stop Sequences。Stop Sequences 是一个可选设置,用于告知 API 何时停止生成令牌。完成将不包含 Stop Sequences,您最多可以传递四个 Stop Sequences。如果未传递任何内容,则默认为令牌<|endoftext|>。此标记表示文本中可能的停止点。 34 | 35 | ### **聊天示例:** 36 | 37 | 在[聊天](https://beta.openai.com/playground/p/default-chat)示例中,使用了三个 Stop Sequences:新行、值“Human:”和值“AI:”。我们的目标是仅生成与当前说话人对应的单行文本。选择的三个 Stop Sequences 创建了实现该目的所需的约束: 38 | 39 | - 完成不能转到新行。 40 | - 完成无法更改扬声器。 41 | - 完成将不允许演讲者连续发言两次 42 | 43 | ### **问答示例:** 44 | 45 | 在问[答](https://beta.openai.com/playground/p/default-qa)示例中,用新行分隔每个问答对的模式使我们相信返回键(新行)将很好地作为我们的 Stop Sequences。您可以在下面看到,在 **A:** 之后的完成将在一行后停止,因为返回键用作 Stop Sequences。 46 | 47 | ![](https://openai.intercom-attachments-7.com/i/o/330834756/65a8a1772e7c8d7b3716bcce/P5YtnyetBetZprUCREzxqg4WvjcIjxmcyM68n9fYItagMMnSTRE2BE-6cAMcDmsKa04USSe6pDAM0Crcrgw6wcPCDxqt-AC2N3j1HjmKuYMBK6TBf-Pj6IB5o-T0FVtCtfF0m2O9) 48 | 49 | ### **列表示例:** 50 | 51 | 还可以使用“Stop Sequences”生成包含特定数量项的列表。例如,通过使用“11.”作为 Stop Sequences,可以生成包含 10 个项目的列表,因为当达到“11.”时完成将停止。这可以从[科幻提示](https://beta.openai.com/playground/p/default-sci-fi-book-list)中看出。 52 | 53 | 这回答了你的问题吗? 54 | -------------------------------------------------------------------------------- /docs/yao-xgen-neo-assistant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/docs/yao-xgen-neo-assistant.png -------------------------------------------------------------------------------- /flows/chat/prompt_template.flow.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "提示模板", 3 | "nodes": [ 4 | { 5 | "name": "template", 6 | "engine": "default", 7 | "query": { 8 | "select": ["id as value", "title as label"], 9 | "wheres": [ 10 | { 11 | ":status": "状态", 12 | "=": "enabled" 13 | } 14 | ], 15 | "from": "$chat.prompttemplate" 16 | } 17 | } 18 | ], 19 | "output": "{{$res.template}}" 20 | } 21 | -------------------------------------------------------------------------------- /flows/utils/export.flow.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "下载", 3 | "version": "1.0.0", 4 | "description": "下载", 5 | "nodes": [ 6 | { 7 | "name": "文件", 8 | "process": "yao.table.Export", 9 | "args": [ 10 | "{{$in.0}}", 11 | {}, 12 | 50 13 | ] 14 | }, 15 | { 16 | "name": "数据", 17 | "process": "fs.system.ReadFile", 18 | "args": [ 19 | "{{$res.文件}}" 20 | ] 21 | } 22 | ], 23 | "output": "{{$res.数据}}" 24 | } -------------------------------------------------------------------------------- /forms/ai/chatlog.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Log", 3 | "action": { 4 | "bind": { 5 | "model": "ai.chatlog", 6 | "option": {} 7 | } 8 | }, 9 | "layout": { 10 | "actions": [ 11 | { 12 | "title": "返回", 13 | "icon": "icon-arrow-left", 14 | "showWhenAdd": true, 15 | "showWhenView": true, 16 | "action": [ 17 | { 18 | "name": "CloseModal", 19 | "type": "Common.closeModal", 20 | "payload": {} 21 | } 22 | ] 23 | }, 24 | { 25 | "title": "保存", 26 | "icon": "icon-check", 27 | "style": "primary", 28 | "showWhenAdd": true, 29 | "action": [ 30 | { 31 | "name": "Submit", 32 | "type": "Form.submit", 33 | "payload": {} 34 | }, 35 | { 36 | "name": "Back", 37 | "type": "Common.closeModal", 38 | "payload": {} 39 | } 40 | ] 41 | }, 42 | { 43 | "action": [ 44 | { 45 | "name": "Confirm", 46 | "type": "Common.confirm", 47 | "payload": { 48 | "title": "确认删除", 49 | "content": "删除后不可撤销!" 50 | } 51 | }, 52 | { 53 | "name": "Delete", 54 | "payload": { 55 | "model": "ai.chatlog" 56 | }, 57 | "type": "Form.delete" 58 | }, 59 | { 60 | "name": "Back", 61 | "type": "Common.closeModal", 62 | "payload": {} 63 | } 64 | ], 65 | "icon": "icon-trash-2", 66 | "style": "danger", 67 | "title": "Delete" 68 | } 69 | ], 70 | "form": { 71 | "sections": [ 72 | { 73 | "columns": [ 74 | { 75 | "name": "提问", 76 | "width": 12 77 | }, 78 | { 79 | "name": "提问时间", 80 | "width": 12 81 | }, 82 | { 83 | "name": "回答", 84 | "width": 24 85 | } 86 | ] 87 | } 88 | ] 89 | }, 90 | "hooks": { 91 | "onChange": {} 92 | }, 93 | "primary": "id" 94 | }, 95 | "fields": { 96 | "form": { 97 | "ID": { 98 | "bind": "id", 99 | "edit": { 100 | "props": { 101 | "disabled": true, 102 | "placeholder": "请输入 ID" 103 | }, 104 | "type": "InputNumber" 105 | } 106 | }, 107 | "提问时间": { 108 | "bind": "created_at", 109 | "edit": { 110 | "props": { 111 | "placeholder": "请选择 创建时间", 112 | "showTime": { 113 | "format": "HH:mm:ss" 114 | } 115 | }, 116 | "type": "DatePicker" 117 | } 118 | }, 119 | "回答": { 120 | "bind": "answer", 121 | "edit": { 122 | "props": { 123 | "autoSize": { 124 | "maxRows": 20, 125 | "minRows": 5 126 | }, 127 | "placeholder": "请输入 回答" 128 | }, 129 | "type": "TextArea" 130 | } 131 | }, 132 | "提问": { 133 | "bind": "question", 134 | "edit": { 135 | "props": { 136 | "placeholder": "请输入 提问" 137 | }, 138 | "type": "Input" 139 | }, 140 | "view": { 141 | "props": {}, 142 | "type": "Tag" 143 | } 144 | }, 145 | "更新时间": { 146 | "bind": "updated_at", 147 | "edit": { 148 | "props": { 149 | "placeholder": "请选择 更新时间", 150 | "showTime": { 151 | "format": "HH:mm:ss" 152 | } 153 | }, 154 | "type": "DatePicker" 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /forms/ai/model.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI Model", 3 | "action": { 4 | "bind": { 5 | "model": "ai.model", 6 | "option": { 7 | "withs": { 8 | "permissions": {} 9 | } 10 | } 11 | } 12 | }, 13 | "layout": { 14 | "actions": [ 15 | { 16 | "title": "返回", 17 | "icon": "icon-arrow-left", 18 | "showWhenAdd": true, 19 | "showWhenView": true, 20 | "action": [ 21 | { 22 | "name": "CloseModal", 23 | "type": "Common.closeModal", 24 | "payload": {} 25 | } 26 | ] 27 | }, 28 | { 29 | "title": "保存", 30 | "icon": "icon-check", 31 | "style": "primary", 32 | "showWhenAdd": true, 33 | "action": [ 34 | { 35 | "name": "Submit", 36 | "type": "Form.submit", 37 | "payload": {} 38 | }, 39 | { 40 | "name": "Back", 41 | "type": "Common.closeModal", 42 | "payload": {} 43 | } 44 | ] 45 | }, 46 | { 47 | "action": [ 48 | { 49 | "name": "Confirm", 50 | "type": "Common.confirm", 51 | "payload": { 52 | "title": "确认删除", 53 | "content": "删除后不可撤销!" 54 | } 55 | }, 56 | { 57 | "name": "Delete", 58 | "payload": { 59 | "model": "ai.setting" 60 | }, 61 | "type": "Form.delete" 62 | }, 63 | { 64 | "name": "Back", 65 | "type": "Common.closeModal", 66 | "payload": {} 67 | } 68 | ], 69 | "icon": "icon-trash-2", 70 | "style": "danger", 71 | "title": "Delete" 72 | } 73 | ], 74 | "form": { 75 | "sections": [ 76 | { 77 | "columns": [ 78 | { 79 | "name": "IDX", 80 | "width": 12 81 | }, 82 | { 83 | "name": "回答", 84 | "width": 12 85 | }, 86 | { 87 | "name": "拥有者", 88 | "width": 12 89 | }, 90 | { 91 | "name": "创建时间", 92 | "width": 12 93 | }, 94 | { 95 | "name": "上级", 96 | "width": 12 97 | }, 98 | { 99 | "name": "根节点", 100 | "width": 12 101 | }, 102 | { 103 | "name": "权限", 104 | "width": 24 105 | } 106 | ] 107 | } 108 | ] 109 | } 110 | }, 111 | "fields": { 112 | "form": { 113 | "权限": { 114 | "bind": "permissions", 115 | "edit": { 116 | "type": "List", 117 | "props": { 118 | "name": "permission", 119 | "showLabel": true 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /forms/ai/permission.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI API Setting", 3 | "action": { 4 | "bind": { 5 | "model": "ai.permission", 6 | "option": { 7 | "withs": { 8 | "model": {} 9 | } 10 | } 11 | } 12 | }, 13 | "layout": { 14 | "actions": [ 15 | { 16 | "title": "返回", 17 | "icon": "icon-arrow-left", 18 | "showWhenAdd": true, 19 | "showWhenView": true, 20 | "action": [ 21 | { 22 | "name": "CloseModal", 23 | "type": "Common.closeModal", 24 | "payload": {} 25 | } 26 | ] 27 | }, 28 | { 29 | "title": "保存", 30 | "icon": "icon-check", 31 | "style": "primary", 32 | "showWhenAdd": true, 33 | "action": [ 34 | { 35 | "name": "Submit", 36 | "type": "Form.submit", 37 | "payload": {} 38 | }, 39 | { 40 | "name": "Back", 41 | "type": "Common.closeModal", 42 | "payload": {} 43 | } 44 | ] 45 | }, 46 | { 47 | "action": [ 48 | { 49 | "name": "Confirm", 50 | "type": "Common.confirm", 51 | "payload": { 52 | "title": "确认删除", 53 | "content": "删除后不可撤销!" 54 | } 55 | }, 56 | { 57 | "name": "Delete", 58 | "payload": { 59 | "model": "ai.setting" 60 | }, 61 | "type": "Form.delete" 62 | }, 63 | { 64 | "name": "Back", 65 | "type": "Common.closeModal", 66 | "payload": {} 67 | } 68 | ], 69 | "icon": "icon-trash-2", 70 | "style": "danger", 71 | "title": "Delete" 72 | } 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /forms/ai/setting.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI API Setting", 3 | "action": { 4 | "bind": { 5 | "model": "ai.setting", 6 | "option": {} 7 | } 8 | }, 9 | "layout": { 10 | "actions": [ 11 | { 12 | "title": "返回", 13 | "icon": "icon-arrow-left", 14 | "showWhenAdd": true, 15 | "showWhenView": true, 16 | "action": [ 17 | { 18 | "name": "CloseModal", 19 | "type": "Common.closeModal", 20 | "payload": {} 21 | } 22 | ] 23 | }, 24 | { 25 | "title": "保存", 26 | "icon": "icon-check", 27 | "style": "primary", 28 | "showWhenAdd": true, 29 | "action": [ 30 | { 31 | "name": "Submit", 32 | "type": "Form.submit", 33 | "payload": {} 34 | }, 35 | { 36 | "name": "Back", 37 | "type": "Common.closeModal", 38 | "payload": {} 39 | } 40 | ] 41 | }, 42 | { 43 | "action": [ 44 | { 45 | "name": "Confirm", 46 | "type": "Common.confirm", 47 | "payload": { 48 | "title": "确认删除", 49 | "content": "删除后不可撤销!" 50 | } 51 | }, 52 | { 53 | "name": "Delete", 54 | "payload": { 55 | "model": "ai.setting" 56 | }, 57 | "type": "Form.delete" 58 | }, 59 | { 60 | "name": "Back", 61 | "type": "Common.closeModal", 62 | "payload": {} 63 | } 64 | ], 65 | "icon": "icon-trash-2", 66 | "style": "danger", 67 | "title": "Delete" 68 | } 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /forms/chat/chat.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Wit AI", 3 | "action": { 4 | "bind": {} 5 | }, 6 | "layout": { 7 | "actions": [{}], 8 | "form": { 9 | "sections": [ 10 | { 11 | "columns": [ 12 | { 13 | "name": "name", 14 | "width": 24 15 | }, 16 | { 17 | "name": "chat", 18 | "width": 12 19 | }, 20 | { 21 | "name": "chat", 22 | "width": 12 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | }, 29 | "fields": { 30 | "form": { 31 | "chat": { 32 | "edit": { 33 | "type": "public/view/chat" 34 | } 35 | }, 36 | "name": { 37 | "edit": { 38 | "type": "Input" 39 | } 40 | } 41 | } 42 | }, 43 | "config": { "full": true } 44 | } 45 | -------------------------------------------------------------------------------- /forms/chat/conversation.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Conversation", 3 | "action": { "bind": { "model": "chat.conversation", "option": {} } }, 4 | "layout": { 5 | "actions": [ 6 | { 7 | "title": "返回", 8 | "icon": "icon-arrow-left", 9 | "showWhenAdd": true, 10 | "showWhenView": true, 11 | "action": [ 12 | { "name": "CloseModal", "type": "Common.closeModal", "payload": {} } 13 | ] 14 | }, 15 | { 16 | "title": "保存", 17 | "icon": "icon-check", 18 | "style": "primary", 19 | "showWhenAdd": true, 20 | "action": [ 21 | { "name": "Submit", "type": "Form.submit", "payload": {} }, 22 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 23 | ] 24 | }, 25 | { 26 | "action": [ 27 | { 28 | "name": "Confirm", 29 | "type": "Common.confirm", 30 | "payload": { 31 | "title": "确认删除", 32 | "content": "删除后不可撤销!" 33 | } 34 | }, 35 | { 36 | "name": "Delete", 37 | "payload": { "model": "chat.conversation" }, 38 | "type": "Form.delete" 39 | }, 40 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 41 | ], 42 | "icon": "icon-trash-2", 43 | "style": "danger", 44 | "title": "Delete" 45 | } 46 | ], 47 | "form": { 48 | "sections": [ 49 | { 50 | "columns": [ 51 | { "name": "ID", "width": 12 }, 52 | { "name": "会话ID", "width": 12 }, 53 | { "name": "标题", "width": 12 }, 54 | { "name": "描述", "width": 12 }, 55 | { "name": "API设置", "width": 12 }, 56 | { "name": "创建时间", "width": 12 }, 57 | { "name": "更新时间", "width": 12 }, 58 | { "name": "会话列表", "width": 24 } 59 | ] 60 | } 61 | ] 62 | }, 63 | "primary": "id" 64 | }, 65 | "fields": { 66 | "form": { 67 | "API设置": { 68 | "bind": "api_setting", 69 | "edit": { 70 | "props": { "placeholder": "请输入 API设置" }, 71 | "type": "InputNumber" 72 | } 73 | }, 74 | "ID": { 75 | "bind": "id", 76 | "edit": { 77 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 78 | "type": "InputNumber" 79 | } 80 | }, 81 | "会话ID": { 82 | "bind": "uuid", 83 | "edit": { 84 | "props": { "placeholder": "请输入 会话ID" }, 85 | "type": "Input" 86 | }, 87 | "view": { "props": {}, "type": "" } 88 | }, 89 | "创建时间": { 90 | "bind": "created_at", 91 | "edit": { 92 | "props": { 93 | "placeholder": "请选择 创建时间", 94 | "showTime": { "format": "HH:mm:ss" } 95 | }, 96 | "type": "DatePicker" 97 | } 98 | }, 99 | "描述": { 100 | "bind": "description", 101 | "edit": { "props": { "placeholder": "请输入 描述" }, "type": "Input" }, 102 | "view": { "props": {}, "type": "" } 103 | }, 104 | "更新时间": { 105 | "bind": "updated_at", 106 | "edit": { 107 | "props": { 108 | "placeholder": "请选择 更新时间", 109 | "showTime": { "format": "HH:mm:ss" } 110 | }, 111 | "type": "DatePicker" 112 | } 113 | }, 114 | "标题": { 115 | "bind": "title", 116 | "edit": { "props": { "placeholder": "请输入 标题" }, "type": "Input" }, 117 | "view": { "props": {}, "type": "Text" } 118 | }, 119 | "会话列表": { 120 | "edit": { 121 | "props": { 122 | "model": "chat.message", 123 | "query": { 124 | "where.conversation_id.eq": "{{id}}" 125 | } 126 | }, 127 | "type": "Table" 128 | }, 129 | "view": { "props": { "model": "chat.message" }, "type": "table" } 130 | } 131 | } 132 | }, 133 | "config": { "full": true } 134 | } 135 | -------------------------------------------------------------------------------- /forms/chat/conversation_simple.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Conversation", 3 | "action": { 4 | "bind": { 5 | "model": "chat.conversation", 6 | "option": { 7 | "withs": { 8 | "chat.message": {} 9 | } 10 | } 11 | } 12 | }, 13 | "layout": { 14 | "actions": [ 15 | { 16 | "title": "返回", 17 | "icon": "icon-arrow-left", 18 | "showWhenAdd": true, 19 | "showWhenView": true, 20 | "action": [ 21 | { "name": "CloseModal", "type": "Common.closeModal", "payload": {} } 22 | ] 23 | }, 24 | { 25 | "title": "保存", 26 | "icon": "icon-check", 27 | "style": "primary", 28 | "showWhenAdd": true, 29 | "action": [ 30 | { "name": "Submit", "type": "Form.submit", "payload": {} }, 31 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 32 | ] 33 | }, 34 | { 35 | "action": [ 36 | { 37 | "name": "Confirm", 38 | "type": "Common.confirm", 39 | "payload": { 40 | "title": "确认删除", 41 | "content": "删除后不可撤销!" 42 | } 43 | }, 44 | { 45 | "name": "Delete", 46 | "payload": { "model": "chat.conversation" }, 47 | "type": "Form.delete" 48 | }, 49 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 50 | ], 51 | "icon": "icon-trash-2", 52 | "style": "danger", 53 | "title": "Delete" 54 | } 55 | ], 56 | "form": { 57 | "sections": [ 58 | { 59 | "columns": [ 60 | { "name": "ID", "width": 6 }, 61 | { "name": "创建时间", "width": 6 }, 62 | { "name": "会话ID", "width": 12 }, 63 | { "name": "标题", "width": 24 }, 64 | { "name": "消息列表", "width": 24 } 65 | ] 66 | } 67 | ] 68 | }, 69 | "primary": "id" 70 | }, 71 | "fields": { 72 | "form": { 73 | "ID": { 74 | "bind": "id", 75 | "edit": { 76 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 77 | "type": "InputNumber" 78 | } 79 | }, 80 | "会话ID": { 81 | "bind": "uuid", 82 | "edit": { 83 | "props": { "placeholder": "请输入 会话ID" }, 84 | "type": "Input" 85 | }, 86 | "view": { "props": {}, "type": "" } 87 | }, 88 | "创建时间": { 89 | "bind": "created_at", 90 | "edit": { 91 | "props": { 92 | "placeholder": "请选择 创建时间", 93 | "showTime": { "format": "HH:mm:ss" } 94 | }, 95 | "type": "DatePicker" 96 | } 97 | }, 98 | "标题": { 99 | "bind": "title", 100 | "edit": { "props": { "placeholder": "请输入 标题" }, "type": "Input" }, 101 | "view": { "props": {}, "type": "Text" } 102 | }, 103 | "消息列表": { 104 | "edit": { 105 | "props": { 106 | "model": "chat.message_simple", 107 | "query": { 108 | "select": "id,prompt,completion,request_total_time", 109 | "where.conversation_id.eq": "{{id}}" 110 | } 111 | }, 112 | "type": "Table" 113 | } 114 | } 115 | } 116 | }, 117 | "config": { "full": true } 118 | } 119 | -------------------------------------------------------------------------------- /forms/chat/message_simple.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Conversation Message", 3 | "action": { "bind": { "model": "chat.message", "option": {} } }, 4 | "layout": { 5 | "actions": [ 6 | { 7 | "title": "返回", 8 | "icon": "icon-arrow-left", 9 | "showWhenAdd": true, 10 | "showWhenView": true, 11 | "action": [ 12 | { "name": "CloseModal", "type": "Common.closeModal", "payload": {} } 13 | ] 14 | }, 15 | { 16 | "title": "保存", 17 | "icon": "icon-check", 18 | "style": "primary", 19 | "showWhenAdd": true, 20 | "action": [ 21 | { "name": "Submit", "type": "Form.submit", "payload": {} }, 22 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 23 | ] 24 | }, 25 | { 26 | "action": [ 27 | { 28 | "name": "Confirm", 29 | "type": "Common.confirm", 30 | "payload": { 31 | "title": "确认删除", 32 | "content": "删除后不可撤销!" 33 | } 34 | }, 35 | { 36 | "name": "Delete", 37 | "payload": { "model": "chat.message" }, 38 | "type": "Form.delete" 39 | }, 40 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 41 | ], 42 | "icon": "icon-trash-2", 43 | "style": "danger", 44 | "title": "Delete" 45 | } 46 | ], 47 | "form": { 48 | "sections": [ 49 | { 50 | "columns": [ 51 | { "name": "ID", "width": 6 }, 52 | { "name": "会话ID", "width": 6 }, 53 | { "name": "创建时间", "width": 6 }, 54 | { "name": "请求时间(秒)", "width": 6 }, 55 | { "name": "发送的消息", "width": 24 }, 56 | { "name": "回复的消息", "width": 24 } 57 | ] 58 | } 59 | ] 60 | }, 61 | "primary": "id" 62 | }, 63 | "fields": { 64 | "form": { 65 | "AI": { 66 | "bind": "ai_user", 67 | "edit": { "props": { "placeholder": "请输入 AI" }, "type": "Input" }, 68 | "view": { "props": {}, "type": "" } 69 | }, 70 | "ID": { 71 | "bind": "id", 72 | "edit": { 73 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 74 | "type": "InputNumber" 75 | } 76 | }, 77 | "创建时间": { 78 | "bind": "created", 79 | "edit": { 80 | "props": { 81 | "placeholder": "请选择 创建时间", 82 | "showTime": { "format": "HH:mm:ss" } 83 | }, 84 | "type": "DatePicker" 85 | } 86 | }, 87 | "发送的消息": { 88 | "bind": "prompt", 89 | "edit": { 90 | "props": { 91 | "autoSize": { "maxRows": 6, "minRows": 2 }, 92 | "placeholder": "请输入 发送的消息" 93 | }, 94 | "type": "TextArea" 95 | } 96 | }, 97 | "回复Token数量": { 98 | "bind": "completion_tokens", 99 | "edit": { 100 | "props": { "placeholder": "请输入 回复Token数量" }, 101 | "type": "InputNumber" 102 | } 103 | }, 104 | "回复的消息": { 105 | "bind": "completion", 106 | "edit": { 107 | "props": { 108 | "autoSize": { "minRows": 6 }, 109 | "placeholder": "请输入 回复的消息" 110 | }, 111 | "type": "TextArea" 112 | } 113 | }, 114 | 115 | "用户": { 116 | "bind": "end_user", 117 | "edit": { "props": { "placeholder": "请输入 用户" }, "type": "Input" }, 118 | "view": { "props": {}, "type": "" } 119 | }, 120 | "请求时间(秒)": { 121 | "bind": "request_total_time", 122 | "edit": { 123 | "props": { "placeholder": "请输入 请求时间(秒)" }, 124 | "type": "InputNumber" 125 | } 126 | } 127 | } 128 | }, 129 | "config": { "full": true } 130 | } 131 | -------------------------------------------------------------------------------- /forms/chat/prompttemplate.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Prompt Template", 3 | "action": { "bind": { "model": "chat.prompttemplate" } }, 4 | "layout": { 5 | "actions": [ 6 | { 7 | "title": "返回", 8 | "icon": "icon-arrow-left", 9 | "showWhenAdd": true, 10 | "showWhenView": true, 11 | "action": [ 12 | { "name": "CloseModal", "type": "Common.closeModal", "payload": {} } 13 | ] 14 | }, 15 | { 16 | "title": "保存", 17 | "icon": "icon-check", 18 | "style": "primary", 19 | "showWhenAdd": true, 20 | "action": [ 21 | { "name": "Submit", "type": "Form.submit", "payload": {} }, 22 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 23 | ] 24 | }, 25 | { 26 | "action": [ 27 | { 28 | "name": "Confirm", 29 | "type": "Common.confirm", 30 | "payload": { 31 | "title": "确认删除", 32 | "content": "删除后不可撤销!" 33 | } 34 | }, 35 | { 36 | "name": "Delete", 37 | "payload": { "model": "ai.chatlog" }, 38 | "type": "Form.delete" 39 | }, 40 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 41 | ], 42 | "icon": "icon-trash-2", 43 | "style": "danger", 44 | "title": "Delete" 45 | } 46 | ], 47 | "config": { "full": true }, 48 | "form": { 49 | "sections": [ 50 | { 51 | "columns": [ 52 | { "name": "提示", "width": 12 }, 53 | { "name": "状态", "width": 12 }, 54 | { "name": "模板内容", "width": 24 } 55 | ] 56 | } 57 | ] 58 | }, 59 | 60 | "primary": "id" 61 | }, 62 | "fields": { 63 | "form": { 64 | "ID": { 65 | "bind": "id", 66 | "edit": { 67 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 68 | "type": "InputNumber" 69 | } 70 | }, 71 | "提示": { 72 | "bind": "title", 73 | "edit": { "props": { "placeholder": "请输入 提示" }, "type": "Input" }, 74 | "view": { "props": {}, "type": "" } 75 | }, 76 | "模板内容": { 77 | "bind": "content", 78 | "edit": { 79 | "props": { 80 | "autoSize": { "minRows": 6 }, 81 | "placeholder": "请输入 模板内容" 82 | }, 83 | "type": "TextArea" 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /forms/chat/sensitive_word.form.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Sensitive Word", 3 | "action": { "bind": { "model": "chat.sensitive_word" } }, 4 | "layout": { 5 | "actions": [ 6 | { 7 | "title": "返回", 8 | "icon": "icon-arrow-left", 9 | "showWhenAdd": true, 10 | "showWhenView": true, 11 | "action": [ 12 | { "name": "CloseModal", "type": "Common.closeModal", "payload": {} } 13 | ] 14 | }, 15 | { 16 | "title": "保存", 17 | "icon": "icon-check", 18 | "style": "primary", 19 | "showWhenAdd": true, 20 | "action": [ 21 | { "name": "Submit", "type": "Form.submit", "payload": {} }, 22 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 23 | ] 24 | }, 25 | { 26 | "action": [ 27 | { 28 | "name": "Confirm", 29 | "type": "Common.confirm", 30 | "payload": { 31 | "title": "确认删除", 32 | "content": "删除后不可撤销!" 33 | } 34 | }, 35 | { 36 | "name": "Delete", 37 | "payload": { "model": "ai.chatlog" }, 38 | "type": "Form.delete" 39 | }, 40 | { "name": "Back", "type": "Common.closeModal", "payload": {} } 41 | ], 42 | "icon": "icon-trash-2", 43 | "style": "danger", 44 | "title": "Delete" 45 | } 46 | ], 47 | "form": { 48 | "sections": [ 49 | { 50 | "columns": [ 51 | { "name": "ID", "width": 12 }, 52 | { "name": "敏感词", "width": 12 }, 53 | { "name": "创建时间", "width": 12 }, 54 | { "name": "更新时间", "width": 12 } 55 | ] 56 | } 57 | ] 58 | }, 59 | "primary": "id" 60 | }, 61 | "fields": { 62 | "form": { 63 | "ID": { 64 | "bind": "id", 65 | "edit": { 66 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 67 | "type": "InputNumber" 68 | } 69 | }, 70 | "创建时间": { 71 | "bind": "created_at", 72 | "edit": { 73 | "props": { 74 | "placeholder": "请选择 创建时间", 75 | "showTime": { "format": "HH:mm:ss" } 76 | }, 77 | "type": "DatePicker" 78 | } 79 | }, 80 | "敏感词": { 81 | "bind": "word", 82 | "edit": { 83 | "props": { "placeholder": "请输入 敏感词" }, 84 | "type": "Input" 85 | }, 86 | "view": { "props": {}, "type": "" } 87 | }, 88 | "更新时间": { 89 | "bind": "updated_at", 90 | "edit": { 91 | "props": { 92 | "placeholder": "请选择 更新时间", 93 | "showTime": { "format": "HH:mm:ss" } 94 | }, 95 | "type": "DatePicker" 96 | } 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /front-end/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /front-end/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /front-end/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 14 | 15 | 16 | -------------------------------------------------------------------------------- /front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-project", 3 | "private": true, 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "vite --host", 7 | "build": "rm -rf ./dist && vue-tsc && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "ant-design-vue": "^3.2.15", 12 | "axios": "^1.3.3", 13 | "install": "^0.13.0", 14 | "vue": "^3.2.45", 15 | "vue-router": "^4.1.6" 16 | }, 17 | "devDependencies": { 18 | "@vitejs/plugin-vue": "^4.0.0", 19 | "typescript": "^4.9.3", 20 | "vite": "^4.1.0", 21 | "vue-tsc": "^1.0.24" 22 | } 23 | } -------------------------------------------------------------------------------- /front-end/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 32 | 64 | -------------------------------------------------------------------------------- /front-end/src/assets/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | 16 | --bg-color: #fafafa; 17 | --text-color: #010101; 18 | /* --bg-color: #213547; */ 19 | } 20 | 21 | a { 22 | font-weight: 500; 23 | color: #646cff; 24 | text-decoration: inherit; 25 | } 26 | 27 | a:hover { 28 | color: #535bf2; 29 | } 30 | 31 | body { 32 | height: 100vh; 33 | height: var(--viewport-height); 34 | font-family: Avenir, Helvetica, Arial, sans-serif; 35 | -webkit-font-smoothing: antialiased; 36 | -moz-osx-font-smoothing: grayscale; 37 | text-align: center; 38 | } 39 | 40 | #app { 41 | height: 100%; 42 | width: 100%; 43 | } 44 | 45 | h1 { 46 | font-size: 3.2em; 47 | line-height: 1.1; 48 | } 49 | 50 | button { 51 | border-radius: 8px; 52 | /* border: 1px solid transparent; */ 53 | padding: 0.6em 1.2em; 54 | font-size: 1em; 55 | font-weight: 500; 56 | font-family: inherit; 57 | background-color: #1a1a1a; 58 | cursor: pointer; 59 | transition: border-color 0.25s; 60 | } 61 | 62 | button:hover { 63 | border-color: #646cff; 64 | } 65 | 66 | button:focus, 67 | button:focus-visible { 68 | outline: 4px auto -webkit-focus-ring-color; 69 | } 70 | 71 | .card { 72 | padding: 2em; 73 | } 74 | 75 | 76 | @media (prefers-color-scheme: light) { 77 | :root { 78 | color: #213547; 79 | background-color: #ffffff; 80 | } 81 | 82 | a:hover { 83 | color: #747bff; 84 | } 85 | 86 | button { 87 | background-color: #f9f9f9; 88 | } 89 | } 90 | 91 | .light-theme { 92 | --bg-color: #ffffff; 93 | --bg-scrollbar-color: #cdd0d2; 94 | --text-color: #000000; 95 | color: #000000; 96 | background-color: var(--bg-color); 97 | } 98 | 99 | .dark-theme { 100 | 101 | --bg-color: #213547; 102 | --bg-scrollbar-color: #446480; 103 | --text-color: #FFF; 104 | color: var(--text-color); 105 | background-color: var(--bg-color); 106 | } -------------------------------------------------------------------------------- /front-end/src/chatpgt.ts: -------------------------------------------------------------------------------- 1 | export interface HttpOption { 2 | url: string; 3 | data?: any; 4 | method?: string; 5 | headers?: any; 6 | onDownloadProgress?: (progressEvent: any) => void; 7 | } 8 | 9 | export interface Response { 10 | data: T; 11 | message: string | null; 12 | status: string; 13 | } 14 | export function post({ 15 | url, 16 | data, 17 | headers, 18 | onDownloadProgress, 19 | }: HttpOption) { 20 | const p = new Promise>((resolve, reject) => { 21 | // return new Promise((resolve, reject) => { 22 | fetch(url, { 23 | method: "POST", 24 | headers: { 25 | "Content-Type": "application/json", 26 | Accept: "text/event-stream", 27 | "Cache-Control": "no-cache", 28 | Connection: "keep-alive", 29 | ...headers, 30 | }, 31 | body: JSON.stringify({ 32 | stream: true, 33 | ...data, 34 | }), 35 | }) 36 | .then((response) => { 37 | const reader = response.body?.getReader(); 38 | function readStream() { 39 | if (reader) { 40 | reader 41 | .read() 42 | .then(({ value, done }) => { 43 | const data = new TextDecoder().decode(value); 44 | // console.log(`data :${data}`); 45 | // console.log(`done:${done}`); 46 | if (!done) { 47 | const lines = data.split("\n\n"); 48 | for (const key in lines) { 49 | const line = lines[key]; 50 | if (line.startsWith("event:session_id\ndata:")) { 51 | let sesseion_id = line.substring( 52 | "event:session_id\ndata:".length 53 | ); 54 | onDownloadProgress?.({ sesseion_id: sesseion_id }); 55 | } else { 56 | // 有可能有nil值,还不知是哪里来的 57 | if (!line.includes("event:messages")) { 58 | continue; 59 | } 60 | let newLines = line.replace(/event:messages\n/g, ""); 61 | newLines = newLines.replace(/data:/g, ""); 62 | onDownloadProgress?.({ message: newLines }); 63 | } 64 | } 65 | 66 | return readStream(); 67 | } else { 68 | // eslint-disable-next-line no-console 69 | console.log("done"); 70 | resolve({ 71 | data: { data: "" }, 72 | message: "", 73 | status: 200, 74 | } as unknown as Response); 75 | } 76 | }) 77 | .catch((error) => { 78 | reject(error); 79 | }); 80 | } 81 | } 82 | return readStream(); 83 | }) 84 | .catch((error) => { 85 | reject(error); 86 | }); 87 | }); 88 | return p; 89 | // return Promise.resolve({ data: '' } as Response) 90 | } 91 | -------------------------------------------------------------------------------- /front-end/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | // import Antd from "ant-design-vue"; 5 | 6 | import "ant-design-vue/dist/antd.css"; 7 | import "./assets/style.css"; 8 | import { setupComponents } from "./plugin"; 9 | 10 | const app = createApp(App); 11 | app.use(router); 12 | setupComponents(app); 13 | app.mount("#app"); 14 | // createApp(App).use(router).use(Antd).mount("#app"); 15 | -------------------------------------------------------------------------------- /front-end/src/plugin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 手动引入组件注册 3 | * 如果在意unplugin-vue-components插件的自动引入性能问题,可以考虑该方式 4 | */ 5 | import { 6 | // Alert, 7 | Avatar, 8 | // Breadcrumb, 9 | Button, 10 | // Card, 11 | // Col, 12 | // DatePicker, 13 | // Divider, 14 | // Dropdown, 15 | // Form, 16 | Input, 17 | Switch, 18 | // Layout, 19 | // Menu, 20 | // Popconfirm, 21 | // Row, 22 | Select, 23 | // Space, 24 | // Spin, 25 | // Table as AntdTable, 26 | } from "ant-design-vue"; 27 | 28 | import type { App } from "vue"; 29 | 30 | export function setupComponents(app: App) { 31 | app 32 | // .use(Alert) 33 | .use(Avatar) 34 | // .use(Breadcrumb) 35 | .use(Button) 36 | .use(Switch) 37 | // .use(Card) 38 | // .use(Col) 39 | // .use(DatePicker) 40 | // .use(Divider) 41 | // .use(Dropdown) 42 | // .use(Form) 43 | .use(Input) 44 | // .use(Layout) 45 | // .use(Menu) 46 | // .use(Popconfirm) 47 | // .use(Row) 48 | .use(Select); 49 | // .use(Space) 50 | // .use(Spin) 51 | // .use(AntdTable); 52 | } 53 | -------------------------------------------------------------------------------- /front-end/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from "vue-router"; 2 | 3 | const routes = [ 4 | { 5 | // path: '/', 6 | // name: 'chat', 7 | // component: () => import('../views/Chat.vue') 8 | path: "/", 9 | name: "talk", 10 | component: () => import("../views/Talk.vue"), 11 | }, 12 | { 13 | // path: '/', 14 | // name: 'chat', 15 | // component: () => import('../views/Chat.vue') 16 | path: "/talk2", 17 | name: "talk2", 18 | component: () => import("../views/Talk2.vue"), 19 | }, 20 | ]; 21 | 22 | const router = createRouter({ 23 | history: createWebHistory(import.meta.env.BASE_URL), 24 | routes, 25 | }); 26 | 27 | export default router; 28 | -------------------------------------------------------------------------------- /front-end/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /front-end/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": ["ESNext", "DOM"], 13 | "skipLibCheck": true, 14 | "noEmit": true 15 | }, 16 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 17 | "references": [{ "path": "./tsconfig.node.json" }] 18 | } 19 | -------------------------------------------------------------------------------- /front-end/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /front-end/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | server: { 8 | proxy: { 9 | "/api": { 10 | target: "http://192.168.126.141:5099/api", 11 | changeOrigin: true, 12 | rewrite: (path: string) => path.replace(/^\/api/, ""), 13 | }, 14 | }, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /icons/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/icons/404.png -------------------------------------------------------------------------------- /icons/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/icons/app.icns -------------------------------------------------------------------------------- /icons/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/icons/app.ico -------------------------------------------------------------------------------- /icons/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwsheng009/yao-chatgpt/43c75eee90912e832715ded8583df3ac53a92cbb/icons/app.png -------------------------------------------------------------------------------- /langs/en/global.yml: -------------------------------------------------------------------------------- 1 | AI Chat Application: AI Chat Application 2 | Chat with AI: Chat with AI 3 | System User Setting: System User Setting 4 | System Setting: System Setting 5 | System Menu Setting: System Menu Setting 6 | Chat Log: Chat Log 7 | AI Connect Setting: AI Connect Setting 8 | Admin Login: Admin Login 9 | User Login: User Login 10 | -------------------------------------------------------------------------------- /langs/zh-cn/global.yml: -------------------------------------------------------------------------------- 1 | AI Chat Application: AI聊天应用 2 | Chat with AI: 与AI机器人聊天 3 | System User Setting: 系统用户设置 4 | System Setting: 系统设置 5 | System Menu Setting: 系统菜单设置 6 | Chat Log: 聊天日志 7 | AI Connect Setting: AI连接设置 8 | Admin Login: 管理员登录 9 | User Login: 普通用户登录 10 | Create: 创建 11 | AI API Setting: AI 接口设置 12 | APP Menu: 应用菜单 13 | Report: 报表 14 | Setting: 系统设置 15 | Another yao application: 另一个Yao应用 16 | Chat Conversation: AI会话 17 | Chat Message: 会话消息 18 | AI model: AI模型 19 | AI model permission: AI模型权限 20 | Sensitive Word: 敏感词 21 | Prompt Template: 提示模板 22 | Chat Conversation Message: 聊天会话消息 23 | Chat Wit AI: 与AI对话 24 | -------------------------------------------------------------------------------- /lists/doc/vector.list.yao: -------------------------------------------------------------------------------- 1 | { 2 | "action": { 3 | "bind": { 4 | "table": "doc.vector" 5 | } 6 | }, 7 | "fields": { 8 | "list": { 9 | "内容": { 10 | "bind": "content", 11 | "edit": { 12 | "props": { 13 | "itemProps": { 14 | "tooltip": "内容" 15 | } 16 | }, 17 | "type": "TextArea" 18 | } 19 | }, 20 | "嵌入": { 21 | "bind": "embedding", 22 | "edit": { 23 | "props": { 24 | "itemProps": { 25 | "tooltip": "嵌入" 26 | } 27 | }, 28 | "type": "TextArea" 29 | } 30 | }, 31 | "索引": { 32 | "bind": "index", 33 | "edit": { 34 | "props": { 35 | "itemProps": { 36 | "rules": [ 37 | { 38 | "type": "integer" 39 | } 40 | ], 41 | "tooltip": "索引" 42 | } 43 | }, 44 | "type": "InputNumber" 45 | } 46 | } 47 | } 48 | }, 49 | "layout": { 50 | "list": { 51 | "columns": [ 52 | { 53 | "name": "索引", 54 | "width": 6 55 | }, 56 | { 57 | "name": "内容", 58 | "width": 24 59 | }, 60 | { 61 | "name": "嵌入", 62 | "width": 24 63 | } 64 | ] 65 | } 66 | }, 67 | "name": "doc_vector" 68 | } -------------------------------------------------------------------------------- /logins/admin.login.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Admin Login", 3 | "action": { 4 | "process": "yao.login.Admin", 5 | "args": [":payload"] 6 | }, 7 | "layout": { 8 | "entry": "/x/Dashboard/ai", 9 | "slogan": "::Chat with AI", 10 | "site": "https://yaoapps.com?from=instance-admin-login" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /logins/user.login.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::User Login", 3 | "action": { 4 | "process": "scripts.user.Login", 5 | "args": [":payload"] 6 | }, 7 | "layout": { 8 | "entry": "/x/Table/pet", 9 | "slogan": "::Make Your Dream With Yao App Engine", 10 | "site": "https://yaoapps.com/?from=instance-user-login" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /models/admin/menu.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "菜单", 3 | "table": { 4 | "name": "xiang_menu", 5 | "comment": "菜单表", 6 | "engine": "InnoDB" 7 | }, 8 | "columns": [ 9 | { 10 | "name": "id", 11 | "type": "ID", 12 | "comment": "ID" 13 | }, 14 | { 15 | "name": "parent", 16 | "type": "bigInteger", 17 | "comment": "父节点", 18 | "nullable": true, 19 | "index": true 20 | }, 21 | { 22 | "name": "name", 23 | "type": "string", 24 | "length": 200, 25 | "comment": "名称", 26 | "index": true 27 | }, 28 | { 29 | "name": "icon", 30 | "type": "string", 31 | "length": 200, 32 | "comment": "图标", 33 | "nullable": true 34 | }, 35 | { 36 | "name": "blocks", 37 | "type": "boolean", 38 | "comment": "block布局", 39 | "default": false, 40 | "index": true 41 | }, 42 | { 43 | "name": "visible_menu", 44 | "type": "boolean", 45 | "comment": "显示", 46 | "default": false, 47 | "index": true 48 | }, 49 | { 50 | "name": "path", 51 | "type": "string", 52 | "length": 200, 53 | "comment": "路由", 54 | "index": true 55 | }, 56 | { 57 | "name": "rank", 58 | "type": "integer", 59 | "default": 9999, 60 | "comment": "排列", 61 | "index": true 62 | }, 63 | { 64 | "label": "状态", 65 | "name": "status", 66 | "type": "enum", 67 | "default": "enabled", 68 | "option": ["enabled", "disabled"], 69 | "comment": "菜单状态 enabled 开启, disabled 关闭", 70 | "index": true 71 | } 72 | ], 73 | "relations": { 74 | "children": { 75 | "type": "hasMany", 76 | "model": "xiang.menu", 77 | "key": "parent", 78 | "foreign": "id", 79 | "query": { 80 | "select": ["name", "icon", "blocks", "path", "rank", "status"] 81 | } 82 | } 83 | }, 84 | "values": [ 85 | { 86 | "name": "数据看板", 87 | "path": "/kanban", 88 | "icon": "icon-activity", 89 | "rank": 1, 90 | "status": "enabled", 91 | "visible_menu": 0, 92 | "blocks": 1 93 | }, 94 | { 95 | "name": "云服务库", 96 | "path": "/table/service", 97 | "icon": "icon-cloud", 98 | "rank": 2, 99 | "status": "enabled", 100 | "visible_menu": 0, 101 | "blocks": 0, 102 | "children": [ 103 | { 104 | "name": "云服务库", 105 | "path": "/table/service", 106 | "icon": "icon-list" 107 | }, 108 | { 109 | "name": "创建云服务库", 110 | "path": "/form/service/0", 111 | "icon": "icon-plus" 112 | } 113 | ] 114 | } 115 | ], 116 | "indexes": [], 117 | "option": { 118 | "timestamps": true, 119 | "soft_deletes": true 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /models/ai/chatlog.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI Chat Log", 3 | "table": { 4 | "name": "ai_chat_log", 5 | "label": "AI日志" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "question", 15 | "label": "提问", 16 | "type": "string", 17 | "length": 1024, 18 | "nullable": false 19 | }, 20 | { 21 | "name": "answer", 22 | "label": "回答", 23 | "type": "text", 24 | "nullable": false 25 | } 26 | ], 27 | "relations": {}, 28 | "values": [], 29 | "indexes": [], 30 | "option": { 31 | "timestamps": true, 32 | "soft_deletes": true 33 | } 34 | } -------------------------------------------------------------------------------- /models/ai/model.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI Model", 3 | "table": { 4 | "name": "ai_model", 5 | "label": "AI模型列表" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "idx", 15 | "label": "模型标识", 16 | "type": "string", 17 | "nullable": false, 18 | "index": true 19 | }, 20 | { 21 | "name": "title", 22 | "label": "标题", 23 | "type": "string" 24 | }, 25 | { 26 | "name": "Remark", 27 | "label": "备注", 28 | "type": "string" 29 | }, 30 | { 31 | "name": "score", 32 | "label": "评分", 33 | "type": "integer" 34 | }, 35 | { 36 | "name": "object", 37 | "label": "类型", 38 | "type": "string", 39 | "nullable": false 40 | }, 41 | { 42 | "label": "状态", 43 | "comment": "Status", 44 | "name": "status", 45 | "type": "enum", 46 | "default": "enabled", 47 | "option": [ 48 | "enabled", 49 | "disabled" 50 | ], 51 | "index": true, 52 | "validations": [ 53 | { 54 | "method": "typeof", 55 | "args": [ 56 | "string" 57 | ], 58 | "message": "{{input}} Error, {{label}} should be string" 59 | }, 60 | { 61 | "method": "enum", 62 | "args": [ 63 | "enabled", 64 | "disabled" 65 | ], 66 | "message": "{{input}} Error, {{label}} should be enabled/disabled" 67 | } 68 | ] 69 | }, 70 | { 71 | "name": "owned_by", 72 | "label": "拥有者", 73 | "type": "string", 74 | "nullable": false 75 | }, 76 | { 77 | "name": "created", 78 | "label": "创建时间", 79 | "type": "datetime" 80 | }, 81 | { 82 | "name": "parent", 83 | "label": "上级", 84 | "type": "string" 85 | }, 86 | { 87 | "name": "root", 88 | "label": "根节点", 89 | "type": "string" 90 | } 91 | ], 92 | "relations": { 93 | "permissions": { 94 | "type": "hasMany", 95 | "model": "ai.permission", 96 | "key": "model_id", 97 | "foreign": "idx" 98 | } 99 | }, 100 | "values": [], 101 | "indexes": [], 102 | "option": { 103 | "timestamps": false, 104 | "soft_deletes": false 105 | } 106 | } -------------------------------------------------------------------------------- /models/ai/permission.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::AI Model Permission", 3 | "table": { 4 | "name": "ai_permission", 5 | "comment": "AI模型权限列表" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "idx", 15 | "label": "权限ID", 16 | "type": "string" 17 | }, 18 | { 19 | "name": "model_id", 20 | "label": "模型标识", 21 | "type": "string", 22 | "index": true 23 | }, 24 | { 25 | "name": "organization", 26 | "label": "组织", 27 | "type": "string" 28 | }, 29 | { 30 | "name": "object", 31 | "label": "对象", 32 | "type": "string" 33 | }, 34 | { 35 | "name": "group", 36 | "label": "组", 37 | "type": "string" 38 | }, 39 | { 40 | "name": "allow_create_engine", 41 | "label": "allow_create_engine", 42 | "type": "boolean", 43 | "default": false 44 | }, 45 | { 46 | "name": "allow_fine_tuning", 47 | "label": "allow_fine_tuning", 48 | "type": "boolean", 49 | "default": false 50 | }, 51 | { 52 | "name": "allow_sampling", 53 | "label": "allow_sampling", 54 | "type": "boolean", 55 | "default": false 56 | }, 57 | { 58 | "name": "allow_search_indices", 59 | "label": "allow_search_indices", 60 | "type": "boolean", 61 | "default": false 62 | }, 63 | { 64 | "name": "is_blocking", 65 | "label": "is_blocking", 66 | "type": "boolean", 67 | "default": false 68 | }, 69 | { 70 | "name": "allow_logprobs", 71 | "label": "allow_logprobs", 72 | "type": "boolean", 73 | "default": false 74 | }, 75 | { 76 | "name": "allow_view", 77 | "label": "allow_view", 78 | "type": "boolean", 79 | "default": false 80 | }, 81 | { 82 | "name": "created", 83 | "label": "created", 84 | "type": "datetime" 85 | } 86 | ], 87 | "relations": { 88 | "model": { 89 | "type": "hasOne", 90 | "model": "ai.model", 91 | "key": "idx", 92 | "foreign": "model_id", 93 | "query": {} 94 | } 95 | }, 96 | "values": [], 97 | "indexes": [], 98 | "option": { 99 | "timestamps": false, 100 | "soft_deletes": false 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /models/chat/conversation.mod.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Conversation", 3 | "table": { 4 | "name": "chat_conversation", 5 | "comment": "AI 会话" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "uuid", 15 | "label": "会话ID", 16 | "type": "string", 17 | "nullable": false 18 | }, 19 | { 20 | "name": "id_str", 21 | "label": "会话ID", 22 | "type": "string", 23 | "nullable": true 24 | }, 25 | { 26 | "name": "title", 27 | "label": "标题", 28 | "type": "text", 29 | "index": true, 30 | "nullable": false 31 | }, 32 | { 33 | "name": "cookie", 34 | "label": "cookie", 35 | "type": "string", 36 | "nullable": true 37 | }, 38 | { 39 | "name": "description", 40 | "label": "描述", 41 | "type": "string", 42 | "nullable": true 43 | }, 44 | { 45 | "name": "api_setting", 46 | "label": "API设置", 47 | "type": "integer", 48 | "nullable": false 49 | } 50 | ], 51 | "relations": { 52 | "messages": { 53 | "type": "hasMany", 54 | "model": "chat.message", 55 | "key": "conversation_id", 56 | "foreign": "id", 57 | "query": {} 58 | } 59 | }, 60 | "values": [], 61 | "indexes": [], 62 | "option": { 63 | "timestamps": true, 64 | "soft_deletes": false 65 | } 66 | } 67 | //yao migrate -n chat.conversation -------------------------------------------------------------------------------- /models/chat/message.mod.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Message", 3 | "table": { 4 | "name": "chat_message", 5 | "comment": "AI 会话消息" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "conversation_id", 15 | "label": "会话ID", 16 | "type": "integer", 17 | "nullable": false 18 | }, 19 | { 20 | "name": "model", 21 | "label": "模型", 22 | "type": "string", 23 | "nullable": false, 24 | "index": true 25 | }, 26 | { 27 | "name": "object", 28 | "label": "对象", 29 | "type": "string", 30 | "nullable": true, 31 | "index": true 32 | }, 33 | { 34 | "name": "ai_user", 35 | "label": "AI", 36 | "type": "string", 37 | "index": true, 38 | "nullable": false 39 | }, 40 | { 41 | "name": "end_user", 42 | "label": "用户", 43 | "type": "string", 44 | "index": true, 45 | "nullable": false 46 | }, 47 | { 48 | "name": "prompt", 49 | "label": "发送的消息", 50 | "type": "text", 51 | "nullable": false 52 | }, 53 | { 54 | "name": "think", 55 | "label": "思考的消息", 56 | "type": "text", 57 | "nullable": true 58 | }, 59 | { 60 | "name": "message_id", 61 | "label": "消息ID", 62 | "type": "integer", 63 | "nullable": true 64 | }, 65 | { 66 | "name": "message_uuid", 67 | "label": "消息UUID", 68 | "comment": "消息UUID", 69 | "type": "string", 70 | "nullable": true 71 | }, 72 | { 73 | "name": "completion", 74 | "label": "回复的消息", 75 | "type": "text", 76 | "nullable": false 77 | }, 78 | { 79 | "name": "search_result", 80 | "label": "搜索结果", 81 | "type": "json", 82 | "nullable": true 83 | }, 84 | { 85 | "name": "prompt_len", 86 | "label": "prompt消息长度", 87 | "type": "integer" 88 | }, 89 | { 90 | "name": "completion_len", 91 | "label": "completion消息长度", 92 | "type": "integer" 93 | }, 94 | { 95 | "name": "completion_tokens", 96 | "label": "回复Token数量", 97 | "type": "integer", 98 | "default": 0 99 | }, 100 | { 101 | "name": "prompt_tokens", 102 | "label": "提问Token数量", 103 | "type": "integer", 104 | "default": 0 105 | }, 106 | { 107 | "name": "total_tokens", 108 | "label": "总Token数量", 109 | "type": "integer", 110 | "default": 0 111 | }, 112 | { 113 | "name": "request_total_time", 114 | "label": "请求时间(秒)", 115 | "type": "float", 116 | "default": 0 117 | }, 118 | { 119 | "name": "created", 120 | "label": "创建时间", 121 | "type": "datetime", 122 | "nullable": true 123 | } 124 | ], 125 | "relations": {}, 126 | "values": [], 127 | "indexes": [], 128 | "option": { 129 | "timestamps": true, 130 | "soft_deletes": false 131 | } 132 | } 133 | // yao migrate --reset -n chat.message -------------------------------------------------------------------------------- /models/chat/prompttemplate.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Prompt Template", 3 | "table": { 4 | "name": "prompt_template", 5 | "comment": "提示模板" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "title", 15 | "label": "提示", 16 | "type": "string", 17 | "index": true, 18 | "nullable": false 19 | }, 20 | { 21 | "name": "content", 22 | "label": "模板内容", 23 | "type": "text" 24 | }, 25 | { 26 | "label": "状态", 27 | "comment": "Status", 28 | "name": "status", 29 | "type": "enum", 30 | "default": "enabled", 31 | "option": ["enabled", "disabled"], 32 | "validations": [ 33 | { 34 | "method": "typeof", 35 | "args": ["string"], 36 | "message": "::{{input}} Error, {{label}} should be string" 37 | }, 38 | { 39 | "method": "enum", 40 | "args": ["enabled", "disabled"], 41 | "message": "::{{input}} Error, {{label}} should be enabled/disabled" 42 | } 43 | ] 44 | } 45 | ], 46 | "option": { 47 | "timestamps": true, 48 | "soft_deletes": false 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /models/chat/sensitive_word.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Sensitive Word", 3 | "table": { 4 | "name": "sensitive_word", 5 | "comment": "敏感词" 6 | }, 7 | "columns": [ 8 | { 9 | "name": "id", 10 | "label": "ID", 11 | "type": "ID" 12 | }, 13 | { 14 | "name": "word", 15 | "label": "敏感词", 16 | "type": "string", 17 | "nullable": false, 18 | "index": true 19 | } 20 | ], 21 | "relations": {}, 22 | "values": [], 23 | "indexes": [], 24 | "option": { 25 | "timestamps": true, 26 | "soft_deletes": false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /models/doc/file.mod.yao: -------------------------------------------------------------------------------- 1 | { 2 | "columns": [ 3 | { 4 | "comment": "ID", 5 | "label": "ID", 6 | "name": "id", 7 | "primary": true, 8 | "type": "ID" 9 | }, 10 | { 11 | "comment": "文件名", 12 | "index": true, 13 | "label": "文件名", 14 | "name": "filename", 15 | "type": "string" 16 | }, 17 | { 18 | "comment": "路径", 19 | "label": "路径", 20 | "name": "path", 21 | "nullable": true, 22 | "type": "text" 23 | }, 24 | { 25 | "comment": "标题", 26 | "label": "标题", 27 | "name": "title", 28 | "nullable": true, 29 | "type": "string" 30 | }, 31 | { 32 | "comment": "文件类型", 33 | "label": "文件类型", 34 | "name": "flile_type", 35 | "nullable": true, 36 | "type": "string" 37 | }, 38 | { 39 | "comment": "分隔器", 40 | "label": "分隔器", 41 | "name": "splitter", 42 | "nullable": true, 43 | "type": "string" 44 | }, 45 | { 46 | "comment": "任务标识", 47 | "label": "任务标识", 48 | "name": "task_id", 49 | "nullable": true, 50 | "type": "integer" 51 | }, 52 | { 53 | "comment": "向量数据库索引状态", 54 | "label": "向量索引状态", 55 | "name": "index_status", 56 | "nullable": true, 57 | "option": [ 58 | "new", 59 | "creating", 60 | "done" 61 | ], 62 | "type": "enum" 63 | } 64 | ], 65 | "name": "文档", 66 | "relations": { 67 | "vector": { 68 | "foreign": "id", 69 | "key": "file_id", 70 | "label": "向量", 71 | "model": "doc.vector", 72 | "name": "vector", 73 | "type": "hasMany" 74 | } 75 | }, 76 | "table": { 77 | "name": "doc_file" 78 | } 79 | } -------------------------------------------------------------------------------- /models/doc/vector.mod.yao: -------------------------------------------------------------------------------- 1 | { 2 | "columns": [ 3 | { 4 | "comment": "ID", 5 | "label": "ID", 6 | "name": "id", 7 | "primary": true, 8 | "type": "ID" 9 | }, 10 | { 11 | "comment": "索引", 12 | "label": "索引", 13 | "name": "index", 14 | "nullable": true, 15 | "type": "integer" 16 | }, 17 | { 18 | "comment": "文件ID", 19 | "label": "文件ID", 20 | "name": "file_id", 21 | "nullable": true, 22 | "type": "integer" 23 | }, 24 | { 25 | "comment": "内容", 26 | "label": "内容", 27 | "name": "content", 28 | "nullable": true, 29 | "type": "text" 30 | }, 31 | { 32 | "comment": "嵌入", 33 | "index": true, 34 | "label": "嵌入", 35 | "length": 768, 36 | "name": "embedding", 37 | "nullable": true, 38 | "type": "vector" 39 | } 40 | ], 41 | "name": "doc_vector", 42 | "relations": { 43 | "file": { 44 | "foreign": "file_id", 45 | "key": "id", 46 | "label": "文档", 47 | "model": "doc.file", 48 | "name": "file", 49 | "type": "hasOne" 50 | } 51 | }, 52 | "table": { 53 | "name": "doc_vector" 54 | } 55 | } 56 | // yao migrate -n doc.vector --reset -------------------------------------------------------------------------------- /neo/neo.yml: -------------------------------------------------------------------------------- 1 | connector: copilot-tencent 2 | use: "neo" # default assistannt 3 | guard: "scripts.auth.token.Check" 4 | 5 | 6 | store: 7 | connector: "default" #Name of the connector used to specify data storage method,default is app database connector 8 | user_field: "user_id" #User ID field name, defaults to "user_id" 9 | prefix: "yao_neo_" #Database table name prefix,will automatically create three table with prefix,+_history,+_chat,+_assistant 10 | max_size: 100 #Maximum storage size limit 11 | ttl: 3600 #Time To Live in seconds 12 | 13 | # Setting RAG settings 14 | rag: 15 | engine: #Engine the vector database engine settings 16 | dirver: "" #qdrant -> vector store driver,currently only support qdrant,empty means not using RAG 17 | options: # use the $ENV.ENV_NAME to get the environment variable 18 | host: "" 19 | api_key: "sr-" 20 | port: 80 21 | 22 | vectorizer: #Vectorizer the text vectorizer settings , use the $ENV.ENV_NAME to get the environment variable 23 | dirver: "" #openai -> embeddings driver,currently only support openai 24 | options: 25 | model: "text-embedding-ada-002" #default model for openai 26 | api_key: "sr-" 27 | 28 | upload: #Upload the file upload settings 29 | async: false 30 | allowed_types: ["image/jpeg", "image/png", "image/jpg"] 31 | chunk_size: 1024 32 | chunk_overlap: 256 33 | index_prefix: "yao_neo_" 34 | 35 | vision: 36 | storage: 37 | driver: "local" #local -> local storage, s3 -> s3 storage 38 | options: #local storage options 39 | path: "./storage/vision" #required for local storage 40 | compression: true #whether to compress the image 41 | base_url: "" # base url for the downloading file 42 | preview_url: "" # preview url for the file 43 | 44 | # options: #s3 storage options 45 | # expiration: "" 46 | # endpoint: "" 47 | # region: "auto" 48 | # key: "" 49 | # secret: "" 50 | # bucket: "" 51 | # prefix: "" 52 | # compression: true 53 | 54 | 55 | model: 56 | driver: "openai" #openai -> openai vision model 57 | options: 58 | model: "openai/clip-vit-base-patch32" 59 | api_key: "sr-" 60 | compression: true 61 | prompt: "" 62 | 63 | # prepare: "scripts.vector.Match" 64 | prompts: 65 | - role: system 66 | content: | 67 | - You are pretending to be YAO's AI assistant and your name is Neo. 68 | - Answer my questions in Chinese from now on. 69 | 70 | option: #options for chatting with the assistant 71 | temperature: 0.8 72 | 73 | 74 | create: "scripts.neo.neo.Create" #create function for the assistant 选择assistant 75 | 76 | prepare: "scripts.neo.neo.prepare" #prepare function for the assistant不再使用,使用assistant代替 77 | write: "scripts.neo.neo.write" #write function for the assistant 不再使用,使用assistant代替 78 | 79 | 80 | allows: 81 | - "http://127.0.0.1:8000" 82 | - "http://127.0.0.1:5099" 83 | - "http://localhost:5099" 84 | - "http://localhost:8000" 85 | 86 | 87 | connectors: 88 | doubao: 89 | tools: true -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yao-init", 3 | "version": "1.0.0", 4 | "description": "yao application for nodejs usage", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node -r ts-node/register -r tsconfig-paths/register scripts/system/table.ts", 8 | "lint": "eslint", 9 | "lint:fix": "eslint --fix" 10 | }, 11 | "keywords": [], 12 | "author": "vincentwwsheng@gmail.com", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@eslint/js": "^9.10.0", 16 | "@stylistic/eslint-plugin": "^2.8.0", 17 | "@types/jest": "^29.5.13", 18 | "@types/node": "^22.5.5", 19 | "@typescript-eslint/eslint-plugin": "^8.5.0", 20 | "@typescript-eslint/parser": "^8.5.0", 21 | "@yaoapps/client": "npm:yao-node-client@^1.1.3", 22 | "@yaoapps/types": "npm:yao-app-ts-types@^1.1.0", 23 | "eslint": "^9.10.0", 24 | "eslint-config-prettier": "^9.1.0", 25 | "eslint-plugin-prettier": "^5.2.1", 26 | "globals": "^15.9.0", 27 | "jest": "^29.7.0", 28 | "prettier": "^3.3.3", 29 | "ts-jest": "^29.2.5", 30 | "ts-node": "^10.9.2", 31 | "tsconfig-paths": "^4.2.0", 32 | "typescript": "^5.6.2", 33 | "typescript-eslint": "^8.5.0" 34 | }, 35 | "dependencies": { 36 | "node-html-parser": "^7.0.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /plugins/httpx/.env.sample: -------------------------------------------------------------------------------- 1 | HTTP_RROXY=http://127.0.0.1:10809 2 | HTTPS_RROXY=http://127.0.0.1:10809 -------------------------------------------------------------------------------- /plugins/httpx/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /plugins/httpx/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Package", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${fileDirname}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /plugins/httpx/Makefile: -------------------------------------------------------------------------------- 1 | 2 | release:clean 3 | go build -o ../httpx.so . 4 | chmod +x ../httpx.so 5 | 6 | clean: 7 | rm -f ../httpx.so -------------------------------------------------------------------------------- /plugins/httpx/go.mod: -------------------------------------------------------------------------------- 1 | module httpclient 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/hashicorp/go-hclog v1.2.0 7 | github.com/joho/godotenv v1.5.1 8 | github.com/json-iterator/go v1.1.12 9 | github.com/yaoapp/gou v0.10.3 10 | github.com/yaoapp/kun v0.9.0 11 | ) 12 | 13 | require ( 14 | github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect 15 | github.com/fatih/color v1.13.0 // indirect 16 | github.com/golang/protobuf v1.5.2 // indirect 17 | github.com/google/go-cmp v0.5.8 // indirect 18 | github.com/hashicorp/go-plugin v1.4.4 // indirect 19 | github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect 20 | github.com/mattn/go-colorable v0.1.12 // indirect 21 | github.com/mattn/go-isatty v0.0.14 // indirect 22 | github.com/miekg/dns v1.1.48 // indirect 23 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/oklog/run v1.1.0 // indirect 27 | github.com/sirupsen/logrus v1.8.1 // indirect 28 | golang.org/x/mod v0.6.0 // indirect 29 | golang.org/x/net v0.3.0 // indirect 30 | golang.org/x/sys v0.4.0 // indirect 31 | golang.org/x/text v0.5.0 // indirect 32 | golang.org/x/tools v0.2.0 // indirect 33 | google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect 34 | google.golang.org/grpc v1.46.2 // indirect 35 | google.golang.org/protobuf v1.28.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /plugins/httpx/httpx/process.go: -------------------------------------------------------------------------------- 1 | package httpx 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/yaoapp/gou/cast" 7 | "github.com/yaoapp/gou/process" 8 | ) 9 | 10 | func Register() { 11 | process.Register("httpx.client.post", processHTTPPost) 12 | } 13 | 14 | // http.Post 15 | // args[0] URL 16 | // args[1] Payload {"foo":"bar"} ["foo", "bar", {"k1":"v1"}], "k1=v1&k2=v2", "/path/root/file", ... 17 | // args[2] Files {"foo":"/path/root/file"} 18 | // args[3] Query Params {"k1":"v1", "k2":"v2"}, ["k1=v1","k1"="v11","k2"="v2"], [{"k1":"v1"},{"k1":"v11"},{"k2":"v2"}], k1=v1&k1=v11&k2=k2 19 | // args[4] Headers {"K1":"V1","K2":"V2"} [{"K1":"V1"},{"K1":"V11"},{"K2":"V2"}] 20 | func processHTTPPost(process *process.Process) interface{} { 21 | 22 | process.ValidateArgNums(1) 23 | req, err := processHTTPNew(process, 3) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | var payload interface{} 29 | if process.NumOfArgs() > 1 { 30 | payload = process.Args[1] 31 | } 32 | 33 | // // Upload a file via payload 34 | // if req.GetHeader("Content-Type") == "multipart/form-data" { 35 | 36 | // if file, ok := payload.(string); ok { 37 | // if fileRoot != "" { 38 | // //可以使用系统的temp目录与程序中的data目录 39 | // if !strings.HasPrefix(file, os.TempDir()) { 40 | // file = filepath.Join(fileRoot, file) 41 | // } 42 | // } 43 | 44 | // fileAbs, err := filepath.Abs(file) 45 | // if err != nil { 46 | // return &http.Response{ 47 | // Status: 400, 48 | // Code: 400, 49 | // Message: fmt.Sprintf("args[%d] parameter error: %s", 2, err.Error()), 50 | // Headers: map[string][]string{}, 51 | // Data: nil, 52 | // } 53 | // } 54 | // payload = fileAbs 55 | // } 56 | // } 57 | 58 | // // Upload files via files 59 | // files := process.ArgsMap(2, map[string]interface{}{}) 60 | // for name, val := range files { 61 | // if file, ok := val.(string); ok { 62 | // if fileRoot != "" { 63 | // //可以使用系统的temp目录与程序中的data目录 64 | // if !strings.HasPrefix(file, os.TempDir()) { 65 | // file = filepath.Join(fileRoot, file) 66 | // } 67 | // } 68 | 69 | // file, err := filepath.Abs(file) 70 | // if err != nil { 71 | // return &http.Response{ 72 | // Status: 400, 73 | // Code: 400, 74 | // Message: fmt.Sprintf("args[%d] parameter error: %s", 2, err.Error()), 75 | // Headers: map[string][]string{}, 76 | // Data: nil, 77 | // } 78 | // } 79 | // req.AddFile(name, file) 80 | // } 81 | // } 82 | 83 | return req.Post(payload) 84 | } 85 | 86 | // make a *http.Request 87 | func processHTTPNew(process *process.Process, from int) (*Request, *Response) { 88 | 89 | req := New(process.ArgsString(0)) 90 | 91 | if process.NumOfArgs() > from { 92 | values, err := cast.AnyToURLValues(process.Args[from]) 93 | if err != nil { 94 | return nil, &Response{ 95 | Status: 400, 96 | Code: 400, 97 | Message: fmt.Sprintf("args[%d] parameter error: %s", from, err.Error()), 98 | Headers: map[string][]string{}, 99 | Data: nil, 100 | } 101 | } 102 | req.WithQuery(values) 103 | } 104 | 105 | if process.NumOfArgs() > from+1 { 106 | headers, err := cast.AnyToHeaders(process.Args[from+1]) 107 | if err != nil { 108 | return nil, &Response{ 109 | Status: 400, 110 | Code: 400, 111 | Message: fmt.Sprintf("args[%d] parameter error: %s", from+1, err.Error()), 112 | Headers: map[string][]string{}, 113 | Data: nil, 114 | } 115 | } 116 | req.WithHeader(headers) 117 | } 118 | 119 | return req, nil 120 | } 121 | -------------------------------------------------------------------------------- /plugins/httpx/httpx/types.go: -------------------------------------------------------------------------------- 1 | package httpx 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | // Request HTTP Request 9 | type Request struct { 10 | url string 11 | query url.Values 12 | headers http.Header 13 | files map[string]string 14 | data interface{} 15 | } 16 | 17 | // Response HTTP Response 18 | type Response struct { 19 | Status int `json:"status"` 20 | Data interface{} `json:"data"` 21 | Headers http.Header `json:"headers"` 22 | Code int `json:"code"` 23 | Message string `json:"message"` 24 | } 25 | -------------------------------------------------------------------------------- /plugins/httpx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //插件模板 4 | import ( 5 | "encoding/json" 6 | "fmt" 7 | "httpclient/httpx" 8 | "io" 9 | "log" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | 14 | "github.com/hashicorp/go-hclog" 15 | "github.com/joho/godotenv" 16 | "github.com/yaoapp/gou/process" 17 | "github.com/yaoapp/kun/grpc" 18 | ) 19 | 20 | // 定义插件类型,包含grpc.Plugin 21 | type DemoPlugin struct{ grpc.Plugin } 22 | 23 | // 设置插件日志到单独的文件 24 | func (demo *DemoPlugin) setLogFile() { 25 | var output io.Writer = os.Stdout 26 | //开启日志 27 | logroot := os.Getenv("PLUGIN_HTTPX_CLIENT") 28 | if logroot == "" { 29 | logroot = "./logs" 30 | } 31 | if logroot != "" { 32 | logfile, err := os.Create(path.Join(logroot, "plugin.log")) 33 | if err == nil { 34 | output = logfile 35 | } 36 | } 37 | demo.Plugin.SetLogger(output, grpc.Trace) 38 | } 39 | 40 | // 插件执行需要实现的方法 41 | // 参数name是在调用插件时的方法名,比如调用插件demo的Hello方法是的规则是plugins.demo.Hello时。 42 | // 43 | // 注意:name会自动的变成小写 44 | // 45 | // args参数是一个数组,需要在插件中自行解析。判断它的长度与类型,再转入具体的go类型。 46 | func (demo *DemoPlugin) Exec(name string, args ...interface{}) (*grpc.Response, error) { 47 | 48 | demo.Logger.Log(hclog.Trace, "plugin method called", name) 49 | demo.Logger.Log(hclog.Trace, "args", args) 50 | 51 | //输出值支持的类型:map/interface/string/integer,int/float,double/array,slice 52 | var out = httpx.Response{} 53 | switch name { 54 | case "post": 55 | if len(args) < 1 { 56 | out = httpx.Response{Status: 400, Message: "参数不足,需要一个参数"} 57 | break 58 | } 59 | var process = process.New("httpx.client.post", args...) 60 | res, err := process.Exec() 61 | if err != nil { 62 | out = httpx.Response{Status: 400, Message: err.Error()} 63 | } else { 64 | if res, ok := res.(*httpx.Response); ok { 65 | out = *res 66 | } 67 | } 68 | 69 | default: 70 | out = httpx.Response{Status: 400, Message: fmt.Sprintf("%s不支持", name)} 71 | } 72 | 73 | //所有输出需要转换成bytes 74 | bytes, err := json.Marshal(out) 75 | if err != nil { 76 | return nil, err 77 | } 78 | //支持的类型:map/interface/string/integer,int/float,double/array,slice 79 | return &grpc.Response{Bytes: bytes, Type: "map"}, nil 80 | } 81 | 82 | // 生成插件时函数名修改成main 83 | func main() { 84 | httpx.Register() 85 | plugin := &DemoPlugin{} 86 | plugin.setLogFile() 87 | grpc.Serve(plugin) 88 | } 89 | 90 | // 调试时开启,需要直接调试时修改成main 91 | func main1() { 92 | httpx.Register() 93 | environmentPath := filepath.Join(".env") 94 | err := godotenv.Load(environmentPath) 95 | 96 | if err != nil { 97 | log.Fatal("Error loading .env file") 98 | } 99 | h1 := os.Getenv("HTTP_RROXY") 100 | h2 := os.Getenv("HTTPS_PROXY") 101 | println(h1, h2) 102 | 103 | plugin := &DemoPlugin{} 104 | plugin.setLogFile() 105 | rest, err := plugin.Exec("post", "https://api.openai.com/v1/chat/completions") 106 | if err != nil { 107 | println("error", err.Error()) 108 | } 109 | println(rest) 110 | } 111 | -------------------------------------------------------------------------------- /plugins/httpx/readme.md: -------------------------------------------------------------------------------- 1 | # http client with proxy 2 | 3 | 支持代理的 http.Post 库。 4 | 5 | 假设你知道怎么使用代理 6 | 7 | ## 构建 8 | 9 | ```sh 10 | go build -o ../httpx.so . 11 | chmod +x ../httpx.so 12 | 13 | # 或是 14 | make release 15 | ``` 16 | 17 | ## 测试 18 | 19 | 设置环境变量 20 | 21 | ```sh 22 | export HTTP_RROXY=http://127.0.0.1:10809 23 | export HTTPS_RROXY=http://127.0.0.1:10809 24 | export ALL_RROXY=http://127.0.0.1:10809 25 | ``` 26 | 27 | 或是修改文件`.env` 28 | 29 | ```sh 30 | HTTP_RROXY="http://127.0.0.1:10809" 31 | HTTPS_PROXY="http://127.0.0.1:10809" 32 | ALL_RROXY=http://127.0.0.1:10809 33 | ``` 34 | 35 | ```sh 36 | cd ../../ 37 | yao run plugins.httpx.post "https://api.openai.com/v1/chat/completions" 38 | 39 | ``` 40 | -------------------------------------------------------------------------------- /public/assets/Talk2-800b822a.js: -------------------------------------------------------------------------------- 1 | /* empty css */import{_ as y,r as _,w as x,v as k,o as l,c,a as t,F as b,e as w,b as h,f as D,i as T,d as u,t as d,g as p,h as m}from"./index-63368c72.js";const S={components:{},data(){return{contentDiv:[],textarea:"",right:!0,show:!1,flag:!0,closeChat:this.close,conversation:[]}},created(){},mounted(){this.scrollToBottom()},updated(){this.scrollToBottom()},methods:{scrollToBottom(){this.$nextTick(()=>{let e=this.$el.querySelector(".talk-content");e.scrollTop=e.scrollHeight})},sendInfo(){alert("aaa")},isShow(){},iptFocus(){},async submit(){let e=this.textarea;this.textarea="";let o=this.$el.querySelector("#textinput");o.style.height="";let n={name:"你",url:"",content:e,right:!0,time:new Date().toLocaleTimeString()};this.contentDiv.push(n);let s={name:"AI",url:"",content:await this.ask(e),right:!1,time:new Date().toLocaleTimeString()};this.contentDiv.push(s)},async ask(e){this.conversation.push({u:e});let n=await(await fetch("/api/ai/ask2",{method:"POST",body:JSON.stringify({prompt:e,conversation:this.conversation}),headers:{"Content-Type":"application/json"}})).json();return this.conversation.push({ai:n}),this.checkLenAndDelete(),n},checkLenAndDelete(){var n,r;let e=0,o=0;for(let s=this.conversation.length-1;s>=0;s--){const a=this.conversation[s];if(a.u?e+=(n=a.u)==null?void 0:n.length:a.ai&&(e+=(r=a.ai)==null?void 0:r.length),e>10240){o=s;break}}console.log(o),console.log(this.conversation),this.conversation.shift(),o--},exit(){this.$emit("close",this.flag)}}},B={class:"talk"},C=t("div",{class:"talk-header"},[t("h2",null,"智能AI对话")],-1),A={class:"talk-content"},F={style:{"margin-top":"20px"}},z={key:0},L={style:{display:"flex","justify-content":"flex-end","align-items":"center"}},N={class:"name_right"},I={style:{"font-size":"0.5rem",color:"#9b9b9b"}},V={class:"url_right"},j={class:"content_right"},K={key:1},H={style:{display:"flex","align-items":"center"}},O={class:"url_left"},$={class:"name_left"},q={style:{"font-size":"0.5rem",color:"#9b9b9b"}},E={class:"content_left"},J={class:"talk-message"},M={class:"talk-message-content"},P={class:"talk-message-send"};function U(e,o,n,r,s,a){const v=_("a-avatar"),f=_("a-textarea"),g=_("a-button");return x((l(),c("div",B,[C,t("div",A,[(l(!0),c(b,null,w(s.contentDiv,i=>(l(),c("div",F,[i.right?(l(),c("div",z,[t("div",L,[t("div",N,[t("p",I,d(i.time),1)]),t("div",V,[h(v,{shape:"circle",size:30},{default:u(()=>[p("你")]),_:1})])]),t("div",j,[t("pre",null,d(i.content),1)])])):m("",!0),i.right?m("",!0):(l(),c("div",K,[t("div",H,[t("div",O,[h(v,{shape:"circle",size:30,style:{backgroundColor:"#f56a00",verticalAlign:"middle"}},{default:u(()=>[p("AI")]),_:1})]),t("div",$,[t("p",q,d(i.time),1)])]),t("div",E,[t("pre",null,d(i.content),1)])]))]))),256))]),t("div",J,[t("div",M,[h(f,{id:"textinput",style:{"overflow-y":"hidden"},value:s.textarea,"onUpdate:value":o[0]||(o[0]=i=>s.textarea=i),resize:"none",type:"textarea",rows:1,onKeypress:D(T(a.submit,["prevent"]),["enter"]),oninput:'this.style.height = "";this.style.height = this.scrollHeight + "px"'},null,8,["value","onKeypress"])]),t("div",P,[h(g,{type:"text",onClick:a.submit},{default:u(()=>[p("发送")]),_:1},8,["onClick"])])])],512)),[[k,s.flag]])}const R=y(S,[["render",U]]);export{R as default}; 2 | -------------------------------------------------------------------------------- /public/assets/talk-53fef37d.css: -------------------------------------------------------------------------------- 1 | .name_right{margin-left:10px}.name_left{margin-right:10px}.content_right{word-wrap:break-word;margin-right:15px;font-size:1rem;text-align:left;display:flex;justify-content:flex-end}.content_right .content{max-width:90%;padding:10px;border-radius:10px;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}.dark-theme .content_right .content{background-color:#1aad19;color:#fff}.input-area{border-radius:5px}.dark-theme .input-area{background-color:var(--bg-color);color:var(--text-color);border-radius:5px}.light-theme .content_right .content{background-color:#1aad19;color:#fcfcfc}.content_left{margin-left:15px;font-size:1rem;text-align:left;padding:2px;display:flex}.content_left .content{max-width:90%;padding:10px;border-radius:10px;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}.dark-theme .content_left .content{background-color:#113f53;color:#fff}.light-theme .content_left .content{background-color:#ececec;color:#000}.url_right,.url_left{margin-left:15px;margin-right:15px}::-webkit-scrollbar{height:10px!important;width:10px!important}::-webkit-scrollbar-thumb{border-radius:0;border-style:dashed;background-color:var(--bg-scrollbar-color);border-color:#e2242400;border-width:1px;background-clip:padding-box}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,.2);background:var(--bg-color)}::-webkit-scrollbar-thumb:hover{background:rgba(157,165,183,.7)}.word-break{word-break:break-all}.infinite-list-wrapper p{text-align:center}.show-input-content{height:100%;width:100%;border:1px solid black}.emotion{width:22px!important;height:22px!important;margin-right:4px;line-height:40px;top:5px}.icon{width:2em;height:2em;vertical-align:-.15em;fill:currentColor;overflow:hidden}.talk{display:flex;flex-direction:column;border-radius:15px;box-shadow:2px 2px #b3b3b3;width:100%;height:100%;margin:0 auto;-webkit-border-radius:15px;-moz-border-radius:15px;-webkit-box-shadow:0px 0px 12px #808080;-moz-box-shadow:0px 0px 12px #808080;box-shadow:0 0 12px gray;padding:10px;font-family:Verdana,Geneva,sans-serif;font-size:12pt;text-align:center}.dark-theme .talk{-webkit-box-shadow:0px 0px 12px #FFE4C4;-moz-box-shadow:0px 0px 12px #FFE4C4;box-shadow:0 0 12px bisque}.talk-header{width:100%;z-index:1;text-align:left;padding-left:20px;display:flex;align-items:center;border-bottom:1px solid #425c75}.talk-message-title{flex:1;font-size:medium;font-weight:700}.talk-role{width:200px}.dark-theme .talk-role{background-color:#949494;color:#dce1e6}.dark-theme .ant-select-selector{background-color:#949494!important;color:#dce1e6!important}.talk-role-title{margin-right:14px}.talk-header-icon{display:flex;justify-content:flex-end}.talk-content{overflow-y:auto;height:350px;flex:1}.talk-content p{margin:auto}.talk-message{display:flex;padding:10px}.talk-message-face{width:40px;padding-left:2px}.talk-message-content{width:280px;flex:1}.talk-message-send{margin:0 0 0 10px;cursor:pointer}.talk-message-send button{border:none;border-radius:4px;background-color:#1aad19;color:#fff;cursor:pointer}.talk-message-send button:hover{background-color:#2ba245}.talk-message-clear{margin:0 0 0 10px;cursor:pointer}.talk-message-clear button{margin:3px 5px;border:none;border-radius:4px;background-color:#b5b6b5;cursor:pointer}.talk-message-clear button:hover{background-color:#ebe70c}@media only screen and (max-width: 980px){.talk{width:100%}}@media only screen and (max-width: 980px){.talk-role{flex:1}.talk-message-clear button{width:60px;padding:1px;font-size:small}.talk-message-title{display:none}} 2 | -------------------------------------------------------------------------------- /public/components/chat.less.js: -------------------------------------------------------------------------------- 1 | System.register([],function(n){return{execute:function(){n("default",".box {\n display: flex;\n background-color: aquamarine;\n}\n")}}}); 2 | -------------------------------------------------------------------------------- /public/components/view/chat.js: -------------------------------------------------------------------------------- 1 | System.register(["react/jsx-runtime","react","../chat.less.js"],function(e){"use strict";var t,n,i,l;return{setters:[function(e){t=e.jsx,n=e.jsxs},function(e){i=e.default},function(e){l=e.default}],execute:function(){let{useState:s}=i;e("default",e=>{let[a,r]=s(""),[o,c]=s(void 0),[d,f]=s([{content:"你好,AI。",right:!0},{content:"你好,有什么可以帮到您。",right:!1}]);async function x(){let e=await m(a);console.log(e),f([...d,{content:e.message,right:!1}]),c(e.session_id)}async function h(){f([...d,{content:a,right:!0}])}async function m(e){let t=await fetch("/api/ai/ask",{method:"POST",body:JSON.stringify({prompt:e,session_id:o}),headers:{"Content-Type":"application/json"}});return await t.json()}i.useEffect(()=>{d&&d[d.length-1]&&!0==d[d.length-1].right&&(r(""),x())},[d]);let g=e=>{r(e.target.value)};return t("div",{className:"xgen-form-item",children:n("div",{className:"xgen-row xgen-form-item-row",children:[t("div",{className:"xgen-col xgen-form-item-label"}),t("div",{className:"xgen-col xgen-form-item-control",children:t("div",{className:"xgen-form-item-control-input",children:n("div",{className:l.box,style:{display:"flex",flexDirection:"column",height:"calc(100vh - 262px)",width:"100%"},children:[t("div",{style:{flex:1,display:"flex",width:"100%",flexDirection:"column",fontSize:"1.5rem",overflow:"auto"},children:d?.map(e=>e.right?t("div",{style:{display:"flex",justifyContent:"flex-end",margin:"10px",padding:"10px"},children:t("div",{style:{maxWidth:"80%"},children:e.content})}):t("div",{style:{display:"flex",margin:"10px",border:"solid 1px gray",borderRadius:"10px",padding:"10px"},children:t("div",{style:{maxWidth:"80%"},children:e.content})}))}),n("div",{style:{display:"flex"},children:[t("textarea",{style:{flex:1},onChange:g,value:a}),t("button",{onClick:h,children:"提问"})]})]})})})]})})})}}}); 2 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | AI聊天 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 项目名称 2 | 3 | 使用 YAO engine 编写一个调用 openai chat gpt 接口的小应用 4 | 5 | ## 简介 6 | 7 | 本项目基于 yao engine 开发的一个 ai 聊天接口应用。它的主要功能是接收用户的提问,并调用 openai chat gpt 的提供的 api 接口。可以为用户提供一个简单的聊天应用。 8 | 9 | ## AI 对话 10 | 11 | ![ai conversation](docs/ai-convesation.png) 12 | 13 | ## XGEN NEO 14 | 15 | ![neo](docs/yao-xgen-neo-assistant.png) 16 | 17 | ## 功能 18 | 19 | - 调用 chat gpt api 接口,提示会话上下文 20 | - 支持 gpt3.5 接口 21 | - 支持会话模板 22 | - 后台保存会话历史 23 | - 提供外部用户调用的 post/get 接口 24 | - 插件 httpx 支持使用代理,可以配置里使用`use_plugin`启用插件访问 openai 25 | - 提供后台系统配置,日志查看功能 26 | - 最新版本的 xgen,可使用 neo 聊天助手 27 | - 自定义 AI 聊天控件 28 | 29 | ## 0.10.3-dev 版本 30 | 31 | 使用 yao 0.10.3-dev 版本可以使用打字机效果了。 32 | 33 | ```sh 34 | git checkout v0.10.3-dev 35 | 36 | yao start 37 | ``` 38 | 39 | ## 安装 40 | 41 | 本项目依赖于 yao 引擎的开发版本 0.10.3,请先下载 yao 应用,下载地址: 42 | https://github.com/YaoApp/yao/actions/workflows/release-linux.yml 43 | 44 | 这里提供本项目的安装步骤,例如: 45 | 46 | ```sh 47 | https://github.com/wwsheng009/yao-chatgpt.git 48 | cd yao-chatgpt 49 | yao migrate 50 | yao start 51 | ``` 52 | 53 | ## 网络插件 httpx 54 | 55 | 如果你的网络可能不好,没办法直接访问 openai 接口,可以启用插件 httpx 的代替 yao 本身的`http.Post`方法。 56 | 57 | [httpx 插件](plugins/httpx/readme.md) 58 | 59 | ## 加载提问模板 60 | 61 | 这一步测试使用脚本自动下载更新会失败。 62 | 所以需要手动下载一个模板文件。 63 | 64 | ```sh 65 | wget https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/README.md -O data/中文调教指南.md.txt 66 | 67 | #执行加载脚本 68 | yao run scripts.loader.prompt_template.Run 69 | 70 | ``` 71 | 72 | ## 使用说明 73 | 74 | 前端,打开就是一个对话的界面 75 | http://127.0.0.1:5199 76 | 77 | 管理端 78 | http://127.0.0.1:5199/admin/login/admin 79 | 80 | 默认用户名: 81 | xiang@iqka.com 82 | 密码: 83 | A123456p+ 84 | 85 | 登录管理系统端后,需要在设置菜单界面输入您的 open ai 的调用 api key。或是执行以下脚本更新 86 | 87 | ```sh 88 | yao run models.ai.setting.Update 1 '::{"api_token":"你的open ai key"}' 89 | ``` 90 | 91 | ## 如何获取 openai api key. 92 | 93 | 可以在 OpenAI 官网上申请,也可以在 GitHub 上搜索,会有一些免费的 API Key 可以使用。 94 | 95 | ## 读取所有的 openai 的模型列表到本地 96 | 97 | 自动下载并更新本地 ai 模型列表,需要先更新 opnen ai 的 key 98 | 99 | ```sh 100 | yao run scripts.ai.model.UpdateModel 101 | 102 | ``` 103 | 104 | ## 管理员 105 | 106 | 删除重置系统用户,或是登录管理页面`http://127.0.0.1:5199/admin/x/Table/yao.user`进行配置 107 | 108 | ```sh 109 | yao run scripts.utils.user.ResetAdmin 18012341234 xxx@qq.com Abcd1234+ 110 | ``` 111 | 112 | ## 测试 api 接口 113 | 114 | ```sh 115 | yao run scripts.ai.chatgpt.Call '::{"prompt":"你好"}' 116 | yao run models.chat.conversation.get '::{}' 117 | yao run models.chat.message.get '::{}' 118 | ``` 119 | 120 | 如果网络不通,请使用代理。[httpx 插件](plugins/httpx/readme.md) 121 | 122 | ## 开发 123 | 124 | 如果使用编辑器 vscode 编写 json 配置文件,已支持字段[提示与格式检查](https://wwsheng009.github.io/yao-docs/Studio/%E7%BC%96%E8%BE%91%E5%99%A8vscode%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA.html#%E7%BC%96%E8%BE%91%E5%99%A8vscode%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA) 125 | 126 | ## 许可证 127 | 128 | 本项目使用 Apache License 2.0 许可证,详情请参阅 [LICENSE.txt](LICENSE) 129 | -------------------------------------------------------------------------------- /scripts/adapter/aardio.js: -------------------------------------------------------------------------------- 1 | 2 | function handler(payload) { 3 | // console.log("payload:", payload) 4 | if (payload == null) { 5 | return 0; 6 | } 7 | const content = payload.replace(/^data:/, ""); 8 | if (typeof ssEvent === "function") { 9 | ssEvent("data", content); 10 | } else { 11 | console.print(content); 12 | } 13 | return 1; 14 | } 15 | // yao run scripts.adapter.aardio.Completions 16 | function Completions(payload) { 17 | const model = payload.model; 18 | if (!model) { 19 | model = "deepseek-r1" 20 | // throw new Exception("缺少参数model", 400) 21 | } 22 | if (!payload.messages) { 23 | throw new Exception("缺少参数messages", 400) 24 | } 25 | const stream = payload.stream; 26 | const token = Process("yao.env.get", "AARDIO_TOKEN"); 27 | const base_url = Process("yao.env.get", "AARDIO_HOST"); 28 | let url = base_url + "/chat/completions"; 29 | 30 | const RequestBody = { 31 | // prompt: prompt, 32 | model, 33 | max_tokens: 4096, 34 | top_p: 1, 35 | temperature: 0.6, 36 | presence_penalty: 1, 37 | frequency_penalty: 0, 38 | stream: true, 39 | ...payload 40 | }; 41 | 42 | url = "http://127.0.0.1:9999" 43 | // aardio的token使用了二进制编码,无法处理。 44 | if (stream) { 45 | let err = http.Stream("POST", url, handler, RequestBody, null, { 46 | "Content-Type": "application/json", 47 | Authorization: `Bearer ` + token, 48 | 'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36" 49 | }); 50 | if (err.code != 200) { 51 | console.log("err:", err); 52 | throw new Exception(err.message, err.code); 53 | } 54 | return { 55 | type: "text/event-stream; charset=utf-8" 56 | } 57 | } else { 58 | throw new Exception("不支持非流式", 400) 59 | } 60 | } 61 | 62 | //yao run scripts.adapter.aardio.test 63 | function test() { 64 | 65 | 66 | // return str; 67 | const requestBody = { 68 | "max_tokens": 1024, 69 | "messages": [ 70 | { 71 | "content": "你是桌面智能助手。", 72 | "role": "system" 73 | }, 74 | { 75 | "content": "请输入问题:", 76 | "role": "user" 77 | } 78 | ], 79 | "model": "test-model-id", 80 | "stream": true, 81 | "temperature": 0.5 82 | } 83 | 84 | const err = http.Stream("POST", "http://127.0.0.1:6099/api/v1/aardio/chat/completions", handler, requestBody, null, { 85 | "Content-Type": "application/json", 86 | Authorization: `Bearer ` + "sk-", 87 | }) 88 | 89 | if (err.code != 200) { 90 | console.log("err:,", err); 91 | 92 | throw new Exception(err.message, err.code); 93 | } 94 | } -------------------------------------------------------------------------------- /scripts/adapter/tencent.js: -------------------------------------------------------------------------------- 1 | 2 | function handler(payload) { 3 | // console.log("payload:", payload) 4 | if (payload == null) { 5 | return 0; 6 | } 7 | const content = payload.replace(/^data:/, ""); 8 | if (typeof ssEvent === "function") { 9 | ssEvent("data", content); 10 | } else { 11 | console.print(content); 12 | } 13 | return 1; 14 | } 15 | // yao run scripts.adapter.tencent.Completions 16 | function Completions(payload) { 17 | const model = payload.model; 18 | if (!model) { 19 | model = "deepseek-r1" 20 | // throw new Exception("缺少参数model", 400) 21 | } 22 | if (!payload.messages) { 23 | throw new Exception("缺少参数messages", 400) 24 | } 25 | const stream = payload.stream; 26 | const token = Process("yao.env.get", "COPILOT_TENCENT_TOKEN"); 27 | const base_url = Process("yao.env.get", "COPILOT_TENCENT_HOST"); 28 | let url = base_url + "/chat/completions"; 29 | 30 | const RequestBody = { 31 | // prompt: prompt, 32 | model, 33 | max_tokens: 4096, 34 | top_p: 1, 35 | temperature: 0.6, 36 | presence_penalty: 1, 37 | frequency_penalty: 0, 38 | stream: true, 39 | ...payload 40 | }; 41 | 42 | if (stream) { 43 | let err = http.Stream("POST", url, handler, RequestBody, null, { 44 | "Content-Type": "application/json", 45 | Authorization: `Bearer ` + token, 46 | }); 47 | if (err.code != 200) { 48 | console.log("err:", err); 49 | throw new Exception(err.message, err.code); 50 | } 51 | return { 52 | type: "text/event-stream; charset=utf-8" 53 | } 54 | } else { 55 | throw new Exception("不支持非流式", 400) 56 | } 57 | } -------------------------------------------------------------------------------- /scripts/ai/bot.md: -------------------------------------------------------------------------------- 1 | # bot.js 脚本使用说明 2 | 3 | `bot.js` 脚本是一个用于与 bot API 进行交互的 JavaScript 脚本。它提供了多种功能,包括调用 API、处理会话、下载会话记录等。以下是详细的使用说明。 4 | 5 | ## 功能概述 6 | 7 | 1. **调用 Bot API**: 通过 `Call` 函数可以发送请求到 Bot API,并获取响应。 8 | 2. **处理会话**: 脚本支持创建新会话、查找会话、更新会话等操作。 9 | 3. **下载会话记录**: 可以将会话记录下载为 Markdown 文件,便于查看和存档。 10 | 4. **删除会话**: 支持从服务器删除指定的会话记录。 11 | 12 | ## 主要函数说明 13 | 14 | ### `Call(prompt)` 15 | - **功能**: 发送请求到 Bot API。 16 | - **参数**: 17 | - `prompt`: 字符串或对象,表示要发送的请求内容。 18 | - **返回**: 返回 API 的响应内容。 19 | 20 | ### `CallGpt(message, setting)` 21 | - **功能**: 处理 POST 请求,并调用 ChatGPT 接口。 22 | - **参数**: 23 | - `message`: 对象,表示消息内容。 24 | - `setting`: 对象,表示设置项。 25 | - **返回**: 返回 API 的响应内容。 26 | 27 | ### `downloadConversation(conversation_id)` 28 | - **功能**: 下载指定会话的记录并保存为 Markdown 文件。 29 | - **参数**: 30 | - `conversation_id`: 字符串,表示会话 ID。 31 | - **返回**: 无返回值,文件将保存到 `./bot/` 目录下。 32 | 33 | ### `getHistory()` 34 | - **功能**: 从服务器获取会话历史记录。 35 | - **参数**: 无。 36 | - **返回**: 返回会话历史记录。 37 | 38 | ### `getConversationById(conversation_id)` 39 | - **功能**: 根据会话 ID 获取会话详细信息。 40 | - **参数**: 41 | - `conversation_id`: 字符串,表示会话 ID。 42 | - **返回**: 返回会话详细信息。 43 | 44 | ### `removeConversationById(conversation_id)` 45 | - **功能**: 从服务器删除指定会话。 46 | - **参数**: 47 | - `conversation_id`: 字符串,表示会话 ID。 48 | - **返回**: 返回删除操作的结果。 49 | 50 | ### `downloadAllConversation()` 51 | - **功能**: 下载所有会话记录并保存为 Markdown 文件。 52 | - **参数**: 无。 53 | - **返回**: 无返回值,文件将保存到 `./bot/` 目录下。 54 | 55 | ## 使用示例 56 | 57 | ### 调用 DeepSeek API -------------------------------------------------------------------------------- /scripts/ai/bot_headers.js: -------------------------------------------------------------------------------- 1 | 2 | function getISO8601Time(date1 = new Date) { 3 | let j = date1.getHours() + 8; 4 | return date1.setHours(j), 5 | new Date(date1).toISOString().replace(/\.[\d]{3}Z/, "+08:00") 6 | } 7 | 8 | const document = { 9 | domain: "chat.deepseek.com", 10 | referrer: "", 11 | } 12 | const navigator = { 13 | appName: "Netscape", 14 | version: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0", 15 | language: "zh-CN", 16 | browserLanguage: "zh-CN", 17 | platform: "Win32", 18 | userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0", 19 | } 20 | const windowScreen = { 21 | width: 1920, 22 | height: 1080, 23 | colorDepth: 24, 24 | } 25 | function md5(str) { 26 | 27 | return Process("crypto.Hash", "MD5", str) 28 | } 29 | 30 | function getMid() { 31 | return generateMID() 32 | } 33 | function generateMID() { 34 | 35 | let docu = document 36 | , nav = navigator 37 | , screen = windowScreen; 38 | function _e(nt) { 39 | let at = 0 40 | , it = 0 41 | , ot = nt.length - 1; 42 | for (ot; ot >= 0; ot--) { 43 | let st = parseInt(nt.charCodeAt(ot), 10); 44 | at = (at << 6 & 268435455) + st + (st << 14), 45 | (it = at & 266338304) != 0 && (at = at ^ it >> 21) 46 | } 47 | return at 48 | } 49 | function tt() { 50 | let nt = [nav.appName, nav.version, nav.language || nav.browserLanguage, nav.platform, nav.userAgent, screen.width, "x", screen.height, screen.colorDepth, docu.referrer].join("") 51 | , at = nt.length 52 | , it = 17;//window.history.length; 53 | for (; it;) 54 | nt += it-- ^ at++; 55 | return (Math.round(Math.random() * 2147483647) ^ _e(nt)) * 2147483647 56 | } 57 | let rt; 58 | return rt = [_e(docu.domain), tt(), +new Date + Math.random() + Math.random()].join(""), 59 | rt = rt.replace(/\./gi, "e"), 60 | rt = rt.substr(0, 32), 61 | // this._saveUid(rt), 62 | rt 63 | } 64 | // yao run scripts.ai.bot_headers.getHeaders 65 | getHeaders = () => { 66 | let arry = ["Web", getISO8601Time(), "1.2", getMid(), md5(navigator.userAgent)] 67 | , headers = { 68 | "device-platform": arry[0], 69 | timestamp: arry[1], 70 | "zm-ver": arry[2], 71 | "access-token": arry[3], 72 | "zm-token": "", 73 | "zm-ua": arry[4] 74 | }; 75 | return arry[3] || arry.splice(3, 1), 76 | headers["zm-token"] = md5(arry.join("")), 77 | { 78 | ...headers, 79 | "func-ver": 1, 80 | "Content-Type": "application/json",//contentType, 81 | mid: '',//client360Store.mid, 82 | sid: arry[3],//client360Store.sid, 83 | "Auth-Token": '',//authToken, 84 | "Request-Id": v4() 85 | } 86 | } 87 | 88 | function v4(a, j, et) { 89 | return Process("scripts.chat.conversation.generateUUID") 90 | } -------------------------------------------------------------------------------- /scripts/ai/chatgpt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 处理GET请求,注意get请求无法处理包含特殊字符的请求 3 | * 4 | * /api/ai/ask?q=xxx.xx.xx 5 | * @param {*} message 6 | * @returns 7 | */ 8 | function Callq(message) { 9 | return Call({ prompt: message }); 10 | } 11 | 12 | /** 13 | * 请求消息 14 | * yao run scripts.ai.chatgpt.Call '::{"prompt":"你好"}' 15 | * yao run scripts.ai.chatgpt.Call '::{"prompt":"可以帮我找一下python学习资源吗","session_id":"938d58a4-b976-46b8-a342-7644a2566476"}' 16 | * yao run scripts.ai.chatgpt.Call '::{"prompt":"廖雪峰的Python教程","session_id":"938d58a4-b976-46b8-a342-7644a2566476"}' 17 | * 18 | *yao run scripts.chat.conversation.FindConversationById "938d58a4-b976-46b8-a342-7644a2566476" 19 | * @param {object} message 请求消息 20 | * @returns 21 | */ 22 | function Call(message) { 23 | const setting = GetSetting(); 24 | if (!setting || !setting.api_token) { 25 | return "请在管理界面维护AI连接设置值"; 26 | } 27 | if (!message || !message.prompt || !message.prompt.length) { 28 | return "请填写您的问题"; 29 | } 30 | const ask = message.prompt; 31 | if (ask.length < 2) { 32 | return "请填写详细的问题"; 33 | } 34 | 35 | setting.user_nickname = setting.user_nickname || "用户"; 36 | setting.ai_nickname = setting.ai_nickname || "AI智能助理"; 37 | 38 | if ( 39 | setting.model.startsWith("gpt-3.5") || 40 | setting.model.startsWith("gpt-4") 41 | ) { 42 | return Process("scripts.ai.chatgpt_chat.Call", message, setting); 43 | } else { 44 | return Process("scripts.ai.chatgpt_complete.Call", message, setting); 45 | } 46 | } 47 | function GetSetting() { 48 | let [setting] = Process("models.ai.setting.Get", { 49 | wheres: [ 50 | { 51 | Column: "default", 52 | Value: true, 53 | }, 54 | { 55 | Column: "deleted_at", 56 | Value: null, 57 | }, 58 | ], 59 | }); 60 | // console.log("setting", setting); 61 | if (setting?.api_token) { 62 | if (setting.api_token == "sk-") { 63 | setting.api_token = ""; 64 | } 65 | if (!setting.api_token) { 66 | const access_key = Process("yao.env.get", "OPENAI_KEY"); 67 | if (access_key) { 68 | setting.api_token = access_key; 69 | } 70 | } 71 | } 72 | return setting; 73 | } 74 | -------------------------------------------------------------------------------- /scripts/ai/dashboard.js: -------------------------------------------------------------------------------- 1 | function dataSource() { 2 | return { 3 | chat_count: [ 4 | { 5 | value: getCount(), 6 | date: "2022-1-1", 7 | }, 8 | ], 9 | }; 10 | } 11 | function getCount() { 12 | var query = new Query(); 13 | 14 | var result = query.Get({ 15 | select: [":SUM(access_count) as number"], 16 | wheres: [{ ":deleted_at": "删除", "=": null }], 17 | from: "$ai.setting", 18 | }); 19 | //console.log(result[0].number) 20 | 21 | if (result && result.length) { 22 | return result[0].number; 23 | } 24 | return 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /scripts/ai/gemini.js: -------------------------------------------------------------------------------- 1 | let g_message = ""; 2 | 3 | function collect(content) { 4 | // console.log(`content:${content}`); 5 | if (typeof ssEvent === "function") { 6 | ssEvent("message", content); 7 | } else { 8 | console.log("send message:", content); 9 | } 10 | } 11 | /** 12 | * 回调函数 13 | * @param {object} payload 数据 14 | * @returns 15 | */ 16 | function handler(payload) { 17 | const lines = payload.split("\n\n"); 18 | for (const line2 of lines) { 19 | const line = line2; 20 | if (line === "") { 21 | continue; 22 | } 23 | if (line === "data: [DONE]") { 24 | collect("[DONE]"); 25 | return 0; 26 | } else if (line.startsWith("data:")) { 27 | const myString = line.substring(5); 28 | try { 29 | let message = JSON.parse(myString); 30 | if (message) { 31 | reply = message; 32 | let content = message.candidates[0].content.parts[0].text; 33 | if (content != null) { 34 | g_message += content; 35 | collect(content); 36 | } 37 | } 38 | } catch (error) { 39 | console.log("error:>>>>>", error.Error); 40 | return -1; 41 | } 42 | } else { 43 | console.log("unexpected", line); 44 | } 45 | } 46 | //异常,返回-1 47 | //正常返回1,默认 48 | //中断返回0 49 | return 1; 50 | } 51 | //yao run scripts.ai.gemini.test 52 | function test() { 53 | const API_KEY = Process("yao.env.get", "GOOGLE_GEMINI_API_ACCESS_KEY"); 54 | const GEMNI_API_HOST = Process("yao.env.get", "GOOGLE_GEMINI_API_HOST"); 55 | const url = `${GEMNI_API_HOST}/v1beta/models/gemini-pro:streamGenerateContent?key=${API_KEY}&alt=sse`; 56 | const RequestBody = { 57 | contents: [ 58 | { parts: [{ text: "Write long a story about a magic backpack." }] }, 59 | ], 60 | }; 61 | let err = http.Stream("POST", url, handler, RequestBody, null, { 62 | Accept: "text/event-stream; charset=utf-8", 63 | "Content-Type": "application/json", 64 | }); 65 | if (err.code != 200) { 66 | throw new Exception(err.message, err.code); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scripts/ai/model.js: -------------------------------------------------------------------------------- 1 | //读取测试 2 | //yao-debug run models.ai.model.get '::{"withs":{"permissions":{}}}' 3 | //yao-debug run models.ai.permission.get '::{"withs":{"model":{}}}' 4 | 5 | /** 6 | * 更新open ai 模型列表 7 | * 测试 yao run scripts.ai.model.UpdateModel 8 | */ 9 | function UpdateModel() { 10 | //清除所有的数据 11 | var qb = new Query(); 12 | let rc = qb.Get({ 13 | sql: { 14 | stmt: "delete from ai_model", 15 | }, 16 | }); 17 | 18 | var qb = new Query(); 19 | rc = qb.Get({ 20 | sql: { 21 | stmt: "delete from ai_permission", 22 | }, 23 | }); 24 | 25 | let setting = Process("scripts.ai.chatgpt.GetSetting"); 26 | token = setting.api_token; 27 | let reply = http.Get("https://api.openai.com/v1/models", null, { 28 | "Content-Type": "application/json", 29 | // 'Authorization': `Bearer ${$ENV.AI_API_KEY}`, 30 | Authorization: `Bearer ` + setting.api_token, 31 | }); 32 | // console.log(arrs.values) 33 | if (reply.code != 200) { 34 | throw new Exception(reply.data.error.message); 35 | // return reply.data.error.message 36 | } 37 | // let fs = new FS("system"); 38 | // fs.WriteFile("openai_models.json", JSON.stringify(reply.data.data)) 39 | // Process("fs.system.writefile", "openai_models.json", reply) 40 | let res = reply.data.data; 41 | 42 | let perms = []; 43 | res.forEach((line) => { 44 | //处理权限 45 | line.permission?.map((item) => { 46 | item.model_id = line.id; 47 | item.idx = item.id; //xgen目前不支持非数字类型的主键 48 | item.created = convertUTCDateToLocalDate(item.created); 49 | delete item.id; 50 | }); 51 | line.idx = line.id; 52 | delete line.id; //xgen目前不支持非数字类型的主键 53 | line.created = convertUTCDateToLocalDate(line.created); 54 | line.title = line.idx; //更新标题 55 | 56 | if (line.permission) { 57 | perms = perms.concat(line.permission); 58 | } 59 | 60 | delete line.permission; 61 | }); 62 | let arrs = Process("utils.arr.Split", perms || []); 63 | rc = Process("models.ai.permission.Insert", arrs.columns, arrs.values); 64 | 65 | arrs = Process("utils.arr.Split", res); 66 | rc = Process("models.ai.model.Insert", arrs.columns, arrs.values); 67 | } 68 | 69 | /** 70 | * yao-debug run scripts.ai.model.ConvertTime 1669085501 71 | * @param {*} unixTimestamp 72 | */ 73 | function ConvertTimeCmd(unixTimestamp) { 74 | times = parseInt(unixTimestamp); 75 | return convertUTCDateToLocalDate(times); 76 | } 77 | /** 78 | * 把utc时间转换成北京时间 79 | * @param {integer} unixTimestamp 80 | * @returns string 81 | */ 82 | function convertUTCDateToLocalDate(unixTimestamp) { 83 | var date = new Date(unixTimestamp * 1000); 84 | // utc时间整时区 85 | var newDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000); 86 | var offset = date.getTimezoneOffset() / 60; 87 | var hours = date.getHours(); 88 | newDate.setHours(hours - offset); 89 | return newDate.toISOString().slice(0, 19).replace("T", " "); 90 | } 91 | -------------------------------------------------------------------------------- /scripts/ai/stat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * before:data hook 3 | * @param {*} params 4 | * @returns 5 | */ 6 | function BeforeData(params) { 7 | log.Info("[chart] before data hook: %s", JSON.stringify(params)); 8 | return [params]; 9 | } 10 | 11 | /** 12 | * after:data hook 13 | * @param {*} data 14 | * @returns 15 | */ 16 | function AfterData(data) { 17 | log.Info("[chart] after data hook: %s", JSON.stringify(data)); 18 | return data; 19 | } 20 | 21 | /** 22 | * Get Data 23 | * @param {*} params 24 | */ 25 | function Data(params) { 26 | //console.log("[chart] process data query: %s", JSON.stringify(params)); 27 | log.Info("[chart] process data query: %s", JSON.stringify(params)); 28 | return { 29 | count: getCount(), 30 | }; 31 | } 32 | 33 | /** 34 | * Compute out 35 | * @param {*} field 36 | * @param {*} value 37 | * @param {*} data 38 | * @returns 39 | */ 40 | function Income(field, value, data) { 41 | log.Info( 42 | "[chart] Income Compute: %s", 43 | JSON.stringify({ field: field, value: value, data: data }) 44 | ); 45 | return value; 46 | } 47 | 48 | function getCount() { 49 | var query = new Query(); 50 | 51 | var result = query.Get({ 52 | select: [":SUM(access_count) as number"], 53 | wheres: [{ ":deleted_at": "删除", "=": null }], 54 | from: "$ai.setting", 55 | }); 56 | //console.log(result[0].number) 57 | 58 | return result[0].number 59 | } 60 | -------------------------------------------------------------------------------- /scripts/auth/token.js: -------------------------------------------------------------------------------- 1 | /** 2 | * api guard,scripts.auth.token.Check,检查身份验证 3 | * @param {string} path api path 4 | * @param {map} params api path params 5 | * @param {map} queries api queries in url query string 6 | * @param {object|string} payload json object or string 7 | * @param {map} headers request headers 8 | */ 9 | function Check(path, params, queries, payload, headers) { 10 | const token = getToken(path, params, queries, payload, headers); 11 | if (!token) { 12 | error(); 13 | } 14 | if (token == "" || token.length == 0) { 15 | error(); 16 | } 17 | 18 | let data = Process("utils.jwt.Verify", token); 19 | return { sid: data.sid, __global: data.data }; 20 | } 21 | function error() { 22 | throw new Exception("Not Authorized", 403); 23 | } 24 | 25 | // yao run scripts.security.CheckToken 26 | function CheckToken(path, params, queries, payload, headers) { 27 | let token = getToken(path, params, queries, payload, headers); 28 | if (!token) { 29 | return; 30 | } 31 | 32 | // try { 33 | let data = Process("utils.jwt.Verify", token); 34 | return { sid: data.sid, __global: data.data }; 35 | // } catch (error) { 36 | // console.log("Error:", error); 37 | // console.log("Invalid Token:", token); 38 | // } 39 | } 40 | 41 | function getToken(path, params, queries, payload, headers) { 42 | // 接受三种身份验证参数传入路径 43 | // 1 headers Authorization 44 | // 2 cookie token 45 | // 3 url查询参数中的token 46 | let token = null; 47 | let auth = headers["Authorization"]; 48 | if (auth) { 49 | token = auth[0]; 50 | } else { 51 | token = token || (queries["token"] && queries["token"][0]); 52 | } 53 | if (!auth) { 54 | let cookies = headers["Cookie"]; 55 | cookies && 56 | cookies.forEach((cookie) => { 57 | const cookies2 = cookie.split(";"); // split the cookies into an array 58 | cookies2.forEach((cookie) => { 59 | const [name, value] = cookie.trim().split("="); // split cookie name and value 60 | if (name === "token") { 61 | token = value; // assign cookie value to token variable 62 | } 63 | }); 64 | }); 65 | } 66 | if (token) { 67 | token = token.replace("Bearer ", ""); 68 | } 69 | return token; 70 | } 71 | -------------------------------------------------------------------------------- /scripts/chatweb.js: -------------------------------------------------------------------------------- 1 | const { ExCallGpt } = Require("ai.chatweb_lib"); 2 | 3 | // chat session 4 | // 检查会话ID 5 | function session() { 6 | const access_key = Process("yao.env.get", "YAO_CHAT_API_KEY"); 7 | let hasAuth = false; 8 | if (access_key) { 9 | hasAuth = true; 10 | } 11 | return { 12 | status: "Success", 13 | message: "", 14 | data: { auth: hasAuth, model: "ChatGPTAPI" }, 15 | }; 16 | } 17 | 18 | // chat process stream 19 | // yao run scripts.chatweb.chat_process '::{"prompt":"测试"}' 20 | function chat_process(payload) { 21 | let request = { 22 | prompt: "", 23 | options: { 24 | conversationId: "", 25 | parentMessageId: "", 26 | }, 27 | systemMessage: "", 28 | temperature: "", 29 | top_p: "", 30 | }; 31 | Object.assign(request, payload); 32 | ExCallGpt(request); 33 | } 34 | 35 | //config 36 | // yao run scripts.chatweb.config 37 | function config() { 38 | return { 39 | status: "Success", 40 | data: { 41 | apiModel: "", 42 | reverseProxy: "", 43 | socksProxy: "", 44 | httpsProxy: "", 45 | balance: "", 46 | timeoutMs: 1000, 47 | }, 48 | }; 49 | } 50 | 51 | // yao run scripts.chatweb.credit_grants 52 | function credit_grants() { 53 | const access_key = Process("yao.env.get", "OPENAI_KEY2"); 54 | 55 | const res = Process( 56 | "http.get", 57 | "https://api.openai.com/dashboard/billing/credit_grants", //不能再使用api key来访问dashboard对象,只能是在浏览器上登录后获取sess-key来访问 58 | "", 59 | { 60 | "Content-Type": "application/json", 61 | Authorization: "Bearer " + access_key, 62 | } 63 | ); 64 | // "Your request to GET /dashboard/billing/usage must be made with a session key 65 | // (that is, it can only be made from the browser). You made it with the following key type: secret.", 66 | console.log("res", res); 67 | } 68 | // yao run scripts.chatweb.usage 69 | function usage() { 70 | const access_key = Process("yao.env.get", "OPENAI_KEY2"); 71 | 72 | const res = Process( 73 | "http.get", 74 | "https://api.openai.com/dashboard/billing/usage?end_date=2024-01-01&start_date=2023-12-01", 75 | "", 76 | { 77 | "Content-Type": "application/json", 78 | Authorization: "Bearer " + access_key, 79 | } 80 | ); 81 | // "Your request to GET /dashboard/billing/usage must be made with a session key 82 | // (that is, it can only be made from the browser). You made it with the following key type: secret.", 83 | console.log("res", res); 84 | } 85 | // scripts.chatweb.verify 86 | function verify(payload) { 87 | let token = payload.token; 88 | 89 | const access_key = Process("yao.env.get", "YAO_CHAT_API_KEY"); 90 | if (!access_key) { 91 | throw new Exception("YAO_CHAT_API_KEY Not set", 403); 92 | } 93 | if (access_key !== token) { 94 | throw new Exception("YAO_CHAT_API_KEY not equal token", 403); 95 | } 96 | 97 | return { status: "Success", message: "Verify successfully", data: undefined }; 98 | } 99 | -------------------------------------------------------------------------------- /scripts/doc/file.js: -------------------------------------------------------------------------------- 1 | function Save(payload) { 2 | //先保存主表,获取id后再保存从表 3 | 4 | const t = new Query(); 5 | t.Run({ 6 | sql: { 7 | stmt: "START TRANSACTION;", 8 | }, 9 | }); 10 | 11 | let res = null; 12 | try { 13 | res = Process("models.doc.file.Save", payload); 14 | if (res.code && res.code > 300) { 15 | throw new Exception(res.message, res.code); 16 | } 17 | SaveRelations(res, payload); 18 | } catch (error) { 19 | console.log("Mode doc.file Save Failed,Payload:", payload); 20 | 21 | t.Run({ 22 | sql: { 23 | stmt: "ROLLBACK;", 24 | }, 25 | }); 26 | 27 | if (error.message && error.code) { 28 | console.log("error:", error.code, error.message); 29 | throw new Exception(error.message, error.code); 30 | } else { 31 | console.log("system error:", error); 32 | throw error; 33 | } 34 | } 35 | 36 | t.Run({ 37 | sql: { 38 | stmt: "COMMIT;", 39 | }, 40 | }); 41 | 42 | return res; 43 | } 44 | //保存关联表数据 45 | function SaveRelations(id, payload) { 46 | Save_vector(id, payload); 47 | return id; 48 | } 49 | 50 | //删除关联表数据 51 | function BeforeDelete(id) { 52 | Delete_vector(id); 53 | } 54 | 55 | //保存doc.vector 56 | function Save_vector(id, payload) { 57 | const items = payload.vector || {}; 58 | const deletes = items.delete || []; 59 | const data = items.data || items || []; 60 | if (data.length > 0 || deletes.length > 0) { 61 | // 忽略实际数据 ( 通过 record 计算获取) 62 | for (const i in data) { 63 | if (typeof data[i].id === "string" && data[i].id.startsWith("_")) { 64 | //新增项目,在前端会生成唯一字符串, 65 | //由于后台使用的自增长ID,不需要生成的唯一字符串,由数据库生成索引 66 | delete data[i].id; 67 | } 68 | } 69 | 70 | // 保存物品清单 71 | var res = Process("models.doc.vector.EachSaveAfterDelete", deletes, data, { 72 | file_id: id, 73 | }); 74 | if (res.code && res.code > 300) { 75 | console.log("doc.vector:AfterSave Error:", res); 76 | //console.log(items) 77 | throw new Exception(res.message, res.code); 78 | } else { 79 | return id; 80 | } 81 | } 82 | } 83 | 84 | //删除doc.vector == file_id 85 | function Delete_vector(id) { 86 | let rows = Process("models.doc.vector.DeleteWhere", { 87 | wheres: [{ column: "file_id", value: id }], 88 | }); 89 | 90 | //remembe to return the id in array format 91 | return [id]; 92 | } 93 | 94 | //多对一表数据查找 95 | function AfterFind(payload) { 96 | const t = new Query(); 97 | payload["vector"] = t.Get({ 98 | from: "doc_vector", 99 | wheres: [ 100 | { 101 | field: "file_id", 102 | op: "=", 103 | value: payload.id, 104 | }, 105 | ], 106 | select: ["id", "index", "file_id", "content", "embedding"], 107 | }); 108 | return payload; 109 | } 110 | -------------------------------------------------------------------------------- /scripts/doc/task.js: -------------------------------------------------------------------------------- 1 | // scripts.doc.task.Process 2 | /** 3 | * 任务绑定的处理器, 4 | * @param {integer} task_id 作业的id, 5 | * @param {any} args 任务的参数,可以有多个,由tasks.xxx.add处理器传入。 6 | * 7 | */ 8 | function TaskProcess(task_id, record_id, filename) { 9 | console.log(`TaskProcess: #${task_id} ${filename}`); 10 | // 业务处理 11 | Process("models.doc.file.update", record_id, { 12 | task_id: task_id, 13 | index_status: "new", 14 | }); 15 | Process("scripts.doc.vector.indexFile", filename, task_id, record_id); 16 | 17 | Process("models.doc.file.update", record_id, { 18 | index_status: "done", 19 | }); 20 | return { 21 | message: "done", 22 | }; 23 | } 24 | var task_id = 1024; 25 | 26 | /** 27 | * 任务标识生成器 28 | * Generate job id,需要返回一个整型的数字,用来生成任务的标识 29 | * @returns 30 | */ 31 | function NextID() { 32 | task_id = task_id + 1; 33 | console.log(`NextID: ${task_id}`); 34 | return task_id; 35 | } 36 | 37 | /** 38 | * OnAdd add event 39 | * @param {*} task_id 任务id 40 | */ 41 | function OnAdd(task_id) { 42 | console.log(`OnAdd: #${task_id}`); 43 | } 44 | 45 | /** 46 | * OnProgress 47 | * @param {*} task_id task id,任务ID 48 | * @param {*} current 49 | * @param {*} total 50 | * @param {*} message 51 | */ 52 | function OnProgress(task_id, current, total, message) { 53 | console.log(`OnProgress: #${task_id} ${current}/${total}${message}\r`); 54 | } 55 | /** 56 | * OnProgress 57 | * @param {*} task_id task id,任务ID 58 | * @param {*} res 任务运行处理器返回的结果 59 | */ 60 | function OnSuccess(task_id, res) { 61 | console.log(`OnSuccess: #${task_id} ${JSON.stringify(res)}`); 62 | } 63 | 64 | function OnError(task_id, err) { 65 | console.log(`OnError: #${task_id} ${err}`); 66 | } 67 | -------------------------------------------------------------------------------- /scripts/doc/test/local.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 使用pg数据库,配合pgvector插件作一个本地向量文档搜索 3 | * 首先需要安装pg数据库,再安装pgvector插件 4 | * 在pg中创建一个数据库,并且执行以下的命令启用插件 5 | * CREATE EXTENSION vector 6 | * 使用yao命令yao migrate 7 | * yao migrate 8 | */ 9 | 10 | // yao run scripts.doc.local.QueryDoc '::{"input":"yao是什么?"}' 11 | function QueryDoc(payload) { 12 | let document = payload.input; 13 | 14 | let input = document.replace(/\*|\n/g, " "); 15 | 16 | const embeddingResponse = http.Post("http://localhost:8001/emb", { 17 | input: input, 18 | }); 19 | if (!embeddingResponse.data) { 20 | throw new Error("请求出错"); 21 | } 22 | 23 | if (embeddingResponse.code != 200) { 24 | throw new Error(embeddingResponse.data.detail[0].msg); 25 | } 26 | const [{ embedding }] = embeddingResponse.data; 27 | const q = new Query(); 28 | const query_embedding = `'${JSON.stringify(embedding)}'`; 29 | const match_threshold = payload.threshold || 0.6; 30 | const match_count = payload.count || 10; 31 | const sql = `select id, filename,path,content, 1 - (embedding <=> ${query_embedding}) as similarity 32 | from doc_vector where 1 - (embedding <=> ${query_embedding}) > ${match_threshold} 33 | order by similarity DESC limit ${match_count}`; 34 | 35 | const data = q.Get({ 36 | sql: { 37 | stmt: sql, 38 | }, 39 | }); 40 | 41 | return { data: data }; 42 | } 43 | 44 | // yao run scripts.test.demoSearch 45 | function demoSearch() { 46 | return searchText2vect("Did anyone adopt a cat this weekend?"); 47 | } 48 | // yao run scripts.test.text2vect 测试 49 | 50 | function saveText2vect(document) { 51 | let input = document.replace(/\*|\n/g, " "); 52 | 53 | const embeddingResponse = http.Post("http://localhost:8001/emb", { 54 | input: input, 55 | }); 56 | 57 | const [{ embedding }] = embeddingResponse.data; 58 | 59 | Process("models.doc.vector.save", { 60 | content: document, 61 | embedding: JSON.stringify(embedding), 62 | }); 63 | 64 | return "ok"; 65 | } 66 | // yao run scripts.test.vector 67 | function vector() { 68 | const q = new Query(); 69 | const sql = `SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;`; 70 | const data = q.Get({ 71 | sql: { 72 | stmt: sql, 73 | }, 74 | }); 75 | 76 | return data; 77 | } 78 | // yao run scripts.test.demoSave 79 | function demoSave() { 80 | texts = [ 81 | "I like to eat broccoli and bananas.", 82 | "I ate a banana and spinach smoothie for breakfast.", 83 | "Chinchillas and kittens are cute.", 84 | "My sister adopted a kitten yesterday.", 85 | "Look at this cute hamster munching on a piece of broccoli.", 86 | ]; 87 | 88 | texts.forEach((text) => { 89 | saveText2vect(text); 90 | }); 91 | } 92 | -------------------------------------------------------------------------------- /scripts/doc/test/openai.js: -------------------------------------------------------------------------------- 1 | // yao run scripts.test.save 测试 2 | function save(document) { 3 | let input = document.replace(/\*|\n/g, " "); 4 | const embeddingResponse = Process( 5 | "openai.Embeddings", 6 | "text-embedding-doubao", 7 | input, 8 | "" 9 | ); 10 | 11 | const [{ embedding }] = embeddingResponse.data; 12 | Process("models.documents.save", { 13 | content: document, 14 | embedding: JSON.stringify(embedding), 15 | }); 16 | 17 | return "ok"; 18 | } 19 | // yao run scripts.test.search "Did anyone adopt a cat this weekend?" 20 | function search(words) { 21 | const embeddingResponse = Process( 22 | "openai.Embeddings", 23 | "text-embedding-doubao", 24 | words, 25 | "user-01" 26 | ); 27 | const [{ embedding }] = embeddingResponse.data; 28 | 29 | // ("${JSON.stringify(embedding)}"); 30 | const q = new Query(); 31 | const query_embedding = `'${JSON.stringify(embedding)}'`; 32 | // console.log("query_embedding", query_embedding); 33 | const match_threshold = 0.7; 34 | const match_count = 10; 35 | const sql = `select id, content, 1 - (embedding <=> ${query_embedding}) as similarity 36 | from documents where 1 - (embedding <=> ${query_embedding}) > ${match_threshold} 37 | order by similarity DESC limit ${match_count}`; 38 | 39 | // const sql = `SELECT * FROM match_documents('${JSON.stringify( 40 | // embedding 41 | // )}', 0.7, 10);`; 42 | const data = q.Get({ 43 | sql: { 44 | stmt: sql, 45 | }, 46 | }); 47 | return data; 48 | } 49 | -------------------------------------------------------------------------------- /scripts/doc/test/test.js: -------------------------------------------------------------------------------- 1 | function test() { 2 | let tableName = "stockcode"; 3 | const wheres = []; 4 | if (tableName) { 5 | wheres.push({ column: "table_name", value: tableName }); 6 | wheres.push({ method: "orwhere", column: "name", value: tableName }); 7 | } else { 8 | wheres.push({ method: "where", column: "name", value: tableName }); 9 | } 10 | 11 | const [line] = Process("models.ddic.model.get", { 12 | wheres: wheres, 13 | with: {}, 14 | limit: 1, 15 | }); 16 | 17 | console.log("id:", line.id); 18 | } 19 | 20 | // scripts.test.pg 21 | function pg() { 22 | const q = new Query(); 23 | const sql = `SELECT time_bucket('15 minutes', time) AS fifteen_min, 24 | location, COUNT(*), 25 | MAX(temperature) AS max_temp, 26 | MAX(humidity) AS max_hum 27 | FROM conditions 28 | WHERE time > NOW() - interval '3 hours' 29 | GROUP BY fifteen_min, location 30 | ORDER BY fifteen_min DESC, max_temp DESC;`; 31 | const data = q.Get({ 32 | sql: { 33 | stmt: sql, 34 | }, 35 | }); 36 | 37 | return data; 38 | } 39 | -------------------------------------------------------------------------------- /scripts/jsproxy.js: -------------------------------------------------------------------------------- 1 | //代理js api请求 2 | // import { Store, Studio, WebSocket } from "yao-node-client"; 3 | // import { Exception, Process, Query } from "yao-node-client"; 4 | // import { $L, FS, http, log } from "yao-node-client"; 5 | /** 6 | * api 代理服务,可以放在yao应用下 7 | * @param {object} payload 8 | * @returns 9 | */ 10 | function Server(payload) { 11 | // console.log("request received"); 12 | // console.log(payload); 13 | // log.Info("debug served called"); 14 | // log.Info(payload); 15 | // JSON.stringify({'a':null,'b':undefined}) 16 | // '{"a":null}' 17 | let resp = { 18 | code: 200, 19 | message: "", 20 | // error: null as Error, //undefined不会出现在返回json key中 21 | data: null, 22 | }; 23 | try { 24 | const type = payload.type; 25 | const method = payload.method; 26 | const args = payload.args; 27 | const space = payload.space; //"dsl","script","system" 28 | let localParams = []; 29 | if (Array.isArray(args)) { 30 | localParams = args; 31 | } else { 32 | localParams.push(args); 33 | } 34 | switch (type) { 35 | case "Process": 36 | resp.data = Process(method, ...localParams); 37 | break; 38 | case "Studio": 39 | // @ts-ignore 40 | __YAO_SU_ROOT = true; 41 | resp.data = Studio(method, ...localParams); 42 | break; 43 | case "Query": 44 | const query = new Query(); 45 | //@ts-ignore 46 | resp.data = query[method](args); 47 | break; 48 | case "FileSystem": 49 | const fs = new FS(space); 50 | //@ts-ignore 51 | resp.data = fs[method](...args); 52 | break; 53 | case "Store": 54 | const cache = new Store(space); 55 | if (method == "Set") { 56 | resp.data = cache.Set(payload.key, payload.value); 57 | } else if (method == "Get") { 58 | resp.data = cache.Get(payload.key); 59 | } 60 | break; 61 | case "Http": 62 | //@ts-ignore 63 | resp.data = http[method](...args); 64 | break; 65 | case "Log": 66 | // console.log("Log args:", args); 67 | //@ts-ignore 68 | log[method](...args); 69 | resp.data = {}; 70 | break; 71 | case "WebSocket": 72 | //目前yao只是实现了push一个方法,也是ws服务连接后push一条信息 73 | const ws = new WebSocket(payload.url, payload.protocols); 74 | if (method == "push") { 75 | ws.push(payload.message); 76 | resp.data = {}; 77 | } 78 | break; 79 | case "Translate": 80 | resp.data = $L(payload.message); 81 | break; 82 | default: 83 | resp.code = 500; 84 | resp.message = `不支持的方法调用${type}`; 85 | } 86 | } catch (error) { 87 | resp.code = error.code || 500; 88 | resp.message = error.message || "接口调用异常"; 89 | } 90 | return resp; 91 | } 92 | // 在外部按这个格式进行封装 93 | // function MyProcess(...args: any[]) { 94 | // return Process("scripts.jsproxy.RemoteProcess",'scripts.ping.Ping' ...args); 95 | // } 96 | /** 97 | * 调用远程处理器,并返回处理结果 98 | * @param method 远程处理器方法 99 | * @param args 远程处理器参数 100 | * @returns 远程处理器结果 101 | */ 102 | function RemoteProcess(method, ...args) { 103 | if (!(typeof method === "string")) { 104 | throw new Exception(`方法格式不正确,方法名不是字符串${method}`, 500); 105 | } 106 | const types = method.split("."); 107 | if (!types.length) { 108 | throw new Exception(`方法格式不正确,没有命名空间${method}`, 500); 109 | } 110 | const type = types[0].toLowerCase(); 111 | if (!["scripts", "services", "studio"].includes(type)) { 112 | throw new Exception( 113 | `不支持的方法,只支持调用scripts/services/studio,${method}`, 114 | 500 115 | ); 116 | } 117 | return RemoteClient("Process", method, ...args); 118 | } 119 | /** 120 | * 远程api接口 121 | * @param type 处理器类型scripts/services/studio 122 | * @param method scripts.xx.xx 123 | * @param args 任何参数 124 | * @returns 远程处理器的结果 125 | */ 126 | function RemoteClient(type, method, ...args) { 127 | // let ret = http.Post(process.env.REMOTE_DEBUG_SERVER, { 128 | let server = Process("utils.env.Get", "REMOTE_DEBUG_SERVER"); 129 | let ret = http.Post(server, { 130 | method: method, 131 | type: type, 132 | args: args, 133 | }); 134 | if (ret.code != 200) { 135 | throw Error(`远程程序执行异常:代码:${ret.code},消息:${ret.message}`); 136 | } 137 | return ret.data; 138 | } 139 | -------------------------------------------------------------------------------- /scripts/loader/prompt_template.js: -------------------------------------------------------------------------------- 1 | // import { ProcessEnum } from "yao-app-ts-types"; 2 | // import { Exception, FS, http, Process, Query } from "yao-node-client"; 3 | /** 4 | * 加载提示词模板 5 | * 数据来源:https://github.com/PlexPt/awesome-chatgpt-prompts-zh/blob/main/README.md 6 | * @returns 7 | */ 8 | function Run() { 9 | var qb = new Query(); 10 | let rc = qb.Get({ 11 | sql: { 12 | stmt: "delete from prompt_template", 13 | }, 14 | }); 15 | var fs = new FS("system"); 16 | if (!fs.Exists("/中文调教指南.md.txt")) { 17 | let document = http.Get( 18 | "https://raw.githubusercontent.com/PlexPt/awesome-chatgpt-prompts-zh/main/README.md" 19 | ); 20 | if (document.status != 200) { 21 | throw new Exception(`网络请求异常${document.message}`, 500); 22 | } 23 | let buffer = Process("Encoding.Base64.Decode", document.data); 24 | fs.WriteFileBuffer("/中文调教指南.md.txt", buffer); 25 | } 26 | var data = fs.ReadFile("/中文调教指南.md.txt"); 27 | const words = data.split("\n"); 28 | let startindex = 0; 29 | let title = ""; 30 | let content = ""; 31 | let newData = []; 32 | words.forEach((line, index) => { 33 | if (line.length == 0) { 34 | return; 35 | } 36 | if (line.startsWith("# 正经指南")) { 37 | startindex = index; 38 | } 39 | if (startindex && index > startindex) { 40 | let matchs = line.match(/##\s(.*)$/); 41 | if (matchs && matchs.length == 2) { 42 | title = matchs[1]; 43 | } 44 | matchs = line.match(/>\s(.*)$/); 45 | if (matchs && matchs.length == 2) { 46 | content = matchs[1]; 47 | } 48 | if (title && content) { 49 | newData.push([title, content]); 50 | title = ""; 51 | content = ""; 52 | } 53 | } 54 | }); 55 | rc = Process( 56 | "models.chat.prompttemplate.Insert", 57 | ["title", "content"], 58 | newData 59 | ); 60 | // console.log(rc); 61 | return rc; 62 | } 63 | // load_prompt_template(); 64 | -------------------------------------------------------------------------------- /scripts/loader/sensitive_word.js: -------------------------------------------------------------------------------- 1 | // import { FS, Process, Query } from "yao-node-client"; 2 | /** 3 | * 从文件加载敏感词到数据库表中 4 | * @returns 5 | */ 6 | function load_sensitive_word() { 7 | var fs = new FS("system"); // /app_root/data 8 | var data = fs.ReadFile("/word.txt"); // /app_root/data/xxx 9 | const words = data.split("\n"); 10 | var qb = new Query(); 11 | let rc = qb.Get({ 12 | sql: { 13 | stmt: "delete from sensitive_word", 14 | }, 15 | }); 16 | let wordArray = []; 17 | words.forEach((word) => wordArray.push([word])); 18 | rc = Process("Models.sensitive_word.Insert", ["word"], wordArray); 19 | console.log(rc); 20 | return rc; 21 | } 22 | // load_sensitive_word(); 23 | -------------------------------------------------------------------------------- /scripts/menu/version.js: -------------------------------------------------------------------------------- 1 | function getVersion() { 2 | 3 | } 4 | 5 | -------------------------------------------------------------------------------- /scripts/neo.js: -------------------------------------------------------------------------------- 1 | function chat(args) { 2 | // console.log(args); 3 | // const date = new Date(); 4 | 5 | // return { 6 | // text: `Hello, World! 7 | // 现在的时间是: 8 | // ${date.getFullYear()}-${ 9 | // date.getMonth() + 1 10 | // }-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} 11 | // `, 12 | // }; 13 | 14 | const resp = Process("scripts.ai.chatgpt.Call", { prompt: args.text }); 15 | return { text: resp.message }; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /scripts/neo/neo.ts: -------------------------------------------------------------------------------- 1 | import { io, neo } from '@yao/yao'; 2 | 3 | /** 4 | * Neo Prepare hook 5 | */ 6 | export function Prepare( 7 | ctx: neo.Context, 8 | messages: neo.Message[] 9 | ): neo.Message[] { 10 | //不再使用,使用assistant代替 11 | return messages; 12 | } 13 | 14 | export function Create( 15 | ctx: neo.Context, 16 | messages: neo.Message[], 17 | writer: io.ResponseWriter 18 | ) { 19 | //console.log('messages', messages); 20 | //check messages contains the item role = 'system' 21 | // let hasSystem = false; 22 | // for (const message of messages) { 23 | // if (message.role === 'system') { 24 | // hasSystem = true; 25 | // break; 26 | // } 27 | // } 28 | // if (!hasSystem || ctx.assistant_id === '') { 29 | // ctx.assistant_id = 'model'; 30 | // } 31 | //console.log('writer', writer); 32 | //update the ctx.assistant_id 33 | return { assistant_id: ctx.assistant_id, chat_id: ctx.chat_id }; 34 | } 35 | 36 | /** 37 | * Neo Write hook 38 | */ 39 | export function Write( 40 | ctx: neo.Context, 41 | messages: neo.Message[], 42 | response: neo.Response, 43 | content?: string, 44 | writer?: io.ResponseWriter 45 | ): neo.Response[] { 46 | console.log('content', content); 47 | console.log('writer', writer); 48 | return [response]; 49 | } 50 | -------------------------------------------------------------------------------- /scripts/remote.js: -------------------------------------------------------------------------------- 1 | // Ping 远程函数 2 | function Ping(...args) { 3 | return Process("scripts.jsproxy.RemoteProcess", "scripts.ping.Ping", ...args); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /scripts/security.js: -------------------------------------------------------------------------------- 1 | /** 2 | * api guard,scripts.security.CheckAccessKey 3 | * @param {string} path api path 4 | * @param {map} params api path params 5 | * @param {map} queries api queries in url query string 6 | * @param {object|string} payload json object or string 7 | * @param {map} headers request headers 8 | */ 9 | function CheckAccessKey(path, params, queries, payload, headers) { 10 | let token = null; 11 | let auth = headers["Authorization"]; 12 | if (auth) { 13 | token = auth[0].replace("Bearer ", ""); 14 | } 15 | token = token || (queries["token"] && queries["token"][0]); 16 | if (!token) { 17 | throw new Exception("token Not set,please login first", 403); 18 | } 19 | const access_key = Process("yao.env.get", "YAO_API_ACCESS_KEY"); 20 | if (!access_key) { 21 | throw new Exception("YAO_API_ACCESS_KEY Not set", 403); 22 | } 23 | if (access_key !== token) { 24 | throw new Exception("YAO_API_ACCESS_KEY not equal token", 403); 25 | } 26 | } 27 | 28 | function CheckChatKey(path, params, queries, payload, headers) { 29 | let token = null; 30 | let auth = headers["Authorization"]; 31 | if (auth) { 32 | token = auth[0].replace("Bearer ", ""); 33 | } 34 | token = token || (queries["token"] && queries["token"][0]); 35 | if (!token) { 36 | throw new Exception("Call token Not set", 403); 37 | } 38 | const access_key = Process("yao.env.get", "YAO_CHAT_API_KEY"); 39 | if (!access_key) { 40 | throw new Exception("YAO_CHAT_API_KEY Not set", 403); 41 | } 42 | if (access_key !== token) { 43 | throw new Exception("YAO_CHAT_API_KEY not equal token", 403); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /scripts/stat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * before:data hook 3 | * @param {*} params 4 | * @returns 5 | */ 6 | function BeforeData(params) { 7 | log.Info("[chart] before data hook: %s", JSON.stringify(params)); 8 | return [params]; 9 | } 10 | 11 | /** 12 | * after:data hook 13 | * @param {*} data 14 | * @returns 15 | */ 16 | function AfterData(data) { 17 | log.Info("[chart] after data hook: %s", JSON.stringify(data)); 18 | return data; 19 | } 20 | 21 | /** 22 | * Get Data 23 | * @param {*} params 24 | */ 25 | function Data(params) { 26 | log.Info("[chart] process data query: %s", JSON.stringify(params)); 27 | return { 28 | income: [ 29 | { value: 40300, date: "2022-1-1" }, 30 | { value: 50800, date: "2022-2-1" }, 31 | { value: 31300, date: "2022-3-1" }, 32 | { value: 48800, date: "2022-4-1" }, 33 | { value: 69900, date: "2022-5-1" }, 34 | { value: 37800, date: "2022-6-1" }, 35 | ], 36 | cost: [ 37 | { value: 28100, date: "2022-1-1" }, 38 | { value: 23000, date: "2022-2-1" }, 39 | { value: 29300, date: "2022-3-1" }, 40 | { value: 26700, date: "2022-4-1" }, 41 | { value: 26400, date: "2022-5-1" }, 42 | { value: 31200, date: "2022-6-1" }, 43 | ], 44 | rate: [ 45 | { value: 8.0, date: "2022-1-1" }, 46 | { value: 7.6, date: "2022-2-1" }, 47 | { value: 9.1, date: "2022-3-1" }, 48 | { value: 8.4, date: "2022-4-1" }, 49 | { value: 6.9, date: "2022-5-1" }, 50 | { value: 9.0, date: "2022-6-1" }, 51 | ], 52 | pet_count: 54, 53 | pet_type: 8, 54 | income_monthly: 68900, 55 | doctor_count: 23, 56 | prev_pet_count: { current: 54, prev: 45 }, 57 | prev_pet_type: { current: 8, prev: 13 }, 58 | prev_income_monthly: { current: 68900, prev: 92000 }, 59 | prev_doctor_count: { current: 23, prev: 27 }, 60 | datasource_type: [ 61 | { type: "猫猫", count: 18 }, 62 | { type: "狗狗", count: 6 }, 63 | { type: "其他", count: 3 }, 64 | ], 65 | datasource_status: [ 66 | { status: "已查看", count: 3 }, 67 | { status: "治疗中", count: 12 }, 68 | { status: "已治愈", count: 9 }, 69 | ], 70 | datasource_cost: [ 71 | { name: "毛毛", stay: 3, cost: 2000 }, 72 | { name: "阿布", stay: 6, cost: 4200 }, 73 | { name: "咪咪", stay: 7, cost: 6000 }, 74 | { name: "狗蛋", stay: 1, cost: 1000 }, 75 | ], 76 | }; 77 | } 78 | 79 | /** 80 | * Compute out 81 | * @param {*} field 82 | * @param {*} value 83 | * @param {*} data 84 | * @returns 85 | */ 86 | function Income(field, value, data) { 87 | log.Info( 88 | "[chart] Income Compute: %s", 89 | JSON.stringify({ field: field, value: value, data: data }) 90 | ); 91 | return value; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /scripts/sub/demo.js: -------------------------------------------------------------------------------- 1 | function Main(name, value) { 2 | return name + value; 3 | } 4 | //# sourceMappingURL=demo.js.map 5 | 6 | -------------------------------------------------------------------------------- /scripts/test/chatgpt2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 处理GET请求,注意get请求无法处理包含特殊字符的请求 3 | * 4 | * /api/ai/ask?q=xxx.xx.xx 5 | * @param {*} message 6 | * @returns 7 | */ 8 | function Callq(message) { 9 | return Call({ prompt: message }); 10 | } 11 | 12 | /** 13 | * 处理post请求,并调用chatgpt接口 14 | * @param {object} message 消息文本 15 | * @returns 16 | */ 17 | function Call(message) { 18 | const setting = GetSetting(); 19 | if (!setting || !setting.api_token) { 20 | return "请在管理界面维护AI连接设置值"; 21 | } 22 | if (!message || !message.prompt) { 23 | return "请填写您的问题"; 24 | } 25 | const ask = message.prompt; 26 | if (ask.length < 2) { 27 | return "请填写详细的问题"; 28 | } 29 | 30 | let conversation = message.conversation; 31 | 32 | conversation = checkLenAndDelete(conversation, setting.max_tokens); 33 | // console.log('对话内容列表:') 34 | // console.log(conversation) 35 | 36 | var chatGptName = setting.ai_nickname || "AI智能助理"; 37 | var userName = setting.user_nickname || "用户"; 38 | var stopword = setting.stop || "<|endoftext|>"; 39 | var prompt = 40 | "提示:你叫" + 41 | chatGptName + 42 | "。" + 43 | stopword + 44 | "当前时间:" + 45 | CurrentTime() + 46 | "。" + 47 | stopword; 48 | 49 | conversation.map((line) => { 50 | if (line.u) { 51 | prompt += userName + ":" + line.u + stopword; 52 | } else if (line.ai) { 53 | prompt += chatGptName + ":" + line.ai + stopword; 54 | } 55 | }); 56 | 57 | prompt += chatGptName + ":"; 58 | 59 | // console.log(prompt); 60 | access_count = setting.access_count; 61 | 62 | //console.log("ask:", message) 63 | let reply = http.Post( 64 | "https://api.openai.com/v1/completions", 65 | { 66 | prompt: prompt, 67 | model: setting.model, 68 | max_tokens: setting.max_tokens, 69 | top_p: setting.top_p, 70 | stop: setting.stop, 71 | temperature: setting.temperature, 72 | presence_penalty: setting.presence_penalty, 73 | frequency_penalty: setting.frequency_penalty, 74 | }, 75 | null, 76 | null, 77 | { 78 | "Content-Type": "application/json", 79 | // 'Authorization': `Bearer ${$ENV.AI_API_KEY}`, 80 | Authorization: `Bearer ` + setting.api_token, 81 | } 82 | ); 83 | if (reply.code != 200) { 84 | return reply.data.error.message; 85 | } 86 | const answer = reply.data.choices[0].text; 87 | SaveLog(ask, answer); 88 | SaveLog2(setting); 89 | return answer; 90 | } 91 | 92 | /** 93 | * 检查会话是否超过限制,如果超过,从开始端删除内容 94 | * @param {Array} conversation 会话列表 95 | * @param {integer} limit token限制 96 | * @returns 97 | */ 98 | function checkLenAndDelete(conversation, limit) { 99 | let total = 0; 100 | let idx = 0; 101 | for (let index = conversation.length - 1; index >= 0; index--) { 102 | const element = conversation[index]; 103 | if (element.u) { 104 | total += element.u?.length; 105 | } else if (element.ai) { 106 | total += element.ai?.length; 107 | } 108 | if (total > limit) { 109 | idx = index; 110 | break; 111 | } 112 | } 113 | 114 | while (idx > 0); 115 | { 116 | conversation.shift(); 117 | idx--; 118 | } 119 | return conversation; 120 | } 121 | function CurrentTime() { 122 | var date = new Date(); 123 | // utc时间整时区 124 | var newDate = new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000); 125 | var offset = date.getTimezoneOffset() / 60; 126 | var hours = date.getHours(); 127 | newDate.setHours(hours - offset); 128 | return newDate.toISOString().slice(0, 19).replace("T", " "); 129 | // return dateObj.toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " "); 130 | } 131 | 132 | /** 133 | * 更新访问统计 134 | * @param {object} setting 135 | * @returns 136 | */ 137 | function SaveLog2(setting) { 138 | return Process("models.ai.setting.Update", setting.id, { 139 | access_count: setting.access_count + 1, 140 | }); 141 | } 142 | function SaveLog(question, answer) { 143 | return Process( 144 | "models.ai.chatlog.Insert", 145 | ["question", "answer"], 146 | [[question, answer]] 147 | ); 148 | } 149 | 150 | function GetSetting() { 151 | const setting = Process("models.ai.setting.Get", { 152 | wheres: [ 153 | { 154 | Column: "default", 155 | Value: true, 156 | }, 157 | { 158 | Column: "deleted_at", 159 | Value: null, 160 | }, 161 | ], 162 | }); 163 | return setting[0]; 164 | } 165 | -------------------------------------------------------------------------------- /scripts/test/chatgpt3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 调用chatgpt接口 3 | * @param {string} message 消息文本 4 | * @returns 5 | */ 6 | function Call(message) { 7 | //console.log("ask:", message) 8 | let reply = http.Post( 9 | "https://api.openai.com/v1/completions", 10 | { 11 | prompt: message, 12 | model: "text-davinci-003", 13 | max_tokens: 1024, 14 | n: 1, 15 | stop: "None", 16 | temperature: 0.5, 17 | }, 18 | null, 19 | null, 20 | { 21 | "Content-Type": "application/json", 22 | // 'Authorization': `Bearer ${$ENV.AI_API_KEY}`, 23 | Authorization: `Bearer ` + getMyApiKey(), 24 | } 25 | ); 26 | //console.log(reply) 27 | if (reply.code != 200) { 28 | return reply.data.error.message; 29 | } 30 | //console.log(reply.data.choices[0].text) 31 | const answer = reply.data.choices[0].text; 32 | SaveLog(message, answer); 33 | return answer; 34 | } 35 | 36 | function SaveLog(question, answer) { 37 | return Process( 38 | "yao.table.Insert", 39 | "aichat", 40 | ["question", "answer"], 41 | [[question, answer]] 42 | ); 43 | } 44 | /** 45 | * 从数据库中读取gpt相关的配置,只读取第1条 46 | * @returns 47 | */ 48 | function GetSetting() { 49 | const setting = Process("models.setting.Find", 1, {}); 50 | return setting; 51 | } 52 | function getMyApiKey() { 53 | return "sk-"; 54 | } 55 | -------------------------------------------------------------------------------- /scripts/test/demo.js: -------------------------------------------------------------------------------- 1 | // const { Process } = require("../../remote-debug"); 2 | // console.log(global); 3 | function Main() { 4 | let setting = GetSetting(); 5 | console.log(setting); 6 | } 7 | function GetSetting() { 8 | const setting = Process("models.ai.setting.Get", { 9 | wheres: [ 10 | { 11 | Column: "default", 12 | Value: true, 13 | }, 14 | { 15 | Column: "deleted_at", 16 | Value: null, 17 | }, 18 | ], 19 | }); 20 | return setting[0]; 21 | } 22 | // Main(); 23 | 24 | -------------------------------------------------------------------------------- /scripts/test/test.js: -------------------------------------------------------------------------------- 1 | function testTrimWord() { 2 | const ls = 3 | "\n\n1. 丽江古城夕照湖:位于云南省,是中国最著名的湖泊之一。\n2. 青海湖:位于青海省,是中国著名的湖泊之一,也是世界文化遗产。\n3. 洞庭湖:位于湖南省,是中国第一大淡水湖,也是中国最著名的湖泊之一。\n4. 天津渤海:位于天津市,是中国最大的沿海湾,也是中国最著名的湖泊之一。\n5. 小五台:位于山西省,是中国著名的湖泊之一,也是世界文化遗产之一。\n6. 合肥太湖:位于安徽省,是中国最大的内陆湖泊,也是中国最著名的湖泊之一。\n7. 洱海:位于云南省,是中国最大的湖泊,也是中国最著名的湖泊之一。"; 4 | 5 | let answer = ls.trim(); 6 | //删除第一个空行 7 | console.log("删除第一个空行:", answer); 8 | answer = answer.replace(/^\s*\n/, ""); 9 | console.log(); 10 | console.log(answer); 11 | } 12 | function CurrentTime() { 13 | var date = new Date(); 14 | // utc时间整时区 15 | 16 | date.setHours(date.getHours() + 8); 17 | // console.log(date.toISOString().slice(0, 19) + "Z08:00"); 18 | return date.toISOString().slice(0, 19) + "Z08:00"; //北京时区 19 | // return newDate.toISOString().slice(0, 19).replace("T", " "); 20 | // return dateObj.toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " "); 21 | } 22 | // CurrentTime(); 23 | 24 | function testTime() { 25 | var newDate = new Date(); 26 | var timeoffset = -480; //newDate.getTimezoneOffset(); 27 | 28 | var offset = timeoffset / 60; 29 | var hours = newDate.getHours(); 30 | newDate.setHours(hours - offset); 31 | console.log(newDate.toISOString()); 32 | console.log(newDate.toUTCString()); 33 | console.log(newDate.toLocaleDateString()); 34 | console.log(newDate.toISOString().slice(0, 19) + "Z08:00"); //北京时区 35 | } 36 | 37 | /** 38 | * 更新访问统计 39 | * @param {object} setting 40 | * @returns 41 | */ 42 | function SaveLog2(setting) { 43 | return Process("models.ai.setting.Update", setting.id, { 44 | access_count: setting.access_count + 1, 45 | }); 46 | } 47 | function SaveLog(question, answer) { 48 | return Process( 49 | "models.ai.chatlog.Insert", 50 | ["question", "answer"], 51 | [[question, answer]] 52 | ); 53 | } 54 | 55 | function CurrentTime() { 56 | var date = new Date(); 57 | // utc时间整时区 58 | 59 | date.setHours(date.getHours() + 8); 60 | // console.log(date.toISOString().slice(0, 19) + "Z08:00"); 61 | return date.toISOString().slice(0, 19) + "Z08:00"; //北京时区 62 | // return newDate.toISOString().slice(0, 19).replace("T", " "); 63 | // return dateObj.toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " "); 64 | } 65 | // CurrentTime(); 66 | 67 | -------------------------------------------------------------------------------- /scripts/text.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 裁剪过长的文本 4 | * @param {string} text 5 | * @returns 6 | */ 7 | function Cut(text) { 8 | // log.Info("Cut text:", text) 9 | if (text && text.length > 20) { 10 | return text.substring(0, 20) + "..." 11 | } 12 | return text 13 | } 14 | -------------------------------------------------------------------------------- /scripts/utils/array.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 按索引剔除数组中的某个位置的数据 4 | * yao run scripts.ai.model.splitAt 2 '::[1,2,3,4,5]' 5 | * @param {*integer} n 需要删除数据的索引 6 | * @param {Array} array 数组 7 | * @returns 8 | */ 9 | function splitAt(n, array) { 10 | const n1 = parseInt(n)//如果是命令行执行一个要转换,要不然会变成字符串 11 | // console.log(array) 12 | // array.splice(n, 1)//!!splice是直接在原数组上删除 13 | return array.slice(0, n1).concat(array.slice(n1 + 1)) 14 | } 15 | /** 16 | * 按索引剔除二维数据中每一列的某个位置的数据 17 | * run scripts.ai.model.splitArrayAt 2 '::[[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]]' 18 | * @param {integer} n 需要删除数据的索引 19 | * @param {Array} array 二维数组 20 | * @returns 21 | */ 22 | function splitArrayAt(n, array) { 23 | let newArray = [] 24 | // array.map(line => { line.splice(n, 1) })//!!splice是直接在原来的数组源上删除 25 | array.forEach(element => { 26 | newArray.push(splitAt(n, element)) 27 | }); 28 | return newArray 29 | } 30 | 31 | 32 | // 在数组里找到某一列,并根据这列的位置去删除另外一个数组中的数据 33 | 34 | // let index = -1 35 | // for (const key in arrs.columns) { 36 | // if (arrs.columns[key] == "permission") { 37 | // index = key 38 | // break; 39 | // } 40 | // } 41 | // arrs.columns.splice(index, 1) 42 | // arrs.values.map(line => { line.splice(index, 1) }) 43 | 44 | 45 | -------------------------------------------------------------------------------- /scripts/utils/test.js: -------------------------------------------------------------------------------- 1 | function test() { 2 | 3 | 4 | 5 | // console.log(obj) 6 | } 7 | -------------------------------------------------------------------------------- /scripts/utils/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 重置管理员密码 3 | * yao run scripts.utils.user.ResetAdmin 18012341234 xxx@qq.com Abcd1234+ 4 | * @param {string} mobile 手机号码 5 | * @param {string} email 邮件地址 6 | * @param {string} password 密码 7 | * @returns newuser 新用户 8 | */ 9 | function ResetAdmin(mobile, email, password) { 10 | var qb = new Query(); 11 | rc = qb.Get({ 12 | sql: { 13 | stmt: "delete from xiang_user", 14 | }, 15 | }); 16 | 17 | Process("models.admin.user.create", { 18 | name: "Admin", 19 | type: "admin", 20 | mobile: mobile, 21 | email: email, 22 | password: password, 23 | status: "enabled", 24 | }); 25 | 26 | const newuser = Process("models.admin.user.get", {}); 27 | // console.log(newuser); 28 | return newuser; 29 | } 30 | -------------------------------------------------------------------------------- /scripts/xgen/compute/json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * scripts.ddic.compute.json.View 3 | * @param {*} data 4 | * @returns 5 | */ 6 | function View(data) { 7 | if (isObject(data)) { 8 | return JSON.stringify(data); 9 | } 10 | return data; 11 | } 12 | /** 13 | * scripts.ddic.compute.json.Edit 14 | * @param {any} data 15 | * @returns 16 | */ 17 | function Edit(data) { 18 | if (isJsonString(data)) { 19 | return JSON.parse(data); 20 | } 21 | return data; 22 | } 23 | function isObject(value) { 24 | return value && typeof value === "object"; // && value.constructor === Object; 25 | } 26 | function isJsonString(obj) { 27 | try { 28 | JSON.parse(obj); 29 | } 30 | catch (e) { 31 | return false; 32 | } 33 | return true; 34 | } 35 | -------------------------------------------------------------------------------- /scripts/xgen/file/upload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 转换图片显示格式 3 | * 4 | * scripts.file.upload.View 5 | * @param data 图片字段设置 6 | * @returns string[] 数组格式的图片地址 7 | */ 8 | function View(data) { 9 | if (!data || !data.length) { 10 | return null; 11 | } 12 | if (!Array.isArray(data)) { 13 | return data.split(","); 14 | } 15 | // let isArray = true; 16 | // try { 17 | // isArray = Array.isArray(JSON.parse(data)); 18 | // } catch (error) { 19 | // isArray = false; 20 | // } 21 | // let array = Array.isArray(data) 22 | // ? data 23 | // : isArray 24 | // ? JSON.parse(data) 25 | // : data.includes(",") 26 | // ? data.split(",") 27 | // : [data]; 28 | // if (!array || array.length == 0) { 29 | // return null; 30 | // } 31 | // return array; 32 | } 33 | /** 34 | * scripts.file.upload.Edit 35 | * 36 | * 这里为什么不使用Compute Upload,Upload会把上传地址的前缀处理掉,只留下文件名.而这里是直接保留了文件的下载地址. 37 | * @param row 行数据 38 | * @param name 字段名称 39 | * @param model_name 模型名称 40 | * @returns 处理后的图片地址 41 | */ 42 | function Edit(row, name, model_name) { 43 | if (Array.isArray(row[name])) { 44 | return row[name].join(","); 45 | } 46 | // const table = Process("schemas.default.TableGet", model_name); 47 | // const column = table.columns.find((col) => col.name === name); 48 | // if (!column || column.type === "json") { 49 | // return row[name]; 50 | // } 51 | // //非json的格式化成json 52 | // return JSON.stringify(row[name]); 53 | } 54 | -------------------------------------------------------------------------------- /stores/cache.lru.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LRU Cache", 3 | "description": "LRU缓存", 4 | "type": "lru", 5 | "option": { "size": 102400 } 6 | } 7 | -------------------------------------------------------------------------------- /tables/ai/chatlog.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Log", 3 | "action": { 4 | "bind": { 5 | "model": "ai.chatlog", 6 | "option": { 7 | "form": "ai.chatlog" 8 | } 9 | } 10 | }, 11 | "fields": { 12 | "table": { 13 | "提问时间": { 14 | "bind": "created_at", 15 | "view": { 16 | "props": { 17 | "format": "YYYY-MM-DD HH:mm:ss" 18 | }, 19 | "type": "Text" 20 | } 21 | }, 22 | "回答": { 23 | "bind": "answer", 24 | "view": { 25 | "props": { 26 | "title": "回答" 27 | }, 28 | "compute": "scripts.text.Cut", 29 | "type": "Tooltip" 30 | } 31 | }, 32 | "提问": { 33 | "bind": "question", 34 | "view": { 35 | "compute": "scripts.text.Cut", 36 | "props": {}, 37 | "type": "Tooltip" 38 | } 39 | }, 40 | "更新时间": { 41 | "bind": "updated_at", 42 | "view": { 43 | "props": { 44 | "format": "YYYY-MM-DD HH:mm:ss" 45 | }, 46 | "type": "Text" 47 | } 48 | } 49 | } 50 | }, 51 | "layout": { 52 | "filter": { 53 | "actions": [ 54 | { 55 | "action": [ 56 | { 57 | "name": "HistoryPush", 58 | "payload": { 59 | "pathname": "/x/Form/ai.chatlog/0/edit" 60 | }, 61 | "type": "Common.historyPush" 62 | } 63 | ], 64 | "icon": "icon-plus", 65 | "title": "::Create", 66 | "width": 3 67 | } 68 | ], 69 | "columns": [ 70 | { 71 | "name": "提问" 72 | } 73 | ] 74 | }, 75 | "header": { 76 | "actions": [], 77 | "preset": {} 78 | }, 79 | "table": { 80 | "columns": [ 81 | { 82 | "name": "提问", 83 | "width": 160 84 | }, 85 | { 86 | "name": "回答", 87 | "width": 260 88 | }, 89 | { 90 | "name": "提问时间", 91 | "width": 160 92 | } 93 | ], 94 | "operation": { 95 | "actions": [ 96 | { 97 | "action": [ 98 | { 99 | "name": "OpenModal", 100 | "payload": { 101 | "Form": { 102 | "model": "ai.chatlog", 103 | "type": "view" 104 | } 105 | }, 106 | "type": "Common.openModal" 107 | } 108 | ], 109 | "icon": "icon-eye", 110 | "title": "查看" 111 | }, 112 | { 113 | "action": [ 114 | { 115 | "name": "OpenModal", 116 | "payload": { 117 | "Form": { 118 | "model": "ai.chatlog", 119 | "type": "edit" 120 | } 121 | }, 122 | "type": "Common.openModal" 123 | } 124 | ], 125 | "icon": "icon-edit-2", 126 | "title": "编辑" 127 | }, 128 | { 129 | "action": [ 130 | { 131 | "name": "Confirm", 132 | "type": "Common.confirm", 133 | "payload": { 134 | "title": "确认删除", 135 | "content": "删除后不可撤销!" 136 | } 137 | }, 138 | { 139 | "name": "Delete", 140 | "payload": { 141 | "model": "ai.chatlog" 142 | }, 143 | "type": "Table.delete" 144 | } 145 | ], 146 | "icon": "icon-trash-2", 147 | "style": "danger", 148 | "title": "Delete" 149 | } 150 | ], 151 | "width": 140 152 | }, 153 | "props": {} 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /tables/ai/chatlog2.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Log", 3 | "action": { 4 | "bind": { 5 | "model": "ai.chatlog" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tables/chat/conversation_simple.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Conversation", 3 | "action": { 4 | "bind": { 5 | "model": "chat.conversation", 6 | "option": { "form": "chat.conversation_simple", "withs": {} } 7 | }, 8 | "before:delete": "scripts.chat.conversation.DeleteMessage", 9 | "before:delete-in": "scripts.chat.conversation.DeleteMessageIn" 10 | }, 11 | "layout": { 12 | "filter": { 13 | "actions": [ 14 | { 15 | "action": [ 16 | { 17 | "name": "HistoryPush", 18 | "payload": { 19 | "pathname": "/x/Form/chat.conversation_simple/0/edit" 20 | }, 21 | "type": "Common.historyPush" 22 | } 23 | ], 24 | "icon": "icon-plus", 25 | "title": "创建", 26 | "width": 3 27 | } 28 | ], 29 | "columns": [{ "name": "标题" }] 30 | }, 31 | "header": { 32 | "actions": [], 33 | "preset": { 34 | "batch": { 35 | "columns": [ 36 | { "name": "ID", "width": 100 }, 37 | { "name": "标题", "width": 160 } 38 | ], 39 | "deletable": true 40 | } 41 | } 42 | }, 43 | "primary": "id", 44 | "table": { 45 | "columns": [ 46 | { "name": "ID", "width": 50 }, 47 | 48 | { "name": "标题", "width": 160 }, 49 | { "name": "创建时间", "width": 160 } 50 | ], 51 | "operation": { 52 | "actions": [ 53 | { 54 | "action": [ 55 | { 56 | "name": "OpenModal", 57 | "payload": { 58 | "Form": { 59 | "model": "chat.conversation_simple", 60 | "type": "view" 61 | } 62 | }, 63 | "type": "Common.openModal" 64 | } 65 | ], 66 | "icon": "icon-eye", 67 | "title": "查看" 68 | }, 69 | { 70 | "action": [ 71 | { 72 | "name": "OpenModal", 73 | "payload": { 74 | "Form": { 75 | "model": "chat.conversation_simple", 76 | "type": "edit" 77 | } 78 | }, 79 | "type": "Common.openModal" 80 | } 81 | ], 82 | "icon": "icon-edit-2", 83 | "title": "编辑" 84 | }, 85 | { 86 | "action": [ 87 | { 88 | "name": "Confirm", 89 | "type": "Common.confirm", 90 | "payload": { 91 | "title": "确认删除", 92 | "content": "删除后不可撤销!" 93 | } 94 | }, 95 | { 96 | "name": "Delete", 97 | "payload": { "model": "chat.conversation_simple" }, 98 | "type": "Table.delete" 99 | } 100 | ], 101 | "icon": "icon-trash-2", 102 | "style": "danger", 103 | "title": "Delete" 104 | } 105 | ], 106 | "width": 140 107 | }, 108 | "props": { "scroll": { "x": "max-content" } } 109 | } 110 | }, 111 | "fields": { 112 | "filter": { 113 | "标题": { 114 | "bind": "where.title.match", 115 | "edit": { 116 | "props": { "placeholder": "请输入 标题" }, 117 | "type": "Input", 118 | "compute": "scripts.text.Cut" 119 | } 120 | } 121 | }, 122 | "table": { 123 | "API设置": { 124 | "bind": "api_setting", 125 | "view": { "props": {}, "type": "Text" } 126 | }, 127 | "ID": { 128 | "bind": "id", 129 | "view": { "props": {}, "type": "Text" } 130 | }, 131 | "会话ID": { 132 | "bind": "uuid", 133 | "view": { "props": {}, "type": "Text" } 134 | }, 135 | "创建时间": { 136 | "bind": "created_at", 137 | "view": { "props": { "format": "YYYY-MM-DD HH:mm:ss" }, "type": "Text" } 138 | }, 139 | "标题": { 140 | "bind": "title", 141 | "edit": { "props": { "placeholder": "请输入 标题" }, "type": "Input" }, 142 | "view": { "props": {}, "type": "Text" } 143 | } 144 | } 145 | }, 146 | "config": { "full": true } 147 | } 148 | -------------------------------------------------------------------------------- /tables/chat/message_simple.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Chat Message", 3 | "action": { 4 | "bind": { 5 | "model": "chat.message", 6 | "option": { "form": "chat.message_simple", "withs": {} } 7 | } 8 | }, 9 | "layout": { 10 | "filter": { 11 | "actions": [ 12 | { 13 | "action": [ 14 | { 15 | "name": "HistoryPush", 16 | "payload": { "pathname": "/x/Form/chat.message_simple/0/edit" }, 17 | "type": "Common.historyPush" 18 | } 19 | ], 20 | "icon": "icon-plus", 21 | "title": "创建", 22 | "width": 3 23 | } 24 | ], 25 | "columns": [{ "name": "发送的消息" }] 26 | }, 27 | "header": { "actions": [], "preset": {} }, 28 | "primary": "id", 29 | "table": { 30 | "columns": [ 31 | { "name": "ID", "width": 50 }, 32 | { "name": "发送的消息", "width": 160 }, 33 | { "name": "回复的消息", "width": 260 }, 34 | { "name": "请求时间(秒)", "width": 50 } 35 | ], 36 | "operation": { 37 | "actions": [ 38 | { 39 | "action": [ 40 | { 41 | "name": "OpenModal", 42 | "payload": { 43 | "Form": { "model": "chat.message_simple", "type": "view" } 44 | }, 45 | "type": "Common.openModal" 46 | } 47 | ], 48 | "icon": "icon-eye", 49 | "title": "查看" 50 | } 51 | ], 52 | "width": 140 53 | }, 54 | "props": { "scroll": { "x": "max-content" } } 55 | } 56 | }, 57 | "fields": { 58 | "filter": { 59 | "发送的消息": { 60 | "bind": "where.prompt.match", 61 | "edit": { 62 | "props": { "placeholder": "请输入 发送的消息" }, 63 | "type": "Input" 64 | } 65 | } 66 | }, 67 | "table": { 68 | "ID": { 69 | "bind": "id", 70 | "view": { "props": {}, "type": "Text" } 71 | }, 72 | "发送的消息": { 73 | "bind": "prompt", 74 | "view": { 75 | "props": { "title": "发送的消息" }, 76 | "type": "Text", 77 | "compute": "scripts.text.Cut" 78 | } 79 | }, 80 | "回复的消息": { 81 | "bind": "completion", 82 | "view": { 83 | "props": { "title": "回复的消息" }, 84 | "type": "Text", 85 | "compute": "scripts.text.Cut" 86 | } 87 | }, 88 | 89 | "请求时间(秒)": { 90 | "bind": "request_total_time", 91 | "view": { "props": {}, "type": "Text" } 92 | } 93 | } 94 | }, 95 | "config": { "full": true } 96 | } 97 | -------------------------------------------------------------------------------- /tables/chat/prompttemplate.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Prompt Template", 3 | "action": { 4 | "bind": { 5 | "model": "chat.prompttemplate", 6 | "option": { "form": "chat.prompttemplate", "withs": {} } 7 | } 8 | }, 9 | "config": { "full": true }, 10 | "layout": { 11 | "header": { "actions": [], "preset": {} }, 12 | "primary": "id", 13 | "filter": { 14 | "actions": [ 15 | { 16 | "action": [ 17 | { 18 | "name": "HistoryPush", 19 | "payload": { "pathname": "/x/Form/chat.prompttemplate/0/edit" }, 20 | "type": "Common.historyPush" 21 | } 22 | ], 23 | "icon": "icon-plus", 24 | "title": "创建", 25 | "width": 3 26 | } 27 | ], 28 | "columns": [{ "name": "提示" }] 29 | }, 30 | "table": { 31 | "columns": [ 32 | { "name": "ID", "width": 50 }, 33 | { "name": "提示", "width": 160 }, 34 | { "name": "模板内容", "width": 260 }, 35 | { "name": "状态", "width": 80 }, 36 | { "name": "创建时间", "width": 160 }, 37 | { "name": "更新时间", "width": 160 } 38 | ], 39 | "operation": { 40 | "actions": [ 41 | { 42 | "action": [ 43 | { 44 | "name": "OpenModal", 45 | "payload": { 46 | "Form": { "model": "chat.prompttemplate", "type": "view" } 47 | }, 48 | "type": "Common.openModal" 49 | } 50 | ], 51 | "icon": "icon-eye", 52 | "title": "查看" 53 | }, 54 | { 55 | "action": [ 56 | { 57 | "name": "OpenModal", 58 | "payload": { 59 | "Form": { "model": "chat.prompttemplate", "type": "edit" } 60 | }, 61 | "type": "Common.openModal" 62 | } 63 | ], 64 | "icon": "icon-edit-2", 65 | "title": "编辑" 66 | }, 67 | { 68 | "action": [ 69 | { 70 | "name": "Confirm", 71 | "type": "Common.confirm", 72 | "payload": { 73 | "title": "确认删除", 74 | "content": "删除后不可撤销!" 75 | } 76 | }, 77 | { 78 | "name": "Delete", 79 | "payload": { "model": "chat.prompttemplate" }, 80 | "type": "Table.delete" 81 | } 82 | ], 83 | "icon": "icon-trash-2", 84 | "style": "danger", 85 | "title": "Delete" 86 | } 87 | ], 88 | "width": 140 89 | }, 90 | "props": { "scroll": { "x": "max-content" } } 91 | } 92 | }, 93 | "fields": { 94 | "filter": { 95 | "提示": { 96 | "bind": "where.title.match", 97 | "edit": { "props": { "placeholder": "请输入 提示" }, "type": "Input" } 98 | } 99 | }, 100 | "table": { 101 | "ID": { 102 | "bind": "id", 103 | "view": { "props": {}, "type": "Text" } 104 | }, 105 | "创建时间": { 106 | "bind": "created_at", 107 | "view": { "props": { "format": "YYYY-MM-DD HH:mm:ss" }, "type": "Text" } 108 | }, 109 | "提示": { 110 | "bind": "title", 111 | "view": { "props": {}, "type": "Text" } 112 | }, 113 | "更新时间": { 114 | "bind": "updated_at", 115 | "view": { "props": { "format": "YYYY-MM-DD HH:mm:ss" }, "type": "Text" } 116 | }, 117 | "模板内容": { 118 | "bind": "content", 119 | "view": { 120 | "props": { "title": "模板内容" }, 121 | "type": "Text", 122 | "compute": "scripts.text.Cut" 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tables/chat/sensitive_word.tab.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "::Sensitive Word", 3 | "action": { 4 | "bind": { 5 | "model": "chat.sensitive_word", 6 | "option": { "form": "chat.sensitive_word", "withs": {} } 7 | } 8 | }, 9 | "layout": { 10 | "filter": { 11 | "actions": [ 12 | { 13 | "action": [ 14 | { 15 | "name": "HistoryPush", 16 | "payload": { "pathname": "/x/Form/chat.sensitive_word/0/edit" }, 17 | "type": "Common.historyPush" 18 | } 19 | ], 20 | "icon": "icon-plus", 21 | "title": "创建", 22 | "width": 3 23 | } 24 | ], 25 | "columns": [{ "name": "敏感词" }] 26 | }, 27 | "header": { "actions": [], "preset": {} }, 28 | "primary": "id", 29 | "table": { 30 | "columns": [ 31 | { "name": "ID", "width": 100 }, 32 | { "name": "敏感词", "width": 160 }, 33 | { "name": "创建时间", "width": 160 }, 34 | { "name": "更新时间", "width": 160 } 35 | ], 36 | "operation": { 37 | "actions": [ 38 | { 39 | "action": [ 40 | { 41 | "name": "OpenModal", 42 | "payload": { 43 | "Form": { "model": "chat.sensitive_word", "type": "view" } 44 | }, 45 | "type": "Common.openModal" 46 | } 47 | ], 48 | "icon": "icon-eye", 49 | "title": "查看" 50 | }, 51 | { 52 | "action": [ 53 | { 54 | "name": "OpenModal", 55 | "payload": { 56 | "Form": { "model": "chat.sensitive_word", "type": "edit" } 57 | }, 58 | "type": "Common.openModal" 59 | } 60 | ], 61 | "icon": "icon-edit-2", 62 | "title": "编辑" 63 | }, 64 | { 65 | "action": [ 66 | { 67 | "name": "Confirm", 68 | "type": "Common.confirm", 69 | "payload": { 70 | "title": "确认删除", 71 | "content": "删除后不可撤销!" 72 | } 73 | }, 74 | { 75 | "name": "Delete", 76 | "payload": { "model": "chat.sensitive_word" }, 77 | "type": "Table.delete" 78 | } 79 | ], 80 | "icon": "icon-trash-2", 81 | "style": "danger", 82 | "title": "Delete" 83 | } 84 | ] 85 | }, 86 | "props": { "scroll": { "x": "max-content" } } 87 | } 88 | }, 89 | "fields": { 90 | "filter": { 91 | "敏感词": { 92 | "bind": "where.word.match", 93 | "edit": { "props": { "placeholder": "请输入 敏感词" }, "type": "Input" } 94 | } 95 | }, 96 | "table": { 97 | "ID": { 98 | "bind": "id", 99 | "edit": { 100 | "props": { "disabled": true, "placeholder": "请输入 ID" }, 101 | "type": "InputNumber" 102 | }, 103 | "view": { "props": {}, "type": "Text" } 104 | }, 105 | "创建时间": { 106 | "bind": "created_at", 107 | "edit": { 108 | "props": { 109 | "placeholder": "请选择 创建时间", 110 | "showTime": { "format": "HH:mm:ss" } 111 | }, 112 | "type": "DatePicker" 113 | }, 114 | "view": { "props": { "format": "YYYY-MM-DD HH:mm:ss" }, "type": "Text" } 115 | }, 116 | "敏感词": { 117 | "bind": "word", 118 | "edit": { 119 | "props": { "placeholder": "请输入 敏感词" }, 120 | "type": "Input" 121 | }, 122 | "view": { "props": {}, "type": "Text" } 123 | }, 124 | "更新时间": { 125 | "bind": "updated_at", 126 | "edit": { 127 | "props": { 128 | "placeholder": "请选择 更新时间", 129 | "showTime": { "format": "HH:mm:ss" } 130 | }, 131 | "type": "DatePicker" 132 | }, 133 | "view": { "props": { "format": "YYYY-MM-DD HH:mm:ss" }, "type": "Text" } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tasks/doc.task.yao: -------------------------------------------------------------------------------- 1 | { 2 | "name": "文档加载", 3 | "worker_nums": 10, 4 | "attempts": 3, 5 | "attempt_after": 200, 6 | "timeout": 2000, 7 | "size": 1000, 8 | "process": "scripts.doc.task.TaskProcess", 9 | "event": { 10 | "next": "", 11 | "add": "scripts.doc.task.OnAdd", 12 | "success": "scripts.doc.task.OnSuccess", 13 | "error": "scripts.doc.task.OnError", 14 | "progress": "scripts.doc.task.OnProgress" 15 | } 16 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "nodenext", 4 | "moduleResolution": "nodenext", 5 | "esModuleInterop": true, 6 | "allowJs": false, 7 | "target": "ESNext", 8 | "paths": { 9 | "@yao/*": [ 10 | "./.vscode/types/*" 11 | ], 12 | "@lib/*": ["./scripts/lib/*"], 13 | "@scripts/*": [ 14 | "./scripts/*" 15 | ] 16 | }, 17 | "lib": ["ESNEXT", "DOM"], 18 | "outDir": "dist", 19 | "skipLibCheck": true, 20 | "sourceMap": true 21 | }, 22 | "exclude": [ 23 | "node_modules", 24 | "dist" 25 | ], 26 | "include": [ 27 | "./assistants/**/*", 28 | "./scripts/**/*", 29 | "./services/**/*", 30 | "./studio/**/*", 31 | "./.vscode/types/**/*" 32 | ], 33 | "ts-node": { 34 | "require": [ 35 | "tsconfig-paths/register" 36 | ] 37 | } 38 | } 39 | --------------------------------------------------------------------------------