Skip to content

Handle F# optional parameters in AIFunctionFactory schema generation#7439

Merged
eiriktsarpalis merged 1 commit intomainfrom
eiriktsarpalis/fsharp-optional-params
Mar 30, 2026
Merged

Handle F# optional parameters in AIFunctionFactory schema generation#7439
eiriktsarpalis merged 1 commit intomainfrom
eiriktsarpalis/fsharp-optional-params

Conversation

@eiriktsarpalis
Copy link
Copy Markdown
Member

@eiriktsarpalis eiriktsarpalis commented Mar 30, 2026

Summary

F# optional parameters declared with the ?param syntax are compiled with Microsoft.FSharp.Core.OptionalArgumentAttribute but do not set the ParameterAttributes.Optional flag, so ParameterInfo.IsOptional returns false. This caused AIFunctionFactory to treat them as required parameters, producing incorrect JSON schemas and throwing ArgumentException when the parameter was omitted during invocation.

Changes

Fix (AIJsonUtilities.Schema.Create.cs)

Updated TryGetEffectiveDefaultValue to:

  1. Check parameterInfo.IsOptional (covers COM interop [Optional] parameters)
  2. Check for OptionalArgumentAttribute by name via IsFSharpOptionalParameter() -- avoids a hard dependency on FSharp.Core
  3. When either is detected, return true with a null default value (equivalent to FSharpOption.None)

This single change fixes both schema generation (parameter no longer in the required array, gets "default": null) and invocation (marshaller returns null instead of throwing).

New F# test project

Added test/Libraries/Microsoft.Extensions.AI.Abstractions.FSharp.Tests/ with 6 tests covering:

  • Schema correctly marks F# optional parameters as not required
  • Schema includes "default": null for optional parameters
  • Invocation works when optional parameters are both omitted and provided
  • Multiple optional parameters work correctly

Test Results

  • 6/6 new F# tests pass
  • 664/664 existing AI tests pass
  • 1611/1611 existing AI Abstractions tests pass

Context

Addresses modelcontextprotocol/csharp-sdk#1476 (comment) -- F# MCP tool methods with optional parameters threw errors when clients omitted those parameters.

Microsoft Reviewers: Open in CodeFlow

@eiriktsarpalis eiriktsarpalis requested a review from a team as a code owner March 30, 2026 13:33
Copilot AI review requested due to automatic review settings March 30, 2026 13:33
@github-actions github-actions Bot added the area-ai Microsoft.Extensions.AI libraries label Mar 30, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates AIFunctionFactory schema generation and invocation so that F# optional parameters (compiled with Microsoft.FSharp.Core.OptionalArgumentAttribute, but not ParameterInfo.IsOptional) are treated as optional with a null default, and adds an F# test project to validate the behavior.

Changes:

  • Extend default-value detection to treat F# ?param optional parameters (and IsOptional parameters without a declared default) as having an effective default of null.
  • Add a new F# test project validating schema required/default behavior and invocation with omitted/provided optional args.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
test/Libraries/Microsoft.Extensions.AI.Abstractions.FSharp.Tests/Microsoft.Extensions.AI.Abstractions.FSharp.Tests.fsproj Introduces a new F# test project to cover F# optional parameter behavior.
test/Libraries/Microsoft.Extensions.AI.Abstractions.FSharp.Tests/FSharpOptionalParameterTests.fs Adds F# tests validating schema generation and invocation with F# optional parameters.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Schema.Create.cs Updates default-value inference to recognize F# optional parameters via OptionalArgumentAttribute.

@eiriktsarpalis eiriktsarpalis force-pushed the eiriktsarpalis/fsharp-optional-params branch from fb878fb to a25d7f2 Compare March 30, 2026 13:47
@eiriktsarpalis eiriktsarpalis requested a review from a team as a code owner March 30, 2026 13:47
F# optional parameters declared with the ?param syntax are compiled with
Microsoft.FSharp.Core.OptionalArgumentAttribute but do NOT set the
ParameterAttributes.Optional flag, causing ParameterInfo.IsOptional to
return false. This meant AIFunctionFactory treated them as required,
producing incorrect JSON schemas and throwing ArgumentException when
the parameter was omitted during invocation.

Updated TryGetEffectiveDefaultValue to detect OptionalArgumentAttribute
by name (avoiding a hard dependency on FSharp.Core) and treat such
parameters as having a null default value. A fast pre-check on the
parameter type (FSharpOption) avoids attribute scanning for non-F#
parameters. Also added handling for parameterInfo.IsOptional to cover
COM interop [Optional] parameters.

Added an F# test project (Microsoft.Extensions.AI.Abstractions.FSharp.Tests)
that validates schema generation and invocation for F# optional parameters.
Updated eng/build.proj to include .fsproj test projects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@eiriktsarpalis eiriktsarpalis force-pushed the eiriktsarpalis/fsharp-optional-params branch from a25d7f2 to 438a564 Compare March 30, 2026 13:59
@eiriktsarpalis eiriktsarpalis merged commit c7753f3 into main Mar 30, 2026
6 checks passed
@eiriktsarpalis eiriktsarpalis deleted the eiriktsarpalis/fsharp-optional-params branch March 30, 2026 19:04
jozkee pushed a commit to jozkee/extensions that referenced this pull request Apr 3, 2026
…otnet#7439)

F# optional parameters declared with the ?param syntax are compiled with
Microsoft.FSharp.Core.OptionalArgumentAttribute but do NOT set the
ParameterAttributes.Optional flag, causing ParameterInfo.IsOptional to
return false. This meant AIFunctionFactory treated them as required,
producing incorrect JSON schemas and throwing ArgumentException when
the parameter was omitted during invocation.

Updated TryGetEffectiveDefaultValue to detect OptionalArgumentAttribute
by name (avoiding a hard dependency on FSharp.Core) and treat such
parameters as having a null default value. A fast pre-check on the
parameter type (FSharpOption) avoids attribute scanning for non-F#
parameters. Also added handling for parameterInfo.IsOptional to cover
COM interop [Optional] parameters.

Added an F# test project (Microsoft.Extensions.AI.Abstractions.FSharp.Tests)
that validates schema generation and invocation for F# optional parameters.
Updated eng/build.proj to include .fsproj test projects.

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

area-ai Microsoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants