Add api server code and workflow

This commit is contained in:
Xin Wang
2025-04-29 15:03:02 +08:00
commit a1a4bceb9a
13 changed files with 16662 additions and 0 deletions
View File
+3
View File
@@ -0,0 +1,3 @@
[pytest]
markers =
integration: marks tests that need access to external services
+8
View File
@@ -0,0 +1,8 @@
fastapi>=0.104.0
uvicorn>=0.24.0
pydantic>=2.4.2
python-dotenv>=1.0.0
httpx>=0.25.0
pytest>=7.4.0
pytest-asyncio>=0.21.0
pytest-cov>=4.1.0
+493
View File
@@ -0,0 +1,493 @@
from fastapi import APIRouter, HTTPException
from ..schemas.models import ProcessRequest_chat, ProcessResponse_chat, ProcessRequest_get, ProcessResponse_get, ProcessRequest_set, ProcessResponse_set, ProcessResponse_delete_session, ProcessRequest_delete_session
import httpx
from datetime import datetime
import os
from dotenv import load_dotenv
from urllib import parse as parse
import json
load_dotenv()
router = APIRouter()
ANALYSIS_SERVICE_URL = os.getenv("ANALYSIS_SERVICE_URL", None)
if ANALYSIS_SERVICE_URL is None:
raise ValueError("ANALYSIS_SERVICE_URL environment variable not set")
ANALYSIS_AUTH_TOKEN = os.getenv("ANALYSIS_AUTH_TOKEN", None)
if ANALYSIS_AUTH_TOKEN is None:
raise ValueError("ANALYSIS_AUTH_TOKEN environment variable not set")
DELETE_SESSION_URL = os.getenv("DELETE_SESSION_URL", None)
if DELETE_SESSION_URL is None:
raise ValueError("DELETE_SESSION_URL environment variable not set")
APP_ID = os.getenv("APP_ID", None)
if APP_ID is None:
raise ValueError("APP_ID environment variable not set")
DELETE_CHAT_URL = os.getenv("DELETE_CHAT_URL", None)
if DELETE_CHAT_URL is None:
raise ValueError("DELETE_CHAT_URL environment variable not set")
GET_CHAT_RECORDS_URL = os.getenv("GET_CHAT_RECORDS_URL", None)
if GET_CHAT_RECORDS_URL is None:
raise ValueError("GET_CHAT_RECORDS_URL environment variable not set")
STATUS_CODE_MAP = {
'0000': '结束通话',
'0001': '转接人工',
'1001': '未准备好通话',
'1002': '通话中',
'2000': '进入单车拍照',
'2001': '请对准车辆碰撞部位拍摄照片',
'2002': '请对准被撞物品拍摄照片',
'2003': '请切换摄像头对准本人拍摄一张正面照片',
'2004': '确认单车车牌',
'2005': '请确认车损位置是在车辆前方、后方还是侧面',
'2010': '进入双车拍照',
'2011': '请对准第一辆车碰撞部位拍摄',
'2012': '请对准第二辆车碰撞部位拍摄',
'2013': '请对准第二方车辆侧后方,看清车牌拍摄',
'2014': '请拍摄另一方驾驶人的正面照片',
'2015': '请切换前置摄像头对准本人拍摄一张正面照片',
'2016': '确认双车中的车牌'
}
async def delete_last_two_chat_records(sessionId):
# 获取最后两条聊天记录
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
chat_data = {
"appId": APP_ID,
"chatId": sessionId,
"offset": 0,
}
async with httpx.AsyncClient() as client:
response = await client.post(
GET_CHAT_RECORDS_URL,
json=chat_data,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
last_two_dataId = [x['dataId'] for x in data['data']['list'][-2:]]
# 删除最后聊天聊天记录
for contentId in last_two_dataId:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
url = DELETE_CHAT_URL.format(contentId=contentId, chatId=sessionId, appId=APP_ID)
async with httpx.AsyncClient() as client:
response = await client.delete(
url,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
# @app.exception_handler(RequestValidationError)
# async def validation_exception_handler(request: Request, exc: RequestValidationError):
# logging.error(f"Validation Error: {exc.errors()}")
# raise HTTPException(status_code=422, detail=exc.errors())
@router.post("/chat", response_model=ProcessResponse_chat)
async def chat(request: ProcessRequest_chat):
try:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
json_data = request.model_dump()
chat_data = {
"chatId": json_data['sessionId'],
"stream": False,
"detail": True,
"messages": [
{
"role": "user",
"content": json_data['text']
}
]
}
# Call external analysis service
async with httpx.AsyncClient() as client:
response = await client.post(
ANALYSIS_SERVICE_URL,
json=chat_data,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
except Exception as e:
print(e)
return ProcessResponse_chat(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
outputText="",
nextStage="",
nextStageCode="",
code="500",
msg="大模型服务器无响应"
)
try:
# Extract content from FastGPT response
content = data['choices'][0]['message']['content']
finish_reason = data['choices'][0]['finish_reason']
transfer_to_human = data['newVariables']['state'].get("transfer_to_human", False)
ywrysw = data['newVariables']['state'].get("ywrysw", False)
ywfjdc = data['newVariables']['state'].get("ywfjdc", False)
ywmtc = data['newVariables']['state'].get("ywmtc", False)
jdcsl = data['newVariables']['state'].get("jdcsl", 0)
accident_info_complete = data['newVariables']['state'].get("accident_info_complete", False)
user_is_ready = data['newVariables']['state'].get("user_is_ready", False)
if isinstance(user_is_ready, str):
user_is_ready = user_is_ready.lower() == 'true'
driver_info_complete = data['newVariables']['state'].get("driver_info_complete", False)
drivers_info_complete = data['newVariables']['state'].get("drivers_info_complete", False)
driver_info_check = data['newVariables']['state'].get("drivers_info_check", False)
drivers_info_check = data['newVariables']['state'].get("drivers_info_check", False)
print(data['newVariables'])
nextStageCode=data['newVariables']['status_code']
nextStage=STATUS_CODE_MAP[nextStageCode]
return ProcessResponse_chat(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
outputText=content,
nextStage=nextStage,
nextStageCode=nextStageCode,
code="200",
msg="",
)
except Exception as e:
print(e)
return ProcessResponse_chat(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
outputText="",
nextStage="",
nextStageCode="",
code="500",
msg="大模型服务返回消息不完整"
)
@router.post("/set_info", response_model=ProcessResponse_set)
async def set_info(request: ProcessRequest_set):
# 获取当前state
try:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
json_data = request.model_dump()
chat_data = {
"chatId": json_data['sessionId'],
"stream": False,
"detail": True,
"messages": [
{
"role": "user",
"content": ""
}
]
}
# Call external analysis service
async with httpx.AsyncClient() as client:
response = await client.post(
ANALYSIS_SERVICE_URL,
json=chat_data,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
current_state = data['newVariables']['state']
if isinstance(current_state, str):
current_state = json.loads(current_state)
print(current_state)
except Exception as e:
print(e)
return ProcessResponse_set(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="大模型服务器无响应"
)
try:
await delete_last_two_chat_records(json_data['sessionId'])
except Exception as e:
print(e)
return ProcessResponse_set(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="大模型后台无响应"
)
# 修改当前state
try:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
json_data = request.model_dump()
# key_map = {"xm1": "xm1", "hpzl1": "hpzl1", "hphm1": "driver1License", "sfzmhm1": "driver1ID", "sfzmwh1": "sfzmwh1", "sjhm1": "driver1Phonenumber","sjwh1": "sjwh1", "xm2":"xm2", "hpzl2": "hpzl2", "hphm2": "driver2License", "sfzmhm2": "driver2ID", "sfzmwh2": "sfzmwh2", "sjhm2": "driver2Phonenumber", "sjwh2": "sjwh2"}
# if json_data['key'] not in key_map:
# raise Exception("check your key")
# key = key_map.get(json_data['key'])
key = json_data['key']
value = json_data['value']
current_state[json_data['key']] = value
print(f'即将上传 {current_state}')
# if key == 'driver1ID' or key == 'driver2ID' or key == 'driver1Phonenumber' or key == 'driver2Phonenumber':
# current_state[json_data['key'].replace('hm','wh')] = value[-4:]
chat_data = {
"chatId": json_data['sessionId'],
"stream": False,
"detail": True,
"variables": {'state':current_state},
"messages": [
{
"role": "user",
"content": ""
}
]
}
# Call external analysis service
async with httpx.AsyncClient() as client:
response = await client.post(
ANALYSIS_SERVICE_URL,
json=chat_data,
headers=headers,
timeout=None
)
response.raise_for_status()
return ProcessResponse_set(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="200",
msg=""
)
except Exception as e:
print(e)
return ProcessResponse_set(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="大模型后台无响应"
)
try:
await delete_last_two_chat_records(json_data['sessionId'])
except Exception as e:
print(e)
return ProcessResponse_set(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="大模型后台无响应"
)
@router.post("/get_info", response_model=ProcessResponse_get)
async def get_info(request: ProcessRequest_get):
# 获取当前state
try:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
json_data = request.model_dump()
chat_data = {
"chatId": json_data['sessionId'],
"stream": False,
"detail": True,
"messages": [
{
"role": "user",
"content": ""
}
]
}
# Call external analysis service
async with httpx.AsyncClient() as client:
response = await client.post(
ANALYSIS_SERVICE_URL,
json=chat_data,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
current_state = data['newVariables']['state']
if isinstance(current_state, str):
current_state = json.loads(current_state)
print(current_state)
except Exception as e:
print(e)
return ProcessResponse_get(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
value="",
code="500",
msg="大模型服务器无响应"
)
try:
await delete_last_two_chat_records(json_data['sessionId'])
except Exception as e:
print(e)
return ProcessResponse_get(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
value="",
code="500",
msg="大模型后台无响应"
)
try:
key = json_data['key']
# key_map = {"hpzl1": "hpzl1", "hphm1": "driver1License", "sfzmhm1": "driver1ID", "sfzmwh1": "sfzmwh1", "sjhm1": "driver1Phonenumber","sjwh1": "sjwh1", "hpzl2": "hpzl2", "hphm2": "driver2License", "sfzmhm2": "driver2ID", "sfzmwh2": "sfzmwh2", "sjhm2": "driver2Phonenumber", "sjwh2": "sjwh2"}
acd_keys = ['ywrysw', 'ywfjdc', 'ywmtc', 'bjrjs', 'sgfssj', 'sfsgxc', 'jdcsl', 'sgyy']
human1_keys = ['xm1', 'hpzl1', 'hphm1', 'sfzmhm1', 'sfzmwh1', 'sjhm1', 'sjwh1', 'csbw1']
human2_keys = ['xm2', 'hpzl2', 'hphm2', 'sfzmhm2', 'sfzmwh2', 'sjhm2', 'sjwh2', 'csbw2']
if key == 'all':
acd_values = {}
for k in acd_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
acd_values[k] = v
human1_values = {}
for k in human1_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
human1_values[k] = v
human2_values = {}
for k in human2_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
human2_values[k] = v
value = {'acdinfo': acd_values, 'acdhuman1': human1_values, 'acdhuman2': human2_values}
value = json.dumps(value)
elif key == "acdinfo":
acd_values = {}
for k in acd_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
acd_values[k] = v
value = json.dumps(acd_values)
elif key == 'acdhuman1':
human1_values = {}
for k in human1_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
human1_values[k] = v
value = json.dumps(human1_value)
elif key == 'acdhuman2':
human2_values = {}
for k in human1_keys:
v = current_state.get(k, '')
if isinstance(v, bool):
v = '1' if v==True else '0'
human2_values[k] = v
value = json.dumps(human2_value)
else:
# if key in key_map:
# real_key = key_map.get(key)
# else:
# real_key = key
value = current_state.get(key, '')
value = json.dumps(value)
# if value == 'null':
# raise Exception("check your key")
print(json_data)
return ProcessResponse_get(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
value=value,
code="200",
msg=""
)
except Exception as e:
print(e)
return ProcessResponse_get(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
value="",
code="500",
msg="入参不规范"
)
@router.delete("/delete_session", response_model=ProcessResponse_delete_session)
async def delete_session(request: ProcessRequest_delete_session):
try:
headers = {
"Authorization": f"Bearer {ANALYSIS_AUTH_TOKEN}",
"Content-Type": "application/json"
}
json_data = request.model_dump()
chatId = json_data.get('sessionId', None)
if chatId is None:
raise ValueError(f'Error in chatId.')
url = DELETE_SESSION_URL.format(appId=APP_ID, chatId=chatId)
# Call external analysis service
async with httpx.AsyncClient() as client:
response = await client.delete(
url,
headers=headers,
timeout=None
)
response.raise_for_status()
data = response.json()
# print(data)
except Exception as e:
print(e)
return ProcessResponse_chat(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="大模型服务器无响应"
)
try:
if data['code'] == 200:
return ProcessResponse_delete_session(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="200",
msg=""
)
else:
raise ValueError("删除会话失败")
except Exception as e:
print(e)
return ProcessResponse_delete_session(
sessionId=json_data['sessionId'],
timeStamp=json_data['timeStamp'],
code="500",
msg="删除会话失败"
)
+1
View File
@@ -0,0 +1 @@
# This file is intentionally left blank.
+11
View File
@@ -0,0 +1,11 @@
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
PROJECT_NAME = "Flexible Employment Analysis API"
API_V1_STR = "/api/v1"
SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key")
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
DEBUG = os.getenv("DEBUG", "false").lower() in ("true", "1", "t")
+16
View File
@@ -0,0 +1,16 @@
from fastapi import FastAPI
import sys
sys.path.append('..')
from api.endpoints import router as api_router
app = FastAPI(
title="AI Accident Information Collection API",
description="AI Accident Information Collection API",
version="1.0.0"
)
@app.get("/")
def read_root():
return {"message": "Server is running."}
app.include_router(api_router)
+1
View File
@@ -0,0 +1 @@
# This file is intentionally left blank.
+50
View File
@@ -0,0 +1,50 @@
from pydantic import BaseModel, Field
from typing import Optional
class ProcessRequest_chat(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
text: str = Field(...)
class ProcessResponse_chat(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
outputText: str = Field(...)
nextStage: str = Field(..., max_length=32)
nextStageCode: str = Field(..., max_length=4)
code: str = Field(..., max_length=4)
msg: Optional[str] = None
class ProcessRequest_get(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
key: str = Field(...)
class ProcessResponse_get(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
value: str = Field(...)
code: str = Field(..., max_length=4)
msg: Optional[str] = None
class ProcessRequest_set(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
key: str = Field(...)
value: str = Field(...)
class ProcessResponse_set(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
code: str = Field(..., max_length=4)
msg: Optional[str] = None
class ProcessRequest_delete_session(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
class ProcessResponse_delete_session(BaseModel):
sessionId: str = Field(..., max_length=64)
timeStamp: str = Field(..., max_length=32)
code: str = Field(..., max_length=4)
msg: Optional[str] = None
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff