microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feature/replace-jsonschema-with-njsonschema

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.AI/Function.cs

147lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Reflection;
5using System.Text.Json;
6using System.Text.Json.Serialization;
7
8using Microsoft.Teams.AI.Annotations;
9using Microsoft.Teams.AI.Messages;
10using Microsoft.Teams.Common.Extensions;
11using Microsoft.Teams.Common.Json;
12
13namespace Microsoft.Teams.AI;
14
15/// <summary>
16/// defines a block of code that
17/// can be called by a model
18/// </summary>
19[JsonConverter(typeof(TrueTypeJsonConverter<IFunction>))]
20public interface IFunction
21{
22 /// <summary>
23 /// the unique name
24 /// </summary>
25 public string Name { get; }
26
27 /// <summary>
28 /// a description of what the function
29 /// should be used for
30 /// </summary>
31 public string? Description { get; }
32
33 /// <summary>
34 /// the Json Schema representing what
35 /// parameters the function accepts
36 /// </summary>
37 public IJsonSchema? Parameters { get; }
38}
39
40/// <summary>
41/// defines a block of code that
42/// can be called by a model
43/// </summary>
44public class Function : IFunction
45{
46 [JsonPropertyName("name")]
47 [JsonPropertyOrder(0)]
48 public string Name { get; set; }
49
50 [JsonPropertyName("description")]
51 [JsonPropertyOrder(1)]
52 public string? Description { get; set; }
53
54 [JsonPropertyName("parameters")]
55 [JsonPropertyOrder(2)]
56 public IJsonSchema? Parameters { get; set; }
57
58 [JsonIgnore]
59 public Delegate Handler { get; set; }
60
61 public Function(string name, string? description, Delegate handler)
62 {
63 Name = name;
64 Description = description;
65 Handler = handler;
66 Parameters = GenerateParametersSchema(handler);
67 }
68
69 public Function(string name, string? description, IJsonSchema parameters, Delegate handler)
70 {
71 Name = name;
72 Description = description;
73 Parameters = parameters;
74 Handler = handler;
75 }
76
77 internal Task<object?> Invoke(FunctionCall call)
78 {
79 if (call.Arguments is not null && Parameters is not null)
80 {
81 var result = Parameters.Validate(call.Arguments);
82
83 if (!result.IsValid)
84 {
85 Console.WriteLine(string.Join("\n", result.Errors.Select(e => $"{e.Path} => {e.Message}")));
86 throw new ArgumentException(
87 string.Join("\n", result.Errors.Select(e => $"{e.Path} => {e.Message}"))
88 );
89 }
90 }
91
92 var args = call.Parse() ?? new Dictionary<string, object?>();
93 var method = Handler.GetMethodInfo();
94 var parameters = method.GetParameters().Select(param =>
95 {
96 var name = param.GetCustomAttribute<ParamAttribute>()?.Name ?? param.Name ?? param.Position.ToString();
97 args.TryGetValue(name, out var value);
98
99 if (value is JsonElement element)
100 {
101 return element.Deserialize(param.ParameterType);
102 }
103
104 // Special param type to get the arguments dictionary (IDictionary<string, object?> args)
105 if (value is null && name == "args" && param.ParameterType == typeof(IDictionary<string, object?>))
106 {
107 value = args;
108 }
109
110 return value;
111 }).ToArray();
112
113 return method.InvokeAsync(Handler.Target, parameters);
114 }
115
116 public override string ToString()
117 {
118 return JsonSerializer.Serialize(this, new JsonSerializerOptions()
119 {
120 WriteIndented = true
121 });
122 }
123
124 /// <summary>
125 /// Generates a JsonSchema for the parameters of a delegate handler using reflection
126 /// </summary>
127 private static IJsonSchema? GenerateParametersSchema(Delegate handler)
128 {
129 var method = handler.GetMethodInfo();
130 var methodParams = method.GetParameters();
131
132 if (methodParams.Length == 0)
133 {
134 return null;
135 }
136
137 var parameters = methodParams.Select(p =>
138 {
139 var paramName = p.GetCustomAttribute<ParamAttribute>()?.Name ?? p.Name ?? p.Position.ToString();
140 var schema = JsonSchemaWrapper.FromType(p.ParameterType);
141 var required = !p.IsOptional;
142 return (paramName, schema, required);
143 }).ToArray();
144
145 return JsonSchemaWrapper.CreateObjectSchema(parameters);
146 }
147}