diff --git a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs index 40adbdcde47..cb3574a8b1e 100644 --- a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs +++ b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.cs @@ -1,31 +1,30 @@ -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version: 17.0.0.0 -// +// // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // -// ------------------------------------------------------------------------------ -namespace AutoGen.SourceGenerator.Template -{ +//------------------------------------------------------------------------------ + +namespace AutoGen.SourceGenerator.Template { using System.Linq; using System.Collections.Generic; using Microsoft.CodeAnalysis; using System; - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")] - internal partial class FunctionCallTemplate : FunctionCallTemplateBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { - this.Write(""); + + internal partial class FunctionCallTemplate : FunctionCallTemplateBase { + + +public string NameSpace {get; set;} +public string ClassName {get; set;} +public IEnumerable FunctionContracts {get; set;} +public bool IsStatic {get; set;} = false; + + + public virtual string TransformText() { + this.GenerationEnvironment = null; this.Write(@"//---------------------- // // This code was generated by a tool. @@ -42,407 +41,281 @@ public virtual string TransformText() if (!String.IsNullOrEmpty(NameSpace)) { this.Write("namespace "); this.Write(this.ToStringHelper.ToStringWithCulture(NameSpace)); - this.Write("\r\n{\r\n"); + this.Write("\n{\n"); } this.Write(" public partial class "); this.Write(this.ToStringHelper.ToStringWithCulture(ClassName)); - this.Write("\r\n {\r\n"); + this.Write("\n {\n"); foreach (var functionContract in FunctionContracts) { - this.Write("\r\n private class "); + this.Write("\n private class "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionSchemaClassName())); - this.Write("\r\n {\r\n"); + this.Write("\n {\n"); foreach (var parameter in functionContract.Parameters) { if (parameter.IsOptional) { this.Write(" [JsonPropertyName(@\""); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name)); - this.Write("\")]\r\n\t\t\tpublic "); + this.Write("\")]\n\t\t\tpublic "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type)); this.Write(" "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name)); this.Write(" {get; set;} = "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.DefaultValue)); - this.Write(";\r\n"); + this.Write(";\n"); } else { this.Write(" [JsonPropertyName(@\""); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name)); - this.Write("\")]\r\n\t\t\tpublic "); + this.Write("\")]\n\t\t\tpublic "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type)); this.Write(" "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name)); - this.Write(" {get; set;}\r\n"); + this.Write(" {get; set;}\n"); } } - this.Write(" }\r\n\r\n public "); + this.Write(" }\n\n public "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.ReturnType)); this.Write(" "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionWrapperName())); - this.Write("(string arguments)\r\n {\r\n var schema = JsonSerializer.Deserializ" + - "e<"); + this.Write("(string arguments)\n {\n var schema = JsonSerializer.Deserialize<" + + ""); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionSchemaClassName())); - this.Write(">(\r\n arguments, \r\n new JsonSerializerOptions\r\n " + - " {\r\n PropertyNamingPolicy = JsonNamingPolicy.CamelC" + - "ase,\r\n });\r\n"); + this.Write(">(\n arguments, \n new JsonSerializerOptions\n " + + " {\n PropertyNamingPolicy = JsonNamingPolicy.CamelCase," + + "\n });\n"); var argumentLists = string.Join(", ", functionContract.Parameters.Select(p => $"schema.{p.Name}")); - this.Write("\r\n return "); + this.Write("\n return "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.Name)); this.Write("("); this.Write(this.ToStringHelper.ToStringWithCulture(argumentLists)); - this.Write(");\r\n }\r\n\r\n public FunctionContract "); + this.Write(");\n }\n\n public FunctionContract "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionContractName())); - this.Write("\r\n {\r\n get => new FunctionContract\r\n {\r\n"); + this.Write("\n {\n get => new FunctionContract\n {\n"); if (functionContract.Namespace != null) { this.Write(" Namespace = @\""); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.Namespace)); - this.Write("\",\r\n"); + this.Write("\",\n"); } if (functionContract.ClassName != null) { this.Write(" ClassName = @\""); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.ClassName)); - this.Write("\",\r\n"); + this.Write("\",\n"); } if (functionContract.Name != null) { this.Write(" Name = @\""); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.Name)); - this.Write("\",\r\n"); + this.Write("\",\n"); } if (functionContract.Description != null) { this.Write(" Description = @\""); - this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.Description)); - this.Write("\",\r\n"); + this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.Description.Replace("\"", "\"\""))); + this.Write("\",\n"); } if (functionContract.ReturnType != null) { this.Write(" ReturnType = typeof("); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.ReturnType)); - this.Write("),\r\n"); + this.Write("),\n"); } if (functionContract.ReturnDescription != null) { this.Write(" ReturnDescription = @\""); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.ReturnDescription)); - this.Write("\",\r\n"); + this.Write("\",\n"); } if (functionContract.Parameters != null) { this.Write(" Parameters = new global::AutoGen.Core.FunctionParameterContract[]" + - "\r\n {\r\n"); + "\n {\n"); foreach (var parameter in functionContract.Parameters) { - this.Write(" new FunctionParameterContract\r\n {\r\n"); + this.Write(" new FunctionParameterContract\n {\n"); if (parameter.Name != null) { this.Write(" Name = @\""); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name)); - this.Write("\",\r\n"); + this.Write("\",\n"); } if (parameter.Description != null) { this.Write(" Description = @\""); - this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Description)); - this.Write("\",\r\n"); + this.Write(this.ToStringHelper.ToStringWithCulture( parameter.Description.Replace("\"", "\"\"") )); + this.Write("\",\n"); } if (parameter.Type != null) { this.Write(" ParameterType = typeof("); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type)); - this.Write("),\r\n"); + this.Write("),\n"); } this.Write(" IsRequired = "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.IsOptional ? "false" : "true")); - this.Write(",\r\n"); + this.Write(",\n"); if (parameter.DefaultValue != null) { this.Write(" DefaultValue = "); this.Write(this.ToStringHelper.ToStringWithCulture(parameter.DefaultValue)); - this.Write(",\r\n"); + this.Write(",\n"); } - this.Write(" },\r\n"); + this.Write(" },\n"); } - this.Write(" },\r\n"); + this.Write(" },\n"); } - this.Write(" };\r\n }\r\n\r\n public global::Azure.AI.OpenAI.FunctionDefin" + - "ition "); + this.Write(" };\n }\n\n public global::Azure.AI.OpenAI.FunctionDefiniti" + + "on "); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionDefinitionName())); - this.Write("\r\n {\r\n get => this."); + this.Write("\n {\n get => this."); this.Write(this.ToStringHelper.ToStringWithCulture(functionContract.GetFunctionContractName())); - this.Write(".ToOpenAIFunctionDefinition();\r\n }\r\n"); + this.Write(".ToOpenAIFunctionDefinition();\n }\n"); } - this.Write(" }\r\n"); + this.Write(" }\n"); if (!String.IsNullOrEmpty(NameSpace)) { - this.Write("}\r\n"); + this.Write("}\n"); } - this.Write("\r\n"); + this.Write("\n"); return this.GenerationEnvironment.ToString(); } - -public string NameSpace {get; set;} -public string ClassName {get; set;} -public IEnumerable FunctionContracts {get; set;} -public bool IsStatic {get; set;} = false; - + + public virtual void Initialize() { + } } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")] - internal class FunctionCallTemplateBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - public System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; + + public class FunctionCallTemplateBase { + + private global::System.Text.StringBuilder builder; + + private global::System.Collections.Generic.IDictionary session; + + private global::System.CodeDom.Compiler.CompilerErrorCollection errors; + + private string currentIndent = string.Empty; + + private global::System.Collections.Generic.Stack indents; + + private ToStringInstanceHelper _toStringHelper = new ToStringInstanceHelper(); + + public virtual global::System.Collections.Generic.IDictionary Session { + get { + return this.session; } - set - { - this.generationEnvironmentField = value; + set { + this.session = value; } } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + + public global::System.Text.StringBuilder GenerationEnvironment { + get { + if ((this.builder == null)) { + this.builder = new global::System.Text.StringBuilder(); } - return this.errorsField; + return this.builder; + } + set { + this.builder = value; } } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); + + protected global::System.CodeDom.Compiler.CompilerErrorCollection Errors { + get { + if ((this.errors == null)) { + this.errors = new global::System.CodeDom.Compiler.CompilerErrorCollection(); } - return this.indentLengthsField; + return this.errors; } } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; + + public string CurrentIndent { + get { + return this.currentIndent; } } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; + + private global::System.Collections.Generic.Stack Indents { + get { + if ((this.indents == null)) { + this.indents = new global::System.Collections.Generic.Stack(); + } + return this.indents; } } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); + + public ToStringInstanceHelper ToStringHelper { + get { + return this._toStringHelper; } } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; + + public void Error(string message) { + this.Errors.Add(new global::System.CodeDom.Compiler.CompilerError(null, -1, -1, null, message)); } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + + public void Warning(string message) { + global::System.CodeDom.Compiler.CompilerError val = new global::System.CodeDom.Compiler.CompilerError(null, -1, -1, null, message); + val.IsWarning = true; + this.Errors.Add(val); } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + + public string PopIndent() { + if ((this.Indents.Count == 0)) { + return string.Empty; + } + int lastPos = (this.currentIndent.Length - this.Indents.Pop()); + string last = this.currentIndent.Substring(lastPos); + this.currentIndent = this.currentIndent.Substring(0, lastPos); + return last; } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); + + public void PushIndent(string indent) { + this.Indents.Push(indent.Length); + this.currentIndent = (this.currentIndent + indent); } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); + + public void ClearIndent() { + this.currentIndent = string.Empty; + this.Indents.Clear(); } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); + + public void Write(string textToAppend) { + this.GenerationEnvironment.Append(textToAppend); } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; + + public void Write(string format, params object[] args) { + this.GenerationEnvironment.AppendFormat(format, args); } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; + + public void WriteLine(string textToAppend) { + this.GenerationEnvironment.Append(this.currentIndent); + this.GenerationEnvironment.AppendLine(textToAppend); } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; + + public void WriteLine(string format, params object[] args) { + this.GenerationEnvironment.Append(this.currentIndent); + this.GenerationEnvironment.AppendFormat(format, args); + this.GenerationEnvironment.AppendLine(); + } + + public class ToStringInstanceHelper { + + private global::System.IFormatProvider formatProvider = global::System.Globalization.CultureInfo.InvariantCulture; + + public global::System.IFormatProvider FormatProvider { + get { + return this.formatProvider; } - set - { - if ((value != null)) - { - this.formatProviderField = value; + set { + if ((value != null)) { + this.formatProvider = value; } } } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { + + public string ToStringWithCulture(object objectToConvert) { + if ((objectToConvert == null)) { throw new global::System.ArgumentNullException("objectToConvert"); } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); + global::System.Type type = objectToConvert.GetType(); + global::System.Type iConvertibleType = typeof(global::System.IConvertible); + if (iConvertibleType.IsAssignableFrom(type)) { + return ((global::System.IConvertible)(objectToConvert)).ToString(this.formatProvider); } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); + global::System.Reflection.MethodInfo methInfo = type.GetMethod("ToString", new global::System.Type[] { + iConvertibleType}); + if ((methInfo != null)) { + return ((string)(methInfo.Invoke(objectToConvert, new object[] { + this.formatProvider}))); } + return objectToConvert.ToString(); } } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion } - #endregion } diff --git a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt index 0d1b221c35c..c7a13ce1fe9 100644 --- a/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt +++ b/dotnet/src/AutoGen.SourceGenerator/Template/FunctionCallTemplate.tt @@ -63,7 +63,7 @@ namespace <#=NameSpace#> Name = @"<#=functionContract.Name#>", <#}#> <#if (functionContract.Description != null) {#> - Description = @"<#=functionContract.Description#>", + Description = @"<#=functionContract.Description.Replace("\"", "\"\"")#>", <#}#> <#if (functionContract.ReturnType != null) {#> ReturnType = typeof(<#=functionContract.ReturnType#>), @@ -81,7 +81,7 @@ namespace <#=NameSpace#> Name = @"<#=parameter.Name#>", <#}#> <#if (parameter.Description != null) {#> - Description = @"<#=parameter.Description#>", + Description = @"<#= parameter.Description.Replace("\"", "\"\"") #>", <#}#> <#if (parameter.Type != null) {#> ParameterType = typeof(<#=parameter.Type#>), diff --git a/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt b/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt index 0439febc52c..a5ffa3c8248 100644 --- a/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt +++ b/dotnet/test/AutoGen.SourceGenerator.Tests/ApprovalTests/FunctionCallTemplateTests.TestFunctionCallTemplate.approved.txt @@ -1,4 +1,4 @@ -//---------------------- +//---------------------- // // This code was generated by a tool. // diff --git a/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateEncodingTests.cs b/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateEncodingTests.cs new file mode 100644 index 00000000000..8ca6e31a8a4 --- /dev/null +++ b/dotnet/test/AutoGen.SourceGenerator.Tests/FunctionCallTemplateEncodingTests.cs @@ -0,0 +1,92 @@ +// Using directives +using System.Text.Json; // Needed for JsonSerializer +using Xunit; // Needed for Fact and Assert +using AutoGen.SourceGenerator.Template; // Needed for FunctionCallTemplate + +namespace AutoGen.SourceGenerator.Tests +{ + public class FunctionCallTemplateEncodingTests + { + private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + }; + + [Fact] + public void FunctionDescription_Should_Encode_DoubleQuotes() + { + // Arrange + var functionContracts = new List + { + new SourceGeneratorFunctionContract + { + Name = "TestFunction", + Description = "This is a \"test\" function", + Parameters = new SourceGeneratorParameterContract[] + { + new SourceGeneratorParameterContract + { + Name = "param1", + Description = "This is a \"parameter\" description", + Type = "string", + IsOptional = false + } + }, + ReturnType = "void" + } + }; + + var template = new FunctionCallTemplate + { + NameSpace = "TestNamespace", + ClassName = "TestClass", + FunctionContracts = functionContracts + }; + + // Act + var result = template.TransformText(); + + // Assert + Assert.Contains("Description = @\"This is a \"\"test\"\" function\"", result); + Assert.Contains("Description = @\"This is a \"\"parameter\"\" description\"", result); + } + + [Fact] + public void ParameterDescription_Should_Encode_DoubleQuotes() + { + // Arrange + var functionContracts = new List + { + new SourceGeneratorFunctionContract + { + Name = "TestFunction", + Description = "This is a test function", + Parameters = new SourceGeneratorParameterContract[] + { + new SourceGeneratorParameterContract + { + Name = "param1", + Description = "This is a \"parameter\" description", + Type = "string", + IsOptional = false + } + }, + ReturnType = "void" + } + }; + + var template = new FunctionCallTemplate + { + NameSpace = "TestNamespace", + ClassName = "TestClass", + FunctionContracts = functionContracts + }; + + // Act + var result = template.TransformText(); + + // Assert + Assert.Contains("Description = @\"This is a \"\"parameter\"\" description\"", result); + } + } +} \ No newline at end of file