Skip to content

Commit d70b08f

Browse files
committed
fix: keep handler reconnect on the calling task on Python 3.11.
asyncio.wait_for spawned a nested task, so recovery cancelled the handler. Also clear the Ruff findings that failed CI.
1 parent 498dd7f commit d70b08f

22 files changed

Lines changed: 140 additions & 125 deletions

File tree

agentconnect/agent/session.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -949,11 +949,13 @@ async def _call(self, op: str, *args: Any, **kwargs: Any) -> dict[str, Any]:
949949
except TransportError as exc:
950950
if _should_reconnect(exc):
951951
try:
952-
await asyncio.wait_for(
953-
self._reconnect(),
954-
timeout=self._foreground_recovery_seconds(),
955-
)
956-
except asyncio.TimeoutError:
952+
# asyncio.timeout keeps reconnect on this task. wait_for()
953+
# on 3.11 wraps it in a new Task, so current_task() inside
954+
# _reconnect is not the handler and _abandon_sdk_handlers
955+
# cancels the caller.
956+
async with asyncio.timeout(self._foreground_recovery_seconds()):
957+
await self._reconnect()
958+
except TimeoutError:
957959
raise SessionError(
958960
"unavailable",
959961
"Session recovery timed out; the Runtime may already have accepted the operation",

agentconnect/index/CLIENT.md

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ client = RegistryAPIClient(
4848
connect_timeout=10.0,
4949
read_timeout=30.0,
5050
max_connections=10,
51-
max_keepalive_connections=5
51+
max_keepalive_connections=5,
5252
)
5353
```
5454

@@ -87,6 +87,7 @@ from agentconnect.index import RegistryAPIClient
8787
from agentconnect.index.registry import AgentRegistration
8888
from agentconnect.core import AgentType, InteractionMode, AgentIdentity, Capability
8989

90+
9091
async def register_agent_example():
9192
async with RegistryAPIClient() as client:
9293
# Create agent registration
@@ -98,19 +99,24 @@ async def register_agent_example():
9899
name="Data Processor",
99100
summary="Processes CSV and JSON data files",
100101
capabilities=[
101-
Capability(name="csv_processing", description="Parse and transform CSV files"),
102-
Capability(name="json_processing", description="Parse and transform JSON data")
102+
Capability(
103+
name="csv_processing", description="Parse and transform CSV files"
104+
),
105+
Capability(
106+
name="json_processing", description="Parse and transform JSON data"
107+
),
103108
],
104-
tags=["data", "processing", "csv", "json"]
109+
tags=["data", "processing", "csv", "json"],
105110
)
106-
111+
107112
# Register agent
108113
success = await client.register(agent)
109114
if success:
110115
print(f"Successfully registered {agent.agent_id}")
111116
else:
112117
print("Registration failed")
113118

119+
114120
# Run the example
115121
asyncio.run(register_agent_example())
116122
```
@@ -120,21 +126,21 @@ asyncio.run(register_agent_example())
120126
```python
121127
async def discover_agents_example():
122128
async with RegistryAPIClient() as client:
123-
124129
# Find agents using semantic search
125130
results = await client.get_by_capability_semantic(
126131
capability_description="process data files and generate reports",
127132
limit=5,
128133
similarity_threshold=0.3,
129-
filters={"tags": ["data", "reporting"]}
134+
filters={"tags": ["data", "reporting"]},
130135
)
131-
136+
132137
# Process results
133138
for agent_reg, score in results:
134139
print(f"Found: {agent_reg.name} (Score: {score:.3f})")
135140
print(f" Capabilities: {[cap.name for cap in agent_reg.capabilities]}")
136141
print(f" Tags: {agent_reg.tags}")
137142

143+
138144
asyncio.run(discover_agents_example())
139145
```
140146

@@ -143,19 +149,21 @@ asyncio.run(discover_agents_example())
143149
```python
144150
async def bulk_operations_example():
145151
async with RegistryAPIClient() as client:
146-
147152
# Get all agents from a specific organization
148153
org_agents = await client.get_by_organization("acme_corp")
149154
print(f"Found {len(org_agents)} agents from Acme Corp")
150-
155+
151156
# Get all verified agents
152157
verified = await client.get_verified_agents()
153158
print(f"Found {len(verified)} verified agents")
154-
159+
155160
# Get agents by interaction mode
156-
api_agents = await client.get_by_interaction_mode(InteractionMode.AGENT_TO_AGENT)
161+
api_agents = await client.get_by_interaction_mode(
162+
InteractionMode.AGENT_TO_AGENT
163+
)
157164
print(f"Found {len(api_agents)} A2A agents")
158165

166+
159167
asyncio.run(bulk_operations_example())
160168
```
161169

@@ -167,20 +175,20 @@ The client includes comprehensive error handling:
167175
async def error_handling_example():
168176
try:
169177
async with RegistryAPIClient() as client:
170-
171178
# This will automatically retry on network errors
172179
result = await client.get_registration("some_agent_id")
173-
180+
174181
if result is None:
175182
print("Agent not found (404)")
176183
else:
177184
print(f"Found agent: {result.name}")
178-
185+
179186
except httpx.RequestError as e:
180187
print(f"Network error after all retries: {e}")
181188
except Exception as e:
182189
print(f"Unexpected error: {e}")
183190

191+
184192
asyncio.run(error_handling_example())
185193
```
186194

@@ -211,16 +219,16 @@ The client uses `settings` from `agentconnect.config` and can be configured via
211219

212220
```python
213221
# Client configuration (available via settings.clients.registry)
214-
settings.clients.registry.base_url # Default: "http://localhost:8000"
215-
settings.clients.registry.default_timeout # Default: 30.0
216-
settings.clients.registry.connect_timeout # Default: 10.0
217-
settings.clients.registry.read_timeout # Default: 30.0
218-
settings.clients.registry.pool_timeout # Default: 5.0
219-
settings.clients.registry.max_retries # Default: 3
220-
settings.clients.registry.retry_backoff_factor # Default: 0.5
221-
settings.clients.registry.retryable_status_codes # Default: [502, 503, 504]
222-
settings.clients.registry.max_connections # Default: 10
223-
settings.clients.registry.max_keepalive_connections # Default: 5
222+
settings.clients.registry.base_url # Default: "http://localhost:8000"
223+
settings.clients.registry.default_timeout # Default: 30.0
224+
settings.clients.registry.connect_timeout # Default: 10.0
225+
settings.clients.registry.read_timeout # Default: 30.0
226+
settings.clients.registry.pool_timeout # Default: 5.0
227+
settings.clients.registry.max_retries # Default: 3
228+
settings.clients.registry.retry_backoff_factor # Default: 0.5
229+
settings.clients.registry.retryable_status_codes # Default: [502, 503, 504]
230+
settings.clients.registry.max_connections # Default: 10
231+
settings.clients.registry.max_keepalive_connections # Default: 5
224232
```
225233

226234
Example `agentconnect.yaml` configuration:
@@ -288,6 +296,7 @@ Enable debug logging for detailed HTTP request/response information:
288296

289297
```python
290298
import logging
299+
291300
logging.basicConfig(level=logging.DEBUG)
292301
293302
# Will show detailed HTTP logs

agentconnect/index/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ app = create_registry_api_app(custom)
8181
# Optionally run with Uvicorn programmatically
8282
if __name__ == "__main__":
8383
import uvicorn
84-
uvicorn.run(app, host=custom.host, port=custom.port, log_level=custom.log_level.lower())
84+
85+
uvicorn.run(
86+
app, host=custom.host, port=custom.port, log_level=custom.log_level.lower()
87+
)
8588
```
8689

8790
Preferred CLI:

agentconnect/index/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,9 @@ async def _request(
273273
or expected_status == 204
274274
): # 204 No Content
275275
# For DELETE or successful updates that don't return body, but we expect a model that might be a simple bool/dict
276-
if response_model == bool:
276+
if response_model is bool:
277277
return True
278-
if response_model == dict:
278+
if response_model is dict:
279279
return {} # common for success messages
280280
return None # Or handle as per specific endpoint needs
281281

agentconnect/index/registry/capability_discovery.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,15 @@ async def precompute_all_capability_embeddings(
289289
precompute_all_capability_embeddings as _precompute_all_capability_embeddings,
290290
)
291291

292-
capability_to_agent_map, total_points = (
293-
await _precompute_all_capability_embeddings(
294-
self._async_qdrant_client,
295-
self.COLLECTION_NAME,
296-
self._embeddings_model,
297-
agent_registrations,
298-
self._get_batch_size(),
299-
)
292+
(
293+
capability_to_agent_map,
294+
total_points,
295+
) = await _precompute_all_capability_embeddings(
296+
self._async_qdrant_client,
297+
self.COLLECTION_NAME,
298+
self._embeddings_model,
299+
agent_registrations,
300+
self._get_batch_size(),
300301
)
301302

302303
# Update capability map

agentconnect/index/registry/capability_discovery_impl/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Each indexed point includes a rich payload with metadata for filtering:
7777
"default_output_modes": ["text"],
7878
"payment_address": "0x1234...",
7979
"doc_id": "agent123_profile",
80-
"doc_type": "agent_profile"
80+
"doc_type": "agent_profile",
8181
}
8282
```
8383

@@ -111,7 +111,7 @@ discovery_service = CapabilityDiscoveryService()
111111
matching_agents = await discovery_service.find_by_capability_semantic(
112112
capability_description="translate English to French",
113113
limit=5,
114-
filters={"tags": ["translation"], "default_input_modes": ["text"]}
114+
filters={"tags": ["translation"], "default_input_modes": ["text"]},
115115
)
116116
```
117117

agentconnect/index/registry/capability_discovery_impl/indexing.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,10 +473,11 @@ async def main():
473473
print("===========[Capability to Agent Map from _generate_points]===========")
474474

475475
# Test precompute_all_capability_embeddings
476-
capability_to_agent_map_precomp, total_points_indexed = (
477-
await precompute_all_capability_embeddings(
478-
async_client, collection_name, embeddings_model, agent_registrations
479-
)
476+
(
477+
capability_to_agent_map_precomp,
478+
total_points_indexed,
479+
) = await precompute_all_capability_embeddings(
480+
async_client, collection_name, embeddings_model, agent_registrations
480481
)
481482
print("===========[Capability to Agent Map after precompute]===========")
482483
print(

agentconnect/index/registry/search/README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Converts `List[Skill]` to `List[Dict[str, str]]` with "name" and "description" k
7878

7979
```python
8080
from agentconnect.index.registry.search import (
81-
AgentSearchInput,
82-
populate_search_result_item
81+
AgentSearchInput,
82+
populate_search_result_item,
8383
)
8484

8585
# Create search input
@@ -88,14 +88,14 @@ search_input = AgentSearchInput(
8888
top_k=10,
8989
strictness=0.3,
9090
output_detail="capabilities",
91-
include_tags=["data", "analysis"]
91+
include_tags=["data", "analysis"],
9292
)
9393

9494
# Transform registry result to search result
9595
search_result = populate_search_result_item(
9696
registration=agent_registration,
9797
similarity_score=0.85,
98-
output_detail_level="capabilities"
98+
output_detail_level="capabilities",
9999
)
100100
```
101101

@@ -106,8 +106,7 @@ from agentconnect.index.registry.search import AgentSearchOutput
106106

107107
# Create output with results
108108
output = AgentSearchOutput(
109-
message="Found 3 agents matching your criteria",
110-
results=[result1, result2, result3]
109+
message="Found 3 agents matching your criteria", results=[result1, result2, result3]
111110
)
112111

113112
# Get clean JSON representation

agentconnect/prebuilt/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ attach from the Session. Conversation state for Team work is ``ctx.history``.
2222
from agentconnect.prebuilt import AIAgent, Tool
2323
from agentconnect.team import Team
2424

25+
2526
async def search_docs(query: str) -> str:
2627
return f"no hits for {query}"
2728

29+
2830
agent = AIAgent(
2931
name="researcher",
3032
model="gpt-4o-mini",

agentconnect/team/directory/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ already available.
1414
from agentconnect.agent import BaseAgent
1515
from agentconnect.team import Team
1616

17+
1718
class Reviewer(BaseAgent):
1819
profile = {
1920
"summary": "Reviews contracts for risk and missing terms.",
@@ -30,6 +31,7 @@ class Reviewer(BaseAgent):
3031
async def handle(self, msg, ctx):
3132
return "reviewed"
3233

34+
3335
team = await Team("content-squad").start()
3436
await Reviewer(name="reviewer").join(team)
3537
await Researcher(name="researcher").join(team)
@@ -65,6 +67,7 @@ Set on the Team, not on each Agent.
6567
async def embed(texts: list[str]) -> list[list[float]]:
6668
return await my_model.encode(texts)
6769

70+
6871
team = await Team("content-squad", embeddings=embed).start()
6972
```
7073

0 commit comments

Comments
 (0)