docker compose

This commit is contained in:
1iaan
2026-04-04 16:30:54 +08:00
parent c1a895258f
commit e993eb6c5c
25 changed files with 241 additions and 95 deletions

12
.env Normal file
View File

@@ -0,0 +1,12 @@
MOONSHOT_API_KEY=sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs
# backend 配置
OPENAI_BASE_URL=https://api.moonshot.cn/v1
OPENAI_MODEL=kimi-k2.5
OPENAI_TEMPERATURE=100
OPENAI_PRESENCE_PENALTY=0
OPENAI_FREQUENCY_PENALTY=0
TOKENIZER_BASE_URL=http://tokenizer:3002
# frontend 对外端口
FRONTEND_PORT=3002

4
.gitignore vendored
View File

@@ -1 +1,3 @@
docs
docs
.workspace.codex

View File

@@ -1,5 +1,12 @@
```shell
protoc \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
./proto/xxx.proto
```
```shell
# chatgpt-web-backend
go mod tidy
@@ -12,12 +19,68 @@ go run cmd/main.go \
--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
# chatgpt-web-frontend
HUSKY=0 pnpm bootstrap
pnpm dev
docker build -t chatgpt-web-frontend:1.0.0 .
# tokenizer
docker build -t tokenizer:1.0.0 .
# keywords-filter
docker build -t keywords-filter:1.0.0 .
```
## backend
访问 tokenizer
```go
url := fmt.Sprintf("http://127.0.0.1:5000/tokenizer/%s", model)
// 改成
tokenizerBaseURL := os.Getenv("TOKENIZER_BASE_URL")
if tokenizerBaseURL == "" {
tokenizerBaseURL = "http://tokenizer:3002"
}
url := fmt.Sprintf("%s/tokenizer/%s", tokenizerBaseURL, model)
// 直接访问 tokenizer
```
## frontend
访问 backend
```conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_http_version 1.1;
proxy_set_header Host $host;
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/;
}
}
```

View File

