@@ -112,51 +112,66 @@ def deploy_agents():
112112 statuses = {}
113113
114114 try :
115- # Define a Scoped Credential to force the correct scope for AI Foundry
116- class ScopedCredential :
117- def __init__ (self , credential , scope ):
118- self .credential = credential
119- self .scope = scope
120-
121- def get_token (self , * scopes , ** kwargs ):
122- # Ignore requested scopes and use the forced one
123- return self .credential .get_token (self .scope , ** kwargs )
124-
125- print ("Using DefaultAzureCredential with forced scope: https://ai.azure.com/.default" )
126- base_credential = DefaultAzureCredential ()
127- credential = ScopedCredential (base_credential , "https://ai.azure.com/.default" )
115+ print ("Initializing Azure AI Project Client..." )
128116
129- # Use the constructor directly with all required arguments
117+ # Use DefaultAzureCredential for authentication
118+ credential = DefaultAzureCredential ()
119+
120+ # Get required environment variables
130121 sub_id = os .getenv ("AZURE_SUBSCRIPTION_ID" )
131122 rg = os .getenv ("AZURE_RESOURCE_GROUP" )
132123 project_name = os .getenv ("AZURE_AI_PROJECT_NAME" )
133124
125+ if not all ([sub_id , rg , project_name ]):
126+ raise ValueError ("Missing required environment variables: AZURE_SUBSCRIPTION_ID, AZURE_RESOURCE_GROUP, AZURE_AI_PROJECT_NAME" )
127+
134128 # Fix endpoint domain if needed (Cognitive Services -> AI Services)
135129 if project_endpoint and "cognitiveservices.azure.com" in project_endpoint :
136- print (f"Adjusting endpoint domain from cognitiveservices.azure.com to services.ai.azure.com" )
130+ print (f"Converting endpoint domain: cognitiveservices.azure.com -> services.ai.azure.com" )
137131 project_endpoint = project_endpoint .replace ("cognitiveservices.azure.com" , "services.ai.azure.com" )
132+ os .environ ["AZURE_AI_PROJECT_ENDPOINT" ] = project_endpoint
133+
134+ # Construct proper project endpoint: https://<hub>.services.ai.azure.com/api/projects/<project>
135+ # Remove any existing path segments first
136+ base_endpoint = project_endpoint .split ("/api/" )[0 ] # Get just the base URL
137+ base_endpoint = base_endpoint .rstrip ('/' )
138+
139+ # Now add the proper API path with project name
140+ full_project_endpoint = f"{ base_endpoint } /api/projects/{ project_name } "
141+
142+ print (f"Project Endpoint (base): { base_endpoint } " )
143+ print (f"Project Endpoint (full): { full_project_endpoint } " )
144+ print (f"Subscription: { sub_id } " )
145+ print (f"Resource Group: { rg } " )
146+ print (f"Project Name: { project_name } " )
147+
148+ # Initialize AIProjectClient with endpoint and credential
149+ # The SDK requires the full project endpoint
150+ project_client = AIProjectClient (
151+ endpoint = full_project_endpoint ,
152+ credential = credential
153+ )
154+
155+ print ("Successfully initialized AIProjectClient" )
156+ print ("Fetching existing agents..." )
157+
158+ existing_agents = {}
159+ try :
160+ agent_list = list (project_client .agents .list_agents ())
161+ existing_agents = {a .name : a for a in agent_list }
162+ print (f"Found { len (existing_agents )} existing agent(s)" )
163+ except Exception as list_err :
164+ print (f"Could not list existing agents (may be first run): { list_err } " )
165+ existing_agents = {}
138166
139- if sub_id and rg and project_name :
140- # Append project path if not present
141- if "/api/projects/" not in project_endpoint :
142- project_endpoint = f"{ project_endpoint .rstrip ('/' )} /api/projects/{ project_name } "
143-
144- print (f"Initializing AIProjectClient with endpoint={ project_endpoint } " )
145- # AIProjectClient(endpoint, credential, **kwargs)
146- project_client = AIProjectClient (
147- endpoint = project_endpoint ,
148- credential = credential ,
149- subscription_id = sub_id ,
150- resource_group_name = rg ,
151- project_name = project_name
152- )
153- else :
154- raise ValueError ("Missing required environment variables for AIProjectClient" )
155-
156- existing_agents = {a .name : a for a in project_client .agents .list_agents ()}
157167 except Exception as e :
158- print (f"⚠ Unable to query existing agents: { e } " )
168+ print (f"ERROR initializing AIProjectClient: { e } " )
169+ import traceback
170+ traceback .print_exc ()
171+ print ("\n Falling back to local pseudo-agents..." )
159172 existing_agents = {}
173+ # Don't exit - continue with fallback IDs
174+ project_client = None
160175
161176 for cfg in agents_config :
162177 name = cfg ["name" ]
@@ -165,52 +180,75 @@ def get_token(self, *scopes, **kwargs):
165180 instr_hash = _hash_instructions (instr )
166181 prior_hash = prior_state .get (env_var , {}).get ("hash" )
167182
168- # Idempotent logic
183+ # Skip if no project client available
184+ if project_client is None :
185+ print (f"[{ env_var } ] No project client - using fallback ID" )
186+ fallback_id = f"asst_local_{ env_var } "
187+ deployed_agents [env_var ] = fallback_id
188+ statuses [env_var ] = "fallback-no-client"
189+ continue
190+
191+ # Idempotent logic - check if agent already exists
169192 if name in existing_agents :
170193 agent_obj = existing_agents [name ]
171194 agent_id = getattr (agent_obj , "id" , None ) or getattr (agent_obj , "agentId" , f"unknown-{ env_var } " )
195+
172196 # Attempt update if instructions changed
173197 if prior_hash and prior_hash != instr_hash :
174- print (f"🔄 Updating agent (instructions changed): { name } " )
198+ print (f"[ { env_var } ] Updating agent (instructions changed): { name } " )
175199 try :
176200 # Try native update if available
177201 try :
178202 project_client .agents .update_agent (agent_id = agent_id , instructions = instr )
179203 statuses [env_var ] = "updated"
204+ print (f"[{ env_var } ] Successfully updated: { agent_id } " )
180205 except Exception :
181206 # Fallback recreate strategy
207+ print (f"[{ env_var } ] Update not supported, recreating..." )
182208 try :
183209 project_client .agents .delete_agent (agent_id )
184210 except Exception :
185211 pass
186- new_agent = project_client .agents .create_agent (model = cfg ["model" ], name = name , instructions = instr )
212+ new_agent = project_client .agents .create_agent (
213+ model = cfg ["model" ],
214+ name = name ,
215+ instructions = instr
216+ )
187217 agent_id = new_agent .id
188218 statuses [env_var ] = "recreated"
189- print (f"✅ Agent updated : { agent_id } " )
219+ print (f"[ { env_var } ] Successfully recreated : { agent_id } " )
190220 except Exception as ue :
191- print (f"⚠ Failed to update { name } : { ue } " )
221+ print (f"[ { env_var } ] Failed to update { name } : { ue } " )
192222 statuses [env_var ] = "existing-no-update"
193223 deployed_agents [env_var ] = agent_id
194224 else :
195- print (f"↩ Reusing existing agent: { name } ({ agent_id } )" )
225+ print (f"[ { env_var } ] Reusing existing agent: { name } ({ agent_id } )" )
196226 deployed_agents [env_var ] = agent_id
197227 statuses [env_var ] = "existing"
198228 continue
199229
200230 # Create new agent
201- print (f"📦 Creating agent: { name } " )
231+ print (f"[ { env_var } ] Creating new agent: { name } " )
202232 try :
203- agent = project_client .agents .create_agent (model = cfg ["model" ], name = name , instructions = instr )
233+ agent = project_client .agents .create_agent (
234+ model = cfg ["model" ],
235+ name = name ,
236+ instructions = instr
237+ )
204238 agent_id = agent .id
205239 deployed_agents [env_var ] = agent_id
206240 statuses [env_var ] = "created"
207- print (f"✅ Created: { name } -> { agent_id } " )
241+ print (f"[ { env_var } ] SUCCESS - Created agent: { agent_id } " )
208242 except Exception as ce :
209- print (f"❌ Failed to create { name } : { ce } " )
243+ print (f"[{ env_var } ] FAILED to create { name } : { ce } " )
244+ import traceback
245+ traceback .print_exc ()
246+
247+ # Use fallback local ID
210248 fallback_id = f"asst_local_{ env_var } "
211249 deployed_agents [env_var ] = fallback_id
212- statuses [env_var ] = "fallback-local "
213- print (f" Using fallback local simulation: { fallback_id } " )
250+ statuses [env_var ] = "fallback-creation-failed "
251+ print (f"[ { env_var } ] Using fallback local simulation: { fallback_id } " )
214252
215253 # Persist state (hash + id)
216254 new_state = {}
@@ -224,35 +262,59 @@ def get_token(self, *scopes, **kwargs):
224262 try :
225263 with open (state_path , "w" , encoding = "utf-8" ) as sf :
226264 json .dump (new_state , sf , indent = 2 )
227- print (f"📝 State file updated: { state_path } " )
265+ print (f"[STATE] State file updated: { state_path } " )
228266 except Exception as se :
229- print (f"⚠ Failed to write state file: { se } " )
267+ print (f"WARNING: Failed to write state file: { se } " )
230268
231269 # Update .env with real agent IDs (early propagation)
232270 env_path = os .path .join (os .path .dirname (__file__ ), '..' , '..' , '.env' )
233271 if os .path .exists (env_path ):
234272 try :
235273 with open (env_path , 'r' , encoding = 'utf-8' ) as f :
236- lines = f .readlines ()
274+ content = f .read ()
275+
276+ # Replace each agent ID
277+ for var , aid in deployed_agents .items ():
278+ # Use regex to replace the value after the = sign
279+ import re
280+ pattern = rf'^{ re .escape (var )} =.*$'
281+ replacement = f'{ var } ={ aid } '
282+ content = re .sub (pattern , replacement , content , flags = re .MULTILINE )
283+
284+ # Also fix the project endpoint domain in .env
285+ if "cognitiveservices.azure.com" in content :
286+ print ("Fixing endpoint domains in .env..." )
287+ content = content .replace (
288+ "AZURE_AI_PROJECT_ENDPOINT=https://aif-" ,
289+ "# AZURE_AI_PROJECT_ENDPOINT=https://aif-" # Comment out old
290+ )
291+ # Add corrected endpoint after the Azure AI Foundry section
292+ if "# Azure AI Foundry Configuration" in content :
293+ content = content .replace (
294+ "# Azure AI Foundry Configuration\n " ,
295+ "# Azure AI Foundry Configuration\n # Note: Agents API uses .services.ai.azure.com domain\n "
296+ )
297+
237298 with open (env_path , 'w' , encoding = 'utf-8' ) as f :
238- for line in lines :
239- wrote = False
240- for var , aid in deployed_agents .items ():
241- if line .startswith (f"{ var } =" ):
242- f .write (f"{ var } ={ aid } \n " )
243- wrote = True
244- break
245- if not wrote :
246- f .write (line )
247- print (f"✅ Updated .env with agent IDs: { env_path } " )
299+ f .write (content )
300+
301+ print (f"[{ env_var } ] Updated .env with agent IDs: { env_path } " )
302+ print ("Agent IDs written:" )
303+ for var , aid in deployed_agents .items ():
304+ print (f" { var } : { aid } " )
248305 except Exception as ee :
249- print (f"⚠ Failed to update .env: { ee } " )
306+ print (f"WARNING: Failed to update .env: { ee } " )
307+ import traceback
308+ traceback .print_exc ()
250309 else :
251- print ("ℹ .env file not found for agent ID propagation" )
310+ print ("INFO: .env file not found for agent ID propagation" )
252311
253- print ("\n Summary:" )
312+ print ("\n " + "=" * 70 )
313+ print ("DEPLOYMENT SUMMARY" )
314+ print ("=" * 70 )
254315 for k , v in deployed_agents .items ():
255- print (f" { k } : { v } ({ statuses .get (k )} )" )
316+ status = statuses .get (k , "unknown" )
317+ print (f" { k } : { v } [{ status } ]" )
256318
257319 # Emit structured JSON sentinel block for Terraform parsing
258320 payload = {"agents" : deployed_agents , "statuses" : statuses }
0 commit comments