feat: 添加数字人服务及相关部署文件

This commit is contained in:
2026-04-22 16:26:07 +08:00
parent 69d948de88
commit abd4c3ce77
5 changed files with 624 additions and 228 deletions

View File

@@ -1,258 +1,176 @@
"""
数字人对接中转服务
-----------------
作为客户端与 Dify LLM 平台之间的中间转发层,处理请求路由、
认证转发及 Redis 缓存,提升响应效率并保护后端 API Key。
环境变量:
DIFY_URL - Dify 服务地址,如 https://atwdify.antiwearvalve.com/v1
DIFY_API_KEY - Dify API 密钥,如 app-PDkSHiHZsZIluoZF7N2OlNZM
REDIS_HOST - Redis 主机地址,默认 localhost
REDIS_PORT - Redis 端口,默认 6379
"""
import os
import json import json
import hashlib
import logging
from contextlib import asynccontextmanager
from typing import Any, Dict, Optional
import httpx
import redis import redis
from redis.exceptions import ConnectionError as RedisConnectionError import uuid
from fastapi import FastAPI, HTTPException, Request import requests
from fastapi.responses import JSONResponse from fastapi.responses import StreamingResponse
from fastapi import FastAPI, Query, Request
from pydantic import BaseModel
from typing import Optional, List
# ────────────────────────────────────────────────────────────── app = FastAPI()
# 日志配置 # 连接到Redis服务器(默认端口是6379)
# ────────────────────────────────────────────────────────────── redis_instance = redis.Redis(host='localhost', port=6379, db=0)
logging.basicConfig( mock_time_created = 20230512084843
level=logging.INFO, mock_id1 = "131424542124"
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s", mock_id2 = "1231414141541252525"
) mock_user = "abc-123"
logger = logging.getLogger("digital-human") dify_url = "http://<your dify ip>/v1/chat-messages"
dify_api_key = 'Bearer <your dify api key>'
# ──────────────────────────────────────────────────────────────
# 应用配置(从环境变量读取)
# ──────────────────────────────────────────────────────────────
DIFY_URL: str = os.getenv("DIFY_URL", "")
DIFY_API_KEY: str = os.getenv("DIFY_API_KEY", "")
REDIS_HOST: str = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
# 验证必填配置
if not DIFY_URL or not DIFY_API_KEY:
logger.warning(
"DIFY_URL 或 DIFY_API_KEY 未配置,"
"部分功能可能不可用。请通过环境变量设置。"
)
# ──────────────────────────────────────────────────────────────
# Redis 客户端(懒加载)
# ──────────────────────────────────────────────────────────────
_redis_client: Optional[redis.Redis] = None
def get_redis() -> redis.Redis: class Message(BaseModel):
"""获取 Redis 客户端实例(延迟初始化)。""" content: str
global _redis_client
if _redis_client is None:
_redis_client = redis.Redis(
host=REDIS_HOST,
port=REDIS_PORT,
decode_responses=True,
socket_connect_timeout=5,
)
return _redis_client
def get_cache_key(payload: Dict[str, Any]) -> str: class Resquest_Body(BaseModel):
"""根据请求体生成 Redis 缓存键。""" messages: List[Message] = []
raw = json.dumps(payload, sort_keys=True) app_id: str
return f"dh:cache:{hashlib.md5(raw.encode()).hexdigest()}" user: str
session_id: str
is_stream: bool
# ────────────────────────────────────────────────────────────── # 对接dify,非流式传输
# FastAPI 应用生命周期 def request_dify_api(question, request, conversation_id):
# ────────────────────────────────────────────────────────────── # 目标URL
@asynccontextmanager url = dify_url
async def lifespan(app: FastAPI): headers_json = {
"""应用启动 / 关闭钩子。""" 'Content-Type': 'application/json',
logger.info("数字人对接服务启动中...") 'Authorization': dify_api_key
logger.info(f" Dify URL : {DIFY_URL}") }
logger.info(f" Redis : {REDIS_HOST}:{REDIS_PORT}") # 请求的数据
data = {
# 尝试连接 Redis(仅记录,不阻塞启动) "inputs": {},
try: "query": question,
get_redis().ping() "response_mode": "blocking",
logger.info("Redis 连接成功") "user": mock_user
except RedisConnectionError as e:
logger.warning(f"Redis 连接失败(服务将继续运行): {e}")
yield
logger.info("数字人对接服务已关闭")
app = FastAPI(
title="数字人对接中转服务",
version="1.0.0",
description="对接 Dify LLM 平台的数字人应用中转服务",
lifespan=lifespan,
)
# ──────────────────────────────────────────────────────────────
# 健康检查
# ──────────────────────────────────────────────────────────────
@app.get("/")
async def root():
"""服务健康检查端点。"""
return {
"status": "ok",
"service": "digital-human-relay",
"version": "1.0.0",
} }
# 发送POST请求
response = requests.post(url, data=json.dumps(data), headers=headers_json)
data = json.loads(response)
return data["answer"]
@app.get("/health")
async def health_check():
"""详细健康检查(包含 Redis 状态)。"""
redis_ok = False
try:
get_redis().ping()
redis_ok = True
except Exception:
pass
return { # 对接dify,流式传输
"status": "healthy" if redis_ok else "degraded", async def generate_streaming_messages(question, request, session_id, conversation_id):
"redis": "connected" if redis_ok else "disconnected", url = dify_url
"dify_configured": bool(DIFY_URL and DIFY_API_KEY), headers_json = {
'Content-Type': 'application/json',
'Authorization': dify_api_key
} }
# 请求的数据
# ────────────────────────────────────────────────────────────── request_data = {
# Dify 响应格式转 OpenAI 兼容格式 "inputs": {},
# ────────────────────────────────────────────────────────────── "query": question,
def dify_to_openai(dify_resp: Dict[str, Any]) -> Dict[str, Any]: "conversation_id": conversation_id,
""" "response_mode": "streaming",
将 Dify /chat-messages 的响应转换为 OpenAI /chat/completions 兼容格式。 "user": mock_user
Dify 响应示例: { answer: "...", conversation_id: "...", task_id: "..." }
OpenAI 目标格式: { id, model, choices: [{ message: { role: "assistant", content: "..." } }] }
"""
return {
"id": f"chatcmpl-{dify_resp.get('task_id', 'unknown')}",
"object": "chat.completion",
"created": 0,
"model": "dify",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": dify_resp.get("answer", ""),
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0,
},
} }
r = requests.post(url, json=request_data, headers=headers_json, verify=False, stream=True)
if r.status_code != 200:
raise Exception(r.text)
data = ""
# ────────────────────────────────────────────────────────────── # 在这里,我们使用了 iter_lines 方法来逐行读取响应体
# 核心转发接口 for line in r.iter_lines():
# ────────────────────────────────────────────────────────────── if await request.is_disconnected():
@app.post("/v1/chat/completions") # 客户端已主动断开,中止流式输出,避免不必要的问题,打印报错信息方便定位
async def chat_completions(request: Request): print("# 客户端已主动断开,中止流式输出")
"""
接收 OpenAI 兼容格式请求,转换为 Dify /chat-messages 格式后转发。
OpenAI 格式: { model, messages: [{role, content}], stream }
Dify 格式: { query, user, response_mode, inputs }
"""
body = await request.json()
# ── 格式转换:OpenAI -> Dify ──────────────────────────────
# 提取用户最新消息
user_content = ""
for msg in reversed(body.get("messages", [])):
if msg.get("role") == "user":
user_content = msg.get("content", "")
break break
if not user_content: # 将每一行解码为字符串
raise HTTPException(status_code=400, detail="未找到用户消息") line = line.decode('utf-8').strip()
# 生成或复用 user 标识(用于 Dify 会话上下文) # 接下来我们要处理三种不同的情况:
dify_user = body.get("user", "anonymous") # 1. 如果当前行是空行,则表明前一个数据块已接收完毕(即前文提到的,通过两个换行符结束数据块传输),我们可以对该数据块进行反序列化,并打印出对应的 content 内容;
response_mode = "blocking" if not body.get("stream", False) else "streaming" # 2. 如果当前行为非空行,且以 data: 开头,则表明这是一个数据块传输的开始,我们去除 data: 前缀后,首先判断是否是结束符 [DONE],如果不是,将数据内容保存到 data 变量;
# 3. 如果当前行为非空行,但不以 data: 开头,则表明当前行仍然归属上一个正在传输的数据块,我们将当前行的内容追加到 data 变量尾部;
# 构建 Dify 请求体 if len(line) == 0:
dify_payload = { if data:
"query": user_content,
"user": dify_user,
"response_mode": response_mode,
"inputs": body.get("inputs", {}),
}
# 若应用要求 inputs 字段但为空,补上空对象
if "inputs" not in dify_payload or dify_payload["inputs"] is None:
dify_payload["inputs"] = {}
# ── 检查 Redis 缓存(基于 OpenAI 原始请求体)──────────────
cache_key = get_cache_key(body)
try: try:
cached = get_redis().get(cache_key) chunk = json.loads(data)
if cached: except:
logger.info(f"缓存命中: {cache_key}") continue
return dify_to_openai(json.loads(cached))
except RedisConnectionError:
logger.warning("Redis 不可用,跳过缓存读取")
# ── 转发到 Dify ───────────────────────────────────────── if chunk["event"] != "message":
continue
# 如果redis里未缓存该session_id,则使用reids关联session_id和conversation_id,保证后续实现多轮对话
if not redis_instance.exists(session_id):
conversiation_id = str(chunk["conversation_id"])
redis_instance.set(session_id, conversiation_id)
# 设置redis中key(session_id)的超时时间,一天换算86400s
redis_instance.expire(session_id, 86400)
content = chunk["answer"]
new_data = {
"id": mock_id2,
"created": mock_time_created,
"choices": [{
"message": {
"content": content
}
}]
}
yield f"data: {json.dumps(new_data, ensure_ascii=False)}\n\n"
data = "" # 重置 data
elif line.startswith("data: "):
data = line.lstrip("data: ")
# 当数据块内容为 [DONE] 时,则表明所有数据块已发送完毕,可断开网络连接
if data == "[DONE]":
break
else:
# 仍然在追加内容时,为其添加一个换行符
data = data + "\n" + line
# 发送结束标记
yield "data: [DONE]\n\n"
@app.post("/digital-human")
async def digital_human(body: Resquest_Body, request: Request):
question = body.messages[0].content
session_id = body.session_id
conversation_id = ""
# 如果redis_instance里有缓存session_id,则说明为多轮对话,获取对应的dify中的多轮对话关键参数conversation_id
if redis_instance.exists(session_id):
# decode()用于将bytes转换为str,因为redis_instance存储的是bytes类型的数据。
conversation_id = redis_instance.get(session_id).decode("utf-8")
# 选择流式传输
if body.is_stream:
return StreamingResponse(
generate_streaming_messages(question, request, session_id, conversation_id),
media_type="text/event-stream",
headers={ headers={
"Authorization": f"Bearer {DIFY_API_KEY}", "Cache-Control": "no-cache",
"Content-Type": "application/json", "Connection": "keep-alive",
} }
dify_endpoint = f"{DIFY_URL.rstrip('/')}/chat-messages"
try:
async with httpx.AsyncClient(timeout=120.0) as client:
resp = await client.post(dify_endpoint, json=dify_payload, headers=headers)
resp.raise_for_status()
result = resp.json()
except httpx.HTTPStatusError as e:
logger.error(f"Dify 返回错误 {e.response.status_code}: {e.response.text}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"Dify 请求失败: {e.response.text}",
) )
except httpx.RequestError as e:
logger.error(f"无法连接 Dify 服务: {e}")
raise HTTPException(status_code=502, detail=f"无法连接 Dify 服务: {e}")
# ── 写入缓存(存 Dify 原始格式)─────────────────────────── # 选择非流式传输
try: answer = request_dify_api(question, request, conversation_id)
get_redis().setex(cache_key, 300, json.dumps(result)) choices = [{
except RedisConnectionError: "index": 0,
pass "message": {
"content": answer
# ── 响应转 OpenAI 兼容格式 ───────────────────────────────── }
return dify_to_openai(result) }]
return {
"id": mock_id1,
"created": mock_time_created,
"choices": choices
}
# ────────────────────────────────────────────────────────────── # 启动应用
# 错误处理器 if __name__ == "__main__":
# ────────────────────────────────────────────────────────────── import uvicorn
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception): uvicorn.run(app, host="0.0.0.0", port=8000)
logger.exception(f"未处理的异常: {exc}")
return JSONResponse(
status_code=500,
content={"error": "Internal Server Error", "detail": str(exc)},
)

