跳转至

FastAPI 应用

演示如何用 FastAPI + Pydantic 构建一个极简的 Agent HTTP 服务, 包含路径参数、查询参数、请求体校验、错误处理与异步路由。 对应教程:第2卷《Python 进阶》第4章 FastAPI 与 Pydantic。 运行方式:: uvicorn examples.volume-02-python.fastapi_app:app --reload

文件路径: examples/volume-02-python\fastapi_app.py


源代码

"""FastAPI 服务示例。

演示如何用 FastAPI + Pydantic 构建一个极简的 Agent HTTP 服务,
包含路径参数、查询参数、请求体校验、错误处理与异步路由。

对应教程:第2卷《Python 进阶》第4章 FastAPI 与 Pydantic。

运行方式::

    uvicorn examples.volume-02-python.fastapi_app:app --reload
"""

from __future__ import annotations

from typing import Annotated

from fastapi import FastAPI, HTTPException, Query, status
from pydantic_models import AgentConfig, ChatLog, Message, RoleEnum

app = FastAPI(title="示例 Agent 服务", version="0.1.0")

# 内存存储,便于演示(生产环境应使用数据库)
_chat_logs: dict[str, ChatLog] = {}


@app.get("/health")
def health() -> dict[str, str]:
    """健康检查接口。

    Returns:
        服务状态字典。
    """
    return {"status": "ok"}


@app.post("/sessions/{session_id}", status_code=status.HTTP_201_CREATED)
def create_session(session_id: str, system_prompt: str = "你是一个友好的助手。") -> ChatLog:
    """创建一个会话。

    Args:
        session_id: 会话 ID。
        system_prompt: 系统提示词。

    Returns:
        新建的 ``ChatLog``。

    Raises:
        HTTPException: 会话已存在时返回 409。
    """
    if session_id in _chat_logs:
        raise HTTPException(status_code=409, detail=f"会话 {session_id} 已存在")
    log = ChatLog(
        session_id=session_id,
        messages=[Message(role=RoleEnum.SYSTEM, content=system_prompt)],
    )
    _chat_logs[session_id] = log
    return log


@app.get("/sessions/{session_id}")
def get_session(session_id: str) -> ChatLog:
    """获取会话内容。

    Args:
        session_id: 会话 ID。

    Returns:
        对应的 ``ChatLog``。

    Raises:
        HTTPException: 会话不存在时返回 404。
    """
    log = _chat_logs.get(session_id)
    if log is None:
        raise HTTPException(status_code=404, detail=f"会话 {session_id} 不存在")
    return log


@app.post("/sessions/{session_id}/messages")
def add_message(session_id: str, message: Message) -> ChatLog:
    """向会话追加一条消息。

    Args:
        session_id: 会话 ID。
        message: 待追加的消息。

    Returns:
        更新后的 ``ChatLog``。
    """
    log = _chat_logs.get(session_id)
    if log is None:
        raise HTTPException(status_code=404, detail=f"会话 {session_id} 不存在")
    log.messages.append(message)
    return log


@app.get("/sessions")
def list_sessions(
    limit: Annotated[int, Query(ge=1, le=100, description="返回数量上限")] = 10,
) -> list[ChatLog]:
    """列出最近创建的若干会话。

    Args:
        limit: 返回数量上限。

    Returns:
        ``ChatLog`` 列表。
    """
    return list(_chat_logs.values())[:limit]


@app.delete("/sessions/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_session(session_id: str) -> None:
    """删除会话。

    Args:
        session_id: 会话 ID。

    Raises:
        HTTPException: 会话不存在时返回 404。
    """
    if session_id not in _chat_logs:
        raise HTTPException(status_code=404, detail=f"会话 {session_id} 不存在")
    del _chat_logs[session_id]


@app.post("/validate/config")
def validate_config(config: AgentConfig) -> dict[str, object]:
    """校验 Agent 配置并返回摘要。

    Args:
        config: 待校验的配置。

    Returns:
        包含 ``valid`` 与 ``config`` 的字典。
    """
    return {"valid": True, "config": config.model_dump()}


def reset_store() -> None:
    """清空内存存储(测试辅助)。"""
    _chat_logs.clear()


def main() -> None:
    """演示用 TestClient 本地调用服务。"""
    import uvicorn  # 延迟导入,仅在直接运行时需要

    uvicorn.run(app, host="127.0.0.1", port=8000)


if __name__ == "__main__":
    main()