1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using Microsoft . TypeSpec . Generator . ClientModel ;
5+ using Microsoft . TypeSpec . Generator . Expressions ;
6+ using Microsoft . TypeSpec . Generator . Primitives ;
7+ using Microsoft . TypeSpec . Generator . Providers ;
8+ using Microsoft . TypeSpec . Generator . Snippets ;
9+ using Microsoft . TypeSpec . Generator . Statements ;
10+ using static OpenAILibraryPlugin . Visitors . VisitorHelpers ;
11+
12+ namespace OpenAILibraryPlugin . Visitors ;
13+
14+ /// <summary>
15+ /// This visitor fixes up usage of the metadata query parameter into the proper format.
16+ /// </summary>
17+ public class MetadataQueryParamVisitor : ScmLibraryVisitor
18+ {
19+
20+ private static readonly string [ ] _chatParamsToReplace = [ "after" , "before" , "limit" , "order" , "model" , "metadata" ] ;
21+
22+ /// <summary>
23+ /// Visits Create*Request methods to modify how metadata query parameters are handled.
24+ /// It replaces the following statements:
25+ /// <code>
26+ /// List<object> list = new List<object>();
27+ /// foreach (var @param in metadata)
28+ /// {
29+ /// uri.AppendQuery($"metadata[{@param.Key}]", @param.Value, true);
30+ /// list.Add(@param.Key);
31+ /// list.Add(@param.Value);
32+ /// }
33+ /// uri.AppendQueryDelimited("metadata", list, ",", null, true);
34+ /// </code>
35+ /// with:
36+ /// <code>
37+ /// foreach (var @param in metadata)
38+ /// {
39+ /// uri.AppendQuery($"metadata[{@param.Key}]", @param.Value, true);
40+ /// }
41+ /// </summary>
42+ /// <param name="method"></param>
43+ /// <returns></returns>
44+ protected override MethodProvider ? VisitMethod ( MethodProvider method )
45+ {
46+ // Check if the method is one of the Create*Request methods and has a signature that takes a metadata parameter like IDictionary<string, string> metadata
47+ if ( method . Signature . Name . StartsWith ( "Create" ) && method . Signature . Name . EndsWith ( "Request" ) &&
48+ method . Signature . Parameters . Any ( p => p . Type . IsDictionary && p . Name == "metadata" ) )
49+ {
50+ ValueExpression ? uri = null ;
51+ var statements = method . BodyStatements ? . ToList ( ) ?? new List < MethodBodyStatement > ( ) ;
52+ VisitExplodedMethodBodyStatements (
53+ statements ! ,
54+ statement =>
55+ {
56+ // Check if the statement is an assignment to a variable named "uri"
57+ // Capture it if so
58+ if ( statement is ExpressionStatement expressionStatement &&
59+ expressionStatement . Expression is AssignmentExpression assignmentExpression &&
60+ assignmentExpression . Variable is DeclarationExpression declarationExpression &&
61+ declarationExpression . Variable is VariableExpression variableExpression &&
62+ variableExpression . Declaration . RequestedName == "uri" )
63+ {
64+ uri = variableExpression ;
65+ }
66+ // Try to remove the unnecessary list declaration
67+ if ( statement is ExpressionStatement expressionStatement2 &&
68+ expressionStatement2 . Expression is AssignmentExpression assignmentExpression2 &&
69+ assignmentExpression2 . Variable is DeclarationExpression declarationExpression2 &&
70+ declarationExpression2 . Variable is VariableExpression variableExpression2 &&
71+ variableExpression2 . Declaration . RequestedName == "list" &&
72+ variableExpression2 . Type . IsCollection && variableExpression2 . Type . IsGenericType )
73+ {
74+ // Remove the list declaration
75+ return new SingleLineCommentStatement ( "Plugin customization: remove unnecessary list declaration" ) ;
76+ }
77+
78+ if ( uri is not null &&
79+ statement is ForEachStatement foreachStatement &&
80+ foreachStatement . Enumerable is DictionaryExpression dictionaryExpression &&
81+ dictionaryExpression . Original is VariableExpression variable &&
82+ variable . Declaration . RequestedName == "metadata" )
83+ {
84+ var formatString = new FormattableStringExpression ( "metadata[{0}]" , [ foreachStatement . ItemVariable . Property ( "Key" ) ] ) ;
85+ var appendQueryStatement = uri . Invoke ( "AppendQuery" , [ formatString , foreachStatement . ItemVariable . Property ( "Value" ) , Snippet . True ] ) ;
86+ foreachStatement . Body . Clear ( ) ;
87+ foreachStatement . Body . Add ( new SingleLineCommentStatement ( "Plugin customization: Properly handle metadata query parameters" ) ) ;
88+ foreachStatement . Body . Add ( appendQueryStatement . Terminate ( ) ) ;
89+ }
90+
91+ // Remove the call to AppendQueryDelimited for metadata
92+ if ( statement is ExpressionStatement expressionStatement3 &&
93+ expressionStatement3 . Expression is InvokeMethodExpression invokeMethodExpression &&
94+ invokeMethodExpression . MethodName == "AppendQueryDelimited" &&
95+ invokeMethodExpression . Arguments . Count == 5 &&
96+ invokeMethodExpression . Arguments [ 0 ] . ToDisplayString ( ) == "\" metadata\" " )
97+ {
98+ return new SingleLineCommentStatement ( "Plugin customization: remove unnecessary AppendQueryDelimited for metadata" ) ;
99+ }
100+ return statement ;
101+ } ) ;
102+
103+ // Rebuild the method body with the modified statements
104+ method . Update ( bodyStatements : statements ) ;
105+ }
106+
107+ return base . VisitMethod ( method ) ;
108+ }
109+ }
0 commit comments