microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
6594a29aa91c928c547a8821d305758bc8d340ed

Branches

Tags

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

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.AI/Function.cs

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