Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose an AIJsonUtilities class in M.E.AI and lower M.E.AI.Abstractions to STJv8 #5513

Merged

Conversation

eiriktsarpalis
Copy link
Member

@eiriktsarpalis eiriktsarpalis commented Oct 11, 2024

Renames FunctionCallHelpers to AIJsonUtilities and exposes the class as public.

Fix #5496. Fix #5482.

Microsoft Reviewers: Open in CodeFlow

@eiriktsarpalis eiriktsarpalis force-pushed the make-functioncallhelpers-public branch from f46ba23 to 514f846 Compare October 14, 2024 12:25
@eiriktsarpalis eiriktsarpalis marked this pull request as ready for review October 14, 2024 13:09
@eiriktsarpalis eiriktsarpalis requested a review from a team as a code owner October 14, 2024 13:09
eiriktsarpalis and others added 2 commits October 14, 2024 14:49
…onSchemaInferenceOptions.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>
@eiriktsarpalis eiriktsarpalis changed the title Expose FunctionCallUtilities class. Expose AIJsonUtilities class. Oct 15, 2024
/// <summary>
/// An options class for configuring the behavior of <see cref="AIJsonUtilities"/> JSON schema creation functionality.
/// </summary>
public sealed class AIJsonSchemaCreateOptions
Copy link
Member Author

Choose a reason for hiding this comment

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

Should we try to hide these classes behind a nested namespace? Microsoft.Extensions.AI.Utilities?

Copy link
Member

Choose a reason for hiding this comment

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

In general I think we should revisit using nested namespace for grouping. Everything is in a flat list right now.
cc: @SteveSandersonMS

@eiriktsarpalis eiriktsarpalis requested a review from a team as a code owner October 16, 2024 16:57
/// <param name="argumentParser">The parsing implementation converting the encoding to a dictionary of arguments.</param>
/// <param name="exceptionFilter">Filters potential parsing exceptions that should be caught and included in the result.</param>
/// <returns>A new instance of <see cref="FunctionCallContent"/> containing the parse result.</returns>
public static FunctionCallContent CreateFromParsedArguments<TEncoding>(
Copy link
Member Author

Choose a reason for hiding this comment

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

This change removes all "helper" code from the Abstractions repo with the exception of this method required by client impls. I used a callback approach to invert dependencies if that's acceptable.

Copy link
Member

Choose a reason for hiding this comment

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

Seems ok, though at this point I'm not sure how much benefit this method really provides.

@eiriktsarpalis eiriktsarpalis changed the title Expose AIJsonUtilities class. Expose an AIJsonUtilities class in M.E.AI and lower M.E.AI.Abstractions to STJv8 Oct 17, 2024
@eiriktsarpalis eiriktsarpalis linked an issue Oct 17, 2024 that may be closed by this pull request
@eiriktsarpalis eiriktsarpalis enabled auto-merge (squash) October 17, 2024 15:30
string callId,
string name,
Func<TEncoding, IDictionary<string, object?>?> argumentParser,
Func<Exception, bool>? exceptionFilter = null)
Copy link
Member

Choose a reason for hiding this comment

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

Do we need the filter? Could we just catch and wrap any exception from the parser and store it as the inner?

Copy link
Member Author

Choose a reason for hiding this comment

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

For JSON stuff we only wrap JsonException and let NotSupportedException bubble up, but maybe it isn't necessary.

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't seem worth promoting an extra delegate into the API for that. I'd suggest either we just hardcode ones we want to allow through and wrap everything else, or just wrap everything.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can't hardcode exception types given that it's a generic helper, so I'll just remove the filter.

Copy link
Member

Choose a reason for hiding this comment

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

We can't

We could; we do elsewhere around calling out to what's possibly user code. We can certainly choose not to.

so I'll just remove the filter

Ok

