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
164 changes: 164 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# =============================================================================
# Web-Agents environment configuration (EXAMPLE)
#
# Copy this file to `.env` and fill in the values:
# cp .env.example .env
#
# This single root `.env` is shared by BOTH the Python backend (loaded via
# python-dotenv) and the Vite frontend (loaded with envDir: '..'). Never commit
# the real `.env` — it is gitignored. Replace every placeholder below; values
# shown are examples, not real credentials.
#
# Legend: [REQUIRED] must be set [OPTIONAL] has a safe default
# =============================================================================


# -----------------------------------------------------------------------------
# Primary LLM — Azure OpenAI [REQUIRED]
# -----------------------------------------------------------------------------
# The SDK auto-detects the provider from these variables. For Azure OpenAI set
# the four AZURE_OPENAI_* values below. AZURE_OPENAI_MODEL is the *deployment*
# name. Leave AZURE_OPENAI_API_KEY empty to use managed identity (e.g. in Azure).
AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.us/"
AZURE_OPENAI_MODEL="gpt-4o"
AZURE_OPENAI_API_KEY="your-azure-openai-api-key"
AZURE_OPENAI_API_VERSION="2025-01-01-preview"

# --- Alternative: OpenAI-compatible provider (e.g. Ollama, OpenAI) -----------
# Use these INSTEAD of the AZURE_OPENAI_* block above for a non-Azure endpoint.
# OPENAI_API_KEY="ollama"
# OPENAI_BASE_URL="http://localhost:11434/v1"
# OPENAI_MODEL="llama3"

# --- LLM tuning --------------------------------------------------------------
# LLM_TEMPERATURE — default sampling temperature, clamped to [0.0, 2.0].
# LLM_TEMPERATURE=0.2 [OPTIONAL]


# -----------------------------------------------------------------------------
# Secondary / summarizer LLM (failover + history compaction) [OPTIONAL]
# -----------------------------------------------------------------------------
# A dedicated Azure OpenAI deployment used for summarization and as a fallback
# when the primary returns 429/throttling. If unset, the primary client is used.
# AZURE_OPENAI_SECONDARY_ENDPOINT="https://your-secondary.openai.azure.com/"
# AZURE_OPENAI_SECONDARY_MODEL="gpt-4o-mini"
# AZURE_OPENAI_SECONDARY_API_KEY="your-secondary-api-key"
# AZURE_OPENAI_SECONDARY_API_VERSION="2024-02-15-preview"


# -----------------------------------------------------------------------------
# Azure Cosmos DB — durable per-user chat memory [REQUIRED]
# -----------------------------------------------------------------------------
# The app FAILS TO START without AZURE_COSMOS_ENDPOINT (there is no in-memory
# fallback). Locally, run the emulator (see the next section). In Azure, point
# this at the deployed account and leave AZURE_COSMOS_KEY empty to use managed
# identity (RBAC: Cosmos DB Built-in Data Contributor).
AZURE_COSMOS_ENDPOINT="https://localhost:8081/"

# AZURE_COSMOS_KEY — account key. Only for local/emulator or non-MI scenarios.
# For the local emulator you can leave this UNSET: the app automatically uses
# the emulator's public well-known key. For a real account, prefer managed
# identity (leave empty).
# AZURE_COSMOS_KEY="your-cosmos-account-key"

# Container/database names (defaults shown). [OPTIONAL]
# AZURE_COSMOS_DATABASE_NAME="agent-memory"
# AZURE_COSMOS_CONTAINER_NAME="chat-history"
# AZURE_COSMOS_CONVERSATIONS_CONTAINER="conversations"

# AZURE_AUTHORITY_HOST — needed by managed identity in Azure Government.
# AZURE_AUTHORITY_HOST="https://login.microsoftonline.us" [OPTIONAL]


# -----------------------------------------------------------------------------
# Cosmos DB Emulator (local development only) [OPTIONAL]
# -----------------------------------------------------------------------------
# When USE_COSMOS_EMULATOR is truthy (true/1/yes/on), the "Start Cosmos
# Emulator" task / script launches the emulator container before the backend.
USE_COSMOS_EMULATOR=true
# COSMOS_EMULATOR_IMAGE="mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
# AZURE_COSMOS_EMULATOR_PARTITION_COUNT=3
# The classic emulator advertises its container IP (e.g. 172.17.0.2). If the
# host cannot reach it, set a host-reachable IP here or use the vnext-preview
# image. (The app also disables endpoint discovery for localhost.)
# AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE=""
# AZURE_COSMOS_EMULATOR_ENDPOINT="https://localhost:8081/" # used by emulator tests


