Metadata-Version: 2.5
Name: python-standard-demo
Version: 0.1.0
Summary: A learning-oriented Python project with LangChain, CLI, and FastAPI examples
Project-URL: Repository, https://github.com/Nezuko-Wen/python-standard-demo
Author: zouzhiwen
License-Expression: MIT
License-File: LICENSE
Requires-Python: <3.15,>=3.12
Requires-Dist: fastapi<1.0,>=0.115
Requires-Dist: langchain-openai<2.0,>=1.0
Requires-Dist: langchain<2.0,>=1.0
Requires-Dist: pydantic-settings<3.0,>=2.0
Requires-Dist: uvicorn<1.0,>=0.30
Description-Content-Type: text/markdown

# Python Standard Demo

一个面向 Python 工程化与 LangChain 学习的示例项目，同时提供 CLI 与 FastAPI 入口。两个入口复用同一套 Prompt、模型配置和调用逻辑，方便先观察最小调用链，再扩展 Agent、Tool、记忆与 RAG。

## Python 系统学习

如果需要先补齐 Python 语言和工程基础，请从 [Python 学习路线](https://github.com/Nezuko-Wen/python-standard-demo/blob/main/python_learning/README.md) 开始。课程先介绍依赖管理、项目布局和打包，再逐步进入语法、面向对象、装饰器、并发与异步等主题。

## 环境要求

- Python 3.12
- [uv](https://docs.astral.sh/uv/)
- 支持 OpenAI Chat Completions 协议的模型服务

## 初始化

```bash
cp .env.example .env
uv sync
```

随后编辑 `.env`：

```dotenv
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_API_KEY=your-api-key
OPENAI_MODEL=your-model-name
OPENAI_USE_RESPONSES_API=false
OPENAI_STREAMING=false
```

使用第三方 OpenAI 兼容服务时，需要同时替换服务地址、密钥和模型名。该脚手架只依赖标准消息内容；第三方接口额外返回的私有字段不在处理范围内。

### 协议选择

- `OPENAI_USE_RESPONSES_API=false`：使用 `/chat/completions`，兼容范围更广。
- `OPENAI_USE_RESPONSES_API=true`：使用 `/responses`，适合支持 Responses API 的服务。
- `OPENAI_STREAMING=false`：按普通 JSON 响应解析。
- `OPENAI_STREAMING=true`：按 SSE 事件流解析；`ainvoke()` 仍会聚合并返回完整答案。

协议和传输方式必须按服务实际能力分别选择。当前本机 Codex 服务只开放 `/backend-api/codex/responses`，并返回 SSE 事件流，因此两个选项都需要设置为 `true`；不要把 `/responses` 自身追加到 `OPENAI_BASE_URL`。

## CLI 入口

```bash
uv run python-standard-demo "请用一句话介绍 LangChain"
```

CLI 适合单步调试：`cli.py` 接收问题，`ChatService` 组装 Prompt 并异步调用模型，最后由字符串解析器返回答案。

## FastAPI 入口

```bash
uv run uvicorn python_standard_demo.api:app --reload
```

健康检查：

```bash
curl http://127.0.0.1:8000/health
```

调用模型：

```bash
curl -X POST http://127.0.0.1:8000/v1/chat \
  -H 'Content-Type: application/json' \
  -d '{"question":"请用一句话介绍 LangChain"}'
```

## MiniFastAPI 教学入口

项目并行提供了一个可由 Uvicorn 加载的简化版 Web 框架：

```bash
uv run uvicorn python_standard_demo.mini_api:app --reload
```

它与正式入口使用相同的 `/health`、`/v1/chat`、Schema 和 `ChatService`，便于逐层对照：

```text
mini_api.py 路由装饰器
  → MiniFastAPI 注册静态路由
  → Uvicorn 调用 ASGI __call__
  → JSON Body 与 Pydantic 校验
  → Depends 依赖解析
  → 执行业务函数
  → response_model 校验并发送 JSON
```

`MiniFastAPI` 只用于理解框架原理，实现了静态 GET/POST 路由、JSON Body、依赖注入、同步/异步函数、请求级依赖缓存和常见错误响应。它没有实现动态路径、查询参数、中间件、OpenAPI、WebSocket、SSE、文件上传及 FastAPI 的完整安全与兼容性能力，不能替代生产环境中的 FastAPI。

## 目录结构

```text
.
├── pyproject.toml
├── src/python_standard_demo
│   ├── api.py           # FastAPI 入口
│   ├── chat_service.py  # Prompt 与模型调用链
│   ├── cli.py           # CLI 入口
│   ├── config.py        # 环境变量配置
│   ├── mini_api.py      # MiniFastAPI 教学入口
│   ├── mini_fastapi.py  # 简化 ASGI 框架实现
│   ├── model.py         # ChatOpenAI 创建逻辑
│   └── schemas.py       # API 数据结构
└── uv.lock              # 依赖锁文件
```

## 代码检查

```bash
uv run ruff check .
uv run ruff format --check .
uv run mypy src
```

## CI/CD 与 PyPI 发布

`.github/workflows/ci.yml` 在提交到 `main` 或创建 Pull Request 时执行依赖锁定校验、Ruff、Mypy、构建以及 Wheel 和 sdist 冒烟验证。普通代码提交不会发布包。

`.github/workflows/release.yml` 只接受 `v0.1.0` 格式的版本 Tag。工作流会先确认 Tag 与 `pyproject.toml` 中的版本一致，再复用完整 CI 构建产物，并由独立发布 Job 上传到 PyPI。

首次发布前，需要完成一次外部配置：

1. 在 GitHub 仓库 `Settings → Environments` 中创建名为 `pypi` 的 Environment，建议配置 Required reviewers。
2. 登录 PyPI，在账号的 `Publishing` 页面新增 GitHub Pending Publisher。
3. 填写项目名 `python-standard-demo`、Owner `Nezuko-Wen`、仓库名 `python-standard-demo`、工作流名 `release.yml` 和 Environment `pypi`。

发布新版本时，先更新版本并提交，再创建完全一致的 Tag：

```bash
uv version 0.1.1
git add pyproject.toml uv.lock
git commit -m "release: v0.1.1"
git tag -a v0.1.1 -m "v0.1.1"
git push origin main
git push origin v0.1.1
```

发布 Job 通过 GitHub OIDC 和 PyPI Trusted Publishing 获取短期凭据，不需要在 GitHub Secrets 中保存长期 `PYPI_TOKEN`。Pending Publisher 会在首次成功发布时创建 PyPI 项目，但不会提前保留项目名称。

## 许可证

本项目使用 [MIT License](https://github.com/Nezuko-Wen/python-standard-demo/blob/main/LICENSE)。

## 建议学习顺序

1. 修改 `chat_service.py` 中的系统提示词，观察 Prompt 对输出的影响。
2. 阅读 `ChatPromptTemplate | ChatOpenAI | StrOutputParser` 组成的 LCEL 调用链。
3. 增加结构化输出，理解 Pydantic 模型与模型输出约束。
4. 增加 Tool 和 Agent，并观察模型如何选择工具。
5. 最后加入向量存储和检索器，完成一个最小 RAG 应用。

当前示例刻意保持为无状态的单轮问答，避免在初始化阶段同时引入会话存储和并发一致性问题。

学习 Web 框架源码时，建议依次阅读 `mini_api.py` 中的装饰器用法、`MiniFastAPI._route()` 的注册逻辑、`MiniFastAPI.__call__()` 的 ASGI 入口、`_build_endpoint_values()` 的参数校验，最后阅读 `_resolve_dependency()` 的递归依赖解析。
