diff --git a/docs/01-infrastructure-setup.md b/docs/01-infrastructure-setup.md index 018e9ba..254dc89 100644 --- a/docs/01-infrastructure-setup.md +++ b/docs/01-infrastructure-setup.md @@ -61,6 +61,13 @@ You'll use the Azure Developer CLI to deploy all required Azure resources using Sign in with your Azure credentials when prompted. This authentication is needed for the Python SDK and other Azure operations in subsequent labs. + > ⚠️ **Important ** + > In some environments, the VS Code integrated terminal may crash or close during the interactive login flow. + > If this happens, authenticate using explicit credentials instead: + > ```powershell + > az login --username --password + > ``` + 1. Provision resources: ```powershell @@ -96,6 +103,16 @@ You'll use the Azure Developer CLI to deploy all required Azure resources using ```powershell azd env get-values > .env ``` + + > ⚠️ **Important – File Encoding** + > + > After generating the `.env` file, make sure it is saved using **UTF-8** encoding. + > + > In editors like **VS Code**, check the encoding indicator in the bottom-right corner. + > If it shows **UTF-16 LE** (or any encoding other than UTF-8), click it, choose **Save with Encoding**, and select **UTF-8**. + > + > Using the wrong encoding may cause environment variables to be read incorrectly + This creates a `.env` file in your project root with all the provisioned resource information: - Resource names and IDs @@ -182,15 +199,17 @@ Interact with your deployed agent from the terminal to verify it's working corre 1. When prompted, ask the agent a question about hiking, for example: ``` - You: I want to go hiking this weekend near Seattle. Any suggestions? + I want to go hiking this weekend near Seattle. Any suggestions? ``` 1. The agent will respond with trail recommendations. Continue the conversation or type `exit` to quit. ``` Agent: I'd recommend checking out Rattlesnake Ledge Trail... - - You: exit + ``` + + ``` + exit ``` ## Verify your deployment diff --git a/infra/core/ai/ai-project.bicep b/infra/core/ai/ai-project.bicep index d0e8753..f41f4ee 100644 --- a/infra/core/ai/ai-project.bicep +++ b/infra/core/ai/ai-project.bicep @@ -98,12 +98,12 @@ resource aiAccount 'Microsoft.CognitiveServices/accounts@2025-06-01' = { @batchSize(1) resource seqDeployments 'deployments' = [ - for dep in (deployments??[]): { + for dep in (deployments ?? []): { name: dep.name + sku: dep.sku properties: { model: dep.model } - sku: dep.sku } ] @@ -314,28 +314,27 @@ output dependentResources object = { } } +// Add simple confirmation outputs (so main.bicep can surface them) +output openAiDeploymentNames array = [for dep in (deployments ?? []): dep.name] + +// Replace the deploymentsType definition with a concrete schema type deploymentsType = { - @description('Specify the name of cognitive service account deployment.') + @description('Deployment name') name: string - @description('Required. Properties of Cognitive Services account deployment model.') + @description('Model definition for the deployment') model: { - @description('Required. The name of Cognitive Services account deployment model.') - name: string - - @description('Required. The format of Cognitive Services account deployment model.') + @description('Model format, e.g. OpenAI') format: string - - @description('Required. The version of Cognitive Services account deployment model.') - version: string + @description('Model name, e.g. gpt-4.1') + name: string + @description('Optional model version') + version: string? } - @description('The resource model definition representing SKU.') + @description('SKU for the deployment') sku: { - @description('Required. The name of the resource model definition representing SKU.') name: string - - @description('The capacity of the resource model definition representing SKU.') capacity: int } }[]? diff --git a/infra/main.bicep b/infra/main.bicep index 3675047..f5fcf9d 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -66,7 +66,33 @@ param aiFoundryResourceName string = '' param aiFoundryProjectName string = 'ai-project-${environmentName}' @description('List of model deployments') -param aiProjectDeploymentsJson string = '[]' +param aiProjectDeploymentsJson string = ''' +[ + { + "name": "gpt-4.1-mini", + "model": { + "format": "OpenAI", + "name": "gpt-4.1-mini" + }, + "sku": { + "name": "GlobalStandard", + "capacity": 10 + } + }, + { + "name": "gpt-4.1", + "model": { + "format": "OpenAI", + "name": "gpt-4.1" + }, + "sku": { + "name": "GlobalStandard", + "capacity": 10 + } + } + +] +''' @description('List of connections') param aiProjectConnectionsJson string = '[]' @@ -166,3 +192,5 @@ output AZURE_AI_SEARCH_SERVICE_NAME string = aiProject.outputs.dependentResource output AZURE_STORAGE_CONNECTION_NAME string = aiProject.outputs.dependentResources.storage.connectionName output AZURE_STORAGE_ACCOUNT_NAME string = aiProject.outputs.dependentResources.storage.accountName +// OpenAI Deployments +output AZURE_OPENAI_DEPLOYMENT_NAMES array = aiProject.outputs.openAiDeploymentNames diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 323829e..de56a9d 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -26,9 +26,6 @@ "principalType": { "value": "${AZURE_PRINCIPAL_TYPE}" }, - "aiProjectDeploymentsJson": { - "value": "${AI_PROJECT_DEPLOYMENTS=[]}" - }, "aiProjectConnectionsJson": { "value": "${AI_PROJECT_CONNECTIONS=[]}" }, diff --git a/requirements.txt b/requirements.txt index 2743d0e..c5bb397 100644 --- a/requirements.txt +++ b/requirements.txt @@ -46,5 +46,4 @@ markdown>=3.5.0 Jinja2>=3.1.0 # GitHub Actions and automation -pyyaml>=6.0.0 - +pyyaml>=6.0.0 \ No newline at end of file diff --git a/src/agents/trail_guide_agent/trail_guide_agent.py b/src/agents/trail_guide_agent/trail_guide_agent.py index 5f80de5..04a951c 100644 --- a/src/agents/trail_guide_agent/trail_guide_agent.py +++ b/src/agents/trail_guide_agent/trail_guide_agent.py @@ -6,7 +6,7 @@ from azure.ai.projects.models import PromptAgentDefinition # Load environment variables from repository root -repo_root = Path(__file__).parent.parent.parent +repo_root = Path(__file__).parent.parent.parent.parent env_file = repo_root / '.env' load_dotenv(env_file) diff --git a/src/tests/interact_with_agent.py b/src/tests/interact_with_agent.py index 8253fda..3a2000f 100644 --- a/src/tests/interact_with_agent.py +++ b/src/tests/interact_with_agent.py @@ -25,6 +25,8 @@ def interact_with_agent(): # Get agent name from environment or use default agent_name = os.getenv("AGENT_NAME", "trail-guide-v1") + + openai_client = project_client.get_openai_client() print(f"\n{'='*60}") print(f"Trail Guide Agent - Interactive Chat") @@ -33,8 +35,8 @@ def interact_with_agent(): print("\nType your questions or requests. Type 'exit' or 'quit' to end the session.\n") # Create a thread for the conversation - thread = project_client.agents.create_thread() - print(f"Started conversation (Thread ID: {thread.id})\n") + conversation = openai_client.conversations.create() + print(f"Started conversation (Conversation ID: {conversation.id})\n") try: while True: @@ -48,27 +50,25 @@ def interact_with_agent(): print("\nEnding session. Goodbye!") break - # Send message to agent - project_client.agents.create_message( - thread_id=thread.id, - role="user", - content=user_input + # 1) Add the user message to the conversation as an item + openai_client.conversations.items.create( + conversation_id=conversation.id, + items=[{ + "type": "message", + "role": "user", + "content": user_input + }] ) - - # Run the agent - run = project_client.agents.create_and_process_run( - thread_id=thread.id, - agent_name=agent_name + + # 2) Ask the agent to respond + response = openai_client.responses.create( + # NOTE: the sample uses `conversation=...` (not conversation_id) + conversation=conversation.id, + extra_body={"agent": {"name": agent_name, "type": "agent_reference"}}, + input="", # sample keeps this empty because the message is already in the conversation items ) - - # Get the assistant's response - messages = project_client.agents.list_messages(thread_id=thread.id) - - # Find the latest assistant message - for message in messages: - if message.role == "assistant": - print(f"\nAgent: {message.content[0].text.value}\n") - break + + print(f"Agent: {response.output_text}") except KeyboardInterrupt: print("\n\nSession interrupted. Goodbye!") @@ -78,10 +78,10 @@ def interact_with_agent(): finally: # Clean up thread try: - project_client.agents.delete_thread(thread.id) + openai_client.conversations.delete(conversation.id) print(f"Conversation thread cleaned up.") except: pass if __name__ == "__main__": - interact_with_agent() + interact_with_agent() \ No newline at end of file