-
Notifications
You must be signed in to change notification settings - Fork 2
Align core data serializers and repository interface with Python SDK #9
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. | ||
| // If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| namespace Utcp.Core.Models.Serialization; | ||
|
|
||
| using Utcp.Core.Models; | ||
| using Utcp.Core.Serialization; | ||
|
|
||
| public sealed class AuthSerializer : Serializer<Auth> | ||
| { | ||
| public override Auth ValidateDictionary(IReadOnlyDictionary<string, object?> obj) | ||
| { | ||
| if (!obj.TryGetValue("auth_type", out var discriminator) || discriminator is not string authType) | ||
| { | ||
| throw new ArgumentException("Dictionary does not contain an auth_type discriminator.", nameof(obj)); | ||
| } | ||
|
|
||
| if (!PolymorphicRegistry.TryGetAuthType(authType, out var type) || type is null) | ||
| { | ||
| throw new InvalidOperationException($"Unknown Auth type: {authType}"); | ||
| } | ||
|
|
||
| if (Runtime.Deserialize(type, obj) is not Auth auth) | ||
| { | ||
| throw new InvalidOperationException($"Unable to deserialize Auth type '{authType}'."); | ||
| } | ||
|
|
||
| return auth; | ||
| } | ||
|
|
||
| public override Dictionary<string, object?> ToDictionary(Auth obj) | ||
| { | ||
| return Runtime.ToDictionary(obj); | ||
| } | ||
| } | ||
|
|
||
| public sealed class CallTemplateSerializer : Serializer<CallTemplate> | ||
| { | ||
| public override CallTemplate ValidateDictionary(IReadOnlyDictionary<string, object?> obj) | ||
| { | ||
| if (!obj.TryGetValue("call_template_type", out var discriminator) || discriminator is not string templateType) | ||
| { | ||
| throw new ArgumentException("Dictionary does not contain a call_template_type discriminator.", nameof(obj)); | ||
| } | ||
|
|
||
| if (!PolymorphicRegistry.TryGetCallTemplateType(templateType, out var type) || type is null) | ||
| { | ||
| throw new InvalidOperationException($"Unknown CallTemplate type: {templateType}"); | ||
| } | ||
|
|
||
| if (Runtime.Deserialize(type, obj) is not CallTemplate template) | ||
| { | ||
| throw new InvalidOperationException($"Unable to deserialize CallTemplate type '{templateType}'."); | ||
| } | ||
|
|
||
| return template; | ||
| } | ||
|
|
||
| public override Dictionary<string, object?> ToDictionary(CallTemplate obj) | ||
| { | ||
| return Runtime.ToDictionary(obj); | ||
| } | ||
| } | ||
|
|
||
| public sealed class JsonSchemaSerializer : Serializer<JsonSchema> | ||
| { | ||
| public override JsonSchema ValidateDictionary(IReadOnlyDictionary<string, object?> obj) | ||
| { | ||
| return Runtime.Deserialize<JsonSchema>(obj); | ||
| } | ||
|
|
||
| public override Dictionary<string, object?> ToDictionary(JsonSchema obj) | ||
| { | ||
| return Runtime.ToDictionary(obj); | ||
| } | ||
| } | ||
|
|
||
| public sealed class ToolSerializer : Serializer<Tool> | ||
| { | ||
| public override Tool ValidateDictionary(IReadOnlyDictionary<string, object?> obj) | ||
| { | ||
| return Runtime.Deserialize<Tool>(obj); | ||
| } | ||
|
|
||
| public override Dictionary<string, object?> ToDictionary(Tool obj) | ||
| { | ||
| return Runtime.ToDictionary(obj); | ||
| } | ||
| } | ||
|
|
||
| public sealed class UtcpManualSerializer : Serializer<UtcpManual> | ||
| { | ||
| public override UtcpManual ValidateDictionary(IReadOnlyDictionary<string, object?> obj) | ||
| { | ||
| return Runtime.Deserialize<UtcpManual>(obj); | ||
| } | ||
|
|
||
| public override Dictionary<string, object?> ToDictionary(UtcpManual obj) | ||
| { | ||
| return Runtime.ToDictionary(obj); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -68,24 +68,84 @@ public async Task<bool> RemoveManualAsync(string manualName, CancellationToken c | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public async Task<bool> RemoveToolAsync(string toolName, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await this.writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.toolsByName.TryRemove(toolName, out _)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var manualName in this.manualsByName.Keys.ToList()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!this.manualsByName.TryGetValue(manualName, out var manual)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var remaining = manual.Tools.Where(t => !string.Equals(t.Name, toolName, StringComparison.OrdinalIgnoreCase)).ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (remaining.Count != manual.Tools.Count) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.manualsByName[manualName] = manual with { Tools = remaining }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finally | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.writeLock.Release(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent case handling can make RemoveToolAsync fail unexpectedly.
Two fixes; prefer A: A) Make dictionaries case-insensitive (applies outside this hunk): - private readonly ConcurrentDictionary<string, Tool> toolsByName = new();
- private readonly ConcurrentDictionary<string, UtcpManual> manualsByName = new();
- private readonly ConcurrentDictionary<string, CallTemplate> manualTemplatesByName = new();
+ private readonly ConcurrentDictionary<string, Tool> toolsByName = new(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, UtcpManual> manualsByName = new(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, CallTemplate> manualTemplatesByName = new(StringComparer.OrdinalIgnoreCase);B) Or, perform a case-insensitive key resolution before removing: - if (!this.toolsByName.TryRemove(toolName, out _))
+ var key = this.toolsByName.Keys.FirstOrDefault(k => string.Equals(k, toolName, StringComparison.OrdinalIgnoreCase));
+ if (key is null || !this.toolsByName.TryRemove(key, out _))
{
return false;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<IReadOnlyList<string>> GetManualNamesAsync(CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var list = (IReadOnlyList<string>)this.manualsByName.Keys.ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(list); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<CallTemplate?> GetManualCallTemplateAsync(string manualCallTemplateName, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.manualTemplatesByName.TryGetValue(manualCallTemplateName, out var template); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(template); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<IReadOnlyList<UtcpManual>> GetManualsAsync(CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var list = (IReadOnlyList<UtcpManual>)this.manualsByName.Values.ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(list); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<UtcpManual?> GetManualAsync(string manualName, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.manualsByName.TryGetValue(manualName, out var manual); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(manual); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<IReadOnlyList<Tool>> GetToolsAsync(CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var list = (IReadOnlyList<Tool>)this.toolsByName.Values.ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(list); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<Tool?> GetToolAsync(string toolName, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.toolsByName.TryGetValue(toolName, out var tool); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult(tool); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<IReadOnlyList<Tool>?> GetToolsByManualAsync(string manualName, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.manualsByName.TryGetValue(manualName, out var manual)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult<IReadOnlyList<Tool>?>(manual.Tools.ToList()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Task.FromResult<IReadOnlyList<Tool>?>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public Task<IReadOnlyList<CallTemplate>> GetManualCallTemplatesAsync(CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var list = (IReadOnlyList<CallTemplate>)this.manualTemplatesByName.Values.ToList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. | ||
| // If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| namespace Utcp.Core.Serialization; | ||
|
|
||
| using System.Text.Json; | ||
| using System.Text.Json.Serialization; | ||
| using Utcp.Core.Models.Serialization; | ||
|
|
||
| public abstract class Serializer<T> | ||
| { | ||
| public abstract T ValidateDictionary(IReadOnlyDictionary<string, object?> obj); | ||
|
|
||
| public abstract Dictionary<string, object?> ToDictionary(T obj); | ||
|
|
||
| public virtual T Copy(T obj) | ||
| { | ||
| return this.ValidateDictionary(this.ToDictionary(obj)); | ||
| } | ||
|
|
||
| protected static class Runtime | ||
| { | ||
| internal static readonly JsonSerializerOptions Options; | ||
|
|
||
| static Runtime() | ||
| { | ||
| Options = new JsonSerializerOptions | ||
| { | ||
| PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, | ||
| DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, | ||
| }; | ||
| Options.Converters.Add(new CallTemplateJsonConverter()); | ||
| Options.Converters.Add(new AuthJsonConverter()); | ||
| } | ||
|
|
||
| public static Dictionary<string, object?> ToDictionary<TValue>(TValue value) | ||
| { | ||
| if (value is null) | ||
| { | ||
| throw new ArgumentNullException(nameof(value)); | ||
| } | ||
|
|
||
| var element = JsonSerializer.SerializeToElement(value, value.GetType(), Options); | ||
| if (element.ValueKind != JsonValueKind.Object) | ||
| { | ||
| throw new InvalidOperationException("Expected JSON object when converting to dictionary."); | ||
| } | ||
|
|
||
| return (Dictionary<string, object?>)ConvertElement(element)!; | ||
| } | ||
|
|
||
| public static TTarget Deserialize<TTarget>(IReadOnlyDictionary<string, object?> data) | ||
| { | ||
| var json = JsonSerializer.Serialize(data); | ||
| var result = JsonSerializer.Deserialize<TTarget>(json, Options); | ||
| if (result is null) | ||
| { | ||
| throw new InvalidOperationException("Unable to deserialize dictionary into target type."); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public static object? Deserialize(Type type, IReadOnlyDictionary<string, object?> data) | ||
| { | ||
| var json = JsonSerializer.Serialize(data); | ||
| return JsonSerializer.Deserialize(json, type, Options); | ||
| } | ||
|
|
||
| private static object? ConvertElement(JsonElement element) | ||
| { | ||
| return element.ValueKind switch | ||
| { | ||
| JsonValueKind.Object => ConvertObject(element), | ||
| JsonValueKind.Array => element.EnumerateArray().Select(ConvertElement).ToList(), | ||
| JsonValueKind.String => element.GetString(), | ||
| JsonValueKind.Number => ConvertNumber(element), | ||
| JsonValueKind.True => true, | ||
| JsonValueKind.False => false, | ||
| JsonValueKind.Null => null, | ||
| JsonValueKind.Undefined => null, | ||
| _ => element.GetRawText(), | ||
| }; | ||
| } | ||
|
|
||
| private static Dictionary<string, object?> ConvertObject(JsonElement element) | ||
| { | ||
| var dict = new Dictionary<string, object?>(); | ||
| foreach (var property in element.EnumerateObject()) | ||
| { | ||
| dict[property.Name] = ConvertElement(property.Value); | ||
| } | ||
|
|
||
| return dict; | ||
| } | ||
|
|
||
| private static object ConvertNumber(JsonElement element) | ||
| { | ||
| if (element.TryGetInt64(out var l)) | ||
| { | ||
| return l; | ||
| } | ||
|
|
||
| if (element.TryGetDouble(out var d)) | ||
| { | ||
| return d; | ||
| } | ||
|
|
||
| return element.GetDecimal(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Case Sensitivity Mismatch in Tool Removal
The
RemoveToolAsyncmethod has inconsistent case sensitivity. It uses case-sensitive comparison when removing a tool fromtoolsByName, but case-insensitive comparison when updating tool lists withinmanualsByName. This can lead to data inconsistency where a tool is removed from one collection but not the other if the casing doesn't match exactly.