Skip to content

feat(thread): port getParticipants + close 8 fidelity gaps#64

Merged
patrick-chinchill merged 2 commits into
mainfrom
get-participants-54
Apr 24, 2026
Merged

feat(thread): port getParticipants + close 8 fidelity gaps#64
patrick-chinchill merged 2 commits into
mainfrom
get-participants-54

Conversation

@patrick-chinchill
Copy link
Copy Markdown
Collaborator

@patrick-chinchill patrick-chinchill commented Apr 24, 2026

Summary

  • Ports Thread.get_participants() from upstream TS (vercel-chat/packages/chat/src/thread.ts:359). Returns unique non-bot, non-self authors who've posted in the thread; seeds from current_message.author when eligible, iterates all_messages() oldest-first, dedupes on user_id. Added to the Thread Protocol.
  • Closes 8 fidelity gaps: 4 [getParticipants] tests from thread.test.ts + 4 [thread] factory tests from chat.test.ts (existing-behavior coverage for Chat.thread(id) that had no Python equivalent).

Refs #54. First of several upstream-parity PRs targeting bundled release 0.4.26.2. CHANGELOG entry lands under ## Unreleased.

Fidelity impact

verify_test_fidelity.py: 40 missing → 32 missing (closed exactly the 8 covered here). Remaining gaps trace to #50 (Options Load), #52 (rehydrate), and StreamingPlan feature work (#53).

Test plan

  • uv run pytest tests/test_thread_faithful.py::TestGetParticipants -q — 4 passed
  • uv run pytest tests/test_chat_faithful.py::TestThreadFactory -q — 4 passed
  • uv run ruff check src/ tests/ scripts/ — clean
  • uv run ruff format --check src/ tests/ scripts/ — clean
  • uv run python scripts/audit_test_quality.py — 0 hard failures
  • uv run python scripts/verify_test_fidelity.py — 8 gaps closed
  • uv run pytest tests/ --tb=short -q — 3553 passed, 2 skipped, 0 failed

Notes

  • Issue Port Thread.getParticipants() method #54's body sketched a simplified implementation that omitted is_me filtering and used messages() (newest-first). I ported the real upstream: filters both is_me and is_bot, and uses all_messages() (oldest-first) to match the map-seeding order.
  • No version bump — accumulating under ## Unreleased per the 0.4.26.1-style bundled-release pattern. A separate release PR will flip to ## 0.4.26.2 (date) + bump pyproject.toml once 2–3 items land.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added get_participants() method to threads for retrieving unique message participants, automatically excluding bots and the current user.
  • Tests

    • Added test coverage for thread operations, including creation, validation, and error handling.
    • Added tests for participant retrieval to verify filtering and deduplication behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Warning

Rate limit exceeded

@patrick-chinchill has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f84f6ef0-db02-4a69-b799-e6ee2c8e4ef4

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec8020 and 1523d27.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/chat_sdk/thread.py
  • src/chat_sdk/types.py
  • tests/test_chat_faithful.py
  • tests/test_thread_faithful.py
📝 Walkthrough

Walkthrough

A new get_participants() method is added to the Thread API that returns unique authors who are neither bots nor the current user. The implementation seeds from the current message's author when eligible, then deduplicates across all messages by user ID. Comprehensive tests validate filtering and error-handling behavior.

Changes

Cohort / File(s) Summary
Thread API Addition
src/chat_sdk/types.py, src/chat_sdk/thread.py
Introduces get_participants() method on the Thread interface and ThreadImpl implementation, filtering unique non-bot authors and excluding the current user.
Tests
tests/test_chat_faithful.py, tests/test_thread_faithful.py
Adds comprehensive test coverage for thread creation/posting behavior and get_participants() participant selection, deduplication, bot exclusion, and empty-list handling.
Documentation
CHANGELOG.md
Documents upstream parity work for get_participants() with participant-selection semantics and test suite porting for chat.thread(id) behavior.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

  • Port Thread.getParticipants() method #54: Implements the Thread.get_participants() API with identical participant-selection and deduplication logic, porting corresponding TypeScript test coverage for upstream parity.

Poem

🐰 A method hops to life so neat,
Filtering authors we shall meet,
No bots, no self, just humans true,
Deduped and sorted, tried and new! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: porting Thread.get_participants() and closing 8 fidelity gaps by porting upstream tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch get-participants-54

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/chat_sdk/types.py

async def get_participants(self) -> list[Author]:
"""Return unique non-bot, non-self authors who've posted in the thread."""
...
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False positive — this is the standard typing.Protocol method body per PEP 544. Consistent with every other Protocol method in this file.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_thread_faithful.py (1)

2585-2616: Add an explicit self-only exclusion test (is_me=True, is_bot=False).

Current bot-focused case won’t fail if is_me filtering regresses while bot filtering remains intact.

Suggested test addition
+    `@pytest.mark.asyncio`
+    async def test_should_exclude_self_messages_even_when_not_bot(self):
+        adapter = create_mock_adapter()
+        state = create_mock_state()
+
+        self_human_msg = create_test_message(
+            "self-1",
+            "my own message",
+            author=Author(user_id="U_SELF", user_name="me", full_name="Me", is_bot=False, is_me=True),
+        )
+        other_human_msg = create_test_message(
+            "other-1",
+            "other user",
+            author=Author(user_id="U1", user_name="alice", full_name="Alice", is_bot=False, is_me=False),
+        )
+
+        adapter.fetch_messages = AsyncMock(  # type: ignore[assignment]
+            return_value=FetchResult(messages=[self_human_msg, other_human_msg], next_cursor=None)
+        )
+
+        thread = _make_thread(adapter, state)
+        participants = await thread.get_participants()
+
+        assert [p.user_id for p in participants] == ["U1"]

