From 1031a646e49b6e9c2d70a8022dfec0c720af2410 Mon Sep 17 00:00:00 2001 From: huimiu <107838226+huimiu@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:47:24 +0000 Subject: [PATCH 1/3] ci: sync sample catalog from foundry-samples (template/stable) --- samples/hosted-agent/sample-catalog.json | 595 ++++++++++++++--------- 1 file changed, 358 insertions(+), 237 deletions(-) diff --git a/samples/hosted-agent/sample-catalog.json b/samples/hosted-agent/sample-catalog.json index 0da832a..50b8681 100644 --- a/samples/hosted-agent/sample-catalog.json +++ b/samples/hosted-agent/sample-catalog.json @@ -1,7 +1,7 @@ { - "commitSha": "d268eada402bd5a16002a882e20bf4f969d78b9a", + "commitSha": "c14a01b61eb6a59aeae140f7db07b420f85fcfb4", "repo": "https://github.com/microsoft-foundry/foundry-samples/", - "generatedAt": "2026-06-24T07:30:28Z", + "generatedAt": "2026-07-01T09:47:23Z", "dimensions": { "language": { "title": "Select a Language", @@ -50,6 +50,10 @@ { "id": "invocations", "displayName": "Invocations API" + }, + { + "id": "invocations_ws", + "displayName": "invocations_ws" } ] } @@ -59,6 +63,276 @@ "placeholder": "Choose a template for your agent" }, "templates": [ + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Caller", + "description": "Concierge agent that delegates questions to a remote specialist and summarizes answers.", + "path": "samples/csharp/hosted-agents/agent-framework/a2a/01-delegation/caller", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Executor", + "description": "Answers arithmetic and math questions as an expert agent.", + "path": "samples/csharp/hosted-agents/agent-framework/a2a/01-delegation/executor", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Agent Skills", + "description": "Agent that loads behavioral guidelines from Foundry Skills at startup for dynamic updates.", + "path": "samples/csharp/hosted-agents/agent-framework/agent-skills", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Azure Search RAG", + "description": "Support agent answers questions using a keyword-indexed knowledge base for grounding.", + "path": "samples/csharp/hosted-agents/agent-framework/azure-search-rag", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Browser Automation", + "description": "Automates web browsing, scraping, and form filling in a remote Chromium browser.", + "path": "samples/csharp/hosted-agents/agent-framework/browser-automation", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "File Tools", + "description": "Agent answers questions using bundled and user-uploaded files as knowledge sources.", + "path": "samples/csharp/hosted-agents/agent-framework/file-tools", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Foundry Memory RAG", + "description": "Personal coach agent that remembers user preferences and goals across sessions.", + "path": "samples/csharp/hosted-agents/agent-framework/foundry-memory-rag", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Foundry Toolbox MCP Skills", + "description": "Agent that dynamically discovers and uses skills from a remote toolbox endpoint.", + "path": "samples/csharp/hosted-agents/agent-framework/foundry-toolbox-mcp-skills", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Foundry Toolbox Server Side", + "description": "Agent that uses tools from a Foundry Toolbox, invoked server-side by the platform.", + "path": "samples/csharp/hosted-agents/agent-framework/foundry-toolbox-server-side", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Hello World", + "description": "Minimal hosted agent that responds to user questions and supports multi-turn conversation.", + "path": "samples/csharp/hosted-agents/agent-framework/hello-world", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "invocations", + "displayName": "Invocations Echo Agent", + "description": "Echoes back input messages with a simple prefix for testing agent hosting.", + "path": "samples/csharp/hosted-agents/agent-framework/invocations-echo-agent", + "requiresModel": false + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Local Tools", + "description": "Hotel search assistant that uses local function tools for queries.", + "path": "samples/csharp/hosted-agents/agent-framework/local-tools", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "MCP Tools", + "description": "Agent answers questions by searching documentation using integrated tool providers.", + "path": "samples/csharp/hosted-agents/agent-framework/mcp-tools", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Observability", + "description": "Agent with built-in observability for traces, metrics, and logs via Azure Monitor.", + "path": "samples/csharp/hosted-agents/agent-framework/observability", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Simple Agent", + "description": "General-purpose AI assistant that replies to user input with model-generated responses.", + "path": "samples/csharp/hosted-agents/agent-framework/simple-agent", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Teams Activity", + "description": "Agent that answers questions about Teams and calendar, and handles file attachments.", + "path": "samples/csharp/hosted-agents/agent-framework/teams-activity", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Text Search RAG", + "description": "Agent answers support questions using retrieved passages from external documents.", + "path": "samples/csharp/hosted-agents/agent-framework/text-search-rag", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Toolbox Auth Paths", + "description": "Agent toolbox demonstrates different authentication methods for accessing upstream tools.", + "path": "samples/csharp/hosted-agents/agent-framework/toolbox-auth-paths", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Workflows", + "description": "Workflow chains three translation agents to show intermediate language outputs.", + "path": "samples/csharp/hosted-agents/agent-framework/workflows", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "invocations", + "displayName": "HelloWorld", + "description": "Hosted agent that returns a streaming hello world response from a Foundry model.", + "path": "samples/csharp/hosted-agents/bring-your-own/invocations/HelloWorld", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "invocations", + "displayName": "Human In The Loop", + "description": "Agent that generates proposals and waits for human approval before proceeding.", + "path": "samples/csharp/hosted-agents/bring-your-own/invocations/human-in-the-loop", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "invocations", + "displayName": "Notetaking Agent", + "description": "Agent that saves and retrieves session-persistent notes with real-time streaming responses.", + "path": "samples/csharp/hosted-agents/bring-your-own/invocations/notetaking-agent", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "invocations_ws", + "displayName": "HelloWorld", + "description": "Routes real-time audio between a browser and a managed voice service for live conversations.", + "path": "samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld", + "requiresModel": false + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "HelloWorld", + "description": "Minimal hosted agent that returns a greeting from a Foundry model using the Responses API.", + "path": "samples/csharp/hosted-agents/bring-your-own/responses/HelloWorld", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Background Agent", + "description": "Handles research analysis requests asynchronously with background processing and streaming updates.", + "path": "samples/csharp/hosted-agents/bring-your-own/responses/background-agent", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Browser Automation", + "description": "", + "path": "samples/csharp/hosted-agents/bring-your-own/responses/browser-automation", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Env Vars Agent", + "description": "Agent retrieves environment variable values with safety policies based on their type.", + "path": "samples/csharp/hosted-agents/bring-your-own/responses/env-vars-agent", + "requiresModel": true + }, + { + "language": "csharp", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Notetaking Agent", + "description": "Agent that saves and retrieves session-persistent notes for each user.", + "path": "samples/csharp/hosted-agents/bring-your-own/responses/notetaking-agent", + "requiresModel": true + }, + { + "language": "python", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Caller", + "description": "Concierge agent that delegates questions to a remote specialist and summarizes the answers.", + "path": "samples/python/hosted-agents/agent-framework/a2a/01-delegation/caller", + "requiresModel": true + }, + { + "language": "python", + "framework": "agent-framework", + "protocol": "responses", + "displayName": "Executor", + "description": "Answers arithmetic and math questions as an expert agent.", + "path": "samples/python/hosted-agents/agent-framework/a2a/01-delegation/executor", + "requiresModel": true + }, { "language": "python", "framework": "agent-framework", @@ -305,20 +579,65 @@ { "language": "python", "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "Notetaking Agent", - "description": "Agent that saves and retrieves notes with per-session isolation and streaming responses.", - "path": "samples/python/hosted-agents/bring-your-own/invocations/notetaking-agent", - "requiresModel": true + "protocol": "invocations", + "displayName": "Notetaking Agent", + "description": "Agent that saves and retrieves notes with per-session isolation and streaming responses.", + "path": "samples/python/hosted-agents/bring-your-own/invocations/notetaking-agent", + "requiresModel": true + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations", + "displayName": "Toolbox", + "description": "Agent connects to a toolbox server and enables tool discovery and invocation during chat.", + "path": "samples/python/hosted-agents/bring-your-own/invocations/toolbox", + "requiresModel": true + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations_ws", + "displayName": "Duplex Live Agent", + "description": "Real-time voice agent that handles conversation and background tasks in parallel.", + "path": "samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent", + "requiresModel": false + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations_ws", + "displayName": "Hello World", + "description": "Real-time voice agent that relays audio and events between browser and a managed service.", + "path": "samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world", + "requiresModel": false + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations_ws", + "displayName": "Livekit Server", + "description": "Voice agent connects to a LiveKit server for real-time audio conversations.", + "path": "samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server", + "requiresModel": false + }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "invocations_ws", + "displayName": "Pipecat Webrtc", + "description": "Real-time voice agent with multi-agent pipeline and WebRTC audio media support.", + "path": "samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc", + "requiresModel": false }, { "language": "python", "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "Toolbox", - "description": "Agent connects to a toolbox server and enables tool discovery and invocation during chat.", - "path": "samples/python/hosted-agents/bring-your-own/invocations/toolbox", - "requiresModel": true + "protocol": "invocations_ws", + "displayName": "Pipecat Ws Server", + "description": "Real-time voice agent with greeting and order-checking capabilities.", + "path": "samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server", + "requiresModel": false }, { "language": "python", @@ -428,6 +747,15 @@ "path": "samples/python/hosted-agents/bring-your-own/responses/optimization-hello-world", "requiresModel": true }, + { + "language": "python", + "framework": "bring-your-own", + "protocol": "responses", + "displayName": "Session Multiplexing", + "description": "Supports multiple users sharing a session while keeping response chains user-specific.", + "path": "samples/python/hosted-agents/bring-your-own/responses/session-multiplexing", + "requiresModel": true + }, { "language": "python", "framework": "bring-your-own", @@ -464,6 +792,24 @@ "path": "samples/python/hosted-agents/bring-your-own/voicelive/hotel-booking-invocations-voicelive", "requiresModel": true }, + { + "language": "python", + "framework": "langgraph", + "protocol": "responses", + "displayName": "A2a Caller", + "description": "Concierge agent that delegates specialist questions to a remote expert agent.", + "path": "samples/python/hosted-agents/langgraph/a2a/a2a-caller", + "requiresModel": true + }, + { + "language": "python", + "framework": "langgraph", + "protocol": "responses", + "displayName": "A2a Executor", + "description": "Math expert agent that answers questions and explains steps, callable by other agents.", + "path": "samples/python/hosted-agents/langgraph/a2a/a2a-executor", + "requiresModel": true + }, { "language": "python", "framework": "langgraph", @@ -535,231 +881,6 @@ "description": "Agent with automatic tracing for observability of all actions and tool invocations.", "path": "samples/python/hosted-agents/langgraph/responses/08-observability", "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Agent Skills", - "description": "Agent that loads behavioral guidelines from Foundry Skills at startup for dynamic updates.", - "path": "samples/csharp/hosted-agents/agent-framework/agent-skills", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Azure Search RAG", - "description": "Support agent answers questions using a keyword-indexed knowledge base for grounding.", - "path": "samples/csharp/hosted-agents/agent-framework/azure-search-rag", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Browser Automation", - "description": "Automates web browsing, scraping, and form filling in a remote Chromium browser.", - "path": "samples/csharp/hosted-agents/agent-framework/browser-automation", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "File Tools", - "description": "Agent answers questions using bundled and user-uploaded files as knowledge sources.", - "path": "samples/csharp/hosted-agents/agent-framework/file-tools", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Foundry Memory RAG", - "description": "Personal coach agent that remembers user preferences and goals across sessions.", - "path": "samples/csharp/hosted-agents/agent-framework/foundry-memory-rag", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Foundry Toolbox MCP Skills", - "description": "Agent that dynamically discovers and uses skills from a remote toolbox endpoint.", - "path": "samples/csharp/hosted-agents/agent-framework/foundry-toolbox-mcp-skills", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Foundry Toolbox Server Side", - "description": "Agent that uses tools from a Foundry Toolbox, invoked server-side by the platform.", - "path": "samples/csharp/hosted-agents/agent-framework/foundry-toolbox-server-side", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Hello World", - "description": "Minimal hosted agent that responds to user questions and supports multi-turn conversation.", - "path": "samples/csharp/hosted-agents/agent-framework/hello-world", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "invocations", - "displayName": "Invocations Echo Agent", - "description": "Echoes back input messages with a simple prefix for testing agent hosting.", - "path": "samples/csharp/hosted-agents/agent-framework/invocations-echo-agent", - "requiresModel": false - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Local Tools", - "description": "Hotel search assistant that uses local function tools for queries.", - "path": "samples/csharp/hosted-agents/agent-framework/local-tools", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "MCP Tools", - "description": "Agent answers questions by searching documentation using integrated tool providers.", - "path": "samples/csharp/hosted-agents/agent-framework/mcp-tools", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Observability", - "description": "Agent with built-in observability for traces, metrics, and logs via Azure Monitor.", - "path": "samples/csharp/hosted-agents/agent-framework/observability", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Simple Agent", - "description": "General-purpose AI assistant that replies to user input with model-generated responses.", - "path": "samples/csharp/hosted-agents/agent-framework/simple-agent", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Teams Activity", - "description": "Agent that answers questions about Teams and calendar, and handles file attachments.", - "path": "samples/csharp/hosted-agents/agent-framework/teams-activity", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Text Search RAG", - "description": "Agent answers support questions using retrieved passages from external documents.", - "path": "samples/csharp/hosted-agents/agent-framework/text-search-rag", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Toolbox Auth Paths", - "description": "Agent toolbox demonstrates different authentication methods for accessing upstream tools.", - "path": "samples/csharp/hosted-agents/agent-framework/toolbox-auth-paths", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "agent-framework", - "protocol": "responses", - "displayName": "Workflows", - "description": "Workflow chains three translation agents to show intermediate language outputs.", - "path": "samples/csharp/hosted-agents/agent-framework/workflows", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "HelloWorld", - "description": "Hosted agent that returns a streaming hello world response from a Foundry model.", - "path": "samples/csharp/hosted-agents/bring-your-own/invocations/HelloWorld", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "Human In The Loop", - "description": "Agent that generates proposals and waits for human approval before proceeding.", - "path": "samples/csharp/hosted-agents/bring-your-own/invocations/human-in-the-loop", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "invocations", - "displayName": "Notetaking Agent", - "description": "Agent that saves and retrieves session-persistent notes with real-time streaming responses.", - "path": "samples/csharp/hosted-agents/bring-your-own/invocations/notetaking-agent", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "responses", - "displayName": "HelloWorld", - "description": "Minimal hosted agent that returns a greeting from a Foundry model using the Responses API.", - "path": "samples/csharp/hosted-agents/bring-your-own/responses/HelloWorld", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "responses", - "displayName": "Background Agent", - "description": "Handles research analysis requests asynchronously with background processing and streaming updates.", - "path": "samples/csharp/hosted-agents/bring-your-own/responses/background-agent", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "responses", - "displayName": "Browser Automation", - "description": "", - "path": "samples/csharp/hosted-agents/bring-your-own/responses/browser-automation", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "responses", - "displayName": "Env Vars Agent", - "description": "Agent retrieves environment variable values with safety policies based on their type.", - "path": "samples/csharp/hosted-agents/bring-your-own/responses/env-vars-agent", - "requiresModel": true - }, - { - "language": "csharp", - "framework": "bring-your-own", - "protocol": "responses", - "displayName": "Notetaking Agent", - "description": "Agent that saves and retrieves session-persistent notes for each user.", - "path": "samples/csharp/hosted-agents/bring-your-own/responses/notetaking-agent", - "requiresModel": true } ] } From 17caddb54750b8175fd602d91c29cfeb4917ce05 Mon Sep 17 00:00:00 2001 From: Yimin Jin Date: Tue, 7 Jul 2026 11:21:47 +0800 Subject: [PATCH 2/3] fix(sample-catalog): discover templates via azure.yaml after upstream restructure Port the generator fix to template/stable (parity with template/dev). Upstream foundry-samples moved hosted-agent templates from an agent.yaml + agent.manifest.yaml layout to an azd azure.yaml service manifest (old getting-started samples relocated to samples-classic/). The stable generator still looked for agent.yaml, so the next sync would find 0 templates and wipe the catalog. - Discover templates by azure.yaml instead of agent.yaml - Parse protocol from the service protocols[] list (unchanged - protocol: field) - Detect requiresModel via AZURE_AI_MODEL_DEPLOYMENT_NAME env var OR an ai-project deployments: block - Remove now-dead agent.manifest.yaml fetch/parse - Also brings the invocations_ws -> 'Invocations (WebSocket)' displayName mapping so the generator matches template/dev exactly Verified against foundry-samples@9c4e42a: 91 templates found; catalog stable (paths/protocols/requiresModel unchanged). --- .github/scripts/generate_sample_catalog.mjs | 147 +++++++------------- 1 file changed, 51 insertions(+), 96 deletions(-) diff --git a/.github/scripts/generate_sample_catalog.mjs b/.github/scripts/generate_sample_catalog.mjs index 28c9f40..75e63c4 100644 --- a/.github/scripts/generate_sample_catalog.mjs +++ b/.github/scripts/generate_sample_catalog.mjs @@ -3,11 +3,11 @@ * Generate samples/hosted-agent/sample-catalog.json from the foundry-samples repository. * * Walks the hosted-agents tree in microsoft-foundry/foundry-samples once via - * the git-tree API, parses each sample's agent.yaml (+ optional - * agent.manifest.yaml) for protocol and model requirements, derives - * displayName from the directory name, and — when AZURE_OPENAI_* secrets are - * set — fills the description with a short LLM-generated sentence sourced - * from the sample's README.md. + * the git-tree API, parses each sample's azure.yaml (the azd service + * manifest) for protocol and model requirements, derives displayName from the + * directory name, and — when AZURE_OPENAI_* secrets are set — fills the + * description with a short LLM-generated sentence sourced from the sample's + * README.md. * * Usage: * node generate_sample_catalog.mjs @@ -35,7 +35,7 @@ const OVERRIDES_PATH = join(REPO_ROOT, 'samples', 'hosted-agent', 'sample-overri const GITHUB_TOKEN = process.env.GITHUB_TOKEN || ''; // Languages, frameworks, and protocols are discovered dynamically from the -// samples repo tree (see discoverLanguagesAndFrameworks / parseAgentYaml) +// samples repo tree (see discoverLanguagesAndFrameworks / parseAzureYaml) // instead of being restricted by an allowlist. These blacklists are the // explicit escape hatch to exclude a specific discovered value; empty by // default means "no restriction" — everything discovered is kept. @@ -74,6 +74,7 @@ const DIMENSION_DEFAULTS = { options: { responses: 'Responses API', invocations: 'Invocations API', + invocations_ws: 'Invocations (WebSocket)', }, }, }; @@ -244,12 +245,12 @@ function discoverLanguagesAndFrameworks(tree) { /** * Find sample template directories under a `hosted-agents//` prefix. - * A template is identified by the presence of an `agent.yaml`. When nested - * agent.yaml files exist (e.g. a sub-agent declared inside a parent sample), - * only the OUTERMOST one is treated as a catalog template — the inner files - * are part of the parent sample. Hidden directories (segments beginning with - * `.`, e.g. `.claude/skills`) are skipped, as are segments that fail the - * `SAFE_PATH_SEGMENT` check. + * A template is identified by the presence of an `azure.yaml` (the azd service + * manifest). When nested azure.yaml files exist (e.g. a sub-agent declared + * inside a parent sample), only the OUTERMOST one is treated as a catalog + * template — the inner files are part of the parent sample. Hidden directories + * (segments beginning with `.`, e.g. `.claude/skills`) are skipped, as are + * segments that fail the `SAFE_PATH_SEGMENT` check. * * Nested templates are kept unless their category segment is listed in * `BLOCKED_CATEGORY_SEGMENTS` (empty by default, so everything is surfaced); @@ -263,8 +264,8 @@ function discoverLanguagesAndFrameworks(tree) { */ function findTemplateDirsUnder(tree, prefix) { const candidates = tree - .filter((entry) => entry.type === 'blob' && entry.path.startsWith(prefix) && entry.path.endsWith('/agent.yaml')) - .map((entry) => entry.path.slice(0, -'/agent.yaml'.length)) + .filter((entry) => entry.type === 'blob' && entry.path.startsWith(prefix) && entry.path.endsWith('/azure.yaml')) + .map((entry) => entry.path.slice(0, -'/azure.yaml'.length)) .filter((dir) => { const rel = dir.slice(prefix.length); if (!rel) { @@ -295,7 +296,7 @@ function findTemplateDirsUnder(tree, prefix) { }) // Lexicographic sort serves two purposes: (1) a parent path always // sorts before its descendants, so the `startsWith` check below - // correctly keeps only the outermost agent.yaml; (2) it preserves + // correctly keeps only the outermost azure.yaml; (2) it preserves // upstream's `NN-` numeric prefix ordering in the picker. .sort(); @@ -310,7 +311,7 @@ function findTemplateDirsUnder(tree, prefix) { } /** - * Infer protocol when `agent.yaml` does not declare one explicitly. Looks for + * Infer protocol when `azure.yaml` does not declare one explicitly. Looks for * a `responses` or `invocations` segment in the path, then for the substring * in the leaf directory name (common for samples like * `hello-world-invocations-voicelive`). Falls back to `responses` and emits a @@ -334,23 +335,26 @@ function inferProtocolFromPath(templatePath) { if (leaf.includes('responses')) { return 'responses'; } - warn(`Could not infer protocol for "${templatePath}"; defaulting to "responses". Add a "- protocol:" entry to agent.yaml or a sample-overrides.json entry to silence this.`); + warn(`Could not infer protocol for "${templatePath}"; defaulting to "responses". Add a "- protocol:" entry to azure.yaml or a sample-overrides.json entry to silence this.`); return 'responses'; } /** - * Minimal parser for agent.yaml. Extracts the declared protocol(s) and - * whether the sample exposes the AZURE_AI_MODEL_DEPLOYMENT_NAME env var - * (used as a heuristic for `requiresModel`). Does NOT use eval or a real - * YAML library — a regex-y scan is sufficient for our two fields. Any - * declared protocol is accepted except those listed in BLOCKED_PROTOCOLS - * (empty by default). + * Minimal parser for a sample's azure.yaml (the azd service manifest). + * Extracts the declared protocol(s) from the hosted-agent service's + * `protocols:` list and whether the sample requires a Foundry model. A sample + * requires a model when it either consumes one (an + * AZURE_AI_MODEL_DEPLOYMENT_NAME env var) or provisions one (an `ai-project` + * `deployments:` block — which replaced the model resource that used to live + * in agent.manifest.yaml). Does NOT use eval or a real YAML library — a line + * scan is sufficient for these fields. Any declared protocol is accepted + * except those listed in BLOCKED_PROTOCOLS (empty by default). * @param {string} content - * @returns {{ protocols: string[], hasModelEnv: boolean }} + * @returns {{ protocols: string[], requiresModel: boolean }} */ -function parseAgentYaml(content) { - /** @type {{ protocols: string[], hasModelEnv: boolean }} */ - const result = { protocols: [], hasModelEnv: false }; +function parseAzureYaml(content) { + /** @type {{ protocols: string[], requiresModel: boolean }} */ + const result = { protocols: [], requiresModel: false }; for (const line of content.split('\n')) { const stripped = line.trim(); @@ -360,8 +364,15 @@ function parseAgentYaml(content) { result.protocols.push(value); } } + // Model consumer: the agent reads a Foundry model deployment name. if (stripped.startsWith('- name:') && stripped.includes('AZURE_AI_MODEL_DEPLOYMENT_NAME')) { - result.hasModelEnv = true; + result.requiresModel = true; + } + // Model provider: the ai-project service declares one or more model + // deployments to provision (the azure.yaml successor to a manifest + // `kind: model` resource). + if (stripped === 'deployments:') { + result.requiresModel = true; } } @@ -369,64 +380,16 @@ function parseAgentYaml(content) { } /** - * Fetch and parse agent.yaml for a sample directory. + * Fetch and parse a sample directory's azure.yaml. * @param {string} samplePath * @param {string} ref - * @returns {Promise<{ protocols: string[], hasModelEnv: boolean } | null>} + * @returns {Promise<{ protocols: string[], requiresModel: boolean } | null>} */ -async function fetchAgentYaml(samplePath, ref) { - const rawUrl = `https://raw.githubusercontent.com/microsoft-foundry/foundry-samples/${ref}/${samplePath}/agent.yaml`; +async function fetchAzureYaml(samplePath, ref) { + const rawUrl = `https://raw.githubusercontent.com/microsoft-foundry/foundry-samples/${ref}/${samplePath}/azure.yaml`; try { const content = await fetchText(rawUrl); - return parseAgentYaml(content); - } catch { - return null; - } -} - -/** - * Minimal parser for agent.manifest.yaml — detects whether the manifest - * declares a top-level `resources:` list containing `kind: model`. Matches - * both `- kind: model` and the multi-line continuation form, and limits - * scanning to the `resources:` block to avoid false positives. - * @param {string} content - * @returns {{ hasModelResource: boolean }} - */ -function parseAgentManifestYaml(content) { - let inResources = false; - for (const rawLine of content.split('\n')) { - const stripped = rawLine.trim(); - // Detect top-level key (column 0, e.g. `resources:`, `metadata:`). - if (/^[A-Za-z_][\w-]*:/.test(rawLine)) { - inResources = stripped.startsWith('resources:'); - continue; - } - if (!inResources) { - continue; - } - // Strip optional `- ` so the inline and continuation forms both match. - const withoutDash = stripped.replace(/^-\s+/, ''); - if (withoutDash.startsWith('kind:')) { - const value = withoutDash.substring('kind:'.length).trim().replace(/^["']|["']$/g, ''); - if (value === 'model') { - return { hasModelResource: true }; - } - } - } - return { hasModelResource: false }; -} - -/** - * Fetch and parse agent.manifest.yaml for a sample directory. - * @param {string} samplePath - * @param {string} ref - * @returns {Promise<{ hasModelResource: boolean } | null>} - */ -async function fetchAgentManifestYaml(samplePath, ref) { - const rawUrl = `https://raw.githubusercontent.com/microsoft-foundry/foundry-samples/${ref}/${samplePath}/agent.manifest.yaml`; - try { - const content = await fetchText(rawUrl); - return parseAgentManifestYaml(content); + return parseAzureYaml(content); } catch { return null; } @@ -586,7 +549,7 @@ function displayNameFromPath(samplePath) { /** * Scan the foundry-samples repo and build the flat template list. Uses one - * recursive git-tree call to enumerate every `agent.yaml` under each + * recursive git-tree call to enumerate every `azure.yaml` under each * `/hosted-agents//` prefix, regardless of intermediate * directories. Languages and frameworks are discovered dynamically from the * tree (see discoverLanguagesAndFrameworks) rather than hard-coded, then @@ -615,26 +578,18 @@ async function scanTemplates(commitSha) { const templateDirs = findTemplateDirsUnder(tree, prefix); for (const templatePath of templateDirs) { - const agentInfo = await fetchAgentYaml(templatePath, commitSha); - if (!agentInfo) { - warn(`Could not fetch or parse agent.yaml for "${templatePath}"; skipping this template.`); + const azureInfo = await fetchAzureYaml(templatePath, commitSha); + if (!azureInfo) { + warn(`Could not fetch or parse azure.yaml for "${templatePath}"; skipping this template.`); continue; } /** @type {string} */ - const protocol = agentInfo.protocols.length > 0 - ? agentInfo.protocols[0] + const protocol = azureInfo.protocols.length > 0 + ? azureInfo.protocols[0] : inferProtocolFromPath(templatePath); - let requiresModel = agentInfo.hasModelEnv; - // Only consult agent.manifest.yaml when agent.yaml reported no - // model env; otherwise we already default to `true`. - if (!requiresModel) { - const manifestInfo = await fetchAgentManifestYaml(templatePath, commitSha); - if (manifestInfo?.hasModelResource) { - requiresModel = true; - } - } + const requiresModel = azureInfo.requiresModel; templates.push({ language, From 95624116110108a890f89d2e62ccb12ce158dbc9 Mon Sep 17 00:00:00 2001 From: Yimin Jin Date: Tue, 7 Jul 2026 14:09:46 +0800 Subject: [PATCH 3/3] update protocol displayname --- .github/scripts/generate_sample_catalog.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generate_sample_catalog.mjs b/.github/scripts/generate_sample_catalog.mjs index 75e63c4..98970b1 100644 --- a/.github/scripts/generate_sample_catalog.mjs +++ b/.github/scripts/generate_sample_catalog.mjs @@ -72,8 +72,8 @@ const DIMENSION_DEFAULTS = { title: 'Select a Protocol', placeholder: 'Choose the protocol for your agent', options: { - responses: 'Responses API', - invocations: 'Invocations API', + responses: 'Responses', + invocations: 'Invocations', invocations_ws: 'Invocations (WebSocket)', }, },