agentex-sdk 0.7.1__py3-none-any.whl → 0.7.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/providers/_modules/sync_provider.py +116 -15
- agentex/lib/cli/commands/init.py +16 -1
- agentex/lib/cli/templates/temporal-openai-agents/.dockerignore.j2 +43 -0
- agentex/lib/cli/templates/temporal-openai-agents/Dockerfile-uv.j2 +48 -0
- agentex/lib/cli/templates/temporal-openai-agents/Dockerfile.j2 +48 -0
- agentex/lib/cli/templates/temporal-openai-agents/README.md.j2 +224 -0
- agentex/lib/cli/templates/temporal-openai-agents/dev.ipynb.j2 +126 -0
- agentex/lib/cli/templates/temporal-openai-agents/environments.yaml.j2 +64 -0
- agentex/lib/cli/templates/temporal-openai-agents/manifest.yaml.j2 +140 -0
- agentex/lib/cli/templates/temporal-openai-agents/project/acp.py.j2 +80 -0
- agentex/lib/cli/templates/temporal-openai-agents/project/activities.py.j2 +116 -0
- agentex/lib/cli/templates/temporal-openai-agents/project/run_worker.py.j2 +56 -0
- agentex/lib/cli/templates/temporal-openai-agents/project/workflow.py.j2 +169 -0
- agentex/lib/cli/templates/temporal-openai-agents/pyproject.toml.j2 +35 -0
- agentex/lib/cli/templates/temporal-openai-agents/requirements.txt.j2 +4 -0
- agentex/lib/cli/templates/temporal-openai-agents/test_agent.py.j2 +147 -0
- agentex/types/__init__.py +2 -2
- {agentex_sdk-0.7.1.dist-info → agentex_sdk-0.7.2.dist-info}/METADATA +1 -1
- {agentex_sdk-0.7.1.dist-info → agentex_sdk-0.7.2.dist-info}/RECORD +23 -9
- {agentex_sdk-0.7.1.dist-info → agentex_sdk-0.7.2.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.7.1.dist-info → agentex_sdk-0.7.2.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.7.1.dist-info → agentex_sdk-0.7.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -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 Async 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
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
#
|
|
32
|
+
# kubernetes:
|
|
33
|
+
# # OPTIONAL - Otherwise it will be derived from separately. However, this can be used to override the derived
|
|
34
|
+
# # namespace and deploy it with in the same namespace that already exists for a separate agent.
|
|
35
|
+
# namespace: "team-{{agent_name}}"
|
|
36
|
+
# ********** END EXAMPLE **********
|
|
37
|
+
|
|
38
|
+
schema_version: "v1" # This is used to validate the file structure and is not used by the agentex CLI
|
|
39
|
+
environments:
|
|
40
|
+
dev:
|
|
41
|
+
auth:
|
|
42
|
+
principal:
|
|
43
|
+
user_id: # TODO: Fill in
|
|
44
|
+
account_id: # TODO: Fill in
|
|
45
|
+
helm_overrides:
|
|
46
|
+
# This is used to override the global helm values.yaml file in the agentex-agent helm charts
|
|
47
|
+
replicaCount: 2
|
|
48
|
+
resources:
|
|
49
|
+
requests:
|
|
50
|
+
cpu: "500m"
|
|
51
|
+
memory: "1Gi"
|
|
52
|
+
limits:
|
|
53
|
+
cpu: "1000m"
|
|
54
|
+
memory: "2Gi"
|
|
55
|
+
temporal-worker:
|
|
56
|
+
enabled: true
|
|
57
|
+
replicaCount: 2
|
|
58
|
+
resources:
|
|
59
|
+
requests:
|
|
60
|
+
cpu: "500m"
|
|
61
|
+
memory: "1Gi"
|
|
62
|
+
limits:
|
|
63
|
+
cpu: "1000m"
|
|
64
|
+
memory: "2Gi"
|
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
# Path to temporal worker file
|
|
57
|
+
# Examples:
|
|
58
|
+
# project/run_worker.py (standard)
|
|
59
|
+
# workers/temporal.py (custom structure)
|
|
60
|
+
# ../shared/worker.py (shared across projects)
|
|
61
|
+
worker: project/run_worker.py
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Agent Configuration
|
|
65
|
+
# -----------------
|
|
66
|
+
agent:
|
|
67
|
+
# Type of agent - either sync or async
|
|
68
|
+
acp_type: async
|
|
69
|
+
|
|
70
|
+
# Unique name for your agent
|
|
71
|
+
# Used for task routing and monitoring
|
|
72
|
+
name: {{ agent_name }}
|
|
73
|
+
|
|
74
|
+
# Description of what your agent does
|
|
75
|
+
# Helps with documentation and discovery
|
|
76
|
+
description: {{ description }}
|
|
77
|
+
|
|
78
|
+
# Temporal workflow configuration
|
|
79
|
+
# This enables your agent to run as a Temporal workflow for long-running tasks
|
|
80
|
+
temporal:
|
|
81
|
+
enabled: true
|
|
82
|
+
workflows:
|
|
83
|
+
# Name of the workflow class
|
|
84
|
+
# Must match the @workflow.defn name in your workflow.py
|
|
85
|
+
- name: {{ workflow_name }}
|
|
86
|
+
|
|
87
|
+
# Queue name for task distribution
|
|
88
|
+
# Used by Temporal to route tasks to your agent
|
|
89
|
+
# Convention: <agent_name>_task_queue
|
|
90
|
+
queue_name: {{ queue_name }}
|
|
91
|
+
|
|
92
|
+
# Optional: Health check port for temporal worker
|
|
93
|
+
# Defaults to 80 if not specified
|
|
94
|
+
# health_check_port: 80
|
|
95
|
+
|
|
96
|
+
# Optional: Credentials mapping
|
|
97
|
+
# Maps Kubernetes secrets to environment variables
|
|
98
|
+
# Common credentials include:
|
|
99
|
+
credentials:
|
|
100
|
+
- env_var_name: REDIS_URL
|
|
101
|
+
secret_name: redis-url-secret
|
|
102
|
+
secret_key: url
|
|
103
|
+
# - env_var_name: OPENAI_API_KEY
|
|
104
|
+
# secret_name: openai-api-key
|
|
105
|
+
# secret_key: api-key
|
|
106
|
+
|
|
107
|
+
# Optional: Set Environment variables for running your agent locally as well
|
|
108
|
+
# as for deployment later on
|
|
109
|
+
env: {}
|
|
110
|
+
# OPENAI_API_KEY: "<YOUR_OPENAI_API_KEY_HERE>"
|
|
111
|
+
# OPENAI_BASE_URL: "<YOUR_OPENAI_BASE_URL_HERE>"
|
|
112
|
+
# OPENAI_ORG_ID: "<YOUR_OPENAI_ORG_ID_HERE>"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Deployment Configuration
|
|
116
|
+
# -----------------------
|
|
117
|
+
# Configuration for deploying your agent to Kubernetes clusters
|
|
118
|
+
deployment:
|
|
119
|
+
# Container image configuration
|
|
120
|
+
image:
|
|
121
|
+
repository: "" # Update with your container registry
|
|
122
|
+
tag: "latest" # Default tag, should be versioned in production
|
|
123
|
+
|
|
124
|
+
imagePullSecrets: [] # Update with your image pull secret name
|
|
125
|
+
# - name: my-registry-secret
|
|
126
|
+
|
|
127
|
+
# Global deployment settings that apply to all clusters
|
|
128
|
+
# These can be overridden in cluster-specific environments (environments.yaml)
|
|
129
|
+
global:
|
|
130
|
+
# Default replica count
|
|
131
|
+
replicaCount: 1
|
|
132
|
+
|
|
133
|
+
# Default resource requirements
|
|
134
|
+
resources:
|
|
135
|
+
requests:
|
|
136
|
+
cpu: "500m"
|
|
137
|
+
memory: "1Gi"
|
|
138
|
+
limits:
|
|
139
|
+
cpu: "1000m"
|
|
140
|
+
memory: "2Gi"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin, ModelActivityParameters
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
from agentex.lib.core.temporal.plugins.openai_agents.interceptors.context_interceptor import ContextInterceptor
|
|
6
|
+
from agentex.lib.core.temporal.plugins.openai_agents.models.temporal_streaming_model import (
|
|
7
|
+
TemporalStreamingModelProvider,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
# === DEBUG SETUP (AgentEx CLI Debug Support) ===
|
|
11
|
+
if os.getenv("AGENTEX_DEBUG_ENABLED") == "true":
|
|
12
|
+
try:
|
|
13
|
+
import debugpy
|
|
14
|
+
from agentex.lib.utils.logging import make_logger
|
|
15
|
+
|
|
16
|
+
logger = make_logger(__name__)
|
|
17
|
+
debug_port = int(os.getenv("AGENTEX_DEBUG_PORT", "5679"))
|
|
18
|
+
debug_type = os.getenv("AGENTEX_DEBUG_TYPE", "acp")
|
|
19
|
+
wait_for_attach = os.getenv("AGENTEX_DEBUG_WAIT_FOR_ATTACH", "false").lower() == "true"
|
|
20
|
+
|
|
21
|
+
# Configure debugpy
|
|
22
|
+
debugpy.configure(subProcess=False)
|
|
23
|
+
debugpy.listen(debug_port)
|
|
24
|
+
|
|
25
|
+
logger.info(f"[{debug_type.upper()}] Debug server listening on port {debug_port}")
|
|
26
|
+
|
|
27
|
+
if wait_for_attach:
|
|
28
|
+
logger.info(f"[{debug_type.upper()}] Waiting for debugger to attach...")
|
|
29
|
+
debugpy.wait_for_client()
|
|
30
|
+
logger.info(f"[{debug_type.upper()}] Debugger attached!")
|
|
31
|
+
else:
|
|
32
|
+
logger.info(f"[{debug_type.upper()}] Ready for debugger attachment")
|
|
33
|
+
|
|
34
|
+
except ImportError:
|
|
35
|
+
print("debugpy not available. Install with: pip install debugpy")
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(f"Debug setup failed: {e}")
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
# === END DEBUG SETUP ===
|
|
41
|
+
|
|
42
|
+
from agentex.lib.sdk.fastacp.fastacp import FastACP
|
|
43
|
+
from agentex.lib.types.fastacp import TemporalACPConfig
|
|
44
|
+
|
|
45
|
+
context_interceptor = ContextInterceptor()
|
|
46
|
+
streaming_model_provider = TemporalStreamingModelProvider()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Create the ACP server
|
|
50
|
+
acp = FastACP.create(
|
|
51
|
+
acp_type="async",
|
|
52
|
+
config=TemporalACPConfig(
|
|
53
|
+
# When deployed to the cluster, the Temporal address will automatically be set to the cluster address
|
|
54
|
+
# For local development, we set the address manually to talk to the local Temporal service set up via docker compose
|
|
55
|
+
type="temporal",
|
|
56
|
+
temporal_address=os.getenv("TEMPORAL_ADDRESS", "localhost:7233"),
|
|
57
|
+
plugins=[OpenAIAgentsPlugin(
|
|
58
|
+
model_params=ModelActivityParameters(
|
|
59
|
+
start_to_close_timeout=timedelta(days=1)
|
|
60
|
+
),
|
|
61
|
+
model_provider=streaming_model_provider
|
|
62
|
+
)],
|
|
63
|
+
interceptors=[context_interceptor]
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Notice that we don't need to register any handlers when we use type="temporal"
|
|
69
|
+
# If you look at the code in agentex.sdk.fastacp.impl.temporal_acp
|
|
70
|
+
# You can see that these handlers are automatically registered when the ACP is created
|
|
71
|
+
|
|
72
|
+
# @acp.on_task_create
|
|
73
|
+
# This will be handled by the method in your workflow that is decorated with @workflow.run
|
|
74
|
+
|
|
75
|
+
# @acp.on_task_event_send
|
|
76
|
+
# This will be handled by the method in your workflow that is decorated with @workflow.signal(name=SignalName.RECEIVE_MESSAGE)
|
|
77
|
+
|
|
78
|
+
# @acp.on_task_cancel
|
|
79
|
+
# This does not need to be handled by your workflow.
|
|
80
|
+
# It is automatically handled by the temporal client which cancels the workflow directly
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Temporal Activities for OpenAI Agents SDK
|
|
3
|
+
==========================================
|
|
4
|
+
|
|
5
|
+
WHAT ARE ACTIVITIES?
|
|
6
|
+
--------------------
|
|
7
|
+
Activities are functions that perform non-deterministic operations - things that
|
|
8
|
+
might have different results each time they run, such as:
|
|
9
|
+
- API calls (weather services, databases, external services)
|
|
10
|
+
- File I/O operations
|
|
11
|
+
- Current time/date lookups
|
|
12
|
+
- Random number generation
|
|
13
|
+
- Any operation with side effects
|
|
14
|
+
|
|
15
|
+
Temporal workflows must be deterministic (same input = same output every time).
|
|
16
|
+
Activities let you safely perform non-deterministic work while Temporal handles
|
|
17
|
+
retries, timeouts, and failure recovery automatically.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
HOW TO ADD NEW ACTIVITIES:
|
|
21
|
+
--------------------------
|
|
22
|
+
Adding a new activity requires 3 steps:
|
|
23
|
+
|
|
24
|
+
1. DEFINE the activity in this file with the @activity.defn decorator:
|
|
25
|
+
|
|
26
|
+
@activity.defn
|
|
27
|
+
async def my_new_activity(param: str) -> str:
|
|
28
|
+
# Your non-deterministic logic here
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
2. REGISTER it in run_worker.py by adding to the activities list:
|
|
32
|
+
|
|
33
|
+
from project.activities import get_weather, my_new_activity
|
|
34
|
+
|
|
35
|
+
all_activities = get_all_activities() + [
|
|
36
|
+
stream_lifecycle_content,
|
|
37
|
+
get_weather,
|
|
38
|
+
my_new_activity, # Add your new activity here
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
3. ADD it as a tool to your OpenAI agent in workflow.py:
|
|
42
|
+
|
|
43
|
+
from project.activities import get_weather, my_new_activity
|
|
44
|
+
|
|
45
|
+
agent = Agent(
|
|
46
|
+
name="...",
|
|
47
|
+
tools=[
|
|
48
|
+
openai_agents.workflow.activity_as_tool(
|
|
49
|
+
get_weather,
|
|
50
|
+
start_to_close_timeout=timedelta(minutes=5),
|
|
51
|
+
),
|
|
52
|
+
openai_agents.workflow.activity_as_tool(
|
|
53
|
+
my_new_activity, # Add your new activity as a tool
|
|
54
|
+
start_to_close_timeout=timedelta(minutes=5),
|
|
55
|
+
),
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
RUNNING ACTIVITIES OUTSIDE OPENAI AGENT SDK:
|
|
61
|
+
--------------------------------------------
|
|
62
|
+
You can also call activities directly from your workflow without going through
|
|
63
|
+
the OpenAI agent. This is useful for setup/teardown operations or when you need
|
|
64
|
+
to run an activity before the agent starts:
|
|
65
|
+
|
|
66
|
+
from temporalio import workflow
|
|
67
|
+
from datetime import timedelta
|
|
68
|
+
|
|
69
|
+
# Inside your workflow method:
|
|
70
|
+
result = await workflow.execute_activity(
|
|
71
|
+
get_weather,
|
|
72
|
+
start_to_close_timeout=timedelta(minutes=5),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
For activities with parameters:
|
|
76
|
+
|
|
77
|
+
result = await workflow.execute_activity(
|
|
78
|
+
my_activity_with_params,
|
|
79
|
+
"param_value", # positional args
|
|
80
|
+
start_to_close_timeout=timedelta(minutes=5),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
TEMPORAL DASHBOARD:
|
|
85
|
+
-------------------
|
|
86
|
+
Monitor your workflows and activities in real-time at:
|
|
87
|
+
|
|
88
|
+
http://localhost:8080
|
|
89
|
+
|
|
90
|
+
The dashboard shows:
|
|
91
|
+
- Running and completed workflows
|
|
92
|
+
- Activity execution history
|
|
93
|
+
- Retries and failures
|
|
94
|
+
- Workflow state and signals
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
from temporalio import activity
|
|
98
|
+
|
|
99
|
+
from agentex.lib.utils.logging import make_logger
|
|
100
|
+
|
|
101
|
+
logger = make_logger(__name__)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@activity.defn
|
|
105
|
+
async def get_weather() -> str:
|
|
106
|
+
"""
|
|
107
|
+
Get the current weather.
|
|
108
|
+
|
|
109
|
+
This is a dummy activity that returns a hardcoded string for demo purposes.
|
|
110
|
+
Replace this with a real weather API call in your implementation.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
A string describing the current weather conditions.
|
|
114
|
+
"""
|
|
115
|
+
logger.info("get_weather activity called")
|
|
116
|
+
return "Sunny, 72°F"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from agentex.lib.core.temporal.activities import get_all_activities
|
|
4
|
+
from agentex.lib.core.temporal.workers.worker import AgentexWorker
|
|
5
|
+
from agentex.lib.utils.logging import make_logger
|
|
6
|
+
from agentex.lib.utils.debug import setup_debug_if_enabled
|
|
7
|
+
from agentex.lib.environment_variables import EnvironmentVariables
|
|
8
|
+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin, ModelActivityParameters
|
|
9
|
+
from datetime import timedelta
|
|
10
|
+
from agentex.lib.core.temporal.plugins.openai_agents.interceptors.context_interceptor import ContextInterceptor
|
|
11
|
+
from agentex.lib.core.temporal.plugins.openai_agents.hooks.activities import stream_lifecycle_content
|
|
12
|
+
from agentex.lib.core.temporal.plugins.openai_agents.models.temporal_streaming_model import (
|
|
13
|
+
TemporalStreamingModelProvider,
|
|
14
|
+
)
|
|
15
|
+
from project.workflow import {{ workflow_class }}
|
|
16
|
+
from project.activities import get_weather
|
|
17
|
+
|
|
18
|
+
environment_variables = EnvironmentVariables.refresh()
|
|
19
|
+
|
|
20
|
+
logger = make_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def main():
|
|
24
|
+
# Setup debug mode if enabled
|
|
25
|
+
setup_debug_if_enabled()
|
|
26
|
+
|
|
27
|
+
task_queue_name = environment_variables.WORKFLOW_TASK_QUEUE
|
|
28
|
+
if task_queue_name is None:
|
|
29
|
+
raise ValueError("WORKFLOW_TASK_QUEUE is not set")
|
|
30
|
+
|
|
31
|
+
# Register all activities here
|
|
32
|
+
# When you add new activities in activities.py, add them to this list
|
|
33
|
+
all_activities = get_all_activities() + [stream_lifecycle_content, get_weather]
|
|
34
|
+
|
|
35
|
+
context_interceptor = ContextInterceptor()
|
|
36
|
+
streaming_model_provider = TemporalStreamingModelProvider()
|
|
37
|
+
|
|
38
|
+
# Create a worker with automatic tracing
|
|
39
|
+
worker = AgentexWorker(
|
|
40
|
+
task_queue=task_queue_name,
|
|
41
|
+
plugins=[OpenAIAgentsPlugin(
|
|
42
|
+
model_params=ModelActivityParameters(
|
|
43
|
+
start_to_close_timeout=timedelta(days=1)
|
|
44
|
+
),
|
|
45
|
+
model_provider=streaming_model_provider
|
|
46
|
+
)],
|
|
47
|
+
interceptors=[context_interceptor],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
await worker.run(
|
|
51
|
+
activities=all_activities,
|
|
52
|
+
workflow={{ workflow_class }},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
asyncio.run(main())
|