258
digital-human2.py Normal file
View File

@@ -0,0 +1,258 @@
"""
数字人对接中转服务
-----------------
作为客户端与 Dify LLM 平台之间的中间转发层,处理请求路由、
认证转发及 Redis 缓存,提升响应效率并保护后端 API Key。
环境变量:
DIFY_URL - Dify 服务地址,如 https://atwdify.antiwearvalve.com/v1
DIFY_API_KEY - Dify API 密钥,如 app-PDkSHiHZsZIluoZF7N2OlNZM
REDIS_HOST - Redis 主机地址,默认 localhost
REDIS_PORT - Redis 端口,默认 6379
"""
import os
import json
import hashlib
import logging
from contextlib import asynccontextmanager
from typing import Any, Dict, Optional
import httpx
import redis
from redis.exceptions import ConnectionError as RedisConnectionError
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
# ──────────────────────────────────────────────────────────────
# 日志配置
# ──────────────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
)
logger = logging.getLogger("digital-human")
# ──────────────────────────────────────────────────────────────
# 应用配置(从环境变量读取)
# ──────────────────────────────────────────────────────────────
DIFY_URL: str = os.getenv("DIFY_URL", "")
DIFY_API_KEY: str = os.getenv("DIFY_API_KEY", "")
REDIS_HOST: str = os.getenv("REDIS_HOST", "localhost")
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
# 验证必填配置
if not DIFY_URL or not DIFY_API_KEY:
logger.warning(
"DIFY_URL 或 DIFY_API_KEY 未配置,"
"部分功能可能不可用。请通过环境变量设置。"
)
# ──────────────────────────────────────────────────────────────
# Redis 客户端(懒加载)
# ──────────────────────────────────────────────────────────────
_redis_client: Optional[redis.Redis] = None
def get_redis() -> redis.Redis:
"""获取 Redis 客户端实例(延迟初始化)。"""
global _redis_client
if _redis_client is None:
_redis_client = redis.Redis(
host=REDIS_HOST,
port=REDIS_PORT,
decode_responses=True,
socket_connect_timeout=5,
)
return _redis_client
def get_cache_key(payload: Dict[str, Any]) -> str:
"""根据请求体生成 Redis 缓存键。"""
raw = json.dumps(payload, sort_keys=True)
return f"dh:cache:{hashlib.md5(raw.encode()).hexdigest()}"
# ──────────────────────────────────────────────────────────────
# FastAPI 应用生命周期
# ──────────────────────────────────────────────────────────────
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用启动 / 关闭钩子。"""
logger.info("数字人对接服务启动中...")
logger.info(f" Dify URL : {DIFY_URL}")
logger.info(f" Redis : {REDIS_HOST}:{REDIS_PORT}")
# 尝试连接 Redis(仅记录,不阻塞启动)
try:
get_redis().ping()
logger.info("Redis 连接成功")
except RedisConnectionError as e:
logger.warning(f"Redis 连接失败(服务将继续运行): {e}")
yield
logger.info("数字人对接服务已关闭")
app = FastAPI(
title="数字人对接中转服务",
version="1.0.0",
description="对接 Dify LLM 平台的数字人应用中转服务",
lifespan=lifespan,
)
# ──────────────────────────────────────────────────────────────
# 健康检查
# ──────────────────────────────────────────────────────────────
@app.get("/")
async def root():
"""服务健康检查端点。"""
return {
"status": "ok",
"service": "digital-human-relay",
"version": "1.0.0",
}
@app.get("/health")
async def health_check():
"""详细健康检查(包含 Redis 状态)。"""
redis_ok = False
try:
get_redis().ping()
redis_ok = True
except Exception:
pass
return {
"status": "healthy" if redis_ok else "degraded",
"redis": "connected" if redis_ok else "disconnected",
"dify_configured": bool(DIFY_URL and DIFY_API_KEY),
}
# ──────────────────────────────────────────────────────────────
# Dify 响应格式转 OpenAI 兼容格式
# ──────────────────────────────────────────────────────────────
def dify_to_openai(dify_resp: Dict[str, Any]) -> Dict[str, Any]:
"""
将 Dify /chat-messages 的响应转换为 OpenAI /chat/completions 兼容格式。
Dify 响应示例: { answer: "...", conversation_id: "...", task_id: "..." }
OpenAI 目标格式: { id, model, choices: [{ message: { role: "assistant", content: "..." } }] }
"""
return {
"id": f"chatcmpl-{dify_resp.get('task_id', 'unknown')}",
"object": "chat.completion",
"created": 0,
"model": "dify",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": dify_resp.get("answer", ""),
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0,
},
}
# ──────────────────────────────────────────────────────────────
# 核心转发接口
# ──────────────────────────────────────────────────────────────
@app.post("/v1/chat/completions")
async def chat_completions(request: Request):
"""
接收 OpenAI 兼容格式请求,转换为 Dify /chat-messages 格式后转发。
OpenAI 格式: { model, messages: [{role, content}], stream }
Dify 格式: { query, user, response_mode, inputs }
"""
body = await request.json()
# ── 格式转换:OpenAI -> Dify ──────────────────────────────
# 提取用户最新消息
user_content = ""
for msg in reversed(body.get("messages", [])):
if msg.get("role") == "user":
user_content = msg.get("content", "")
break
if not user_content:
raise HTTPException(status_code=400, detail="未找到用户消息")
# 生成或复用 user 标识(用于 Dify 会话上下文)
dify_user = body.get("user", "anonymous")
response_mode = "blocking" if not body.get("stream", False) else "streaming"
# 构建 Dify 请求体
dify_payload = {
"query": user_content,
"user": dify_user,
"response_mode": response_mode,
"inputs": body.get("inputs", {}),
}
# 若应用要求 inputs 字段但为空,补上空对象
if "inputs" not in dify_payload or dify_payload["inputs"] is None:
dify_payload["inputs"] = {}
# ── 检查 Redis 缓存(基于 OpenAI 原始请求体)──────────────
cache_key = get_cache_key(body)
try:
cached = get_redis().get(cache_key)
if cached:
logger.info(f"缓存命中: {cache_key}")
return dify_to_openai(json.loads(cached))
except RedisConnectionError:
logger.warning("Redis 不可用,跳过缓存读取")
# ── 转发到 Dify ─────────────────────────────────────────
headers = {
"Authorization": f"Bearer {DIFY_API_KEY}",
"Content-Type": "application/json",
}
dify_endpoint = f"{DIFY_URL.rstrip('/')}/chat-messages"
try:
async with httpx.AsyncClient(timeout=120.0) as client:
resp = await client.post(dify_endpoint, json=dify_payload, headers=headers)
resp.raise_for_status()
result = resp.json()
except httpx.HTTPStatusError as e:
logger.error(f"Dify 返回错误 {e.response.status_code}: {e.response.text}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"Dify 请求失败: {e.response.text}",
)
except httpx.RequestError as e:
logger.error(f"无法连接 Dify 服务: {e}")
raise HTTPException(status_code=502, detail=f"无法连接 Dify 服务: {e}")
# ── 写入缓存(存 Dify 原始格式)───────────────────────────
try:
get_redis().setex(cache_key, 300, json.dumps(result))
except RedisConnectionError:
pass
# ── 响应转 OpenAI 兼容格式 ─────────────────────────────────
return dify_to_openai(result)
# ──────────────────────────────────────────────────────────────
# 错误处理器
# ──────────────────────────────────────────────────────────────
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.exception(f"未处理的异常: {exc}")
return JSONResponse(
status_code=500,
content={"error": "Internal Server Error", "detail": str(exc)},
)

176
downloads/digital-human.py Normal file
View File

@@ -0,0 +1,176 @@
import json
import redis
import uuid
import requests
from fastapi.responses import StreamingResponse
from fastapi import FastAPI, Query, Request
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI()
# 连接到Redis服务器(默认端口是6379)
redis_instance = redis.Redis(host='localhost', port=6379, db=0)
mock_time_created = 20230512084843
mock_id1 = "131424542124"
mock_id2 = "1231414141541252525"
mock_user = "abc-123"
dify_url = "http://<your dify ip>/v1/chat-messages"
dify_api_key = 'Bearer <your dify api key>'
class Message(BaseModel):
content: str
class Resquest_Body(BaseModel):
messages: List[Message] = []
app_id: str
user: str
session_id: str
is_stream: bool
# 对接dify,非流式传输
def request_dify_api(question, request, conversation_id):
# 目标URL
url = dify_url
headers_json = {
'Content-Type': 'application/json',
'Authorization': dify_api_key
}
# 请求的数据
data = {
"inputs": {},
"query": question,
"response_mode": "blocking",
"user": mock_user
}
# 发送POST请求
response = requests.post(url, data=json.dumps(data), headers=headers_json)
data = json.loads(response)
return data["answer"]
# 对接dify,流式传输
async def generate_streaming_messages(question, request, session_id, conversation_id):
url = dify_url
headers_json = {
'Content-Type': 'application/json',
'Authorization': dify_api_key
}
# 请求的数据
request_data = {
"inputs": {},
"query": question,
"conversation_id": conversation_id,
"response_mode": "streaming",
"user": mock_user
}
r = requests.post(url, json=request_data, headers=headers_json, verify=False, stream=True)
if r.status_code != 200:
raise Exception(r.text)
data = ""
# 在这里,我们使用了 iter_lines 方法来逐行读取响应体
for line in r.iter_lines():
if await request.is_disconnected():
# 客户端已主动断开,中止流式输出,避免不必要的问题,打印报错信息方便定位
print("# 客户端已主动断开,中止流式输出")
break
# 将每一行解码为字符串
line = line.decode('utf-8').strip()
# 接下来我们要处理三种不同的情况:
# 1. 如果当前行是空行,则表明前一个数据块已接收完毕(即前文提到的,通过两个换行符结束数据块传输),我们可以对该数据块进行反序列化,并打印出对应的 content 内容;
# 2. 如果当前行为非空行,且以 data: 开头,则表明这是一个数据块传输的开始,我们去除 data: 前缀后,首先判断是否是结束符 [DONE],如果不是,将数据内容保存到 data 变量;
# 3. 如果当前行为非空行,但不以 data: 开头,则表明当前行仍然归属上一个正在传输的数据块,我们将当前行的内容追加到 data 变量尾部;
if len(line) == 0:
if data:
try:
chunk = json.loads(data)
except:
continue
if chunk["event"] != "message":
continue
# 如果redis里未缓存该session_id,则使用reids关联session_id和conversation_id,保证后续实现多轮对话
if not redis_instance.exists(session_id):
conversiation_id = str(chunk["conversation_id"])
redis_instance.set(session_id, conversiation_id)
# 设置redis中key(session_id)的超时时间,一天换算86400s
redis_instance.expire(session_id, 86400)
content = chunk["answer"]
new_data = {
"id": mock_id2,
"created": mock_time_created,
"choices": [{
"message": {
"content": content
}
}]
}
yield f"data: {json.dumps(new_data, ensure_ascii=False)}\n\n"
data = "" # 重置 data
elif line.startswith("data: "):
data = line.lstrip("data: ")
# 当数据块内容为 [DONE] 时,则表明所有数据块已发送完毕,可断开网络连接
if data == "[DONE]":
break
else:
# 仍然在追加内容时,为其添加一个换行符
data = data + "\n" + line
# 发送结束标记
yield "data: [DONE]\n\n"
@app.post("/digital-human")
async def digital_human(body: Resquest_Body, request: Request):
question = body.messages[0].content
session_id = body.session_id
conversation_id = ""
# 如果redis_instance里有缓存session_id,则说明为多轮对话,获取对应的dify中的多轮对话关键参数conversation_id
if redis_instance.exists(session_id):
# decode()用于将bytes转换为str,因为redis_instance存储的是bytes类型的数据。
conversation_id = redis_instance.get(session_id).decode("utf-8")
# 选择流式传输
if body.is_stream:
return StreamingResponse(
generate_streaming_messages(question, request, session_id, conversation_id),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
}
)
# 选择非流式传输
answer = request_dify_api(question, request, conversation_id)
choices = [{
"index": 0,
"message": {
"content": answer
}
}]
return {
"id": mock_id1,
"created": mock_time_created,
"choices": choices
}
# 启动应用
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Digital Human Service
After=network.target
[Service]
Type=simple
ExecStart=nohup python3 /digital-human.py &
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# 回到根路径
cd /
rm -rf /digital-human.rar
rm -rf /digital-human.py
rm -rf /etc/systemd/system/digital-human.service
# apt安装rar和redis-server
sudo apt update
sudo apt install rar redis-server
#启动redis-server,并设置开机自启动 redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server
# pip安装所需的包
pip install redis fastapi uvicorn -i https://pypi.tuna.tsinghua.edu.cn/simple
# 从obs桶获取转发服务代码
wget https://documentation-samples.obs.cn-north-4.myhuaweicloud.com/solution-as-code-publicbucket/solution-as-code-moudle/building-a-dify-llm-application-development-platform/human/digital-human.rar
rar x digital-human.rar
# 替换dify对应的url和api key
sed -i "s/<your dify ip>/$1/g" digital-human.py
sed -i "s/<your dify api key>/$2/g" digital-human.py
#启动对接数字人的python服务,并设置开机自启动
mv digital-human.service /etc/systemd/system/digital-human.service
sudo systemctl daemon-reload
sudo systemctl enable digital-human.service
sudo systemctl start digital-human.service