Skip to content

feat: Client updates for Axon SSE methods#778

Open
tode-rl wants to merge 11 commits intomainfrom
feature/ts-pr-765-port
Open

feat: Client updates for Axon SSE methods#778
tode-rl wants to merge 11 commits intomainfrom
feature/ts-pr-765-port

Conversation

@tode-rl
Copy link
Copy Markdown
Contributor

@tode-rl tode-rl commented Apr 1, 2026

No description provided.

tode-rl added 5 commits April 1, 2026 16:31
- Create CancellationToken class with sync/async event support
- Update poll_until() and async_poll_until() with cancellation_token parameter
- Add cancellable sleep using threading.Event.wait() and asyncio.wait_for()
- Update PollingRequestOptions TypedDict with cancellation_token field
- Propagate cancellation_token through Blueprint and ScenarioRun polling methods

Part of porting TypeScript PR #765 features to Python SDK.
- Update Blueprints: await_build_complete, create_and_await_build_complete
- Update ScenarioRuns: await_scored, score_and_await
- Update Devboxes: await_running, await_suspended, create_and_await_running
- Update DiskSnapshots: await_completed
- Update Executions: await_completed
- Update SDK wrappers: ScenarioRun.await_env_ready
- Add comprehensive docstrings with PollingCancelled exception

All methods support both sync and async variants.
Part of porting TypeScript PR #765 features to Python SDK.
- Update AsyncScenarioRun.await_env_ready to pass cancellation_token
- Completes cancellation support across all SDK polling operations

Part of porting TypeScript PR #765 features to Python SDK.
- Add AxonSubscribeSseParams with after_sequence parameter (internal use only)
- Wrap subscribe_sse() with ReconnectingStream/AsyncReconnectingStream
- Automatically resume from last received event on timeout using sequence-based offset
- Add opt-out via RAW_RESPONSE_HEADER for users who want raw streams
- after_sequence is handled internally by reconnector, not exposed in public API

Per code review feedback from @dines-rl on TypeScript PR #765:
'after_sequence should just be a variable in the reconnector for follow-up'

Part of porting TypeScript PR #765 features to Python SDK.
Add extensive test coverage for:

1. CancellationToken class (test_cancellation.py):
   - Thread-safe cancellation behavior
   - Sync and async event handling
   - Lazy async event creation
   - raise_if_cancelled() method
   - Multiple tokens independence
   - Cross-thread cancellation

2. Polling with cancellation (test_polling.py):
   - Cancellation before first poll
   - Cancellation during polling loop
   - Cancellation during sleep with immediate wake-up
   - Cancellation with error handlers
   - Backward compatibility (None token)
   - Multiple polls with same token

3. Async polling with cancellation (test_polling_async.py):
   - All sync tests adapted for async
   - Concurrent polling with shared token
   - Cancellation from different async tasks
   - asyncio.TimeoutError handling

4. SSE auto-reconnect for Axons (test_axon_sse_reconnect.py):
   - ReconnectingStream/AsyncReconnectingStream usage
   - Sequence-based resumption after disconnect
   - RAW_RESPONSE_HEADER opt-out mechanism
   - Missing/None sequence handling
   - Request options preservation
   - AxonSubscribeSseParams structure

Total: 60+ new test cases ensuring robustness of both features.

Part of porting TypeScript PR #765 features to Python SDK.
@tode-rl tode-rl requested a review from alb-rl April 1, 2026 23:50
Port verification and testing from TypeScript PR #767 which fixed slow list
endpoints that were auto-paginating through all pages.

## Analysis Results

The Python SDK does NOT suffer from the bug that affected TypeScript because:
- Python implementation uses direct property access (`page.items`)
- TypeScript was using async iteration (`for await`) which auto-paginated
- Our existing unit tests already used correct mocking patterns

## What Was Added

