agentex-sdk 0.8.2__py3-none-any.whl → 0.9.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.
- agentex/_base_client.py +134 -11
- agentex/_models.py +16 -1
- agentex/_types.py +9 -0
- agentex/_version.py +1 -1
- agentex/lib/cli/commands/agents.py +97 -0
- agentex/lib/cli/commands/init.py +13 -2
- agentex/lib/cli/handlers/agent_handlers.py +130 -12
- agentex/lib/cli/templates/sync-openai-agents/.dockerignore.j2 +43 -0
- agentex/lib/cli/templates/sync-openai-agents/Dockerfile-uv.j2 +42 -0
- agentex/lib/cli/templates/sync-openai-agents/Dockerfile.j2 +43 -0
- agentex/lib/cli/templates/sync-openai-agents/README.md.j2 +313 -0
- agentex/lib/cli/templates/sync-openai-agents/dev.ipynb.j2 +167 -0
- agentex/lib/cli/templates/sync-openai-agents/environments.yaml.j2 +53 -0
- agentex/lib/cli/templates/sync-openai-agents/manifest.yaml.j2 +115 -0
- agentex/lib/cli/templates/sync-openai-agents/project/acp.py.j2 +137 -0
- agentex/lib/cli/templates/sync-openai-agents/pyproject.toml.j2 +32 -0
- agentex/lib/cli/templates/sync-openai-agents/requirements.txt.j2 +5 -0
- agentex/lib/cli/templates/sync-openai-agents/test_agent.py.j2 +70 -0
- {agentex_sdk-0.8.2.dist-info → agentex_sdk-0.9.1.dist-info}/METADATA +2 -2
- {agentex_sdk-0.8.2.dist-info → agentex_sdk-0.9.1.dist-info}/RECORD +23 -12
- {agentex_sdk-0.8.2.dist-info → agentex_sdk-0.9.1.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.8.2.dist-info → agentex_sdk-0.9.1.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.8.2.dist-info → agentex_sdk-0.9.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
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 agentex.types import TextContent\n",
|
|
60
|
+
"\n",
|
|
61
|
+
"# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
|
|
62
|
+
"# - TextContent: A message with just text content \n",
|
|
63
|
+
"# - DataContent: A message with JSON-serializable data content\n",
|
|
64
|
+
"# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
|
|
65
|
+
"# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
|
|
66
|
+
"\n",
|
|
67
|
+
"# 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",
|
|
68
|
+
"\n",
|
|
69
|
+
"rpc_response = client.agents.send_message(\n",
|
|
70
|
+
" agent_name=AGENT_NAME,\n",
|
|
71
|
+
" params={\n",
|
|
72
|
+
" \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
|
|
73
|
+
" \"stream\": False\n",
|
|
74
|
+
" }\n",
|
|
75
|
+
")\n",
|
|
76
|
+
"\n",
|
|
77
|
+
"if not rpc_response or not rpc_response.result:\n",
|
|
78
|
+
" raise ValueError(\"No result in response\")\n",
|
|
79
|
+
"\n",
|
|
80
|
+
"# Extract and print just the text content from the response\n",
|
|
81
|
+
"for task_message in rpc_response.result:\n",
|
|
82
|
+
" content = task_message.content\n",
|
|
83
|
+
" if isinstance(content, TextContent):\n",
|
|
84
|
+
" text = content.content\n",
|
|
85
|
+
" print(text)\n"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"cell_type": "code",
|
|
90
|
+
"execution_count": null,
|
|
91
|
+
"id": "79688331",
|
|
92
|
+
"metadata": {},
|
|
93
|
+
"outputs": [],
|
|
94
|
+
"source": [
|
|
95
|
+
"# Test streaming response\n",
|
|
96
|
+
"from agentex.types.task_message_update import StreamTaskMessageDelta, StreamTaskMessageFull\n",
|
|
97
|
+
"from agentex.types.text_delta import TextDelta\n",
|
|
98
|
+
"\n",
|
|
99
|
+
"\n",
|
|
100
|
+
"# The result object of message/send will be a TaskMessageUpdate which is a union of the following types:\n",
|
|
101
|
+
"# - StreamTaskMessageStart: \n",
|
|
102
|
+
"# - An indicator that a streaming message was started, doesn't contain any useful content\n",
|
|
103
|
+
"# - StreamTaskMessageDelta: \n",
|
|
104
|
+
"# - A delta of a streaming message, contains the text delta to aggregate\n",
|
|
105
|
+
"# - StreamTaskMessageDone: \n",
|
|
106
|
+
"# - An indicator that a streaming message was done, doesn't contain any useful content\n",
|
|
107
|
+
"# - StreamTaskMessageFull: \n",
|
|
108
|
+
"# - A non-streaming message, there is nothing to aggregate, since this contains the full message, not deltas\n",
|
|
109
|
+
"\n",
|
|
110
|
+
"# Whenn processing StreamTaskMessageDelta, if you are expecting more than TextDeltas, such as DataDelta, ToolRequestDelta, or ToolResponseDelta, you can process them as well\n",
|
|
111
|
+
"# Whenn processing StreamTaskMessageFull, if you are expecting more than TextContent, such as DataContent, ToolRequestContent, or ToolResponseContent, you can process them as well\n",
|
|
112
|
+
"\n",
|
|
113
|
+
"for agent_rpc_response_chunk in client.agents.send_message_stream(\n",
|
|
114
|
+
" agent_name=AGENT_NAME,\n",
|
|
115
|
+
" params={\n",
|
|
116
|
+
" \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
|
|
117
|
+
" \"stream\": True\n",
|
|
118
|
+
" }\n",
|
|
119
|
+
"):\n",
|
|
120
|
+
" # We know that the result of the message/send when stream is set to True will be a TaskMessageUpdate\n",
|
|
121
|
+
" task_message_update = agent_rpc_response_chunk.result\n",
|
|
122
|
+
" # Print oly the text deltas as they arrive or any full messages\n",
|
|
123
|
+
" if isinstance(task_message_update, StreamTaskMessageDelta):\n",
|
|
124
|
+
" delta = task_message_update.delta\n",
|
|
125
|
+
" if isinstance(delta, TextDelta):\n",
|
|
126
|
+
" print(delta.text_delta, end=\"\", flush=True)\n",
|
|
127
|
+
" else:\n",
|
|
128
|
+
" print(f\"Found non-text {type(task_message)} object in streaming message.\")\n",
|
|
129
|
+
" elif isinstance(task_message_update, StreamTaskMessageFull):\n",
|
|
130
|
+
" content = task_message_update.content\n",
|
|
131
|
+
" if isinstance(content, TextContent):\n",
|
|
132
|
+
" print(content.content)\n",
|
|
133
|
+
" else:\n",
|
|
134
|
+
" print(f\"Found non-text {type(task_message)} object in full message.\")\n"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"cell_type": "code",
|
|
139
|
+
"execution_count": null,
|
|
140
|
+
"id": "c5e7e042",
|
|
141
|
+
"metadata": {},
|
|
142
|
+
"outputs": [],
|
|
143
|
+
"source": []
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"metadata": {
|
|
147
|
+
"kernelspec": {
|
|
148
|
+
"display_name": ".venv",
|
|
149
|
+
"language": "python",
|
|
150
|
+
"name": "python3"
|
|
151
|
+
},
|
|
152
|
+
"language_info": {
|
|
153
|
+
"codemirror_mode": {
|
|
154
|
+
"name": "ipython",
|
|
155
|
+
"version": 3
|
|
156
|
+
},
|
|
157
|
+
"file_extension": ".py",
|
|
158
|
+
"mimetype": "text/x-python",
|
|
159
|
+
"name": "python",
|
|
160
|
+
"nbconvert_exporter": "python",
|
|
161
|
+
"pygments_lexer": "ipython3",
|
|
162
|
+
"version": "3.12.9"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"nbformat": 4,
|
|
166
|
+
"nbformat_minor": 5
|
|
167
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Agent Environment Configuration
|
|
2
|
+
# ------------------------------
|
|
3
|
+
# This file defines environment-specific settings for your agent.
|
|
4
|
+
# This DIFFERS from the manifest.yaml file in that it is used to program things that are ONLY per environment.
|
|
5
|
+
|
|
6
|
+
# ********** EXAMPLE **********
|
|
7
|
+
# schema_version: "v1" # This is used to validate the file structure and is not used by the agentex CLI
|
|
8
|
+
# environments:
|
|
9
|
+
# dev:
|
|
10
|
+
# auth:
|
|
11
|
+
# principal:
|
|
12
|
+
# user_id: "1234567890"
|
|
13
|
+
# user_name: "John Doe"
|
|
14
|
+
# user_email: "john.doe@example.com"
|
|
15
|
+
# user_role: "admin"
|
|
16
|
+
# user_permissions: "read, write, delete"
|
|
17
|
+
# helm_overrides: # This is used to override the global helm values.yaml file in the agentex-agent helm charts
|
|
18
|
+
# replicas: 3
|
|
19
|
+
# resources:
|
|
20
|
+
# requests:
|
|
21
|
+
# cpu: "1000m"
|
|
22
|
+
# memory: "2Gi"
|
|
23
|
+
# limits:
|
|
24
|
+
# cpu: "2000m"
|
|
25
|
+
# memory: "4Gi"
|
|
26
|
+
# env:
|
|
27
|
+
# - name: LOG_LEVEL
|
|
28
|
+
# value: "DEBUG"
|
|
29
|
+
# - name: ENVIRONMENT
|
|
30
|
+
# value: "staging"
|
|
31
|
+
# kubernetes:
|
|
32
|
+
# # OPTIONAL - Otherwise it will be derived from separately. However, this can be used to override the derived
|
|
33
|
+
# # namespace and deploy it with in the same namespace that already exists for a separate agent.
|
|
34
|
+
# namespace: "team-{{agent_name}}"
|
|
35
|
+
# ********** END EXAMPLE **********
|
|
36
|
+
|
|
37
|
+
schema_version: "v1" # This is used to validate the file structure and is not used by the agentex CLI
|
|
38
|
+
environments:
|
|
39
|
+
dev:
|
|
40
|
+
auth:
|
|
41
|
+
principal:
|
|
42
|
+
user_id: # TODO: Fill in
|
|
43
|
+
account_id: # TODO: Fill in
|
|
44
|
+
helm_overrides:
|
|
45
|
+
replicaCount: 2
|
|
46
|
+
resources:
|
|
47
|
+
requests:
|
|
48
|
+
cpu: "500m"
|
|
49
|
+
memory: "1Gi"
|
|
50
|
+
limits:
|
|
51
|
+
cpu: "1000m"
|
|
52
|
+
memory: "2Gi"
|
|
53
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Agent Manifest Configuration
|
|
2
|
+
# ---------------------------
|
|
3
|
+
# This file defines how your agent should be built and deployed.
|
|
4
|
+
|
|
5
|
+
# Build Configuration
|
|
6
|
+
# ------------------
|
|
7
|
+
# The build config defines what gets packaged into your agent's Docker image.
|
|
8
|
+
# This same configuration is used whether building locally or remotely.
|
|
9
|
+
#
|
|
10
|
+
# When building:
|
|
11
|
+
# 1. All files from include_paths are collected into a build context
|
|
12
|
+
# 2. The context is filtered by dockerignore rules
|
|
13
|
+
# 3. The Dockerfile uses this context to build your agent's image
|
|
14
|
+
# 4. The image is pushed to a registry and used to run your agent
|
|
15
|
+
build:
|
|
16
|
+
context:
|
|
17
|
+
# Root directory for the build context
|
|
18
|
+
root: ../ # Keep this as the default root
|
|
19
|
+
|
|
20
|
+
# Paths to include in the Docker build context
|
|
21
|
+
# Must include:
|
|
22
|
+
# - Your agent's directory (your custom agent code)
|
|
23
|
+
# These paths are collected and sent to the Docker daemon for building
|
|
24
|
+
include_paths:
|
|
25
|
+
- {{ project_path_from_build_root }}
|
|
26
|
+
|
|
27
|
+
# Path to your agent's Dockerfile
|
|
28
|
+
# This defines how your agent's image is built from the context
|
|
29
|
+
# Relative to the root directory
|
|
30
|
+
dockerfile: {{ project_path_from_build_root }}/Dockerfile
|
|
31
|
+
|
|
32
|
+
# Path to your agent's .dockerignore
|
|
33
|
+
# Filters unnecessary files from the build context
|
|
34
|
+
# Helps keep build context small and builds fast
|
|
35
|
+
dockerignore: {{ project_path_from_build_root }}/.dockerignore
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Local Development Configuration
|
|
39
|
+
# -----------------------------
|
|
40
|
+
# Only used when running the agent locally
|
|
41
|
+
local_development:
|
|
42
|
+
agent:
|
|
43
|
+
port: 8000 # Port where your local ACP server is running
|
|
44
|
+
host_address: host.docker.internal # Host address for Docker networking (host.docker.internal for Docker, localhost for direct)
|
|
45
|
+
|
|
46
|
+
# File paths for local development (relative to this manifest.yaml)
|
|
47
|
+
paths:
|
|
48
|
+
# Path to ACP server file
|
|
49
|
+
# Examples:
|
|
50
|
+
# project/acp.py (standard)
|
|
51
|
+
# src/server.py (custom structure)
|
|
52
|
+
# ../shared/acp.py (shared across projects)
|
|
53
|
+
# /absolute/path/acp.py (absolute path)
|
|
54
|
+
acp: project/acp.py
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Agent Configuration
|
|
58
|
+
# -----------------
|
|
59
|
+
agent:
|
|
60
|
+
acp_type: sync
|
|
61
|
+
# Unique name for your agent
|
|
62
|
+
# Used for task routing and monitoring
|
|
63
|
+
name: {{ agent_name }}
|
|
64
|
+
|
|
65
|
+
# Description of what your agent does
|
|
66
|
+
# Helps with documentation and discovery
|
|
67
|
+
description: {{ description }}
|
|
68
|
+
|
|
69
|
+
# Temporal workflow configuration
|
|
70
|
+
# Set enabled: true to use Temporal workflows for long-running tasks
|
|
71
|
+
temporal:
|
|
72
|
+
enabled: false
|
|
73
|
+
|
|
74
|
+
# Optional: Credentials mapping
|
|
75
|
+
# Maps Kubernetes secrets to environment variables
|
|
76
|
+
# Common credentials include:
|
|
77
|
+
credentials: [] # Update with your credentials
|
|
78
|
+
# - env_var_name: OPENAI_API_KEY
|
|
79
|
+
# secret_name: openai-api-key
|
|
80
|
+
# secret_key: api-key
|
|
81
|
+
|
|
82
|
+
# Optional: Set Environment variables for running your agent locally as well
|
|
83
|
+
# as for deployment later on
|
|
84
|
+
env: {} # Update with your environment variables
|
|
85
|
+
# OPENAI_API_KEY: "<YOUR_OPENAI_API_KEY_HERE>"
|
|
86
|
+
# OPENAI_BASE_URL: "<YOUR_OPENAI_BASE_URL_HERE>"
|
|
87
|
+
# OPENAI_ORG_ID: "<YOUR_OPENAI_ORG_ID_HERE>"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Deployment Configuration
|
|
91
|
+
# -----------------------
|
|
92
|
+
# Configuration for deploying your agent to Kubernetes clusters
|
|
93
|
+
deployment:
|
|
94
|
+
# Container image configuration
|
|
95
|
+
image:
|
|
96
|
+
repository: "" # Update with your container registry
|
|
97
|
+
tag: "latest" # Default tag, should be versioned in production
|
|
98
|
+
|
|
99
|
+
imagePullSecrets: [] # Update with your image pull secret names
|
|
100
|
+
# - name: my-registry-secret
|
|
101
|
+
|
|
102
|
+
# Global deployment settings that apply to all clusters
|
|
103
|
+
# These can be overridden in cluster-specific environments (environments.yaml)
|
|
104
|
+
global:
|
|
105
|
+
# Default replica count
|
|
106
|
+
replicaCount: 1
|
|
107
|
+
|
|
108
|
+
# Default resource requirements
|
|
109
|
+
resources:
|
|
110
|
+
requests:
|
|
111
|
+
cpu: "500m"
|
|
112
|
+
memory: "1Gi"
|
|
113
|
+
limits:
|
|
114
|
+
cpu: "1000m"
|
|
115
|
+
memory: "2Gi"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import AsyncGenerator, List
|
|
3
|
+
|
|
4
|
+
from agentex.lib import adk
|
|
5
|
+
from agentex.lib.adk.providers._modules.sync_provider import SyncStreamingProvider, convert_openai_to_agentex_events
|
|
6
|
+
from agentex.lib.sdk.fastacp.fastacp import FastACP
|
|
7
|
+
from agentex.lib.types.acp import SendMessageParams
|
|
8
|
+
from agentex.lib.core.tracing.tracing_processor_manager import add_tracing_processor_config
|
|
9
|
+
from agentex.lib.types.tracing import SGPTracingProcessorConfig
|
|
10
|
+
from agentex.lib.utils.model_utils import BaseModel
|
|
11
|
+
|
|
12
|
+
from agentex.types.task_message_update import TaskMessageUpdate
|
|
13
|
+
from agentex.types.task_message_content import TaskMessageContent
|
|
14
|
+
from agentex.lib.utils.logging import make_logger
|
|
15
|
+
from agents import Agent, Runner, RunConfig, function_tool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = make_logger(__name__)
|
|
19
|
+
|
|
20
|
+
SGP_API_KEY = os.environ.get("SGP_API_KEY", "")
|
|
21
|
+
SGP_ACCOUNT_ID = os.environ.get("SGP_ACCOUNT_ID", "")
|
|
22
|
+
|
|
23
|
+
if SGP_API_KEY and SGP_ACCOUNT_ID:
|
|
24
|
+
add_tracing_processor_config(
|
|
25
|
+
SGPTracingProcessorConfig(
|
|
26
|
+
sgp_api_key=SGP_API_KEY,
|
|
27
|
+
sgp_account_id=SGP_ACCOUNT_ID,
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
MODEL = "gpt-4o-mini"
|
|
33
|
+
|
|
34
|
+
SYSTEM_PROMPT = """
|
|
35
|
+
<role>
|
|
36
|
+
You are a helpful assistant. Use your tools to help the user.
|
|
37
|
+
</role>
|
|
38
|
+
|
|
39
|
+
<communication_style>
|
|
40
|
+
Communicate in a witty and friendly manner
|
|
41
|
+
</communication_style>
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
AGENT_NAME = "{{ agent_name }}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@function_tool
|
|
48
|
+
async def get_weather() -> str:
|
|
49
|
+
"""
|
|
50
|
+
Get the current weather.
|
|
51
|
+
|
|
52
|
+
This is a dummy activity that returns a hardcoded string for demo purposes.
|
|
53
|
+
Replace this with a real weather API call in your implementation.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
A string describing the current weather conditions.
|
|
57
|
+
"""
|
|
58
|
+
logger.info("get_weather activity called")
|
|
59
|
+
return "Sunny, 72°F"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Create an ACP server
|
|
64
|
+
acp = FastACP.create(
|
|
65
|
+
acp_type="sync",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
class StateModel(BaseModel):
|
|
69
|
+
input_list: List[dict]
|
|
70
|
+
turn_number: int
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@acp.on_message_send
|
|
74
|
+
async def handle_message_send(
|
|
75
|
+
params: SendMessageParams
|
|
76
|
+
) -> TaskMessageContent | list[TaskMessageContent] | AsyncGenerator[TaskMessageUpdate, None]:
|
|
77
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
78
|
+
yield StreamTaskMessageFull(
|
|
79
|
+
index=0,
|
|
80
|
+
type="full",
|
|
81
|
+
content=TextContent(
|
|
82
|
+
author="agent",
|
|
83
|
+
content="Hey, sorry I'm unable to respond to your message because you're running this example without an OpenAI API key. Please set the OPENAI_API_KEY environment variable to run this example. Do this by either by adding a .env file to the project/ directory or by setting the environment variable in your terminal.",
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
user_prompt = params.content.content
|
|
88
|
+
|
|
89
|
+
# Retrieve the task state. Each event is handled as a new turn, so we need to get the state for the current turn.
|
|
90
|
+
task_state = await adk.state.get_by_task_and_agent(task_id=params.task.id, agent_id=params.agent.id)
|
|
91
|
+
if not task_state:
|
|
92
|
+
# If the state doesn't exist, create it.
|
|
93
|
+
state = StateModel(input_list=[], turn_number=0)
|
|
94
|
+
task_state = await adk.state.create(task_id=params.task.id, agent_id=params.agent.id, state=state)
|
|
95
|
+
else:
|
|
96
|
+
state = StateModel.model_validate(task_state.state)
|
|
97
|
+
|
|
98
|
+
state.turn_number += 1
|
|
99
|
+
state.input_list.append({"role": "user", "content": user_prompt})
|
|
100
|
+
|
|
101
|
+
# Initialize the sync provider and run config to allow for tracing
|
|
102
|
+
provider = SyncStreamingProvider(
|
|
103
|
+
trace_id=params.task.id,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
run_config = RunConfig(
|
|
107
|
+
model_provider=provider,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Initialize the agent
|
|
111
|
+
agent = Agent(
|
|
112
|
+
name=AGENT_NAME,
|
|
113
|
+
instructions=SYSTEM_PROMPT,
|
|
114
|
+
model=MODEL,
|
|
115
|
+
tools=[get_weather],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Run the agent with the conversation history from state
|
|
119
|
+
result = Runner.run_streamed(
|
|
120
|
+
agent,
|
|
121
|
+
state.input_list,
|
|
122
|
+
run_config=run_config
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Convert the OpenAI events to Agentex events and stream them back to the client
|
|
126
|
+
async for agentex_event in convert_openai_to_agentex_events(result.stream_events()):
|
|
127
|
+
yield agentex_event
|
|
128
|
+
|
|
129
|
+
# After streaming is complete, update state with the full conversation history
|
|
130
|
+
state.input_list = result.to_input_list()
|
|
131
|
+
await adk.state.update(
|
|
132
|
+
state_id=task_state.id,
|
|
133
|
+
task_id=params.task.id,
|
|
134
|
+
agent_id=params.agent.id,
|
|
135
|
+
state=state,
|
|
136
|
+
trace_id=params.task.id,
|
|
137
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ project_name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "{{ description }}"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"agentex-sdk",
|
|
12
|
+
"scale-gp",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
dev = [
|
|
17
|
+
"pytest",
|
|
18
|
+
"black",
|
|
19
|
+
"isort",
|
|
20
|
+
"flake8",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["project"]
|
|
25
|
+
|
|
26
|
+
[tool.black]
|
|
27
|
+
line-length = 88
|
|
28
|
+
target-version = ['py312']
|
|
29
|
+
|
|
30
|
+
[tool.isort]
|
|
31
|
+
profile = "black"
|
|
32
|
+
line_length = 88
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sample tests for AgentEx ACP agent.
|
|
3
|
+
|
|
4
|
+
This test suite demonstrates how to test the main AgentEx API functions:
|
|
5
|
+
- Non-streaming message sending
|
|
6
|
+
- Streaming message sending
|
|
7
|
+
- Task creation via RPC
|
|
8
|
+
|
|
9
|
+
To run these tests:
|
|
10
|
+
1. Make sure the agent is running (via docker-compose or `agentex agents run`)
|
|
11
|
+
2. Set the AGENTEX_API_BASE_URL environment variable if not using default
|
|
12
|
+
3. Run: pytest test_agent.py -v
|
|
13
|
+
|
|
14
|
+
Configuration:
|
|
15
|
+
- AGENTEX_API_BASE_URL: Base URL for the AgentEx server (default: http://localhost:5003)
|
|
16
|
+
- AGENT_NAME: Name of the agent to test (default: {{ agent_name }})
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import pytest
|
|
21
|
+
from agentex import Agentex
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Configuration from environment variables
|
|
25
|
+
AGENTEX_API_BASE_URL = os.environ.get("AGENTEX_API_BASE_URL", "http://localhost:5003")
|
|
26
|
+
AGENT_NAME = os.environ.get("AGENT_NAME", "{{ agent_name }}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def client():
|
|
31
|
+
"""Create an AgentEx client instance for testing."""
|
|
32
|
+
return Agentex(base_url=AGENTEX_API_BASE_URL)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def agent_name():
|
|
37
|
+
"""Return the agent name for testing."""
|
|
38
|
+
return AGENT_NAME
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def agent_id(client, agent_name):
|
|
43
|
+
"""Retrieve the agent ID based on the agent name."""
|
|
44
|
+
agents = client.agents.list()
|
|
45
|
+
for agent in agents:
|
|
46
|
+
if agent.name == agent_name:
|
|
47
|
+
return agent.id
|
|
48
|
+
raise ValueError(f"Agent with name {agent_name} not found.")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestNonStreamingMessages:
|
|
52
|
+
"""Test non-streaming message sending."""
|
|
53
|
+
|
|
54
|
+
def test_send_message(self, client: Agentex, _agent_name: str):
|
|
55
|
+
"""Test sending a message and receiving a response."""
|
|
56
|
+
# TODO: Fill in the test based on what data your agent is expected to handle
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestStreamingMessages:
|
|
61
|
+
"""Test streaming message sending."""
|
|
62
|
+
|
|
63
|
+
def test_send_stream_message(self, client: Agentex, _agent_name: str):
|
|
64
|
+
"""Test streaming a message and aggregating deltas."""
|
|
65
|
+
# TODO: Fill in the test based on what data your agent is expected to handle
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
pytest.main([__file__, "-v"])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: agentex-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: The official Python library for the agentex API
|
|
5
5
|
Project-URL: Homepage, https://github.com/scaleapi/scale-agentex-python
|
|
6
6
|
Project-URL: Repository, https://github.com/scaleapi/scale-agentex-python
|
|
@@ -47,7 +47,7 @@ Requires-Dist: pyyaml<7,>=6.0.2
|
|
|
47
47
|
Requires-Dist: questionary<3,>=2.0.1
|
|
48
48
|
Requires-Dist: redis<6,>=5.2.0
|
|
49
49
|
Requires-Dist: rich<14,>=13.9.2
|
|
50
|
-
Requires-Dist: scale-gp-beta
|
|
50
|
+
Requires-Dist: scale-gp-beta>=0.1.0a20
|
|
51
51
|
Requires-Dist: scale-gp>=0.1.0a59
|
|
52
52
|
Requires-Dist: sniffio
|
|
53
53
|
Requires-Dist: temporalio<2,>=1.18.2
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
agentex/__init__.py,sha256=TvS8DtvGAnubcoUjYIsuCpBzpsdpxBaJCS76s-l-PRo,2712
|
|
2
|
-
agentex/_base_client.py,sha256=
|
|
2
|
+
agentex/_base_client.py,sha256=gb-HyiWzYErOgODj-HypzcOTPQnxZxCuW2wZD2wixrY,73410
|
|
3
3
|
agentex/_client.py,sha256=39ydvoIqEPVYkM891ZMhCMKrbXR59QUxuKTtrTJGgMM,28482
|
|
4
4
|
agentex/_compat.py,sha256=DQBVORjFb33zch24jzkhM14msvnzY7mmSmgDLaVFUM8,6562
|
|
5
5
|
agentex/_constants.py,sha256=oGldMuFz7eZtwD8_6rJUippKhZB5fGSA7ffbCDGourA,466
|
|
6
6
|
agentex/_exceptions.py,sha256=B09aFjWFRSShb9BFJd-MNDblsGDyGk3w-vItYmjg_AI,3222
|
|
7
7
|
agentex/_files.py,sha256=KnEzGi_O756MvKyJ4fOCW_u3JhOeWPQ4RsmDvqihDQU,3545
|
|
8
|
-
agentex/_models.py,sha256=
|
|
8
|
+
agentex/_models.py,sha256=R3MpO2z4XhTAnD3ObEks32suRXleF1g7BEgQTOLIxTs,32112
|
|
9
9
|
agentex/_qs.py,sha256=craIKyvPktJ94cvf9zn8j8ekG9dWJzhWv0ob34lIOv4,4828
|
|
10
10
|
agentex/_resource.py,sha256=S1t7wmR5WUvoDIhZjo_x-E7uoTJBynJ3d8tPJMQYdjw,1106
|
|
11
11
|
agentex/_response.py,sha256=Tb9zazsnemO2rTxWtBjAD5WBqlhli5ZaXGbiKgdu5DE,28794
|
|
12
12
|
agentex/_streaming.py,sha256=aOvLOte7kaclPGm-D0iNdM3uRcWrZ-T2B8t9BDNmpuA,10225
|
|
13
|
-
agentex/_types.py,sha256=
|
|
14
|
-
agentex/_version.py,sha256=
|
|
13
|
+
agentex/_types.py,sha256=fa7vkH4XTd2n7GdGxf8-i_2kMDtXgwO3X_usN1GyknI,7595
|
|
14
|
+
agentex/_version.py,sha256=SYGGl3sszoM1tIOioAVTbNFef-xpUz6G437ZzdjEers,159
|
|
15
15
|
agentex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
agentex/_utils/__init__.py,sha256=7fch0GT9zpNnErbciSpUNa-SjTxxjY6kxHxKMOM4AGs,2305
|
|
17
17
|
agentex/_utils/_compat.py,sha256=D8gtAvjJQrDWt9upS0XaG9Rr5l1QhiAx_I_1utT_tt0,1195
|
|
@@ -52,8 +52,8 @@ agentex/lib/adk/utils/_modules/client.py,sha256=UYSTThuZoX3nqF8iuRmRI8uPO00NrYgC
|
|
|
52
52
|
agentex/lib/adk/utils/_modules/templating.py,sha256=tSiJGoDrF-XkMEi4MB_wVH6nyKyhSQwVZ8krN5R-86Q,3598
|
|
53
53
|
agentex/lib/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
54
|
agentex/lib/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
-
agentex/lib/cli/commands/agents.py,sha256
|
|
56
|
-
agentex/lib/cli/commands/init.py,sha256=
|
|
55
|
+
agentex/lib/cli/commands/agents.py,sha256=QbqRdZEJNlbfSpQ5HrN0xfQBL6bCKD0l1PijTYSb1ak,17014
|
|
56
|
+
agentex/lib/cli/commands/init.py,sha256=pf8FHuJKO1NtTNEKkpB34EcJ-V8D7TcPP_EUrgbkaZs,16041
|
|
57
57
|
agentex/lib/cli/commands/main.py,sha256=QWychw-Xq3nQ00BMypgOEF4w1WauNrgQ06PDardNP_8,1042
|
|
58
58
|
agentex/lib/cli/commands/secrets.py,sha256=t4zvoyjOHmw-8qp6nsxdZCvqy43QXOl0MWXA3ooBO6Q,5640
|
|
59
59
|
agentex/lib/cli/commands/tasks.py,sha256=fvZJjYI8Dh-6nbTVpRa2-VdF6A8D4w3Dmd4tMslSTPI,3741
|
|
@@ -62,7 +62,7 @@ agentex/lib/cli/debug/__init__.py,sha256=-ZF2Hg8tyREDjPTefhJG_KesX4DA1gZue89pzf4
|
|
|
62
62
|
agentex/lib/cli/debug/debug_config.py,sha256=XlMyq7jP9NPAncO9Saob1Ga7dn-6DhmJ86iKAlQC9WY,3388
|
|
63
63
|
agentex/lib/cli/debug/debug_handlers.py,sha256=CZ8i5Xf50IQOeMvNfNgE5AgphkxnPo4jf2VbMt2jDEE,5690
|
|
64
64
|
agentex/lib/cli/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
-
agentex/lib/cli/handlers/agent_handlers.py,sha256=
|
|
65
|
+
agentex/lib/cli/handlers/agent_handlers.py,sha256=KwfWIwjJX1gYaddU19X-U_fIUZegAqoLDPM2FmfAIhg,9985
|
|
66
66
|
agentex/lib/cli/handlers/cleanup_handlers.py,sha256=-MISeCxZ9zOcSL8gBzEHrSMwhW3cJtoXrQkGAzGeknE,7179
|
|
67
67
|
agentex/lib/cli/handlers/deploy_handlers.py,sha256=DcTIi0QUaZRiJG0LcPhKfN2Jkd1aOcRhEZYk8kUQt1w,16243
|
|
68
68
|
agentex/lib/cli/handlers/run_handlers.py,sha256=UFUm-vOUq2aIGUYiuAre7c73U3hd6AHrDXPA1fPmAEw,15963
|
|
@@ -89,6 +89,17 @@ agentex/lib/cli/templates/sync/pyproject.toml.j2,sha256=eyN6dYqJTFzb5WztJMxboy9W
|
|
|
89
89
|
agentex/lib/cli/templates/sync/requirements.txt.j2,sha256=iTmO-z8qFkUa1jTctFCs0WYuq7Sqi6VNQAwATakh2fQ,94
|
|
90
90
|
agentex/lib/cli/templates/sync/test_agent.py.j2,sha256=zMJMCqWcEvKUUxfXSC0GRtLveqvGTknZjZWy2-HbtNU,2049
|
|
91
91
|
agentex/lib/cli/templates/sync/project/acp.py.j2,sha256=PUVXnN7IikBWYnfqLMgual-iXdl_benAjEfNcRADHBk,998
|
|
92
|
+
agentex/lib/cli/templates/sync-openai-agents/.dockerignore.j2,sha256=hweGFxw5eDZYsb5EnRHpv27o9M1HF2PEWOxqsfBBcAE,320
|
|
93
|
+
agentex/lib/cli/templates/sync-openai-agents/Dockerfile-uv.j2,sha256=9-xbz3mh5yGuSxtQ6FRltzY45OyUzvi1ZmlfwOioK-M,1085
|
|
94
|
+
agentex/lib/cli/templates/sync-openai-agents/Dockerfile.j2,sha256=-P2CwE84h4mwO1Gnl779c4MdoOcVX8_ndpesq9M4fQQ,1093
|
|
95
|
+
agentex/lib/cli/templates/sync-openai-agents/README.md.j2,sha256=_S7Ngl4qOUQHPFldLXDBvuIWPFU2-WcuxGmr5EXLX6k,8816
|
|
96
|
+
agentex/lib/cli/templates/sync-openai-agents/dev.ipynb.j2,sha256=Z42iRveuI_k5LcJqWX-3H1glPtNTkxg_MKVe1lwuJos,6055
|
|
97
|
+
agentex/lib/cli/templates/sync-openai-agents/environments.yaml.j2,sha256=BGprRPca_Y2sPA7kOiSK8COYp4_USoikB4cQ3wbAg94,1769
|
|
98
|
+
agentex/lib/cli/templates/sync-openai-agents/manifest.yaml.j2,sha256=pvH3AmdSsp4NvOtQkGTHE9ZM5wou0j99RF5ITWK5wH8,3848
|
|
99
|
+
agentex/lib/cli/templates/sync-openai-agents/pyproject.toml.j2,sha256=eyN6dYqJTFzb5WztJMxboy9Wc0XPXVnKYaF5JBxJE7o,507
|
|
100
|
+
agentex/lib/cli/templates/sync-openai-agents/requirements.txt.j2,sha256=iTmO-z8qFkUa1jTctFCs0WYuq7Sqi6VNQAwATakh2fQ,94
|
|
101
|
+
agentex/lib/cli/templates/sync-openai-agents/test_agent.py.j2,sha256=zMJMCqWcEvKUUxfXSC0GRtLveqvGTknZjZWy2-HbtNU,2049
|
|
102
|
+
agentex/lib/cli/templates/sync-openai-agents/project/acp.py.j2,sha256=a4DFVzRnKksz3Nz0z9FvHJp1dA3kno5NSuYyKrRPQVw,4354
|
|
92
103
|
agentex/lib/cli/templates/temporal/.dockerignore.j2,sha256=hweGFxw5eDZYsb5EnRHpv27o9M1HF2PEWOxqsfBBcAE,320
|
|
93
104
|
agentex/lib/cli/templates/temporal/Dockerfile-uv.j2,sha256=g8zECsR9_byvlc8bPdbY4Lw97w9nlWb8edx5FPiJav0,1426
|
|
94
105
|
agentex/lib/cli/templates/temporal/Dockerfile.j2,sha256=N1Z73jb8pnxsjP9zbs-tSyNHO6usVzyOdtWorbR5gVY,1434
|
|
@@ -351,8 +362,8 @@ agentex/types/messages/batch_update_params.py,sha256=Ug5CThbD49a8j4qucg04OdmVrp_
|
|
|
351
362
|
agentex/types/messages/batch_update_response.py,sha256=TbSBe6SuPzjXXWSj-nRjT1JHGBooTshHQQDa1AixQA8,278
|
|
352
363
|
agentex/types/shared/__init__.py,sha256=IKs-Qn5Yja0kFh1G1kDqYZo43qrOu1hSoxlPdN-85dI,149
|
|
353
364
|
agentex/types/shared/delete_response.py,sha256=8qH3zvQXaOHYQSHyXi7UQxdR4miTzR7V9K4zXVsiUyk,215
|
|
354
|
-
agentex_sdk-0.
|
|
355
|
-
agentex_sdk-0.
|
|
356
|
-
agentex_sdk-0.
|
|
357
|
-
agentex_sdk-0.
|
|
358
|
-
agentex_sdk-0.
|
|
365
|
+
agentex_sdk-0.9.1.dist-info/METADATA,sha256=hWv6l0eRVIQOL3XRpMWMNC9gSbzfvphmd1pqzTyakKA,15553
|
|
366
|
+
agentex_sdk-0.9.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
367
|
+
agentex_sdk-0.9.1.dist-info/entry_points.txt,sha256=V7vJuMZdF0UlvgX6KiBN7XUvq_cxF5kplcYvc1QlFaQ,62
|
|
368
|
+
agentex_sdk-0.9.1.dist-info/licenses/LICENSE,sha256=Eejl902yry8m7Bl6gRYRUObLifIrC61CmV369C6-dqQ,11337
|
|
369
|
+
agentex_sdk-0.9.1.dist-info/RECORD,,
|
|
File without changes
|