@@ -428,9 +427,10 @@ private IEnumerable<ChatRequestMessage> ToAzureAIInferenceChatMessages(IEnumerab
string? result = resultContent.Result as string;
if (result is null && resultContent.Result is not null)
{
JsonSerializerOptions options = ToolCallJsonSerializerOptions ?? JsonContext.Default.Options;
Copy link
Member

Choose a reason for hiding this comment

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

Could be a separate change if we decide to do it, but should ToolCallJsonSerializerOptions be made non-nullable like it is on the middleware clients, just defaulting it to the default options?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's possible, but it would require moving the defaults to Abstractions so that clients can reference it.

Copy link
Member

@stephentoub stephentoub Oct 17, 2024

Choose a reason for hiding this comment

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

I'm not understanding why this would require any movement. I'm simply suggesting changing:

public JsonSerializerOptions? ToolCallJsonSerializerOptions { get; set; }

to

private JsonSerializerOptions _toolCallOptions = JsonContext.Default.Options;
...
public JsonSerializerOptions ToolCallJsonSerializerOptions
{
    get => _toolCallOptions;
    set => _toolCallOptions = Throw.IfNull(value);
}

and then here instead of:

JsonSerializerOptions options = ToolCallJsonSerializerOptions ?? JsonContext.Default.Options;

it'd just be:

JsonSerializerOptions options = ToolCallJsonSerializerOptions;

Copy link
Member

Choose a reason for hiding this comment

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

Unless we're trying to avoid exposing this options instance?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just pointing out that unless we expose a default options instance on Abstractions users will have to define their own contexts to fill in the default _toolCallOptions.

Copy link
Member

Choose a reason for hiding this comment

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

I'm just pointing out that unless we expose a default options instance on Abstractions users will have to define their own contexts to fill in the default _toolCallOptions.

I don't understand why. The current code doesn't require that, just substituting JsonContext.Default.Options when the user hasn't supplied their own. I'm simply suggesting moving around where we default back to that.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see what you mean. It is appropriate to default to JsonContext.Default.Options in the particular locations where it is being done because they're serializing specific types (JsonElement for FCR and IDictionary<string, object> for FCC) which are known to be defined in the local JsonContext. I don't think it would be safe to assume the same thing in other locations where ToolCallJsonSerializerOptions is being used (e.g. when serializing AdditionalProperties), so if we wanted to default to something in that case we should be using the global options instead.

@@ -62,6 +62,7 @@
<SystemSecurityCryptographyXmlVersion>9.0.0-rc.2.24473.5</SystemSecurityCryptographyXmlVersion>
<SystemTextEncodingsWebVersion>9.0.0-rc.2.24473.5</SystemTextEncodingsWebVersion>
<SystemTextJsonVersion>9.0.0-rc.2.24473.5</SystemTextJsonVersion>
<SystemTextJson8Version>8.0.5</SystemTextJson8Version>
Copy link
Member

Choose a reason for hiding this comment

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

If STJ revs, e.g. with a security patch, we'll need to remember to update this, or will it be automatically updated by darc? Do we need a corresponding entry in Version.Details.xml?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not clear to me that it would be updated automatically. Maybe @ViktorHofer knows.

Copy link
Member

@ViktorHofer ViktorHofer Oct 17, 2024

Choose a reason for hiding this comment

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

Version.Details.xml is the input source for Maestro dependency flow which handles repo -> repo internal flow. If you want to get a version updated based on a new package being available on nuget.org, you would need to use Dependabot.

If you just want to make sure that you don't use a vulnerable version of STJ, then you could enable NuGet Audit in this repository which would flag the dependency at restore and build time.

Choose a reason for hiding this comment

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

Forgive my ignorance but if this extension doesn't have a dependency on .Net 9, why the package is a 9.x-preview and not a 8.x-preview (targeting NET 8 dependencies only) ?

Copy link
Member

Choose a reason for hiding this comment

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

Which package are you asking about?

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Thanks!

@eiriktsarpalis eiriktsarpalis merged commit d894f98 into dotnet:main Oct 17, 2024
6 checks passed
@stephentoub
Copy link
Member

stephentoub commented Oct 17, 2024

Was this set to auto-merge and that's why it was merged without comments being addressed?

We should either stop using auto-merge or I'll need to adapt and stop approving with comments.

@eiriktsarpalis
Copy link
Member Author

Was this set to auto-merge and that's why it was merged without comments being addressed?

Exactly what happened.

@eiriktsarpalis
Copy link
Member Author

We should either stop using auto-merge or I'll need to adapt and stop approving with comments.

Some repos don't trigger auto merges unless all review items have been resolved. Seems like the best of both worlds if we turned that on.

@eiriktsarpalis eiriktsarpalis deleted the make-functioncallhelpers-public branch October 17, 2024 16:43
eiriktsarpalis added a commit to eiriktsarpalis/extensions that referenced this pull request Oct 17, 2024
stephentoub pushed a commit that referenced this pull request Oct 17, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Nov 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
4 participants