New smoke tests in `tests/smoketests/sdk/test_list_pagination.py`:
- 28 comprehensive tests (15 async, 13 sync)
- Tests all 13 resource types (agents, devboxes, blueprints, etc.)
- Verifies `list(limit=N)` returns at most N items
- Ensures no auto-pagination occurs
- Includes data creation test to verify with actual API calls

## Verification

All SDK list methods verified correct:
✅ AsyncDevboxOps.list() - accesses page.devboxes
✅ AsyncSnapshotOps.list() - accesses page.snapshots
✅ AsyncBlueprintOps.list() - accesses page.blueprints
✅ AsyncStorageObjectOps.list() - accesses page.objects
✅ AsyncAxonOps.list() - accesses result.axons
✅ AsyncScorerOps.list() - accesses page.scorers
✅ AsyncAgentOps.list() - accesses page.agents
✅ AsyncScenarioOps.list() - accesses page.scenarios
✅ AsyncBenchmarkOps.list() - accesses page.benchmarks
✅ AsyncNetworkPolicyOps.list() - accesses page.network_policies
✅ AsyncGatewayConfigOps.list() - accesses page.gateway_configs
✅ AsyncMcpConfigOps.list() - accesses page.mcp_configs
✅ AsyncSecretOps.list() - accesses result.secrets
(+ all sync equivalents)

## Benefits

✅ Faster list results - only fetches requested page
✅ Fewer API requests - no unnecessary pagination
✅ Better resource usage - respects limit parameter
✅ Documented behavior - tests serve as specification
✅ Regression prevention - ensures future changes maintain correctness

## Related

- TypeScript PR: runloopai/api-client-ts#767
- Detailed analysis: PR_767_PORT_SUMMARY.md
- Code comparison: IMPLEMENTATION_COMPARISON.md
- Quick reference: PR_767_PORT.md

## Testing

Run the new tests:
```bash
uv run pytest tests/smoketests/sdk/test_list_pagination.py -v
```

No source code changes required - Python implementation already correct.
@tode-rl
Copy link
Copy Markdown
Contributor Author

tode-rl commented Apr 2, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

tode-rl and others added 5 commits April 1, 2026 20:52
Resolved conflicts in src/runloop_api_client/resources/axons/axons.py by:
- Keeping auto-reconnection logic from feature branch (ReconnectingStream)
- Adding support for user-provided after_sequence parameter from main
- Initial stream uses user's after_sequence, reconnections use last received sequence
- Both sync and async subscribe_sse methods now properly support both features

Changes from origin/main (v1.15.0):
- Updated version to 1.15.0
- Added axon_subscribe_sse_params type
- Updated benchmark_runs and related tests

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After merging origin/main, fixed type handling and test assertions:

1. Fixed type handling in subscribe_sse():
   - Properly handle after_sequence parameter of type int | Omit
   - When last_sequence is provided (reconnection), use it as int
   - When user provides after_sequence, use it
   - When neither (default), use omit which gets filtered by transform

2. Fixed test assertions in test_axon_sse_reconnect.py:
   - Changed from `options["params"]["after_sequence"] is None`
   - To `"after_sequence" not in options["params"]`
   - Reason: transform() filters out Omit values, they don't become None

3. Created AGENTS.md:
   - Documents workflow for making code changes
   - Emphasizes running lint before and after changes
   - Includes common patterns and merge conflict resolution

The merge combined two features:
- Auto-reconnection (from feature branch)
- User-provided after_sequence parameter (from main v1.15.0)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed 3 mypy errors:
1. Removed duplicate import of axon_subscribe_sse_params on line 32
2. Added explicit type annotation for sequence_to_use: int | Omit (sync)
3. Added explicit type annotation for sequence_to_use: int | Omit (async)

This allows the variable to hold either an int or an Omit value,
which is then properly filtered by the transform() function.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Run ruff format to fix code style in axons.py
- Update AGENTS.md to include format step in workflow
- Document that format should be run after lint and before tests

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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