agentex-sdk 0.1.1__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.
Files changed (37) hide show
  1. agentex/_version.py +1 -1
  2. agentex/lib/adk/_modules/messages.py +2 -2
  3. agentex/lib/adk/_modules/streaming.py +2 -2
  4. agentex/lib/adk/providers/_modules/litellm.py +2 -2
  5. agentex/lib/adk/providers/_modules/openai.py +2 -2
  6. agentex/lib/cli/commands/init.py +8 -4
  7. agentex/lib/cli/handlers/deploy_handlers.py +20 -0
  8. agentex/lib/cli/handlers/run_handlers.py +7 -0
  9. agentex/lib/cli/templates/default/README.md.j2 +23 -2
  10. agentex/lib/cli/templates/default/dev.ipynb.j2 +127 -0
  11. agentex/lib/cli/templates/sync/README.md.j2 +22 -2
  12. agentex/lib/cli/templates/sync/dev.ipynb.j2 +181 -0
  13. agentex/lib/cli/templates/sync/project/acp.py.j2 +63 -14
  14. agentex/lib/cli/templates/temporal/README.md.j2 +24 -3
  15. agentex/lib/cli/templates/temporal/dev.ipynb.j2 +127 -0
  16. agentex/lib/cli/templates/temporal/project/workflow.py.j2 +1 -1
  17. agentex/lib/cli/utils/__init__.py +0 -0
  18. agentex/lib/cli/utils/auth_utils.py +18 -0
  19. agentex/lib/core/adapters/streams/adapter_redis.py +4 -4
  20. agentex/lib/core/adapters/streams/port.py +1 -1
  21. agentex/lib/core/services/adk/streaming.py +2 -3
  22. agentex/lib/core/temporal/activities/__init__.py +2 -2
  23. agentex/lib/environment_variables.py +4 -1
  24. agentex/lib/sdk/config/agent_manifest.py +2 -1
  25. agentex/lib/sdk/config/deployment_config.py +5 -1
  26. agentex/lib/sdk/fastacp/base/base_acp_server.py +14 -0
  27. agentex/lib/utils/dev_tools/__init__.py +9 -0
  28. agentex/lib/utils/dev_tools/async_messages.py +386 -0
  29. agentex/resources/agents.py +5 -6
  30. agentex/types/agent_rpc_by_name_params.py +55 -5
  31. agentex/types/agent_rpc_params.py +27 -7
  32. {agentex_sdk-0.1.1.dist-info → agentex_sdk-0.2.1.dist-info}/METADATA +1 -1
  33. {agentex_sdk-0.1.1.dist-info → agentex_sdk-0.2.1.dist-info}/RECORD +36 -30
  34. agentex/types/agent_rpc_params1.py +0 -21
  35. {agentex_sdk-0.1.1.dist-info → agentex_sdk-0.2.1.dist-info}/WHEEL +0 -0
  36. {agentex_sdk-0.1.1.dist-info → agentex_sdk-0.2.1.dist-info}/entry_points.txt +0 -0
  37. {agentex_sdk-0.1.1.dist-info → agentex_sdk-0.2.1.dist-info}/licenses/LICENSE +0 -0
agentex/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "agentex"
4
- __version__ = "0.1.1" # x-release-please-version
4
+ __version__ = "0.2.1" # x-release-please-version
@@ -3,7 +3,7 @@ from datetime import timedelta
3
3
  from temporalio.common import RetryPolicy
4
4
 
5
5
  from agentex import AsyncAgentex
6
- from agentex.lib.core.adapters.streams.adapter_redis import RedisEventStreamRepository
6
+ from agentex.lib.core.adapters.streams.adapter_redis import RedisStreamRepository
7
7
  from agentex.lib.core.services.adk.messages import MessagesService
8
8
  from agentex.lib.core.services.adk.streaming import StreamingService
9
9
  from agentex.lib.core.temporal.activities.activity_helpers import ActivityHelpers
@@ -38,7 +38,7 @@ class MessagesModule:
38
38
  ):
39
39
  if messages_service is None:
40
40
  agentex_client = AsyncAgentex()
