agentex-sdk 0.2.0__py3-none-any.whl → 0.2.2__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.
- agentex/_version.py +1 -1
- agentex/lib/adk/_modules/acp.py +2 -1
- agentex/lib/adk/_modules/agent_task_tracker.py +2 -1
- agentex/lib/adk/_modules/agents.py +2 -1
- agentex/lib/adk/_modules/events.py +2 -1
- agentex/lib/adk/_modules/messages.py +4 -3
- agentex/lib/adk/_modules/state.py +2 -1
- agentex/lib/adk/_modules/streaming.py +4 -3
- agentex/lib/adk/_modules/tasks.py +2 -1
- agentex/lib/adk/_modules/tracing.py +2 -1
- agentex/lib/adk/providers/_modules/litellm.py +2 -2
- agentex/lib/adk/providers/_modules/openai.py +2 -2
- agentex/lib/adk/utils/_modules/client.py +12 -0
- agentex/lib/cli/commands/init.py +8 -4
- agentex/lib/cli/templates/default/README.md.j2 +23 -2
- agentex/lib/cli/templates/default/dev.ipynb.j2 +126 -0
- agentex/lib/cli/templates/sync/README.md.j2 +22 -2
- agentex/lib/cli/templates/sync/dev.ipynb.j2 +167 -0
- agentex/lib/cli/templates/sync/project/acp.py.j2 +63 -14
- agentex/lib/cli/templates/temporal/README.md.j2 +24 -3
- agentex/lib/cli/templates/temporal/dev.ipynb.j2 +126 -0
- agentex/lib/core/adapters/streams/adapter_redis.py +4 -4
- agentex/lib/core/adapters/streams/port.py +1 -1
- agentex/lib/core/services/adk/streaming.py +2 -3
- agentex/lib/core/temporal/activities/__init__.py +2 -2
- agentex/lib/sdk/fastacp/base/base_acp_server.py +11 -2
- agentex/lib/utils/dev_tools/__init__.py +9 -0
- agentex/lib/utils/dev_tools/async_messages.py +386 -0
- agentex/resources/agents.py +511 -3
- agentex/resources/tasks.py +4 -4
- agentex/types/agent_rpc_response.py +32 -4
- {agentex_sdk-0.2.0.dist-info → agentex_sdk-0.2.2.dist-info}/METADATA +1 -1
- {agentex_sdk-0.2.0.dist-info → agentex_sdk-0.2.2.dist-info}/RECORD +36 -30
- {agentex_sdk-0.2.0.dist-info → agentex_sdk-0.2.2.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.2.0.dist-info → agentex_sdk-0.2.2.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.2.0.dist-info → agentex_sdk-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,26 +1,75 @@
|
|
1
|
-
|
1
|
+
import json
|
2
|
+
from agentex.lib import adk
|
2
3
|
from agentex.lib.sdk.fastacp.fastacp import FastACP
|
3
|
-
from agentex.lib.types.
|
4
|
+
from agentex.lib.types.fastacp import AgenticACPConfig
|
5
|
+
from agentex.lib.types.acp import CancelTaskParams, CreateTaskParams, SendEventParams
|
4
6
|
|
5
|
-
from agentex.lib.types.task_message_updates import TaskMessageUpdate
|
6
|
-
from agentex.types.task_message_content import TaskMessageContent
|
7
7
|
from agentex.types.text_content import TextContent
|
8
8
|
from agentex.lib.utils.logging import make_logger
|
9
9
|
|
10
10
|
logger = make_logger(__name__)
|
11
11
|
|
12
12
|
|
13
|
-
# Create an ACP server
|
13
|
+
# Create an ACP server with base configuration
|
14
|
+
# This sets up the core server that will handle task creation, events, and cancellation
|
14
15
|
acp = FastACP.create(
|
15
|
-
acp_type="
|
16
|
+
acp_type="agentic",
|
17
|
+
config=AgenticACPConfig(
|
18
|
+
type="base",
|
19
|
+
),
|
16
20
|
)
|
17
21
|
|
18
|
-
@acp.
|
19
|
-
async def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
@acp.on_task_create
|
23
|
+
async def handle_task_create(params: CreateTaskParams):
|
24
|
+
# This handler is called first whenever a new task is created.
|
25
|
+
# It's a good place to initialize any state or resources needed for the task.
|
26
|
+
|
27
|
+
#########################################################
|
28
|
+
# 1. (👋) Do task initialization here.
|
29
|
+
#########################################################
|
30
|
+
|
31
|
+
# Acknowledge that the task has been created.
|
32
|
+
await adk.messages.create(
|
33
|
+
task_id=params.task.id,
|
34
|
+
content=TextContent(
|
35
|
+
author="agent",
|
36
|
+
content=f"Hello! I've received your task. Normally you can do some state initialization here, or just pass and do nothing until you get your first event. For now I'm just acknowledging that I've received a task with the following params:\n\n{json.dumps(params.params, indent=2)}.\n\nYou should only see this message once, when the task is created. All subsequent events will be handled by the `on_task_event_send` handler.",
|
37
|
+
),
|
26
38
|
)
|
39
|
+
|
40
|
+
@acp.on_task_event_send
|
41
|
+
async def handle_event_send(params: SendEventParams):
|
42
|
+
# This handler is called whenever a new event (like a message) is sent to the task
|
43
|
+
|
44
|
+
#########################################################
|
45
|
+
# 2. (👋) Echo back the client's message to show it in the UI.
|
46
|
+
#########################################################
|
47
|
+
|
48
|
+
# This is not done by default so the agent developer has full control over what is shown to the user.
|
49
|
+
if params.event.content:
|
50
|
+
await adk.messages.create(task_id=params.task.id, content=params.event.content)
|
51
|
+
|
52
|
+
#########################################################
|
53
|
+
# 3. (👋) Send a simple response message.
|
54
|
+
#########################################################
|
55
|
+
|
56
|
+
# In future tutorials, this is where we'll add more sophisticated response logic.
|
57
|
+
await adk.messages.create(
|
58
|
+
task_id=params.task.id,
|
59
|
+
content=TextContent(
|
60
|
+
author="agent",
|
61
|
+
content=f"Hello! I've received your message. I can't respond right now, but in future tutorials we'll see how you can get me to intelligently respond to your message.",
|
62
|
+
),
|
63
|
+
)
|
64
|
+
|
65
|
+
@acp.on_task_cancel
|
66
|
+
async def handle_task_cancel(params: CancelTaskParams):
|
67
|
+
# This handler is called when a task is cancelled.
|
68
|
+
# It's useful for cleaning up any resources or state associated with the task.
|
69
|
+
|
70
|
+
#########################################################
|
71
|
+
# 4. (👋) Do task cleanup here.
|
72
|
+
#########################################################
|
73
|
+
|
74
|
+
# This is mostly for durable workflows that are cancellable like Temporal, but we will leave it here for demonstration purposes.
|
75
|
+
logger.info(f"Hello! I've received task cancel for task {params.task.id}: {params.task}. This isn't necessary for this example, but it's good to know that it's available.")
|
@@ -68,6 +68,7 @@ This file is essential for both local development and deployment of your agent.
|
|
68
68
|
│ └── run_worker.py # Temporal worker setup
|
69
69
|
├── Dockerfile # Container definition
|
70
70
|
├── manifest.yaml # Deployment config
|
71
|
+
├── dev.ipynb # Development notebook for testing
|
71
72
|
{% if use_uv %}
|
72
73
|
└── pyproject.toml # Dependencies (uv)
|
73
74
|
{% else %}
|
@@ -82,12 +83,32 @@ This file is essential for both local development and deployment of your agent.
|
|
82
83
|
- Add your own tools and capabilities
|
83
84
|
- Implement custom state management
|
84
85
|
|
85
|
-
### 2.
|
86
|
+
### 2. Test Your Agent with the Development Notebook
|
87
|
+
Use the included `dev.ipynb` Jupyter notebook to test your agent interactively:
|
88
|
+
|
89
|
+
```bash
|
90
|
+
# Start Jupyter notebook (make sure you have jupyter installed)
|
91
|
+
jupyter notebook dev.ipynb
|
92
|
+
|
93
|
+
# Or use VS Code to open the notebook directly
|
94
|
+
code dev.ipynb
|
95
|
+
```
|
96
|
+
|
97
|
+
The notebook includes:
|
98
|
+
- **Setup**: Connect to your local AgentEx backend
|
99
|
+
- **Task creation**: Create a new task for the conversation
|
100
|
+
- **Event sending**: Send events to the agent and get responses
|
101
|
+
- **Async message subscription**: Subscribe to server-side events to receive agent responses
|
102
|
+
- **Rich message display**: Beautiful formatting with timestamps and author information
|
103
|
+
|
104
|
+
The notebook automatically uses your agent name (`{{ agent_name }}`) and demonstrates the agentic ACP workflow: create task → send event → subscribe to responses.
|
105
|
+
|
106
|
+
### 3. Develop Temporal Workflows
|
86
107
|
- Edit `workflow.py` to define your agent's async workflow logic
|
87
108
|
- Modify `activities.py` to add custom activities
|
88
109
|
- Use `run_worker.py` to configure the Temporal worker
|
89
110
|
|
90
|
-
###
|
111
|
+
### 4. Manage Dependencies
|
91
112
|
|
92
113
|
{% if use_uv %}
|
93
114
|
You chose **uv** for package management. Here's how to work with dependencies:
|
@@ -135,7 +156,7 @@ pip install -r requirements.txt
|
|
135
156
|
- Wide compatibility
|
136
157
|
{% endif %}
|
137
158
|
|
138
|
-
###
|
159
|
+
### 5. Configure Credentials
|
139
160
|
- Add any required credentials to your manifest.yaml
|
140
161
|
- For local development, create a `.env` file in the project directory
|
141
162
|
- Use `load_dotenv()` only in development mode:
|
@@ -0,0 +1,126 @@
|
|
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
|
+
"import uuid\n",
|
34
|
+
"\n",
|
35
|
+
"rpc_response = client.agents.create_task(\n",
|
36
|
+
" agent_name=AGENT_NAME,\n",
|
37
|
+
" params={\n",
|
38
|
+
" \"name\": f\"{str(uuid.uuid4())[:8]}-task\",\n",
|
39
|
+
" \"params\": {}\n",
|
40
|
+
" }\n",
|
41
|
+
")\n",
|
42
|
+
"\n",
|
43
|
+
"task = rpc_response.result\n",
|
44
|
+
"print(task)"
|
45
|
+
]
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"cell_type": "code",
|
49
|
+
"execution_count": null,
|
50
|
+
"id": "b03b0d37",
|
51
|
+
"metadata": {},
|
52
|
+
"outputs": [],
|
53
|
+
"source": [
|
54
|
+
"# Send an event to the agent\n",
|
55
|
+
"\n",
|
56
|
+
"# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
|
57
|
+
"# - TextContent: A message with just text content \n",
|
58
|
+
"# - DataContent: A message with JSON-serializable data content\n",
|
59
|
+
"# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
|
60
|
+
"# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
|
61
|
+
"\n",
|
62
|
+
"# 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",
|
63
|
+
"\n",
|
64
|
+
"rpc_response = client.agents.send_event(\n",
|
65
|
+
" agent_name=AGENT_NAME,\n",
|
66
|
+
" params={\n",
|
67
|
+
" \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
|
68
|
+
" \"task_id\": task.id,\n",
|
69
|
+
" }\n",
|
70
|
+
")\n",
|
71
|
+
"\n",
|
72
|
+
"event = rpc_response.result\n",
|
73
|
+
"print(event)"
|
74
|
+
]
|
75
|
+
},
|
76
|
+
{
|
77
|
+
"cell_type": "code",
|
78
|
+
"execution_count": null,
|
79
|
+
"id": "a6927cc0",
|
80
|
+
"metadata": {},
|
81
|
+
"outputs": [],
|
82
|
+
"source": [
|
83
|
+
"# Subscribe to the async task messages produced by the agent\n",
|
84
|
+
"from agentex.lib.utils.dev_tools import subscribe_to_async_task_messages\n",
|
85
|
+
"\n",
|
86
|
+
"task_messages = subscribe_to_async_task_messages(\n",
|
87
|
+
" client=client,\n",
|
88
|
+
" task=task, \n",
|
89
|
+
" only_after_timestamp=event.created_at, \n",
|
90
|
+
" print_messages=True,\n",
|
91
|
+
" rich_print=True,\n",
|
92
|
+
" timeout=5,\n",
|
93
|
+
")"
|
94
|
+
]
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"cell_type": "code",
|
98
|
+
"execution_count": null,
|
99
|
+
"id": "4864e354",
|
100
|
+
"metadata": {},
|
101
|
+
"outputs": [],
|
102
|
+
"source": []
|
103
|
+
}
|
104
|
+
],
|
105
|
+
"metadata": {
|
106
|
+
"kernelspec": {
|
107
|
+
"display_name": ".venv",
|
108
|
+
"language": "python",
|
109
|
+
"name": "python3"
|
110
|
+
},
|
111
|
+
"language_info": {
|
112
|
+
"codemirror_mode": {
|
113
|
+
"name": "ipython",
|
114
|
+
"version": 3
|
115
|
+
},
|
116
|
+
"file_extension": ".py",
|
117
|
+
"mimetype": "text/x-python",
|
118
|
+
"name": "python",
|
119
|
+
"nbconvert_exporter": "python",
|
120
|
+
"pygments_lexer": "ipython3",
|
121
|
+
"version": "3.12.9"
|
122
|
+
}
|
123
|
+
},
|
124
|
+
"nbformat": 4,
|
125
|
+
"nbformat_minor": 5
|
126
|
+
}
|
@@ -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
|
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
|
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
|
-
|
127
|
-
|
126
|
+
DRedisStreamRepository = Annotated[
|
127
|
+
RedisStreamRepository | None, Depends(RedisStreamRepository)
|
128
128
|
]
|
@@ -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
|
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:
|
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
|
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 =
|
60
|
+
stream_repository = RedisStreamRepository()
|
61
61
|
agentex_client = AsyncAgentex()
|
62
62
|
tracer = AsyncTracer(agentex_client)
|
63
63
|
|
@@ -2,6 +2,7 @@ import asyncio
|
|
2
2
|
import base64
|
3
3
|
import inspect
|
4
4
|
import json
|
5
|
+
import os
|
5
6
|
from collections.abc import AsyncGenerator, Awaitable, Callable
|
6
7
|
from contextlib import asynccontextmanager
|
7
8
|
from typing import Any
|
@@ -13,7 +14,7 @@ from fastapi.responses import StreamingResponse
|
|
13
14
|
from pydantic import TypeAdapter, ValidationError
|
14
15
|
|
15
16
|
# from agentex.lib.sdk.fastacp.types import BaseACPConfig
|
16
|
-
from agentex.lib.environment_variables import EnvironmentVariables
|
17
|
+
from agentex.lib.environment_variables import EnvironmentVariables, refreshed_environment_variables
|
17
18
|
from agentex.lib.types.acp import (
|
18
19
|
PARAMS_MODEL_BY_METHOD,
|
19
20
|
RPC_SYNC_METHODS,
|
@@ -390,8 +391,16 @@ class BaseACPServer(FastAPI):
|
|
390
391
|
registration_url, json=registration_data, timeout=30.0
|
391
392
|
)
|
392
393
|
if response.status_code == 200:
|
394
|
+
agent = response.json()
|
395
|
+
agent_id, agent_name = agent["id"], agent["name"]
|
396
|
+
|
397
|
+
os.environ["AGENT_ID"] = agent_id
|
398
|
+
os.environ["AGENT_NAME"] = agent_name
|
399
|
+
refreshed_environment_variables.AGENT_ID = agent_id
|
400
|
+
refreshed_environment_variables.AGENT_NAME = agent_name
|
401
|
+
|
393
402
|
logger.info(
|
394
|
-
f"Successfully registered agent '{
|
403
|
+
f"Successfully registered agent '{agent_name}' with Agentex server with acp_url: {full_acp_url}. Registration data: {registration_data}"
|
395
404
|
)
|
396
405
|
return # Success, exit the retry loop
|
397
406
|
else:
|