chat web app for teams, sass with user management and ratelimit, support chatgpt(openai & azure), claude or custom model

  • By Hao Wu
  • Last update: Aug 13, 2023
  • Comments: 8

Demo

video.webm

image

image

image

规则

  • 第一个消息是系统消息(prompt)
  • 上下文默认附带最新创建的4条消息
  • 第一个注册的用户是管理员
  • 默认限流 100 chatGPT call /10分钟 (OPENAI_RATELIMIT=100)
  • 根据对话生成可以分享的静态页面(like ShareGPT), 也可以继续会话.
  • 对话快照目录(对话集), 支持全文查找(Enlgish), 方便整理, 搜索会话记录.
  • 支持OPEN AI, Claude 模型 免费申请链接
  • 提示词管理, 提示词快捷键 '/'

参与开发

  1. git clone
  2. golang dev
cd chat; cd api
go install github.com/cosmtrek/air@latest
go mod tidy
# export env var, change base on your env
export PG_HOST=192.168.0.135
export PG_DB=hwu
export PG_USER=hwu
export PG_PASS=pass
export PG_PORT=5432
# export DATABASE_URL= postgres://user:[email protected]:5432/db?sslmode=disable

# export OPENAI_API_KEY=sk-xxx, not required if you use `debug` model
# export OPENAI_RATELIMIT=100
#
make serve
  1. node env
cd ..; cd web
npm install
npm run dev
  1. e2e test
cd ..; cd e2e
# export env var, change base on your env
export PG_HOST=192.168.0.135
export PG_DB=hwu
export PG_USER=hwu
export PG_PASS=pass
export PG_PORT=5432
npm install
npx playwright test # --ui 

The instruction might not be accurate, ask in issue or discussion if unclear.

如何部署

参考 docker-compose.yaml

Deploy on Railway

然后配置环境变量就可以了.

PORT=8080
OPENAI_RATELIMIT=0

别的两个 api key 有就填.

image

部署之后, 注册用户, 第一个用户是管理员, 然后到 https://$hostname/static/#/admin/user, 设置 ratelimit, 公网部署, 只对信任的email 增加 ratelimit, 这样即使有人注册, 也是不能用的.

image

致谢

LICENCE: MIT

How to Use

  • The first message is a system message (prompt)
  • by default, the latest 4 messages are context
  • First user is superuser.
  • 100 chatgpt api call / 10 mins (OPENAI_RATELIMIT=100)
  • Snapshot conversation and Share (like ShareGPT)
  • Support OPEN AI, Claude model free application link

How to Deploy

Refer to docker-compose.yaml

Deploy on Railway

Then configure the environment variables.

PORT=8080
OPENAI_RATELIMIT=0

Fill in the other two keys if you have them.

image

After deployment, registering users, the first user is an administrator, then go to https://$hostname/static/#/admin/user to set rate limiting. Public deployment, only adds rate limiting to trusted emails, so even if someone registers, it will not be available.

image

This helps ensure only authorized users can access the deployed system by limiting registration to trusted emails and enabling rate limiting controls.

Acknowledgments

Download

chat.zip