41
- stream_repository = RedisEventStreamRepository()
41
+ stream_repository = RedisStreamRepository()
42
42
  streaming_service = StreamingService(
43
43
  agentex_client=agentex_client,
44
44
  stream_repository=stream_repository,
@@ -1,7 +1,7 @@
1
1
  from temporalio.common import RetryPolicy
2
2
 
3
3
  from agentex import AsyncAgentex
4
- from agentex.lib.core.adapters.streams.adapter_redis import RedisEventStreamRepository
4
+ from agentex.lib.core.adapters.streams.adapter_redis import RedisStreamRepository
5
5
  from agentex.lib.core.services.adk.streaming import (
6
6
  StreamingService,
7
7
  StreamingTaskMessageContext,
@@ -33,7 +33,7 @@ class StreamingModule:
33
33
  a new service will be created with default parameters.
34
34
  """
35
35
  if streaming_service is None:
36
- stream_repository = RedisEventStreamRepository()
36
+ stream_repository = RedisStreamRepository()
37
37
  agentex_client = AsyncAgentex()
38
38
  self._streaming_service = StreamingService(
39
39
  agentex_client=agentex_client,
@@ -5,7 +5,7 @@ from temporalio.common import RetryPolicy
5
5
 
6
6
  from agentex import AsyncAgentex
7
7
  from agentex.lib.core.adapters.llm.adapter_litellm import LiteLLMGateway
8
- from agentex.lib.core.adapters.streams.adapter_redis import RedisEventStreamRepository
8
+ from agentex.lib.core.adapters.streams.adapter_redis import RedisStreamRepository
9
9
  from agentex.lib.core.services.adk.providers.litellm import LiteLLMService
10
10
  from agentex.lib.core.services.adk.streaming import StreamingService
11
11
  from agentex.lib.core.temporal.activities.activity_helpers import ActivityHelpers
@@ -40,7 +40,7 @@ class LiteLLMModule:
40
40
  if litellm_service is None:
41
41
  # Create default service
42
42
  agentex_client = AsyncAgentex()
43
- stream_repository = RedisEventStreamRepository()
43
+ stream_repository = RedisStreamRepository()
44
44
  streaming_service = StreamingService(
45
45
  agentex_client=agentex_client,
46
46
  stream_repository=stream_repository,
@@ -10,7 +10,7 @@ from mcp import StdioServerParameters
10
10
  from temporalio.common import RetryPolicy
11
11
 
12
12
  from agentex import AsyncAgentex
13
- from agentex.lib.core.adapters.streams.adapter_redis import RedisEventStreamRepository
13
+ from agentex.lib.core.adapters.streams.adapter_redis import RedisStreamRepository
14
14
  from agentex.lib.core.services.adk.providers.openai import OpenAIService
15
15
  from agentex.lib.core.services.adk.streaming import StreamingService
16
16
  from agentex.lib.core.temporal.activities.activity_helpers import ActivityHelpers
@@ -47,7 +47,7 @@ class OpenAIModule:
47
47
  if openai_service is None:
48
48
  # Create default service
49
49
  agentex_client = AsyncAgentex()
50
- stream_repository = RedisEventStreamRepository()
50
+ stream_repository = RedisStreamRepository()
51
51
  streaming_service = StreamingService(
52
52
  agentex_client=agentex_client,
53
53
  stream_repository=stream_repository,
@@ -1,5 +1,6 @@
1
1
  from enum import Enum
2
2
  from pathlib import Path
3
+ from typing import Any, Dict
3
4
 
4
5
  import questionary
5
6
  from jinja2 import Environment, FileSystemLoader
@@ -23,7 +24,7 @@ class TemplateType(str, Enum):
23
24
 
24
25
 
25
26
  def render_template(
26
- template_path: str, context: dict, template_type: TemplateType
27
+ template_path: str, context: Dict[str, Any], template_type: TemplateType
27
28
  ) -> str:
28
29
  """Render a template with the given context"""
29
30
  env = Environment(loader=FileSystemLoader(TEMPLATES_DIR / template_type.value))
@@ -32,7 +33,7 @@ def render_template(
32
33
 
33
34
 
34
35
  def create_project_structure(
35
- path: Path, context: dict, template_type: TemplateType, use_uv: bool
36
+ path: Path, context: Dict[str, Any], template_type: TemplateType, use_uv: bool
36
37
  ):
37
38
  """Create the project structure from templates"""
38
39
  # Create project directory
@@ -74,6 +75,9 @@ def create_project_structure(
74
75
  root_templates["requirements.txt.j2"] = "requirements.txt"
75
76
  root_templates["Dockerfile.j2"] = "Dockerfile"
76
77
 
78
+ # Add development notebook for agents
79
+ root_templates["dev.ipynb.j2"] = "dev.ipynb"
80
+
77
81
  for template, output in root_templates.items():
78
82
  output_path = project_dir / output
79
83
  output_path.write_text(render_template(template, context, template_type))
@@ -81,7 +85,7 @@ def create_project_structure(
81
85
  console.print(f"\n[green]✓[/green] Created project structure at: {project_dir}")
82
86
 
83
87
 
84
- def get_project_context(answers: dict, project_path: Path, manifest_root: Path) -> dict:
88
+ def get_project_context(answers: Dict[str, Any], project_path: Path, manifest_root: Path) -> Dict[str, Any]:
85
89
  """Get the project context from user answers"""
86
90
  # Use agent_directory_name as project_name
87
91
  project_name = answers["agent_directory_name"].replace("-", "_")
@@ -112,7 +116,7 @@ def init():
112
116
  )
113
117
 
114
118
  # Use a Rich table for template descriptions
115
- table = Table(show_header=True, header_style="bold magenta")
119
+ table = Table(show_header=True, header_style="bold blue")
116
120
  table.add_column("Template", style="cyan", no_wrap=True)
117
121
  table.add_column("Description", style="white")
118
122
  table.add_row(
@@ -8,8 +8,10 @@ import yaml
8
8
  from pydantic import BaseModel, Field
9
9
  from rich.console import Console
10
10
 
11
+ from agentex.lib.cli.utils.auth_utils import _encode_principal_context
11
12
  from agentex.lib.cli.utils.exceptions import DeploymentError, HelmError
12
13
  from agentex.lib.cli.utils.kubectl_utils import check_and_switch_cluster_context
14
+ from agentex.lib.environment_variables import EnvVarKeys
13
15
  from agentex.lib.sdk.config.agent_config import AgentConfig
14
16
  from agentex.lib.sdk.config.agent_manifest import AgentManifest
15
17
  from agentex.lib.sdk.config.deployment_config import ClusterConfig
@@ -92,6 +94,12 @@ def load_override_config(override_file_path: str | None = None) -> ClusterConfig
92
94
  ) from e
93
95
 
94
96
 
97
+
98
+ def convert_env_vars_dict_to_list(env_vars: dict[str, str]) -> list[dict[str, str]]:
99
+ """Convert a dictionary of environment variables to a list of dictionaries"""
100
+ return [{"name": key, "value": value} for key, value in env_vars.items()]
101
+
102
+
95
103
  def merge_deployment_configs(
96
104
  manifest: AgentManifest,
97
105
  cluster_config: ClusterConfig | None,
@@ -168,6 +176,10 @@ def merge_deployment_configs(
168
176
  if TEMPORAL_WORKER_KEY in helm_values:
169
177
  helm_values[TEMPORAL_WORKER_KEY]["env"] = agent_config.env
170
178
 
179
+ encoded_principal = _encode_principal_context(manifest)
180
+ if encoded_principal:
181
+ helm_values["env"][EnvVarKeys.AUTH_PRINCIPAL_B64] = encoded_principal
182
+
171
183
  if manifest.deployment and manifest.deployment.imagePullSecrets:
172
184
  pull_secrets = [
173
185
  pull_secret.to_dict()
@@ -213,6 +225,14 @@ def merge_deployment_configs(
213
225
  if cluster_config.additional_overrides:
214
226
  _deep_merge(helm_values, cluster_config.additional_overrides)
215
227
 
228
+ # Convert the env vars to a list of dictionaries
229
+ if "env" in helm_values:
230
+ helm_values["env"] = convert_env_vars_dict_to_list(helm_values["env"])
231
+ if TEMPORAL_WORKER_KEY in helm_values and "env" in helm_values[TEMPORAL_WORKER_KEY]:
232
+ helm_values[TEMPORAL_WORKER_KEY]["env"] = convert_env_vars_dict_to_list(
233
+ helm_values[TEMPORAL_WORKER_KEY]["env"]
234
+ )
235
+ print("Deploying with the following helm values: ", helm_values)
216
236
  return helm_values
217
237
 
218
238
 
@@ -6,10 +6,12 @@ from pathlib import Path
6
6
  from rich.console import Console
7
7
  from rich.panel import Panel
8
8
 
9
+ from agentex.lib.cli.utils.auth_utils import _encode_principal_context
9
10
  from agentex.lib.cli.handlers.cleanup_handlers import (
10
11
  cleanup_agent_workflows,
11
12
  should_cleanup_on_restart
12
13
  )
14
+ from agentex.lib.environment_variables import EnvVarKeys
13
15
  from agentex.lib.sdk.config.agent_manifest import AgentManifest
14
16
  from agentex.lib.utils.logging import make_logger
15
17
 
@@ -427,6 +429,11 @@ def create_agent_environment(manifest: AgentManifest) -> dict[str, str]:
427
429
  "ACP_PORT": str(manifest.local_development.agent.port),
428
430
  }
429
431
 
432
+ # Add authorization principal if set
433
+ encoded_principal = _encode_principal_context(manifest)
434
+ if encoded_principal:
435
+ env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64] = encoded_principal
436
+
430
437
  # Add description if available
431
438
  if manifest.agent.description:
432
439
  env_vars["AGENT_DESCRIPTION"] = manifest.agent.description
@@ -62,6 +62,7 @@ This file is essential for both local development and deployment of your agent.
62
62
  │ └── acp.py # ACP server and event handlers
63
63
  ├── Dockerfile # Container definition
64
64
  ├── manifest.yaml # Deployment config
65
+ ├── dev.ipynb # Development notebook for testing
65
66
  {% if use_uv %}
66
67
  └── pyproject.toml # Dependencies (uv)
67
68
  {% else %}
@@ -76,7 +77,27 @@ This file is essential for both local development and deployment of your agent.
76
77
  - Add your own tools and capabilities
77
78
  - Implement custom state management
78
79
 
79
- ### 2. Manage Dependencies
80
+ ### 2. Test Your Agent with the Development Notebook
81
+ Use the included `dev.ipynb` Jupyter notebook to test your agent interactively:
82
+
83
+ ```bash
84
+ # Start Jupyter notebook (make sure you have jupyter installed)
85
+ jupyter notebook dev.ipynb
86
+
87
+ # Or use VS Code to open the notebook directly
88
+ code dev.ipynb
89
+ ```
90
+
91
+ The notebook includes:
92
+ - **Setup**: Connect to your local AgentEx backend
93
+ - **Task creation**: Create a new task for the conversation
94
+ - **Event sending**: Send events to the agent and get responses
95
+ - **Async message subscription**: Subscribe to server-side events to receive agent responses
96
+ - **Rich message display**: Beautiful formatting with timestamps and author information
97
+
98
+ The notebook automatically uses your agent name (`{{ agent_name }}`) and demonstrates the agentic ACP workflow: create task → send event → subscribe to responses.
99
+
100
+ ### 3. Manage Dependencies
80
101
 
81
102
  {% if use_uv %}
82
103
  You chose **uv** for package management. Here's how to work with dependencies:
@@ -115,7 +136,7 @@ pip install -r requirements.txt
115
136
  - Wide compatibility
116
137
  {% endif %}
117
138
 
118
- ### 3. Configure Credentials
139
+ ### 4. Configure Credentials
119
140
  Options:
120
141
  1. Add any required credentials to your manifest.yaml via the `env` section
121
142
  2. Export them in your shell: `export OPENAI_API_KEY=...`
@@ -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
+ }
@@ -60,6 +60,7 @@ This file is essential for both local development and deployment of your agent.
60
60
  │ └── acp.py # ACP server and event handlers
61
61
  ├── Dockerfile # Container definition
62
62
  ├── manifest.yaml # Deployment config
63
+ ├── dev.ipynb # Development notebook for testing
63
64
  {% if use_uv %}
64
65
  └── pyproject.toml # Dependencies (uv)
65
66
  {% else %}
@@ -74,7 +75,26 @@ This file is essential for both local development and deployment of your agent.
74
75
  - Add your own tools and capabilities
75
76
  - Implement custom response generation
76
77
 
77
- ### 2. Manage Dependencies
78
+ ### 2. Test Your Agent with the Development Notebook
79
+ Use the included `dev.ipynb` Jupyter notebook to test your agent interactively:
80
+
81
+ ```bash
82
+ # Start Jupyter notebook (make sure you have jupyter installed)
83
+ jupyter notebook dev.ipynb
84
+
85
+ # Or use VS Code to open the notebook directly
86
+ code dev.ipynb
87
+ ```
88
+
89
+ The notebook includes:
90
+ - **Setup**: Connect to your local AgentEx backend
91
+ - **Non-streaming tests**: Send messages and get complete responses
92
+ - **Streaming tests**: Test real-time streaming responses
93
+ - **Task management**: Optional task creation and management
94
+
95
+ The notebook automatically uses your agent name (`{{ agent_name }}`) and provides examples for both streaming and non-streaming message handling.
96
+
97
+ ### 3. Manage Dependencies
78
98
 
79
99
  {% if use_uv %}
80
100
  You chose **uv** for package management. Here's how to work with dependencies:
@@ -113,7 +133,7 @@ pip install -r requirements.txt
113
133
  - Wide compatibility
114
134
  {% endif %}
115
135
 
116
- ### 3. Configure Credentials
136
+ ### 4. Configure Credentials
117
137
  Options:
118
138
  1. Add any required credentials to your manifest.yaml via the `env` section
119
139
  2. Export them in your shell: `export OPENAI_API_KEY=...`
@@ -0,0 +1,181 @@
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
+ "# # (Optional) Create a new task. If you don't create a new task, each message will be sent to a new task. The server will create the task for you.\n",
33
+ "\n",
34
+ "# import uuid\n",
35
+ "\n",
36
+ "# TASK_ID = str(uuid.uuid4())[:8]\n",
37
+ "\n",
38
+ "# rpc_response = client.agents.rpc_by_name(\n",
39
+ "# agent_name=AGENT_NAME,\n",
40
+ "# method=\"task/create\",\n",
41
+ "# params={\n",
42
+ "# \"name\": f\"{TASK_ID}-task\",\n",
43
+ "# \"params\": {}\n",
44
+ "# }\n",
45
+ "# )\n",
46
+ "\n",
47
+ "# task = rpc_response.result\n",
48
+ "# print(task)"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": null,
54
+ "id": "b03b0d37",
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "# Test non streaming response\n",
59
+ "from typing import List, cast\n",
60
+ "from agentex.types import TaskMessage, TextContent\n",
61
+ "\n",
62
+ "# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
63
+ "# - TextContent: A message with just text content \n",
64
+ "# - DataContent: A message with JSON-serializable data content\n",
65
+ "# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
66
+ "# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
67
+ "\n",
68
+ "# 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",
69
+ "\n",
70
+ "rpc_response = client.agents.rpc_by_name(\n",
71
+ " agent_name=AGENT_NAME,\n",
72
+ " method=\"message/send\",\n",
73
+ " params={\n",
74
+ " \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
75
+ " \"stream\": False\n",
76
+ " }\n",
77
+ ")\n",
78
+ "\n",
79
+ "# # Extract and print just the text content from the response\n",
80
+ "# # The response is expected to be a dict with a \"result\" key containing a list of message dicts\n",
81
+ "if rpc_response and rpc_response.result:\n",
82
+ "\n",
83
+ " # We know that the result of the message/send when stream is set to False will be a list of TaskMessage objects\n",
84
+ " task_message_list = cast(List[TaskMessage], rpc_response.result)\n",
85
+ " for task_message in rpc_response.result:\n",
86
+ " if isinstance(task_message, TaskMessage):\n",
87
+ " content = task_message.content\n",
88
+ " if isinstance(content, TextContent):\n",
89
+ " text = content.content\n",
90
+ " print(text)\n",
91
+ " else:\n",
92
+ " print(f\"Found non-text {type(task_message)} object in response.\")\n"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "id": "79688331",
99
+ "metadata": {},
100
+ "outputs": [],
101
+ "source": [
102
+ "# Test streaming response\n",
103
+ "import json\n",
104
+ "from agentex.types import AgentRpcResponse\n",
105
+ "from agentex.types.agent_rpc_result import StreamTaskMessageDelta, StreamTaskMessageFull\n",
106
+ "from agentex.types.text_delta import TextDelta\n",
107
+ "from agentex.types.task_message_update import TaskMessageUpdate\n",
108
+ "\n",
109
+ "\n",
110
+ "# The result object of message/send will be a TaskMessageUpdate which is a union of the following types:\n",
111
+ "# - StreamTaskMessageStart: \n",
112
+ "# - An indicator that a streaming message was started, doesn't contain any useful content\n",
113
+ "# - StreamTaskMessageDelta: \n",
114
+ "# - A delta of a streaming message, contains the text delta to aggregate\n",
115
+ "# - StreamTaskMessageDone: \n",
116
+ "# - An indicator that a streaming message was done, doesn't contain any useful content\n",
117
+ "# - StreamTaskMessageFull: \n",
118
+ "# - A non-streaming message, there is nothing to aggregate, since this contains the full message, not deltas\n",
119
+ "\n",
120
+ "# Whenn processing StreamTaskMessageDelta, if you are expecting more than TextDeltas, such as DataDelta, ToolRequestDelta, or ToolResponseDelta, you can process them as well\n",
121
+ "# Whenn processing StreamTaskMessageFull, if you are expecting more than TextContent, such as DataContent, ToolRequestContent, or ToolResponseContent, you can process them as well\n",
122
+ "\n",
123
+ "with client.agents.with_streaming_response.rpc_by_name(\n",
124
+ " agent_name=AGENT_NAME,\n",
125
+ " method=\"message/send\",\n",
126
+ " params={\n",
127
+ " \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
128
+ " \"stream\": True\n",
129
+ " }\n",
130
+ ") as response:\n",
131
+ " for agent_rpc_response_str in response.iter_text():\n",
132
+ " chunk_rpc_response = AgentRpcResponse.model_validate(json.loads(agent_rpc_response_str))\n",
133
+ " # We know that the result of the message/send when stream is set to True will be a TaskMessageUpdate\n",
134
+ " task_message_update = cast(TaskMessageUpdate, chunk_rpc_response.result)\n",
135
+ "\n",
136
+ " # Print oly the text deltas as they arrive or any full messages\n",
137
+ " if isinstance(task_message_update, StreamTaskMessageDelta):\n",
138
+ " delta = task_message_update.delta\n",
139
+ " if isinstance(delta, TextDelta):\n",
140
+ " print(delta.text_delta, end=\"\", flush=True)\n",
141
+ " else:\n",
142
+ " print(f\"Found non-text {type(task_message)} object in streaming message.\")\n",
143
+ " elif isinstance(task_message_update, StreamTaskMessageFull):\n",
144
+ " content = task_message_update.content\n",
145
+ " if isinstance(content, TextContent):\n",
146
+ " print(content.content)\n",
147
+ " else:\n",
148
+ " print(f\"Found non-text {type(task_message)} object in full message.\")\n"
149
+ ]
150
+ },
151
+ {
152
+ "cell_type": "code",
153
+ "execution_count": null,
154
+ "id": "4ffb663c",
155
+ "metadata": {},
156
+ "outputs": [],
157
+ "source": []
158
+ }
159
+ ],
160
+ "metadata": {
161
+ "kernelspec": {
162
+ "display_name": ".venv",
163
+ "language": "python",
164
+ "name": "python3"
165
+ },
166
+ "language_info": {
167
+ "codemirror_mode": {
168
+ "name": "ipython",
169
+ "version": 3
170
+ },
171
+ "file_extension": ".py",
172
+ "mimetype": "text/x-python",
173
+ "name": "python",
174
+ "nbconvert_exporter": "python",
175
+ "pygments_lexer": "ipython3",
176
+ "version": "3.12.9"
177
+ }
178
+ },
179
+ "nbformat": 4,
180
+ "nbformat_minor": 5
181
+ }