Merge branch 'main' of http://182.92.86.220:8300/wx44wx/ZNJJ-api-server
# Conflicts: # docs/视频快处智能信息采集机器人交互接口文档V1.0.10.docx # src/api/endpoints.py # src/core/fastgpt_client.py
This commit is contained in:
+2
-5
@@ -3,11 +3,8 @@ SECRET_KEY=your_secret_key
|
||||
DEBUG=True
|
||||
|
||||
ANALYSIS_SERVICE_URL=http://127.0.0.1:3030
|
||||
ANALYSIS_AUTH_TOKEN=fastgpt-hSPnXMoBNGVAEpTLkQT3YfAnN26gQSyvLd4ABL1MRDoh68nL4RDlopFHXqmH8
|
||||
APP_ID=683ea1bc86197e19f71fc1ae
|
||||
DELETE_SESSION_URL=http://127.0.0.1:3030/api/core/chat/delHistory?chatId={chatId}&appId={appId}
|
||||
DELETE_CHAT_URL=http://127.0.0.1:3030/api/core/chat/item/delete?contentId={contentId}&chatId={chatId}&appId={appId}
|
||||
GET_CHAT_RECORDS_URL=http://127.0.0.1:3030/api/core/chat/getPaginationRecords
|
||||
ANALYSIS_AUTH_TOKEN=replace-with-fastgpt-api-key
|
||||
APP_ID=replace-with-fastgpt-app-id
|
||||
|
||||
# Voice demo (Pipecat /ws-product). Relative to project root, or an absolute path.
|
||||
VOICE_CONFIG=config/voice.json
|
||||
|
||||
+40
-14
@@ -57,6 +57,13 @@ def normalize_stage_code(stage_code: str) -> str:
|
||||
return stage_code
|
||||
|
||||
|
||||
def external_stage_code(client_mode: str, stage_code: str) -> str:
|
||||
"""Apply the external namespace required by the client mode."""
|
||||
if client_mode == "browser_addon":
|
||||
return f"browser_addon.{stage_code}"
|
||||
return stage_code
|
||||
|
||||
|
||||
def extract_first_state_and_clean_content(text: str) -> tuple[str | None, str]:
|
||||
"""Return the first state code and content with all state tags removed."""
|
||||
match = STATE_TAG_PATTERN.search(text)
|
||||
@@ -73,7 +80,7 @@ def format_set_info_input(payload: dict, include_input_info: bool) -> str:
|
||||
|
||||
async def delete_last_two_chat_records(
|
||||
client: AsyncChatClient,
|
||||
session_id: str
|
||||
session_id: str,
|
||||
) -> None:
|
||||
"""Delete the last two chat records."""
|
||||
try:
|
||||
@@ -126,17 +133,19 @@ async def chat(
|
||||
):
|
||||
"""Handle chat completion request."""
|
||||
json_data = request.model_dump()
|
||||
client_mode = json_data['clientMode']
|
||||
need_form_update = json_data.get('needFormUpdate', False)
|
||||
use_text_chunk = json_data.get('useTextChunk', False)
|
||||
chat_input = ChatInput(
|
||||
session_id=json_data['sessionId'],
|
||||
text=json_data['text'],
|
||||
need_form_update=need_form_update,
|
||||
client_mode=client_mode,
|
||||
)
|
||||
request_started_at = time.perf_counter()
|
||||
logger.info(
|
||||
"Chat request received "
|
||||
f"sessionId={json_data['sessionId']} stream={stream} "
|
||||
f"sessionId={json_data['sessionId']} clientMode={client_mode} stream={stream} "
|
||||
f"needFormUpdate={need_form_update} useTextChunk={use_text_chunk} "
|
||||
f"text_len={len(json_data.get('text', ''))} "
|
||||
f"input={json_data.get('text', '')!r}"
|
||||
@@ -280,11 +289,17 @@ async def chat(
|
||||
if state_code:
|
||||
|
||||
# Apply logic to map/adjust state code
|
||||
nextStageCode = normalize_stage_code(state_code)
|
||||
nextStage = STATUS_CODE_MAP.get(nextStageCode, '')
|
||||
normalized_stage_code = normalize_stage_code(state_code)
|
||||
nextStageCode = external_stage_code(
|
||||
client_mode,
|
||||
normalized_stage_code,
|
||||
)
|
||||
nextStage = STATUS_CODE_MAP.get(normalized_stage_code, '')
|
||||
logger.info(
|
||||
"FastGPT stream stage_code parsed "
|
||||
f"sessionId={json_data['sessionId']} "
|
||||
f"clientMode={client_mode} "
|
||||
f"rawStageCode={state_code} "
|
||||
f"nextStageCode={nextStageCode} nextStage={nextStage}"
|
||||
)
|
||||
|
||||
@@ -426,15 +441,17 @@ async def chat(
|
||||
logger.error(f"content既不是list也不是str, type: {type(content)}")
|
||||
raise ValueError("大模型回复不是list也不是str")
|
||||
|
||||
nextStageCode = content_stage_code or result.status_code
|
||||
if not nextStageCode:
|
||||
raw_stage_code = content_stage_code or result.status_code
|
||||
if not raw_stage_code:
|
||||
raise ValueError("大模型回复中缺少state")
|
||||
nextStageCode = normalize_stage_code(nextStageCode)
|
||||
nextStage = STATUS_CODE_MAP.get(nextStageCode, '')
|
||||
normalized_stage_code = normalize_stage_code(raw_stage_code)
|
||||
nextStageCode = external_stage_code(client_mode, normalized_stage_code)
|
||||
nextStage = STATUS_CODE_MAP.get(normalized_stage_code, '')
|
||||
form_update = result.form_update
|
||||
logger.info(
|
||||
"Chat non-stream completed "
|
||||
f"sessionId={json_data['sessionId']} "
|
||||
f"clientMode={client_mode} rawStageCode={raw_stage_code} "
|
||||
f"duration_ms={(time.perf_counter() - request_started_at) * 1000:.1f} "
|
||||
f"nextStageCode={nextStageCode} nextStage={nextStage} "
|
||||
f"output_len={len(content)} formUpdate_type={type(form_update).__name__} "
|
||||
@@ -468,7 +485,7 @@ async def chat(
|
||||
@router.post("/set_info", response_model=ProcessResponse_set)
|
||||
async def set_info(
|
||||
request: ProcessRequest_set,
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client)
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client),
|
||||
):
|
||||
"""Set information in chat state."""
|
||||
json_data = request.model_dump()
|
||||
@@ -504,7 +521,10 @@ async def set_info(
|
||||
)
|
||||
|
||||
try:
|
||||
await delete_last_two_chat_records(client, json_data['sessionId'])
|
||||
await delete_last_two_chat_records(
|
||||
client,
|
||||
json_data['sessionId'],
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting chat records: {e}")
|
||||
return ProcessResponse_set(
|
||||
@@ -533,7 +553,10 @@ async def set_info(
|
||||
response.raise_for_status()
|
||||
|
||||
# Delete records again after update
|
||||
await delete_last_two_chat_records(client, json_data['sessionId'])
|
||||
await delete_last_two_chat_records(
|
||||
client,
|
||||
json_data['sessionId'],
|
||||
)
|
||||
|
||||
return ProcessResponse_set(
|
||||
sessionId=json_data['sessionId'],
|
||||
@@ -554,7 +577,7 @@ async def set_info(
|
||||
@router.post("/get_info", response_model=ProcessResponse_get)
|
||||
async def get_info(
|
||||
request: ProcessRequest_get,
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client)
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client),
|
||||
):
|
||||
"""Get information from chat state."""
|
||||
json_data = request.model_dump()
|
||||
@@ -591,7 +614,10 @@ async def get_info(
|
||||
)
|
||||
|
||||
try:
|
||||
await delete_last_two_chat_records(client, json_data['sessionId'])
|
||||
await delete_last_two_chat_records(
|
||||
client,
|
||||
json_data['sessionId'],
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting records: {e}")
|
||||
return ProcessResponse_get(
|
||||
@@ -656,7 +682,7 @@ async def get_info(
|
||||
@router.delete("/delete_session", response_model=ProcessResponse_delete_session)
|
||||
async def delete_session(
|
||||
request: ProcessRequest_delete_session,
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client)
|
||||
client: AsyncChatClient = Depends(get_fastgpt_client),
|
||||
):
|
||||
"""Delete a chat session."""
|
||||
json_data = request.model_dump()
|
||||
|
||||
@@ -12,6 +12,7 @@ class ChatInput:
|
||||
session_id: str
|
||||
text: str
|
||||
need_form_update: bool = False
|
||||
client_mode: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
+24
-7
@@ -18,7 +18,7 @@ from .chat import (
|
||||
)
|
||||
|
||||
|
||||
FORM_EXTRACT_MODULE_NAME = "文本内容提取事故信息"
|
||||
FORM_EXTRACT_MODULE_NAME = "更新表单"
|
||||
|
||||
|
||||
def _parse_json_value(value: Any) -> Any:
|
||||
@@ -46,12 +46,23 @@ def _extract_form_update(nodes: Any) -> Any:
|
||||
if node.get("moduleName") != FORM_EXTRACT_MODULE_NAME:
|
||||
continue
|
||||
|
||||
extract_result = node.get("extractResult", {})
|
||||
if not isinstance(extract_result, dict):
|
||||
# 代码节点返回值在 customOutputs;兼容旧版/其他形态
|
||||
outputs = node.get("customOutputs")
|
||||
if not isinstance(outputs, dict):
|
||||
outputs = node.get("nodeOutputs")
|
||||
if not isinstance(outputs, dict):
|
||||
outputs = node.get("extractResult")
|
||||
if not isinstance(outputs, dict):
|
||||
return {}
|
||||
|
||||
form_update = extract_result.get("formUpdate", "")
|
||||
return _parse_json_value(form_update) if form_update else {}
|
||||
form_update = outputs.get("formUpdate", "")
|
||||
if form_update in ("", None):
|
||||
return {}
|
||||
parsed = _parse_json_value(form_update)
|
||||
# 无变更时代码节点常返回 {},直接透传空对象
|
||||
if not isinstance(parsed, dict):
|
||||
return {}
|
||||
return parsed
|
||||
|
||||
return {}
|
||||
|
||||
@@ -88,7 +99,10 @@ class FastGPTBackend:
|
||||
chatId=chat_input.session_id,
|
||||
stream=True,
|
||||
detail=True,
|
||||
variables={"needFormUpdate": chat_input.need_form_update},
|
||||
variables={
|
||||
"needFormUpdate": chat_input.need_form_update,
|
||||
"clientMode": chat_input.client_mode,
|
||||
},
|
||||
)
|
||||
|
||||
async for event in aiter_stream_events(response):
|
||||
@@ -123,7 +137,10 @@ class FastGPTBackend:
|
||||
chatId=chat_input.session_id,
|
||||
stream=False,
|
||||
detail=True,
|
||||
variables={"needFormUpdate": chat_input.need_form_update},
|
||||
variables={
|
||||
"needFormUpdate": chat_input.need_form_update,
|
||||
"clientMode": chat_input.client_mode,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
+4
-6
@@ -14,17 +14,15 @@ class Config:
|
||||
FASTGPT_API_KEY = os.getenv("ANALYSIS_AUTH_TOKEN")
|
||||
FASTGPT_BASE_URL = os.getenv("ANALYSIS_SERVICE_URL")
|
||||
FASTGPT_APP_ID = os.getenv("APP_ID")
|
||||
DELETE_SESSION_URL = os.getenv("DELETE_SESSION_URL")
|
||||
DELETE_CHAT_URL = os.getenv("DELETE_CHAT_URL")
|
||||
GET_CHAT_RECORDS_URL = os.getenv("GET_CHAT_RECORDS_URL")
|
||||
|
||||
@classmethod
|
||||
def validate(cls):
|
||||
"""Validate required configuration."""
|
||||
required = [
|
||||
("FASTGPT_API_KEY", cls.FASTGPT_API_KEY),
|
||||
("FASTGPT_APP_ID", cls.FASTGPT_APP_ID),
|
||||
("FASTGPT_BASE_URL (ANALYSIS_SERVICE_URL)", cls.FASTGPT_BASE_URL),
|
||||
("FASTGPT_API_KEY (ANALYSIS_AUTH_TOKEN)", cls.FASTGPT_API_KEY),
|
||||
("FASTGPT_APP_ID (APP_ID)", cls.FASTGPT_APP_ID),
|
||||
]
|
||||
missing = [name for name, value in required if not value]
|
||||
if missing:
|
||||
raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
|
||||
raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
|
||||
|
||||
@@ -6,7 +6,7 @@ from ..backends.chat import ChatBackend
|
||||
from ..backends.fastgpt import FastGPTBackend
|
||||
from .config import Config
|
||||
|
||||
# Global client instance
|
||||
|
||||
_fastgpt_client: AsyncChatClient | None = None
|
||||
_chat_backend: ChatBackend | None = None
|
||||
|
||||
@@ -16,8 +16,7 @@ async def lifespan(app: FastAPI):
|
||||
"""Manage FastGPT client lifecycle."""
|
||||
global _chat_backend, _fastgpt_client
|
||||
Config.validate()
|
||||
|
||||
# Initialize client
|
||||
|
||||
_fastgpt_client = AsyncChatClient(
|
||||
api_key=Config.FASTGPT_API_KEY,
|
||||
base_url=Config.FASTGPT_BASE_URL,
|
||||
@@ -39,7 +38,7 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
|
||||
def get_fastgpt_client() -> AsyncChatClient:
|
||||
"""Get the FastGPT client instance."""
|
||||
"""Get the initialized FastGPT client."""
|
||||
if _fastgpt_client is None:
|
||||
raise RuntimeError("FastGPT client not initialized")
|
||||
return _fastgpt_client
|
||||
|
||||
+15
-3
@@ -1,10 +1,22 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Any, Optional
|
||||
from pydantic import BaseModel, BeforeValidator, Field
|
||||
from typing import Annotated, Any, Literal, Optional
|
||||
|
||||
ClientMode = Literal["direct", "browser_addon"]
|
||||
|
||||
|
||||
def normalize_client_mode(value: Any) -> Any:
|
||||
if value is None or (isinstance(value, str) and value.strip() == ""):
|
||||
return "direct"
|
||||
return value
|
||||
|
||||
|
||||
NormalizedClientMode = Annotated[ClientMode, BeforeValidator(normalize_client_mode)]
|
||||
|
||||
class ProcessRequest_chat(BaseModel):
|
||||
sessionId: str = Field(..., max_length=64)
|
||||
timeStamp: str = Field(..., max_length=32)
|
||||
text: str = Field(...)
|
||||
clientMode: NormalizedClientMode = "direct"
|
||||
needFormUpdate: bool = False
|
||||
useTextChunk: bool = False
|
||||
|
||||
@@ -14,7 +26,7 @@ class ProcessResponse_chat(BaseModel):
|
||||
outputText: str = Field(...)
|
||||
formUpdate: Any = Field(default_factory=dict)
|
||||
nextStage: str = Field(..., max_length=32)
|
||||
nextStageCode: str = Field(..., max_length=4)
|
||||
nextStageCode: str = Field(..., max_length=32)
|
||||
code: str = Field(..., max_length=4)
|
||||
msg: Optional[str] = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user