Skip to content

Add WorkflowCore.AI: ChatClientStep for Microsoft.Extensions.AI IChatClient - #1453

Open
danielgerlag wants to merge 1 commit into
masterfrom
feature/chat-client-step
Open

danielgerlag wants to merge 1 commit into
masterfrom
feature/chat-client-step

Conversation

@danielgerlag

Copy link
Copy Markdown
Owner

What and why

Adds a new package, WorkflowCore.AI, with one step body, ChatClientStep. It calls a model through IChatClient from Microsoft.Extensions.AI, resolved from DI, so any provider with an IChatClient (OpenAI, Azure OpenAI, Ollama and others) can be used from a workflow. Temporal and Microsoft Agent Framework already offer this pattern for .NET; Workflow Core only had the Azure Foundry specific steps.

services.AddChatClient(myChatClient);
services.AddWorkflowChatClientStep();

builder
    .StartWith<ChatClientStep>()
        .Input(step => step.SystemPrompt, data => "Summarize in one sentence.")
        .Input(step => step.Prompt, data => data.Ticket)
        .Output(data => data.Summary, step => step.ResponseText);

Design choices

  • Inputs: Prompt, Messages (IList<ChatMessage>), SystemPrompt, Options (ChatOptions). The request is sent as system prompt, then messages, then prompt as a user message. At least one of Prompt or Messages is required, otherwise the step throws before calling the model.
  • Outputs: ResponseText and the full ChatResponse as Response.
  • Separate project in src/extensions/WorkflowCore.AI, netstandard2.0 like the other extensions, so WorkflowCore gets no new dependency. It depends only on Microsoft.Extensions.AI.Abstractions 10.10.1.
  • No custom fluent builder. The standard .Input / .Output mapping already covers it, so AddWorkflowChatClientStep() (registers the step in DI) is the only helper.
  • README in the project folder, linked from the main README.

How it was tested

New test/WorkflowCore.AI.Tests (net6.0, net8.0, net10.0) with a fake IChatClient, no network or keys:

  • message order and reply mapping, ChatOptions pass through, messages only, missing input throws without calling the model, null client rejected
  • one end to end scenario using WorkflowCore.Testing: a real workflow host resolves the step and the client from DI and maps the reply into workflow data

Results:

  • dotnet build WorkflowCore.sln: succeeded, 0 errors
  • WorkflowCore.AI.Tests: 6/6 on each of net6.0, net8.0, net10.0
  • WorkflowCore.AI.AzureFoundry.Tests: 14/14
  • WorkflowCore.UnitTests: 62 per framework; should_run_pre_middlewares (SyncWorkflowRunnerTests) failed on net8.0 and net10.0 in this run. This PR does not touch core, and the same test also fails intermittently on master, so it is pre-existing and unrelated.

Limits

  • One request and one response per step. No streaming, no tool calling loop, no conversation store.
  • Only one IChatClient per container is used; keyed clients are not supported yet.
  • If Response is mapped to workflow data it is persisted with the instance, which can be large.
  • A failed model call throws and follows normal step retry, which sends the request again.
  • Not tested against a real model provider.

@danielgerlag
danielgerlag requested a review from glucaci as a code owner October 13, 2026 15:30

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant