Compare commits
2 Commits
f433490e0d
...
5ce2a5f1a3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ce2a5f1a3 | ||
|
|
94bdba930e |
7
.env
@@ -1,7 +0,0 @@
|
||||
MOONSHOT_API_KEY=sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs
|
||||
|
||||
# frontend 对外端口
|
||||
FRONTEND_PORT=1025
|
||||
|
||||
# ai-chat-service embedding 配置
|
||||
AI_CHAT_EMBEDDING_API_KEY=d51b903546814cc9981d3649a4a899a3.NQOtz3ocRtQwimh9
|
||||
26
README.md
@@ -8,36 +8,12 @@ protoc \
|
||||
```
|
||||
|
||||
```shell
|
||||
# chatgpt-web-backend
|
||||
go mod tidy
|
||||
go run cmd/main.go \
|
||||
--frontend-path www \
|
||||
--openapi-key $MOONSHOT_API_KEY \
|
||||
--openapi-base-url https://api.moonshot.cn/v1 \
|
||||
--openai-model kimi-k2.5 \
|
||||
--openai-temperature 100 \
|
||||
--openai-presence-penalty 0 \
|
||||
--openai-frequency-penalty 0
|
||||
|
||||
docker build -t chatgpt-web-backend:1.0.0 .
|
||||
|
||||
docker run -d --name chatgpt-web-backend \
|
||||
-p 7080:7080 \
|
||||
chatgpt-web-backend:1.0.0 \
|
||||
/app/server \
|
||||
--frontend-path www \
|
||||
--openapi-key "$MOONSHOT_API_KEY" \
|
||||
--openapi-base-url https://api.moonshot.cn/v1 \
|
||||
--openai-model kimi-k2.5 \
|
||||
--openai-temperature 100 \
|
||||
--openai-presence-penalty 0 \
|
||||
--openai-frequency-penalty 0
|
||||
# ai-chat-web
|
||||
|
||||
# ai-chat-backend
|
||||
docker build -t ai-chat-backend:1.0.0 .
|
||||
|
||||
# ai-chat-service
|
||||
GOCACHE=/tmp/ai-chat-service-gocache go build -o ai-chat-service-bin ./chat-server
|
||||
docker build -t ai-chat-service:1.0.0 .
|
||||
|
||||
# chatgpt-web-frontend
|
||||
|
||||
@@ -7,7 +7,7 @@ log:
|
||||
logPath: "runtime/logs/app.log"
|
||||
chat:
|
||||
# 使用的训练模型
|
||||
model: "kimi-k2.5"
|
||||
model: "kimi-k2-turbo-preview"
|
||||
# 单次请求的上下文总长度,包括:请求消息+响应消息
|
||||
max_tokens: 4096
|
||||
# 表示语言模型输出的随机性和创造性
|
||||
|
||||
@@ -6,7 +6,7 @@ log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
chat:
|
||||
model: "kimi-k2.5"
|
||||
model: "kimi-k2-turbo-preview"
|
||||
max_tokens: 4096
|
||||
temperature: 1
|
||||
top_p: 1
|
||||
|
||||
@@ -14,7 +14,7 @@ type Config struct {
|
||||
BasicAuthUser string `mapstructure:"basic_auth_user"`
|
||||
BasicAuthPassword string `mapstructure:"basic_auth_password"`
|
||||
FrontendPath string `mapstructure:"frontend_path"`
|
||||
Log struct {
|
||||
Log struct {
|
||||
Level string
|
||||
LogPath string `mapstructure:"logPath"`
|
||||
} `mapstructure:"log"`
|
||||
@@ -74,7 +74,7 @@ func normalizeConfig(conf *Config) {
|
||||
conf.FrontendPath = "www"
|
||||
}
|
||||
if conf.Chat.Model == "" {
|
||||
conf.Chat.Model = "kimi-k2.5"
|
||||
conf.Chat.Model = "kimi-k2-turbo-preview"
|
||||
}
|
||||
if conf.Chat.MaxTokens == 0 {
|
||||
conf.Chat.MaxTokens = 4096
|
||||
|
||||
5
ai-chat-service/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.git
|
||||
.gitignore
|
||||
ai-chat-service-bin
|
||||
runtime/logs
|
||||
runtime/*.log
|
||||
@@ -1,11 +1,34 @@
|
||||
FROM golang:1.25 AS builder
|
||||
|
||||
ENV GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct \
|
||||
GOSUMDB=sum.golang.google.cn \
|
||||
CGO_ENABLED=0 \
|
||||
GOOS=linux \
|
||||
GOARCH=amd64
|
||||
|
||||
WORKDIR /src/ai-chat-service
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go mod download
|
||||
|
||||
COPY . .
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go build -o /out/ai-chat-service ./chat-server
|
||||
|
||||
FROM alpine:3.18
|
||||
|
||||
ADD ./grpc_health_probe-linux-amd64 /usr/bin/grpc_health_probe
|
||||
RUN chmod +x /usr/bin/grpc_health_probe
|
||||
|
||||
LABEL maintainer="nick"
|
||||
|
||||
WORKDIR /app/
|
||||
COPY ./ai-chat-service-bin ./ai-chat-service
|
||||
|
||||
COPY --from=builder /out/ai-chat-service ./ai-chat-service
|
||||
COPY ./docker.config.yaml /app/config.yaml
|
||||
# 指定入口程序
|
||||
|
||||
ENTRYPOINT ["./ai-chat-service"]
|
||||
# 指定容器的启动命令或者入口程序的参数
|
||||
CMD ["--config=config.yaml"]
|
||||
|
||||
@@ -6,9 +6,9 @@ log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
chat:
|
||||
api_key: "sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs"
|
||||
api_key: "xxxxxxxxxxx"
|
||||
base_url: "https://api.moonshot.cn/v1"
|
||||
model: "kimi-k2.5"
|
||||
model: "kimi-k2-turbo-preview"
|
||||
max_tokens: 4096
|
||||
temperature: 1
|
||||
top_p: 0.95
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -113,6 +114,7 @@ func InitConfig(filePath string, typ ...string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
normalizeConfig(conf)
|
||||
applySecretEnvOverrides(conf)
|
||||
|
||||
}
|
||||
|
||||
@@ -167,3 +169,15 @@ func normalizeConfig(conf *Config) {
|
||||
conf.Embedding.Timeout = 10
|
||||
}
|
||||
}
|
||||
|
||||
func applySecretEnvOverrides(conf *Config) {
|
||||
if v := os.Getenv("MOONSHOT_API_KEY"); v != "" {
|
||||
conf.Chat.ApiKey = v
|
||||
}
|
||||
if v := os.Getenv("AI_CHAT_EMBEDDING_API_KEY"); v != "" {
|
||||
conf.Embedding.ApiKey = v
|
||||
}
|
||||
if v := os.Getenv("REDIS_PASSWORD"); v != "" {
|
||||
conf.Redis.Pwd = v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ services:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
command:
|
||||
- --default-authentication-plugin=mysql_native_password
|
||||
# ports:
|
||||
# - "3306:3306"
|
||||
volumes:
|
||||
- /data/mysql:/var/lib/mysql
|
||||
- /home/lian/share/aichat/init/create_db.sql:/docker-entrypoint-initdb.d/create_db.sql:ro
|
||||
@@ -33,20 +31,22 @@ services:
|
||||
- /home/lian/share/aichat/init/pgvector-init.sql:/docker-entrypoint-initdb.d/pgvector-init.sql:ro
|
||||
|
||||
tokenizer:
|
||||
build:
|
||||
context: ../tokenizer
|
||||
image: tokenizer:1.0.0
|
||||
container_name: tokenizer
|
||||
# 容器内端口:3002
|
||||
ports:
|
||||
- "3002:3002"
|
||||
restart: unless-stopped
|
||||
|
||||
sensitive-filter:
|
||||
build:
|
||||
context: ../keywords-filter
|
||||
image: keywords-filter:1.0.0
|
||||
container_name: sensitive-filter
|
||||
# 容器内端口:50053
|
||||
volumes:
|
||||
- /home/lian/share/aichat/keywords-filter/dev.config.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/keywords-filter/dict.txt:/app/dict.txt:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/sensitive.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/sensitive-dict.txt:/app/dict.txt:ro
|
||||
command:
|
||||
- --config=/app/config.yaml
|
||||
- --dict=/app/dict.txt
|
||||
@@ -55,12 +55,13 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
keywords-filter:
|
||||
build:
|
||||
context: ../keywords-filter
|
||||
image: keywords-filter:1.0.0
|
||||
container_name: keywords-filter
|
||||
# 容器内端口:50054
|
||||
volumes:
|
||||
- /home/lian/share/aichat/keywords-filter/dev.kw.config.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/keywords-filter/keyword-dict.txt:/app/dict.txt:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/keywords.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/keywords-dict.txt:/app/dict.txt:ro
|
||||
command:
|
||||
- --config=/app/config.yaml
|
||||
- --dict=/app/dict.txt
|
||||
@@ -70,11 +71,13 @@ services:
|
||||
|
||||
ai-chat-service:
|
||||
build:
|
||||
context: ./ai-chat-service
|
||||
context: ../ai-chat-service
|
||||
image: ai-chat-service:1.0.0
|
||||
container_name: ai-chat-service
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- /home/lian/share/aichat/ai-chat-service/docker.config.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/ai-chat-service.yaml:/app/config.yaml:ro
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
ports:
|
||||
@@ -92,24 +95,26 @@ services:
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
chatgpt-web-backend:
|
||||
ai-chat-backend:
|
||||
build:
|
||||
context: ./ai-chat-backend
|
||||
context: ../ai-chat-backend
|
||||
image: ai-chat-backend:1.0.0
|
||||
container_name: chatgpt-web-backend
|
||||
# 容器内端口:7080
|
||||
container_name: ai-chat-backend
|
||||
ports:
|
||||
- "7080:7080"
|
||||
volumes:
|
||||
- /home/lian/share/aichat/ai-chat-backend/docker.config.yaml:/app/config.yaml:ro
|
||||
- /home/lian/share/aichat/ai-chat-stack/configs/ai-chat-backend.yaml:/app/config.yaml:ro
|
||||
depends_on:
|
||||
- ai-chat-service
|
||||
restart: unless-stopped
|
||||
|
||||
chatgpt-web-frontend:
|
||||
image: chatgpt-web-frontend:1.0.0
|
||||
container_name: chatgpt-web-frontend
|
||||
ai-chat-web:
|
||||
build:
|
||||
context: ../ai-chat-web
|
||||
image: ai-chat-web:1.0.0
|
||||
container_name: ai-chat-web
|
||||
depends_on:
|
||||
- chatgpt-web-backend
|
||||
# 容器内端口:80
|
||||
- ai-chat-backend
|
||||
ports:
|
||||
- "${FRONTEND_PORT}:80"
|
||||
- "${FRONTEND_PORT:-1025}:80"
|
||||
restart: unless-stopped
|
||||
22
ai-chat-stack/configs/ai-chat-backend.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
http:
|
||||
ip: 0.0.0.0
|
||||
port: 7080
|
||||
frontend_path: "www"
|
||||
log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
chat:
|
||||
model: "kimi-k2-turbo-preview"
|
||||
max_tokens: 4096
|
||||
temperature: 1
|
||||
top_p: 1
|
||||
presence_penalty: 0
|
||||
frequency_penalty: 0
|
||||
bot_desc: "你是一个AI助手,我需要你模拟一名资深的软件工程师来回答我的问题"
|
||||
min_response_tokens: 600
|
||||
context_ttl: 1800
|
||||
context_len: 4
|
||||
dependOn:
|
||||
ai-chat-service:
|
||||
address: "ai-chat-service:50055"
|
||||
accessToken: "me256487ang1chubdpdialoud22sev1ozhoguumyqca"
|
||||
54
ai-chat-stack/configs/ai-chat-service.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
server:
|
||||
ip: 0.0.0.0
|
||||
port: 50055
|
||||
accessToken: "me256487ang1chubdpdialoud22sev1ozhoguumyqca"
|
||||
log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
chat:
|
||||
api_key: "__SET_FROM_ENV__"
|
||||
base_url: "https://api.moonshot.cn/v1"
|
||||
model: "kimi-k2-turbo-preview" # kimi-k2.5
|
||||
max_tokens: 4096
|
||||
temperature: 1
|
||||
top_p: 0.95
|
||||
presence_penalty: 0
|
||||
frequency_penalty: 0
|
||||
bot_desc: "你是一个AI助手,我需要你模拟一名资深的软件工程师来回答我的问题"
|
||||
min_response_tokens: 600
|
||||
context_ttl: 1800
|
||||
context_len: 4
|
||||
redis:
|
||||
host: "host.docker.internal"
|
||||
port: 8888
|
||||
pwd: "123456"
|
||||
mysql:
|
||||
dsn: "root:root@tcp(mysql:3306)/ai_chat?collation=utf8mb4_unicode_ci&charset=utf8mb4"
|
||||
maxLifeTime: 3600
|
||||
maxOpenConn: 10
|
||||
maxIdleConn: 10
|
||||
dependOn:
|
||||
sensitive:
|
||||
address: "sensitive-filter:50053"
|
||||
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
|
||||
keywords:
|
||||
address: "keywords-filter:50054"
|
||||
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
|
||||
tokenizer:
|
||||
address: "http://tokenizer:3002"
|
||||
vector:
|
||||
provider: "pgvector"
|
||||
threshold: 0.99
|
||||
pgvector:
|
||||
dsn: "postgres://postgres:postgres@pgvector:5432/ai_chat?sslmode=disable"
|
||||
table: "chat_record_vectors"
|
||||
dimensions: 1024
|
||||
maxLifeTime: 3600
|
||||
maxOpenConn: 10
|
||||
maxIdleConn: 10
|
||||
embedding:
|
||||
provider: "openai-compatible"
|
||||
base_url: "https://open.bigmodel.cn/api/paas/v4"
|
||||
api_key: "__SET_FROM_ENV__"
|
||||
model: "embedding-2"
|
||||
timeout: 10
|
||||
192
ai-chat-stack/configs/keywords-dict.txt
Normal file
@@ -0,0 +1,192 @@
|
||||
golang
|
||||
defer
|
||||
recover
|
||||
sync
|
||||
Protobuf
|
||||
gin
|
||||
grpc-gateway
|
||||
OpenTelemetry
|
||||
OTel
|
||||
otel
|
||||
k8s
|
||||
Kubernetes
|
||||
kubernetes
|
||||
Docker
|
||||
docker
|
||||
Istio
|
||||
istio
|
||||
Prometheus
|
||||
prometheus
|
||||
cadvisor
|
||||
cAdvisor
|
||||
Elastic
|
||||
Kibana
|
||||
Grafana
|
||||
apiserver
|
||||
CI/CD
|
||||
ci/cd
|
||||
ArgoCD
|
||||
argo
|
||||
Argo
|
||||
kaniko
|
||||
Mesh
|
||||
Volume
|
||||
volume
|
||||
promQL
|
||||
PromQL
|
||||
kafka
|
||||
ingress
|
||||
StorageClass
|
||||
VolumeClaim
|
||||
gitlab
|
||||
openflow
|
||||
dpdk
|
||||
vpp
|
||||
ovs
|
||||
spdk
|
||||
virtio
|
||||
vhost
|
||||
qemu
|
||||
vSwitch
|
||||
bridge
|
||||
hugepage
|
||||
nvme
|
||||
dpvs
|
||||
iperf3
|
||||
rfc2544
|
||||
ioengine
|
||||
PCI
|
||||
vxlan
|
||||
gre
|
||||
kni
|
||||
Kernel
|
||||
内核
|
||||
KernelThread
|
||||
内核线程
|
||||
Virtual
|
||||
memory
|
||||
虚拟内存
|
||||
内存屏障
|
||||
内存管理
|
||||
Scheduler
|
||||
调度器
|
||||
File
|
||||
文件系统
|
||||
Device
|
||||
driver
|
||||
设备驱动程序
|
||||
Syscall
|
||||
系统调用
|
||||
Process
|
||||
scheduling
|
||||
进程调度
|
||||
Page
|
||||
页表
|
||||
Swap
|
||||
交换空间
|
||||
Mount
|
||||
Inode
|
||||
挂载
|
||||
索引节点
|
||||
Block
|
||||
块设备
|
||||
Character
|
||||
字符设备
|
||||
IRQ
|
||||
Kconfig
|
||||
内核配置
|
||||
Perf
|
||||
Ftrace
|
||||
内核跟踪工具
|
||||
Valgrind
|
||||
内存调试工具
|
||||
System
|
||||
系统定时器
|
||||
DMA
|
||||
伙伴系统
|
||||
信号与槽
|
||||
Signals
|
||||
Slots
|
||||
事件处理程序
|
||||
Event
|
||||
QML
|
||||
多线程编程
|
||||
Multithreading
|
||||
Programming
|
||||
QThread
|
||||
QtQuick
|
||||
模型
|
||||
视图架构
|
||||
Model/View
|
||||
QObject
|
||||
QWidget
|
||||
QRegularExpression
|
||||
QDesktopWidget
|
||||
QNetworkAccessManager
|
||||
QTcpServer
|
||||
QTcpSocket
|
||||
QUdpSocket
|
||||
QMutex
|
||||
SQLite/MySQL
|
||||
MySQL编程
|
||||
SQLite编程
|
||||
OpenCV
|
||||
OpenGL
|
||||
Qt数据库编程
|
||||
Qt网络编程
|
||||
Linux
|
||||
tcp
|
||||
redis
|
||||
mysql
|
||||
网络
|
||||
nginx
|
||||
协程
|
||||
io_uring
|
||||
内存泄漏
|
||||
bpf
|
||||
ebpf
|
||||
skynet
|
||||
openresty
|
||||
RocksDB
|
||||
TiDB
|
||||
ceph
|
||||
etcd
|
||||
fuse
|
||||
p2p
|
||||
|
||||
http
|
||||
mqtt
|
||||
cuda
|
||||
mutex
|
||||
spinlock
|
||||
hash
|
||||
rbtree
|
||||
btree
|
||||
Makefile
|
||||
git
|
||||
wrk
|
||||
Cuda
|
||||
CUDA
|
||||
D3D
|
||||
d3d
|
||||
ffmpeg
|
||||
RTSP
|
||||
WebRTC
|
||||
PCM
|
||||
RGB
|
||||
YUv
|
||||
MP4
|
||||
FLV
|
||||
TS
|
||||
VLC
|
||||
EasylCE
|
||||
flvAnalyser
|
||||
mp4box
|
||||
audacity
|
||||
Elecard
|
||||
AAC
|
||||
h264
|
||||
SDL
|
||||
AVFormat
|
||||
AVCodec
|
||||
AVPacket
|
||||
7
ai-chat-stack/configs/keywords.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
server:
|
||||
ip: 0.0.0.0
|
||||
port: 50054
|
||||
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
|
||||
log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
13992
ai-chat-stack/configs/sensitive-dict.txt
Normal file
7
ai-chat-stack/configs/sensitive.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
server:
|
||||
ip: 0.0.0.0
|
||||
port: 50053
|
||||
accessToken: "ang1chubdev1ozhome256487d22sapguuv1ozhom"
|
||||
log:
|
||||
level: "info"
|
||||
logPath: "runtime/logs/app.log"
|
||||
@@ -5,3 +5,4 @@ Dockerfile
|
||||
.*
|
||||
*/.*
|
||||
!.env
|
||||
!.env.production
|
||||
12
ai-chat-web/.env.develop
Normal file
@@ -0,0 +1,12 @@
|
||||
# Glob API URL
|
||||
VITE_GLOB_API_URL=/api
|
||||
|
||||
VITE_APP_API_BASE_URL=http://localhost:7080/
|
||||
|
||||
# Whether long replies are supported, which may result in higher API fees
|
||||
VITE_GLOB_OPEN_LONG_REPLY=false
|
||||
|
||||
# When you want to use PWA
|
||||
VITE_GLOB_APP_PWA=false
|
||||
|
||||
VITE_USER_CENTER="http://localhost:8082?sys=ai"
|
||||
@@ -1,10 +1,8 @@
|
||||
# Glob API URL
|
||||
VITE_GLOB_API_URL=/api
|
||||
|
||||
VITE_APP_API_BASE_URL=http://127.0.0.1:7080/
|
||||
|
||||
# Whether long replies are supported, which may result in higher API fees
|
||||
VITE_GLOB_OPEN_LONG_REPLY=false
|
||||
|
||||
# When you want to use PWA
|
||||
VITE_GLOB_APP_PWA=false
|
||||
|
||||
VITE_USER_CENTER="https://user.0voice.com?sys=ai"
|
||||
41
ai-chat-web/.github/workflows/build_docker.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: build_docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [created] # 表示在创建新的 Release 时触发
|
||||
|
||||
jobs:
|
||||
build_docker:
|
||||
name: Build docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- run: |
|
||||
echo "本次构建的版本为:${GITHUB_REF_NAME} (但是这个变量目前上下文中无法获取到)"
|
||||
echo 本次构建的版本为:${{ github.ref_name }}
|
||||
env
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:${{ github.ref_name }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:latest
|
||||
47
ai-chat-web/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Setup
|
||||
run: npm i -g @antfu/ni
|
||||
|
||||
- name: Install
|
||||
run: nci
|
||||
|
||||
- name: Lint
|
||||
run: nr lint:fix
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Setup
|
||||
run: npm i -g @antfu/ni
|
||||
|
||||
- name: Install
|
||||
run: nci
|
||||
|
||||
- name: Typecheck
|
||||
run: nr type-check
|
||||
59
ai-chat-web/.gitlab-ci.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
variables:
|
||||
DEPLOY_ENV: ''
|
||||
DEPLOY_IMG: ''
|
||||
REPO: 'chatgpt-frontend'
|
||||
SERVICE_NAME: 'chatgpt-stack_chatgpt-frontend'
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "dev" && $CI_PIPELINE_SOURCE == "push"
|
||||
variables:
|
||||
DEPLOY_ENV: 'dev'
|
||||
DEPLOY_IMG: "${REPO}:${CI_COMMIT_SHORT_SHA}"
|
||||
- if: $CI_COMMIT_TAG
|
||||
variables:
|
||||
DEPLOY_ENV: 'prod'
|
||||
DEPLOY_IMG: "${REPO}:${CI_COMMIT_TAG}"
|
||||
- when: never
|
||||
stages:
|
||||
# 编译阶段
|
||||
- build
|
||||
# 部署阶段(部署到测试环境/部署到生产环境)
|
||||
- deploy
|
||||
build-job:
|
||||
stage: build
|
||||
tags:
|
||||
- builder
|
||||
before_script:
|
||||
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
|
||||
script:
|
||||
- docker build -t ${DOCKER_REGISTRY}/${DEPLOY_IMG} .
|
||||
- docker push ${DOCKER_REGISTRY}/${DEPLOY_IMG}
|
||||
|
||||
deploy-dev-job:
|
||||
only:
|
||||
variables:
|
||||
- $DEPLOY_ENV == "dev"
|
||||
stage: deploy
|
||||
tags:
|
||||
- deployer
|
||||
variables:
|
||||
CONF_RM_STR: ""
|
||||
before_script:
|
||||
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
|
||||
script:
|
||||
- docker service update ${SERVICE_NAME} --image ${DOCKER_REGISTRY}/${DEPLOY_IMG}
|
||||
|
||||
deploy-prod-job:
|
||||
only:
|
||||
variables:
|
||||
- $DEPLOY_ENV == "prod"
|
||||
stage: deploy
|
||||
tags:
|
||||
- deployer
|
||||
variables:
|
||||
CONF_RM_STR: ""
|
||||
before_script:
|
||||
- docker login -u ${DOCKER_REGISTRY_USER} -p ${DOCKER_REGISTRY_PWD} ${DOCKER_REGISTRY}
|
||||
script:
|
||||
- docker service update ${SERVICE_NAME} --image ${DOCKER_REGISTRY}/${DEPLOY_IMG}
|
||||
4
ai-chat-web/.husky/commit-msg
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no -- commitlint --edit
|
||||
4
ai-chat-web/.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
3
ai-chat-web/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "dbaeumer.vscode-eslint"]
|
||||
}
|
||||
65
ai-chat-web/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"json",
|
||||
"jsonc",
|
||||
"json5",
|
||||
"yaml",
|
||||
"yml",
|
||||
"markdown"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"antfu",
|
||||
"axios",
|
||||
"bumpp",
|
||||
"chatgpt",
|
||||
"chenzhaoyu",
|
||||
"commitlint",
|
||||
"davinci",
|
||||
"dockerhub",
|
||||
"esno",
|
||||
"GPTAPI",
|
||||
"highlightjs",
|
||||
"hljs",
|
||||
"iconify",
|
||||
"katex",
|
||||
"katexmath",
|
||||
"linkify",
|
||||
"logprobs",
|
||||
"mdhljs",
|
||||
"mila",
|
||||
"nodata",
|
||||
"OPENAI",
|
||||
"pinia",
|
||||
"Popconfirm",
|
||||
"rushstack",
|
||||
"Sider",
|
||||
"tailwindcss",
|
||||
"traptitech",
|
||||
"tsup",
|
||||
"Typecheck",
|
||||
"unplugin",
|
||||
"VITE",
|
||||
"vueuse",
|
||||
"Zhao"
|
||||
],
|
||||
"i18n-ally.enabledParsers": [
|
||||
"ts"
|
||||
],
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": true,
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
@@ -1,57 +1,3 @@
|
||||
## v2.11.0
|
||||
|
||||
`2023-04-26`
|
||||
|
||||
> [chatgpt-web-plus](https://github.com/Chanzhaoyu/chatgpt-web-plus) 新界面、完整用户管理
|
||||
|
||||
## Enhancement
|
||||
- 更新默认 `accessToken` 反代地址为 [[pengzhile](https://github.com/pengzhile)] 的 `https://ai.fakeopen.com/api/conversation` [[24min](https://github.com/Chanzhaoyu/chatgpt-web/pull/1567/files)]
|
||||
- 添加自定义 `temperature` 和 `top_p` [[quzard](https://github.com/Chanzhaoyu/chatgpt-web/pull/1260)]
|
||||
- 优化代码 [[shunyue1320](https://github.com/Chanzhaoyu/chatgpt-web/pull/1328)]
|
||||
- 优化复制代码反馈效果
|
||||
|
||||
## BugFix
|
||||
- 修复余额查询和文案 [[luckywangxi](https://github.com/Chanzhaoyu/chatgpt-web/pull/1174)][[zuoning777](https://github.com/Chanzhaoyu/chatgpt-web/pull/1296)]
|
||||
- 修复默认语言错误 [[idawnwon](https://github.com/Chanzhaoyu/chatgpt-web/pull/1352)]
|
||||
- 修复 `onRegenerate` 下问题 [[leafsummer](https://github.com/Chanzhaoyu/chatgpt-web/pull/1188)]
|
||||
|
||||
## Other
|
||||
- 引导用户触发提示词 [[RyanXinOne](https://github.com/Chanzhaoyu/chatgpt-web/pull/1183)]
|
||||
- 添加韩语翻译 [[Kamilake](https://github.com/Chanzhaoyu/chatgpt-web/pull/1372)]
|
||||
- 添加俄语翻译 [[aquaratixc](https://github.com/Chanzhaoyu/chatgpt-web/pull/1571)]
|
||||
- 优化翻译和文本检查 [[PeterDaveHello](https://github.com/Chanzhaoyu/chatgpt-web/pull/1460)]
|
||||
- 移除无用文件
|
||||
|
||||
## v2.10.9
|
||||
|
||||
`2023-04-03`
|
||||
|
||||
> 更新默认 `accessToken` 反代地址为 [[pengzhile](https://github.com/pengzhile)] 的 `https://ai.fakeopen.com/api/conversation`
|
||||
|
||||
## Enhancement
|
||||
- 添加 `socks5` 代理认证 [[yimiaoxiehou](https://github.com/Chanzhaoyu/chatgpt-web/pull/999)]
|
||||
- 添加 `socks` 代理用户名密码的配置 [[hank-cp](https://github.com/Chanzhaoyu/chatgpt-web/pull/890)]
|
||||
- 添加可选日志打印 [[zcong1993](https://github.com/Chanzhaoyu/chatgpt-web/pull/1041)]
|
||||
- 更新侧边栏按钮本地化[[simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/911)]
|
||||
- 优化代码块滚动条高度 [[Fog3211](https://github.com/Chanzhaoyu/chatgpt-web/pull/1153)]
|
||||
## BugFix
|
||||
- 修复 `PWA` 问题 [[bingo235](https://github.com/Chanzhaoyu/chatgpt-web/pull/807)]
|
||||
- 修复 `ESM` 错误 [[kidonng](https://github.com/Chanzhaoyu/chatgpt-web/pull/826)]
|
||||
- 修复反向代理开启时限流失效的问题 [[gitgitgogogo](https://github.com/Chanzhaoyu/chatgpt-web/pull/863)]
|
||||
- 修复 `docker` 构建时 `.env` 可能被忽略的问题 [[zaiMoe](https://github.com/Chanzhaoyu/chatgpt-web/pull/877)]
|
||||
- 修复导出异常错误 [[KingTwinkle](https://github.com/Chanzhaoyu/chatgpt-web/pull/938)]
|
||||
- 修复空值异常 [[vchenpeng](https://github.com/Chanzhaoyu/chatgpt-web/pull/1103)]
|
||||
- 移动端上的体验问题
|
||||
|
||||
## Other
|
||||
- `Docker` 容器名字名义 [[LOVECHEN](https://github.com/Chanzhaoyu/chatgpt-web/pull/1035)]
|
||||
- `kubernetes` 部署配置 [[CaoYunzhou](https://github.com/Chanzhaoyu/chatgpt-web/pull/1001)]
|
||||
- 感谢 [[assassinliujie](https://github.com/Chanzhaoyu/chatgpt-web/pull/962)] 和 [[puppywang](https://github.com/Chanzhaoyu/chatgpt-web/pull/1017)] 的某些贡献
|
||||
- 更新 `kubernetes/deploy.yaml` [[idawnwon](https://github.com/Chanzhaoyu/chatgpt-web/pull/1085)]
|
||||
- 文档更新 [[#yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/883)]
|
||||
- 文档更新 [[weifeng12x](https://github.com/Chanzhaoyu/chatgpt-web/pull/880)]
|
||||
- 依赖更新
|
||||
|
||||
## v2.10.8
|
||||
|
||||
`2023-03-23`
|
||||
@@ -125,7 +71,7 @@
|
||||
|
||||
`2023-03-13`
|
||||
|
||||
更新依赖,`access_token` 默认代理为 [pengzhile](https://github.com/pengzhile) 的 `https://bypass.duti.tech/api/conversation`
|
||||
更新依赖,`access_token` 默认代理为 [acheong08](https://github.com/acheong08) 的 `https://bypass.duti.tech/api/conversation`
|
||||
|
||||
## Feature
|
||||
- `Prompt` 商店在线导入可以导入两种 `recommend.json`里提到的模板 [simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/521)
|
||||
15
ai-chat-web/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM quay.io/0voice/node:lts-alpine AS frontend
|
||||
RUN npm install pnpm -g
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json pnpm-lock.yaml .npmrc ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN pnpm run build-only
|
||||
|
||||
FROM quay.io/0voice/nginx:1.25.4 AS web
|
||||
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=frontend /app/dist/ /usr/share/nginx/html/
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
64
ai-chat-web/README_0voice.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# ai-chat-web
|
||||
|
||||
## node 安装
|
||||
### windows
|
||||
1. 上[官网](https://nodejs.org/en)下载18.16.0 LTS版本
|
||||
2. 查看node 是否安装成功
|
||||
```
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
3. 安装pnpm
|
||||
```
|
||||
npm install pnpm -g
|
||||
```
|
||||
|
||||
### ubuntu
|
||||
1. 设置 apt 源,设置后可查看/etc/apt/sources.list.d/nodesource.list 文件
|
||||
```
|
||||
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||
```
|
||||
2. 安装nodejs
|
||||
```
|
||||
sudo apt-get install -y nodejs
|
||||
```
|
||||
3. 验证
|
||||
```
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
4. 安装pnpm
|
||||
```
|
||||
sudo npm install pnpm -g
|
||||
```
|
||||
|
||||
## 编译运行
|
||||
1. 依赖安装
|
||||
```
|
||||
pnpm bootstrap
|
||||
```
|
||||
2. 本地运行
|
||||
```
|
||||
pnpm dev
|
||||
```
|
||||
3. 打包发布版本
|
||||
```
|
||||
pnpm build-only
|
||||
```
|
||||
|
||||
## 提交代码的规则
|
||||
```
|
||||
* commitlint 规则是指在提交代码时要遵循的规范,常见的 commitlint 规则如下:
|
||||
|
||||
* type:用于说明 commit 的类型,例如 feat(新功能)、fix(修复 bug)、docs(文档更新)、style(样式修改)、refactor(重构代码)等。
|
||||
|
||||
* scope:用于说明 commit 影响的范围,例如组件、模块、页面等。
|
||||
|
||||
* subject:用于简短地描述 commit 的内容,建议不超过 50 个字符。
|
||||
|
||||
* body:用于详细描述 commit 的改动内容,可以分成多行。
|
||||
|
||||
* footer:用于关闭 issue 或者添加相关链接等信息。
|
||||
|
||||
* 长度限制:commit message 不应该过长,一般不超过 72 个字符。
|
||||
```
|
||||
1
ai-chat-web/config/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './proxy'
|
||||
16
ai-chat-web/config/proxy.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { ProxyOptions } from 'vite'
|
||||
|
||||
export function createViteProxy(isOpenProxy: boolean, viteEnv: ImportMetaEnv) {
|
||||
if (!isOpenProxy)
|
||||
return
|
||||
|
||||
const proxy: Record<string, string | ProxyOptions> = {
|
||||
'/api': {
|
||||
target: viteEnv.VITE_APP_API_BASE_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace('/api/', '/'),
|
||||
},
|
||||
}
|
||||
|
||||
return proxy
|
||||
}
|
||||
@@ -2,39 +2,33 @@ version: '3'
|
||||
|
||||
services:
|
||||
app:
|
||||
container_name: chatgpt-web
|
||||
image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
||||
ports:
|
||||
- 3002:3002
|
||||
environment:
|
||||
# 二选一
|
||||
OPENAI_API_KEY:
|
||||
OPENAI_API_KEY: sk-xxx
|
||||
# 二选一
|
||||
OPENAI_ACCESS_TOKEN:
|
||||
OPENAI_ACCESS_TOKEN: xxx
|
||||
# API接口地址,可选,设置 OPENAI_API_KEY 时可用
|
||||
OPENAI_API_BASE_URL:
|
||||
OPENAI_API_BASE_URL: xxx
|
||||
# API模型,可选,设置 OPENAI_API_KEY 时可用
|
||||
OPENAI_API_MODEL:
|
||||
OPENAI_API_MODEL: xxx
|
||||
# 反向代理,可选
|
||||
API_REVERSE_PROXY:
|
||||
API_REVERSE_PROXY: xxx
|
||||
# 访问权限密钥,可选
|
||||
AUTH_SECRET_KEY:
|
||||
AUTH_SECRET_KEY: xxx
|
||||
# 每小时最大请求次数,可选,默认无限
|
||||
MAX_REQUEST_PER_HOUR: 0
|
||||
# 超时,单位毫秒,可选
|
||||
TIMEOUT_MS: 60000
|
||||
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
|
||||
SOCKS_PROXY_HOST:
|
||||
SOCKS_PROXY_HOST: xxx
|
||||
# Socks代理端口,可选,和 SOCKS_PROXY_HOST 一起时生效
|
||||
SOCKS_PROXY_PORT:
|
||||
# Socks代理用户名,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
||||
SOCKS_PROXY_USERNAME:
|
||||
# Socks代理密码,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
||||
SOCKS_PROXY_PASSWORD:
|
||||
SOCKS_PROXY_PORT: xxx
|
||||
# HTTPS_PROXY 代理,可选
|
||||
HTTPS_PROXY:
|
||||
HTTPS_PROXY: http://xxx:7890
|
||||
nginx:
|
||||
container_name: nginx
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- '80:80'
|
||||
@@ -3,13 +3,6 @@ server {
|
||||
server_name localhost;
|
||||
charset utf-8;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
# 防止爬虫抓取
|
||||
if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot")
|
||||
{
|
||||
return 403;
|
||||
}
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files $uri /index.html;
|
||||
@@ -15,6 +15,6 @@ server {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://chatgpt-web-backend:7080/api/;
|
||||
proxy_pass http://ai-chat-backend:7080/api/;
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.jpg">
|
||||
<meta content="yes" name="apple-mobile-web-app-capable"/>
|
||||
<link rel="apple-touch-icon" href="/favicon.ico">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<title>ChatGPT Web</title>
|
||||
<title>零声教学AI助手(公测)</title>
|
||||
</head>
|
||||
|
||||
<body class="dark:bg-black">
|
||||
@@ -79,5 +79,14 @@
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
const hm = document.createElement('script')
|
||||
hm.src = 'https://hm.baidu.com/hm.js?29783f1f3a946661c3d41e96752536d6'
|
||||
const s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(hm, s)
|
||||
})()
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "chatgpt-web",
|
||||
"version": "2.11.0",
|
||||
"version": "2.10.8",
|
||||
"private": false,
|
||||
"description": "ChatGPT Web",
|
||||
"description": "零声教学AI助手",
|
||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||
"keywords": [
|
||||
"chatgpt-web",
|
||||
@@ -11,10 +11,10 @@
|
||||
"vue"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --mode develop",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"build-only": "vite build --mode production",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
6903
ai-chat-web/pnpm-lock.yaml
generated
Normal file
BIN
ai-chat-web/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
ai-chat-web/public/favicon.jpg
Normal file
|
After Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
BIN
ai-chat-web/public/pwa-192x192.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ai-chat-web/public/pwa-512x512.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -1,6 +1,6 @@
|
||||
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
|
||||
import { post } from '@/utils/request'
|
||||
import { useAuthStore, useSettingStore } from '@/store'
|
||||
import { useSettingStore } from '@/store'
|
||||
|
||||
export function fetchChatAPI<T = any>(
|
||||
prompt: string,
|
||||
@@ -28,25 +28,10 @@ export function fetchChatAPIProcess<T = any>(
|
||||
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
|
||||
) {
|
||||
const settingStore = useSettingStore()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
let data: Record<string, any> = {
|
||||
prompt: params.prompt,
|
||||
options: params.options,
|
||||
}
|
||||
|
||||
if (authStore.isChatGPTAPI) {
|
||||
data = {
|
||||
...data,
|
||||
systemMessage: settingStore.systemMessage,
|
||||
temperature: settingStore.temperature,
|
||||
top_p: settingStore.top_p,
|
||||
}
|
||||
}
|
||||
|
||||
return post<T>({
|
||||
url: '/chat-process',
|
||||
data,
|
||||
data: { prompt: params.prompt, options: params.options, systemMessage: settingStore.systemMessage },
|
||||
signal: params.signal,
|
||||
onDownloadProgress: params.onDownloadProgress,
|
||||
})
|
||||
@@ -64,3 +49,17 @@ export function fetchVerify<T>(token: string) {
|
||||
data: { token },
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchCode<T>(phone: string) {
|
||||
return post<T>({
|
||||
url: '/v1/sms/send/code',
|
||||
data: { phone },
|
||||
})
|
||||
}
|
||||
|
||||
export function login<T>(phone: string, code: string) {
|
||||
return post<T>({
|
||||
url: '/v1/user/login',
|
||||
data: { user_name: phone, pwd: code, type: 1 },
|
||||
})
|
||||
}
|
||||
BIN
ai-chat-web/src/assets/0voice-avatar.jpg
Normal file
|
After Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -147,7 +147,7 @@ const clearPromptTemplate = () => {
|
||||
message.success(t('common.clearSuccess'))
|
||||
}
|
||||
|
||||
const importPromptTemplate = (from = 'online') => {
|
||||
const importPromptTemplate = () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(tempPromptValue.value)
|
||||
let key = ''
|
||||
@@ -168,7 +168,7 @@ const importPromptTemplate = (from = 'online') => {
|
||||
}
|
||||
|
||||
for (const i of jsonData) {
|
||||
if (!(key in i) || !(value in i))
|
||||
if (!('key' in i) || !('value' in i))
|
||||
throw new Error(t('store.importError'))
|
||||
let safe = true
|
||||
for (const j of promptList.value) {
|
||||
@@ -191,8 +191,6 @@ const importPromptTemplate = (from = 'online') => {
|
||||
catch {
|
||||
message.error('JSON 格式错误,请检查 JSON 格式')
|
||||
}
|
||||
if (from === 'local')
|
||||
showModal.value = !showModal.value
|
||||
}
|
||||
|
||||
// 模板导出
|
||||
@@ -471,7 +469,7 @@ const dataSource = computed(() => {
|
||||
block
|
||||
type="primary"
|
||||
:disabled="inputStatus"
|
||||
@click="() => { importPromptTemplate('local') }"
|
||||
@click="() => { importPromptTemplate() }"
|
||||
>
|
||||
{{ t('common.import') }}
|
||||
</NButton>
|
||||
@@ -11,7 +11,7 @@ interface ConfigState {
|
||||
apiModel?: string
|
||||
socksProxy?: string
|
||||
httpsProxy?: string
|
||||
usage?: string
|
||||
balance?: string
|
||||
}
|
||||
|
||||
const authStore = useAuthStore()
|
||||
@@ -52,17 +52,17 @@ onMounted(() => {
|
||||
href="https://github.com/Chanzhaoyu/chatgpt-web"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub
|
||||
Github
|
||||
</a>
|
||||
,免费且基于 MIT 协议,没有任何形式的付费行为!
|
||||
</p>
|
||||
<p>
|
||||
如果你觉得此项目对你有帮助,请在 GitHub 帮我点个 Star 或者给予一点赞助,谢谢!
|
||||
如果你觉得此项目对你有帮助,请在 Github 帮我点个 Star 或者给予一点赞助,谢谢!
|
||||
</p>
|
||||
</div>
|
||||
<p>{{ $t("setting.api") }}:{{ config?.apiModel ?? '-' }}</p>
|
||||
<p v-if="isChatGPTAPI">
|
||||
{{ $t("setting.monthlyUsage") }}:{{ config?.usage ?? '-' }}
|
||||
{{ $t("setting.balance") }}:{{ config?.balance ?? '-' }}
|
||||
</p>
|
||||
<p v-if="!isChatGPTAPI">
|
||||
{{ $t("setting.reverseProxy") }}:{{ config?.reverseProxy ?? '-' }}
|
||||
46
ai-chat-web/src/components/common/Setting/Advanced.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { NButton, NInput, useMessage } from 'naive-ui'
|
||||
import { useSettingStore } from '@/store'
|
||||
import type { SettingsState } from '@/store/modules/settings/helper'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const ms = useMessage()
|
||||
|
||||
const systemMessage = ref(settingStore.systemMessage ?? '')
|
||||
|
||||
function updateSettings(options: Partial<SettingsState>) {
|
||||
settingStore.updateSetting(options)
|
||||
ms.success(t('common.success'))
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
settingStore.resetSetting()
|
||||
ms.success(t('common.success'))
|
||||
window.location.reload()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 space-y-5 min-h-[200px]">
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.role') }}</span>
|
||||
<div class="flex-1">
|
||||
<NInput v-model:value="systemMessage" placeholder="" />
|
||||
</div>
|
||||
<NButton size="tiny" text type="primary" @click="updateSettings({ systemMessage })">
|
||||
{{ $t('common.save') }}
|
||||
</NButton>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<span class="flex-shrink-0 w-[100px]"> </span>
|
||||
<NButton size="small" @click="handleReset">
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -57,8 +57,6 @@ const languageOptions: { label: string; key: Language; value: Language }[] = [
|
||||
{ label: '简体中文', key: 'zh-CN', value: 'zh-CN' },
|
||||
{ label: '繁體中文', key: 'zh-TW', value: 'zh-TW' },
|
||||
{ label: 'English', key: 'en-US', value: 'en-US' },
|
||||
{ label: '한국어', key: 'ko-KR', value: 'ko-KR' },
|
||||
{ label: 'Русский язык', key: 'ru-RU', value: 'ru-RU' },
|
||||
]
|
||||
|
||||
function updateUserInfo(options: Partial<UserInfo>) {
|
||||
@@ -1,13 +1,12 @@
|
||||
<script setup lang='ts'>
|
||||
import { computed, useAttrs } from 'vue'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import type { IconifyIcon } from '@iconify/vue'
|
||||
|
||||
interface Props {
|
||||
icon?: string | IconifyIcon
|
||||
icon?: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
defineProps<Props>()
|
||||
|
||||
const attrs = useAttrs()
|
||||
|
||||
@@ -18,5 +17,5 @@ const bindAttrs = computed<{ class: string; style: string }>(() => ({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon :icon="props.icon ?? ''" v-bind="bindAttrs" />
|
||||
<Icon :icon="icon" v-bind="bindAttrs" />
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed } from 'vue'
|
||||
import { enUS, koKR, zhCN, zhTW } from 'naive-ui'
|
||||
import { enUS, zhCN, zhTW } from 'naive-ui'
|
||||
import { useAppStore } from '@/store'
|
||||
import { setLocale } from '@/locales'
|
||||
|
||||
@@ -11,12 +11,6 @@ export function useLanguage() {
|
||||
case 'en-US':
|
||||
setLocale('en-US')
|
||||
return enUS
|
||||
case 'ru-RU':
|
||||
setLocale('ru-RU')
|
||||
return enUS
|
||||
case 'ko-KR':
|
||||
setLocale('ko-KR')
|
||||
return koKR
|
||||
case 'zh-CN':
|
||||
setLocale('zh-CN')
|
||||
return zhCN
|
||||
@@ -25,7 +19,7 @@ export function useLanguage() {
|
||||
return zhTW
|
||||
default:
|
||||
setLocale('zh-CN')
|
||||
return zhCN
|
||||
return enUS
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,10 +9,11 @@ export function useTheme() {
|
||||
const OsTheme = useOsTheme()
|
||||
|
||||
const isDark = computed(() => {
|
||||
if (appStore.theme === 'auto')
|
||||
return OsTheme.value === 'dark'
|
||||
else
|
||||
return appStore.theme === 'dark'
|
||||
return true
|
||||
// if (appStore.theme === 'auto')
|
||||
// return OsTheme.value === 'dark'
|
||||
// else
|
||||
// return appStore.theme === 'dark'
|
||||
})
|
||||
|
||||
const theme = computed(() => {
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@@ -28,8 +28,7 @@ export default {
|
||||
unauthorizedTips: 'Unauthorized, please verify first.',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: 'New Chat',
|
||||
placeholder: 'Ask me anything...(Shift + Enter = line break, "/" to trigger prompts)',
|
||||
placeholder: 'Ask me anything...(Shift + Enter = line break)',
|
||||
placeholderMobile: 'Ask me anything...',
|
||||
copy: 'Copy',
|
||||
copied: 'Copied',
|
||||
@@ -59,8 +58,6 @@ export default {
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
role: 'Role',
|
||||
temperature: 'Temperature',
|
||||
top_p: 'Top_p',
|
||||
resetUserInfo: 'Reset UserInfo',
|
||||
chatHistory: 'ChatHistory',
|
||||
theme: 'Theme',
|
||||
@@ -71,10 +68,8 @@ export default {
|
||||
socks: 'Socks',
|
||||
httpsProxy: 'HTTPS Proxy',
|
||||
balance: 'API Balance',
|
||||
monthlyUsage: 'Monthly Usage',
|
||||
},
|
||||
store: {
|
||||
siderButton: 'Prompt Store',
|
||||
local: 'Local',
|
||||
online: 'Online',
|
||||
title: 'Title',
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { App } from 'vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import enUS from './en-US'
|
||||
import koKR from './ko-KR'
|
||||
import zhCN from './zh-CN'
|
||||
import zhTW from './zh-TW'
|
||||
import ruRU from './ru-RU'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import type { Language } from '@/store/modules/app/helper'
|
||||
|
||||
@@ -18,10 +16,8 @@ const i18n = createI18n({
|
||||
allowComposition: true,
|
||||
messages: {
|
||||
'en-US': enUS,
|
||||
'ko-KR': koKR,
|
||||
'zh-CN': zhCN,
|
||||
'zh-TW': zhTW,
|
||||
'ru-RU': ruRU,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -24,12 +24,11 @@ export default {
|
||||
wrong: '好像出错了,请稍后再试。',
|
||||
success: '操作成功',
|
||||
failed: '操作失败',
|
||||
verify: '验证',
|
||||
verify: '登录',
|
||||
unauthorizedTips: '未经授权,请先进行验证。',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: '新建聊天',
|
||||
placeholder: '来说点什么吧...(Shift + Enter = 换行,"/" 触发提示词)',
|
||||
placeholder: '来说点什么吧...(Shift + Enter = 换行)',
|
||||
placeholderMobile: '来说点什么...',
|
||||
copy: '复制',
|
||||
copied: '复制成功',
|
||||
@@ -59,8 +58,6 @@ export default {
|
||||
name: '名称',
|
||||
description: '描述',
|
||||
role: '角色设定',
|
||||
temperature: 'Temperature',
|
||||
top_p: 'Top_p',
|
||||
resetUserInfo: '重置用户信息',
|
||||
chatHistory: '聊天记录',
|
||||
theme: '主题',
|
||||
@@ -71,10 +68,8 @@ export default {
|
||||
socks: 'Socks',
|
||||
httpsProxy: 'HTTPS Proxy',
|
||||
balance: 'API余额',
|
||||
monthlyUsage: '本月使用量',
|
||||
},
|
||||
store: {
|
||||
siderButton: '提示词商店',
|
||||
local: '本地',
|
||||
online: '在线',
|
||||
title: '标题',
|
||||
@@ -28,8 +28,7 @@ export default {
|
||||
unauthorizedTips: '未經授權,請先進行驗證。',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: '新增對話',
|
||||
placeholder: '來說點什麼...(Shift + Enter = 換行,"/" 觸發提示詞)',
|
||||
placeholder: '來說點什麼...(Shift + Enter = 換行)',
|
||||
placeholderMobile: '來說點什麼...',
|
||||
copy: '複製',
|
||||
copied: '複製成功',
|
||||
@@ -53,14 +52,12 @@ export default {
|
||||
setting: {
|
||||
setting: '設定',
|
||||
general: '總覽',
|
||||
advanced: '進階',
|
||||
advanced: '高級',
|
||||
config: '設定',
|
||||
avatarLink: '頭貼連結',
|
||||
name: '名稱',
|
||||
description: '描述',
|
||||
role: '角色設定',
|
||||
temperature: 'Temperature',
|
||||
top_p: 'Top_p',
|
||||
resetUserInfo: '重設使用者資訊',
|
||||
chatHistory: '紀錄',
|
||||
theme: '主題',
|
||||
@@ -70,11 +67,9 @@ export default {
|
||||
timeout: '逾時',
|
||||
socks: 'Socks',
|
||||
httpsProxy: 'HTTPS Proxy',
|
||||
balance: 'API Credit 餘額',
|
||||
monthlyUsage: '本月使用量',
|
||||
balance: 'API余額',
|
||||
},
|
||||
store: {
|
||||
siderButton: '提示詞商店',
|
||||
local: '本機',
|
||||
online: '線上',
|
||||
title: '標題',
|
||||
30
ai-chat-web/src/router/permission.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { Router } from 'vue-router'
|
||||
import { useAuthStoreWithout } from '@/store/modules/auth'
|
||||
|
||||
export function setupPageGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const authStore = useAuthStoreWithout()
|
||||
if (!authStore.session) {
|
||||
// try {
|
||||
// const data = await authStore.getSession()
|
||||
// if (String(data.auth) === 'false' && authStore.token)
|
||||
// authStore.removeToken()
|
||||
// if (to.path === '/500')
|
||||
// next({ name: 'Root' })
|
||||
// else
|
||||
// next()
|
||||
// }
|
||||
// catch (error) {
|
||||
// if (to.path !== '/500')
|
||||
// next({ name: '500' })
|
||||
// else
|
||||
// next()
|
||||
// }
|
||||
|
||||
next()
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,7 @@ const LOCAL_NAME = 'appSetting'
|
||||
|
||||
export type Theme = 'light' | 'dark' | 'auto'
|
||||
|
||||
export type Language = 'zh-CN' | 'zh-TW' | 'en-US' | 'ko-KR' | 'ru-RU'
|
||||
export type Language = 'zh-CN' | 'zh-TW' | 'en-US'
|
||||
|
||||
export interface AppState {
|
||||
siderCollapsed: boolean
|
||||
@@ -2,12 +2,7 @@ import { ss } from '@/utils/storage'
|
||||
|
||||
const LOCAL_NAME = 'promptStore'
|
||||
|
||||
export interface PromptItem {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type PromptList = PromptItem[]
|
||||
export type PromptList = []
|
||||
|
||||
export interface PromptStore {
|
||||
promptList: PromptList
|
||||
@@ -1,12 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { PromptList, PromptStore } from './helper'
|
||||
import type { PromptStore } from './helper'
|
||||
import { getLocalPromptList, setLocalPromptList } from './helper'
|
||||
|
||||
export const usePromptStore = defineStore('prompt-store', {
|
||||
state: (): PromptStore => getLocalPromptList(),
|
||||
|
||||
actions: {
|
||||
updatePromptList(promptList: PromptList) {
|
||||
updatePromptList(promptList: []) {
|
||||
this.$patch({ promptList })
|
||||
setLocalPromptList({ promptList })
|
||||
},
|
||||
@@ -4,15 +4,12 @@ const LOCAL_NAME = 'settingsStorage'
|
||||
|
||||
export interface SettingsState {
|
||||
systemMessage: string
|
||||
temperature: number
|
||||
top_p: number
|
||||
}
|
||||
|
||||
export function defaultSetting(): SettingsState {
|
||||
const currentDate = new Date().toISOString().split('T')[0]
|
||||
return {
|
||||
systemMessage: 'You are ChatGPT, a large language model trained by OpenAI. Follow the user\'s instructions carefully. Respond using markdown.',
|
||||
temperature: 0.8,
|
||||
top_p: 1,
|
||||
systemMessage: `You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.\nKnowledge cutoff: 2021-09-01\nCurrent date: ${currentDate}`,
|
||||
}
|
||||
}
|
||||
|
||||