Skip to content

Commit 0bba954

Browse files
committed
feat:compatible to workflows
1 parent cc43bde commit 0bba954

File tree

5 files changed

+223
-57
lines changed

5 files changed

+223
-57
lines changed

quickstart/ai_giphy/app/activities.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,35 @@
1212

1313
class AIGiphyActivities(ActivitiesInterface):
1414
@activity.defn
15-
async def run_ai_agent(self, input_string: str) -> Dict[str, Any]:
15+
async def run_ai_agent(self, config: Dict[str, Any]) -> Dict[str, Any]:
1616
"""
17-
Runs the AI agent with the given input string.
17+
Runs the AI agent with the given input string and configuration.
1818
1919
Args:
20-
input_string (str): The input to send to the AI agent.
20+
config (Dict[str, Any]): Configuration dictionary containing:
21+
- ai_input_string (str): The input to send to the AI agent
22+
- openai_api_key (str, optional): OpenAI API key (falls back to env var)
23+
- openai_model_name (str, optional): OpenAI model name (falls back to env var, then default)
24+
- openai_base_url (str, optional): OpenAI base URL (falls back to env var)
25+
- giphy_api_key (str, optional): Giphy API key (falls back to env var)
26+
- smtp_host (str, optional): SMTP host (falls back to env var, then default)
27+
- smtp_port (int, optional): SMTP port (falls back to env var, then default)
28+
- smtp_username (str, optional): SMTP username (falls back to env var, then default)
29+
- smtp_password (str, optional): SMTP password (falls back to env var)
30+
- smtp_sender (str, optional): SMTP sender email (falls back to env var)
2131
2232
Returns:
2333
Dict[str, Any]: The output from the AI agent.
2434
"""
2535
try:
36+
input_string = config.get(
37+
"ai_input_string", "Fetch a cat gif and send it to [email protected]"
38+
)
2639
logger.info(f"Received input for AI agent: {input_string}")
27-
chain = get_chain()
40+
41+
# Extract config for get_chain (exclude ai_input_string)
42+
chain_config = {k: v for k, v in config.items() if k != "ai_input_string"}
43+
chain = get_chain(chain_config)
2844
result = chain.invoke({"input": input_string})
2945
logger.info(f"AI agent execution successful. Output: {result}")
3046
return result

quickstart/ai_giphy/app/ai_agent.py

Lines changed: 114 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import smtplib
44
from email.mime.text import MIMEText
5+
from typing import Any, Dict, Optional
56

67
import requests
78
from application_sdk.observability.logger_adaptor import get_logger
@@ -14,36 +15,42 @@
1415
load_dotenv()
1516
logger = get_logger(__name__)
1617

17-
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.sendgrid.net")
18-
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
19-
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "apikey")
20-
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
21-
SMTP_SENDER = os.getenv("SMTP_SENDER", "[email protected]")
22-
GIPHY_API_KEY = os.getenv("GIPHY_API_KEY")
23-
SMTP_SENDER = os.getenv("SMTP_SENDER", "[email protected]")
18+
# Default values from environment variables (with defaults where applicable)
19+
_DEFAULT_SMTP_HOST = os.getenv("SMTP_HOST", "smtp.sendgrid.net")
20+
_DEFAULT_SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
21+
_DEFAULT_SMTP_USERNAME = os.getenv("SMTP_USERNAME", "apikey")
22+
_DEFAULT_SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
23+
_DEFAULT_SMTP_SENDER = os.getenv("SMTP_SENDER", "[email protected]")
24+
_DEFAULT_GIPHY_API_KEY = os.getenv("GIPHY_API_KEY")
25+
_DEFAULT_OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
26+
_DEFAULT_OPENAI_MODEL_NAME = os.getenv("OPENAI_MODEL_NAME", "gpt-4.1-mini")
27+
_DEFAULT_OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL")
2428

25-
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
26-
OPENAI_MODEL_NAME = os.getenv("OPENAI_MODEL_NAME", "gpt-4.1-mini")
27-
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL")
2829

29-
30-
def fetch_gif(search_term: str) -> str:
30+
def _fetch_gif_impl(search_term: str, config: Optional[Dict[str, Any]] = None) -> str:
3131
"""
3232
Fetches a random GIF from Giphy API based on the search term.
3333
3434
Args:
3535
search_term (str): The search query to find a relevant GIF.
36+
config (Optional[Dict[str, Any]]): Configuration dictionary with giphy_api_key.
3637
3738
Returns:
3839
str: URL of the fetched GIF. Returns a fallback GIF URL if the fetch fails.
3940
"""
40-
if not GIPHY_API_KEY:
41+
# Priority: config -> env vars -> None
42+
giphy_api_key = None
43+
if config:
44+
giphy_api_key = config.get("giphy_api_key")
45+
giphy_api_key = giphy_api_key or _DEFAULT_GIPHY_API_KEY
46+
47+
if not giphy_api_key:
4148
raise ValueError(
42-
"GIPHY_API_KEY is not set, please set it in the environment variables for the application. "
49+
"GIPHY_API_KEY is not set, please set it in the workflow_args or environment variables for the application. "
4350
"For reference, please refer to the README.md file and .env.example file."
4451
)
4552

46-
url = f"https://api.giphy.com/v1/gifs/random?api_key={GIPHY_API_KEY}&tag={search_term}&rating=pg"
53+
url = f"https://api.giphy.com/v1/gifs/random?api_key={giphy_api_key}&tag={search_term}&rating=pg"
4754
try:
4855
response = requests.get(url, timeout=5)
4956
response.raise_for_status()
@@ -56,17 +63,42 @@ def fetch_gif(search_term: str) -> str:
5663
return "https://media.giphy.com/media/3o7abAHdYvZdBNnGZq/giphy.gif"
5764

5865

59-
def send_email_with_gify(to: str, gify_url: str):
66+
def _send_email_with_gify_impl(
67+
to: str, gify_url: str, config: Optional[Dict[str, Any]] = None
68+
):
6069
"""
6170
Send an email to the given recipient with the specified subject and body.
71+
72+
Args:
73+
to (str): Recipient email address.
74+
gify_url (str): URL of the GIF to include.
75+
config (Optional[Dict[str, Any]]): Configuration dictionary with SMTP settings.
6276
"""
63-
if not SMTP_PASSWORD:
77+
# Priority: config -> env vars -> defaults
78+
if config:
79+
smtp_host = config.get("smtp_host") or _DEFAULT_SMTP_HOST
80+
smtp_port = config.get("smtp_port")
81+
if smtp_port is None:
82+
smtp_port = _DEFAULT_SMTP_PORT
83+
else:
84+
smtp_port = int(smtp_port)
85+
smtp_username = config.get("smtp_username") or _DEFAULT_SMTP_USERNAME
86+
smtp_password = config.get("smtp_password") or _DEFAULT_SMTP_PASSWORD
87+
smtp_sender = config.get("smtp_sender") or _DEFAULT_SMTP_SENDER
88+
else:
89+
smtp_host = _DEFAULT_SMTP_HOST
90+
smtp_port = _DEFAULT_SMTP_PORT
91+
smtp_username = _DEFAULT_SMTP_USERNAME
92+
smtp_password = _DEFAULT_SMTP_PASSWORD
93+
smtp_sender = _DEFAULT_SMTP_SENDER
94+
95+
if not smtp_password:
6496
raise ValueError(
65-
"SMTP_PASSWORD is not set, please set it in the environment variables for the application. "
97+
"SMTP_PASSWORD is not set, please set it in the workflow_args or environment variables for the application. "
6698
"For reference, please refer to the README.md file and .env.example file."
6799
)
68100

