Langchain-Chatchat 代码贡献工作流:Poetry 依赖管理、本地开发环境与 ruff 格式化/Lint 实践
本篇围绕 Langchain-Chatchat 仓库的贡献指南文档 docs/contributing/code.md,系统讲解作为外部贡献者参与该项目代码开发的完整技术链路:如何通过 Fork & PR 流程提交代码、如何用 Poetry 正确管理依赖与本地开发环境、如何用 ruff 完成代码格式化与增量格式化、以及如何本地复现 CI 的 lint 与测试检查。读完本文,你可以独立完成从克隆仓库到提交 PR 的全流程,并理解仓库中 Makefile、pyproject.toml 与 CI 工作流之间的对应关系。
贡献流程总览
按照 docs/contributing/code.md 的说明,Langchain-Chatchat 的代码贡献遵循标准的 Fork & Pull Request 流程:除非你是项目维护者,否则不要直接向主分支提交代码,应通过 fork 后再发起 PR 的方式贡献。
提交 PR 之前有两个硬性要求:
- 按 PR 模板操作,并预期 CI 系统会自动运行 linting 和测试,确保代码符合项目标准;
- 保持单元测试与文档同步更新:
- 添加新功能时,更新受影响的操作文档;
- 修复 bug 时,尽可能添加一个单元测试,放置在
tests/integration_tests或tests/unit_tests目录中。
对应地,仓库中确实存在这两个测试目录(位于 libs/chatchat-server/tests 下,包含 integration_tests/、unit_tests/ 以及针对知识库向量库、文档加载器等模块的专项测试),并配有共享测试配置 conftest.py。贡献指南的总入口在 docs/contributing/README.md,其中还说明了 Issue 管理约定(15 天无回复的 Issue 会被关闭)以及遇到问题时可联系维护者协商规则调整。
依赖管理:Poetry 与虚拟环境策略
本项目使用 Poetry 管理依赖。这一点在仓库结构中可以直接印证:仓库根目录和两个子项目(服务端 libs/chatchat-server/pyproject.toml、SDK libs/python-sdk)各自带有 pyproject.toml 和 poetry.toml。
安装 Poetry 前的环境准备
贡献文档给出了两条关键提示:
- 如果你使用 Conda,建议先创建并激活一个独立的 Conda 环境再安装 Poetry,例如:
conda create -n chatchat python=3.9
需要注意一个版本约束差异:上述命令是文档中的示例写法,而 libs/chatchat-server/pyproject.toml 中对服务端的实际 Python 约束为 python = ">=3.10,<3.12,!=3.9.7",因此创建环境时建议实际选用 3.10 或 3.11,避免安装后因版本不符而无法解析依赖。
- 如果没有其他用 Poetry 管理的项目,pipx 或 pip 都可以完成 Poetry 的安装(Poetry 官方安装文档支持 pipx、pip、独立安装脚本等多种方式)。
让 Poetry 复用 Conda/Pyenv 的 Python
贡献文档中的第三条 Note 非常实用:如果你用 Conda 或 Pyenv 作为环境/包管理器,安装 Poetry 后应执行:
poetry config virtualenvs.prefer-active-python true
这条配置让 Poetry 优先使用当前已激活的 Python(如 Conda 环境中的 Python),而不是再单独找一个 virtualenv 解释器,从而避免环境割裂。
另外,从仓库中的 libs/chatchat-server/poetry.toml 可以看到项目自身的 Poetry 约定:
[virtualenvs]
in-project = true
[plugins]
[plugins.pypi_mirror]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
即虚拟环境创建在项目目录内(.venv),且默认配置了国内 PyPI 镜像加速源;libs/chatchat-server/pyproject.toml 中的 [[tool.poetry.source]] 也将清华源声明为 primary 源。
本地开发环境安装
贡献文档给出的两步操作如下:
- 选择主项目目录(服务端代码位于
libs/chatchat-server,而不是仓库根目录):
cd Langchain-Chatchat/libs/chatchat-server/
- 安装 chatchat 依赖(用于运行 chatchat 的 lint 与 tests):
poetry install --with lint,test
这里 --with lint,test 的含义可以直接在 pyproject.toml 中找到依据:lint 与 test 都是标记为 optional = true 的依赖组。其中:
- lint 组(L170-L175):
ruff = "^0.1.5"; - test 组(L148-L167):
pytest、pytest-cov、pytest-dotenv、pytest-watcher、freezegun、responses、pytest-asyncio、pytest-mock、pytest-socket、syrupy、requests-mock等,注释明确说明该组只收录运行测试所需的依赖; - 此外还有可选的
codespell组(拼写检查)和dev组(jupyter、setuptools)。
pyproject.toml 中 [tool.poetry.dependencies] 部分列出了核心运行时依赖:langchain 0.1.17、fastapi ~0.109.2、streamlit 1.34.0、faiss-cpu ~1.7.4、SQLAlchemy ~2.0.25 等,并通过 [tool.poetry.extras] 提供 xinference、zhipuai、ollama 等可选扩展(L74-L77)。
开发环境如何被“安装”:direct_url.json 机制
贡献文档还特别解释了 poetry install 的一个底层行为,这对理解 editable 安装很有价值:
Poetry install 后会在你的 site-packages 安装一个
chatchat-<version>.dist-info文件夹,带有direct_url.json文件,这个文件指向你的开发环境。
其原理是:该包在 pyproject.toml 中声明了 packages = [{include = "chatchat"}, {include = "langchain_chatchat"}] 两个本地包,Poetry 对本地包默认做 editable(可编辑)安装,direct_url.json 中的 url 字段记录了指向本仓库路径的信息,因此你在本地修改 chatchat/ 或 langchain_chatchat/ 下的源码后,运行环境中的行为会即时生效,无需重复安装。这也是贡献者可以一边改代码一边跑 lint/test 的基础。
代码格式化:ruff 与 make format
贡献文档指出本项目使用 ruff 进行代码格式化,并给出两条命令:
# 对整个库进行格式化
cd {chatchat-server}
make format
# 仅格式化当前分支相对主分支已修改的文件
make format_diff
这两条命令的真实实现见 Makefile:
format format_diff:
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES)
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I --fix $(PYTHON_FILES)
可以看到 format 实际执行两个动作:ruff format(代码风格格式化)和 ruff --select I --fix(import 排序自动修复)。两者的差异在于 PYTHON_FILES 变量:
make format时PYTHON_FILES=.,作用于整个项目;make format_diff时(Makefile L48),PYTHON_FILES由git diff动态生成——取当前分支相对master分支已变更(且未删除)的.py与.ipynb文件列表:
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/langchain --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')
这正是贡献文档强调的使用场景:当你只修改了项目的一部分,希望确保改动部分格式正确、而不触碰代码库其余部分时,make format_diff 可以最小化 diff,避免 PR 中混入大量与功能无关的格式变更,也降低了与主线合并时的冲突概率。
ruff 的具体规则集在 pyproject.toml 中声明:
[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"T201", # print
]
即启用 pycodestyle 风格规则、pyflakes 错误检测、import 排序,并将 print 语句(T201)标记为问题——这对一个以 FastAPI + Streamlit 构建的服务端项目是合理的约束(服务端日志应走 loguru 而非 print)。同时 tool.ruff 排除了个别非 UTF-8 的测试样例文件。
Lint 检查:make lint 的完整检查链
虽然贡献文档主体聚焦格式化,但它明确要求 CI 会自动运行 linting 和测试,因此贡献者需要了解 make lint 实际执行了哪些检查。Makefile L53-L59 中,lint(及其 lint_diff/lint_package/lint_tests 变体)按顺序执行五层检查:
lint lint_diff lint_package lint_tests:
./scripts/check_pydantic.sh .
./scripts/lint_imports.sh
poetry run ruff .
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
[ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I $(PYTHON_FILES)
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
前两个脚本是本仓库特有的两条硬性约束,值得单独说明:
1. scripts/check_pydantic.sh:用 git grep 搜索以 import pydantic 或 from pydantic 开头的行,一旦发现即报错退出,并提示改为从 langchain_core.pydantic_v1 导入(例如把 from pydantic import BaseModel 替换为 from langchain_core.pydantic_v1 import BaseModel)。这条规则的背景是:项目同时兼容 pydantic v1/v2 两套 API(仓库中可见 chatchat/pydantic_v1.py 与 chatchat/pydantic_v2.py 两个兼容层),直接依赖 pydantic 具体版本会破坏兼容性,因此统一收敛导入路径。贡献新功能时如果触发了该报错,按脚本提示替换导入即可。
2. scripts/lint_imports.sh:用 git grep 检查是否存在以 from chatchat. 开头的导入行,若发现则以非零状态退出。从源码结构看,项目对外发布的是 chatchat 与 langchain_chatchat 两个顶层包(见 pyproject.toml 的 packages 声明),该脚本用于约束代码内部的导入方式,避免引入不符合约定的子模块导入路径。
其后是通用的 ruff 检查、ruff format --diff(只打印 diff 不落地修改,用于校验格式是否合规)、ruff --select I(校验 import 排序)以及 mypy 类型检查。mypy 的全局配置在 pyproject.toml L207-L210:ignore_missing_imports = "True"、disallow_untyped_defs = "True",即要求函数定义带类型注解;lint_diff/lint_tests 分别使用独立的 mypy 缓存目录(.mypy_cache_test)以避免污染。
Makefile 还提供了按范围执行的变体,方便局部修改时聚焦检查:
make lint_package:仅检查chatchat包目录;make lint_tests:仅检查tests目录;make lint_diff:仅检查相对master的变更文件。
此外 Makefile 还包含拼写检查目标 spell_check/spell_fix(基于 codespell,配置见 pyproject.toml L242-L249 的 skip 与 ignore-words-list)。
本地运行测试:与 CI 对齐的测试命令
贡献文档要求修复 bug 时“尽可能在 tests/integration_tests 或 tests/unit_tests 中添加单元测试”。本地验证测试时,可以使用 Makefile 中定义的一组目标:
TEST_FILE ?= tests/unit_tests/
test tests:
poetry run pytest --disable-socket --allow-unix-socket $(TEST_FILE)
coverage:
poetry run pytest --cov --cov-config=.coveragerc \
--cov-report xml --cov-report term-missing:skip-covered $(TEST_FILE)
integration_tests:
poetry run pytest tests/integration_tests
要点:
TEST_FILE可覆盖:make test TEST_FILE=tests/unit_tests/test_xxx.py可只跑单个测试文件,适合开发循环中快速反馈;--disable-socket --allow-unix-socket:由 pyproject.toml 中的pytest-socket插件提供,强制单测禁止发起真实网络请求(仅允许 Unix socket),保证测试的确定性与离线可运行——这与“修复 bug 需可测试”的贡献要求相呼应;make coverage生成覆盖率报告,tool.coverage.run 中已排除tests/*。
pyproject.toml 的 [tool.pytest.ini_options] 还定义了全局测试行为:--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv,注册了三个自定义 marker(requires、scheduled、compile),并启用 asyncio_mode = "auto" 以支持 asyncio 测试。
tests/conftest.py 进一步实现了两套自定义机制,理解它们有助于编写符合约定的测试:
--only-extended/--only-core选项(L15-L26):配合[tool.poetry.extras]中定义的大型extended_testing依赖组(pyproject.toml L79-L146,包含beautifulsoup4、datasets、pandas周边等大量可选包)区分核心测试与扩展测试;requiresmarker(L55-L93):标注为@pytest.mark.requires("pkg")的测试在依赖包未安装时自动跳过,在--only-extended模式下则会直接失败以提醒补齐依赖。新写的测试如果依赖可选包,应使用该 marker 而非硬编码 import。
CI 检查如何复现:GitHub Actions 工作流
贡献文档提到“CI 系统会自动运行 linting 和测试”。具体实现见 .github/workflows/_test.yml:
- 固定使用 Poetry 1.7.1(
env.POETRY_VERSION),并通过自定义 action.github/actions/poetry_setup完成 Python + Poetry 安装与缓存; - 在
ubuntu-latest/windows-latest/macos-latest三平台 × Python 版本矩阵上并行执行; - 核心步骤依次为:
poetry install --with test(L41)→make test(L46); - 最有特色的一步是测试纯净性检查(L64-L75):测试结束后执行
git status并断言输出包含nothing to commit, working tree clean,即测试代码不允许在仓库中产生任何未跟踪文件(如临时生成的tests/unit_tests/config/chatchat/目录会被显式清理)。
这意味着本地写测试时也应遵循同样纪律:不要在被测代码路径或仓库目录中生成落盘文件,或确保 fixture 能自清理。该工作流还支持 workflow_dispatch 手动触发,并允许通过 working-directory 输入切换到其他子项目(默认 ./libs/chatchat-server)。
小结
本文沿 docs/contributing/code.md 的主线,覆盖了 Langchain-Chatchat 代码贡献的完整技术闭环:
- 流程:Fork & PR、按 PR 模板操作、新功能更新文档、修 bug 补充
tests/integration_tests或tests/unit_tests中的单测; - 依赖:Poetry 管理依赖,Conda/Pyenv 用户建议
poetry config virtualenvs.prefer-active-python true,注意服务端 Python 约束为>=3.10,<3.12,!=3.9.7; - 环境:
cd Langchain-Chatchat/libs/chatchat-server/后执行poetry install --with lint,test,editable 安装通过direct_url.json指向本地源码; - 格式化:
make format全量、make format_diff仅针对相对master的变更文件(ruff format+ruff --select I --fix); - Lint:
make lint五层检查链(pydantic 导入约束、from chatchat.导入约束、ruff、格式 diff 校验、mypy 类型注解要求); - 测试与 CI:
make test禁用 socket 网络请求,本地保持工作树干净以通过 CI 的纯净性断言。
按照以上链路完成本地验证后,再发起 PR,即可显著降低 CI 失败与评审往返的成本。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00