Add test.sh
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# PostgreSQL 密碼
|
# PostgreSQL 密碼
|
||||||
DB_PASSWORD=your_secure_password_here
|
DB_PASSWORD=your_secure_password_here
|
||||||
|
|
||||||
# Chatwoot Secret Key (使用以下命令生成: openssl rand -hex 64)
|
# Papercups Secret Key (使用以下命令生成: openssl rand -hex 64)
|
||||||
CHATWOOT_SECRET_KEY=your_chatwoot_secret_key_here
|
PAPERCUPS_SECRET_KEY=your_papercups_secret_key_here
|
||||||
|
|
||||||
|
# Papercups API Token (從 Papercups Settings 取得)
|
||||||
|
PAPERCUPS_API_TOKEN=your_papercups_api_token_here
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ fastapi==0.104.1
|
|||||||
uvicorn==0.24.0
|
uvicorn==0.24.0
|
||||||
pydantic==2.5.0
|
pydantic==2.5.0
|
||||||
asyncpg==0.29.0
|
asyncpg==0.29.0
|
||||||
|
httpx==0.25.2
|
||||||
|
|||||||
268
api/server.py
268
api/server.py
@@ -2,7 +2,7 @@
|
|||||||
API 轉接層 - 偽裝 OpenAI API,將請求轉為人工回覆隊列
|
API 轉接層 - 偽裝 OpenAI API,將請求轉為人工回覆隊列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request, Header
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import asyncpg
|
import asyncpg
|
||||||
@@ -10,8 +10,10 @@ import asyncio
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
|
import httpx
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import hashlib
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@@ -27,6 +29,10 @@ DB_CONFIG = {
|
|||||||
"password": os.getenv("DB_PASSWORD", "tobiichi_password")
|
"password": os.getenv("DB_PASSWORD", "tobiichi_password")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Papercups 設定
|
||||||
|
PAPERCUPS_URL = os.getenv("PAPERCUPS_URL", "http://papercups:4000")
|
||||||
|
PAPERCUPS_API_TOKEN = os.getenv("PAPERCUPS_API_TOKEN", "")
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseModel):
|
class Message(BaseModel):
|
||||||
role: str
|
role: str
|
||||||
@@ -39,6 +45,112 @@ class ChatRequest(BaseModel):
|
|||||||
stream: Optional[bool] = False
|
stream: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
|
class PapercupsWebhook(BaseModel):
|
||||||
|
"""Papercups Webhook 資料格式"""
|
||||||
|
event: str
|
||||||
|
payload: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
|
async def get_user_name(user_id: str):
|
||||||
|
"""從資料庫獲取用戶真實姓名"""
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
row = await conn.fetchrow(
|
||||||
|
'SELECT name, email FROM "user" WHERE id = $1',
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
if row:
|
||||||
|
name = row['name'] or row['email']
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 獲取用戶名稱失敗: {e}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def get_or_create_papercups_conversation(user_id: str, chat_id: str, user_name: str = None):
|
||||||
|
"""獲取或創建 Papercups 對話(使用 chat_id 作為唯一標識)"""
|
||||||
|
if not PAPERCUPS_API_TOKEN:
|
||||||
|
print("⚠️ Papercups 未配置,跳過推送")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
headers = {"Authorization": f"Bearer {PAPERCUPS_API_TOKEN}"}
|
||||||
|
|
||||||
|
# 使用 chat_id 作為唯一標識符
|
||||||
|
identifier = chat_id if chat_id else user_id
|
||||||
|
|
||||||
|
# 如果沒有提供用戶名,嘗試從數據庫獲取
|
||||||
|
if not user_name and user_id:
|
||||||
|
user_name = await get_user_name(user_id)
|
||||||
|
|
||||||
|
# 生成顯示名稱
|
||||||
|
if user_name:
|
||||||
|
display_name = user_name
|
||||||
|
elif user_id:
|
||||||
|
display_name = f"User-{user_id[:8]}"
|
||||||
|
else:
|
||||||
|
display_name = f"Chat-{chat_id[:8]}"
|
||||||
|
|
||||||
|
# Papercups API: 創建或獲取對話
|
||||||
|
conversation_url = f"{PAPERCUPS_URL}/api/conversations"
|
||||||
|
conversation_payload = {
|
||||||
|
"customer": {
|
||||||
|
"external_id": identifier,
|
||||||
|
"name": display_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conversation_response = await client.post(conversation_url, json=conversation_payload, headers=headers)
|
||||||
|
if conversation_response.status_code in [200, 201]:
|
||||||
|
conversation_data = conversation_response.json()
|
||||||
|
papercups_conv_id = conversation_data.get("id")
|
||||||
|
print(f"✅ Papercups 對話: #{papercups_conv_id} ({display_name})")
|
||||||
|
return papercups_conv_id
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Papercups 對話創建失敗: {conversation_response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Papercups 錯誤: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def send_message_to_papercups(papercups_conv_id: str, message: str, sent_by: str = "customer"):
|
||||||
|
"""發送訊息到 Papercups 對話"""
|
||||||
|
if not PAPERCUPS_API_TOKEN or not papercups_conv_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
headers = {"Authorization": f"Bearer {PAPERCUPS_API_TOKEN}"}
|
||||||
|
msg_url = f"{PAPERCUPS_URL}/api/messages"
|
||||||
|
msg_payload = {
|
||||||
|
"conversation_id": papercups_conv_id,
|
||||||
|
"body": message,
|
||||||
|
"sent_by": sent_by
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_response = await client.post(msg_url, json=msg_payload, headers=headers)
|
||||||
|
if msg_response.status_code in [200, 201]:
|
||||||
|
print(f"✅ 訊息已發送到 Papercups 對話 #{papercups_conv_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"⚠️ 訊息發送失敗: {msg_response.status_code}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 發送訊息錯誤: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def init_db():
|
async def init_db():
|
||||||
"""初始化資料庫連接池和表格"""
|
"""初始化資料庫連接池和表格"""
|
||||||
global db_pool
|
global db_pool
|
||||||
@@ -50,18 +162,29 @@ async def init_db():
|
|||||||
CREATE TABLE IF NOT EXISTS reply_queue (
|
CREATE TABLE IF NOT EXISTS reply_queue (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
conversation_id VARCHAR(50) UNIQUE NOT NULL,
|
conversation_id VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
user_id VARCHAR(255),
|
||||||
|
chat_id VARCHAR(255),
|
||||||
user_message TEXT NOT NULL,
|
user_message TEXT NOT NULL,
|
||||||
admin_reply TEXT,
|
admin_reply TEXT,
|
||||||
status VARCHAR(20) DEFAULT 'pending',
|
status VARCHAR(20) DEFAULT 'pending',
|
||||||
created_at TIMESTAMP DEFAULT NOW(),
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
replied_at TIMESTAMP
|
replied_at TIMESTAMP,
|
||||||
|
papercups_conversation_id VARCHAR(100)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# 添加新欄位(如果不存在)
|
||||||
|
try:
|
||||||
|
await conn.execute("ALTER TABLE reply_queue ADD COLUMN IF NOT EXISTS user_id VARCHAR(255);")
|
||||||
|
await conn.execute("ALTER TABLE reply_queue ADD COLUMN IF NOT EXISTS chat_id VARCHAR(255);")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
# 建立索引
|
# 建立索引
|
||||||
await conn.execute("""
|
await conn.execute("""
|
||||||
CREATE INDEX IF NOT EXISTS idx_status ON reply_queue(status);
|
CREATE INDEX IF NOT EXISTS idx_status ON reply_queue(status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_conversation_id ON reply_queue(conversation_id);
|
CREATE INDEX IF NOT EXISTS idx_conversation_id ON reply_queue(conversation_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chat_id ON reply_queue(chat_id);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
@@ -91,12 +214,12 @@ async def list_models():
|
|||||||
"object": "list",
|
"object": "list",
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"id": "human-admin",
|
"id": "tobiichiGPT",
|
||||||
"object": "model",
|
"object": "model",
|
||||||
"created": int(time.time()),
|
"created": int(time.time()),
|
||||||
"owned_by": "tobiichi",
|
"owned_by": "tobiichi",
|
||||||
"permission": [],
|
"permission": [],
|
||||||
"root": "human-admin",
|
"root": "tobiichiGPT",
|
||||||
"parent": None
|
"parent": None
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -104,14 +227,18 @@ async def list_models():
|
|||||||
|
|
||||||
|
|
||||||
@app.post("/v1/chat/completions")
|
@app.post("/v1/chat/completions")
|
||||||
async def chat_completions(request: ChatRequest):
|
async def chat_completions(
|
||||||
|
request_data: ChatRequest,
|
||||||
|
http_request: Request,
|
||||||
|
authorization: Optional[str] = Header(None)
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
模擬 OpenAI Chat Completions API
|
模擬 OpenAI Chat Completions API
|
||||||
將用戶訊息寫入資料庫,等待管理員回覆
|
將用戶訊息寫入資料庫,等待管理員回覆(無超時限制)
|
||||||
"""
|
"""
|
||||||
# 取得最後一則用戶訊息
|
# 取得最後一則用戶訊息
|
||||||
user_message = None
|
user_message = None
|
||||||
for msg in reversed(request.messages):
|
for msg in reversed(request_data.messages):
|
||||||
if msg.role == "user":
|
if msg.role == "user":
|
||||||
user_message = msg.content
|
user_message = msg.content
|
||||||
break
|
break
|
||||||
@@ -122,46 +249,70 @@ async def chat_completions(request: ChatRequest):
|
|||||||
content={"error": "No user message found"}
|
content={"error": "No user message found"}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 生成對話 ID
|
# 嘗試從請求中提取用戶信息
|
||||||
conversation_id = str(uuid.uuid4())
|
user_id = None
|
||||||
|
chat_id = None
|
||||||
|
|
||||||
|
# 從 headers 提取信息
|
||||||
|
headers_dict = dict(http_request.headers)
|
||||||
|
user_id = headers_dict.get("x-user-id") or headers_dict.get("user-id")
|
||||||
|
chat_id = headers_dict.get("x-chat-id") or headers_dict.get("chat-id")
|
||||||
|
|
||||||
|
# 如果沒有,生成一個基於授權 token 的穩定 ID
|
||||||
|
if not user_id and authorization:
|
||||||
|
user_id = hashlib.md5(authorization.encode()).hexdigest()[:16]
|
||||||
|
|
||||||
|
# 生成消息 ID(用於追蹤單條消息)
|
||||||
|
message_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# 如果沒有 chat_id,使用 user_id
|
||||||
|
if not chat_id:
|
||||||
|
chat_id = user_id if user_id else message_id
|
||||||
|
|
||||||
|
# 獲取用戶真實姓名
|
||||||
|
user_name = await get_user_name(user_id) if user_id else None
|
||||||
|
|
||||||
|
print(f"📝 收到訊息 [user:{user_name or user_id}, chat:{chat_id}]: {user_message[:50]}...")
|
||||||
|
|
||||||
|
# 獲取或創建 Papercups 對話
|
||||||
|
papercups_conv_id = await get_or_create_papercups_conversation(user_id, chat_id, user_name)
|
||||||
|
|
||||||
# 寫入資料庫
|
# 寫入資料庫
|
||||||
async with db_pool.acquire() as conn:
|
async with db_pool.acquire() as conn:
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO reply_queue (conversation_id, user_message, status)
|
INSERT INTO reply_queue (conversation_id, user_id, chat_id, user_message, status, papercups_conversation_id)
|
||||||
VALUES ($1, $2, 'pending')
|
VALUES ($1, $2, $3, $4, 'pending', $5)
|
||||||
""",
|
""",
|
||||||
conversation_id, user_message
|
message_id, user_id, chat_id, user_message, papercups_conv_id
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"📝 收到訊息 [{conversation_id}]: {user_message[:50]}...")
|
# 推送到 Papercups
|
||||||
|
if papercups_conv_id:
|
||||||
|
await send_message_to_papercups(papercups_conv_id, user_message, "customer")
|
||||||
|
|
||||||
# 等待管理員回覆 (最多 15 分鐘)
|
# 無限等待管理員回覆
|
||||||
max_wait = 900 # 15 分鐘
|
check_interval = 2 # 每 2 秒檢查一次
|
||||||
check_interval = 3 # 每 3 秒檢查一次
|
|
||||||
waited = 0
|
|
||||||
|
|
||||||
while waited < max_wait:
|
while True:
|
||||||
await asyncio.sleep(check_interval)
|
await asyncio.sleep(check_interval)
|
||||||
waited += check_interval
|
|
||||||
|
|
||||||
async with db_pool.acquire() as conn:
|
async with db_pool.acquire() as conn:
|
||||||
row = await conn.fetchrow(
|
row = await conn.fetchrow(
|
||||||
"SELECT admin_reply, status FROM reply_queue WHERE conversation_id = $1",
|
"SELECT admin_reply, status FROM reply_queue WHERE conversation_id = $1",
|
||||||
conversation_id
|
message_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if row and row['status'] == 'replied' and row['admin_reply']:
|
if row and row['status'] == 'replied' and row['admin_reply']:
|
||||||
admin_reply = row['admin_reply']
|
admin_reply = row['admin_reply']
|
||||||
print(f"✅ 管理員已回覆 [{conversation_id}]")
|
print(f"✅ 管理員已回覆 [chat:{chat_id}]")
|
||||||
|
|
||||||
# 回傳 OpenAI 格式的回應
|
# 回傳 OpenAI 格式的回應
|
||||||
return {
|
return {
|
||||||
"id": f"chatcmpl-{conversation_id}",
|
"id": f"chatcmpl-{message_id}",
|
||||||
"object": "chat.completion",
|
"object": "chat.completion",
|
||||||
"created": int(time.time()),
|
"created": int(time.time()),
|
||||||
"model": request.model,
|
"model": request_data.model,
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"index": 0,
|
"index": 0,
|
||||||
@@ -178,25 +329,58 @@ async def chat_completions(request: ChatRequest):
|
|||||||
"total_tokens": len(user_message) + len(admin_reply)
|
"total_tokens": len(user_message) + len(admin_reply)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# 超時回應
|
|
||||||
print(f"⏰ 等待超時 [{conversation_id}]")
|
@app.post("/papercups/webhook")
|
||||||
return {
|
async def papercups_webhook(request: Request):
|
||||||
"id": f"chatcmpl-{conversation_id}",
|
"""接收 Papercups Webhook 回調"""
|
||||||
"object": "chat.completion",
|
try:
|
||||||
"created": int(time.time()),
|
data = await request.json()
|
||||||
"model": request.model,
|
event = data.get("event")
|
||||||
"choices": [
|
|
||||||
{
|
# 只處理訊息建立事件
|
||||||
"index": 0,
|
if event != "message:created":
|
||||||
"message": {
|
return {"status": "ignored", "event": event}
|
||||||
"role": "assistant",
|
|
||||||
"content": "抱歉,管理員目前忙碌中,請稍後再試。"
|
payload = data.get("payload", {})
|
||||||
},
|
message = payload.get("message", {})
|
||||||
"finish_reason": "stop"
|
|
||||||
}
|
# 檢查是否為管理員回覆(user type = "user")
|
||||||
]
|
user = message.get("user")
|
||||||
}
|
if not user:
|
||||||
|
return {"status": "ignored", "reason": "not_agent_reply"}
|
||||||
|
|
||||||
|
# 取得訊息內容和對話 ID
|
||||||
|
content = message.get("body")
|
||||||
|
conversation_id = message.get("conversation_id")
|
||||||
|
|
||||||
|
if not content or not conversation_id:
|
||||||
|
return {"status": "error", "message": "Missing content or conversation_id"}
|
||||||
|
|
||||||
|
# 更新資料庫中所有該對話的待處理消息(最新的一條)
|
||||||
|
async with db_pool.acquire() as conn:
|
||||||
|
result = await conn.execute(
|
||||||
|
"""
|
||||||
|
UPDATE reply_queue
|
||||||
|
SET admin_reply = $1, status = 'replied', replied_at = NOW()
|
||||||
|
WHERE papercups_conversation_id = $2
|
||||||
|
AND status = 'pending'
|
||||||
|
AND id = (
|
||||||
|
SELECT id FROM reply_queue
|
||||||
|
WHERE papercups_conversation_id = $2 AND status = 'pending'
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
content, conversation_id
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"✅ Papercups 回覆已處理: 對話 #{conversation_id}")
|
||||||
|
return {"status": "success", "conversation_id": conversation_id}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Webhook 處理錯誤: {e}")
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ services:
|
|||||||
- DB_NAME=tobiichiGPT
|
- DB_NAME=tobiichiGPT
|
||||||
- DB_USER=tobiichi3227
|
- DB_USER=tobiichi3227
|
||||||
- DB_PASSWORD=${DB_PASSWORD}
|
- DB_PASSWORD=${DB_PASSWORD}
|
||||||
|
- PAPERCUPS_URL=http://papercups:4000
|
||||||
|
- PAPERCUPS_API_TOKEN=${PAPERCUPS_API_TOKEN}
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/data/External/tobiichiGPT/api_data:/app
|
- /mnt/data/External/tobiichiGPT/api_data:/app
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
@@ -65,53 +67,25 @@ services:
|
|||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
# Redis - Chatwoot 依賴
|
# Papercups - 管理員對話介面
|
||||||
redis:
|
papercups:
|
||||||
image: redis:7-alpine
|
image: papercups/papercups:latest
|
||||||
container_name: tobiichiGPT-redis
|
container_name: tobiichiGPT-papercups
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
networks:
|
|
||||||
- tobiichiGPT-network
|
|
||||||
|
|
||||||
# Chatwoot - 管理員對話介面
|
|
||||||
chatwoot:
|
|
||||||
image: chatwoot/chatwoot:latest
|
|
||||||
container_name: tobiichiGPT-chatwoot
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "13000:3000"
|
- "14000:4000"
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- DATABASE_URL=postgresql://tobiichi3227:${DB_PASSWORD}@postgres:5432/papercups
|
||||||
- REDIS_URL=redis://redis:6379
|
- SECRET_KEY_BASE=${PAPERCUPS_SECRET_KEY}
|
||||||
- POSTGRES_HOST=postgres
|
- BACKEND_URL=http://localhost:14000
|
||||||
- POSTGRES_PORT=5432
|
- MIX_ENV=prod
|
||||||
- POSTGRES_DATABASE=chatwoot
|
|
||||||
- POSTGRES_USERNAME=tobiichi3227
|
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
||||||
- SECRET_KEY_BASE=${CHATWOOT_SECRET_KEY}
|
|
||||||
- INSTALLATION_NAME=TobiichiGPT
|
|
||||||
- FORCE_SSL=false
|
|
||||||
- RAILS_LOG_TO_STDOUT=true
|
|
||||||
volumes:
|
|
||||||
- chatwoot-data:/app/storage
|
|
||||||
networks:
|
networks:
|
||||||
- tobiichiGPT-network
|
- tobiichiGPT-network
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
postgres:
|
||||||
- redis
|
condition: service_healthy
|
||||||
command: >
|
|
||||||
sh -c "
|
|
||||||
bundle exec rails db:chatwoot_prepare &&
|
|
||||||
bundle exec rails s -b 0.0.0.0 -p 3000
|
|
||||||
"
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
tobiichiGPT-network:
|
tobiichiGPT-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
name: tobiichiGPT-network # 固定網路名稱,讓 proxy stack 可以連接
|
name: tobiichiGPT-network # 固定網路名稱,讓 proxy stack 可以連接
|
||||||
|
|
||||||
volumes:
|
|
||||||
redis-data:
|
|
||||||
chatwoot-data:
|
|
||||||
|
|||||||
6
test.sh
6
test.sh
@@ -64,8 +64,8 @@ fi
|
|||||||
# ==================== 測試 3: API Models 端點 ====================
|
# ==================== 測試 3: API Models 端點 ====================
|
||||||
print_test "測試 API Models 端點"
|
print_test "測試 API Models 端點"
|
||||||
MODELS_RESPONSE=$(curl -s http://localhost:18000/v1/models)
|
MODELS_RESPONSE=$(curl -s http://localhost:18000/v1/models)
|
||||||
if echo "$MODELS_RESPONSE" | grep -q "human-admin"; then
|
if echo "$MODELS_RESPONSE" | grep -q "tobiichiGPT"; then
|
||||||
print_success "Models 端點返回 human-admin 模型"
|
print_success "Models 端點返回 tobiichiGPT 模型"
|
||||||
else
|
else
|
||||||
print_error "Models 端點測試失敗"
|
print_error "Models 端點測試失敗"
|
||||||
fi
|
fi
|
||||||
@@ -104,7 +104,7 @@ echo " → 發送測試訊息到 API..."
|
|||||||
curl -X POST http://localhost:18000/v1/chat/completions \
|
curl -X POST http://localhost:18000/v1/chat/completions \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"model": "human-admin",
|
"model": "tobiichiGPT",
|
||||||
"messages": [
|
"messages": [
|
||||||
{"role": "user", "content": "自動化測試訊息"}
|
{"role": "user", "content": "自動化測試訊息"}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user