Skip to content

Commit 14085b9

Browse files
tode-rlclaude
andcommitted
fix: resolve merge conflicts and fix type errors in axons.py
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>
1 parent 8ced825 commit 14085b9

3 files changed

Lines changed: 158 additions & 4 deletions

File tree

AGENTS.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Agent Workflow Guidelines
2+
3+
## Making Code Changes
4+
5+
When making changes to the codebase, follow this workflow to ensure code quality:
6+
7+
### 1. Run Lint Before Changes
8+
9+
Before making any code changes, run the linter to establish a baseline:
10+
11+
```bash
12+
./scripts/lint
13+
```
14+
15+
This runs:
16+
- `pyright` - Type checking
17+
- `mypy` - Additional type checking
18+
- `ruff check` - Code linting
19+
- `ruff format --check` - Format checking
20+
- Import validation
21+
22+
### 2. Make Your Changes
23+
24+
Make the necessary code changes, ensuring you:
25+
- Follow existing code patterns
26+
- Update type annotations
27+
- Handle edge cases (e.g., `Omit`, `NotGiven`, `None`)
28+
- Maintain backward compatibility
29+
30+
### 3. Update Tests
31+
32+
Update or add tests for your changes:
33+
- Unit tests in `tests/`
34+
- Smoke tests in `tests/smoketests/`
35+
- Ensure tests match the new behavior
36+
37+
### 4. Run Lint After Changes
38+
39+
After making changes, run the linter again to catch any issues:
40+
41+
```bash
42+
./scripts/lint
43+
```
44+
45+
Fix any new errors or warnings that appear.
46+
47+
### 5. Run Tests
48+
49+
Run the test suite to ensure everything works:
50+
51+
```bash
52+
# Run all tests
53+
uv run pytest
54+
55+
# Run specific tests
56+
uv run pytest tests/test_axon_sse_reconnect.py -xvs
57+
58+
# Run smoke tests
59+
uv run pytest tests/smoketests/ -m smoketest
60+
```
61+
62+
### 6. Commit Changes
63+
64+
Once lint and tests pass, commit your changes:
65+
66+
```bash
67+
git add -A
68+
git commit -m "type: description
69+
70+
Detailed explanation of changes
71+
72+
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
73+
```
74+
75+
## Common Patterns
76+
77+
### Handling Optional Parameters
78+
79+
When dealing with parameters that can be omitted:
80+
81+
```python
82+
# Parameter definition
83+
def method(self, param: int | Omit = omit):
84+
...
85+
86+
# In implementation, check for Omit before using
87+
if not isinstance(param, Omit):
88+
use_param(param)
89+
else:
90+
# param was omitted, use default behavior
91+
pass
92+
```
93+
94+
### Type-Safe Transformations
95+
96+
The `transform` function automatically filters out `Omit` and `NotGiven` values:
97+
98+
```python
99+
# This dict with omitted values
100+
{"field": omit, "other": 123}
101+
102+
# Becomes this after transform
103+
{"other": 123}
104+
```
105+
106+
### Testing with Mocks
107+
108+
When testing methods that call `_get` or similar:
109+
110+
```python
111+
with patch.object(client.resource, "_get") as mock_get:
112+
mock_stream = Mock(spec=Stream)
113+
mock_get.return_value = mock_stream
114+
115+
# Your test code
116+
result = client.resource.method()
117+
118+
# Verify the call
119+
call_args = mock_get.call_args
120+
options = call_args.kwargs["options"]
121+
assert options["params"]["field"] == expected_value
122+
```
123+
124+
## Merge Conflict Resolution
125+
126+
When merging branches:
127+
128+
1. **Understand both changes** - Read the diff from both sides
129+
2. **Combine features intelligently** - Don't just pick one side
130+
3. **Update tests** - Tests may need adjustments for merged behavior
131+
4. **Fix type errors** - Merges can introduce type mismatches
132+
5. **Validate syntax** - Run `python3 -m py_compile` on changed files
133+
6. **Run full lint** - Ensure everything passes
134+
135+
## Example: Recent Merge Fix
136+
137+
The merge of `origin/main` into `feature/ts-pr-765-port` required:
138+
139+
1. **Code fix** - Handle `Omit` type properly in reconnection logic
140+
2. **Test fix** - Update assertions from `is None` to `not in dict`
141+
3. **Syntax check** - Verify Python syntax is valid
142+
4. **Type check** - Ensure mypy/pyright pass
143+
144+
See commit history for the resolution pattern.

src/runloop_api_client/resources/axons/axons.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,12 @@ def subscribe_sse(
311311

312312
def create_stream(last_sequence: str | None) -> Stream[AxonEventView]:
313313
# Use user-provided after_sequence for initial stream, then use last_sequence for reconnections
314-
sequence_to_use = after_sequence if last_sequence is None else int(last_sequence)
314+
if last_sequence is not None:
315+
sequence_to_use = int(last_sequence)
316+
elif not isinstance(after_sequence, Omit):
317+
sequence_to_use = after_sequence
318+
else:
319+
sequence_to_use = omit
315320
return self._get(
316321
path_template("/v1/axons/{id}/subscribe/sse", id=id),
317322
options=make_request_options(
@@ -621,7 +626,12 @@ async def subscribe_sse(
621626

622627
async def create_stream(last_sequence: str | None) -> AsyncStream[AxonEventView]:
623628
# Use user-provided after_sequence for initial stream, then use last_sequence for reconnections
624-
sequence_to_use = after_sequence if last_sequence is None else int(last_sequence)
629+
if last_sequence is not None:
630+
sequence_to_use = int(last_sequence)
631+
elif not isinstance(after_sequence, Omit):
632+
sequence_to_use = after_sequence
633+
else:
634+
sequence_to_use = omit
625635
return await self._get(
626636
path_template("/v1/axons/{id}/subscribe/sse", id=id),
627637
options=make_request_options(

tests/test_axon_sse_reconnect.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def empty_iter(_self: object) -> Iterator[object]:
161161
assert options["headers"]["Accept"] == "text/event-stream"
162162
assert options["headers"]["X-Custom"] == "value"
163163
assert options["params"]["param"] == "value"
164-
assert options["params"]["after_sequence"] is None
164+
assert "after_sequence" not in options["params"]
165165
assert options["timeout"] == 30.0
166166

167167

@@ -322,7 +322,7 @@ async def mock_iter():
322322
assert options["headers"]["Accept"] == "text/event-stream"
323323
assert options["headers"]["X-Custom"] == "value"
324324
assert options["params"]["param"] == "value"
325-
assert options["params"]["after_sequence"] is None
325+
assert "after_sequence" not in options["params"]
326326
assert options["timeout"] == 30.0
327327

328328

0 commit comments

Comments
 (0)