.NET: Restructure .NET samples: progressive learning path (01→05)#3765
.NET: Restructure .NET samples: progressive learning path (01→05)#3765eavanvalkenburg wants to merge 2 commits intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Restructures the .NET samples into a progressive learning path (01→05), adds/updates documentation to reflect the new layout, and stages legacy samples under _to_delete/ for review.
Changes:
- Added new sample hierarchy (
01-get-started→05-end-to-end) with updated section-level READMEs and conventions. - Added many new single-file workflow/agent/tool/provider samples with docs snippet tags.
- Moved older multi-project samples into
dotnet/samples/_to_delete/and removed several Azure Functionslocal.settings.jsonfiles.
Reviewed changes
Copilot reviewed 127 out of 817 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| dotnet/samples/AGENTS.md | Documents the new sample structure, conventions, and docs mapping. |
| dotnet/samples/01-get-started/README.md | Introduces the progressive “get started” steps and links deeper sections. |
| dotnet/samples/01-get-started/01_HelloAgent.cs | Step 1 sample using Foundry default provider + snippet tags. |
| dotnet/samples/01-get-started/02_AddTools.cs | Step 2 sample adding tools + streaming. |
| dotnet/samples/01-get-started/03_MultiTurn.cs | Step 3 multi-turn conversation sample (Foundry conversations). |
| dotnet/samples/01-get-started/04_Memory.cs | Step 4 memory sample using vector store + context provider. |
| dotnet/samples/01-get-started/05_FirstWorkflow.cs | Step 5 workflow intro sample. |
| dotnet/samples/01-get-started/06_HostYourAgent.cs | Step 6 hosting sample consuming A2A skills as tools. |
| dotnet/samples/02-agents/README.md | Index for agents/tools/middleware/conversations/providers samples. |
| dotnet/samples/02-agents/BackgroundResponses.cs | Demonstrates background responses polling/resumption. |
| dotnet/samples/02-agents/DeclarativeAgents.cs | Demonstrates YAML-defined agents. |
| dotnet/samples/02-agents/Observability.cs | Demonstrates OpenTelemetry for agents/chat clients. |
| dotnet/samples/02-agents/Rag.cs | Demonstrates RAG with vector search. |
| dotnet/samples/02-agents/StructuredOutput.cs | Demonstrates typed structured output and streaming deserialization. |
| dotnet/samples/02-agents/conversations/CustomStorage.cs | Shows custom chat history storage via vector store. |
| dotnet/samples/02-agents/conversations/PersistentConversation.cs | Shows session serialization/deserialization. |
| dotnet/samples/02-agents/middleware/*.cs | Adds middleware concept samples (guardrails, PII, approval, etc.). |
| dotnet/samples/02-agents/providers/*.cs | Adds provider samples (Foundry, Azure OpenAI, OpenAI, Anthropic, Ollama, Copilot, etc.). |
| dotnet/samples/02-agents/tools/*.cs | Adds tool samples (function tools, approval, MCP, file/web search, code interpreter). |
| dotnet/samples/03-workflows/README.md | Index for workflow patterns. |
| dotnet/samples/03-workflows//.cs | Adds workflow pattern samples (sequential, branching, HITL, checkpoints, visualization, etc.). |
| dotnet/samples/04-hosting/README.md | New hosting/protocol section index. |
| dotnet/samples/05-end-to-end/README.md | New end-to-end apps section index. |
| dotnet/samples/Durable/Agents/AzureFunctions/*/local.settings.json | Removes local settings files from Azure Functions samples. |
| dotnet/samples/_to_delete/** | Stages legacy samples/projects for reviewer inspection (not deleted). |
| </div> | ||
| <div class="assistant-message-header">Assistant</div> | ||
| <div class="assistant-message-text"> | ||
| <div>@((MarkupString)text)</div> |
There was a problem hiding this comment.
Rendering model-provided text as MarkupString will render raw HTML and can enable XSS (the model can output <script>/event handlers). Prefer rendering as plain text (default Blazor encoding), or sanitize/allowlist HTML before converting to MarkupString.
| <div>@((MarkupString)text)</div> | |
| <div>@text</div> |
| } | ||
|
|
||
| @code { | ||
| private static readonly ConditionalWeakTable<ChatMessage, ChatMessageItem> SubscribersLookup = new(); |
There was a problem hiding this comment.
ConditionalWeakTable<TKey,TValue> does not have an AddOrUpdate API in the BCL. This will not compile unless a custom extension method is in scope (none is shown here). Use Remove + Add, or use GetValue/GetOrCreateValue patterns to register/update entries.
|
|
||
| protected override void OnInitialized() | ||
| { | ||
| SubscribersLookup.AddOrUpdate(Message, this); |
There was a problem hiding this comment.
ConditionalWeakTable<TKey,TValue> does not have an AddOrUpdate API in the BCL. This will not compile unless a custom extension method is in scope (none is shown here). Use Remove + Add, or use GetValue/GetOrCreateValue patterns to register/update entries.
| SubscribersLookup.AddOrUpdate(Message, this); | |
| SubscribersLookup.Remove(Message); | |
| SubscribersLookup.Add(Message, this); |
| var csTask = cs.GetValueAsync(default).AsTask(); | ||
| if (!csTask.IsCompletedSuccessfully) | ||
| { | ||
| throw new InvalidOperationException("Connection string could not be resolved!"); | ||
| } | ||
|
|
||
| #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits | ||
| builder.WithInitialState(new CustomResourceSnapshot | ||
| { | ||
| ResourceType = "Azure AI Inference Model", | ||
| State = KnownResourceStates.Running, | ||
| Properties = [ | ||
| new("ConnectionString", csTask.Result ) { IsSensitive = true } | ||
| ] | ||
| }); | ||
| #pragma warning restore VSTHRD002 |
There was a problem hiding this comment.
GetValueAsync(...).AsTask() is very likely to be incomplete at this point, causing a throw in normal run mode. If you must resolve synchronously for WithInitialState, block explicitly (e.g., GetAwaiter().GetResult() under the pragma) or refactor to avoid resolving here (e.g., store the ReferenceExpression/defer snapshot population). As written, this can make startup fail nondeterministically.
| var csTask = cs.GetValueAsync(default).AsTask(); | |
| if (!csTask.IsCompletedSuccessfully) | |
| { | |
| throw new InvalidOperationException("Connection string could not be resolved!"); | |
| } | |
| #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits | |
| builder.WithInitialState(new CustomResourceSnapshot | |
| { | |
| ResourceType = "Azure AI Inference Model", | |
| State = KnownResourceStates.Running, | |
| Properties = [ | |
| new("ConnectionString", csTask.Result ) { IsSensitive = true } | |
| ] | |
| }); | |
| #pragma warning restore VSTHRD002 | |
| #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits | |
| var connectionString = cs.GetValueAsync(default).GetAwaiter().GetResult(); | |
| #pragma warning restore VSTHRD002 | |
| builder.WithInitialState(new CustomResourceSnapshot | |
| { | |
| ResourceType = "Azure AI Inference Model", | |
| State = KnownResourceStates.Running, | |
| Properties = [ | |
| new("ConnectionString", connectionString ) { IsSensitive = true } | |
| ] | |
| }); |
| Ollama, | ||
| OpenAI, | ||
| AzureOpenAI, | ||
| AzureAIInference, |
There was a problem hiding this comment.
ClientChatProvider includes AzureAIInference, but the corresponding factory switch in ChatClientExtensions doesn’t handle it (it will throw NotSupportedException). Either add an AzureAIInference branch in the switch (and document the expected connection string keys), or remove the enum value to avoid advertising a supported provider that cannot be constructed.
| AzureAIInference, |
| const addedUserMessage = mutations.some(m => Array.from(m.addedNodes).some(n => n.parentElement === this && n.classList?.contains('user-message'))); | ||
| const elem = this.lastElementChild; | ||
| if (ChatMessages._isFirstAutoScroll || addedUserMessage || this._elemIsNearScrollBoundary(elem, 300)) { | ||
| elem.scrollIntoView({ behavior: ChatMessages._isFirstAutoScroll ? 'instant' : 'smooth' }); |
There was a problem hiding this comment.
scrollIntoView’s behavior values are typically 'auto' | 'smooth'. 'instant' is not consistently supported across browsers and may be ignored. Consider using 'auto' for the first scroll to ensure cross-browser behavior.
| elem.scrollIntoView({ behavior: ChatMessages._isFirstAutoScroll ? 'instant' : 'smooth' }); | |
| elem.scrollIntoView({ behavior: ChatMessages._isFirstAutoScroll ? 'auto' : 'smooth' }); |
| } | ||
| else | ||
| { | ||
| throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentId must be provided"); |
There was a problem hiding this comment.
This error message references configuration keys (A2AServer:ApiKey / A2AServer:ConnectionString) that aren’t what the code reads (it reads OPENAI_API_KEY and AZURE_FOUNDRY_PROJECT_ENDPOINT, plus agentId). Update the message to match the actual required inputs so users can fix the configuration quickly.
| throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentId must be provided"); | |
| throw new ArgumentException("Either OPENAI_API_KEY or AZURE_FOUNDRY_PROJECT_ENDPOINT and agentId must be provided"); |
| ChatClientAgent narrator = new( | ||
| chatClient, | ||
| """ | ||
| You are are the narrator of a puzzle involving knights (who always tell the truth) and knaves (who always lie). |
There was a problem hiding this comment.
Correct the duplicated word 'are' in the prompt text.
| You are are the narrator of a puzzle involving knights (who always tell the truth) and knaves (who always lie). | |
| You are the narrator of a puzzle involving knights (who always tell the truth) and knaves (who always lie). |
…o _to_delete New structure: - 01-get-started/ (6 progressive samples, single-file .cs) - 02-agents/ (tools, middleware, conversations, providers) - 03-workflows/ (9 workflow patterns) - 04-hosting/ (a2a, ag-ui, azure-functions, openai-endpoints - multi-project) - 05-end-to-end/ (full apps) - _to_delete/ (old samples for reviewer reference) Uses Azure AI Foundry Responses API via AIProjectClient as default provider. Old files preserved in _to_delete/ for review before deletion.
3f352b1 to
5aa9f39
Compare
|
Closing to redo with updated structure — workflows will be handled separately. |
Summary
Restructures the .NET samples into a clear progressive learning path with 5 sections:
Key design decisions
.csdemonstrates a single topic// <name>/// </name>tags for docs:::codereferencesEnvironment.GetEnvironmentVariable()with clear variable namesAIProjectClientWhat changed
_to_delete/for reviewer inspection (not deleted)README.mdwith new category structureRelated PRs
semantic-kernel-prrepo)