agentex-sdk 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "36834357",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "from agentex import Agentex\n",
11
+ "\n",
12
+ "client = Agentex(base_url=\"http://localhost:5003\")"
13
+ ]
14
+ },
15
+ {
16
+ "cell_type": "code",
17
+ "execution_count": null,
18
+ "id": "d1c309d6",
19
+ "metadata": {},
20
+ "outputs": [],
21
+ "source": [
22
+ "AGENT_NAME = \"{{ agent_name }}\""
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
+ "id": "9f6e6ef0",
29
+ "metadata": {},
30
+ "outputs": [],
31
+ "source": [
32
+ "# (REQUIRED) Create a new task. For Agentic agents, you must create a task for messages to be associated with.\n",
33
+ "\n",
34
+ "from typing import cast\n",
35
+ "import uuid\n",
36
+ "\n",
37
+ "from agentex.types import Task\n",
38
+ "\n",
39
+ "TASK_ID = str(uuid.uuid4())[:8]\n",
40
+ "\n",
41
+ "rpc_response = client.agents.rpc_by_name(\n",
42
+ " agent_name=AGENT_NAME,\n",
43
+ " method=\"task/create\",\n",
44
+ " params={\n",
45
+ " \"name\": f\"{TASK_ID}-task\",\n",
46
+ " \"params\": {}\n",
47
+ " }\n",
48
+ ")\n",
49
+ "\n",
50
+ "task = cast(Task, rpc_response.result)\n",
51
+ "print(task)"
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "code",
56
+ "execution_count": null,
57
+ "id": "b03b0d37",
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": [
61
+ "# Test non streaming response\n",
62
+ "from typing import cast\n",
63
+ "from agentex.types import Event\n",
64
+ "\n",
65
+ "# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
66
+ "# - TextContent: A message with just text content \n",
67
+ "# - DataContent: A message with JSON-serializable data content\n",
68
+ "# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
69
+ "# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
70
+ "\n",
71
+ "# When processing the message/send response, if you are expecting more than TextContent, such as DataContent, ToolRequestContent, or ToolResponseContent, you can process them as well\n",
72
+ "\n",
73
+ "rpc_response = client.agents.rpc_by_name(\n",
74
+ " agent_name=AGENT_NAME,\n",
75
+ " method=\"event/send\",\n",
76
+ " params={\n",
77
+ " \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
78
+ " \"task_id\": task.id,\n",
79
+ " }\n",
80
+ ")\n",
81
+ "\n",
82
+ "event = cast(Event, rpc_response.result)\n",
83
+ "print(event)"
84
+ ]
85
+ },
86
+ {
87
+ "cell_type": "code",
88
+ "execution_count": null,
89
+ "id": "a6927cc0",
90
+ "metadata": {},
91
+ "outputs": [],
92
+ "source": [
93
+ "from agentex.lib.utils.dev_tools import subscribe_to_async_task_messages\n",
94
+ "\n",
95
+ "task_messages = subscribe_to_async_task_messages(\n",
96
+ " client=client,\n",
97
+ " task=task, \n",
98
+ " only_after_timestamp=event.created_at, \n",
99
+ " print_messages=True,\n",
100
+ " rich_print=True,\n",
101
+ " timeout=5,\n",
102
+ ")"
103
+ ]
104
+ }
105
+ ],
106
+ "metadata": {
107
+ "kernelspec": {
108
+ "display_name": ".venv",
109
+ "language": "python",
110
+ "name": "python3"
111
+ },
112
+ "language_info": {
113
+ "codemirror_mode": {
114
+ "name": "ipython",
115
+ "version": 3
116
+ },
117
+ "file_extension": ".py",
118
+ "mimetype": "text/x-python",
119
+ "name": "python",
120
+ "nbconvert_exporter": "python",
121
+ "pygments_lexer": "ipython3",
122
+ "version": "3.12.9"
123
+ }
124
+ },
125
+ "nbformat": 4,
126
+ "nbformat_minor": 5
127
+ }
@@ -7,13 +7,13 @@ from typing import Annotated, Any
7
7
  import redis.asyncio as redis
8
8
  from fastapi import Depends
9
9
 
