Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,16 @@ TEAMS_AUTH_AUDIENCES=<audience-1>,<audience-2>
AZURE_SPEECH_REGION=<your-speech-region>
AZURE_SPEECH_RESOURCE_ID=<your-speech-resource-name>

# Azure Voice Live (preview — Foundry resource)
# Azure Voice Live (Foundry resource)
# Server runtime reads VOICE_PROVIDER to decide whether to attach /api/voice-live bridge.
# Keep this in sync with the client bundle provider selected by npm scripts
# (build:client:* or start:* sets __SPEECH_PROVIDER__).
# Required when VOICE_PROVIDER=voicelive and client is built for voicelive.
# Auth: server-side DefaultAzureCredential (no key); identity needs "Cognitive Services User" on the resource
# gpt-realtime requires a supported Voice Live region; use East US 2 for the documented Azure setup.
AZURE_VOICELIVE_RESOURCE=<your-voicelive-resource>
AZURE_VOICELIVE_MODEL=gpt-realtime
AZURE_VOICELIVE_API_VERSION=2025-10-01
AZURE_VOICELIVE_API_VERSION=2026-04-10

# Voice provider selector (server runtime): "azure" (default), "webspeech", or "voicelive"
# Client provider is chosen at build time via __SPEECH_PROVIDER__.
Expand Down
18 changes: 13 additions & 5 deletions src/500-application/516-chat-with-your-factory/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Chat With Factory
description: A voice-enabled AI agent web application for industrial environments powered by Azure AI Foundry Agents or Copilot Studio
ms.date: 2026-06-29
ms.date: 2026-08-04
ms.topic: overview
keywords:
- chat with factory
Expand Down Expand Up @@ -200,7 +200,7 @@ Create the Foundry resource, project, and model deployment:

```powershell
$RG_NAME = "rg-chat-with-your-factory"
$LOCATION = "eastus"
$LOCATION = "eastus2"
Comment thread
benljbrooks marked this conversation as resolved.
$AI_NAME = "chat-factory-ai"
$PROJECT = "chat-factory-project"

Expand All @@ -214,6 +214,10 @@ az cognitiveservices account create `
--location $LOCATION `
--custom-domain $AI_NAME

# East US 2 supports the default gpt-realtime model for Voice Live.
# Existing AI Services resources cannot change regions; create a replacement
# in East US 2 and update AZURE_VOICELIVE_RESOURCE when migrating from East US.

az cognitiveservices account deployment create `
--name $AI_NAME `
--resource-group $RG_NAME `
Expand Down Expand Up @@ -544,7 +548,7 @@ Each concern maps to a specific technology. No component serves double duty. The
| Concern | Technology | Details | MVP Upgrade |
|---------------------------|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| Speech-to-text (STT) | Configurable: Azure Speech SDK (default) or Browser Web Speech API or Voice Live | Build-time switch via `npm run build:client:azure`, `npm run build:client:webspeech`, or `npm run build:client:voicelive`. esbuild tree-shakes the unused providers at build time. Voice Live (`VOICE_PROVIDER=voicelive`) uses a server-proxied WebSocket bridge at `/api/voice-live` — see ADR 0001. | Server-side real-time STT via Azure Speech SDK. Browser streams raw audio to Express server via WebSocket; server runs Speech SDK for transcription. |
| Text-to-speech (TTS) | None today | Voice Live can produce TTS in principle, but the current bridge runs it as STT/VAD only (`modalities: ['text']`, `create_response: false`) — see ADR 0001. Agent responses render as text in all configurations. | Azure Speech neural TTS for optional read-aloud via `/api/tts` endpoint, or enable Voice Live audio output once topology supports it. |
| Text-to-speech (TTS) | Voice Live PCM16 output | The authoritative backend Markdown is persisted and rendered as text. A citation- and URL-free derivative is sent to Voice Live, and the browser plays the returned 24 kHz PCM16 audio. | Add distributed synthesis latency and interruption telemetry. |
| AI / LLM backend | Copilot Studio (Agents SDK), Azure AI Foundry, or Copilot Studio (Direct Line) | Configurable via `AGENT_BACKEND` env var (`copilotstudio` default). Agents SDK uses `@microsoft/agents-copilotstudio-client` with OBO token exchange and streaming. Foundry uses Assistants-style threads with `createAndPoll`. Direct Line uses fire-and-forget POST with WebSocket relay for bot replies. | Foundry streaming via `createAndStream` + SSE for real-time token delivery. |
| Server framework | Express v5 (TypeScript, ESM) | Serves static files, REST API routes, SSE broadcast, and `Permissions-Policy` header for iframe microphone access. | Same. Native async error handling. Managed Identity via `DefaultAzureCredential` in production. |
| User authentication | Teams SSO + JWT validation (`jose`) | Validates AAD v2.0 tokens. Dev bypass via `SKIP_AUTH=true`. | Remove `SKIP_AUTH` bypass in production. |
Expand All @@ -560,7 +564,7 @@ A single user interaction follows this path:

