Compare commits

..
Author SHA1 Message Date
Evan MattsonandGitHub a84ad42f6d Bump Python package versions for 1.7.0 release (#6142)
Bumps the released 1.6.0 packages agent-framework, agent-framework-core, agent-framework-foundry, and agent-framework-openai to 1.7.0, with root continuing to exactly pin agent-framework-core[all]. Bumps the changed prerelease packages agent-framework-a2a, agent-framework-chatkit, agent-framework-declarative, agent-framework-devui, and agent-framework-foundry-hosting to the 260528 date stamp, raises core floors on the packages included in this release, raises Foundry's OpenAI floor alongside OpenAI, and raises ChatKit's openai-chatkit floor to the minimum version required by the current typed API usage. No beta cohort bump was applied; the absent mistal/mistral package was intentionally not bumped because no such package exists in this branch.
2026-05-28 19:45:31 +09:00
Peter IbekweandGitHub ded17b178c Python: [Breaking] Remove Python-only declarative actions and rename alias kinds to C# canonical names (#6126)
* Remove Python-only declarative actions and rename alias kinds to C# canonical names

* Address PR comments.

* Address PR comments.

* Reduce verbose and duplicate output from sample workflow.
2026-05-28 10:16:22 +00:00
Yufeng HeandGitHub 55dc3ce734 Python: fix: pass Foundry agent default headers (#6040)
* fix: pass Foundry agent default headers

* test: loosen Foundry default header assertions
2026-05-28 10:08:14 +00:00
BaidarandGitHub 9d8e5ca4f5 Python: Allow hosted checkpoints to restore MessageRole (#6049)
* Python: Allow hosted checkpoints to restore MessageRole

Allow Responses hosting checkpoint storage to deserialize the Azure Responses MessageRole enum that hosted workflows can persist inside Agent Framework Message objects.

Add regression coverage for both direct load() and the hosted get_latest() restore path, including the plain-storage failure mode where list_checkpoints logs the blocked type and get_latest() returns None.

Ruff also normalizes a duplicate contextlib import in the touched hosting module.

* Address MessageRole checkpoint review comments

* Cover hosted MessageRole checkpoint restore path
2026-05-28 09:13:30 +00:00
af787569b3 Python: Align c# and python TodoProvider tool names (#6107)
* Align c# and python TodoProvider tool names

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Address PR review: remove __slots__ and add typed schemas for tool params

- Remove __slots__ from TodoItem, TodoInput, and TodoCompleteInput classes
  (not needed for low-instance-count objects and hinders dev scenarios)
- Add _TodoAddItemSchema and _TodoCompleteItemSchema TypedDicts to provide
  proper JSON schema for todos_add and todos_complete tool parameters
- Use typing_extensions for Python 3.10 compatibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 08:40:13 +00:00
3db2004e49 Python: read headers defensively to support stream wrappers without .headers (#6028) (#6029)
`OpenAIChatClient._inner_get_response()` reads `.headers` on the raw streaming
response returned by `client.responses.with_raw_response.create(stream=True)`
(and its three sibling call sites - retrieve-streaming, non-streaming create
and background retrieve) to surface the `x-ms-served-model` Azure header,
introduced in #5910.

When `azure-ai-projects>=2.1.0` experimental GenAI tracing is enabled
(`AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true`), the instrumentor wraps the
raw streaming response in an inline `AsyncStreamWrapper` that exposes
`.response` but not `.headers`. Reading `raw_create_response.headers` then
raises `AttributeError: 'AsyncStreamWrapper' object has no attribute 'headers'`,
which `FoundryChatClient` rethrows as a `ChatClientException` and breaks every
streaming call (workflows and free chat).

Fix: read the header dict via `getattr(raw_response, "headers", None)` at all
four call sites. `_extract_served_model()` already short-circuits on `None`,
so the served-model surfacing degrades gracefully (model stays the deployment
alias) instead of crashing when the response is wrapped by an instrumentor
that does not proxy `.headers`.

Regression test added:
`test_streaming_response_without_headers_attribute_does_not_crash`
simulates a stream wrapper that raises `AttributeError` on `.headers` and
asserts the stream still completes with the deployment alias as `update.model`.

Fixes #6028

Co-authored-by: Emilien Mottet <emilien.mottet@michelin.com>
2026-05-28 08:37:38 +00:00
efdabd56dc feat(a2a): add A2AAgentSession with reference_task_ids and input-required support (#5980)
* feat(a2a): link follow-up messages via reference_task_ids

Track the task_id from A2A responses (task, status_update, artifact_update,
and message payloads) on session.state and include it as reference_task_ids
on subsequent outgoing messages. This enables remote agents to correlate
follow-up messages as task refinements per the A2A spec.

Resolves #5938

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(a2a): add A2AAgentSession for typed protocol state tracking

Introduce A2AAgentSession (subclass of AgentSession) with context_id,
task_id, and task_state properties. This follows the DurableAgentSession
pattern and mirrors the .NET A2AAgentSession design.

- Track task_id, context_id, and task_state from all response payload types
- Validate context_id consistency (raise on mismatch)
- Auto-assign server-generated context_id when not set
- Only A2AAgentSession gets reference tracking (no state dict fallback)
- Plain AgentSession continues to work without reference tracking
- Add serialization support (to_dict/from_dict)
- Export via agent_framework.a2a and agent_framework_a2a

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* style: remove unnecessary string annotation (pyupgrade)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: use AgentSession.from_dict for state deserialization

Avoids importing private _deserialize_state, matching the
DurableAgentSession pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: track context_id from message payloads in A2AAgentSession

Previously, context_id was only captured from task, status_update, and
artifact_update payloads. Message-only responses (which carry context_id
but may lack task_id) were silently lost. This fix:

- Captures msg.context_id in the message handler
- Persists session state when either last_task_id or last_context_id is
  present (not only when task_id is truthy)
- Only updates task_id/task_state when a task_id was actually returned
- Adds a test for message-only context_id tracking

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* addressed comments

* Gate status content to INPUT_REQUIRED/terminal states (match .NET)

Match .NET's GetUserInputRequests pattern: only emit TaskStatusUpdateEvent
message content when state is INPUT_REQUIRED or terminal. Intermediate
status text (WORKING, SUBMITTED) is no longer surfaced to callers.

When state is INPUT_REQUIRED, set additional_properties['input_required']
= True so callers can distinguish input requests from final responses.

Closes #5937

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review: remove message task_id tracking, defensive fallbacks, and input_required flag

- Do not track task_id from Message payloads (simple interactions
  without task tracking)
- Remove 'or last_task_id' fallback from status_update and
  artifact_update handlers (spec guarantees task_id is always set)
- Remove additional_properties['input_required'] flag (content gating
  to INPUT_REQUIRED/terminal states is the signal itself)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 08:36:49 +00:00
371a869e44 Fix deprecated asyncio.iscoroutinefunction usage in test_cleanup_hooks.py (#4563)
Fixes #4522

Replace deprecated `asyncio.iscoroutinefunction()` with `inspect.iscoroutinefunction()`
to resolve Python 3.13+ deprecation warning.

Changes:
- Added `import inspect` to imports
- Replaced `asyncio.iscoroutinefunction(hook)` with `inspect.iscoroutinefunction(hook)` on line 126
- This makes the code consistent with other test methods in the same file (lines 201, 236)

The rest of the file already uses `inspect.iscoroutinefunction()` correctly, making
this change consistent with the existing codebase pattern.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tao Chen <taochen@microsoft.com>
2026-05-28 02:29:31 +00:00
e532ced950 Add hosting samples overview README (#5407)
Co-authored-by: whenpoem <187613766+whenpoem@users.noreply.github.com>
2026-05-27 21:08:17 +00:00
5d8dd4ea4b .NET: [BREAKING] Remove Support for Code-Gen in Declarative Workflows (#6095)
* Removed

* Remove sample

* Remove orphaned code-gen related code path

* Remove remaining references to code gen.

---------

Co-authored-by: Chris Rickman <crickman@microsoft.com>
Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
2026-05-27 20:14:38 +00:00
Yufeng HeandGitHub 4c4e1d9b87 Python: fix: keep citation get_url metadata (#6037)
* fix: keep citation get_url metadata

* fix: satisfy citation metadata mypy check
2026-05-27 20:09:02 +00:00
1d301af7d2 .NET: Add MCP-based skills support (skill-md type) (#6108)
* Add MCP-based skills support

- Add AgentMcpSkill, AgentMcpSkillResource, AgentMcpSkillsSource, and McpSkillIndex to Microsoft.Agents.AI.Mcp
- Add AgentSkillsProviderBuilderMcpExtensions for DI integration
- Add Agent_Step06_McpBasedSkills sample project
- Add unit tests for AgentMcpSkillsSource
- Update solution file and project references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove unnecessary [Experimental] attributes from MCP package

The package is already alpha, so the [Experimental] attribute is redundant.
Removed from both AgentSkillsProviderBuilderMcpExtensions and
AgentMcpSkillsSource classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make Agent_Step06_McpBasedSkills self-contained and add to verify-samples

Embed an internal MCP server (launched via --server flag as a child process)
that serves skill://index.json and skill://unit-converter/SKILL.md resources,
replacing the external MCP_SKILLS_ENDPOINT dependency. The sample now uses
StdioClientTransport and a fixed prompt instead of an interactive loop.

Added SampleDefinition to AgentsSamples.cs for automated verification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Sort usings

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-27 18:38:57 +00:00
westeyandGitHub 8fbda1de22 Remove responses experimental flag from FoundryAgent et.al. (#6121) 2026-05-27 18:18:44 +00:00
191 changed files with 2861 additions and 9604 deletions
+4 -4
View File
@@ -173,11 +173,11 @@ new SampleDefinition
```csharp
new SampleDefinition
{
Name = "Workflow_Declarative_GenerateCode",
ProjectPath = "samples/03-workflows/Declarative/GenerateCode",
Name = "Workflow_Visualization",
ProjectPath = "samples/03-workflows/Visualization",
IsDeterministic = true,
MustContain = ["WORKFLOW: Parsing", "WORKFLOW: Defined"],
ExpectedOutputDescription = ["The output should show a YAML workflow being parsed and C# code being generated from it."],
MustContain = ["Generating workflow visualization...", "Mermaid string:", "DiGraph string:"],
ExpectedOutputDescription = ["The output should show workflow visualization in Mermaid and DiGraph formats."],
},
```
+7 -7
View File
@@ -117,17 +117,18 @@
<Project Path="samples/02-agents/AgentSkills/Agent_Step03_ClassBasedSkills/Agent_Step03_ClassBasedSkills.csproj" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step04_MixedSkills/Agent_Step04_MixedSkills.csproj" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step05_SkillsWithDI/Agent_Step05_SkillsWithDI.csproj" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step06_McpBasedSkills/Agent_Step06_McpBasedSkills.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/Harness/">
<File Path="samples/02-agents/Harness/README.md" />
<Project Path="samples/02-agents/Harness/ConsoleReactiveComponents/ConsoleReactiveComponents.csproj" />
<Project Path="samples/02-agents/Harness/ConsoleReactiveFramework/ConsoleReactiveFramework.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Shared_Console/Harness_Shared_Console.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Shared_Console_OpenAI/Harness_Shared_Console_OpenAI.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step01_Research/Harness_Step01_Research.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Harness_Step02_Research_WithBackgroundAgents.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step03_DataProcessing/Harness_Step03_DataProcessing.csproj" />
<Project Path="samples/02-agents/Harness/Harness_Step04_CodeExecution/Harness_Step04_CodeExecution.csproj" />
<Project Path="samples/02-agents/Harness/ConsoleReactiveFramework/ConsoleReactiveFramework.csproj" />
<Project Path="samples/02-agents/Harness/ConsoleReactiveComponents/ConsoleReactiveComponents.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AGUI/Step05_StateManagement/">
<Project Path="samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj" />
@@ -241,11 +242,10 @@
<Project Path="samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj" />
<Project Path="samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj" />
<Project Path="samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj" />
<Project Path="samples/03-workflows/Declarative/GenerateCode/GenerateCode.csproj" />
<Project Path="samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj" />
<Project Path="samples/03-workflows/Declarative/InputArguments/InputArguments.csproj" />
<Project Path="samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj" />
<Project Path="samples/03-workflows/Declarative/InvokeFoundryToolboxMcp/InvokeFoundryToolboxMcp.csproj" />
<Project Path="samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj" />
<Project Path="samples/03-workflows/Declarative/InvokeHttpRequest/InvokeHttpRequest.csproj" />
<Project Path="samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj" />
<Project Path="samples/03-workflows/Declarative/Marketing/Marketing.csproj" />
@@ -597,8 +597,8 @@
<Project Path="src/Microsoft.Agents.AI.DurableTask/Microsoft.Agents.AI.DurableTask.csproj" />
<Project Path="src/Microsoft.Agents.AI.Foundry.Hosting/Microsoft.Agents.AI.Foundry.Hosting.csproj" />
<Project Path="src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj" />
<Project Path="src/Microsoft.Agents.AI.Harness/Microsoft.Agents.AI.Harness.csproj" />
<Project Path="src/Microsoft.Agents.AI.GitHub.Copilot/Microsoft.Agents.AI.GitHub.Copilot.csproj" />
<Project Path="src/Microsoft.Agents.AI.Harness/Microsoft.Agents.AI.Harness.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A/Microsoft.Agents.AI.Hosting.A2A.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj" />
@@ -624,8 +624,8 @@
<Project Path="tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletion.IntegrationTests.csproj" />
<Project Path="tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj" />
<Project Path="tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj" />
<Project Path="tests/Foundry.Hosting.IntegrationTests/Foundry.Hosting.IntegrationTests.csproj" />
<Project Path="tests/Foundry.Hosting.IntegrationTests.TestContainer/Foundry.Hosting.IntegrationTests.TestContainer.csproj" />
<Project Path="tests/Foundry.Hosting.IntegrationTests/Foundry.Hosting.IntegrationTests.csproj" />
<Project Path="tests/Foundry.IntegrationTests/Foundry.IntegrationTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.GitHub.Copilot.IntegrationTests/Microsoft.Agents.AI.GitHub.Copilot.IntegrationTests.csproj" />
@@ -650,8 +650,8 @@
<Project Path="tests/Microsoft.Agents.AI.Declarative.UnitTests/Microsoft.Agents.AI.Declarative.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.DevUI.UnitTests/Microsoft.Agents.AI.DevUI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Harness.UnitTests/Microsoft.Agents.AI.Harness.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Microsoft.Agents.AI.Hosting.A2A.UnitTests.csproj" />
-3
View File
@@ -11,9 +11,6 @@
<ItemGroup Condition="'$(InjectSharedIntegrationTestAzureCredentialsCode)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)\..\..\src\Shared\IntegrationTestsAzureCredentials\*.cs" LinkBase="Shared\IntegrationTestsAzureCredentials" />
</ItemGroup>
<ItemGroup Condition="'$(InjectSharedBuildTestCode)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)\..\..\src\Shared\CodeTests\*.cs" LinkBase="Shared\CodeTests" />
</ItemGroup>
<ItemGroup Condition="'$(InjectSharedWorkflowsExecution)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)\..\..\src\Shared\Workflows\Execution\*.cs" LinkBase="Shared\Workflows" />
</ItemGroup>
@@ -363,6 +363,25 @@ internal static class AgentsSamples
],
},
new SampleDefinition
{
Name = "Agent_Step06_McpBasedSkills",
ProjectPath = "samples/02-agents/AgentSkills/Agent_Step06_McpBasedSkills",
RequiredEnvironmentVariables = ["AZURE_OPENAI_ENDPOINT"],
OptionalEnvironmentVariables = ["AZURE_OPENAI_DEPLOYMENT_NAME"],
MustContain =
[
"Discovering MCP-based skills",
"Agent:",
],
ExpectedOutputDescription =
[
"The output should show the agent converting 26.2 miles to kilometers and 75 kilograms to pounds.",
"The response should contain approximate numeric values for both conversions.",
"The output should not contain error messages or stack traces.",
],
},
// ── AgentWithMemory ─────────────────────────────────────────────────
new SampleDefinition
@@ -439,15 +439,6 @@ internal static class WorkflowSamples
ExpectedOutputDescription = ["The output should show a workflow calling function tools (e.g. a menu plugin) to answer a question about restaurant specials."],
},
new SampleDefinition
{
Name = "Workflow_Declarative_GenerateCode",
ProjectPath = "samples/03-workflows/Declarative/GenerateCode",
IsDeterministic = true,
MustContain = ["WORKFLOW: Parsing", "WORKFLOW: Defined"],
ExpectedOutputDescription = ["The output should show a YAML workflow being parsed and C# code being generated from it."],
},
new SampleDefinition
{
Name = "Workflow_Declarative_HostedWorkflow",
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);MAAI001;MCPEXP001</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="ModelContextProtocol" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Mcp\Microsoft.Agents.AI.Mcp.csproj" />
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,142 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to discover Agent Skills served over MCP.
//
// When launched with "--server", this executable runs a small MCP stdio server
// that exposes a unit-converter skill via the SEP-2640 convention:
// - skill://index.json — discovery document listing all skills
// - skill://unit-converter/SKILL.md — the skill instructions
//
// In default (client) mode the sample launches itself as a child process,
// connects via StdioClientTransport, and uses AgentSkillsProviderBuilder
// to discover and inject the skill into a ChatClientAgent.
using System.ComponentModel;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Client;
using ModelContextProtocol.Server;
using OpenAI.Responses;
if (args.Length > 0 && args[0] == "--server")
{
await RunMcpServerAsync();
return;
}
// --- Configuration ---
string openAiEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-5.4-mini";
// --- MCP client + skill discovery ---
// Launch this same assembly as a stdio MCP server in a child process.
var thisAssemblyPath = typeof(Program).Assembly.Location;
Console.WriteLine("Discovering MCP-based skills");
await using McpClient client = await McpClient.CreateAsync(
new StdioClientTransport(new()
{
Name = "skills-server",
Command = "dotnet",
Arguments = [thisAssemblyPath, "--server"],
}));
var skillsProvider = new AgentSkillsProviderBuilder()
.UseMcpSkills(client)
.Build();
// --- Agent ---
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(openAiEndpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "SkillsAgent",
ChatOptions = new()
{
Instructions = "You are a helpful assistant. Use available skills to answer the user.",
},
AIContextProviders = [skillsProvider],
},
model: deploymentName);
// --- Run ---
Console.WriteLine(new string('-', 60));
AgentResponse response = await agent.RunAsync(
"How many kilometers is a marathon (26.2 miles)? And how many pounds is 75 kilograms?");
Console.WriteLine($"Agent: {response.Text}");
// --- Server mode (launched as a child process via --server) ---------------------------------
static async Task RunMcpServerAsync()
{
var builder = Host.CreateApplicationBuilder();
// Critical for stdio transport: any provider that writes to stdout will corrupt the
// JSON-RPC channel. Clear all providers; the MCP SDK routes its own diagnostics
// appropriately.
builder.Logging.ClearProviders();
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);
builder.Services.AddMcpServer(o => o.ServerInfo = new() { Name = "SkillsServer", Version = "1.0.0" })
.WithStdioServerTransport()
.WithResources<SkillResources>();
await builder.Build().RunAsync();
}
#pragma warning disable CA1812 // Discovered by MCP SDK via [McpServerResourceType] attribute
[McpServerResourceType]
internal sealed class SkillResources
#pragma warning restore CA1812
{
private const string IndexJson = """
{
"$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
"skills": [
{
"name": "unit-converter",
"type": "skill-md",
"description": "Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.",
"url": "skill://unit-converter/SKILL.md"
}
]
}
""";
private const string SkillMd = """
---
name: unit-converter
description: Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.
---
## Usage
When the user requests a unit conversion, use these factors:
| From | To | Factor |
|-------------|-------------|----------|
| miles | kilometers | 1.60934 |
| kilometers | miles | 0.621371 |
| pounds | kilograms | 0.453592 |
| kilograms | pounds | 2.20462 |
Formula: result = value Ă— factor
""";
[McpServerResource(UriTemplate = "skill://index.json", Name = "Skill Index", MimeType = "application/json")]
[Description("SEP-2640 skill discovery index")]
public static string GetIndex() => IndexJson;
[McpServerResource(UriTemplate = "skill://unit-converter/SKILL.md", Name = "Unit Converter Skill", MimeType = "text/markdown")]
[Description("Unit converter skill instructions")]
public static string GetSkillMd() => SkillMd;
}
@@ -0,0 +1,34 @@
# MCP-Based Agent Skills Sample
This sample demonstrates how to discover **Agent Skills served over MCP** with a `ChatClientAgent`.
## What it demonstrates
- Hosting a small MCP server (in this same executable, launched with `--server`) that
exposes skill resources following the SEP-2640 convention.
- Connecting an `McpClient` to the embedded server via stdio transport.
- Building an `AgentSkillsProvider` via `UseMcpSkills(client)`, which reads
`skill://index.json` (SEP-2640 canonical discovery) and constructs skills from the
index entries.
- The progressive disclosure pattern across MCP: advertise → load → read resources, exactly
as for filesystem-backed skills.
## Running the Sample
### Prerequisites
- .NET 10.0 SDK
- Azure OpenAI endpoint with a deployed model
### Setup
```powershell
$env:AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-5.4-mini"
```
### Run
```powershell
dotnet run
```
@@ -9,6 +9,7 @@ Samples demonstrating Agent Skills capabilities. Each sample shows a different w
| [Agent_Step03_ClassBasedSkills](Agent_Step03_ClassBasedSkills/) | Define skills as C# classes using `AgentClassSkill`. |
| [Agent_Step04_MixedSkills](Agent_Step04_MixedSkills/) | **(Advanced)** Combine file-based, code-defined, and class-based skills using `AgentSkillsProviderBuilder`. |
| [Agent_Step05_SkillsWithDI](Agent_Step05_SkillsWithDI/) | Use Dependency Injection with both code-defined (`AgentInlineSkill`) and class-based (`AgentClassSkill`) skills. |
| [Agent_Step06_McpBasedSkills](Agent_Step06_McpBasedSkills/) | Discover skills served over the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) via `AgentMcpSkillsSource`. Spins up an in-process MCP server that exposes skills as resources (`skill://...`) and connects an `McpClient` to it. |
## Key Concepts
@@ -7,20 +7,20 @@ using Microsoft.Extensions.AI;
namespace Harness.Shared.Console.ToolFormatters;
/// <summary>
/// Formats <c>TodoList_*</c> tool calls with tree-view output for added items
/// Formats <c>todos_*</c> tool calls with tree-view output for added items
/// and structured output for complete/remove operations.
/// </summary>
public sealed class TodoToolFormatter : ToolCallFormatter
{
/// <inheritdoc/>
public override bool CanFormat(FunctionCallContent call) => call.Name.StartsWith("TodoList_", StringComparison.Ordinal);
public override bool CanFormat(FunctionCallContent call) => call.Name.StartsWith("todos_", StringComparison.Ordinal);
/// <inheritdoc/>
public override string? FormatDetail(FunctionCallContent call) => call.Name switch
{
"TodoList_Add" => FormatAddTodos(call),
"TodoList_Complete" => FormatCompleteTodos(call),
"TodoList_Remove" => FormatIdList(call, "ids", "Remove"),
"todos_add" => FormatAddTodos(call),
"todos_complete" => FormatCompleteTodos(call),
"todos_remove" => FormatIdList(call, "ids", "Remove"),
_ => null,
};
@@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);CA1812</NoWarn>
</PropertyGroup>
<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Logging" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
</ItemGroup>
</Project>
@@ -1,105 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics;
using Microsoft.Agents.AI.Workflows.Declarative;
namespace Demo.DeclarativeEject;
/// <summary>
/// HOW TO: Convert a workflow from a declartive (yaml based) definition to code.
/// </summary>
/// <remarks>
/// <b>Usage</b>
/// Provide the path to the workflow definition file as the first argument.
/// All other arguments are intepreted as a queue of inputs.
/// When no input is queued, interactive input is requested from the console.
/// </remarks>
internal sealed class Program
{
public static void Main(string[] args)
{
Program program = new(args);
program.Execute();
}
private void Execute()
{
// Read and parse the declarative workflow.
Notify($"WORKFLOW: Parsing {Path.GetFullPath(this.WorkflowFile)}");
Stopwatch timer = Stopwatch.StartNew();
// Use DeclarativeWorkflowBuilder to generate code based on a YAML file.
string code =
DeclarativeWorkflowBuilder.Eject(
this.WorkflowFile,
DeclarativeWorkflowLanguage.CSharp,
workflowNamespace: "Demo.DeclarativeCode",
workflowPrefix: "Sample");
Notify($"\nWORKFLOW: Defined {timer.Elapsed}\n");
Console.WriteLine(code);
}
private const string DefaultWorkflow = "Marketing.yaml";
private string WorkflowFile { get; }
private Program(string[] args)
{
this.WorkflowFile = ParseWorkflowFile(args);
}
private static string ParseWorkflowFile(string[] args)
{
string workflowFile = args.FirstOrDefault() ?? DefaultWorkflow;
if (!File.Exists(workflowFile) && !Path.IsPathFullyQualified(workflowFile))
{
string? repoFolder = GetRepoFolder();
if (repoFolder is not null)
{
workflowFile = Path.Combine(repoFolder, "declarative-agents", "workflow-samples", workflowFile);
workflowFile = Path.ChangeExtension(workflowFile, ".yaml");
}
}
if (!File.Exists(workflowFile))
{
throw new InvalidOperationException($"Unable to locate workflow: {Path.GetFullPath(workflowFile)}.");
}
return workflowFile;
static string? GetRepoFolder()
{
DirectoryInfo? current = new(Directory.GetCurrentDirectory());
while (current is not null)
{
if (Directory.Exists(Path.Combine(current.FullName, ".git")))
{
return current.FullName;
}
current = current.Parent;
}
return null;
}
}
private static void Notify(string message)
{
Console.ForegroundColor = ConsoleColor.Cyan;
try
{
Console.WriteLine(message);
}
finally
{
Console.ResetColor();
}
}
}
@@ -1,28 +0,0 @@
{
"profiles": {
"Marketing": {
"commandName": "Project",
"commandLineArgs": "\"Marketing.yaml\""
},
"MathChat": {
"commandName": "Project",
"commandLineArgs": "\"MathChat.yaml\""
},
"Question": {
"commandName": "Project",
"commandLineArgs": "\"Question.yaml\""
},
"Research": {
"commandName": "Project",
"commandLineArgs": "\"DeepResearch.yaml\""
},
"ResponseObject": {
"commandName": "Project",
"commandLineArgs": "\"ResponseObject.yaml\""
},
"UserInput": {
"commandName": "Project",
"commandLineArgs": "\"UserInput.yaml\""
}
}
}
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -13,7 +12,6 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
@@ -22,7 +20,6 @@ namespace Azure.AI.Projects;
/// <summary>
/// Provides extension methods for <see cref="AIProjectClient"/>.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static partial class AIProjectClientExtensions
{
/// <summary>
@@ -374,6 +371,7 @@ public static partial class AIProjectClientExtensions
if (agentDefinition is DeclarativeAgentDefinition { Tools: { Count: > 0 } definitionTools })
{
// Check if no tools were provided while the agent definition requires in-proc tools.
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (requireInvocableTools && chatOptions?.Tools is not { Count: > 0 } && definitionTools.Any(t => t is FunctionTool))
{
throw new ArgumentException("The agent definition in-process tools must be provided in the extension method tools parameter.");
@@ -406,6 +404,7 @@ public static partial class AIProjectClientExtensions
(agentTools ??= []).Add(responseTool.AsAITool());
}
#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (requireInvocableTools && missingTools is { Count: > 0 })
{
@@ -1,13 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Foundry;
@@ -17,7 +15,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <c>to_prompt_agent(agent)</c> function for agents whose underlying chat client is a
/// <see cref="FoundryChatClient"/>.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static class ChatClientAgentFoundryExtensions
{
/// <summary>
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using OpenAI.Responses;
#pragma warning disable OPENAI001
@@ -27,7 +26,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <c>FoundryAITool.CreateOpenApiTool(definition)</c>
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static class FoundryAITool
{
/// <summary>
@@ -4,14 +4,12 @@ using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Foundry;
@@ -36,7 +34,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <c>AsAIAgent</c> extension methods on <see cref="AIProjectClient"/>.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public sealed class FoundryAgent : DelegatingAIAgent
{
/// <summary>
@@ -261,6 +258,7 @@ public sealed class FoundryAgent : DelegatingAIAgent
return innerAgent;
}
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (innerAgent.ChatClient.GetService<OpenAIRequestPolicies>() is { } policies)
{
OpenAIRequestPoliciesReflection.AddPolicyIfMissing(
@@ -268,6 +266,7 @@ public sealed class FoundryAgent : DelegatingAIAgent
ClientHeadersPolicy.Instance,
PipelinePosition.PerCall);
}
#pragma warning restore MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
return new ClientHeadersAgent(innerAgent);
}
@@ -2,13 +2,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Files;
using OpenAI.VectorStores;
@@ -23,7 +21,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <see cref="FoundryChatClient"/> at the agent level so callers do not need to drop down to
/// <c>agent.GetService&lt;FoundryChatClient&gt;().X()</c> for common workflows.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static class FoundryAgentExtensions
{
/// <summary>
@@ -4,7 +4,6 @@ using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -13,7 +12,6 @@ using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Files;
using OpenAI.Responses;
@@ -53,7 +51,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// behind an Agent Endpoint. It is not synonymous with the Agent Endpoint mode itself.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public sealed class FoundryChatClient : DelegatingChatClient
{
private readonly ChatClientMetadata _metadata;
@@ -652,6 +649,7 @@ public sealed class FoundryChatClient : DelegatingChatClient
/// <summary>Best-effort registration of <see cref="AgentFrameworkUserAgentPolicy"/> via the MEAI <see cref="OpenAIRequestPolicies"/> hook with at-most-once dedup per pipeline.</summary>
private static void TryRegisterAgentFrameworkUserAgentPolicy(IChatClient? innerClient)
{
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (innerClient?.GetService<OpenAIRequestPolicies>() is { } policies)
{
// OpenAIRequestPoliciesReflection.AddPolicyIfMissing performs a check-then-add against
@@ -663,6 +661,7 @@ public sealed class FoundryChatClient : DelegatingChatClient
AgentFrameworkUserAgentPolicy.Instance,
PipelinePosition.PerCall);
}
#pragma warning restore MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}
/// <summary>
@@ -675,6 +674,7 @@ public sealed class FoundryChatClient : DelegatingChatClient
/// </summary>
private static void TryRegisterServedModelPolicy(IChatClient? innerClient)
{
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
if (innerClient?.GetService<OpenAIRequestPolicies>() is { } policies)
{
OpenAIRequestPoliciesReflection.AddPolicyIfMissing(
@@ -682,6 +682,7 @@ public sealed class FoundryChatClient : DelegatingChatClient
ServedModelPolicy.Instance,
PipelinePosition.PerCall);
}
#pragma warning restore MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}
/// <summary>Default OAuth scope for the Azure AI resource. Matches the scope used by <c>Azure.AI.Extensions.OpenAI</c>'s internal authentication helper so the bearer token is accepted by the Foundry control plane.</summary>
@@ -1,14 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
@@ -34,7 +32,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <item><description><b>Agent Endpoint (Mode 3)</b>: throw — no local definition exists to convert.</description></item>
/// </list>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
internal static class FoundryPromptAgentConverter
{
/// <summary>Performs the conversion for an agent whose chat client and chat options are supplied.</summary>
@@ -3,7 +3,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
namespace Microsoft.Agents.AI.Foundry;
@@ -22,7 +21,6 @@ namespace Microsoft.Agents.AI.Foundry;
/// <c>FoundryAITool.CreateHostedMcpToolbox(...)</c> factory overloads.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public sealed class HostedMcpToolboxAITool : HostedMcpServerTool
{
/// <summary>
@@ -6,7 +6,6 @@
related per-agent endpoint surface). Flip back to IsReleased=true once Azure.AI.Projects
ships a stable 2.1.0. -->
<InjectSharedThrow>true</InjectSharedThrow>
<NoWarn>$(NoWarn);OPENAI001</NoWarn>
</PropertyGroup>
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
@@ -13,7 +13,6 @@ namespace Azure.AI.Extensions.OpenAI;
/// Provides extension methods for <see cref="ProjectResponsesClient"/>
/// to simplify the creation of AI agents that work with Azure AI services.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static class ProjectResponsesClientExtensions
{
/// <summary>
@@ -4,13 +4,15 @@
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
<RootNamespace>Microsoft.Agents.AI.Mcp</RootNamespace>
<VersionSuffix>alpha</VersionSuffix>
<NoWarn>$(NoWarn);MEAI001;MCPEXP001</NoWarn>
<NoWarn>$(NoWarn);MEAI001;MAAI001;MCPEXP001</NoWarn>
</PropertyGroup>
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
</PropertyGroup>
@@ -34,4 +36,8 @@
<InternalsVisibleTo Include="Microsoft.Agents.AI.Mcp.UnitTests" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,133 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.Diagnostics;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace Microsoft.Agents.AI;
/// <summary>
/// An <see cref="AgentSkill"/> discovered from an MCP server exposing the Agent Skills convention.
/// </summary>
/// <remarks>
/// <para>
/// The skill is constructed from <c>skill://index.json</c> discovery metadata only; <see cref="GetContentAsync"/>
/// fetches the full <c>SKILL.md</c> content from the MCP server on demand via <c>resources/read</c>.
/// </para>
/// <para>
/// Per SEP-2640, resources referenced inside SKILL.md are fetched on demand via the originating MCP
/// server: <see cref="GetResourceAsync"/> resolves a relative resource name against the
/// skill's root URI, issues a <c>resources/read</c> request, and returns an <see cref="AgentMcpSkillResource"/>
/// with pre-fetched content.
/// </para>
/// </remarks>
internal sealed class AgentMcpSkill : AgentSkill
{
private const string SkillMdSuffix = "SKILL.md";
private readonly McpClient _client;
private readonly string _skillMdUri;
private readonly string _skillRootUri;
private string? _content;
/// <summary>
/// Initializes a new instance of the <see cref="AgentMcpSkill"/> class.
/// </summary>
/// <param name="frontmatter">The parsed frontmatter metadata for this skill.</param>
/// <param name="skillMdUri">
/// The full MCP resource URI of the <c>SKILL.md</c> resource (e.g. <c>skill://unit-converter/SKILL.md</c>).
/// Used by <see cref="GetContentAsync"/> to fetch the skill content on demand. The skill's root URI
/// (used to resolve sibling resources) is derived by stripping the trailing <c>SKILL.md</c> segment.
/// </param>
/// <param name="client">The MCP client used to fetch resources on demand.</param>
public AgentMcpSkill(AgentSkillFrontmatter frontmatter, string skillMdUri, McpClient client)
{
this.Frontmatter = Throw.IfNull(frontmatter);
this._skillMdUri = Throw.IfNullOrWhitespace(skillMdUri);
this._skillRootUri = ComputeSkillRootUri(skillMdUri);
this._client = Throw.IfNull(client);
}
/// <inheritdoc/>
public override AgentSkillFrontmatter Frontmatter { get; }
/// <inheritdoc/>
/// <remarks>
/// Fetches the <c>SKILL.md</c> content from the MCP server via <c>resources/read</c> on the first call
/// and caches the result.
/// </remarks>
public override async ValueTask<string> GetContentAsync(CancellationToken cancellationToken = default)
{
if (this._content is not null)
{
return this._content;
}
#pragma warning disable CA2234 // Pass system uri objects instead of strings
ReadResourceResult result = await this._client.ReadResourceAsync(this._skillMdUri, cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2234 // Pass system uri objects instead of strings
string text = string.Join("\n", result.Contents.OfType<TextResourceContents>().Select(c => c.Text));
if (text.Length == 0)
{
throw new InvalidOperationException($"The MCP server returned no text content for SKILL.md resource '{this._skillMdUri}'.");
}
return this._content = text;
}
/// <inheritdoc/>
/// <remarks>
/// Resolves <paramref name="name"/> as a relative path against the skill's root URI, issues a
/// <c>resources/read</c> request to the MCP server, and returns an <see cref="AgentMcpSkillResource"/>
/// with the pre-fetched content. Returns <see langword="null"/> when the name is empty, the server
/// returns no content, or the resource does not exist on the server.
/// </remarks>
public override async ValueTask<AgentSkillResource?> GetResourceAsync(string name, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
string uri = this._skillRootUri + name;
ReadResourceResult result;
try
{
#pragma warning disable CA2234 // Pass system uri objects instead of strings
result = await this._client.ReadResourceAsync(uri, cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2234 // Pass system uri objects instead of strings
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
return null;
}
return new AgentMcpSkillResource(name: name, result: result);
}
/// <summary>
/// Strips the trailing <c>SKILL.md</c> from the URI to produce the skill's root directory URI.
/// If the URI doesn't end with <c>SKILL.md</c>, ensures it ends with a trailing slash.
/// </summary>
private static string ComputeSkillRootUri(string skillMdUri)
{
if (skillMdUri.EndsWith(SkillMdSuffix, StringComparison.Ordinal))
{
return skillMdUri.Substring(0, skillMdUri.Length - SkillMdSuffix.Length);
}
if (skillMdUri.EndsWith("/", StringComparison.Ordinal))
{
return skillMdUri;
}
return skillMdUri + "/";
}
}
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
using ModelContextProtocol;
using ModelContextProtocol.Protocol;
namespace Microsoft.Agents.AI;
/// <summary>
/// An <see cref="AgentSkillResource"/> backed by content fetched from an MCP server.
/// </summary>
/// <remarks>
/// The <see cref="ReadResourceResult"/> is fetched eagerly by <see cref="AgentMcpSkill.GetResourceAsync"/>
/// at construction time; <see cref="ReadAsync"/> extracts the content from the result.
/// </remarks>
internal sealed class AgentMcpSkillResource : AgentSkillResource
{
private readonly ReadResourceResult _result;
/// <summary>
/// Initializes a new instance of the <see cref="AgentMcpSkillResource"/> class with a pre-fetched result.
/// </summary>
/// <param name="name">The resource name (e.g. a relative path or identifier).</param>
/// <param name="result">The result returned by the MCP server's <c>resources/read</c> request.</param>
/// <param name="description">An optional description of the resource.</param>
public AgentMcpSkillResource(string name, ReadResourceResult result, string? description = null)
: base(Throw.IfNullOrWhitespace(name), description)
{
this._result = Throw.IfNull(result);
}
/// <inheritdoc/>
/// <returns>
/// A <see cref="DataContent"/> when the resource contains binary content, a <see cref="string"/> when
/// it contains text, or <see langword="null"/> when the server returned no content blocks.
/// </returns>
public override Task<object?> ReadAsync(IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default)
{
BlobResourceContents? blob = this._result.Contents.OfType<BlobResourceContents>().FirstOrDefault();
if (blob is not null)
{
return Task.FromResult<object?>(blob.ToAIContent());
}
string text = string.Join("\n", this._result.Contents.OfType<TextResourceContents>().Select(c => c.Text));
if (text.Length == 0)
{
return Task.FromResult<object?>(null);
}
return Task.FromResult<object?>(text);
}
}
@@ -0,0 +1,182 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Shared.Diagnostics;
using ModelContextProtocol;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace Microsoft.Agents.AI;
/// <summary>
/// An <see cref="AgentSkillsSource"/> that discovers Agent Skills served over the Model Context Protocol (MCP).
/// </summary>
/// <remarks>
/// <para>
/// Discovery follows the SEP-2640 recommended approach: the source reads the well-known
/// <c>skill://index.json</c> resource and constructs one <see cref="AgentSkill"/> per
/// <c>skill-md</c> entry directly from the entry's <c>name</c>, <c>description</c>, and <c>url</c> fields.
/// The referenced <c>SKILL.md</c> resource is not read during discovery; hosts fetch its body on
/// demand via <c>resources/read</c> against the URI exposed on the resulting skill.
/// </para>
/// <para>
/// Only index entries of type <c>skill-md</c> are supported at the moment; entries of any other
/// type are skipped.
/// </para>
/// <para>
/// If <c>skill://index.json</c> is absent, unreadable, empty, or fails to parse, this source
/// returns an empty list. Discovered skills serve their referenced resources on demand via
/// <see cref="AgentSkill.GetResourceAsync"/>; they do not enumerate sibling files up front.
/// </para>
/// </remarks>
internal sealed partial class AgentMcpSkillsSource : AgentSkillsSource
{
/// <summary>
/// SEP-2640 canonical discovery document URI.
/// </summary>
private const string IndexUri = "skill://index.json";
private const string SkillMdEntryType = "skill-md";
private readonly McpClient _client;
private readonly ILogger _logger;
/// <summary>
/// Initializes a new instance of the <see cref="AgentMcpSkillsSource"/> class.
/// </summary>
/// <param name="client">An MCP client connected to a server that exposes Agent Skills resources.</param>
/// <param name="loggerFactory">Optional logger factory.</param>
public AgentMcpSkillsSource(McpClient client, ILoggerFactory? loggerFactory = null)
{
this._client = Throw.IfNull(client);
this._logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<AgentMcpSkillsSource>();
}
/// <inheritdoc/>
public override async Task<IList<AgentSkill>> GetSkillsAsync(CancellationToken cancellationToken = default)
{
McpSkillIndex? index = await this.TryReadIndexAsync(cancellationToken).ConfigureAwait(false);
var skills = new List<AgentSkill>();
foreach (var entry in index?.Skills ?? [])
{
if (this.TryCreateSkill(entry, out AgentMcpSkill? skill, out string skipReason))
{
skills.Add(skill);
LogSkillLoaded(this._logger, skill.Frontmatter.Name);
}
else
{
LogIndexEntrySkipped(this._logger, entry.Name ?? "(unnamed)", skipReason);
}
}
LogSkillsLoadedTotal(this._logger, skills.Count);
return skills;
}
private async Task<McpSkillIndex?> TryReadIndexAsync(CancellationToken cancellationToken)
{
ReadResourceResult result;
try
{
#pragma warning disable CA2234 // Pass system uri objects instead of strings
result = await this._client.ReadResourceAsync(IndexUri, cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2234 // Pass system uri objects instead of strings
}
catch (McpException ex) when (ex is McpProtocolException pex && pex.ErrorCode == McpErrorCode.ResourceNotFound)
{
LogIndexAbsent(this._logger, ex.Message);
return null;
}
catch (McpException ex)
{
LogIndexReadFailed(this._logger, ex);
return null;
}
string? indexText = result.Contents.OfType<TextResourceContents>().FirstOrDefault()?.Text;
if (string.IsNullOrWhiteSpace(indexText))
{
LogIndexEmpty(this._logger);
return null;
}
try
{
return JsonSerializer.Deserialize(indexText, McpJsonContext.Default.McpSkillIndex);
}
catch (JsonException ex)
{
LogIndexParseFailed(this._logger, ex);
return null;
}
}
private bool TryCreateSkill(
McpSkillIndexEntry entry,
[NotNullWhen(true)] out AgentMcpSkill? skill,
out string skipReason)
{
skill = null;
if (!string.Equals(entry.Type, SkillMdEntryType, StringComparison.Ordinal))
{
skipReason = $"unsupported type '{entry.Type ?? "(none)"}'";
return false;
}
if (string.IsNullOrWhiteSpace(entry.Url))
{
skipReason = "missing required 'url' field";
return false;
}
AgentSkillFrontmatter frontmatter;
try
{
frontmatter = new AgentSkillFrontmatter(entry.Name!, entry.Description!);
}
catch (ArgumentException ex)
{
skipReason = $"invalid metadata: {ex.Message}";
return false;
}
skill = new AgentMcpSkill(frontmatter, entry.Url!, this._client);
skipReason = string.Empty;
return true;
}
[LoggerMessage(LogLevel.Information, "Loaded MCP skill: {SkillName}")]
private static partial void LogSkillLoaded(ILogger logger, string skillName);
[LoggerMessage(LogLevel.Information, "Successfully loaded {Count} skills from MCP server")]
private static partial void LogSkillsLoadedTotal(ILogger logger, int count);
[LoggerMessage(LogLevel.Debug, "No skill://index.json resource available on MCP server: {Reason}")]
private static partial void LogIndexAbsent(ILogger logger, string reason);
[LoggerMessage(LogLevel.Warning, "Failed to read skill://index.json from MCP server.")]
private static partial void LogIndexReadFailed(ILogger logger, Exception exception);
[LoggerMessage(LogLevel.Debug, "skill://index.json on MCP server returned empty/non-text contents")]
private static partial void LogIndexEmpty(ILogger logger);
[LoggerMessage(LogLevel.Warning, "Failed to parse skill://index.json JSON document.")]
private static partial void LogIndexParseFailed(ILogger logger, Exception exception);
[LoggerMessage(LogLevel.Debug, "Skipping skill index entry '{SkillName}': {Reason}")]
private static partial void LogIndexEntrySkipped(ILogger logger, string skillName, string reason);
}
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Shared.Diagnostics;
using ModelContextProtocol.Client;
namespace Microsoft.Agents.AI;
/// <summary>
/// MCP-specific extension methods for <see cref="AgentSkillsProviderBuilder"/>.
/// </summary>
public static class AgentSkillsProviderBuilderMcpExtensions
{
/// <summary>
/// Adds a skill source that discovers skills served over MCP via the supplied <paramref name="client"/>.
/// </summary>
/// <param name="builder">The builder to extend.</param>
/// <param name="client">An MCP client connected to a server exposing Agent Skills resources.</param>
/// <returns>The builder instance for chaining.</returns>
public static AgentSkillsProviderBuilder UseMcpSkills(this AgentSkillsProviderBuilder builder, McpClient client)
{
_ = Throw.IfNull(builder);
_ = Throw.IfNull(client);
return builder.UseSource(new AgentMcpSkillsSource(client));
}
}
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI;
/// <summary>
/// Source-generated JSON context for MCP-skills well-known DTOs.
/// </summary>
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web, AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip)]
[JsonSerializable(typeof(McpSkillIndex))]
[JsonSerializable(typeof(McpSkillIndexEntry))]
internal sealed partial class McpJsonContext : JsonSerializerContext;
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI;
/// <summary>
/// DTO for the skill discovery index document served at <c>skill://index.json</c>.
/// </summary>
/// <remarks>
/// <para>
/// Schema reference: <see href="https://schemas.agentskills.io/discovery/0.2.0/schema.json"/>
/// (Agent Skills Discovery v0.2.0), as bound to MCP by SEP-2640. The MCP binding differs from the
/// base schema in two ways: the <c>url</c> field contains a full MCP resource URI, and the
/// <c>digest</c> field is omitted (integrity is the transport's concern over an authenticated
/// MCP connection).
/// </para>
/// <para>
/// All properties are nullable so that deserialization succeeds even when the server-side index
/// is incomplete or malformed; callers MUST validate required fields before use.
/// </para>
/// </remarks>
internal sealed class McpSkillIndex
{
/// <summary>
/// Gets or sets the opaque schema identifier URI. Required by the base schema; clients SHOULD
/// match this against known schema URIs (e.g.
/// <c>https://schemas.agentskills.io/discovery/0.2.0/schema.json</c>) before processing the index.
/// </summary>
[JsonPropertyName("$schema")]
public string? Schema { get; set; }
/// <summary>
/// Gets or sets the array of skill entries. Required by the schema; an empty or missing
/// <c>skills</c> array means the index advertises no skills.
/// </summary>
[JsonPropertyName("skills")]
public List<McpSkillIndexEntry>? Skills { get; set; }
}
/// <summary>
/// A single entry in the skill discovery index.
/// </summary>
/// <remarks>
/// Field requirements per the v0.2.0 schema and the SEP-2640 binding:
/// <list type="bullet">
/// <item><description><c>type</c>, <c>description</c>, and <c>url</c> are REQUIRED.</description></item>
/// <item><description><c>name</c> is REQUIRED for <c>skill-md</c> and <c>archive</c> entries; OMITTED for <c>mcp-resource-template</c>.</description></item>
/// <item><description><c>digest</c> is part of the base schema but OMITTED under the SEP-2640 MCP binding; carried here for compatibility with non-MCP indices.</description></item>
/// </list>
/// All properties are nullable to keep deserialization lenient; callers validate required fields before use.
/// </remarks>
internal sealed class McpSkillIndexEntry
{
/// <summary>
/// Gets or sets the skill name (1-64 chars, lowercase alphanumeric and hyphens; no leading,
/// trailing, or consecutive hyphens). Required for <c>skill-md</c> and <c>archive</c> entries;
/// omitted for <c>mcp-resource-template</c>.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }
/// <summary>
/// Gets or sets the entry distribution type. Required. Schema-defined values are
/// <c>skill-md</c> and <c>archive</c>; the SEP-2640 MCP binding additionally defines
/// <c>mcp-resource-template</c>.
/// </summary>
[JsonPropertyName("type")]
public string? Type { get; set; }
/// <summary>
/// Gets or sets the skill description (max 1024 chars per the Agent Skills specification).
/// Required. For <c>skill-md</c> entries, SHOULD match the <c>description</c> in the skill's
/// <c>SKILL.md</c> frontmatter.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; }
/// <summary>
/// Gets or sets the artifact URL. Required. For <c>skill-md</c>, points at the
/// <c>SKILL.md</c> resource. For <c>archive</c>, points at the archive file. For
/// <c>mcp-resource-template</c>, an RFC 6570 URI template that resolves to a <c>SKILL.md</c>
/// resource URI.
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }
/// <summary>
/// Gets or sets the SHA-256 digest of the artifact bytes (e.g. <c>sha256:abcd1234...</c>).
/// Required by the base v0.2.0 schema, but OMITTED under the SEP-2640 MCP binding because
/// integrity is the transport's concern over an authenticated MCP connection.
/// </summary>
[JsonPropertyName("digest")]
public string? Digest { get; set; }
}
@@ -1,27 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal abstract class ActionTemplate : CodeTemplate, IModeledAction
{
public string Id { get; private set; } = string.Empty;
public string Name { get; private set; } = string.Empty;
public string ParentId { get; private set; } = string.Empty;
public bool UseAgentProvider { get; init; }
protected TAction Initialize<TAction>(TAction model) where TAction : DialogAction
{
this.Id = model.GetId();
this.ParentId = model.GetParentId() ?? WorkflowActionVisitor.Steps.Root();
this.Name = this.Id.FormatType();
return model;
}
}
@@ -1,395 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class AddConversationMessageTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Adds a new message to the specified agent conversation\n/// </s" +
"ummary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExe" +
"cutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true);
this.Write("\n if (string.IsNullOrWhiteSpace(conversationId))\n {\n thr" +
"ow new DeclarativeActionException($\"Conversation identifier must be defined: {th" +
"is.Id}\");\n }\n ChatMessage newMessage = new(ChatRole.");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatEnum(this.Model.Role, RoleMap)));
this.Write(", await this.GetContentAsync(context).ConfigureAwait(false)) { AdditionalProperti" +
"es = this.GetMetadata() };\n newMessage = await agentProvider.CreateMessag" +
"eAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);");
AssignVariable(this.Message, "newMessage");
this.Write("\n return default;\n }\n\n private async ValueTask<IList<AIContent>> Get" +
"ContentAsync(IWorkflowContext context)\n {\n List<AIContent> content = [" +
"];\n ");
int index = 0;
foreach (AddConversationMessageContent content in this.Model.Content)
{
++index;
EvaluateMessageTemplate(content.Value, $"contentValue{index}");
AgentMessageContentType contentType = content.Type.Value;
if (contentType == AgentMessageContentType.ImageUrl)
{
this.Write("\n content.Add(UriContent(contentValue");
this.Write(this.ToStringHelper.ToStringWithCulture(index));
this.Write(", \"image/*\"));");
}
else if (contentType == AgentMessageContentType.ImageFile)
{
this.Write("\n content.Add(new HostedFileContent(contentValue");
this.Write(this.ToStringHelper.ToStringWithCulture(index));
this.Write("));");
}
else
{
this.Write("\n content.Add(new TextContent(contentValue");
this.Write(this.ToStringHelper.ToStringWithCulture(index));
this.Write("));");
}
}
this.Write("\n return content;\n }\n\n private AdditionalPropertiesDictionary? GetMe" +
"tadata()\n {");
EvaluateRecordExpression<object>(this.Model.Metadata, "metadata");
this.Write("\n\n if (metadata is null)\n {\n return null; \n }\n" +
"\n return new AdditionalPropertiesDictionary(metadata);\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateRecordExpression<TValue>(ObjectExpression<RecordDataValue> expression, string targetVariable)
{
string resultTypeName = $"Dictionary<string, {GetTypeAlias<TValue>()}?>?";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" =\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "string?" : "string";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "string.Empty"));
this.Write(";");
}
else if (expression.IsLiteral)
{
if (expression.LiteralValue.Contains("\n"))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = \n \"\"\"\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write("\n \"\"\";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.LiteralValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<string>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateMessageTemplate(TemplateLine templateLine, string variableName)
{
if (templateLine is not null)
{
this.Write("\n string ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" =\n await context.FormatTemplateAsync(\n \"\"\"");
FormatMessageTemplate(templateLine);
this.Write("\n \"\"\");");
}
else
{
this.Write("\n string? ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" = null;");
}
}
void FormatMessageTemplate(TemplateLine line)
{
foreach (string text in line.ToTemplateString().ByLine())
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(text));
}
}
}
}
@@ -1,67 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateRecordExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateStringExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/FormatMessageTemplate.tt" once="true" #>
/// <summary>
/// Adds a new message to the specified agent conversation
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true); #>
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new DeclarativeActionException($"Conversation identifier must be defined: {this.Id}");
}
ChatMessage newMessage = new(ChatRole.<#= FormatEnum(this.Model.Role, RoleMap) #>, await this.GetContentAsync(context).ConfigureAwait(false)) { AdditionalProperties = this.GetMetadata() };
newMessage = await agentProvider.CreateMessageAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);<#
AssignVariable(this.Message, "newMessage");
#>
return default;
}
private async ValueTask<IList<AIContent>> GetContentAsync(IWorkflowContext context)
{
List<AIContent> content = [];
<#
int index = 0;
foreach (AddConversationMessageContent content in this.Model.Content)
{
++index;
EvaluateMessageTemplate(content.Value, $"contentValue{index}");
AgentMessageContentType contentType = content.Type.Value;
if (contentType == AgentMessageContentType.ImageUrl)
{#>
content.Add(UriContent(contentValue<#= index #>, "image/*"));<#
}
else if (contentType == AgentMessageContentType.ImageFile)
{#>
content.Add(new HostedFileContent(contentValue<#= index #>));<#
}
else
{#>
content.Add(new TextContent(contentValue<#= index #>));<#
}
}#>
return content;
}
private AdditionalPropertiesDictionary? GetMetadata()
{<#
EvaluateRecordExpression<object>(this.Model.Metadata, "metadata"); #>
if (metadata is null)
{
return null;
}
return new AdditionalPropertiesDictionary(metadata);
}
}
@@ -1,31 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.Agents.ObjectModel;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class AddConversationMessageTemplate
{
public AddConversationMessageTemplate(AddConversationMessage model)
{
this.Model = this.Initialize(model);
this.Message = this.Model.Message?.Path;
this.UseAgentProvider = true;
}
public AddConversationMessage Model { get; }
public PropertyPath? Message { get; }
public const string DefaultRole = nameof(ChatRole.User);
public static readonly FrozenDictionary<AgentMessageRoleWrapper, string> RoleMap =
new Dictionary<AgentMessageRoleWrapper, string>()
{
[AgentMessageRoleWrapper.Get(AgentMessageRole.User)] = nameof(ChatRole.User),
[AgentMessageRoleWrapper.Get(AgentMessageRole.Agent)] = nameof(ChatRole.Assistant),
}.ToFrozenDictionary();
}
@@ -1,221 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System.Collections.Generic;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ClearAllVariablesTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Reset all the state for the targeted variable scope.\n/// </sum" +
"mary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateEnumExpression<VariablesToClearWrapper, string>(this.Model.Variables, "targetScopeName", ScopeMap, isNullable: true);
this.Write("\n await context.QueueClearScopeAsync(targetScopeName).ConfigureAwait(false" +
");\n\n return default;\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateEnumExpression<TWrapper, TValue>(
EnumExpression<TWrapper> expression,
string targetVariable,
IDictionary<TWrapper, string> resultMap,
string defaultValue = null,
bool qualifyResult = false,
bool isNullable = false)
where TWrapper : EnumWrapper
{
string resultType = $"{GetTypeAlias<TValue>()}{(isNullable ? "?" : "")}";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatValue<TValue>(defaultValue)));
this.Write(";");
}
else if (expression.IsLiteral)
{
resultMap.TryGetValue(expression.LiteralValue, out string resultValue);
if (qualifyResult)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(".");
this.Write(this.ToStringHelper.ToStringWithCulture(resultValue));
this.Write(";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatValue<TValue>(resultValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,21 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateEnumExpressionTemplate.tt" once="true" #>
/// <summary>
/// Reset all the state for the targeted variable scope.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateEnumExpression<VariablesToClearWrapper, string>(this.Model.Variables, "targetScopeName", ScopeMap, isNullable: true); #>
await context.QueueClearScopeAsync(targetScopeName).ConfigureAwait(false);
return default;
}
}
@@ -1,25 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ClearAllVariablesTemplate
{
public ClearAllVariablesTemplate(ClearAllVariables model)
{
this.Model = this.Initialize(model);
}
public ClearAllVariables Model { get; }
public static readonly FrozenDictionary<VariablesToClearWrapper, string?> ScopeMap =
new Dictionary<VariablesToClearWrapper, string?>()
{
[VariablesToClearWrapper.Get(VariablesToClear.AllGlobalVariables)] = VariableScopeNames.Global,
[VariablesToClearWrapper.Get(VariablesToClear.ConversationScopedVariables)] = WorkflowFormulaState.DefaultScopeName,
}.ToFrozenDictionary();
}
@@ -1,339 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal abstract class CodeTemplate
{
private bool _endsWithNewline;
private string CurrentIndentField { get; set; } = string.Empty;
/// <summary>
/// Create the template output
/// </summary>
public abstract string TransformText();
#region Object Model helpers
public static string VariableName(PropertyPath path) => Throw.IfNull(path.VariableName);
public static string VariableScope(PropertyPath path) => Throw.IfNull(path.NamespaceAlias);
public static string FormatBoolValue(bool? value, bool defaultValue = false) =>
value ?? defaultValue ? "true" : "false";
public static string FormatStringValue(string? value)
{
if (value is null)
{
return "null";
}
if (value.Contains('\n') || value.Contains('\r'))
{
return @$"""""""{Environment.NewLine}{value}{Environment.NewLine}""""""";
}
if (value.Contains('"') || value.Contains('\\'))
{
return @$"""""""{value}""""""";
}
return @$"""{value}""";
}
public static string FormatValue<TValue>(string? value)
{
if (typeof(TValue) == typeof(string))
{
return FormatStringValue(value);
}
if (value is null)
{
return "null";
}
if (typeof(TValue).IsEnum)
{
return $"{typeof(TValue).Name}.{value}";
}
return $"{value}";
}
public static string FormatDataValue(DataValue value) =>
value switch
{
BlankDataValue => "null",
BooleanDataValue booleanValue => FormatBoolValue(booleanValue.Value),
FloatDataValue decimalValue => $"{decimalValue.Value}",
NumberDataValue numberValue => $"{numberValue.Value}",
DateDataValue dateValue => $"new DateTime({dateValue.Value.Ticks}, DateTimeKind.{dateValue.Value.Kind})",
DateTimeDataValue datetimeValue => $"new DateTimeOffset({datetimeValue.Value.Ticks}, TimeSpan.FromTicks({datetimeValue.Value.Offset}))",
TimeDataValue timeValue => $"TimeSpan.FromTicks({timeValue.Value.Ticks})",
StringDataValue stringValue => FormatStringValue(stringValue.Value),
OptionDataValue optionValue => @$"""{optionValue.Value}""",
// Indenting is important here to make the generated code readable. Don't change it without testing the output.
RecordDataValue recordValue =>
$"""
[
{string.Join(",\n ", recordValue.Properties.Select(p => $"[\"{p.Key}\"] = {FormatDataValue(p.Value)}"))}
]
""",
_ => throw new DeclarativeModelException($"Unable to format '{value.GetType().Name}'"),
};
public static TTarget FormatEnum<TSource, TTarget>(TSource value, IDictionary<TSource, TTarget> map, TTarget? defaultValue = default)
{
if (map.TryGetValue(value, out TTarget? target))
{
return target;
}
if (defaultValue is null)
{
throw new DeclarativeModelException($"No default value suppied for '{typeof(TTarget).Name}'");
}
return defaultValue;
}
public static string GetTypeAlias<TValue>() => GetTypeAlias(typeof(TValue));
public static string GetTypeAlias(Type type)
{
return type switch
{
Type t when t == typeof(bool) => "bool",
Type t when t == typeof(byte) => "byte",
Type t when t == typeof(sbyte) => "sbyte",
Type t when t == typeof(char) => "char",
Type t when t == typeof(decimal) => "decimal",
Type t when t == typeof(double) => "double",
Type t when t == typeof(float) => "float",
Type t when t == typeof(int) => "int",
Type t when t == typeof(uint) => "uint",
Type t when t == typeof(long) => "long",
Type t when t == typeof(ulong) => "ulong",
Type t when t == typeof(nint) => "nint",
Type t when t == typeof(nuint) => "nuint",
Type t when t == typeof(short) => "short",
Type t when t == typeof(ushort) => "ushort",
Type t when t == typeof(string) => "string",
Type t when t == typeof(object) => "object",
_ => type.Name
};
}
#endregion
#region Properties
/// <summary>
/// The string builder that generation-time code is using to assemble generated output
/// </summary>
public StringBuilder GenerationEnvironment
{
get
{
return field ??= new StringBuilder();
}
set;
}
/// <summary>
/// The error collection for the generation process
/// </summary>
public CompilerErrorCollection Errors => field ??= [];
/// <summary>
/// A list of the lengths of each indent that was added with PushIndent
/// </summary>
private List<int> IndentLengths { get => field ??= []; }
/// <summary>
/// Gets the current indent we use when adding lines to the output
/// </summary>
public string CurrentIndent
{
get
{
return this.CurrentIndentField;
}
}
/// <summary>
/// Current transformation session
/// </summary>
public virtual IDictionary<string, object>? Session { get; set; }
#endregion
#region Transform-time helpers
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void Write(string textToAppend)
{
if (string.IsNullOrEmpty(textToAppend))
{
return;
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
if ((this.GenerationEnvironment.Length == 0)
|| this._endsWithNewline)
{
this.GenerationEnvironment.Append(this.CurrentIndentField);
this._endsWithNewline = false;
}
// Check if the current text ends with a newline
if (textToAppend.EndsWith(Environment.NewLine, StringComparison.CurrentCulture))
{
this._endsWithNewline = true;
}
// This is an optimization. If the current indent is "", then we don't have to do any
// of the more complex stuff further down.
if (this.CurrentIndentField.Length == 0)
{
this.GenerationEnvironment.Append(textToAppend);
return;
}
// Everywhere there is a newline in the text, add an indent after it
textToAppend = textToAppend.Replace(Environment.NewLine, Environment.NewLine + this.CurrentIndentField);
// If the text ends with a newline, then we should strip off the indent added at the very end
// because the appropriate indent will be added when the next time Write() is called
if (this._endsWithNewline)
{
this.GenerationEnvironment.Append(textToAppend, 0, textToAppend.Length - this.CurrentIndentField.Length);
}
else
{
this.GenerationEnvironment.Append(textToAppend);
}
}
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void WriteLine(string textToAppend)
{
this.Write(textToAppend);
this.GenerationEnvironment.AppendLine();
this._endsWithNewline = true;
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void Write(string format, params object[] args)
{
this.Write(string.Format(CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void WriteLine(string format, params object[] args)
{
this.WriteLine(string.Format(CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Raise an error
/// </summary>
public void Error(string message)
{
CompilerError error = new()
{
ErrorText = message
};
this.Errors.Add(error);
}
/// <summary>
/// Raise a warning
/// </summary>
public void Warning(string message)
{
CompilerError error = new()
{
ErrorText = message,
IsWarning = true
};
error.ErrorText = message;
error.IsWarning = true;
this.Errors.Add(error);
}
/// <summary>
/// Increase the indent
/// </summary>
public void PushIndent(string indent)
{
if (indent is null)
{
throw new ArgumentNullException(nameof(indent));
}
this.CurrentIndentField += indent;
this.IndentLengths.Add(indent.Length);
}
/// <summary>
/// Remove the last indent that was added with PushIndent
/// </summary>
public string PopIndent()
{
string returnValue = string.Empty;
if (this.IndentLengths.Count > 0)
{
int indentLength = this.IndentLengths[this.IndentLengths.Count - 1];
this.IndentLengths.RemoveAt(this.IndentLengths.Count - 1);
if (indentLength > 0)
{
returnValue = this.CurrentIndentField.Substring(this.CurrentIndentField.Length - indentLength);
this.CurrentIndentField = this.CurrentIndentField.Remove(this.CurrentIndentField.Length - indentLength);
}
}
return returnValue;
}
/// <summary>
/// Remove any indentation
/// </summary>
public void ClearIndent()
{
this.IndentLengths.Clear();
this.CurrentIndentField = string.Empty;
}
#endregion
#region ToString Helpers
/// <summary>
/// Utility class to produce culture-oriented representation of an object as a string.
/// </summary>
public sealed class ToStringInstanceHelper
{
/// <summary>
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
/// </summary>
#pragma warning disable CA1822 // Required to be non-static for use in generated code
public string ToStringWithCulture(object objectToConvert) => $"{objectToConvert}";
#pragma warning restore CA1822
}
/// <summary>
/// Helper to produce culture-oriented representation of an object as a string
/// </summary>
public ToStringInstanceHelper ToStringHelper { get; } = new();
#endregion
}
@@ -1,172 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.ObjectModel;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ConditionGroupTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Conditional branching similar to an if / elseif / elseif / els" +
"e chain.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
for (int index = 0; index < this.Model.Conditions.Length; ++index)
{
ConditionItem conditionItem = this.Model.Conditions[index];
if (conditionItem.Condition is null)
{
continue; // Skip if no condition is defined
}
EvaluateBoolExpression(conditionItem.Condition, $"condition{index}");
this.Write("\n if (condition");
this.Write(this.ToStringHelper.ToStringWithCulture(index));
this.Write(")\n {\n return \"");
this.Write(this.ToStringHelper.ToStringWithCulture(ConditionGroupExecutor.Steps.Item(this.Model, conditionItem)));
this.Write("\";\n }\n ");
}
this.Write("\n return \"");
this.Write(this.ToStringHelper.ToStringWithCulture(ConditionGroupExecutor.Steps.Else(this.Model)));
this.Write("\";\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateBoolExpression(BoolExpression expression, string targetVariable, bool defaultValue = false)
{
if (expression is null)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatBoolValue(defaultValue)));
this.Write(";");
}
else if (expression.IsLiteral)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatBoolValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<bool>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<bool>>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<bool>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,34 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.ObjectModel" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateBoolExpressionTemplate.tt" once="true" #>
/// <summary>
/// Conditional branching similar to an if / elseif / elseif / else chain.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
for (int index = 0; index < this.Model.Conditions.Length; ++index)
{
ConditionItem conditionItem = this.Model.Conditions[index];
if (conditionItem.Condition is null)
{
continue; // Skip if no condition is defined
}
EvaluateBoolExpression(conditionItem.Condition, $"condition{index}");#>
if (condition<#= index #>)
{
return "<#= ConditionGroupExecutor.Steps.Item(this.Model, conditionItem)#>";
}
<#
}
#>
return "<#= ConditionGroupExecutor.Steps.Else(this.Model)#>";
}
}
@@ -1,15 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ConditionGroupTemplate
{
public ConditionGroupTemplate(ConditionGroup model)
{
this.Model = this.Initialize(model);
}
public ConditionGroup Model { get; }
}
@@ -1,324 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using Microsoft.Extensions.AI;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class CopyConversationMessagesTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Copies one or more messages into the specified agent conversat" +
"ion.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExe" +
"cutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true);
this.Write("\n if (string.IsNullOrWhiteSpace(conversationId))\n {\n thr" +
"ow new DeclarativeActionException($\"Conversation identifier must be defined: {th" +
"is.Id}\");\n }");
EvaluateValueExpression<ChatMessage[]>(this.Model.Messages, "messages");
this.Write(@"
if (messages is not null)
{
foreach (ChatMessage message in messages)
{
await agentProvider.CreateMessageAsync(conversationId, message, cancellationToken).ConfigureAwait(false);
}
}
return default;
}
}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "string?" : "string";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "string.Empty"));
this.Write(";");
}
else if (expression.IsLiteral)
{
if (expression.LiteralValue.Contains("\n"))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = \n \"\"\"\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write("\n \"\"\";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.LiteralValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<string>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateValueExpression(ValueExpression expression, string targetVariable) =>
EvaluateValueExpression<object>(expression, targetVariable);
void EvaluateValueExpression<TValue>(ValueExpression expression, string targetVariable)
{
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,33 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ import namespace="Microsoft.Extensions.AI" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateStringExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateValueExpressionTemplate.tt" once="true" #>
/// <summary>
/// Copies one or more messages into the specified agent conversation.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true); #>
if (string.IsNullOrWhiteSpace(conversationId))
{
throw new DeclarativeActionException($"Conversation identifier must be defined: {this.Id}");
}<#
EvaluateValueExpression<ChatMessage[]>(this.Model.Messages, "messages");
#>
if (messages is not null)
{
foreach (ChatMessage message in messages)
{
await agentProvider.CreateMessageAsync(conversationId, message, cancellationToken).ConfigureAwait(false);
}
}
return default;
}
}
@@ -1,16 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class CopyConversationMessagesTemplate
{
public CopyConversationMessagesTemplate(CopyConversationMessages model)
{
this.Model = this.Initialize(model);
this.UseAgentProvider = true;
}
public CopyConversationMessages Model { get; }
}
@@ -1,78 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class CreateConversationTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Creates a new conversation and stores the identifier value to " +
"the \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.ConversationId));
this.Write("\" variable.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExe" +
"cutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write(@""", session)
{
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);");
AssignVariable(this.ConversationId, "conversationId");
this.Write("\n await context.AddEventAsync(new ConversationUpdateEvent(conversationId))" +
".ConfigureAwait(false);\n\n return default;\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
}
}
@@ -1,19 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
/// <summary>
/// Creates a new conversation and stores the identifier value to the "<#= this.Model.ConversationId #>" variable.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
{
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);<#
AssignVariable(this.ConversationId, "conversationId");#>
await context.AddEventAsync(new ConversationUpdateEvent(conversationId)).ConfigureAwait(false);
return default;
}
}
@@ -1,20 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class CreateConversationTemplate
{
public CreateConversationTemplate(CreateConversation model)
{
this.Model = this.Initialize(model);
this.ConversationId = Throw.IfNull(this.Model.ConversationId);
this.UseAgentProvider = true;
}
public CreateConversation Model { get; }
public PropertyPath ConversationId { get; }
}
@@ -1,40 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class DefaultTemplate : ActionTemplate, IModeledAction
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\nDelegateExecutor ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
this.Write(" = new(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
this.Write(".Session");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Action is not null ? $", {this.Action}" : ""));
this.Write(");\n");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,4 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate, IModeledAction" visibility="internal" linePragmas="false" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
<#@ assembly name="System.Core" #>
DelegateExecutor <#= this.InstanceVariable #> = new(id: "<#= this.Id #>", <#= this.RootVariable #>.Session<#= this.Action is not null ? $", {this.Action}" : "" #>);
@@ -1,21 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class DefaultTemplate
{
public DefaultTemplate(DialogAction model, string rootId, string? action = null)
{
this.Initialize(model);
this.Action = action;
this.InstanceVariable = this.Id.FormatName();
this.RootVariable = rootId.FormatName();
}
public string? Action { get; }
public string InstanceVariable { get; }
public string RootVariable { get; }
}
@@ -1,51 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class EdgeTemplate : CodeTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
if (this.Condition is not null)
{
this.Write("\n builder.AddEdge(");
this.Write(this.ToStringHelper.ToStringWithCulture(this.SourceId));
this.Write(", ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.TargetId));
this.Write(", (object? result) => ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Condition));
this.Write(");");
}
else
{
this.Write("\n builder.AddEdge(");
this.Write(this.ToStringHelper.ToStringWithCulture(this.SourceId));
this.Write(", ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.TargetId));
this.Write(");");
}
this.Write("\n");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,10 +0,0 @@
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" linePragmas="false" #>
<#@ assembly name="System.Core" #>
<# if (this.Condition is not null)
{#>
builder.AddEdge(<#= this.SourceId #>, <#= this.TargetId #>, (object? result) => <#= this.Condition #>);<#
}
else
{#>
builder.AddEdge(<#= this.SourceId #>, <#= this.TargetId #>);<#
} #>
@@ -1,19 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class EdgeTemplate
{
public EdgeTemplate(string sourceId, string targetId, string? condition = null)
{
this.SourceId = sourceId.FormatName();
this.TargetId = targetId.FormatName();
this.Condition = condition;
}
public string SourceId { get; }
public string TargetId { get; }
public string? Condition { get; }
}
@@ -1,37 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class EditTableV2Template : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Modify items in a list\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,14 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
/// <summary>
/// Modify items in a list
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
return default;
}
}
@@ -1,15 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class EditTableV2Template
{
public EditTableV2Template(EditTableV2 model)
{
this.Model = this.Initialize(model);
}
public EditTableV2 Model { get; }
}
@@ -1,40 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class EmptyTemplate : CodeTemplate, IModeledAction
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\nDelegateExecutor ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
this.Write(" = new(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
this.Write(".Session");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Action is not null ? $", {this.Action}" : ""));
this.Write(");\n");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,4 +0,0 @@
<#@ template language="C#" inherits="CodeTemplate, IModeledAction" visibility="internal" linePragmas="false" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
<#@ assembly name="System.Core" #>
DelegateExecutor <#= this.InstanceVariable #> = new(id: "<#= this.Id #>", <#= this.RootVariable #>.Session<#= this.Action is not null ? $", {this.Action}" : "" #>);
@@ -1,23 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class EmptyTemplate
{
public EmptyTemplate(string actionId, string rootId, string? action = null)
{
this.Id = actionId;
this.Name = this.Id.FormatType();
this.InstanceVariable = this.Id.FormatName();
this.RootVariable = rootId.FormatName();
this.Action = action;
}
public string Id { get; }
public string Name { get; }
public string InstanceVariable { get; }
public string RootVariable { get; }
public string? Action { get; }
}
@@ -1,250 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ForeachTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Loops over a list assignign the loop variable to \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Value));
this.Write("\" variable.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write(@""", session)
{
private int _index;
private object[] _values = [];
public bool HasValue { get; private set; }
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
this._index = 0;");
EvaluateValueExpression(this.Model.Items, "evaluatedValue");
this.Write(@"
if (evaluatedValue == null)
{
this._values = [];
this.HasValue = false;
}
else
if (evaluatedValue is IEnumerable evaluatedList)
{
this._values = [.. evaluatedList];
}
else
{
this._values = [evaluatedValue];
}
await this.ResetAsync(context, cancellationToken).ConfigureAwait(false);
return default;
}
public async ValueTask TakeNextAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
{
if (this.HasValue = this._index < this._values.Length)
{
object value = this._values[this._index];
");
AssignVariable(this.Value, "value", tightFormat: true);
if (this.Index is not null)
{
AssignVariable(this.Index, "this._index", tightFormat: true);
}
this.Write(@"
this._index++;
}
}
public async ValueTask CompleteAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
{
await this.ResetAsync(context, cancellationToken).ConfigureAwait(false);
}
private async ValueTask ResetAsync(IWorkflowContext context, CancellationToken cancellationToken)
{");
AssignVariable(this.Value, "UnassignedValue.Instance", tightFormat: true);
if (this.Index is not null)
{
AssignVariable(this.Index, "UnassignedValue.Instance", tightFormat: true);
}
this.Write("\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateValueExpression(ValueExpression expression, string targetVariable) =>
EvaluateValueExpression<object>(expression, targetVariable);
void EvaluateValueExpression<TValue>(ValueExpression expression, string targetVariable)
{
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,77 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateValueExpressionTemplate.tt" once="true" #>
/// <summary>
/// Loops over a list assignign the loop variable to "<#= this.Model.Value #>" variable.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
private int _index;
private object[] _values = [];
public bool HasValue { get; private set; }
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
this._index = 0;<#
EvaluateValueExpression(this.Model.Items, "evaluatedValue");#>
if (evaluatedValue == null)
{
this._values = [];
this.HasValue = false;
}
else
if (evaluatedValue is IEnumerable evaluatedList)
{
this._values = [.. evaluatedList];
}
else
{
this._values = [evaluatedValue];
}
await this.ResetAsync(context, cancellationToken).ConfigureAwait(false);
return default;
}
public async ValueTask TakeNextAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
{
if (this.HasValue = this._index < this._values.Length)
{
object value = this._values[this._index];
<#
AssignVariable(this.Value, "value", tightFormat: true);
if (this.Index is not null)
{
AssignVariable(this.Index, "this._index", tightFormat: true);
}
#>
this._index++;
}
}
public async ValueTask CompleteAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
{
await this.ResetAsync(context, cancellationToken).ConfigureAwait(false);
}
private async ValueTask ResetAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
AssignVariable(this.Value, "UnassignedValue.Instance", tightFormat: true);
if (this.Index is not null)
{
AssignVariable(this.Index, "UnassignedValue.Instance", tightFormat: true);
}
#>
}
}
@@ -1,20 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ForeachTemplate
{
public ForeachTemplate(Foreach model)
{
this.Model = this.Initialize(model);
this.Index = this.Model.Index?.Path;
this.Value = Throw.IfNull(this.Model.Value);
}
public Foreach Model { get; }
public PropertyPath? Index { get; }
public PropertyPath Value { get; }
}
@@ -1,38 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class InstanceTemplate : CodeTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write(this.ToStringHelper.ToStringWithCulture(this.ExecutorType));
this.Write("Executor ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
this.Write(" = new(");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
this.Write(".Session");
this.Write(this.ToStringHelper.ToStringWithCulture(this.HasProvider ? ", options.AgentProvider" : ""));
this.Write(");");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,3 +0,0 @@
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" linePragmas="false" #>
<#@ assembly name="System.Core" #>
<#= this.ExecutorType #>Executor <#= this.InstanceVariable #> = new(<#= this.RootVariable #>.Session<#= this.HasProvider ? ", options.AgentProvider" : "" #>);
@@ -1,21 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class InstanceTemplate
{
public InstanceTemplate(string executorId, string rootId, bool hasProvider = false)
{
this.InstanceVariable = executorId.FormatName();
this.ExecutorType = executorId.FormatType();
this.RootVariable = rootId.FormatName();
this.HasProvider = hasProvider;
}
public string InstanceVariable { get; }
public string ExecutorType { get; }
public string RootVariable { get; }
public bool HasProvider { get; }
}
@@ -1,415 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System.Collections.Generic;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
using Microsoft.Extensions.AI;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class InvokeAzureAgentTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Invokes an agent to process messages and return a response wit" +
"hin a conversation context.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : AgentExec" +
"utor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session, agentProvider)\n{\n // <inheritdoc />\n protected override async V" +
"alueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cance" +
"llationToken)\n {");
EvaluateStringExpression(this.Model.Agent.Name, "agentName", isNullable: true);
this.Write("\n\n if (string.IsNullOrWhiteSpace(agentName))\n {\n throw n" +
"ew DeclarativeActionException($\"Agent name must be defined: {this.Id}\");\n " +
" }\n ");
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true);
EvaluateBoolExpression(this.Model.Output?.AutoSend, "autoSend", defaultValue: true);
EvaluateListExpression<ChatMessage>(this.Model.Input?.Messages, "inputMessages");
this.Write(@"
AgentResponse agentResponse =
await InvokeAgentAsync(
context,
agentName,
conversationId,
autoSend,
inputMessages,
cancellationToken).ConfigureAwait(false);
if (autoSend)
{
await context.AddEventAsync(new AgentResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
}
");
AssignVariable(this.Messages, "agentResponse.Messages");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateBoolExpression(BoolExpression expression, string targetVariable, bool defaultValue = false)
{
if (expression is null)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatBoolValue(defaultValue)));
this.Write(";");
}
else if (expression.IsLiteral)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatBoolValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<bool>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<bool>>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n bool ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<bool>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateListExpression<TElement>(ValueExpression expression, string targetVariable)
{
string typeName = GetTypeAlias<TElement>();
if (expression is null)
{
this.Write("\n IList<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n IList<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n IList<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadListAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TElement>()));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n IList<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write("> = await context.EvaluateListAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n IList<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateListAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "string?" : "string";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "string.Empty"));
this.Write(";");
}
else if (expression.IsLiteral)
{
if (expression.LiteralValue.Contains("\n"))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = \n \"\"\"\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write("\n \"\"\";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.LiteralValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<string>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,48 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ import namespace="Microsoft.Extensions.AI" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateBoolExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateListExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateStringExpressionTemplate.tt" once="true" #>
/// <summary>
/// Invokes an agent to process messages and return a response within a conversation context.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : AgentExecutor(id: "<#= this.Id #>", session, agentProvider)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateStringExpression(this.Model.Agent.Name, "agentName", isNullable: true);#>
if (string.IsNullOrWhiteSpace(agentName))
{
throw new DeclarativeActionException($"Agent name must be defined: {this.Id}");
}
<#
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true);
EvaluateBoolExpression(this.Model.Output?.AutoSend, "autoSend", defaultValue: true);
EvaluateListExpression<ChatMessage>(this.Model.Input?.Messages, "inputMessages");#>
AgentResponse agentResponse =
await InvokeAgentAsync(
context,
agentName,
conversationId,
autoSend,
inputMessages,
cancellationToken).ConfigureAwait(false);
if (autoSend)
{
await context.AddEventAsync(new AgentResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
}
<#
AssignVariable(this.Messages, "agentResponse.Messages"); #>
return default;
}
}
@@ -1,19 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class InvokeAzureAgentTemplate
{
public InvokeAzureAgentTemplate(InvokeAzureAgent model)
{
this.Model = this.Initialize(model);
this.Messages = this.Model.Output?.Messages?.Path;
this.UseAgentProvider = true;
}
public InvokeAzureAgent Model { get; }
public PropertyPath? Messages { get; }
}
@@ -1,99 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ParseValueTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Parses a string or untyped value to the provided data type. Wh" +
"en the input is a string, it will be treated as JSON.\n/// </summary>\ninternal se" +
"aled class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" { \n VariableType targetType = ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.GetVariableType()));
this.Write(";");
if (this.Model.Value.IsVariableReference && this.Model.Value.VariableReference.SegmentCount == 2)
{
this.Write("\n object? parsedValue = await context.ConvertValueAsync(targetType, key: \"" +
"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Value.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Value.VariableReference.NamespaceAlias));
this.Write("\", cancellationToken).ConfigureAwait(false);");
}
else if (this.Model.Value.IsVariableReference)
{
this.Write("\n object? parsedValue = await context.ConvertValueAsync(targetType, ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(this.Model.Value.VariableReference.ToString())));
this.Write(", cancellationToken).ConfigureAwait(false);");
}
else
{
this.Write("\n object? parsedValue = await context.ConvertValueAsync(targetType, ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(this.Model.Value.ExpressionText)));
this.Write(", cancellationToken).ConfigureAwait(false);");
}
AssignVariable(this.Variable, "parsedValue");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
}
}
@@ -1,30 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
/// <summary>
/// Parses a string or untyped value to the provided data type. When the input is a string, it will be treated as JSON.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
VariableType targetType = <#= this.GetVariableType() #>;<#
if (this.Model.Value.IsVariableReference && this.Model.Value.VariableReference.SegmentCount == 2)
{#>
object? parsedValue = await context.ConvertValueAsync(targetType, key: "<#= this.Model.Value.VariableReference.VariableName #>", scopeName: "<#= this.Model.Value.VariableReference.NamespaceAlias #>", cancellationToken).ConfigureAwait(false);<#
}
else if (this.Model.Value.IsVariableReference)
{#>
object? parsedValue = await context.ConvertValueAsync(targetType, <#= FormatStringValue(this.Model.Value.VariableReference.ToString()) #>, cancellationToken).ConfigureAwait(false);<#
}
else
{#>
object? parsedValue = await context.ConvertValueAsync(targetType, <#= FormatStringValue(this.Model.Value.ExpressionText) #>, cancellationToken).ConfigureAwait(false);<#
}
AssignVariable(this.Variable, "parsedValue"); #>
return default;
}
}
@@ -1,40 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Linq;
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ParseValueTemplate
{
public ParseValueTemplate(ParseValue model)
{
this.Model = this.Initialize(model);
this.Variable = Throw.IfNull(this.Model.Variable);
}
public ParseValue Model { get; }
public PropertyPath Variable { get; }
private string GetVariableType()
{
return GetVariableType(this.Model.ValueType);
static string GetVariableType(DataType? dataType) =>
dataType switch
{
null => "null",
StringDataType => "typeof(string)",
BooleanDataType => "typeof(bool)",
FloatDataType => "typeof(double)",
NumberDataType => "typeof(decimal)",
DateTimeDataType => "typeof(DateTime)",
DateDataType => "typeof(DateTime)",
TimeDataType => "typeof(TimeSpan)",
RecordDataType recordType => $"\nVariableType.Record(\n{string.Join(",\n ", recordType.Properties.Select(property => @$"( ""{property.Key}"", {GetVariableType(property.Value.Type)} )"))})",
TableDataType tableType => $"\nVariableType.Record(\n{string.Join(",\n ", tableType.Properties.Select(property => @$"( ""{property.Key}"", {GetVariableType(property.Value.Type)} )"))})",
_ => throw new DeclarativeModelException($"Unsupported data type: {dataType}"),
};
}
}
@@ -1,118 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ProviderTemplate : CodeTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write(@"
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// </auto-generated>
// ------------------------------------------------------------------------------
#nullable enable
#pragma warning disable IDE0005 // Extra using directive is ok.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Agents.AI.Workflows.Declarative;
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
using Microsoft.Extensions.AI;
");
if (this.Namespace is not null)
{
this.Write("\nnamespace ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Namespace));
this.Write(";\n");
}
this.Write(@"
/// <summary>
/// This class provides a factory method to create a <see cref=""Workflow"" /> instance.
/// </summary>
/// <remarks>
/// The workflow defined here was generated from a declarative workflow definition.
/// Declarative workflows utilize Power FX for defining conditions and expressions.
/// To learn more about Power FX, see:
/// https://learn.microsoft.com/power-platform/power-fx/formula-reference-copilot-studio
/// </remarks>
public static class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Prefix ?? string.Empty));
this.Write("WorkflowProvider\n{");
foreach (string executor in ByLine(this.Executors, formatGroup: true))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(executor));
}
this.Write(@"
public static Workflow CreateWorkflow<TInput>(
DeclarativeWorkflowOptions options,
Func<TInput, ChatMessage>? inputTransform = null)
where TInput : notnull
{
// Create root executor to initialize the workflow.
inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message);
");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootExecutorType));
this.Write("Executor<TInput> ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootInstance));
this.Write(" = new(options, inputTransform);");
// Create executor instances
foreach (string instance in ByLine(this.Instances))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(instance));
}
this.Write("\n\n // Define the workflow builder\n WorkflowBuilder builder = new(");
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootInstance));
this.Write(");\n\n // Connect executors");
foreach (string edge in ByLine(this.Edges))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(edge));
}
this.Write("\n\n // Build the workflow\n return builder.Build(validateOrphans: fal" +
"se);\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,75 +0,0 @@
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" linePragmas="false" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ assembly name="System.Core" #>
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// </auto-generated>
// ------------------------------------------------------------------------------
#nullable enable
#pragma warning disable IDE0005 // Extra using directive is ok.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Agents.AI.Workflows.Declarative;
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
using Microsoft.Extensions.AI;
<#
if (this.Namespace is not null)
{#>
namespace <#= this.Namespace #>;
<#
}
#>
/// <summary>
/// This class provides a factory method to create a <see cref="Workflow" /> instance.
/// </summary>
/// <remarks>
/// The workflow defined here was generated from a declarative workflow definition.
/// Declarative workflows utilize Power FX for defining conditions and expressions.
/// To learn more about Power FX, see:
/// https://learn.microsoft.com/power-platform/power-fx/formula-reference-copilot-studio
/// </remarks>
public static class <#= this.Prefix ?? string.Empty #>WorkflowProvider
{<#
foreach (string executor in ByLine(this.Executors, formatGroup: true))
{ #>
<#= executor #><#
}
#>
public static Workflow CreateWorkflow<TInput>(
DeclarativeWorkflowOptions options,
Func<TInput, ChatMessage>? inputTransform = null)
where TInput : notnull
{
// Create root executor to initialize the workflow.
inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message);
<#= this.RootExecutorType #>Executor<TInput> <#= this.RootInstance #> = new(options, inputTransform);<#
// Create executor instances
foreach (string instance in ByLine(this.Instances))
{ #>
<#= instance #><#
}#>
// Define the workflow builder
WorkflowBuilder builder = new(<#= this.RootInstance #>);
// Connect executors<#
foreach (string edge in ByLine(this.Edges))
{ #>
<#= edge #><#
}
#>
// Build the workflow
return builder.Build(validateOrphans: false);
}
}
@@ -1,48 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ProviderTemplate
{
public ProviderTemplate(
string workflowId,
IEnumerable<string> executors,
IEnumerable<string> instances,
IEnumerable<string> edges)
{
this.Executors = executors;
this.Instances = instances;
this.Edges = edges;
this.RootInstance = workflowId.FormatName();
this.RootExecutorType = workflowId.FormatType();
}
public string? Namespace { get; init; }
public string? Prefix { get; init; }
public string RootInstance { get; }
public string RootExecutorType { get; }
public IEnumerable<string> Executors { get; }
public IEnumerable<string> Instances { get; }
public IEnumerable<string> Edges { get; }
public static IEnumerable<string> ByLine(IEnumerable<string> templates, bool formatGroup = false)
{
foreach (string template in templates)
{
foreach (string line in template.ByLine())
{
yield return line;
}
if (formatGroup)
{
yield return string.Empty;
}
}
}
}
@@ -1,37 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class QuestionTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Request input.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,14 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
/// <summary>
/// Request input.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{
return default;
}
}
@@ -1,15 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class QuestionTemplate
{
public QuestionTemplate(Question model)
{
this.Model = this.Initialize(model);
}
public Question Model { get; }
}
@@ -1,74 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class ResetVariableTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Resets the value of the \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Variable));
this.Write("\" variable, potentially causing re-evaluation \n/// of the default value, question" +
" or action that provides the value to this variable.\n/// </summary>\ninternal sea" +
"led class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n protected override async ValueTask<object?> ExecuteAsync(IWorkf" +
"lowContext context, CancellationToken cancellationToken)\n {");
AssignVariable(this.Variable, "UnassignedValue.Instance");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
}
}
@@ -1,17 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
/// <summary>
/// Resets the value of the "<#= this.Model.Variable #>" variable, potentially causing re-evaluation
/// of the default value, question or action that provides the value to this variable.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
AssignVariable(this.Variable, "UnassignedValue.Instance"); #>
return default;
}
}
@@ -1,19 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class ResetVariableTemplate
{
public ResetVariableTemplate(ResetVariable model)
{
this.Model = this.Initialize(model);
this.Variable = Throw.IfNull(this.Model.Variable);
}
public ResetVariable Model { get; }
public PropertyPath Variable { get; }
}
@@ -1,310 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class RetrieveConversationMessageTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Retrieves a list of messages from an agent conversation.\n/// <" +
"/summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExe" +
"cutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
EvaluateStringExpression(this.Model.MessageId, "messageId");
this.Write("\n ChatMessage message = await agentProvider.GetMessageAsync(conversationId" +
", messageId, cancellationToken).ConfigureAwait(false);");
AssignVariable(this.Model.Message, "message");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateRecordExpression<TValue>(ObjectExpression<RecordDataValue> expression, string targetVariable)
{
string resultTypeName = $"Dictionary<string, {GetTypeAlias<TValue>()}?>?";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" =\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "string?" : "string";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "string.Empty"));
this.Write(";");
}
else if (expression.IsLiteral)
{
if (expression.LiteralValue.Contains("\n"))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = \n \"\"\"\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write("\n \"\"\";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.LiteralValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<string>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,23 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateRecordExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateStringExpressionTemplate.tt" once="true" #>
/// <summary>
/// Retrieves a list of messages from an agent conversation.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
EvaluateStringExpression(this.Model.MessageId, "messageId"); #>
ChatMessage message = await agentProvider.GetMessageAsync(conversationId, messageId, cancellationToken).ConfigureAwait(false);<#
AssignVariable(this.Model.Message, "message");
#>
return default;
}
}
@@ -1,16 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class RetrieveConversationMessageTemplate
{
public RetrieveConversationMessageTemplate(RetrieveConversationMessage model)
{
this.Model = this.Initialize(model);
this.UseAgentProvider = true;
}
public RetrieveConversationMessage Model { get; }
}
@@ -1,584 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using System.Collections.Generic;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class RetrieveConversationMessagesTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Retrieves a specific message from an agent conversation.\n/// <" +
"/summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExe" +
"cutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
EvaluateIntExpression(this.Model.Limit, "limit");
EvaluateStringExpression(this.Model.MessageAfter, "after", isNullable: true);
EvaluateStringExpression(this.Model.MessageBefore, "before", isNullable: true);
EvaluateEnumExpression<AgentMessageSortOrderWrapper, bool>(this.Model.SortOrder, "newestFirst", SortMap, defaultValue: DefaultSort);
this.Write(@"
IAsyncEnumerable<ChatMessage> messagesResult =
agentProvider.GetMessagesAsync(
conversationId,
limit,
after,
before,
newestFirst,
cancellationToken);
List<ChatMessage> messages = [];
await foreach (ChatMessage message in messagesResult.ConfigureAwait(false))
{
messages.Add(message);
}");
AssignVariable(this.Model.Messages, "messages");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateEnumExpression<TWrapper, TValue>(
EnumExpression<TWrapper> expression,
string targetVariable,
IDictionary<TWrapper, string> resultMap,
string defaultValue = null,
bool qualifyResult = false,
bool isNullable = false)
where TWrapper : EnumWrapper
{
string resultType = $"{GetTypeAlias<TValue>()}{(isNullable ? "?" : "")}";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatValue<TValue>(defaultValue)));
this.Write(";");
}
else if (expression.IsLiteral)
{
resultMap.TryGetValue(expression.LiteralValue, out string resultValue);
if (qualifyResult)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(".");
this.Write(this.ToStringHelper.ToStringWithCulture(resultValue));
this.Write(";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatValue<TValue>(resultValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultType));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateIntExpression(IntExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "int?" : "int";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "0"));
this.Write(";");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<int>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateRecordExpression<TValue>(ObjectExpression<RecordDataValue> expression, string targetVariable)
{
string resultTypeName = $"Dictionary<string, {GetTypeAlias<TValue>()}?>?";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" =\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateExpressionAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(resultTypeName));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
{
string typeName = isNullable ? "string?" : "string";
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(isNullable ? "null" : "string.Empty"));
this.Write(";");
}
else if (expression.IsLiteral)
{
if (expression.LiteralValue.Contains("\n"))
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = \n \"\"\"\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.LiteralValue));
this.Write("\n \"\"\";");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.LiteralValue)));
this.Write(";");
}
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<string>(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(typeName));
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<string>(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,42 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateEnumExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateIntExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateRecordExpressionTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateStringExpressionTemplate.tt" once="true" #>
/// <summary>
/// Retrieves a specific message from an agent conversation.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session, ResponseAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
EvaluateIntExpression(this.Model.Limit, "limit");
EvaluateStringExpression(this.Model.MessageAfter, "after", isNullable: true);
EvaluateStringExpression(this.Model.MessageBefore, "before", isNullable: true);
EvaluateEnumExpression<AgentMessageSortOrderWrapper, bool>(this.Model.SortOrder, "newestFirst", SortMap, defaultValue: DefaultSort); #>
IAsyncEnumerable<ChatMessage> messagesResult =
agentProvider.GetMessagesAsync(
conversationId,
limit,
after,
before,
newestFirst,
cancellationToken);
List<ChatMessage> messages = [];
await foreach (ChatMessage message in messagesResult.ConfigureAwait(false))
{
messages.Add(message);
}<#
AssignVariable(this.Model.Messages, "messages");
#>
return default;
}
}
@@ -1,27 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class RetrieveConversationMessagesTemplate
{
public RetrieveConversationMessagesTemplate(RetrieveConversationMessages model)
{
this.Model = this.Initialize(model);
this.UseAgentProvider = true;
}
public RetrieveConversationMessages Model { get; }
public const string DefaultSort = "false";
public static readonly FrozenDictionary<AgentMessageSortOrderWrapper, string> SortMap =
new Dictionary<AgentMessageSortOrderWrapper, string>()
{
[AgentMessageSortOrderWrapper.Get(AgentMessageSortOrder.NewestFirst)] = "true",
[AgentMessageSortOrderWrapper.Get(AgentMessageSortOrder.OldestFirst)] = "false",
}.ToFrozenDictionary();
}
@@ -1,79 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class RootTemplate : CodeTemplate, IModeledAction
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// The root executor for a declarative workflow.\n/// </summary>\ni" +
"nternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName));
this.Write("Executor<TInput>(\n DeclarativeWorkflowOptions options,\n Func<TInput, ChatMe" +
"ssage> inputTransform) :\n RootExecutor<TInput>(\"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", options, inputTransform)\n where TInput : notnull\n{\n protected override a" +
"sync ValueTask ExecuteAsync(TInput message, IWorkflowContext context, Cancellati" +
"onToken cancellationToken)\n {");
if (this.TypeInfo.EnvironmentVariables.Count > 0)
{
this.Write("\n // Set environment variables\n await this.InitializeEnvironmentAsy" +
"nc(\n context,");
int index = this.TypeInfo.EnvironmentVariables.Count - 1;
foreach (string variableName in this.TypeInfo.EnvironmentVariables)
{
this.Write("\n \"");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write("\"");
this.Write(this.ToStringHelper.ToStringWithCulture(index > 0 ? "," : ""));
--index;
}
this.Write(").ConfigureAwait(false);\n");
}
if (this.TypeInfo.UserVariables.Count > 0)
{
this.Write("\n // Initialize variables");
foreach (VariableInformationDiagnostic variableInfo in this.TypeInfo.UserVariables)
{
this.Write("\n await context.QueueStateUpdateAsync(\"");
this.Write(this.ToStringHelper.ToStringWithCulture(variableInfo.Path.VariableName));
this.Write("\", UnassignedValue.Instance, \"");
this.Write(this.ToStringHelper.ToStringWithCulture(variableInfo.Path.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
}
this.Write("\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
}
}
@@ -1,40 +0,0 @@
<#@ template language="C#" inherits="CodeTemplate, IModeledAction" visibility="internal" linePragmas="false" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ assembly name="System.Core" #>
/// <summary>
/// The root executor for a declarative workflow.
/// </summary>
internal sealed class <#= this.TypeName #>Executor<TInput>(
DeclarativeWorkflowOptions options,
Func<TInput, ChatMessage> inputTransform) :
RootExecutor<TInput>("<#= this.Id #>", options, inputTransform)
where TInput : notnull
{
protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken)
{<#
if (this.TypeInfo.EnvironmentVariables.Count > 0)
{ #>
// Set environment variables
await this.InitializeEnvironmentAsync(
context,<#
int index = this.TypeInfo.EnvironmentVariables.Count - 1;
foreach (string variableName in this.TypeInfo.EnvironmentVariables)
{#>
"<#= variableName #>"<#= index > 0 ? "," : "" #><#
--index;
}#>).ConfigureAwait(false);
<#}
if (this.TypeInfo.UserVariables.Count > 0)
{
#>
// Initialize variables<#
foreach (VariableInformationDiagnostic variableInfo in this.TypeInfo.UserVariables)
{#>
await context.QueueStateUpdateAsync("<#= variableInfo.Path.VariableName #>", UnassignedValue.Instance, "<#= variableInfo.Path.NamespaceAlias #>").ConfigureAwait(false);<#
}
}#>
}
}
@@ -1,22 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class RootTemplate
{
internal RootTemplate(
string workflowId,
WorkflowTypeInfo typeInfo)
{
this.Id = workflowId;
this.TypeInfo = typeInfo;
this.TypeName = workflowId.FormatType();
}
public string Id { get; }
public WorkflowTypeInfo TypeInfo { get; }
public string TypeName { get; }
}
@@ -1,109 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class SendActivityTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Formats a message template and sends an activity event.\n/// </" +
"summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" { ");
if (this.Model.Activity is MessageActivityTemplate messageActivity)
{
this.Write("\n string activityText = \n await context.FormatTemplateAsync( ");
foreach (TemplateLine line in messageActivity.Text)
{
this.Write("\n \"\"\"");
foreach (string text in line.ToTemplateString().ByLine())
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(text));
}
this.Write("\n \"\"\"");
}
this.Write("\n );\n AgentResponse response = new([new ChatMessage(ChatRole.As" +
"sistant, activityText)]);\n await context.AddEventAsync(new AgentResponseE" +
"vent(this.Id, response)).ConfigureAwait(false);");
}
this.Write("\n\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void EvaluateMessageTemplate(TemplateLine templateLine, string variableName)
{
if (templateLine is not null)
{
this.Write("\n string ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" =\n await context.FormatTemplateAsync(\n \"\"\"");
FormatMessageTemplate(templateLine);
this.Write("\n \"\"\");");
}
else
{
this.Write("\n string? ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" = null;");
}
}
void FormatMessageTemplate(TemplateLine line)
{
foreach (string text in line.ToTemplateString().ByLine())
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(text));
}
}
}
}
@@ -1,36 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/FormatMessageTemplate.tt" once="true" #>
/// <summary>
/// Formats a message template and sends an activity event.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{ <#
if (this.Model.Activity is MessageActivityTemplate messageActivity)
{ #>
string activityText =
await context.FormatTemplateAsync( <#
foreach (TemplateLine line in messageActivity.Text)
{ #>
"""<#
foreach (string text in line.ToTemplateString().ByLine())
{ #>
<#= text #><#
} #>
"""<#
}
#>
);
AgentResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
await context.AddEventAsync(new AgentResponseEvent(this.Id, response)).ConfigureAwait(false);<#
} #>
return default;
}
}
@@ -1,15 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class SendActivityTemplate
{
public SendActivityTemplate(SendActivity model)
{
this.Model = this.Initialize(model);
}
public SendActivity Model { get; }
}
@@ -1,195 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class SetMultipleVariablesTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Assigns an evaluated expression, other variable, or literal va" +
"lue to one or more variables.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
int index = 0;
foreach (var assignment in this.Model.Assignments)
{
// Separate assigments with a blank line for readability
if (index > 0)
{
this.Write("\n ");
}
++index;
EvaluateValueExpression(assignment.Value, $"evaluatedValue{index}");
AssignVariable(assignment.Variable, $"evaluatedValue{index}");
}
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateValueExpression(ValueExpression expression, string targetVariable) =>
EvaluateValueExpression<object>(expression, targetVariable);
void EvaluateValueExpression<TValue>(ValueExpression expression, string targetVariable)
{
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,29 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateValueExpressionTemplate.tt" once="true" #>
/// <summary>
/// Assigns an evaluated expression, other variable, or literal value to one or more variables.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<# int index = 0;
foreach (var assignment in this.Model.Assignments)
{
// Separate assigments with a blank line for readability
if (index > 0)
{#>
<#
}
++index;
EvaluateValueExpression(assignment.Value, $"evaluatedValue{index}");
AssignVariable(assignment.Variable, $"evaluatedValue{index}");
}
#>
return default;
}
}
@@ -1,15 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class SetMultipleVariablesTemplate
{
public SetMultipleVariablesTemplate(SetMultipleVariables model)
{
this.Model = this.Initialize(model);
}
public SetMultipleVariables Model { get; }
}
@@ -1,117 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class SetTextVariableTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Assigns an evaluated message template to the \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Variable));
this.Write("\" variable.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n protected override async ValueTask<object?> ExecuteAsync(IWorkf" +
"lowContext context, CancellationToken cancellationToken)\n {");
EvaluateMessageTemplate(this.Model.Value, "textValue");
AssignVariable(this.Variable, "textValue");
this.Write("\n return default;\n }\n}");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateMessageTemplate(TemplateLine templateLine, string variableName)
{
if (templateLine is not null)
{
this.Write("\n string ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" =\n await context.FormatTemplateAsync(\n \"\"\"");
FormatMessageTemplate(templateLine);
this.Write("\n \"\"\");");
}
else
{
this.Write("\n string? ");
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
this.Write(" = null;");
}
}
void FormatMessageTemplate(TemplateLine line)
{
foreach (string text in line.ToTemplateString().ByLine())
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(text));
}
}
}
}
@@ -1,19 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/FormatMessageTemplate.tt" once="true" #>
/// <summary>
/// Assigns an evaluated message template to the "<#= this.Model.Variable #>" variable.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateMessageTemplate(this.Model.Value, "textValue");
AssignVariable(this.Variable, "textValue"); #>
return default;
}
}
@@ -1,19 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.ObjectModel;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
internal partial class SetTextVariableTemplate
{
public SetTextVariableTemplate(SetTextVariable model)
{
this.Model = this.Initialize(model);
this.Variable = Throw.IfNull(this.Model.Variable);
}
public SetTextVariable Model { get; }
public PropertyPath Variable { get; }
}
@@ -1,186 +0,0 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 18.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
{
using Microsoft.Agents.ObjectModel;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
internal partial class SetVariableTemplate : ActionTemplate
{
/// <summary>
/// Create the template output
/// </summary>
public override string TransformText()
{
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n");
this.Write("\n/// <summary>\n/// Assigns an evaluated expression, other variable, or literal va" +
"lue to the \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Model.Variable));
this.Write("\" variable.\n/// </summary>\ninternal sealed class ");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Name));
this.Write("Executor(FormulaSession session) : ActionExecutor(id: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
this.Write("\", session)\n{\n // <inheritdoc />\n protected override async ValueTask<object" +
"?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)\n " +
" {");
EvaluateValueExpression(this.Model.Value, "evaluatedValue");
AssignVariable(this.Variable, "evaluatedValue");
this.Write("\n return default;\n }\n}\n");
return this.GenerationEnvironment.ToString();
}
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
{
if (targetVariable is not null)
{
this.Write("\n await context.QueueStateUpdateAsync(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableName(targetVariable)));
this.Write("\", value: ");
this.Write(this.ToStringHelper.ToStringWithCulture(valueVariable));
this.Write(", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(VariableScope(targetVariable)));
this.Write("\").ConfigureAwait(false);");
if (!tightFormat)
{
this.Write("\n ");
}
}
}
void EvaluateValueExpression(ValueExpression expression, string targetVariable) =>
EvaluateValueExpression<object>(expression, targetVariable);
void EvaluateValueExpression<TValue>(ValueExpression expression, string targetVariable)
{
if (expression is null)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = null;");
}
else if (expression.IsLiteral)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatDataValue(expression.LiteralValue)));
this.Write(";");
}
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.ReadStateAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(key: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.VariableName));
this.Write("\", scopeName: \"");
this.Write(this.ToStringHelper.ToStringWithCulture(expression.VariableReference.NamespaceAlias));
this.Write("\").ConfigureAwait(false);");
}
else if (expression.IsVariableReference)
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.VariableReference.ToString())));
this.Write(").ConfigureAwait(false);");
}
else
{
this.Write("\n ");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write("? ");
this.Write(this.ToStringHelper.ToStringWithCulture(targetVariable));
this.Write(" = await context.EvaluateValueAsync<");
this.Write(this.ToStringHelper.ToStringWithCulture(GetTypeAlias<TValue>()));
this.Write(">(");
this.Write(this.ToStringHelper.ToStringWithCulture(FormatStringValue(expression.ExpressionText)));
this.Write(").ConfigureAwait(false);");
}
}
}
}
@@ -1,19 +0,0 @@
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="Microsoft.Agents.ObjectModel" #>
<#@ include file="Snippets/AssignVariableTemplate.tt" once="true" #>
<#@ include file="Snippets/EvaluateValueExpressionTemplate.tt" once="true" #>
/// <summary>
/// Assigns an evaluated expression, other variable, or literal value to the "<#= this.Model.Variable #>" variable.
/// </summary>
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
{
// <inheritdoc />
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
{<#
EvaluateValueExpression(this.Model.Value, "evaluatedValue");
AssignVariable(this.Variable, "evaluatedValue"); #>
return default;
}
}

Some files were not shown because too many files have changed in this diff Show More