Python port of Vercel Chat SDK (synced to upstream v4.31.0). Multi-platform async chat framework.
uv sync --group dev # install dependencies
uv run pytest tests/ -q # run tests
uv run ruff check src/ tests/ scripts/ # lint
uv run ruff format src/ tests/ scripts/ # format
# Full validation — run before declaring any task done
uv run ruff check src/ tests/ scripts/ && \
uv run ruff format --check src/ tests/ scripts/ && \
uv run python scripts/audit_test_quality.py && \
uv run python scripts/verify_test_fidelity.py && \
uv run pytest tests/ --tb=short -qOur version embeds the upstream Vercel Chat version: 0.{upstream_major}.{upstream_minor}[.patch]
0.4.25= synced to upstream4.25.00.4.25.1= Python-only fix on top of4.25.00.4.26= synced to upstream4.26.00.4.26.3= Python-only fixes on top of4.26.00.4.27= synced to upstream4.27.00.4.27.1= Python-only fix on top of4.27.00.4.29= synced to upstream4.29.0(upstream never tagged 4.27/4.28 aschat@*)0.4.30= synced to upstream4.30.00.4.31= synced to upstream4.31.00.4.31.1= Python-only fixes on top of4.31.0(Slack #138/#95)UPSTREAM_PARITYconstant in__init__.py= programmatic access
src/chat_sdk/chat.py-- Chat orchestrator (handlers, routing, concurrency)src/chat_sdk/thread.py-- Thread (streaming, pagination, subscriptions)src/chat_sdk/channel.py-- Channel (thread listing, metadata)src/chat_sdk/plan.py-- Plan (PostableObject for structured task lists)src/chat_sdk/types.py-- All types (Message, Author, Adapter protocol)src/chat_sdk/adapters/-- 9 platform adapterssrc/chat_sdk/shared/-- Markdown parser, format converter, streaming renderersrc/chat_sdk/state/-- Memory, Redis, Postgres backendstests/-- 3,400+ tests
All thread IDs follow: {adapter}:{channel}:{thread}
- Slack:
slack:C123ABC:1234567890.123456 - Teams:
teams:{base64(conversationId)}:{base64(serviceUrl)} - Google Chat:
gchat:spaces/ABC123:{base64(threadName)} - Discord:
discord:{guildId}:{channelId}[:{threadId}] - GitHub:
github:owner/repo:42(PR) orgithub:owner/repo:issue:42
- Platform sends webhook → adapter verifies + parses
- Adapter calls
chat.process_message()(orprocess_action, etc.) - Chat acquires lock, deduplicates, then routes:
- Subscribed thread →
on_subscribed_messagehandlers - @mention →
on_mentionhandlers - DM →
on_direct_messagehandlers - Pattern match →
on_messagehandlers
- Subscribed thread →
- Every test must fail when the code is wrong. No
assert Truestubs, no bare truthiness checks when specific values are available, no MagicMock where AsyncMock is needed. If a test can't catch a regression, it's not a test. - Every async call must be awaited. Unawaited coroutines silently return truthy objects. Use AsyncMock (not MagicMock) in tests to surface these.
- No two tests should verify the same thing. Duplicates inflate counts without catching more bugs.
Before declaring any change ready, run the adversarial checks in docs/SELF_REVIEW.md. In short: input sweeps, emit/parse symmetry, pass-interaction, unforgeable sentinels, divergence budget, rebind/state coherence, and the pre-ship "what would an adversarial reviewer find?" question. Apply these especially to novel logic, new regex/substitution passes, and anything that lands as a divergence from upstream.
See docs/UPSTREAM_SYNC.md for the full 15-hazard guide. Key patterns:
x or default→x if x is not None else default(truthiness trap)datetime.utcnow()→datetime.now(tz=timezone.utc)(deprecated, naive)- Raw dicts to
process_*→ typed dataclasses (ActionEvent, etc.) - camelCase dispatch keys → snake_case internally, camelCase at serialization boundary
random.choicesfor tokens →secrets.token_hex- Optional deps at module level → lazy import inside methods
==for signatures →hmac.compare_digest- Validate external URLs before requests (SSRF)
chat.activate()>register_singleton()> error (3-level resolver)
See docs/UPSTREAM_SYNC.md for the full sync procedure, porting hazards, review checklist, and known non-parity list.
- Markdown parser handles common cases but is not full CommonMark
- StreamingMarkdownRenderer's _remend is simplified vs the npm
remendlibrary - No setext headings, no footnotes, no HTML nodes in the parser
CI runs scripts/audit_test_quality.py before tests. It catches phantoms,
async mock bugs, and cross-file duplicates. PRs that introduce hard failures
will not pass CI.
Fidelity check (scripts/verify_test_fidelity.py) verifies every TS
it("...") in the mapped core files has a matching Python def test_*(),
pinned to chat@4.31.0 (matches UPSTREAM_PARITY; upstream never tagged
chat@4.27.0/chat@4.28.0). The MAPPING dict in that script is the
authoritative scope list — extending it to the remaining unmapped
packages/chat/src/*.test.ts files is tracked as issue #78.
CI runs --strict (see .github/workflows/lint.yml):
any missing translation in a mapped file fails the build, and a missing
upstream checkout also fails (the script exits non-zero when any mapped
TS file isn't found). Baseline mode (the default without --strict) is
retained for local workflows where a few ports land in flight —
regenerate via --update-baseline after documenting intentional
divergence in docs/UPSTREAM_SYNC.md.
Before the fidelity check can run locally, clone the pinned upstream
checkout (same command CI uses in lint.yml):
git clone --depth 1 --branch chat@4.31.0 \
https://github.com/vercel/chat.git /tmp/vercel-chatThen TS_ROOT=/tmp/vercel-chat uv run python scripts/verify_test_fidelity.py --strict.