msyql 容器化, aichatservice 容器化
This commit is contained in:
8
.env
8
.env
@@ -1,13 +1,5 @@
|
|||||||
MOONSHOT_API_KEY=sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs
|
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 对外端口
|
||||||
FRONTEND_PORT=1025
|
FRONTEND_PORT=1025
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ docker run -d --name chatgpt-web-backend \
|
|||||||
# ai-chat-backend
|
# ai-chat-backend
|
||||||
docker build -t ai-chat-backend:1.0.0 .
|
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
|
# chatgpt-web-frontend
|
||||||
HUSKY=0 pnpm bootstrap
|
HUSKY=0 pnpm bootstrap
|
||||||
pnpm dev
|
pnpm dev
|
||||||
|
|||||||
22
ai-chat-backend/docker.config.yaml
Normal file
22
ai-chat-backend/docker.config.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.5"
|
||||||
|
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"
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"github.com/spf13/viper"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -45,7 +41,6 @@ type Config struct {
|
|||||||
var conf *Config
|
var conf *Config
|
||||||
|
|
||||||
func InitConfig(filePath string, typ ...string) {
|
func InitConfig(filePath string, typ ...string) {
|
||||||
loadProjectDotEnv(filePath)
|
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigFile(filePath)
|
v.SetConfigFile(filePath)
|
||||||
if len(typ) > 0 {
|
if len(typ) > 0 {
|
||||||
@@ -61,7 +56,6 @@ func InitConfig(filePath string, typ ...string) {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
normalizeConfig(conf)
|
normalizeConfig(conf)
|
||||||
overrideConfigFromEnv(conf)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,88 +92,3 @@ func normalizeConfig(conf *Config) {
|
|||||||
conf.DependOn.AiChatService.Address = "localhost:50055"
|
conf.DependOn.AiChatService.Address = "localhost:50055"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func overrideConfigFromEnv(conf *Config) {
|
|
||||||
overrideString(&conf.Http.IP, os.Getenv("SERVER_HOST"))
|
|
||||||
overrideInt(&conf.Http.Port, os.Getenv("SERVER_PORT"))
|
|
||||||
overrideString(&conf.BasicAuthUser, os.Getenv("BASIC_AUTH_USER"))
|
|
||||||
overrideString(&conf.BasicAuthPassword, os.Getenv("BASIC_AUTH_PASSWORD"))
|
|
||||||
overrideString(&conf.FrontendPath, os.Getenv("FRONTEND_PATH"))
|
|
||||||
overrideString(&conf.Chat.Model, os.Getenv("OPENAI_MODEL"))
|
|
||||||
overrideInt(&conf.Chat.MaxTokens, os.Getenv("OPENAI_MAX_TOKENS"))
|
|
||||||
overrideScaledFloat32(&conf.Chat.Temperature, os.Getenv("OPENAI_TEMPERATURE"))
|
|
||||||
overrideScaledFloat32(&conf.Chat.PresencePenalty, os.Getenv("OPENAI_PRESENCE_PENALTY"))
|
|
||||||
overrideScaledFloat32(&conf.Chat.FrequencyPenalty, os.Getenv("OPENAI_FREQUENCY_PENALTY"))
|
|
||||||
overrideInt(&conf.Chat.MinResponseTokens, os.Getenv("CHAT_MIN_RESPONSE_TOKENS"))
|
|
||||||
overrideString(&conf.DependOn.AiChatService.Address, firstNonEmpty(
|
|
||||||
os.Getenv("AI_CHAT_SERVICE_ADDRESS"),
|
|
||||||
os.Getenv("AI_CHAT_SERVICE_ADDR"),
|
|
||||||
))
|
|
||||||
overrideString(&conf.DependOn.AiChatService.AccessToken, os.Getenv("AI_CHAT_SERVICE_ACCESS_TOKEN"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadProjectDotEnv(configFilePath string) {
|
|
||||||
projectRoot := filepath.Dir(filepath.Dir(configFilePath))
|
|
||||||
envPath := filepath.Join(projectRoot, ".env")
|
|
||||||
file, err := os.Open(envPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || strings.HasPrefix(line, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key, value, ok := strings.Cut(line, "=")
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key = strings.TrimSpace(key)
|
|
||||||
value = strings.TrimSpace(value)
|
|
||||||
if key == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, exists := os.LookupEnv(key); exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_ = os.Setenv(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func firstNonEmpty(values ...string) string {
|
|
||||||
for _, value := range values {
|
|
||||||
if strings.TrimSpace(value) != "" {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideString(target *string, value string) {
|
|
||||||
if strings.TrimSpace(value) != "" {
|
|
||||||
*target = strings.TrimSpace(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideInt(target *int, value string) {
|
|
||||||
value = strings.TrimSpace(value)
|
|
||||||
if value == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parsed, err := strconv.Atoi(value); err == nil {
|
|
||||||
*target = parsed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideScaledFloat32(target *float32, value string) {
|
|
||||||
value = strings.TrimSpace(value)
|
|
||||||
if value == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if parsed, err := strconv.ParseFloat(value, 32); err == nil {
|
|
||||||
*target = float32(parsed / 100.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
# 编译阶段
|
FROM alpine:3.18
|
||||||
FROM quay.io/0voice/golang:1.20 as stage0
|
|
||||||
RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct
|
|
||||||
ADD ./ /src/ai-chat-service
|
|
||||||
WORKDIR /src/ai-chat-service
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ai-chat-service ./chat-server
|
|
||||||
|
|
||||||
FROM quay.io/0voice/alpine:3.18 as stage1
|
|
||||||
ADD ./grpc_health_probe-linux-amd64 /usr/bin/grpc_health_probe
|
ADD ./grpc_health_probe-linux-amd64 /usr/bin/grpc_health_probe
|
||||||
RUN chmod +x /usr/bin/grpc_health_probe
|
RUN chmod +x /usr/bin/grpc_health_probe
|
||||||
MAINTAINER nick
|
LABEL maintainer="nick"
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
ADD ./dev.config.yaml /app/config.yaml
|
COPY ./ai-chat-service-bin ./ai-chat-service
|
||||||
COPY --from=stage0 /src/ai-chat-service/ai-chat-service ./
|
COPY ./docker.config.yaml /app/config.yaml
|
||||||
# 指定入口程序
|
# 指定入口程序
|
||||||
ENTRYPOINT ["./ai-chat-service"]
|
ENTRYPOINT ["./ai-chat-service"]
|
||||||
# 指定容器的启动命令或者入口程序的参数
|
# 指定容器的启动命令或者入口程序的参数
|
||||||
|
|||||||
BIN
ai-chat-service/ai-chat-service-bin
Executable file
BIN
ai-chat-service/ai-chat-service-bin
Executable file
Binary file not shown.
54
ai-chat-service/docker.config.yaml
Normal file
54
ai-chat-service/docker.config.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: "sk-8NMdsGbDAMpWdd6hrKHepr1tNVXTy2QppKAqJkoJcHd6TYLs"
|
||||||
|
base_url: "https://api.moonshot.cn/v1"
|
||||||
|
model: "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: "d51b903546814cc9981d3649a4a899a3.NQOtz3ocRtQwimh9"
|
||||||
|
model: "embedding-2"
|
||||||
|
timeout: 10
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@@ -102,7 +98,6 @@ type Config struct {
|
|||||||
var conf *Config
|
var conf *Config
|
||||||
|
|
||||||
func InitConfig(filePath string, typ ...string) {
|
func InitConfig(filePath string, typ ...string) {
|
||||||
loadProjectDotEnv(filePath)
|
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigFile(filePath)
|
v.SetConfigFile(filePath)
|
||||||
if len(typ) > 0 {
|
if len(typ) > 0 {
|
||||||
@@ -171,83 +166,4 @@ func normalizeConfig(conf *Config) {
|
|||||||
if conf.Embedding.Timeout == 0 {
|
if conf.Embedding.Timeout == 0 {
|
||||||
conf.Embedding.Timeout = 10
|
conf.Embedding.Timeout = 10
|
||||||
}
|
}
|
||||||
overrideChatFromEnv(conf)
|
|
||||||
overrideEmbeddingFromEnv(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideChatFromEnv(conf *Config) {
|
|
||||||
if value := os.Getenv("AI_CHAT_OPENAI_BASE_URL"); value != "" {
|
|
||||||
conf.Chat.BaseUrl = value
|
|
||||||
} else if value := os.Getenv("OPENAI_BASE_URL"); value != "" {
|
|
||||||
conf.Chat.BaseUrl = value
|
|
||||||
}
|
|
||||||
|
|
||||||
if value := os.Getenv("AI_CHAT_OPENAI_MODEL"); value != "" {
|
|
||||||
conf.Chat.Model = value
|
|
||||||
} else if value := os.Getenv("OPENAI_MODEL"); value != "" {
|
|
||||||
conf.Chat.Model = value
|
|
||||||
}
|
|
||||||
|
|
||||||
if value := os.Getenv("AI_CHAT_OPENAI_API_KEY"); value != "" {
|
|
||||||
conf.Chat.ApiKey = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if value := os.Getenv("OPENAI_API_KEY"); value != "" {
|
|
||||||
conf.Chat.ApiKey = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if value := os.Getenv("MOONSHOT_API_KEY"); value != "" {
|
|
||||||
conf.Chat.ApiKey = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func overrideEmbeddingFromEnv(conf *Config) {
|
|
||||||
if value := os.Getenv("AI_CHAT_EMBEDDING_BASE_URL"); value != "" {
|
|
||||||
conf.Embedding.BaseUrl = value
|
|
||||||
}
|
|
||||||
if value := os.Getenv("AI_CHAT_EMBEDDING_MODEL"); value != "" {
|
|
||||||
conf.Embedding.Model = value
|
|
||||||
}
|
|
||||||
if value := os.Getenv("AI_CHAT_EMBEDDING_API_KEY"); value != "" {
|
|
||||||
conf.Embedding.ApiKey = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if value := os.Getenv("ZAI_API_KEY"); value != "" {
|
|
||||||
conf.Embedding.ApiKey = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadProjectDotEnv(configFilePath string) {
|
|
||||||
projectRoot := filepath.Dir(filepath.Dir(configFilePath))
|
|
||||||
loadDotEnvFile(filepath.Join(projectRoot, ".env"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDotEnvFile(path string) {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || strings.HasPrefix(line, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key, value, ok := strings.Cut(line, "=")
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key = strings.TrimSpace(key)
|
|
||||||
value = strings.TrimSpace(value)
|
|
||||||
value = strings.Trim(value, `"'`)
|
|
||||||
if key == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, exists := os.LookupEnv(key); exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_ = os.Setenv(key, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,23 @@
|
|||||||
services:
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: ai-chat-mysql
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
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
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
pgvector:
|
pgvector:
|
||||||
image: pgvector/pgvector:pg16
|
image: pgvector/pgvector:pg16
|
||||||
container_name: ai-chat-pgvector
|
container_name: ai-chat-pgvector
|
||||||
@@ -49,24 +68,40 @@ services:
|
|||||||
- "50054:50054"
|
- "50054:50054"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
ai-chat-service:
|
||||||
|
build:
|
||||||
|
context: ./ai-chat-service
|
||||||
|
image: ai-chat-service:1.0.0
|
||||||
|
container_name: ai-chat-service
|
||||||
|
volumes:
|
||||||
|
- /home/lian/share/aichat/ai-chat-service/docker.config.yaml:/app/config.yaml:ro
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
ports:
|
||||||
|
- "50055:50055"
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
- tokenizer
|
||||||
|
- sensitive-filter
|
||||||
|
- keywords-filter
|
||||||
|
- pgvector
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "grpc_health_probe", "-addr=:50055"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
chatgpt-web-backend:
|
chatgpt-web-backend:
|
||||||
build:
|
build:
|
||||||
context: ./ai-chat-backend
|
context: ./ai-chat-backend
|
||||||
image: ai-chat-backend:1.0.0
|
image: ai-chat-backend:1.0.0
|
||||||
container_name: chatgpt-web-backend
|
container_name: chatgpt-web-backend
|
||||||
# 容器内端口:7080
|
# 容器内端口:7080
|
||||||
environment:
|
volumes:
|
||||||
SERVER_HOST: 0.0.0.0
|
- /home/lian/share/aichat/ai-chat-backend/docker.config.yaml:/app/config.yaml:ro
|
||||||
SERVER_PORT: 7080
|
depends_on:
|
||||||
FRONTEND_PATH: www
|
- ai-chat-service
|
||||||
OPENAI_MODEL: ${OPENAI_MODEL}
|
|
||||||
OPENAI_TEMPERATURE: "${OPENAI_TEMPERATURE}"
|
|
||||||
OPENAI_PRESENCE_PENALTY: "${OPENAI_PRESENCE_PENALTY}"
|
|
||||||
OPENAI_FREQUENCY_PENALTY: "${OPENAI_FREQUENCY_PENALTY}"
|
|
||||||
AI_CHAT_SERVICE_ADDRESS: ${AI_CHAT_SERVICE_ADDRESS:-host.docker.internal:50055}
|
|
||||||
AI_CHAT_SERVICE_ACCESS_TOKEN: ${AI_CHAT_SERVICE_ACCESS_TOKEN:-me256487ang1chubdpdialoud22sev1ozhoguumyqca}
|
|
||||||
extra_hosts:
|
|
||||||
- "host.docker.internal:host-gateway"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
chatgpt-web-frontend:
|
chatgpt-web-frontend:
|
||||||
|
|||||||
Reference in New Issue
Block a user