As per coding guidelines, "Every test must fail when the code is wrong."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_thread_faithful.py` around lines 2585 - 2616, The test currently
only verifies bot exclusion (is_bot=True) but misses a case where a message is
from self (is_me=True, is_bot=False), so add an extra assertion within
test_should_exclude_bot_messages (or a new small test) that includes a
create_test_message with Author(... is_bot=False, is_me=True) in the fetched
messages via adapter.fetch_messages and assert that thread.get_participants()
still excludes that self message (i.e., participants length remains 1 and the
remaining user_id is "U1"); update the AsyncMock return_value to include this
self-only message alongside the human message to ensure is_me-based filtering is
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_thread_faithful.py`:
- Around line 2585-2616: The test currently only verifies bot exclusion
(is_bot=True) but misses a case where a message is from self (is_me=True,
is_bot=False), so add an extra assertion within test_should_exclude_bot_messages
(or a new small test) that includes a create_test_message with Author(...
is_bot=False, is_me=True) in the fetched messages via adapter.fetch_messages and
assert that thread.get_participants() still excludes that self message (i.e.,
participants length remains 1 and the remaining user_id is "U1"); update the
AsyncMock return_value to include this self-only message alongside the human
message to ensure is_me-based filtering is covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 04ddb6fb-bd92-4743-bde9-5dc34ca880a1

📥 Commits

Reviewing files that changed from the base of the PR and between d23b6d9 and 9ec8020.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/chat_sdk/thread.py
  • src/chat_sdk/types.py
  • tests/test_chat_faithful.py
  • tests/test_thread_faithful.py

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ec8020ac9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/chat_sdk/thread.py
if (
self._current_message is not None
and not self._current_message.author.is_me
and not self._current_message.author.is_bot
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare is_bot explicitly against True

Author.is_bot is typed as bool | "unknown", but this check relies on truthiness, so authors with is_bot="unknown" are treated as bots and excluded from participants. That causes get_participants() to silently drop valid users in adapters that emit unknown bot status (for example Telegram fallback authors), so the method can return incomplete results even when non-self humans posted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional upstream parity — in TS, !author.isBot also excludes the string "unknown" because non-empty strings are truthy in JS. Explicit is True would diverge from vercel/chat. See thread.ts:359-384.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the Thread.get_participants() method to retrieve unique, non-bot, and non-self authors from a thread, aligning with the upstream TypeScript SDK. It also includes new tests for the thread factory and participant retrieval. Feedback was provided regarding the truthiness check for bot status, which may inadvertently exclude authors with an "unknown" status if not explicitly checked against a boolean value.

Comment thread src/chat_sdk/thread.py
seen[self._current_message.author.user_id] = self._current_message.author

async for msg in self.all_messages():
if msg.author.is_me or msg.author.is_bot or msg.author.user_id in seen:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check msg.author.is_bot will evaluate to True if the value is the string "unknown", causing authors with unknown bot status to be excluded from the participants list. If the intention is to only exclude confirmed bots, consider using msg.author.is_bot is True. However, if this behavior is intended to maintain strict parity with the TypeScript SDK (where !author.isBot also excludes 'unknown'), then the current implementation is correct.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional upstream parity — in TS, !author.isBot on is_bot="unknown" also evaluates false (non-empty strings truthy). Python's if ... or author.is_bot: mirrors that. See upstream thread.ts:359-384.

patrick-chinchill added a commit that referenced this pull request Apr 24, 2026
Addresses bot review feedback on PR #64 — the existing bot-exclusion
test doesn't isolate the is_me=True, is_bot=False path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@patrick-chinchill
Copy link
Copy Markdown
Collaborator Author

Review verdict: LGTM. Refreshed and reviewed latest head 431c5b6 against main. No issues found in getParticipants/thread factory changes. Verification: TestGetParticipants + TestThreadFactory passed (9 passed), full faithful chat/thread slice passed (232 passed, 2 skipped), and ruff passed on touched files. Formal GitHub approval is blocked because the authenticated account owns this PR.

patrick-chinchill and others added 2 commits April 24, 2026 02:42
Adds Thread.get_participants() mirroring upstream TS Thread.getParticipants()
(vercel-chat/packages/chat/src/thread.ts:359). Returns unique non-bot, non-self
authors who've posted in the thread — seeds from current_message.author when
eligible, then iterates all_messages() oldest-first and dedupes on user_id.
Added to the Thread Protocol so consumers typed against the interface can call
it.

Also ports the 4 [getParticipants] tests from thread.test.ts and the 4 [thread]
factory tests from chat.test.ts (existing-behavior coverage for Chat.thread(id)
that had no Python equivalent). Closes 8 fidelity gaps; verify_test_fidelity
now shows 32 missing, down from 40.

Changelog entry under Unreleased — will batch into 0.4.26.2 alongside other
Tier 1/2 parity fixes in flight.

Refs #54.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses bot review feedback on PR #64 — the existing bot-exclusion
test doesn't isolate the is_me=True, is_bot=False path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@patrick-chinchill patrick-chinchill merged commit a31d0fc into main Apr 24, 2026
10 checks passed
patrick-chinchill added a commit that referenced this pull request Apr 24, 2026
Parity catch-up release for upstream chat@4.26.0. Bundles 8 PRs (#64 #65 #66 #67 #73 #74 #75 #76) + small followup cleanups. See CHANGELOG.md for details.
patrick-chinchill pushed a commit that referenced this pull request May 7, 2026
Per gemini-code-assist review on PR #83. Without the repo prefix, GitHub
auto-links the upstream PR numbers to local PRs in chat-sdk-python, which
collides with the local refs (#64, #66, #67, #74, #82) elsewhere in the
file. Use vercel/chat#NNN so the upstream refs link correctly.

https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant