22import os
33import smtplib
44from email .mime .text import MIMEText
5+ from typing import Any , Dict , Optional
56
67import requests
78from application_sdk .observability .logger_adaptor import get_logger
1415load_dotenv ()
1516logger = 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 ),
0 commit comments