openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
user/travisw/mcptoolfilter-serialization-draft

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/Custom/Embeddings/OpenAIEmbedding.cs

139lines · modecode

1using Microsoft.TypeSpec.Generator.Customizations;
2using System;
3using System.Buffers;
4using System.Buffers.Binary;
5using System.Buffers.Text;
6using System.ClientModel.Primitives;
7using System.Runtime.InteropServices;
8using System.Text.Json;
9
10namespace OpenAI.Embeddings;
11
12// CUSTOM: Renamed.
13/// <summary> Represents an embedding vector returned by embedding endpoint. </summary>
14[CodeGenType("Embedding")]
15[CodeGenSuppress("OpenAIEmbedding", typeof(int), typeof(BinaryData))]
16public partial class OpenAIEmbedding
17{
18 private ReadOnlyMemory<float> _vector;
19
20 // CUSTOM: Made private. The value of the embedding is publicly exposed as ReadOnlyMemory<float> instead of BinaryData.
21 [CodeGenMember("Embedding")]
22 private BinaryData EmbeddingProperty { get; }
23
24 // CUSTOM: Made private. This property does not add value in the context of a strongly-typed class.
25 [CodeGenMember("Object")]
26 private string Object { get; } = "embedding";
27
28 // CUSTOM: Added logic to handle additional custom properties.
29#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
30 internal OpenAIEmbedding(int index, BinaryData embeddingProperty, string @object, in JsonPatch patch)
31 {
32 Index = index;
33 EmbeddingProperty = embeddingProperty;
34 Object = @object;
35 _patch = patch;
36
37 // Handle additional custom properties.
38 _vector = ConvertToVectorOfFloats(embeddingProperty);
39 }
40#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
41
42 // CUSTOM: Entirely custom constructor used by the Model Factory.
43 internal OpenAIEmbedding(int index, ReadOnlyMemory<float> vector)
44 {
45 Index = index;
46 _vector = vector;
47 }
48
49 // CUSTOM: Added as a public, custom method. For slightly better performance, the embedding is always requested as a base64-encoded
50 // string and then manually transformed into a more user-friendly ReadOnlyMemory<float>.
51 /// <summary>
52 /// Gets the embedding vector as a list of floats.
53 /// </summary>
54 /// <returns>A read-only memory segment of floats representing the embedding vector.</returns>
55 public ReadOnlyMemory<float> ToFloats() => _vector;
56
57 private static ReadOnlyMemory<float> ConvertToVectorOfFloats(BinaryData binaryData)
58 {
59 ReadOnlySpan<byte> bytes = binaryData.ToMemory().Span;
60
61 // Remove quotes around base64 string.
62 if (bytes.Length > 2 && bytes[0] == (byte)'"' && bytes[bytes.Length - 1] == (byte)'"')
63 {
64 return ConvertFromBase64(bytes);
65 }
66 return ConvertFromJsonArray(binaryData);
67 }
68
69 private static ReadOnlyMemory<float> ConvertFromBase64(ReadOnlySpan<byte> base64)
70 {
71 base64 = base64.Slice(1, base64.Length - 2);
72
73 // Decode base64 string to bytes.
74 byte[] bytes = null;
75 try
76 {
77 bytes = ArrayPool<byte>.Shared.Rent(Base64.GetMaxDecodedFromUtf8Length(base64.Length));
78 OperationStatus status = Base64.DecodeFromUtf8(base64, bytes.AsSpan(), out int bytesConsumed, out int bytesWritten);
79 if (status != OperationStatus.Done || bytesWritten % sizeof(float) != 0)
80 {
81 ThrowInvalidData();
82 }
83
84 // Interpret bytes as floats
85 float[] vector = new float[bytesWritten / sizeof(float)];
86 bytes.AsSpan(0, bytesWritten).CopyTo(MemoryMarshal.AsBytes(vector.AsSpan()));
87 if (!BitConverter.IsLittleEndian)
88 {
89 Span<int> ints = MemoryMarshal.Cast<float, int>(vector.AsSpan());
90#if NET8_0_OR_GREATER
91 BinaryPrimitives.ReverseEndianness(ints, ints);
92#else
93 for (int i = 0; i < ints.Length; i++)
94 {
95 ints[i] = BinaryPrimitives.ReverseEndianness(ints[i]);
96 }
97#endif
98 }
99
100 return new ReadOnlyMemory<float>(vector);
101 }
102 finally
103 {
104 if (bytes is not null)
105 {
106 ArrayPool<byte>.Shared.Return(bytes);
107 }
108 }
109
110 static void ThrowInvalidData()
111 => throw new FormatException("The input is not a valid Base64 string of encoded floats.");
112 }
113
114 private static ReadOnlyMemory<float> ConvertFromJsonArray(BinaryData jsonArray)
115 {
116 using JsonDocument document = JsonDocument.Parse(jsonArray);
117 JsonElement array = document.RootElement;
118 if (array.ValueKind != JsonValueKind.Array)
119 {
120 throw new FormatException("The input is not a valid JSON array");
121 }
122
123 int arrayLength = array.GetArrayLength();
124 float[] vector = new float[arrayLength];
125 int index = 0;
126 try
127 {
128 foreach (JsonElement value in array.EnumerateArray())
129 {
130 vector[index++] = value.GetSingle();
131 }
132 return vector.AsMemory();
133 }
134 catch
135 {
136 throw new FormatException("The input is not a valid JSON array of float values");
137 }
138 }
139}
140