-
Notifications
You must be signed in to change notification settings - Fork 162
update #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update #60
Changes from all commits
c906c1f
096b6aa
9094c4e
8e4f512
b22bee1
23d8ac7
a9984e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,3 +30,7 @@ Thumbs.db | |
| **/coverage | ||
| **/.pytest_cache | ||
| **/.mypy_cache | ||
|
|
||
| # Large documentation files | ||
| foundry*.md | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # Hindsight Agent API - Azure Container Apps Deployment | ||
| # Multi-stage build for smaller image | ||
|
|
||
| FROM python:3.12-slim as builder | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install build dependencies | ||
| RUN pip install --no-cache-dir --upgrade pip | ||
|
|
||
| # Copy requirements and install | ||
| COPY requirements-agent-api.txt . | ||
| RUN pip install --no-cache-dir -r requirements-agent-api.txt | ||
|
|
||
| # Production stage | ||
| FROM python:3.12-slim | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Copy installed packages from builder | ||
| COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages | ||
| COPY --from=builder /usr/local/bin /usr/local/bin | ||
|
|
||
| # Copy application code | ||
| COPY hindsight_agent_api.py . | ||
| COPY hindsight_client.py . | ||
| COPY config.py . | ||
|
|
||
| # Create non-root user for security | ||
| RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app | ||
| USER appuser | ||
|
|
||
| # Azure Container Apps expects port 8080 by default | ||
| ENV PORT=8080 | ||
| ENV HOST=0.0.0.0 | ||
|
|
||
| EXPOSE 8080 | ||
|
|
||
| # Health check | ||
| HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ | ||
| CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1 | ||
|
|
||
| CMD ["python", "hindsight_agent_api.py"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| """ | ||
| Centralized configuration module for Hindsight Foundry Agent. | ||
|
|
||
| Loads configuration from Azure App Configuration with fallback to environment variables. | ||
| """ | ||
| import os | ||
| from dataclasses import dataclass | ||
| from typing import Optional | ||
|
|
||
| # Try to import Azure App Configuration provider | ||
| try: | ||
| from azure.appconfiguration.provider import load | ||
| # DefaultAzureCredential unused but keeping if needed for future AAD auth | ||
| HAS_APP_CONFIG = True | ||
| except ImportError: | ||
| HAS_APP_CONFIG = False | ||
|
|
||
|
|
||
| @dataclass | ||
| class HindsightConfig: | ||
| """Configuration for the Hindsight Foundry Agent.""" | ||
| project_endpoint: str | ||
| model_deployment_name: str | ||
| mcp_base_url: str | ||
| default_bank_id: str | ||
| agent_name: str | ||
|
|
||
| @property | ||
| def mcp_url(self) -> str: | ||
| """Full MCP URL including bank_id path.""" | ||
| base = self.mcp_base_url.rstrip('/') | ||
| return f"{base}/mcp/{self.default_bank_id}/" | ||
|
|
||
|
|
||
| # Azure App Configuration endpoint and connection string | ||
| APP_CONFIG_ENDPOINT = "https://hindsightapp.azconfig.io" | ||
| # Use environment variable for secrets - never hardcode credentials | ||
| APP_CONFIG_CONNECTION_STRING = os.environ.get( | ||
| "AZURE_APP_CONFIG_CONNECTION_STRING", | ||
| "" # Must be set in environment for App Config to work | ||
| ) | ||
|
|
||
| # Prefix for configuration keys in App Configuration | ||
| CONFIG_PREFIX = "Hindsight:" | ||
|
|
||
|
|
||
| def _get_from_app_config() -> Optional[dict]: | ||
| """Load configuration from Azure App Configuration.""" | ||
| if not HAS_APP_CONFIG: | ||
| print("Azure App Configuration SDK not installed") | ||
| return None | ||
|
|
||
| try: | ||
| from azure.appconfiguration.provider import SettingSelector | ||
|
|
||
| # Load using connection string (access key auth) | ||
| if not APP_CONFIG_CONNECTION_STRING: | ||
| print("WARNING: AZURE_APP_CONFIG_CONNECTION_STRING is empty. App Config will fail.") | ||
|
|
||
| config = load( | ||
| connection_string=APP_CONFIG_CONNECTION_STRING, | ||
| selects=[SettingSelector(key_filter=f"{CONFIG_PREFIX}*")], | ||
| trim_prefixes=[CONFIG_PREFIX], | ||
| ) | ||
|
|
||
| if config: | ||
| print("Loaded configuration from Azure App Configuration") | ||
| return dict(config) | ||
|
|
||
| except Exception as e: | ||
| print(f"WARNING: Could not load from App Configuration: {e}") | ||
|
|
||
| return None | ||
|
|
||
|
|
||
| def _get_from_env() -> dict: | ||
| """Load configuration from environment variables.""" | ||
| return { | ||
| "ProjectEndpoint": os.environ.get( | ||
| "HINDSIGHT_PROJECT_ENDPOINT", | ||
| "https://jacob-1216-resource.services.ai.azure.com/api/projects/jacob-1216" | ||
| ), | ||
| # Use gpt-4o as default - verified working deployment | ||
| # Set HINDSIGHT_MODEL_DEPLOYMENT_NAME to override | ||
| "ModelDeploymentName": os.environ.get( | ||
| "HINDSIGHT_MODEL_DEPLOYMENT_NAME", | ||
| "gpt-5.2-chat" | ||
| ), | ||
| "McpBaseUrl": os.environ.get( | ||
| "HINDSIGHT_MCP_BASE_URL", | ||
| "https://hindsight-api.politebay-1635b4f9.centralus.azurecontainerapps.io" | ||
| ), | ||
| "DefaultBankId": os.environ.get( | ||
| "HINDSIGHT_DEFAULT_BANK_ID", | ||
| "hindsight_agent_bank" | ||
| ), | ||
| "AgentName": os.environ.get( | ||
| "HINDSIGHT_AGENT_NAME", | ||
| "Hindsight" | ||
| ), | ||
| } | ||
|
|
||
|
|
||
| def get_config() -> HindsightConfig: | ||
| """ | ||
| Get configuration from Azure App Configuration or environment variables. | ||
|
|
||
| Priority: | ||
| 1. Azure App Configuration (if available) | ||
| 2. Environment variables | ||
| 3. Hardcoded defaults | ||
| """ | ||
| # Try App Configuration first | ||
| config_dict = _get_from_app_config() | ||
|
|
||
| # Allow environment variable overrides | ||
| if config_dict: | ||
| if "HINDSIGHT_MODEL_DEPLOYMENT_NAME" in os.environ: | ||
| print(f"ℹ Overriding model with: {os.environ['HINDSIGHT_MODEL_DEPLOYMENT_NAME']}") | ||
| config_dict["ModelDeploymentName"] = os.environ["HINDSIGHT_MODEL_DEPLOYMENT_NAME"] | ||
|
|
||
| # Fall back to environment variables | ||
| if not config_dict: | ||
| print("Using environment/default configuration") | ||
| config_dict = _get_from_env() | ||
|
|
||
| return HindsightConfig( | ||
| project_endpoint=config_dict.get("ProjectEndpoint", ""), | ||
| model_deployment_name=config_dict.get("ModelDeploymentName", "gpt-5.2-chat"), | ||
| mcp_base_url=config_dict.get("McpBaseUrl", ""), | ||
| default_bank_id=config_dict.get("DefaultBankId", "hindsight_agent_bank"), | ||
| agent_name=config_dict.get("AgentName", "Hindsight"), | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| # Test configuration loading | ||
| print("Testing configuration loading...") | ||
| config = get_config() | ||
| print(f"\nConfiguration loaded:") | ||
| print(f" Project Endpoint: {config.project_endpoint}") | ||
| print(f" Model: {config.model_deployment_name}") | ||
| print(f" MCP Base URL: {config.mcp_base_url}") | ||
| print(f" Default Bank ID: {config.default_bank_id}") | ||
| print(f" Agent Name: {config.agent_name}") | ||
| print(f" Full MCP URL: {config.mcp_url}") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| """Create Hindsight agent with complete OpenAPI spec.""" | ||
| import json | ||
| from azure.identity import AzureCliCredential | ||
| from azure.ai.projects import AIProjectClient | ||
| from azure.ai.projects.models import ( | ||
| PromptAgentDefinition, | ||
| OpenApiAgentTool, | ||
| OpenApiFunctionDefinition, | ||
| OpenApiManagedAuthDetails, | ||
| OpenApiManagedSecurityScheme | ||
| ) | ||
|
|
||
| # Load full spec | ||
| with open('hindsight-tools-openapi-full.json', 'r') as f: | ||
| openapi_spec = json.load(f) | ||
|
|
||
| print(f"Loaded spec with {len(openapi_spec['paths'])} endpoints") | ||
|
|
||
| # Connect | ||
| credential = AzureCliCredential() | ||
| import os | ||
| project_endpoint = os.environ.get( | ||
| 'HINDSIGHT_PROJECT_ENDPOINT', | ||
| 'https://jacob-1216-resource.services.ai.azure.com/api/projects/jacob-1216' | ||
| ) | ||
| client = AIProjectClient(credential=credential, endpoint=project_endpoint) | ||
|
|
||
| # Create OpenAPI function definition | ||
| openapi_func_def = OpenApiFunctionDefinition( | ||
| name='hindsight_memory_api', | ||
| description='Complete Hindsight Memory API - memory storage, retrieval, reflection, bank management, entity management, documents, operations, and system monitoring', | ||
| spec=openapi_spec, | ||
| auth=OpenApiManagedAuthDetails( | ||
| security_scheme=OpenApiManagedSecurityScheme(audience='https://cognitiveservices.azure.com') | ||
| ) | ||
| ) | ||
|
|
||
| # Create OpenAPI tool | ||
| openapi_tool = OpenApiAgentTool(openapi=openapi_func_def) | ||
|
|
||
| # Agent instructions | ||
| INSTRUCTIONS = '''You are Hindsight, an AI with persistent memory capabilities. | ||
|
|
||
| ## Core Memory Operations | ||
| - **retain**: Store new memories (facts, experiences, opinions) | ||
| - **recall**: Search and retrieve relevant memories using semantic search | ||
| - **reflect**: Synthesize memories into coherent understanding | ||
|
|
||
| ## Memory Types | ||
| - **world**: Facts about the external world | ||
| - **experience**: Personal experiences and events | ||
| - **opinion**: Beliefs, preferences, and judgments | ||
|
|
||
| ## Admin Capabilities | ||
| You can also manage the memory system: | ||
| - List, create, update, and delete memory banks | ||
| - View bank profiles and statistics | ||
| - List and manage memories within banks | ||
| - View entities and their observations | ||
| - Manage documents and chunks | ||
| - Monitor async operations | ||
| - Check system health and metrics | ||
|
|
||
| ## Behavior | ||
| 1. Before answering questions about past conversations or user preferences, use recall | ||
| 2. Store important information the user shares using retain | ||
| 3. Use reflect to build comprehensive understanding of topics | ||
| 4. When asked about system status, use health/metrics endpoints | ||
| 5. Help users manage their memory banks when requested | ||
|
|
||
| Always be helpful and use your memory capabilities proactively.''' | ||
|
|
||
| # Create agent definition (name goes in create_version, not definition) | ||
| agent_def = PromptAgentDefinition( | ||
| model='gpt-5.2-chat', | ||
| instructions=INSTRUCTIONS, | ||
| tools=[openapi_tool] | ||
| ) | ||
|
|
||
| # Create new version | ||
| result = client.agents.create_version('Hindsight-v3', definition=agent_def) | ||
| print(f"Created agent: {result.name}:{result.version}") | ||
| print(f"Model: {result.definition.model}") | ||
| print(f"Tools: {len(result.definition.tools)}") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| #!/usr/bin/env pwsh | ||
| <# | ||
| .SYNOPSIS | ||
| Deploy Hindsight Agent API to Azure using Bicep | ||
|
|
||
| .DESCRIPTION | ||
| This script deploys the Hindsight Agent API infrastructure using Bicep | ||
| and builds/pushes the container image to ACR. | ||
|
|
||
| .PARAMETER ResourceGroup | ||
| The Azure resource group name (default: hindsight-rg) | ||
|
|
||
| .PARAMETER Location | ||
| The Azure region (default: centralus) | ||
|
|
||
| .EXAMPLE | ||
| .\deploy-bicep.ps1 | ||
| .\deploy-bicep.ps1 -ResourceGroup my-rg -Location eastus | ||
| #> | ||
|
|
||
| param( | ||
| [string]$ResourceGroup = "hindsight-rg", | ||
| [string]$Location = "centralus", | ||
| [string]$ImageTag = "latest", | ||
| [string]$AiResourceGroup = "jacob-1216-resource", | ||
| [string]$AiResourceName = "jacob-1216-resource" | ||
| ) | ||
|
|
||
| $ErrorActionPreference = "Stop" | ||
| $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path | ||
|
|
||
| Write-Host "Deploying Hindsight Agent API with Bicep" -ForegroundColor Cyan | ||
| Write-Host " Resource Group: $ResourceGroup" | ||
| Write-Host " Location: $Location" | ||
|
|
||
| # Check Azure CLI login | ||
| Write-Host "`nChecking Azure CLI authentication..." -ForegroundColor Yellow | ||
| $accountJson = az account show 2>$null | ||
| if (-not $accountJson) { | ||
| Write-Host "Not logged in to Azure CLI. Run 'az login' first." -ForegroundColor Red | ||
| exit 1 | ||
| } | ||
| $account = $accountJson | ConvertFrom-Json | ||
| Write-Host " Logged in as: $($account.user.name)" | ||
| Write-Host " Subscription: $($account.name)" | ||
|
|
||
| # Create resource group if needed | ||
| Write-Host "`nEnsuring resource group exists..." -ForegroundColor Yellow | ||
| az group create --name $ResourceGroup --location $Location --output none 2>$null | ||
|
|
||
| # Deploy Bicep template | ||
| Write-Host "`nDeploying infrastructure with Bicep..." -ForegroundColor Yellow | ||
| $deploymentJson = az deployment group create --resource-group $ResourceGroup --template-file "$ScriptDir/infra/agent-api.bicep" --parameters location=$Location imageTag=$ImageTag aiProjectResourceGroup=$AiResourceGroup aiResourceName=$AiResourceName --query "properties.outputs" --output json | ||
|
|
||
| if (-not $deploymentJson) { | ||
| Write-Host "Bicep deployment failed" -ForegroundColor Red | ||
| exit 1 | ||
| } | ||
|
|
||
| $deploymentOutput = $deploymentJson | ConvertFrom-Json | ||
|
|
||
| $acrLoginServer = $deploymentOutput.acrLoginServer.value | ||
| $containerAppName = $deploymentOutput.containerAppName.value | ||
| $containerAppUrl = $deploymentOutput.containerAppUrl.value | ||
| $principalId = $deploymentOutput.principalId.value | ||
|
|
||
| Write-Host " ACR: $acrLoginServer" | ||
| Write-Host " Container App: $containerAppName" | ||
| Write-Host " Principal ID: $principalId" | ||
|
|
||
| # Build and push container image | ||
| Write-Host "`nBuilding and pushing container image..." -ForegroundColor Yellow | ||
| $acrName = $acrLoginServer.Split('.')[0] | ||
|
|
||
| az acr build --registry $acrName --resource-group $ResourceGroup --image "${containerAppName}:$ImageTag" --file "$ScriptDir/Dockerfile.agent-api" $ScriptDir | ||
|
|
||
| # Update container app to use new image (triggers deployment) | ||
| Write-Host "`nUpdating container app..." -ForegroundColor Yellow | ||
| az containerapp update --name $containerAppName --resource-group $ResourceGroup --image "$acrLoginServer/${containerAppName}:$ImageTag" --output none | ||
|
|
||
| # Assign RBAC to AI Project (cross-resource-group) | ||
| Write-Host "`nAssigning RBAC to AI Project..." -ForegroundColor Yellow | ||
| $subscriptionId = $account.id | ||
| $aiResourceId = "/subscriptions/$subscriptionId/resourceGroups/$AiResourceGroup/providers/Microsoft.CognitiveServices/accounts/$AiResourceName" | ||
|
|
||
| az role assignment create --assignee $principalId --role "Cognitive Services User" --scope $aiResourceId --output none 2>$null | ||
|
Comment on lines
81
to
86
|
||
|
|
||
| Write-Host "`nDeployment complete!" -ForegroundColor Green | ||
| Write-Host "" | ||
| Write-Host " API URL: $containerAppUrl" -ForegroundColor Cyan | ||
| Write-Host " API Docs: $containerAppUrl/docs" | ||
| Write-Host " Health: $containerAppUrl/health" | ||
| Write-Host "" | ||
| Write-Host "Test with:" -ForegroundColor Yellow | ||
| Write-Host "Invoke-RestMethod -Uri '$containerAppUrl/chat' -Method Post -ContentType 'application/json' -Body '{""message"":""Hello!""}'" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5.2 is very slow and we don't want poor first-user experience