@@ -4,8 +4,9 @@ import (
predis "ai-chat-service/pkg/db/redis"
"context"
"encoding/json"
"github.com/redis/go-redis/v9"
"time"
"github.com/redis/go-redis/v9"
)
type redisCache struct {

View File

@@ -15,13 +15,14 @@ import (
"ai-chat-service/proto"
"flag"
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"net/http"
"net"
)

View File

@@ -11,9 +11,10 @@ import (
keywords_proto "ai-chat-service/services/keywords-filter/proto"
"ai-chat-service/services/tokenizer"
"context"
"time"
"github.com/google/uuid"
"github.com/sashabaranov/go-openai"
"time"
)
const ChatPrimedTokens = 2

View File

@@ -11,13 +11,14 @@ import (
"ai-chat-service/services/tokenizer"
"context"
"encoding/json"
"github.com/golang/protobuf/jsonpb"
"github.com/google/uuid"
"github.com/sashabaranov/go-openai"
"io"
"strconv"
"strings"
"time"
"github.com/golang/protobuf/jsonpb"
"github.com/google/uuid"
"github.com/sashabaranov/go-openai"
)
type chatService struct {

View File

@@ -10,7 +10,7 @@ chat:
# openai key
api_key: "i0jey84SdkFdw5u43780yjr3h7se8nth0yi295nr94ksDngKprEh"
# openai 接口地址
base_url: "http://localhost:8084/v1"
base_url: "https://api.moonshot.cn/v1"
# 使用的训练模型
model: "gpt-3.5-turbo"
# 单次请求的上下文总长度,包括:请求消息+响应消息
@@ -37,11 +37,11 @@ chat:
# 上下文消息条数
context_len: 4
redis:
host: "192.168.239.161"
port: 6379
host: "127.0.0.1"
port: 8888
pwd: "123456"
mysql:
dsn: "root:123456@tcp(192.168.239.161:3306)/ai_chat?collation=utf8mb4_unicode_ci&charset=utf8mb4"
dsn: "root:root@tcp(127.0.0.1:3306)/ai_chat?collation=utf8mb4_unicode_ci&charset=utf8mb4"
maxLifeTime: 3600
maxOpenConn: 10
maxIdleConn: 10

View File

@@ -1,6 +1,8 @@
module ai-chat-service
go 1.20
go 1.21
toolchain go1.24.2
require (
github.com/go-sql-driver/mysql v1.8.1

View File

@@ -1347,7 +1347,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
@@ -1388,6 +1390,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@@ -1423,6 +1426,7 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -1623,6 +1627,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
@@ -1667,6 +1672,7 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -1689,6 +1695,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=

View File

@@ -1,8 +1,9 @@
package config
import (
"github.com/spf13/viper"
"log"
"github.com/spf13/viper"
)
type Config struct {

View File

@@ -4,8 +4,9 @@ import (
"ai-chat-service/pkg/config"
"context"
"fmt"
redis "github.com/redis/go-redis/v9"
"sync"
redis "github.com/redis/go-redis/v9"
)
type RedisPool interface {

View File

@@ -2,10 +2,11 @@ package services
import (
"context"
"google.golang.org/grpc/metadata"
)
func AppendBearerTokenToContext(ctx context.Context, accessToken string) context.Context {
md := metadata.Pairs("Authorization", "Bearer "+accessToken)
return metadata.NewOutgoingContext(ctx, md)
}
}

View File

@@ -1,11 +1,24 @@
FROM chenzhaoyu94/chatgpt-web:v2.10.9 as frontend
FROM arvintian/chatgpt-web-base:v1
COPY --from=frontend /app/public /app/public
ADD dist/server /app/server
EXPOSE 7080
CMD ["/app/server"]
FROM golang:1.23-alpine AS builder
WORKDIR /src
ENV GOPROXY=https://goproxy.cn,direct
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags="-w -s" -o /out/server ./cmd/main.go
FROM alpine:3.20
WORKDIR /app
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /out/server /app/server
COPY ./www /app/www
EXPOSE 7080
CMD ["/app/server"]

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"os"
"github.com/sashabaranov/go-openai"
)
@@ -16,7 +17,13 @@ type tokenInfo struct {
}
func GetTokenCount(message openai.ChatCompletionMessage, model string) (int, error) {
url := fmt.Sprintf("http://127.0.0.1:5000/tokenizer/%s", model)
// url := fmt.Sprintf("http://127.0.0.1:5000/tokenizer/%s", model)
tokenizerBaseURL := os.Getenv("TOKENIZER_BASE_URL")
if tokenizerBaseURL == "" {
tokenizerBaseURL = "http://tokenizer:3002"
}
url := fmt.Sprintf("%s/tokenizer/%s", tokenizerBaseURL, model)
info := tokenInfo{}
if err := postJSON(url, &message, &info); err != nil {
return 0, err

View File

@@ -1,56 +1,8 @@
# build front-end
FROM node:lts-alpine AS frontend
RUN npm install pnpm -g
WORKDIR /app
COPY ./package.json /app
COPY ./pnpm-lock.yaml /app
RUN pnpm install
COPY . /app
RUN pnpm run build
# build backend
FROM node:lts-alpine as backend
RUN npm install pnpm -g
WORKDIR /app
COPY /service/package.json /app
COPY /service/pnpm-lock.yaml /app
RUN pnpm install
COPY /service /app
RUN pnpm build
# service
FROM node:lts-alpine
RUN npm install pnpm -g
WORKDIR /app
COPY /service/package.json /app
COPY /service/pnpm-lock.yaml /app
RUN pnpm install --production && rm -rf /root/.npm /root/.pnpm-store /usr/local/share/.cache /tmp/*
COPY /service /app
COPY --from=frontend /app/dist /app/public
COPY --from=backend /app/build /app/build
EXPOSE 3002
CMD ["pnpm", "run", "prod"]
FROM nginx:1.27-alpine
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY ./dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,20 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_http_version 1.1;
proxy_set_header Host $host;
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/;
}
}

View File

@@ -1,12 +1,13 @@
<script setup lang='ts'>
import { computed, useAttrs } from 'vue'
import { Icon } from '@iconify/vue'
import type { IconifyIcon } from '@iconify/vue'
interface Props {
icon?: string
icon?: string | IconifyIcon
}
defineProps<Props>()
const props = defineProps<Props>()
const attrs = useAttrs()
@@ -17,5 +18,5 @@ const bindAttrs = computed<{ class: string; style: string }>(() => ({
</script>
<template>
<Icon :icon="icon" v-bind="bindAttrs" />
<Icon :icon="props.icon ?? ''" v-bind="bindAttrs" />
</template>

View File

@@ -2,7 +2,12 @@ import { ss } from '@/utils/storage'
const LOCAL_NAME = 'promptStore'
export type PromptList = []
export interface PromptItem {
key: string
value: string
}
export type PromptList = PromptItem[]
export interface PromptStore {
promptList: PromptList

View File

@@ -1,12 +1,12 @@
import { defineStore } from 'pinia'
import type { PromptStore } from './helper'
import type { PromptList, PromptStore } from './helper'
import { getLocalPromptList, setLocalPromptList } from './helper'
export const usePromptStore = defineStore('prompt-store', {
state: (): PromptStore => getLocalPromptList(),
actions: {
updatePromptList(promptList: []) {
updatePromptList(promptList: PromptList) {
this.$patch({ promptList })
setLocalPromptList({ promptList })
},

View File

@@ -15,6 +15,7 @@ import { useBasicLayout } from '@/hooks/useBasicLayout'
import { useChatStore, usePromptStore } from '@/store'
import { fetchChatAPIProcess } from '@/api'
import { t } from '@/locales'
import type { PromptItem } from '@/store/modules/prompt/helper'
let controller = new AbortController()
@@ -44,7 +45,7 @@ const inputRef = ref<Ref | null>(null)
const promptStore = usePromptStore()
// 使用storeToRefs保证store修改后联想部分能够重新渲染
const { promptList: promptTemplate } = storeToRefs<any>(promptStore)
const { promptList: promptTemplate } = storeToRefs(promptStore) as { promptList: Ref<PromptItem[]> }
// 未知原因刷新页面loading 状态不会重置,手动重置
dataSources.value.forEach((item, index) => {

View File

@@ -33,7 +33,7 @@ export default defineConfig((env) => {
plugins: setupPlugins(viteEnv),
server: {
host: '0.0.0.0',
port: 1025,
port: 3002,
open: false,
proxy: {
'/api': {

View File

@@ -1,9 +1,46 @@
version: '3.3'
services:
tokenizer:
image: tokenizer:1.0.0
container_name: tokenizer
# 容器内端口3002
restart: unless-stopped
keywords-filter:
image: keywords-filter:1.0.0
container_name: keywords-filter
# 容器内端口50053
restart: unless-stopped
chatgpt-web-backend:
image: chatgpt-web-backend:1.0.0
container_name: chatgpt-web-backend
# 容器内端口7080
environment:
TOKENIZER_BASE_URL: ${TOKENIZER_BASE_URL}
command:
- /app/server
- --frontend-path
- www
- --openapi-key
- ${MOONSHOT_API_KEY}
- --openapi-base-url
- ${OPENAI_BASE_URL}
- --openai-model
- ${OPENAI_MODEL}
- --openai-temperature
- "${OPENAI_TEMPERATURE}"
- --openai-presence-penalty
- "${OPENAI_PRESENCE_PENALTY}"
- --openai-frequency-penalty
- "${OPENAI_FREQUENCY_PENALTY}"
restart: unless-stopped
chatgpt-web-frontend:
image: chatgpt-web-frontend:1.0.0
container_name: chatgpt-web-frontend
depends_on:
- chatgpt-web-backend
# 容器内端口80
ports:
- "5000:3002"
restart: unless-stopped
- "${FRONTEND_PORT}:80"
restart: unless-stopped

View File

@@ -0,0 +1,6 @@
# keywords-filter
## 镜像构建
```
docker build -t keywords-filter:1.0.0 .
```

11
run.sh Normal file
View File

@@ -0,0 +1,11 @@
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
pnpm dev