Skip to content

Add Responses web-service sample + integration tests for C# SDK#681

Draft
MaanavD wants to merge 4 commits intomainfrom
agents/implement-responses-api-csharp-sdk
Draft

Add Responses web-service sample + integration tests for C# SDK#681
MaanavD wants to merge 4 commits intomainfrom
agents/implement-responses-api-csharp-sdk

Conversation

@MaanavD
Copy link
Copy Markdown
Collaborator

@MaanavD MaanavD commented Apr 28, 2026

Pivoted approach: The C# SDK does not gain a new Responses API client surface. Instead, this PR adds a focused sample and integration tests that exercise the OpenAI Responses API against the Foundry Local web service, mirroring how samples/cs/foundry-local-web-server calls chat completions.

What's in this PR

New sample

  • samples/cs/responses-foundry-local-web-server/
    • Program.cs loads qwen2.5-0.5b via FoundryLocalManager, starts the local web service, and points OpenAI.Responses.ResponsesClient at <service-url>/v1
    • Demonstrates: non-streaming CreateResponse, streaming with StreamingResponseOutputTextDeltaUpdate, and a full function-calling round-trip via previous_response_id
    • ResponsesFoundryLocalWebServer.csproj mirrors the existing FoundryLocalWebServer.csproj
  • samples/cs/README.md updated with a row for the new sample

Integration tests

  • sdk/cs/test/FoundryLocal.Tests/ResponsesIntegrationTests.cs
    • NonStreaming_SimplePrompt_ReturnsText
    • Streaming_EmitsTextDeltaAndCompletionEvents
    • FunctionCalling_FullRoundTrip_ProducesAssistantText
    • Skips with Skip.Test(...) when qwen2.5-0.5b is not cached locally
    • [After(Class)] cleanup stops the web service and unloads the model
  • OpenAI 2.10.0 added to the test csproj only

Package bump

  • samples/cs/Directory.Packages.props: centrally-managed OpenAI 2.5.0 → 2.10.0 (needed for stable ResponsesClient)

What's NOT in this PR

  • No new SDK Responses client/types in Microsoft.AI.Foundry.Local
  • No FFI/native Responses implementation
  • No HTTP fallback in the SDK
  • No changes to chat / audio / embedding / model lifecycle code
  • No Betalgo removal
  • All earlier additions on this branch (ResponsesClient.cs, ResponsesTypes.cs, manager GetResponsesClient* methods, IModel extension, custom ResponsesClientSettings) are reverted

Validation

  • dotnet build samples/cs/responses-foundry-local-web-server -c Release — clean
  • dotnet build sdk/cs/test/FoundryLocal.Tests -c Release — clean (2 unrelated pre-existing nullable warnings)
  • Local test execution is currently blocked by a pre-existing GetRepoRoot() issue when this repo is checked out as a git worktree (Utils.AssemblyInit throws because .git is a file rather than a directory in worktrees) — this affects every test in the project, not just Responses
  • Per the prompt: known-good runtime validation may require nightly Foundry Local Core, e.g. 1.1.0-dev.202605010202 from the ORT-Nightly feed if the stable bits are stale

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

Implements `OpenAIResponsesClient` for the OpenAI Responses API surface in the C# SDK, mirroring the Python (#670) and JS (#671) SDKs and incorporating their resolved review feedback up front.

- New `Microsoft.AI.Foundry.Local.OpenAI.OpenAIResponsesClient` (HTTP-only, `HttpClient`-based, no FFI)

- Non-streaming + IAsyncEnumerable streaming (`Channel<T>` SSE pipeline)

- Full CRUD: `GetAsync`, `DeleteAsync`, `CancelAsync`, `GetInputItemsAsync`, `ListAsync(limit, order, after)`

- Polymorphic content parts and response items via `[JsonPolymorphic]` + source-gen context

- Streaming events for lifecycle, output, text deltas, function calls, and reasoning

- Vision helpers: `InputImageContent.FromFile/FromUrl/FromBytes`