69-
sender = SMTP_SENDER
101+
sender = smtp_sender
70102
subject = "Your Surprise GIF!"
71103
body = f"""
72104
<html>
@@ -84,32 +116,84 @@ def send_email_with_gify(to: str, gify_url: str):
84116
msg["To"] = to
85117

86118
try:
87-
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
119+
with smtplib.SMTP(smtp_host, smtp_port) as server:
88120
server.starttls()
89-
server.login(SMTP_USERNAME, SMTP_PASSWORD) # pyright: ignore[reportArgumentType]
121+
server.login(smtp_username, smtp_password) # pyright: ignore[reportArgumentType]
90122
server.send_message(msg)
91123
return "Email sent successfully"
92124
except Exception as e:
93125
logger.error(f"Failed to send email: {e}")
94126
raise
95127

96128

97-
def get_chain():
98-
# Only API key is required. Base URL is optional.
99-
if not os.getenv("OPENAI_API_KEY"):
100-
raise ValueError(
101-
"OPENAI_API_KEY is not set, please set it in the environment variables for the application."
129+
def get_chain(config: Optional[Dict[str, Any]] = None):
130+
"""
131+
Creates and returns an AI agent chain with tools.
132+
133+
Args:
134+
config (Optional[Dict[str, Any]]): Configuration dictionary containing:
135+
- openai_api_key (str, optional): OpenAI API key (falls back to env var)
136+
- openai_model_name (str, optional): OpenAI model name (falls back to env var, then default)
137+
- openai_base_url (str, optional): OpenAI base URL (falls back to env var)
138+
- giphy_api_key (str, optional): Giphy API key (falls back to env var)
139+
- smtp_host (str, optional): SMTP host (falls back to env var, then default)
140+
- smtp_port (int, optional): SMTP port (falls back to env var, then default)
141+
- smtp_username (str, optional): SMTP username (falls back to env var, then default)
142+
- smtp_password (str, optional): SMTP password (falls back to env var)
143+
- smtp_sender (str, optional): SMTP sender email (falls back to env var)
144+
145+
Returns:
146+
AgentExecutor: The configured AI agent executor.
147+
"""
148+
# Priority: config -> env vars -> defaults
149+
if config:
150+
openai_api_key = config.get("openai_api_key") or _DEFAULT_OPENAI_API_KEY
151+
openai_model_name = (
152+
config.get("openai_model_name") or _DEFAULT_OPENAI_MODEL_NAME
102153
)
154+
openai_base_url = config.get("openai_base_url") or _DEFAULT_OPENAI_BASE_URL
155+
else:
156+
openai_api_key = _DEFAULT_OPENAI_API_KEY
157+
openai_model_name = _DEFAULT_OPENAI_MODEL_NAME
158+
openai_base_url = _DEFAULT_OPENAI_BASE_URL
103159

104-
# Optional: Only used if someone is using a proxy / Azure / custom gateway
105-
openai_base_url = os.getenv("OPENAI_BASE_URL", None)
160+
if not openai_api_key:
161+
raise ValueError(
162+
"OPENAI_API_KEY is not set, please set it in the workflow_args or environment variables for the application."
163+
)
106164

107165
local_llm = ChatOpenAI(
108-
api_key=OPENAI_API_KEY,
109-
model=OPENAI_MODEL_NAME,
166+
api_key=openai_api_key,
167+
model=openai_model_name,
110168
base_url=openai_base_url, # Passing None is allowed and simply ignored
111169
)
112170

171+
# Create closures that capture the config
172+
def fetch_gif(search_term: str) -> str:
173+
"""
174+
Fetches a random GIF from Giphy API based on the search term.
175+
176+
Args:
177+
search_term (str): The search query to find a relevant GIF.
178+
179+
Returns:
180+
str: URL of the fetched GIF. Returns a fallback GIF URL if the fetch fails.
181+
"""
182+
return _fetch_gif_impl(search_term, config)
183+
184+
def send_email_with_gify(to: str, gify_url: str):
185+
"""
186+
Send an email to the given recipient with the specified GIF.
187+
188+
Args:
189+
to (str): Recipient email address.
190+
gify_url (str): URL of the GIF to include in the email.
191+
192+
Returns:
193+
str: Success message if email is sent successfully.
194+
"""
195+
return _send_email_with_gify_impl(to, gify_url, config)
196+
113197
local_tools = [
114198
StructuredTool.from_function(fetch_gif),
115199
StructuredTool.from_function(send_email_with_gify),

quickstart/ai_giphy/app/workflow.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,36 @@ async def run(self, workflow_config: Dict[str, Any]) -> None:
4141
ai_input_string: str = workflow_args.get(
4242
"ai_input_string", "Fetch a cat gif and send it to [email protected]"
4343
)
44+
giphy_api_key: str = workflow_args.get("giphy_api_key")
45+
smtp_host: str = workflow_args.get("smtp_host")
46+
smtp_port: int = workflow_args.get("smtp_port")
47+
smtp_username: str = workflow_args.get("smtp_username")
48+
smtp_password: str = workflow_args.get("smtp_password")
49+
smtp_sender: str = workflow_args.get("smtp_sender")
50+
openai_api_key: str = workflow_args.get("openai_api_key")
51+
openai_model_name: str = workflow_args.get("openai_model_name")
52+
openai_base_url: str = workflow_args.get("openai_base_url")
4453

4554
logger.info(f"Starting AI Giphy workflow with input: {ai_input_string}")
4655

56+
# Prepare config dict with all configuration values
57+
agent_config = {
58+
"ai_input_string": ai_input_string,
59+
"giphy_api_key": giphy_api_key,
60+
"smtp_host": smtp_host,
61+
"smtp_port": smtp_port,
62+
"smtp_username": smtp_username,
63+
"smtp_password": smtp_password,
64+
"smtp_sender": smtp_sender,
65+
"openai_api_key": openai_api_key,
66+
"openai_model_name": openai_model_name,
67+
"openai_base_url": openai_base_url,
68+
}
69+
4770
# Execute the AI agent activity
4871
agent_output = await workflow.execute_activity(
4972
activities_instance.run_ai_agent,
50-
ai_input_string,
73+
agent_config,
5174
start_to_close_timeout=timedelta(
5275
seconds=60
5376
), # Increased timeout for potentially longer AI tasks

0 commit comments

Comments
 (0)