# -----------------------------------------------------------------------------
# Authentication — Microsoft Entra ID (Azure AD) [REQUIRED in prod]
# -----------------------------------------------------------------------------
# Shared by backend (token validation) and frontend (MSAL sign-in). The
# OAUTH_AZURE_GOV_AD_* names take precedence; AZURE_AD_* are accepted fallbacks.
OAUTH_AZURE_GOV_AD_TENANT_ID="00000000-0000-0000-0000-000000000000"
OAUTH_AZURE_GOV_AD_CLIENT_ID="00000000-0000-0000-0000-000000000000"
# AZURE_AD_TENANT_ID="" # fallback if OAUTH_AZURE_GOV_AD_TENANT_ID is unset
# AZURE_AD_CLIENT_ID="" # fallback if OAUTH_AZURE_GOV_AD_CLIENT_ID is unset

# Authority base URL. Gov default shown; commercial = https://login.microsoftonline.com
# AZURE_AD_AUTHORITY="https://login.microsoftonline.us" [OPTIONAL]

# AZURE_AD_CLIENT_SECRET — only required for MCP servers using on-behalf-of
# (OBO) token exchange. Backend-only; never exposed to the frontend.
# AZURE_AD_CLIENT_SECRET="your-client-secret" [OPTIONAL]

# Disable authentication entirely — LOCAL DEVELOPMENT ONLY. Never in production.
# AUTH_DISABLED=true [OPTIONAL]


# -----------------------------------------------------------------------------
# Azure AI Search — retrieval / "search context" provider [OPTIONAL]
# -----------------------------------------------------------------------------
# Enables the search context provider and SQL/search tools. If unset, search
# context is reported as unavailable. Leave SEARCH_API_KEY empty for managed identity.
# SEARCH_SERVICE_ENDPOINT="https://your-search-service.search.azure.us"
# SEARCH_INDEX_NAME="your-index-name"
# SEARCH_API_KEY="your-search-api-key"
# SEARCH_TOP_K=5
# SEARCH_SEMANTIC_CONFIGURATION_NAME="your-semantic-config"


# -----------------------------------------------------------------------------
# Azure SQL tool (optional database query tool) [OPTIONAL]
# -----------------------------------------------------------------------------
# Use Authentication=ActiveDirectoryMsi (App Service) or ActiveDirectoryDefault
# / ActiveDirectoryInteractive (local). Requires ODBC Driver 18 for SQL Server.
# AZURE_SQL_CONNECTIONSTRING="Driver={ODBC Driver 18 for SQL Server};Server=tcp:your-server.database.usgovcloudapi.net,1433;Database=your-db;Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;Authentication=ActiveDirectoryDefault"


# -----------------------------------------------------------------------------
# Branding / UI (defaults shown) [OPTIONAL]
# -----------------------------------------------------------------------------
# APP_NAME="Web-Agents"
# APP_TAGLINE="AI Agent Framework"
# APP_LOGO="/Microsoft.png"
# CLASSIFICATION_BANNER="UNCLASSIFIED"


# -----------------------------------------------------------------------------
# Limits & tuning (defaults shown) [OPTIONAL]
# -----------------------------------------------------------------------------
# MAX_USER_INPUT_CHARS=8000 # max characters accepted per user message
# MAX_SESSIONS=5 # frontend: conversations kept in the left pane
# MAX_QUERY_RESULT_ROWS=100 # SQL tool: max rows returned
# MAX_QUERY_RESULT_CHARS=12000 # SQL tool: max chars in a result payload
# MAX_SQL_CELL_CHARS=1200 # SQL tool: max chars per cell
# MAX_LOG_QUERY_CHARS=500 # logging: truncate logged SQL
# MAX_LOG_TOOL_RESULT_CHARS=100 # logging: truncate logged tool results
# MAX_SEARCH_SNIPPET_CHARS=500 # search: max chars per snippet


# -----------------------------------------------------------------------------
# Evaluation tracing [OPTIONAL]
# -----------------------------------------------------------------------------
# Directory where per-day eval trace JSONL files are written.
# EVAL_TRACE_DIR="eval_traces"


# -----------------------------------------------------------------------------
# Frontend-only overrides (Vite) [OPTIONAL]
# -----------------------------------------------------------------------------
# At runtime the frontend fetches config from the backend (/api/auth/config),
# so the auth/branding values above are normally sufficient. This VITE_ var is
# the only one read directly from import.meta.env as a build-time fallback.
# VITE_AZURE_AD_AUTHORITY="https://login.microsoftonline.us"
72 changes: 0 additions & 72 deletions .envexample

This file was deleted.

