77from llama_index .agent .openai import OpenAIAgent
88from llama_index .llms .openai import OpenAI
99
10- load_dotenv (' .env' )
10+ load_dotenv (" .env" )
1111PROJECTS_FILE = "projects.json"
1212
13+
1314def load_projects ():
1415 """Load the project mapping from the JSON file."""
1516 if os .path .exists (PROJECTS_FILE ):
1617 with open (PROJECTS_FILE , "r" ) as f :
1718 return json .load (f )
1819 return {}
1920
21+
2022def save_projects (project_map ):
2123 """Save the project mapping to the JSON file."""
2224 with open (PROJECTS_FILE , "w" ) as f :
2325 json .dump (project_map , f )
2426
27+
2528# Store project name -> project info mapping.
2629PROJECT_MAP = load_projects ()
2730
31+
2832def normalize_project_info (project_info ):
2933 """
3034 Ensure that the project info is a dictionary.
@@ -34,6 +38,7 @@ def normalize_project_info(project_info):
3438 return {"id" : project_info }
3539 return project_info
3640
41+
3742def create_ess_project (project_name : str ) -> str :
3843 """
3944 Creates a Serverless project by calling the ESS API.
@@ -52,15 +57,11 @@ def create_ess_project(project_name: str) -> str:
5257 return "Error: API_KEY is not set in the environment."
5358
5459 url = f"{ env_url } /api/v1/serverless/projects/elasticsearch"
55- headers = {
56- "Authorization" : f"ApiKey { api_key } " ,
57- "Content-Type" : "application/json"
58- }
59- payload = {
60- "name" : project_name ,
61- "region_id" : region_id
62- }
63-
60+
61+ headers = {"Authorization" : f"ApiKey { api_key } " , "Content-Type" : "application/json" }
62+ payload = {"name" : project_name ,"region_id" : region_id }
63+
64+
6465 try :
6566 response = requests .post (url , headers = headers , json = payload )
6667 response .raise_for_status ()
@@ -73,25 +74,31 @@ def create_ess_project(project_name: str) -> str:
7374 return "Error: No project ID returned from the API."
7475
7576 if not re .fullmatch (r"^[a-z0-9]{32}$" , project_id ):
76- return f"Error: Received project ID '{ project_id } ' does not match expected format."
77-
77+ return (
78+ f"Error: Received project ID '{ project_id } ' does not match expected format."
79+ )
80+
81+
7882 credentials = data .get ("credentials" )
7983 endpoints = data .get ("endpoints" )
8084
8185 PROJECT_MAP [project_name ] = {
8286 "id" : project_id ,
8387 "credentials" : credentials ,
84- "endpoints" : endpoints
88+ "endpoints" : endpoints ,
8589 }
8690 save_projects (PROJECT_MAP )
87-
91+
92+
8893 summary = (
8994 f"Project '{ project_name } ' created successfully.\n "
9095 f"Project ID: { project_id } \n "
9196 f"Endpoints: { endpoints } "
9297 )
9398 return summary
9499
100+
101+
95102def delete_ess_project (project_name : str ) -> str :
96103 """
97104 Deletes a Serverless project by using the project name.
@@ -102,32 +109,36 @@ def delete_ess_project(project_name: str) -> str:
102109 if not project_info :
103110 return f"Error: No project found with name '{ project_name } '. Ensure the project was created previously."
104111 project_info = normalize_project_info (project_info )
105-
112+
113+
106114 project_id = project_info .get ("id" )
107115 env_url = os .environ .get ("ES_URL" )
108116 api_key = os .environ .get ("API_KEY" )
109-
117+
118+
110119 if not env_url :
111120 return "Error: ES_URL is not set in the environment."
112121 if not api_key :
113122 return "Error: API_KEY is not set in the environment."
114-
123+
124+
115125 url = f"{ env_url } /api/v1/serverless/projects/elasticsearch/{ project_id } "
116- headers = {
117- "Authorization" : f"ApiKey { api_key } " ,
118- "Content-Type" : "application/json"
119- }
126+ headers = {"Authorization" : f"ApiKey { api_key } " ,"Content-Type" : "application/json" }
120127
128+
121129 try :
122130 response = requests .delete (url , headers = headers )
123131 response .raise_for_status ()
124132 except requests .exceptions .RequestException as e :
125133 return f"Error during project deletion: { e } "
126-
134+
135+
127136 del PROJECT_MAP [project_name ]
128137 save_projects (PROJECT_MAP )
129138 return f"Project '{ project_name } ' (ID: { project_id } ) deleted successfully."
130139
140+
141+
131142def get_ess_project_status (project_name : str ) -> str :
132143 """
133144 Retrieves the status of a Serverless project by using its project name.
@@ -138,19 +149,22 @@ def get_ess_project_status(project_name: str) -> str:
138149 if not project_info :
139150 return f"Error: No project found with name '{ project_name } '."
140151 project_info = normalize_project_info (project_info )
141-
152+
153+
142154 project_id = project_info .get ("id" )
143155 env_url = os .environ .get ("ES_URL" )
144156 api_key = os .environ .get ("API_KEY" )
145-
157+
158+
146159 if not env_url :
147160 return "Error: ES_URL is not set in the environment."
148161 if not api_key :
149162 return "Error: API_KEY is not set in the environment."
150163
151164 url = f"{ env_url } /api/v1/serverless/projects/elasticsearch/{ project_id } /status"
152165 headers = {"Authorization" : f"ApiKey { api_key } " }
153-
166+
167+
154168 try :
155169 response = requests .get (url , headers = headers )
156170 response .raise_for_status ()
@@ -160,6 +174,7 @@ def get_ess_project_status(project_name: str) -> str:
160174 status_data = response .json ()
161175 return json .dumps (status_data , indent = 4 )
162176
177+
163178def get_ess_project_details (project_name : str ) -> str :
164179 """
165180 Retrieves the full details of a Serverless project by using its project name.
@@ -171,25 +186,29 @@ def get_ess_project_details(project_name: str) -> str:
171186 if not project_info :
172187 return f"Error: No project found with name '{ project_name } '."
173188 project_info = normalize_project_info (project_info )
174-
189+
190+
175191 project_id = project_info .get ("id" )
176192 env_url = os .environ .get ("ES_URL" )
177193 api_key = os .environ .get ("API_KEY" )
178-
194+
195+
179196 if not env_url :
180197 return "Error: ES_URL is not set in the environment."
181198 if not api_key :
182199 return "Error: API_KEY is not set in the environment."
183200
184201 url = f"{ env_url } /api/v1/serverless/projects/elasticsearch/{ project_id } "
185202 headers = {"Authorization" : f"ApiKey { api_key } " }
186-
203+
204+
187205 try :
188206 response = requests .get (url , headers = headers )
189207 response .raise_for_status ()
190208 except requests .exceptions .RequestException as e :
191209 return f"Error retrieving project details: { e } "
192-
210+
211+
193212 details_data = response .json ()
194213 if not details_data .get ("credentials" ):
195214 details_data ["credentials" ] = project_info .get ("credentials" )
@@ -198,6 +217,7 @@ def get_ess_project_details(project_name: str) -> str:
198217 return json .dumps (details_data , indent = 4 )
199218
200219
220+
201221def get_index_status_in_project (project_name : str , index_name : str ) -> str :
202222 """
203223 Retrieves the status/details of an index in a Serverless Elasticsearch project.
@@ -213,37 +233,49 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
213233 if not project_info :
214234 return f"Error: No project found with name '{ project_name } '. Ensure the project was created previously."
215235 project_info = normalize_project_info (project_info )
216-
236+
237+
217238 project_id = project_info .get ("id" )
218239 env_url = os .environ .get ("ES_URL" )
219240 api_key = os .environ .get ("API_KEY" )
220241 if not env_url or not api_key :
221242 return "Error: ES_URL or API_KEY is not set in the environment."
222-
243+
244+
223245 details_url = f"{ env_url } /api/v1/serverless/projects/elasticsearch/{ project_id } "
224246 headers = {"Authorization" : f"ApiKey { api_key } " }
225247 try :
226248 resp = requests .get (details_url , headers = headers )
227249 resp .raise_for_status ()
228250 except requests .exceptions .RequestException as e :
229251 return f"Error retrieving project details: { e } "
230-
252+
253+
231254 details_data = resp .json ()
232255 credentials = details_data .get ("credentials" ) or project_info .get ("credentials" )
233256 endpoints = details_data .get ("endpoints" ) or project_info .get ("endpoints" )
234-
235- if not credentials or not credentials .get ("username" ) or not credentials .get ("password" ):
236- return ("Error: Credentials not available. The project may still be initializing. "
237- "Please wait a few minutes and try again." )
257+
258+
259+ if (
260+ not credentials
261+ or not credentials .get ("username" )
262+ or not credentials .get ("password" )
263+ ):
264+ return (
265+ "Error: Credentials not available. The project may still be initializing. "
266+ "Please wait a few minutes and try again."
267+ )
238268
239269 if not endpoints or not endpoints .get ("elasticsearch" ):
240270 return "Error: Elasticsearch endpoint not found in project details."
241-
271+
272+
242273 username = credentials .get ("username" )
243274 password = credentials .get ("password" )
244275 es_endpoint = endpoints .get ("elasticsearch" )
245276 index_url = f"{ es_endpoint } /{ index_name } "
246-
277+
278+
247279 try :
248280 index_resp = requests .get (index_url , auth = (username , password ))
249281 index_resp .raise_for_status ()
@@ -252,6 +284,7 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
252284
253285 return json .dumps (index_resp .json (), indent = 4 )
254286
287+
255288create_project_tool = FunctionTool .from_defaults (
256289 create_ess_project ,
257290 name = "create_ess_project" ,
@@ -260,7 +293,7 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
260293 "It requires a single parameter: project_name. "
261294 "The API environment URL (ES_URL), API key (API_KEY), and region (REGION) are read from the environment (.env file). "
262295 "The function stores the project details (ID, credentials, endpoints) for later use and persists them to a file."
263- )
296+ ),
264297)
265298
266299delete_project_tool = FunctionTool .from_defaults (
@@ -270,7 +303,7 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
270303 "Deletes a Serverless Elasticsearch project using its project name. "
271304 "It automatically looks up the project ID (stored during project creation and persisted in a file) and deletes the project. "
272305 "It returns a confirmation message or an error message."
273- )
306+ ),
274307)
275308
276309get_status_tool = FunctionTool .from_defaults (
@@ -280,7 +313,7 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
280313 "Retrieves the status of a Serverless Elasticsearch project by using its project name. "
281314 "It looks up the project ID (from a persisted mapping) and calls the /status endpoint. "
282315 "It returns the status information as a JSON string."
283- )
316+ ),
284317)
285318
286319get_details_tool = FunctionTool .from_defaults (
@@ -290,28 +323,32 @@ def get_index_status_in_project(project_name: str, index_name: str) -> str:
290323 "Retrieves the full details of a Serverless Elasticsearch project by using its project name. "
291324 "It looks up the project ID (from a persisted mapping) and calls the GET project endpoint. "
292325 "It returns the project details as a JSON string."
293- )
326+ ),
294327)
295328
296329openai_api_key = os .environ .get ("OPENAI_API_KEY" )
297330if not openai_api_key :
298- raise ValueError ("Please set the OPENAI_API_KEY environment variable with your OpenAI API key." )
331+ raise ValueError (
332+ "Please set the OPENAI_API_KEY environment variable with your OpenAI API key."
333+ )
299334
300335llm = OpenAI (model = "gpt-4o" , api_key = openai_api_key )
301336agent = OpenAIAgent .from_tools (
302337 [create_project_tool , delete_project_tool , get_status_tool , get_details_tool ],
303338 llm = llm ,
304- verbose = True
339+ verbose = True ,
305340)
306341
342+
307343def main ():
308344 print ("\n Welcome to the Serverless Project AI Agent Tool!\n " )
309345 print ("You can ask things like:" )
310346 print (" - 'Create a serverless project named my_project'" )
311347 print (" - 'Delete the serverless project named my_project'" )
312348 print (" - 'Get the status of the serverless project named my_project'" )
313349 print (" - 'Get the details of the serverless project named my_project'" )
314-
350+
351+
315352 while True :
316353 user_input = input ("\n User: " )
317354 if user_input .strip ().lower () in {"exit" , "quit" }:
@@ -321,4 +358,4 @@ def main():
321358 print ("\n Agent:" , response )
322359
323360if __name__ == "__main__" :
324- main ()
361+ main ()
0 commit comments