- `FoundryLocalManager.GetResponsesClient(modelId?)` and `IModel.GetResponsesClientAsync`

Pre-applied PR review feedback from the Python and JS PRs:

- `Settings.Store` defaults to `null` (omit) instead of forcing `store=true`

- `InputImageContent.MediaType` is optional; unknown extensions omit the field so the server infers

- `InputImageContent.FromFile` throws `FileNotFoundException` on missing path

- `HttpClient.Timeout = Timeout.InfiniteTimeSpan`; callers use `CancellationToken` for deadlines (avoids 100s default cutting off SSE)

- `ListAsync` accepts `limit`, `order`, `after`; `ListResponsesResult` exposes `first_id`, `last_id`, `has_more`

- `InputImageContent.Validate()` enforces mutual exclusivity of `ImageUrl` / `ImageData` at request build time

- BMP supported in `DetectMediaType` alongside png/jpg/jpeg/gif/webp

- Uses shared `FoundryLocalException` for transport/parse errors, no dedicated `ResponsesException`

Tests: 22 unit tests (mocked HTTP) + integration tests gated on a running service. `dotnet build sdk/cs/src` clean; all Responses tests pass.

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

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview, Comment May 3, 2026 11:40pm

Request Review

Replaces the hand-rolled Responses DTO/SSE/serialization layer with the official `OpenAI` 2.10.0 NuGet package's `OpenAI.Responses.ResponsesClient` while keeping `Microsoft.AI.Foundry.Local.OpenAIResponsesClient` as the public Foundry Local-shaped wrapper.

- Public surface unchanged: `OpenAIResponsesClient` ctor, `Settings`, `CreateAsync` / `CreateStreamingAsync` / `GetAsync` / `DeleteAsync` / `CancelAsync` / `GetInputItemsAsync` / `ListAsync`

- Endpoints come from `OpenAI.Responses.ResponsesClient` configured with the Foundry Local web service URL via `OpenAIClientOptions.Endpoint`; only `ListAsync` keeps a small `HttpClient` shim for Foundry Local's list-responses extension (the official client doesn't expose that yet)

- `ResponsesClientSettings` keeps the same Foundry Local-shaped knobs but applies them onto official `CreateResponseOptions` (`MaxOutputTokenCount`, `StoredOutputEnabled`, `ParallelToolCallsEnabled`, etc.)

- New `ResponseContentPartHelpers` produces official `ResponseContentPart` instances for vision: file (auto-detect MIME, throws on missing/unknown extension), bytes (data URI), and URL

- Vision now uses official OpenAI shape (`input_image` + `image_url` data URI) rather than Foundry Local's `image_data` + `media_type` extension

- Deleted the hand-rolled `ResponsesTypes.cs` polymorphic DTO surface and the source-gen `ResponsesSerializationContext`

Bug fixes:

- Avoid `op_Implicit(null)` on `ResponseImageDetailLevel` and `ResponseItemCollectionOrder` value-type structs when callers pass null/empty options

- Map `ClientResultException` -> `FoundryLocalException` consistently across CRUD methods

Tests:

- Unit tests rewritten for the official surface; cover settings defaults, input validation, image helper factories, MIME detection, and `ListResponsesResult.FromJson` parsing

- Integration tests updated to use `CreateResponseOptions` / `ResponseItem` / `StreamingResponseUpdate` types

- `dotnet build` clean; all Responses tests pass (51/61 overall, the 10 failures are pre-existing `EmbeddingClientTests` infra)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MaanavD MaanavD changed the title Add OpenAI Responses API client to C# SDK Add OpenAI Responses API client to C# SDK (wrapping official OpenAI package) Apr 29, 2026
This PR pivots away from adding a Responses API client to the C# SDK and instead adds a focused sample and integration tests that exercise the OpenAI Responses API against the Foundry Local web service, mirroring how `samples/cs/foundry-local-web-server` calls chat completions.

Reverted on this branch:

- `sdk/cs/src/OpenAI/ResponsesClient.cs` and `ResponsesTypes.cs` (deleted)

- `ResponsesClientSettings`, factory methods, `IModel.GetResponsesClientAsync`, `Detail/Model.cs` and `Detail/ModelVariant.cs` additions

- `OpenAI` PackageReference on the SDK project

- `Utils.cs` test-side change

- Earlier `ResponsesClientTests.cs` and `ResponsesIntegrationTests.cs`

Added:

- `samples/cs/responses-foundry-local-web-server/` (Program.cs + csproj)

  - Loads `qwen2.5-0.5b` via `FoundryLocalManager`, starts the web service

  - Uses `OpenAI.Responses.ResponsesClient` (official OpenAI .NET package, 2.10.0) pointed at `<service-url>/v1`

  - Demonstrates non-streaming, streaming (`StreamingResponseOutputTextDeltaUpdate`), and a full function-calling round-trip via `previous_response_id`

- `sdk/cs/test/FoundryLocal.Tests/ResponsesIntegrationTests.cs`

  - Three integration tests: NonStreaming, Streaming, and FunctionCalling round-trip

  - Skips automatically when `qwen2.5-0.5b` is not in the local cache

  - Cleans up: stops web service and unloads model in `[After(Class)]`

- Bumped centrally-managed `OpenAI` package version 2.5.0 -> 2.10.0 (needed for stable `ResponsesClient`); added `OpenAI 2.10.0` to test project

- Updated `samples/cs/README.md` with a row for the new sample

Validation:

- `dotnet build samples/cs/responses-foundry-local-web-server -c Release`: 0 warnings, 0 errors

- `dotnet build sdk/cs/test/FoundryLocal.Tests -c Release`: 0 errors (2 unrelated pre-existing nullable warnings)

- Local test run is currently blocked by a pre-existing `GetRepoRoot()` issue in worktrees (`Utils.AssemblyInit` throws when `.git` is a file rather than a directory); affects every test in the project, not Responses-specific

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MaanavD MaanavD changed the title Add OpenAI Responses API client to C# SDK (wrapping official OpenAI package) Add Responses web-service sample + integration tests for C# SDK May 1, 2026
… locally

Cross-references the JS Responses sample/tests (PR #671) to keep the C# pattern consistent.

Sample (samples/cs/responses-foundry-local-web-server):
- Added README.md mirroring the JS sample (prereqs, run, expected output, troubleshooting)
- Tool now uses an empty-params schema (matches JS PR), which the small qwen2.5-0.5b reliably calls
- Single ResponseTool reused on the follow-up call; deterministic options (Temperature=0, MaxOutputTokenCount=64)
- Cleanup wrapped in try/finally so StopWebService/Unload run even on exceptions

Integration tests (sdk/cs/test/FoundryLocal.Tests/ResponsesIntegrationTests.cs):
- Mirrors the JS suite responsesWebService.test.ts (NonStreaming, Streaming, FunctionCalling)
- Skips when Utils.IsRunningInCI() is true and when qwen2.5-0.5b is not pre-cached
- Streaming asserts response.created, response.output_text.delta, and response.completed events (parity with JS)
- Tool-calling test reuses the same get_weather empty-params definition
- Streaming options include StreamingEnabled = true so the official ResponsesClient allows the call

Pre-existing fix (test infra only):
- Utils.GetRepoRoot() previously failed in git worktrees because .git is a file, not a directory; now accepts either form. This unblocked test execution in worktree checkouts.

Validation:
- dotnet build samples/cs/responses-foundry-local-web-server -c Release: 0 warnings, 0 errors
- dotnet build sdk/cs/test/FoundryLocal.Tests -c Release: 0 errors
- dotnet test --filter ResponsesIntegration: all 3 Responses tests pass end-to-end against a real local model
- The 10 remaining failures across the project are pre-existing EmbeddingClientTests infra (different model not cached), unrelated to this PR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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