Skip to content

Commit 8b34d7c

Browse files
authored
Generate ListChatCompletions with pagination (#549)
1 parent 0042302 commit 8b34d7c

File tree

60 files changed

+1899
-23
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1899
-23
lines changed

api/OpenAI.net8.0.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,14 @@ public class ChatClient {
14301430
public virtual Task<ClientResult> GetChatCompletionAsync(string completionId, RequestOptions options);
14311431
[Experimental("OPENAI001")]
14321432
public virtual Task<ClientResult<ChatCompletion>> GetChatCompletionAsync(string completionId, CancellationToken cancellationToken = default);
1433+
[Experimental("OPENAI001")]
1434+
public virtual CollectionResult<ChatCompletion> GetChatCompletions(ChatCompletionCollectionOptions options = null, CancellationToken cancellationToken = default);
1435+
[Experimental("OPENAI001")]
1436+
public virtual CollectionResult GetChatCompletions(string after, int? limit, string order, IDictionary<string, string> metadata, string model, RequestOptions options);
1437+
[Experimental("OPENAI001")]
1438+
public virtual AsyncCollectionResult<ChatCompletion> GetChatCompletionsAsync(ChatCompletionCollectionOptions options = null, CancellationToken cancellationToken = default);
1439+
[Experimental("OPENAI001")]
1440+
public virtual AsyncCollectionResult GetChatCompletionsAsync(string after, int? limit, string order, IDictionary<string, string> metadata, string model, RequestOptions options);
14331441
}
14341442
public class ChatCompletion : IJsonModel<ChatCompletion>, IPersistableModel<ChatCompletion> {
14351443
[Experimental("OPENAI001")]
@@ -1460,6 +1468,33 @@ public class ChatCompletion : IJsonModel<ChatCompletion>, IPersistableModel<Chat
14601468
protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options);
14611469
}
14621470
[Experimental("OPENAI001")]
1471+
public class ChatCompletionCollectionOptions : IJsonModel<ChatCompletionCollectionOptions>, IPersistableModel<ChatCompletionCollectionOptions> {
1472+
public string AfterId { get; set; }
1473+
public IDictionary<string, string> Metadata { get; }
1474+
public string Model { get; set; }
1475+
public ChatCompletionCollectionOrder? Order { get; set; }
1476+
public int? PageSizeLimit { get; set; }
1477+
protected virtual ChatCompletionCollectionOptions JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options);
1478+
protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options);
1479+
protected virtual ChatCompletionCollectionOptions PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options);
1480+
protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options);
1481+
}
1482+
[Experimental("OPENAI001")]
1483+
public readonly partial struct ChatCompletionCollectionOrder : IEquatable<ChatCompletionCollectionOrder> {
1484+
public ChatCompletionCollectionOrder(string value);
1485+
public static ChatCompletionCollectionOrder Ascending { get; }
1486+
public static ChatCompletionCollectionOrder Descending { get; }
1487+
public readonly bool Equals(ChatCompletionCollectionOrder other);
1488+
[EditorBrowsable(EditorBrowsableState.Never)]
1489+
public override readonly bool Equals(object obj);
1490+
[EditorBrowsable(EditorBrowsableState.Never)]
1491+
public override readonly int GetHashCode();
1492+
public static bool operator ==(ChatCompletionCollectionOrder left, ChatCompletionCollectionOrder right);
1493+
public static implicit operator ChatCompletionCollectionOrder(string value);
1494+
public static bool operator !=(ChatCompletionCollectionOrder left, ChatCompletionCollectionOrder right);
1495+
public override readonly string ToString();
1496+
}
1497+
[Experimental("OPENAI001")]
14631498
public class ChatCompletionDeletionResult : IJsonModel<ChatCompletionDeletionResult>, IPersistableModel<ChatCompletionDeletionResult> {
14641499
public string ChatCompletionId { get; }
14651500
public bool Deleted { get; }

api/OpenAI.netstandard2.0.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,10 @@ public class ChatClient {
12851285
public virtual ClientResult<ChatCompletion> GetChatCompletion(string completionId, CancellationToken cancellationToken = default);
12861286
public virtual Task<ClientResult> GetChatCompletionAsync(string completionId, RequestOptions options);
12871287
public virtual Task<ClientResult<ChatCompletion>> GetChatCompletionAsync(string completionId, CancellationToken cancellationToken = default);
1288+
public virtual CollectionResult<ChatCompletion> GetChatCompletions(ChatCompletionCollectionOptions options = null, CancellationToken cancellationToken = default);
1289+
public virtual CollectionResult GetChatCompletions(string after, int? limit, string order, IDictionary<string, string> metadata, string model, RequestOptions options);
1290+
public virtual AsyncCollectionResult<ChatCompletion> GetChatCompletionsAsync(ChatCompletionCollectionOptions options = null, CancellationToken cancellationToken = default);
1291+
public virtual AsyncCollectionResult GetChatCompletionsAsync(string after, int? limit, string order, IDictionary<string, string> metadata, string model, RequestOptions options);
12881292
}
12891293
public class ChatCompletion : IJsonModel<ChatCompletion>, IPersistableModel<ChatCompletion> {
12901294
public IReadOnlyList<ChatMessageAnnotation> Annotations { get; }
@@ -1308,6 +1312,31 @@ public class ChatCompletion : IJsonModel<ChatCompletion>, IPersistableModel<Chat
13081312
protected virtual ChatCompletion PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options);
13091313
protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options);
13101314
}
1315+
public class ChatCompletionCollectionOptions : IJsonModel<ChatCompletionCollectionOptions>, IPersistableModel<ChatCompletionCollectionOptions> {
1316+
public string AfterId { get; set; }
1317+
public IDictionary<string, string> Metadata { get; }
1318+
public string Model { get; set; }
1319+
public ChatCompletionCollectionOrder? Order { get; set; }
1320+
public int? PageSizeLimit { get; set; }
1321+
protected virtual ChatCompletionCollectionOptions JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options);
1322+
protected virtual void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options);
1323+
protected virtual ChatCompletionCollectionOptions PersistableModelCreateCore(BinaryData data, ModelReaderWriterOptions options);
1324+
protected virtual BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options);
1325+
}
1326+
public readonly partial struct ChatCompletionCollectionOrder : IEquatable<ChatCompletionCollectionOrder> {
1327+
public ChatCompletionCollectionOrder(string value);
1328+
public static ChatCompletionCollectionOrder Ascending { get; }
1329+
public static ChatCompletionCollectionOrder Descending { get; }
1330+
public readonly bool Equals(ChatCompletionCollectionOrder other);
1331+
[EditorBrowsable(EditorBrowsableState.Never)]
1332+
public override readonly bool Equals(object obj);
1333+
[EditorBrowsable(EditorBrowsableState.Never)]
1334+
public override readonly int GetHashCode();
1335+
public static bool operator ==(ChatCompletionCollectionOrder left, ChatCompletionCollectionOrder right);
1336+
public static implicit operator ChatCompletionCollectionOrder(string value);
1337+
public static bool operator !=(ChatCompletionCollectionOrder left, ChatCompletionCollectionOrder right);
1338+
public override readonly string ToString();
1339+
}
13111340
public class ChatCompletionDeletionResult : IJsonModel<ChatCompletionDeletionResult>, IPersistableModel<ChatCompletionDeletionResult> {
13121341
public string ChatCompletionId { get; }
13131342
public bool Deleted { get; }

codegen/README.MD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Debugging the generator
2+
3+
To configure VS Code for debugging the generator, specifically visitors, add the following to your `launch.json` in the root of the workspace
4+
5+
```json
6+
{
7+
"version": "0.2.0",
8+
"configurations": [
9+
{
10+
"name": "Debug OpenAI Library Plugin",
11+
"type": "coreclr",
12+
"request": "launch",
13+
"program": "dotnet",
14+
"args": [
15+
"${workspaceFolder}/codegen/dist/generator/Microsoft.TypeSpec.Generator.dll",
16+
"${workspaceFolder}",
17+
"-g",
18+
"OpenAILibraryGenerator"
19+
],
20+
}
21+
]
22+
}
23+
```

codegen/generator/src/OpenAILibraryGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ protected override void Configure()
3838
AddVisitor(new ModelSerializationVisitor());
3939
AddVisitor(new ExperimentalAttributeVisitor());
4040
AddVisitor(new ModelDirectoryVisitor());
41+
AddVisitor(new PaginationVisitor());
42+
AddVisitor(new MetadataQueryParamVisitor());
4143
}
4244
}
4345
}

codegen/generator/src/Visitors/ExplicitConversionFromClientResultVisitor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ protected override MethodProvider VisitMethod(MethodProvider method)
1515
if (method.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit) &&
1616
method.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator) &&
1717
method.Signature.Parameters.Count == 1 &&
18-
method.Signature.Parameters[0].Type.Name == nameof(ClientResult))
18+
method.Signature.Parameters[0].Type.Name == nameof(ClientResult) &&
19+
!method.EnclosingType.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal))
1920
{
2021
return null;
2122
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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

Comments
 (0)