10
- from agentex.lib.core.adapters.streams.port import EventStreamRepository
10
+ from agentex.lib.core.adapters.streams.port import StreamRepository
11
11
  from agentex.lib.utils.logging import make_logger
12
12
 
13
13
  logger = make_logger(__name__)
14
14
 
15
15
 
16
- class RedisEventStreamRepository(EventStreamRepository):
16
+ class RedisStreamRepository(StreamRepository):
17
17
  """
18
18
  A simplified Redis implementation of the EventStreamRepository interface.
19
19
  Optimized for text/JSON streaming with SSE.
@@ -123,6 +123,6 @@ class RedisEventStreamRepository(EventStreamRepository):
123
123
  raise
124
124
 
125
125
 
126
- DRedisEventStreamRepository = Annotated[
127
- RedisEventStreamRepository | None, Depends(RedisEventStreamRepository)
126
+ DRedisStreamRepository = Annotated[
127
+ RedisStreamRepository | None, Depends(RedisStreamRepository)
128
128
  ]
@@ -3,7 +3,7 @@ from collections.abc import AsyncIterator
3
3
  from typing import Any
4
4
 
5
5
 
6
- class EventStreamRepository(ABC):
6
+ class StreamRepository(ABC):
7
7
  """
8
8
  Interface for event streaming repositories.
9
9
  Used to publish and subscribe to event streams.
@@ -2,7 +2,7 @@ import json
2
2
  from typing import Literal, cast
3
3
 
4
4
  from agentex import AsyncAgentex