Comments(8)

  • 1

    chat通过docker-compose无法启动

    日志:

    time="2023-04-29T14:10:57Z" level=info msg="{OPENAI:{API_KEY:sk-xxx RATELIMIT:100 PROXY_URL:http://127.0.0.1:7890} CLAUDE:{API_KEY:} PG:{HOST:db PORT:5432 USER:postgres PASS:postgres DB:postgres}}"
    panic: EOF
    
    goroutine 1 [running]:
    main.main()
    	/app/main.go:137 +0x1225
    
  • 2

    Prompt length2

    生成最终opean ai request的时候, 第一个从prompt里面拿(role is system), 剩余的从message 里面拿(不改变其role). 清空的时候保留prompt 和 promptNumebr -1 条消息.

    还有一个上下文的概念, 默认最新10条消息是发送过去的.

    综上, 发过去的消息 (askMessage) = 1 chat_prompt + (promptLength -1) top chat_messages + last N context messages (topN, lastN 去重)

    最好有个UI indicator, 标记 prompt 和 promptLength message. 这些是确定的.

    上下文在变, 不好标记.

  • 3

    user stat table with rate limit

    Summary by OpenAI

    Release Notes:

    This pull request adds rate limit and user stat table functionality to the backend, as well as an admin tab to the frontend. It also includes changes to improve authentication and authorization, such as checking for admin access and adding a role parameter to token generation. Additionally, it removes commented-out code and replaces lodash with lodash-es.

    User Story:

    As an administrator, I want to be able to view user statistics and set rate limits for users, so that I can manage user activity on the platform more effectively.

  • 4

    create chat snapshot and make the conversation sharable

    -- for share chat feature
    CREATE TABLE IF NOT EXISTS chat_snapshot (
        id SERIAL PRIMARY KEY,
        uuid VARCHAR(255) NOT NULL default '',
        user_id INTEGER NOT NULL default 0,
        title VARCHAR(255) NOT NULL default '',
        summary TEXT NOT NULL default '',
        tags JSONB DEFAULT '{}' NOT NULL,
        conversation JSONB NOT NULL default '{}',
        created_at TIMESTAMP DEFAULT now() NOT NULL
    );
    
  • 5

    修复数据库重新部署后,chatgpt无法使用问题。

    问题描述

    1. 当我删除了sql中的databse时,此时将数据 dump 后。我发现在存储chat信息至数据库时,报错了主键 ID 已存在。目前我发现的存在问题的表有 chat_prompt、chat_message。

    解决方法

    该pr是我的解决方法,主要逻辑是:在插入数据时,每次使用最大的 ID。

    备注

    这也许不是代码层面bug,我并不了解 postgres。猜测可能是因为 postgres 数据库逻辑导致。或者就是我不会用。

  • 6

    一直报错 FATAL: role "root" does not exist

    web | 218.82.136.139 - - [10/Apr/2023:09:13:46 +0000] "POST /api/chat_stream HTTP/1.1" 500 61 "http://1x:8084/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" "-" api | 172.18.0.4 - - [10/Apr/2023:09:13:46 +0000] "POST /chat_stream HTTP/1.1" 500 61 chat-db-1 | 2023-04-10 17:13:46.927 CST [156] FATAL: role "root" does not exist api | 2023/04/10 09:13:50 chatSessionUuid: 1fdf2591-898a-4af6-9a62-d0be5a00fead api | 2023/04/10 09:13:50 chatUuid: 1e5032dd-0c0c-45ea-a04f-d68c412b05f6 api | 2023/04/10 09:13:50 newQuestion: 1+1等于多少 api | api | 172.18.0.4 - - [10/Apr/2023:09:13:50 +0000] "POST /chat_stream HTTP/1.1" 200 0 web | 218.82.136.139 - - [10/Apr/2023:09:13:50 +0000] "POST /api/chat_stream HTTP/1.1" 200 0 "http://1x:8084/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36" "-" chat-db-1 | 2023-04-10 17:13:52.070 CST [165] FATAL: role "root" does not exist chat-db-1 | 2023-04-10 17:13:57.215 CST [173] FATAL: role "root" does not exist chat-db-1 | 2023-04-10 17:14:02.357 CST [181] FATAL: role "root" does not exist

  • 7

    build error, how to fix? thanks

    only set 2 variables , PORT and OPENAI_RATELIMIT, but railway build error, here is build logs as below, could you help me ? and tell me what I need to do? thanks

    
    #19 15.58 src/views/admin/index.vue(43,11): error TS2769: No overload matches this call.
    #19 15.58   The last overload gave the following error.
    #19 15.58     Argument of type '_RouterLinkI' is not assignable to parameter of type 'DefineComponent<{ to: { name: string; }; }, {}, {}, ComputedOptions, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>'.
    #19 15.58       Type '_RouterLinkI' is not assignable to type 'ComponentPublicInstanceConstructor<{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, c...'.
    #19 15.58         Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is not assignable to type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...'.
    #19 15.58           Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is missing the following properties from type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...': $, $data, $attrs, $refs, and 8 more.
    #19 15.58 src/views/admin/index.vue(56,7): error TS2769: No overload matches this call.
    #19 15.58   The last overload gave the following error.
    #19 15.58     Argument of type '_RouterLinkI' is not assignable to parameter of type 'DefineComponent<{ to: { name: string; }; }, {}, {}, ComputedOptions, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>'.
    #19 15.58       Type '_RouterLinkI' is not assignable to type 'ComponentPublicInstanceConstructor<{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, c...'.
    #19 15.58         Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is not assignable to type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...'.
    #19 15.58           Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is missing the following properties from type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...': $, $data, $attrs, $refs, and 8 more.
     
    #19 15.75 ERROR: "type-check" exited with 2.
    #19 ERROR: process "/bin/sh -c npm run build" did not complete successfully: exit code: 1
     
    -----
    > [frontend_builder 6/6] RUN npm run build:
    #19 15.58       Type '_RouterLinkI' is not assignable to type 'ComponentPublicInstanceConstructor<{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, c...'.
    #19 15.58         Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is not assignable to type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...'.
    #19 15.58           Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is missing the following properties from type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...': $, $data, $attrs, $refs, and 8 more.
    #19 15.58 src/views/admin/index.vue(56,7): error TS2769: No overload matches this call.
    #19 15.58   The last overload gave the following error.
    #19 15.58     Argument of type '_RouterLinkI' is not assignable to parameter of type 'DefineComponent<{ to: { name: string; }; }, {}, {}, ComputedOptions, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, ... 4 more ..., {}>'.
    #19 15.58       Type '_RouterLinkI' is not assignable to type 'ComponentPublicInstanceConstructor<{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, c...'.
     
    #19 15.58         Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is not assignable to type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...'.
    #19 15.58           Type '{ $props: AllowedComponentProps & ComponentCustomProps & VNodeProps & RouterLinkProps; $slots: { ...; }; }' is missing the following properties from type '{ $: ComponentInternalInstance; $data: {}; $props: Record<string, unknown> & Partial<{}> & Omit<Readonly<{ to: { name: string; }; }> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infe...': $, $data, $attrs, $refs, and 8 more.
    #19 15.75 ERROR: "type-check" exited with 2.
    -----
    Dockerfile:15
    
  • 8

    claude api 访问有问题异常处理

    当时写的时候 debug mode 主要针对openai的 claude 没有弄. 有空我搞一下. 应该有报错.

    Originally posted by @swuecho in https://github.com/swuecho/chat/discussions/371#discussioncomment-6351161