1010using  System . Runtime . CompilerServices ; 
1111using  System . Text ; 
1212using  System . Text . Json ; 
13+ using  System . Text . RegularExpressions ; 
1314using  System . Threading ; 
1415using  System . Threading . Tasks ; 
1516using  Microsoft . Shared . Diagnostics ; 
1920#pragma warning disable CA1308  // Normalize strings to uppercase 
2021#pragma warning disable EA0011  // Consider removing unnecessary conditional access operator (?) 
2122#pragma warning disable S1067  // Expressions should not be too complex 
23+ #pragma warning disable S2333  // Unnecessary partial 
2224#pragma warning disable S3011  // Reflection should not be used to increase accessibility of classes, methods, or fields 
2325#pragma warning disable SA1202  // Elements should be ordered by access 
26+ #pragma warning disable SA1203  // Constants should appear before fields 
2427#pragma warning disable SA1204  // Static elements should appear before instance elements 
2528
2629namespace  Microsoft . Extensions . AI ; 
2730
2831/// <summary>Represents an <see cref="IChatClient"/> for an OpenAI <see cref="OpenAIClient"/> or <see cref="ChatClient"/>.</summary> 
29- internal  sealed  class  OpenAIChatClient  :  IChatClient 
32+ internal  sealed  partial   class  OpenAIChatClient  :  IChatClient 
3033{ 
3134    // These delegate instances are used to call the internal overloads of CompleteChatAsync and CompleteChatStreamingAsync that accept 
3235    // a RequestOptions. These should be replaced once a better way to pass RequestOptions is available. 
@@ -157,10 +160,11 @@ internal static ChatTool ToOpenAIChatTool(AIFunctionDeclaration aiFunction, Chat
157160                input . Role  ==  OpenAIClientExtensions . ChatRoleDeveloper ) 
158161            { 
159162                var  parts  =  ToOpenAIChatContent ( input . Contents ) ; 
163+                 string ?  name  =  SanitizeAuthorName ( input . AuthorName ) ; 
160164                yield  return 
161-                     input . Role  ==  ChatRole . System  ?  new  SystemChatMessage ( parts )  {  ParticipantName  =  input . AuthorName  }  : 
162-                     input . Role  ==  OpenAIClientExtensions . ChatRoleDeveloper  ?  new  DeveloperChatMessage ( parts )  {  ParticipantName  =  input . AuthorName  }  : 
163-                     new  UserChatMessage ( parts )  {  ParticipantName  =  input . AuthorName  } ; 
165+                     input . Role  ==  ChatRole . System  ?  new  SystemChatMessage ( parts )  {  ParticipantName  =  name  }  : 
166+                     input . Role  ==  OpenAIClientExtensions . ChatRoleDeveloper  ?  new  DeveloperChatMessage ( parts )  {  ParticipantName  =  name  }  : 
167+                     new  UserChatMessage ( parts )  {  ParticipantName  =  name  } ; 
164168            } 
165169            else  if  ( input . Role  ==  ChatRole . Tool ) 
166170            { 
@@ -233,7 +237,7 @@ internal static ChatTool ToOpenAIChatTool(AIFunctionDeclaration aiFunction, Chat
233237                        new ( ChatMessageContentPart . CreateTextPart ( string . Empty ) ) ; 
234238                } 
235239
236-                 message . ParticipantName  =  input . AuthorName ; 
240+                 message . ParticipantName  =  SanitizeAuthorName ( input . AuthorName ) ; 
237241                message . Refusal  =  refusal ; 
238242
239243                yield  return  message ; 
@@ -568,7 +572,6 @@ private ChatCompletionOptions ToOpenAIOptions(ChatOptions? options)
568572        result . TopP  ??=  options . TopP ; 
569573        result . PresencePenalty  ??=  options . PresencePenalty ; 
570574        result . Temperature  ??=  options . Temperature ; 
571-         result . AllowParallelToolCalls  ??=  options . AllowMultipleToolCalls ; 
572575        result . Seed  ??=  options . Seed ; 
573576
574577        if  ( options . StopSequences  is  {  Count :  >  0  }  stopSequences ) 
@@ -589,6 +592,11 @@ private ChatCompletionOptions ToOpenAIOptions(ChatOptions? options)
589592                } 
590593            } 
591594
595+             if  ( result . Tools . Count  >  0 ) 
596+             { 
597+                 result . AllowParallelToolCalls  ??=  options . AllowMultipleToolCalls ; 
598+             } 
599+ 
592600            if  ( result . ToolChoice  is  null  &&  result . Tools . Count  >  0 ) 
593601            { 
594602                switch  ( options . ToolMode ) 
@@ -749,11 +757,41 @@ internal static void ConvertContentParts(ChatMessageContent content, IList<AICon
749757            _ =>  new  ChatFinishReason ( s ) , 
750758        } ; 
751759
760+     /// <summary>Sanitizes the author name to be appropriate for including as an OpenAI participant name.</summary> 
761+     private  static string ?  SanitizeAuthorName ( string ?  name ) 
762+     { 
763+         if  ( name  is  not null ) 
764+         { 
765+             const  int  MaxLength  =  64 ; 
766+ 
767+             name  =  InvalidAuthorNameRegex ( ) . Replace ( name ,  string . Empty ) ; 
768+             if  ( name . Length  ==  0 ) 
769+             { 
770+                 name  =  null ; 
771+             } 
772+             else  if  ( name . Length  >  MaxLength ) 
773+             { 
774+                 name  =  name . Substring ( 0 ,  MaxLength ) ; 
775+             } 
776+         } 
777+ 
778+         return  name ; 
779+     } 
780+ 
752781    /// <summary>POCO representing function calling info. Used to concatenation information for a single function call from across multiple streaming updates.</summary> 
753782    private  sealed  class  FunctionCallInfo 
754783    { 
755784        public  string ?  CallId ; 
756785        public  string ?  Name ; 
757786        public  StringBuilder ?  Arguments ; 
758787    } 
788+ 
789+     private  const  string  InvalidAuthorNamePattern  =  @"[^a-zA-Z0-9_]+" ; 
790+ #if NET 
791+     [ GeneratedRegex ( InvalidAuthorNamePattern ) ] 
792+     private  static partial  Regex  InvalidAuthorNameRegex ( ) ; 
793+ #else
794+     private  static Regex  InvalidAuthorNameRegex ( )  =>  _invalidAuthorNameRegex ; 
795+     private  static readonly  Regex  _invalidAuthorNameRegex  =  new ( InvalidAuthorNamePattern ,  RegexOptions . Compiled ) ; 
796+ #endif
759797} 
0 commit comments