5
- from agentex.lib.core.adapters.streams.port import EventStreamRepository
5
+ from agentex.lib.core.adapters.streams.port import StreamRepository
6
6
  from agentex.lib.types.task_message_updates import (
7
7
  TaskMessageDelta,
8
8
  TaskMessageUpdate,
@@ -22,7 +22,6 @@ from agentex.types.task_message import (
22
22
  TaskMessage,
23
23
  TaskMessageContent,
24
24
  )
25
- from agentex.types.task_message_content_param import TaskMessageContentParam
26
25
  from agentex.types.text_content import TextContent
27
26
  from agentex.types.tool_request_content import ToolRequestContent
28
27
  from agentex.types.tool_response_content import ToolResponseContent
@@ -221,7 +220,7 @@ class StreamingService:
221
220
  def __init__(
222
221
  self,
223
222
  agentex_client: AsyncAgentex,
224
- stream_repository: EventStreamRepository,
223
+ stream_repository: StreamRepository,
225
224
  ):
226
225
  self._agentex_client = agentex_client
227
226
  self._stream_repository = stream_repository
@@ -2,7 +2,7 @@ from scale_gp import SGPClient, SGPClientError
2
2
 
3
3
  from agentex import AsyncAgentex
4
4
  from agentex.lib.core.adapters.llm.adapter_litellm import LiteLLMGateway
5
- from agentex.lib.core.adapters.streams.adapter_redis import RedisEventStreamRepository
5
+ from agentex.lib.core.adapters.streams.adapter_redis import RedisStreamRepository
6
6
  from agentex.lib.core.services.adk.acp.acp import ACPService
7
7
  from agentex.lib.core.services.adk.agent_task_tracker import AgentTaskTrackerService
8
8
  from agentex.lib.core.services.adk.events import EventsService
@@ -57,7 +57,7 @@ def get_all_activities(sgp_client=None):
57
57
  sgp_client = None
58
58
 
59
59
  llm_gateway = LiteLLMGateway()
60
- stream_repository = RedisEventStreamRepository()
60
+ stream_repository = RedisStreamRepository()
61
61
  agentex_client = AsyncAgentex()
62
62
  tracer = AsyncTracer(agentex_client)
63
63
 
@@ -0,0 +1,9 @@
1
+ """Development tools for AgentEx."""
2
+
3
+ from .async_messages import print_task_message, print_task_message_update, subscribe_to_async_task_messages
4
+
5
+ __all__ = [
6
+ "print_task_message",
7
+ "print_task_message_update",
8
+ "subscribe_to_async_task_messages",
9
+ ]
@@ -0,0 +1,386 @@
1
+ """
2
+ Development utility for subscribing to async task messages with streaming support.
3
+
4
+ This module provides utilities to read existing messages from a task and subscribe
5
+ to new streaming messages, handling mid-stream connections gracefully.
6
+ """
7
+
8
+ import json
9
+ from datetime import datetime, timezone
10
+ from typing import List, Optional
11
+
12
+ from yaspin.core import Yaspin
13
+
14
+ from agentex import Agentex
15
+ from agentex.types import Task, TaskMessage, TextContent, ToolRequestContent, ToolResponseContent
16
+ from agentex.types.task_message_update import (
17
+ TaskMessageUpdate,
18
+ StreamTaskMessageStart,
19
+ StreamTaskMessageDelta,
20
+ StreamTaskMessageFull,
21
+ StreamTaskMessageDone
22
+ )
23
+ from agentex.types.text_delta import TextDelta
24
+
25
+ from rich.console import Console
26
+ from rich.panel import Panel
27
+ from rich.markdown import Markdown
28
+ from yaspin import yaspin
29
+
30
+
31
+ def print_task_message(
32
+ message: TaskMessage,
33
+ print_messages: bool = True,
34
+ rich_print: bool = True,
35
+ ) -> None:
36
+ """
37
+ Print a task message in a formatted way.
38
+
39
+ Args:
40
+ message: The task message to print
41
+ print_messages: Whether to actually print the message (for debugging)
42
+ rich_print: Whether to use rich to print the message
43
+ """
44
+ if not print_messages:
45
+ return
46
+
47
+ # Skip empty messages
48
+ if isinstance(message.content, TextContent) and not message.content.content.strip():
49
+ return
50
+
51
+ timestamp = message.created_at.strftime("%m/%d/%Y %H:%M:%S") if message.created_at else "N/A"
52
+
53
+ console = None
54
+ if rich_print:
55
+ console = Console(width=80) # Fit better in Jupyter cells
56
+
57
+ if isinstance(message.content, TextContent):
58
+ content = message.content.content
59
+ content_type = "text"
60
+ elif isinstance(message.content, ToolRequestContent):
61
+ tool_name = message.content.name
62
+ tool_args = message.content.arguments
63
+
64
+ # Format arguments as pretty JSON
65
+ try:
66
+ if isinstance(tool_args, str):
67
+ parsed_args = json.loads(tool_args)
68
+ formatted_args = json.dumps(parsed_args, indent=2)
69
+ else:
70
+ formatted_args = json.dumps(tool_args, indent=2)
71
+ content = f"🔧 **Tool Request: {tool_name}**\n\n**Arguments:**\n```json\n{formatted_args}\n```"
72
+ except (json.JSONDecodeError, TypeError):
73
+ content = f"🔧 **Tool Request: {tool_name}**\n\n**Arguments:**\n```json\n{tool_args}\n```"
74
+
75
+ content_type = "tool_request"
76
+ elif isinstance(message.content, ToolResponseContent):
77
+ tool_name = message.content.name
78
+ tool_response = message.content.content
79
+
80
+ # Try to parse and format JSON response nicely
81
+ try:
82
+ if isinstance(tool_response, str):
83
+ parsed_response = json.loads(tool_response)
84
+ formatted_json = json.dumps(parsed_response, indent=2)
85
+ content = f"✅ **Tool Response: {tool_name}**\n\n**Response:**\n```json\n{formatted_json}\n```"
86
+ else:
87
+ formatted_json = json.dumps(tool_response, indent=2)
88
+ content = f"✅ **Tool Response: {tool_name}**\n\n**Response:**\n```json\n{formatted_json}\n```"
89
+ except (json.JSONDecodeError, TypeError):
90
+ # If it's not valid JSON, display as text
91
+ if isinstance(tool_response, str):
92
+ # Try to extract text content if it's a JSON string with text field
93
+ try:
94
+ parsed = json.loads(tool_response)
95
+ if isinstance(parsed, dict) and "text" in parsed:
96
+ text_content = str(parsed["text"])
97
+ content = f"✅ **Tool Response: {tool_name}**\n\n{text_content}"
98
+ else:
99
+ content = f"✅ **Tool Response: {tool_name}**\n\n{tool_response}"
100
+ except json.JSONDecodeError:
101
+ content = f"✅ **Tool Response: {tool_name}**\n\n{tool_response}"
102
+ else:
103
+ content = f"✅ **Tool Response: {tool_name}**\n\n{tool_response}"
104
+
105
+ content_type = "tool_response"
106
+ else:
107
+ content = f"{type(message.content).__name__}: {message.content}"
108
+ content_type = "other"
109
+
110
+ if rich_print and console:
111
+ author_color = "bright_cyan" if message.content.author == "user" else "green"
112
+ title = f"[bold {author_color}]{message.content.author.upper()}[/bold {author_color}] [{timestamp}]"
113
+
114
+ # Use different border styles for tool messages
115
+ if content_type == "tool_request":
116
+ border_style = "yellow"
117
+ elif content_type == "tool_response":
118
+ border_style = "bright_green"
119
+ else:
120
+ border_style = author_color
121
+
122
+ panel = Panel(Markdown(content), title=title, border_style=border_style, width=80)
123
+ console.print(panel)
124
+ else:
125
+ title = f"{message.content.author.upper()} [{timestamp}]"
126
+ print(f"{title}\n{content}\n")
127
+
128
+
129
+ def print_task_message_update(
130
+ task_message_update: TaskMessageUpdate,
131
+ print_messages: bool = True,
132
+ rich_print: bool = True,
133
+ show_deltas: bool = True,
134
+ ) -> None:
135
+ """
136
+ Print a task message update in a formatted way.
137
+
138
+ This function handles different types of TaskMessageUpdate objects:
139
+ - StreamTaskMessageStart: Shows start indicator
140
+ - StreamTaskMessageDelta: Shows deltas in real-time (if show_deltas=True)
141
+ - StreamTaskMessageFull: Shows complete message content
142
+ - StreamTaskMessageDone: Shows completion indicator
143
+
144
+ Args:
145
+ task_message_update: The TaskMessageUpdate object to print
146
+ print_messages: Whether to actually print the message (for debugging)
147
+ rich_print: Whether to use rich formatting
148
+ show_deltas: Whether to show delta updates in real-time
149
+ """
150
+ if not print_messages:
151
+ return
152
+
153
+ console = None
154
+ if rich_print:
155
+ console = Console(width=80)
156
+
157
+ if isinstance(task_message_update, StreamTaskMessageStart):
158
+ if rich_print and console:
159
+ console.print("🚀 [cyan]Agent started responding...[/cyan]")
160
+ else:
161
+ print("🚀 Agent started responding...")
162
+
163
+ elif isinstance(task_message_update, StreamTaskMessageDelta):
164
+ if show_deltas and task_message_update.delta:
165
+ if isinstance(task_message_update.delta, TextDelta):
166
+ print(task_message_update.delta.text_delta, end="", flush=True)
167
+ elif rich_print and console:
168
+ console.print(f"[yellow]Non-text delta: {type(task_message_update.delta).__name__}[/yellow]")
169
+ else:
170
+ print(f"Non-text delta: {type(task_message_update.delta).__name__}")
171
+
172
+ elif isinstance(task_message_update, StreamTaskMessageFull):
173
+ if isinstance(task_message_update.content, TextContent):
174
+ timestamp = datetime.now().strftime("%m/%d/%Y %H:%M:%S")
175
+
176
+ if rich_print and console:
177
+ author_color = "bright_cyan" if task_message_update.content.author == "user" else "green"
178
+ title = f"[bold {author_color}]{task_message_update.content.author.upper()}[/bold {author_color}] [{timestamp}]"
179
+ panel = Panel(Markdown(task_message_update.content.content), title=title, border_style=author_color, width=80)
180
+ console.print(panel)
181
+ else:
182
+ title = f"{task_message_update.content.author.upper()} [{timestamp}]"
183
+ print(f"\n{title}\n{task_message_update.content.content}\n")
184
+ else:
185
+ content_type = type(task_message_update.content).__name__
186
+ if rich_print and console:
187
+ console.print(f"[yellow]Non-text content: {content_type}[/yellow]")
188
+ else:
189
+ print(f"Non-text content: {content_type}")
190
+
191
+ else: # StreamTaskMessageDone
192
+ if rich_print and console:
193
+ console.print("\n✅ [green]Agent finished responding.[/green]")
194
+ else:
195
+ print("\n✅ Agent finished responding.")
196
+
197
+
198
+ def subscribe_to_async_task_messages(
199
+ client: Agentex,
200
+ task: Task,
201
+ only_after_timestamp: Optional[datetime] = None,
202
+ print_messages: bool = True,
203
+ rich_print: bool = True,
204
+ timeout: int = 10,
205
+ ) -> List[TaskMessage]:
206
+ """
207
+ Subscribe to async task messages and collect completed messages.
208
+
209
+ This function:
210
+ 1. Reads all existing messages from the task
211
+ 2. Optionally filters messages after a timestamp
212
+ 3. Shows a loading message while listening
213
+ 4. Subscribes to task message events
214
+ 5. Fetches and displays complete messages when they finish
215
+ 6. Returns all messages collected during the session
216
+
217
+ Features:
218
+ - Uses Rich library for beautiful formatting in Jupyter notebooks
219
+ - Agent messages are formatted as Markdown
220
+ - User and agent messages are displayed in colored panels with fixed width
221
+ - Optimized for Jupyter notebook display
222
+
223
+ Args:
224
+ client: The Agentex client instance
225
+ task: The task to subscribe to
226
+ print_messages: Whether to print messages as they arrive
227
+ only_after_timestamp: Only include messages created after this timestamp. If None, all messages will be included.
228
+ rich_print: Whether to use rich to print the message
229
+ timeout: The timeout in seconds for the streaming connection. If the connection times out, the function will return with any messages collected so far.
230
+ Returns:
231
+ List of TaskMessage objects collected during the session
232
+
233
+ Raises:
234
+ ValueError: If the task doesn't have a name (required for streaming)
235
+ """
236
+
237
+ messages_to_return: List[TaskMessage] = []
238
+
239
+ # Read existing messages
240
+ messages = []
241
+ try:
242
+ # List all messages for this task - MessageListResponse is just a List[TaskMessage]
243
+ messages = client.messages.list(task_id=task.id)
244
+
245
+ except Exception as e:
246
+ print(f"Error reading existing messages: {e}")
247
+
248
+ # Filter and display existing messages
249
+ for message in messages:
250
+ if only_after_timestamp:
251
+ if message.created_at is not None:
252
+ # Handle timezone comparison - make both datetimes timezone-aware
253
+ message_time = message.created_at
254
+ if message_time.tzinfo is None:
255
+ # If message time is naive, assume it's in UTC
256
+ message_time = message_time.replace(tzinfo=timezone.utc)
257
+
258
+ comparison_time = only_after_timestamp
259
+ if comparison_time.tzinfo is None:
260
+ # If comparison time is naive, assume it's in UTC
261
+ comparison_time = comparison_time.replace(tzinfo=timezone.utc)
262
+
263
+ if message_time < comparison_time:
264
+ continue
265
+ else:
266
+ messages_to_return.append(message)
267
+ print_task_message(message, print_messages, rich_print)
268
+ else:
269
+ messages_to_return.append(message)
270
+ print_task_message(message, print_messages, rich_print)
271
+
272
+ # Subscribe to server-side events using tasks.stream_events_by_name
273
+ # This is the proper way to get agent responses after sending an event in agentic agents
274
+
275
+ # Ensure task has a name
276
+ if not task.name:
277
+ print("Error: Task must have a name to use stream_events_by_name")
278
+ raise ValueError("Task name is required")
279
+
280
+ try:
281
+ # Use stream_events_by_name to subscribe to TaskMessageUpdate events for this task
282
+ # This doesn't require knowing the agent_id, just the task name
283
+
284
+ # Track active streaming spinners per message index
285
+ active_spinners: dict[int, Yaspin] = {} # index -> yaspin spinner object
286
+
287
+ with client.tasks.with_streaming_response.stream_events_by_name(
288
+ task_name=task.name,
289
+ timeout=timeout
290
+ ) as response:
291
+
292
+ try:
293
+ for task_message_update_str in response.iter_text():
294
+ try:
295
+ # Parse SSE format
296
+ if task_message_update_str.strip().startswith('data: '):
297
+ task_message_update_json = task_message_update_str.strip()[6:] # Remove 'data: ' prefix
298
+ task_message_update_data = json.loads(task_message_update_json)
299
+
300
+ # Deserialize the discriminated union TaskMessageUpdate based on the "type" field
301
+ message_type = task_message_update_data.get("type", "unknown")
302
+
303
+ # Handle different message types for streaming progress
304
+ if message_type == "start":
305
+ task_message_update = StreamTaskMessageStart.model_validate(task_message_update_data)
306
+ index = task_message_update.index or 0
307
+
308
+ # Start a yaspin spinner for this message
309
+ if print_messages and index not in active_spinners:
310
+ spinner = yaspin(text="🔄 Agent responding...")
311
+ spinner.start()
312
+ active_spinners[index] = spinner
313
+
314
+ elif message_type == "delta":
315
+ task_message_update = StreamTaskMessageDelta.model_validate(task_message_update_data)
316
+ index = task_message_update.index or 0
317
+
318
+ # Spinner continues running (no update needed for HTML) or if spinner has not been created yet, create it
319
+ if print_messages and index not in active_spinners:
320
+ spinner = yaspin(text="🔄 Agent responding...")
321
+ spinner.start()
322
+ active_spinners[index] = spinner
323
+
324
+ elif message_type == "full":
325
+ task_message_update = StreamTaskMessageFull.model_validate(task_message_update_data)
326
+ index = task_message_update.index or 0
327
+
328
+ # Stop spinner and show message
329
+ if index in active_spinners:
330
+ active_spinners[index].stop()
331
+ del active_spinners[index]
332
+
333
+ if task_message_update.parent_task_message and task_message_update.parent_task_message.id:
334
+ finished_message = client.messages.retrieve(task_message_update.parent_task_message.id)
335
+ messages_to_return.append(finished_message)
336
+ print_task_message(finished_message, print_messages, rich_print)
337
+
338
+ elif message_type == "done":
339
+ task_message_update = StreamTaskMessageDone.model_validate(task_message_update_data)
340
+ index = task_message_update.index or 0
341
+
342
+ # Stop spinner and show message
343
+ if index in active_spinners:
344
+ active_spinners[index].stop()
345
+ del active_spinners[index]
346
+
347
+ if task_message_update.parent_task_message and task_message_update.parent_task_message.id:
348
+ finished_message = client.messages.retrieve(task_message_update.parent_task_message.id)
349
+ messages_to_return.append(finished_message)
350
+ print_task_message(finished_message, print_messages, rich_print)
351
+
352
+ # Ignore "connected" message type
353
+ elif message_type == "connected":
354
+ pass
355
+ else:
356
+ if print_messages:
357
+ print(f"Unknown TaskMessageUpdate type: {message_type}")
358
+
359
+ except json.JSONDecodeError:
360
+ # Skip invalid JSON or SSE metadata lines
361
+ if task_message_update_str.strip() and not task_message_update_str.startswith(':'):
362
+ if print_messages:
363
+ print(f"Skipping non-JSON: {task_message_update_str.strip()}")
364
+ continue
365
+ except Exception as e:
366
+ if print_messages:
367
+ print(f"Error processing TaskMessageUpdate: {e}")
368
+ print(f"Raw data: {task_message_update_str.strip()}")
369
+ continue
370
+ finally:
371
+ # Stop any remaining spinners when we're done
372
+ for spinner in active_spinners.values():
373
+ spinner.stop()
374
+ active_spinners.clear()
375
+
376
+ except Exception as e:
377
+ # Handle timeout gracefully
378
+ if "timeout" in str(e).lower() or "timed out" in str(e).lower():
379
+ if print_messages:
380
+ print(f"Streaming timed out after {timeout} seconds - returning collected messages")
381
+ else:
382
+ if print_messages:
383
+ print(f"Error subscribing to events: {e}")
384
+ print("Make sure your agent is running and the task exists")
385
+
386
+ return messages_to_return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: agentex-sdk
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: The official Python library for the agentex API
5
5
  Project-URL: Homepage, https://github.com/scaleapi/agentex-python
6
6
  Project-URL: Repository, https://github.com/scaleapi/agentex-python