1. The user clicks the mic button or types a message.
2. For voice: the `useSpeech` barrel hook delegates to either the Web Speech API, Azure Speech SDK, or Voice Live hook based on the build-time `__SPEECH_PROVIDER__` constant.
3. For Web Speech and Azure Speech providers, the React client sends a POST to `/api/chat` with the message text. For Voice Live, the browser sends raw PCM16 audio over a WebSocket to `/api/voice-live`; the server bridge handles transcription and agent dispatch (via `dispatchChat`) no `/api/chat` POST is made for voice turns. Agent responses arrive via SSE and render as text (no TTS playback today).
3. For Web Speech and Azure Speech providers, the React client sends a POST to `/api/chat` with the message text. For Voice Live, the browser sends raw PCM16 audio over a WebSocket to `/api/voice-live`; the server bridge handles transcription and agent dispatch (via `dispatchChat`), so no `/api/chat` POST is made for voice turns. Agent responses arrive as authoritative Markdown through SSE, while Voice Live returns a speech-safe PCM16 rendition for browser playback.
4. The Express server validates the JWT, checks the user's session ACL, stores the user message locally, and broadcasts it via SSE.
5. **Copilot Studio (Agents SDK) backend**: The server performs an OBO token exchange using MSAL, connects to the Copilot Studio agent via `@microsoft/agents-copilotstudio-client`, streams the response, stores it locally, broadcasts via SSE, and returns it in the HTTP response.
6. **Foundry backend**: The server forwards the message to the Foundry agent via `agentsClient.messages.create`, calls `createAndPoll`, retrieves the response, stores it locally, broadcasts via SSE, and returns it in the HTTP response.
Expand Down Expand Up @@ -634,7 +638,11 @@ the result back to Foundry.
4. The agent response appears in the chat panel.

> [!TIP]
> The default speech provider (Azure Speech SDK) works across all browsers. To use the browser Web Speech API instead (Chrome/Edge only), run `npm run start:webspeech`. To use Voice Live (preview), set `VOICE_PROVIDER=voicelive` and `AZURE_VOICELIVE_RESOURCE` in `.env`, then run `npm run start:voicelive`. See ADR 0001 for details. The text input fallback is always available.
> The default speech provider (Azure Speech SDK) works across all browsers. To use the browser Web Speech API instead (Chrome/Edge only), run `npm run start:webspeech`.
>
> To use Voice Live, create the AI Services resource in a [region that supports `gpt-realtime`](https://learn.microsoft.com/azure/ai-services/speech-service/regions?tabs=voice-live#regions), such as East US 2. Set `VOICE_PROVIDER=voicelive`, `AZURE_VOICELIVE_RESOURCE`, `AZURE_VOICELIVE_MODEL=gpt-realtime`, and `AZURE_VOICELIVE_API_VERSION=2026-04-10` in `.env`, then run `npm run start:voicelive`.
>
> Existing AI Services resources cannot change regions and must be replaced when migrating from East US. See ADR 0001 for details. The text input fallback is always available.
>
> Plain `npm start` always rebuilds the client with the **default Azure Speech SDK** provider, which will overwrite any voicelive / webspeech bundle you previously built. Use `npm run start:voicelive` or `npm run start:webspeech` to keep the matching client bundle in sync with the server in one step.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ spec:
metadata:
labels:
{{- include "chat-with-your-factory.selectorLabels" . | nindent 8 }}
{{- with .Values.serviceAccount.name }}
azure.workload.identity/use: "true"
{{- end }}
spec:
{{- with .Values.serviceAccount.name }}
serviceAccountName: {{ . | quote }}
{{- end }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ imagePullSecrets:
nameOverride: ""
fullnameOverride: ""

# Externally managed Azure Workload Identity ServiceAccount. Leave name empty
# to render no ServiceAccount binding or workload identity pod label.
serviceAccount:
name: ""

# Dedicated namespace scopes RBAC, NetworkPolicies, and quotas to this workload.
# Operators may override via --set namespace=<value>.
namespace: chat-with-your-factory
Expand All @@ -39,6 +44,14 @@ resources:
env:
AGENT_BACKEND: "foundry"
PORT: "3978"
# Runtime VOICE_PROVIDER=voicelive requires an image built with the Docker
# build argument SPEECH_PROVIDER=voicelive; SPEECH_PROVIDER is build-time only.
VOICE_PROVIDER: "azure"
# Required deployment override when VOICE_PROVIDER=voicelive. The default
# gpt-realtime model requires a supported Voice Live region such as East US 2.
AZURE_VOICELIVE_RESOURCE: ""
AZURE_VOICELIVE_MODEL: "gpt-realtime"
AZURE_VOICELIVE_API_VERSION: "2026-04-10"
# Foundry backend (AGENT_BACKEND=foundry): point at the 085-ai-foundry component.
# FOUNDRY_ENDPOINT is the Foundry project endpoint; FOUNDRY_AGENT_ID is the deployed agent.
# Create the agent reproducibly with `npm run provision:agent` (needs FOUNDRY_MODEL_DEPLOYMENT).
Expand Down Expand Up @@ -72,3 +85,5 @@ env:
secrets: {}
# DIRECT_LINE_SECRET: "<copilot-studio-web-channel-secret>"
# TEAMS_APP_CLIENT_SECRET: "<client-secret-from-app-registration>"
# Required outside development; inject externally with no committed default.
# RESUME_TOKEN_SECRET: "<externally-injected-resume-token-secret>"
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ FROM node:22-slim@sha256:813a7480f28fdadac1f7f5c824bcdad435b5bc1322a5968bbbdef8d
WORKDIR /app

ARG SPEECH_PROVIDER=azure
ARG NPM_REGISTRY=https://packagefeedproxy.microsoft.io/npm/

COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
RUN npm ci --ignore-scripts --registry="${NPM_REGISTRY}"
Comment thread
benljbrooks marked this conversation as resolved.

COPY tsconfig.json tsconfig.server.json ./
COPY src/ src/
Expand Down
Loading
Loading