Update chatwoot docker

This commit is contained in:
ChenKaiLiuG
2025-12-21 16:49:44 +08:00
parent 13a6b1ed0e
commit ccb0202579
5 changed files with 152 additions and 141 deletions

View File

@@ -1,5 +1,4 @@
fastapi==0.104.1
uvicorn==0.24.0
pydantic==2.5.0
psycopg2-binary==2.9.9
asyncpg==0.29.0

View File

@@ -2,7 +2,8 @@
API 轉接層 - 偽裝 OpenAI API將請求轉為人工回覆隊列
"""
from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel
import asyncpg
import asyncio
@@ -74,134 +75,130 @@ async def startup():
async def shutdown():
if db_pool:
await db_pool.close()
print("👋 資料庫連接已關閉")
@app.get("/")
async def root():
return {
"status": "TobiichiGPT API Running",
"database": "Connected" if db_pool else "Disconnected"
}
"""根路徑"""
return {"status": "ok", "service": "TobiichiGPT API"}
@app.get("/v1/models")
async def list_models():
"""模擬 OpenAI 模型列表"""
"""模擬 OpenAI 的 /v1/models 端點"""
return {
"object": "list",
"data": [{
"id": "human-admin",
"object": "model",
"created": int(time.time()),
"owned_by": "tobiichi"
}]
"data": [
{
"id": "human-admin",
"object": "model",
"created": int(time.time()),
"owned_by": "tobiichi",
"permission": [],
"root": "human-admin",
"parent": None
}
]
}
@app.post("/v1/chat/completions")
async def chat_completions(request: ChatRequest):
"""接收用戶訊息,等待管理員回覆"""
# 生成對話 ID
conv_id = str(uuid.uuid4())[:12]
# 提取用戶訊息
user_message = ""
"""
模擬 OpenAI Chat Completions API
將用戶訊息寫入資料庫,等待管理員回覆
"""
# 取得最後一則用戶訊息
user_message = None
for msg in reversed(request.messages):
if msg.role == "user":
user_message = msg.content
break
if not user_message:
return {
"id": f"chatcmpl-{conv_id}",
"object": "chat.completion",
"created": int(time.time()),
"model": request.model,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "無法識別您的訊息,請重新輸入。"
},
"finish_reason": "stop"
}]
}
return JSONResponse(
status_code=400,
content={"error": "No user message found"}
)
# 生成對話 ID
conversation_id = str(uuid.uuid4())
# 寫入資料庫
async with db_pool.acquire() as conn:
await conn.execute("""
await conn.execute(
"""
INSERT INTO reply_queue (conversation_id, user_message, status)
VALUES ($1, $2, 'pending')
""", conv_id, user_message)
""",
conversation_id, user_message
)
print(f"[新對話 {conv_id}] {user_message[:50]}...")
print(f"📝 收到訊息 [{conversation_id}]: {user_message[:50]}...")
# 等待管理員回覆最多 15 分鐘
timeout = 900
start_time = time.time()
reply_content = None
# 等待管理員回覆 (最多 15 分鐘)
max_wait = 900 # 15 分鐘
check_interval = 3 # 每 3 秒檢查一次
waited = 0
while time.time() - start_time < timeout:
while waited < max_wait:
await asyncio.sleep(check_interval)
waited += check_interval
async with db_pool.acquire() as conn:
row = await conn.fetchrow("""
SELECT admin_reply, status
FROM reply_queue
WHERE conversation_id = $1
""", conv_id)
row = await conn.fetchrow(
"SELECT admin_reply, status FROM reply_queue WHERE conversation_id = $1",
conversation_id
)
if row and row['status'] == 'replied' and row['admin_reply']:
reply_content = row['admin_reply']
print(f"[已回覆 {conv_id}] {reply_content[:50]}...")
break
await asyncio.sleep(3) # 每 3 秒檢查一次
# 如果超時
if not reply_content:
reply_content = "抱歉,管理員暫時無法回覆,請稍後再試。"
async with db_pool.acquire() as conn:
await conn.execute("""
UPDATE reply_queue
SET status = 'timeout'
WHERE conversation_id = $1
""", conv_id)
admin_reply = row['admin_reply']
print(f"✅ 管理員已回覆 [{conversation_id}]")
# 回傳 OpenAI 格式的回應
return {
"id": f"chatcmpl-{conversation_id}",
"object": "chat.completion",
"created": int(time.time()),
"model": request.model,
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": admin_reply
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": len(user_message),
"completion_tokens": len(admin_reply),
"total_tokens": len(user_message) + len(admin_reply)
}
}
# 超時回應
print(f"⏰ 等待超時 [{conversation_id}]")
return {
"id": f"chatcmpl-{conv_id}",
"id": f"chatcmpl-{conversation_id}",
"object": "chat.completion",
"created": int(time.time()),
"model": request.model,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": reply_content
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": len(user_message),
"completion_tokens": len(reply_content) if reply_content else 0,
"total_tokens": len(user_message) + (len(reply_content) if reply_content else 0)
}
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "抱歉,管理員目前忙碌中,請稍後再試。"
},
"finish_reason": "stop"
}
]
}
@app.get("/health")
async def health():
"""健康檢查"""
db_ok = db_pool is not None
return {"status": "healthy" if db_ok else "unhealthy", "database": db_ok}
if __name__ == "__main__":
import uvicorn
print("=" * 60)
print("🚀 TobiichiGPT API 啟動中...")
print("=" * 60)
print(f"📊 資料庫: {DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}")
print(f"🌐 API 端點: http://0.0.0.0:8000/v1")
print("=" * 60)
uvicorn.run(app, host="0.0.0.0", port=8000)