Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions agentic_ai/observability/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""

import os
import sys
import logging
from typing import Optional

Expand All @@ -26,6 +27,43 @@
_initialized = False


def _ensure_utf8_console() -> None:
"""Force stdout/stderr to UTF-8 so emoji/unicode never crash a print().

On Windows the default console encoding is cp1252, which raises
UnicodeEncodeError when printing emoji (✅, ⚠️, 🚀, etc.). Because this
module is imported at process startup (before any agent output), calling
this here makes every downstream print()/log safe. Uses errors="replace"
as a final guard so even an un-encodable glyph degrades gracefully instead
of crashing the service.
"""
for stream_name in ("stdout", "stderr"):
stream = getattr(sys, stream_name, None)
reconfigure = getattr(stream, "reconfigure", None)
if reconfigure is None:
continue
try:
reconfigure(encoding="utf-8", errors="replace")
except Exception:
# Never let console hardening break startup.
pass


# Harden the console as soon as this module is imported.
_ensure_utf8_console()


def _safe_print(message: str) -> None:
"""print() that can never raise on un-encodable characters."""
try:
print(message)
except Exception:
try:
print(message.encode("ascii", "replace").decode("ascii"))
except Exception:
pass


def setup_observability(
connection_string: Optional[str] = None,
service_name: str = "contoso-agent",
Expand Down Expand Up @@ -89,19 +127,19 @@ def _safe_model_dump_json(self, **kwargs):
pydantic.BaseModel.model_dump_json = _safe_model_dump_json # type: ignore[assignment]

_initialized = True
print(f"✅ Application Insights observability enabled (service: {service_name})")
_safe_print(f"✅ Application Insights observability enabled (service: {service_name})")
logger.info(f"✅ Application Insights observability enabled (service: {service_name})")
return True

except ImportError as e:
print(f"❌ Observability dependencies not installed: {e}")
_safe_print(f"❌ Observability dependencies not installed: {e}")
logger.warning(f"Observability dependencies not installed: {e}")
return False
except BaseException as e:
# Catch ALL errors including KeyboardInterrupt from import deadlocks
# in azure-ai-projects telemetry instrumentor (openai SDK version conflicts).
# Observability is never worth crashing the service.
print(f"⚠️ Observability setup failed (non-fatal): {type(e).__name__}: {e}")
_safe_print(f"⚠️ Observability setup failed (non-fatal): {type(e).__name__}: {e}")
logger.warning(f"Observability setup failed (non-fatal): {type(e).__name__}: {e}")
return False

Expand Down
61 changes: 51 additions & 10 deletions agentic_ai/workflow/fraud_detection_durable/.env.sample
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
# Azure OpenAI Configuration
# ============================================================================
# Azure OpenAI
# ============================================================================
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-4o
# Optional: Use API key instead of Azure CLI auth

# Authentication: by default the worker and backend use Entra ID via
# DefaultAzureCredential (run `az login`). This is the recommended path and
# needs NO API key. Only uncomment AZURE_OPENAI_API_KEY if you specifically
# want key-based auth — note a stale/invalid key here is a common source of
# 401 errors, so leave it commented unless you need it.
# AZURE_OPENAI_API_KEY=your-api-key

# MCP Server
# ============================================================================
# MCP Server (Contoso tools)
# ============================================================================
MCP_SERVER_URI=http://localhost:8000/mcp

# Durable Task Scheduler
# Local emulator (default)
# ============================================================================
# Durable Task Scheduler (DTS) — choose ONE mode
# ============================================================================
# The code selects the mode purely from the DTS_ENDPOINT prefix:
# - http://localhost... -> local emulator, insecure channel, NO credential
# - anything else -> Azure DTS, secure channel, DefaultAzureCredential
#
# --- Mode A: Local emulator (Docker) -----------------------------------------
# Start it first:
# docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 \
# mcr.microsoft.com/dts/dts-emulator:latest
# Or use: .\start.ps1 -StartEmulator
DTS_ENDPOINT=http://localhost:8080
DTS_TASKHUB=fraud-detection
#
# --- Mode B: Azure DTS (managed) ---------------------------------------------
# Requires `az login` (DefaultAzureCredential) with the Durable Task Data
# Contributor role on the scheduler. Provision with provision_dts.ps1.
# DTS_ENDPOINT=https://your-scheduler.<region>.durabletask.io
# DTS_TASKHUB=fraud-detection

# Azure-hosted (production)
# DTS_ENDPOINT=https://your-dts-endpoint.azure.com
# DTS_TASKHUB=fraud-detection-prod

# Human-in-the-loop Configuration
# ============================================================================
# Human-in-the-loop
# ============================================================================
ANALYST_APPROVAL_TIMEOUT_HOURS=72
MAX_REVIEW_ATTEMPTS=3

# ============================================================================
# Layer 1 ambient event producer
# ============================================================================
# Default OFF so a presenter controls when the ambient feed begins (start it
# from the UI power button or POST /api/producer/start). Set to true to
# auto-start the feed on backend boot.
EVENT_PRODUCER_ENABLED=false
# Seconds between synthetic telemetry events.
EVENT_INTERVAL_SECONDS=3.0
# Probability (0-1) that a post-warmup event is anomalous.
ANOMALY_PROBABILITY=0.08

# ============================================================================
# Application Insights (optional - enables telemetry)
# ============================================================================
# Telemetry is fully optional and never required for the demo. Observability
# setup is hardened to never crash startup if this is unset or misconfigured.
# APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...
# ENABLE_SENSITIVE_DATA=true
# Backend telemetry is gated separately (off by default):
# BACKEND_OBSERVABILITY=false
65 changes: 60 additions & 5 deletions agentic_ai/workflow/fraud_detection_durable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,18 +549,59 @@ This section covers how to run the reference implementation locally to see the p

### Prerequisites

- **Docker** — for DTS emulator
- **Python 3.12+** with **uv**
- **Node.js 18+** — for React UI
- **Azure OpenAI** — with a deployed chat model
- **MCP Server** — Contoso tools on port 8000
- **A Durable Task Scheduler** — either the local Docker emulator **or** an Azure DTS endpoint (see modes below)
- **Docker** — only if you use the local DTS emulator (Mode A)

### Service Startup Order
### Choose your DTS mode

The code picks the mode automatically from the `DTS_ENDPOINT` prefix in `.env`:

| | **Mode A — Local emulator** | **Mode B — Azure DTS** |
|---|---|---|
| `DTS_ENDPOINT` | `http://localhost:8080` | `https://<scheduler>.<region>.durabletask.io` |
| Channel | insecure | secure (TLS) |
| Auth | none | `DefaultAzureCredential` (`az login`) |
| Needs Docker | ✅ yes | ❌ no |
| Setup | `docker run … dts-emulator` | [provision_dts.ps1](provision_dts.ps1) |

> ⚠️ Make sure `DTS_ENDPOINT` in your `.env` matches the mode you actually start.
> A common mistake is starting the Docker emulator while `.env` points at Azure DTS
> (or vice-versa).

### Quick start (Windows)

The fastest path on Windows — handles UTF-8, startup order, and health gating:

```powershell
cd agentic_ai/workflow/fraud_detection_durable
uv sync
# Mode B (Azure DTS, default in .env):
.\start.ps1
# Mode A (also start the local Docker emulator):
.\start.ps1 -StartEmulator

# When done:
.\stop.ps1 # add -StopEmulator if you used Mode A
```

`start.ps1` launches MCP → worker → backend → UI in order, each in its own
window with logs under `.\logs\`, then prints the `/health` readiness summary.

> The ambient event feed starts **OFF** by default so you control when Scenario 1
> begins — press the ⏻ power button in the **Live Feed** panel, or
> `POST http://localhost:8001/api/producer/start`. Set `EVENT_PRODUCER_ENABLED=true`
> in `.env` to auto-start it.

### Manual startup (cross-platform)

```mermaid
%%{init: {'theme': 'base'}}%%
flowchart LR
S1["1️⃣ DTS Emulator<br/>Port 8080"]
S1["1️⃣ DTS<br/>emulator :8080 or Azure"]
S2["2️⃣ MCP Server<br/>Port 8000"]
S3["3️⃣ Worker<br/>Pulls from DTS"]
S4["4️⃣ Backend<br/>Port 8001"]
Expand All @@ -575,7 +616,7 @@ flowchart LR
style S5 fill:#e8f4fd,stroke:#4A90D9
```

#### 1. Start DTS Emulator
#### 1. Start the DTS (Mode A only)

```bash
docker run -d --name dts-emulator \
Expand All @@ -585,7 +626,8 @@ docker run -d --name dts-emulator \

Dashboard: http://localhost:8082

> **Production:** Replace with Azure DTS — same SDK, just change `DTS_ENDPOINT`. See [provision_dts.ps1](provision_dts.ps1).
> **Mode B (Azure DTS):** skip this step — just `az login` and set `DTS_ENDPOINT`
> to your scheduler. See [provision_dts.ps1](provision_dts.ps1).

#### 2. Start MCP Server

Expand Down Expand Up @@ -613,6 +655,19 @@ cd ui && npm install && npm run dev
# Open http://localhost:3000
```

> **Windows note:** the services force UTF-8 stdout/stderr so emoji in logs
> render correctly. If you launch them yourself with a custom wrapper, set
> `PYTHONUTF8=1` to be safe.

#### Verify readiness

```bash
curl http://localhost:8001/health
```

Returns `status: healthy` only when DTS, MCP, and the Azure OpenAI credential
all pass; otherwise `503` with a per-dependency breakdown.

---

## Demo Scenarios
Expand Down
Loading
Loading