4 changes: 3 additions & 1 deletion .github/agents/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- `config/agents.yaml` (built-in), localStorage (custom agents) (009-agents-page-grouping)
- TypeScript 5.9.x and React 19.x for frontend; Python 3.12.6/FastAPI backend unchanged + React, Vite, existing `useTheme` and `useAuth` hooks; no new dependencies (010-admin-settings-page)
- Existing localStorage key `webagents_theme`; no backend storage changes (010-admin-settings-page)
- Python 3.12.6 (FastAPI backend); TypeScript 5.9.x + React 19.x (frontend) + `agent-framework-core`/`agent-framework-openai` (existing); NEW `agent-framework-azure-cosmos` (provides `CosmosHistoryProvider`, re-exported as `agent_framework.azure.CosmosHistoryProvider`); `azure-cosmos` (async SDK, pulled in by the provider); `azure-identity` (`DefaultAzureCredential`, already used) (011-cosmos-agent-memory)
- Azure Cosmos DB for NoSQL — one database, two containers: `chat-history` (messages, partition key `/session_id`, managed by `CosmosHistoryProvider`) and `conversations` (per-user index, partition key `/user_id`, managed by new backend code). Local dev/tests use a Cosmos key/emulator or an in-memory fallback (011-cosmos-agent-memory)

## Project Structure

Expand Down Expand Up @@ -60,6 +62,6 @@ uv run pytest # Run tests
- Package manager: uv only (never pip)

## Recent Changes
- 011-cosmos-agent-memory: Added Python 3.12.6 (FastAPI backend); TypeScript 5.9.x + React 19.x (frontend) + `agent-framework-core`/`agent-framework-openai` (existing); NEW `agent-framework-azure-cosmos` (provides `CosmosHistoryProvider`, re-exported as `agent_framework.azure.CosmosHistoryProvider`); `azure-cosmos` (async SDK, pulled in by the provider); `azure-identity` (`DefaultAzureCredential`, already used)
- 010-admin-settings-page: Added TypeScript 5.9.x and React 19.x for frontend; Python 3.12.6/FastAPI backend unchanged + React, Vite, existing `useTheme` and `useAuth` hooks; no new dependencies
- 009-agents-page-grouping: Added Python 3.12+ (backend), TypeScript (frontend) + FastAPI (backend), React (frontend), Vite (bundler)
- 008-agents-as-tools: Added Python 3.12+ (backend), TypeScript / React 18 (frontend) + FastAPI, `agent-framework-core` (`Agent.as_tool()`), `agent-framework-azure-ai-search`, OpenAIChatClient (Azure OpenAI Government endpoint), React, Vite
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ venv/

.env
.env*
!.env.example
*.log

.ruff*
Expand Down
18 changes: 17 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
"group": "build",
"problemMatcher": []
},
{
"label": "Start Cosmos Emulator",
"type": "shell",
"command": "bash scripts/start_cosmos_emulator.sh",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [],
"presentation": {
"reveal": "silent",
"panel": "shared"
}
},
{
"label": "Run Backend",
"type": "shell",
Expand All @@ -20,7 +33,10 @@
},
"isBackground": true,
"problemMatcher": [],
"dependsOn": "Build Frontend"
"dependsOn": [
"Build Frontend",
"Start Cosmos Emulator"
]
},
{
"label": "Build & Run",
Expand Down
29 changes: 10 additions & 19 deletions agent_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path
from typing import Any, Sequence

from agent_framework import CompactionProvider, InMemoryHistoryProvider, SkillsProvider
from agent_framework import CompactionProvider, SkillsProvider
from agent_framework import Agent as RuntimeAgent
from agent_framework._compaction import (
CharacterEstimatorTokenizer,
Expand All @@ -28,6 +28,7 @@
)
from mcp_servers import get_search_context_provider
from sub_agent_tools import derive_sub_agent_tool_surface, disambiguate_tool_names
from cosmos_memory import get_history_provider


@dataclass(frozen=True)
Expand Down Expand Up @@ -92,24 +93,14 @@ def _build_skills_provider(
if not skill_names or not _SKILLS_DIR.is_dir():
return None

provider = SkillsProvider(skill_paths=_SKILLS_DIR)
from agent_framework import FileSkillsSource, FilteringSkillsSource

# Filter to only the requested skills
discovered = set(provider._skills.keys())
requested = set(skill_names)
missing = requested - discovered
if missing:
logger.warning("Requested skills not found in %s: %s", _SKILLS_DIR, missing)

# Remove skills that weren't requested
to_remove = discovered - requested
for name in to_remove:
del provider._skills[name]

if not provider._skills:
return None

return provider
selected = set(skill_names)
source = FilteringSkillsSource(
FileSkillsSource(_SKILLS_DIR),
predicate=lambda skill: skill.frontmatter.name in selected,
)
return SkillsProvider(source)


def _get_default_temperature() -> float:
Expand Down Expand Up @@ -145,7 +136,7 @@ def _build_context_providers(
],
)

history = InMemoryHistoryProvider(skip_excluded=True)
history = get_history_provider()
compaction = CompactionProvider(
before_strategy=pipeline,
after_strategy=pipeline,
Expand Down
Loading