Skip to content

Commit

Permalink
Cherry pick branch 'genexuslabs:embedding-data-type' into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
claudiamurialdo authored and Beta Bot committed Nov 14, 2024
1 parent abec843 commit f26685f
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 99 deletions.
16 changes: 8 additions & 8 deletions dotnet/src/dotnetcore/GxClasses/Domain/GxEmbedding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static GxEmbedding GenerateEmbedding(GxEmbedding embeddingInfo, string te
{
try
{
ReadOnlyMemory<float> embedding = AIServiceFactory.Instance.GenerateEmbeddingAsync(embeddingInfo.Model, embeddingInfo.Dimensions, text).GetAwaiter().GetResult();
ReadOnlyMemory<float> embedding = AIEmbeddingactory.Instance.GenerateEmbeddingAsync(embeddingInfo.Model, embeddingInfo.Dimensions, text).GetAwaiter().GetResult();
return new GxEmbedding(embedding, embeddingInfo.Model, embeddingInfo.Dimensions);
}
catch (Exception ex)
Expand All @@ -51,18 +51,18 @@ public static GxEmbedding GenerateEmbedding(GxEmbedding embeddingInfo, string te

internal ReadOnlyMemory<float> Data => _embedding;
}
internal interface IAIService
internal interface IEmbeddingService
{
Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string model, int dimensions, string input);
}
internal class AIServiceFactory
internal class AIEmbeddingactory
{
private static readonly IGXLogger log = GXLoggerFactory.GetLogger<AIServiceFactory>();
private static readonly IGXLogger log = GXLoggerFactory.GetLogger<AIEmbeddingactory>();

private static volatile IAIService instance;
private static volatile IEmbeddingService instance;
private static object syncRoot = new object();
private const string AI_PROVIDER = "GeneXus.AI.Core, GxAI, Version=10.1.0.0, Culture=neutral, PublicKeyToken=null";
public static IAIService Instance
private const string AI_PROVIDER = "GeneXus.AI.EmbeddingService, GxAI, Version=10.1.0.0, Culture=neutral, PublicKeyToken=null";
public static IEmbeddingService Instance
{
get
{
Expand All @@ -76,7 +76,7 @@ public static IAIService Instance
try
{
Type type = AssemblyLoader.GetType(AI_PROVIDER);
instance = (IAIService)Activator.CreateInstance(type);
instance = (IEmbeddingService)Activator.CreateInstance(type);
}
catch (Exception e)
{
Expand Down
2 changes: 1 addition & 1 deletion dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0" PrivateAssets="All" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.7.0" PrivateAssets="All" />
<PackageReference Include="Pgvector" Version="0.3.0" />
<PackageReference Include="Pgvector" Version="0.3.0" PrivateAssets="All" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="MySqlConnector" Version="2.2.3" PrivateAssets="All"/>
Expand Down
13 changes: 13 additions & 0 deletions dotnet/src/dotnetcore/Providers/AI/Model/GXAgent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using GeneXus.Procedure;
using GeneXus.Utils;

namespace GeneXus.AI
{
internal class GXAgent : GXProcedure
{
protected string CallAssistant( string caller, GXProperties properties, CallResult response)
{
return string.Empty;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,15 @@
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using GeneXus.Configuration;
using OpenAI;
using OpenAI.Chat;
using OpenAI.Embeddings;
namespace GeneXus.Utils
namespace GeneXus.AI
{
internal class OpenAiService
{
protected OpenAIClient _openAIClient;
protected string API_KEY;
protected virtual string DEFAULT_PROVIDER => "https://api.saia.ai/embeddings";
protected virtual string DEFAULT_API_KEY => "apitokenfortest_";
protected const string AI_PROVIDER = "AI_PROVIDER";
protected const string AI_PROVIDER_API_KEY = "AI_PROVIDER_API_KEY";
internal OpenAiService()
{
string val;
Uri providerUri = new Uri(DEFAULT_PROVIDER);
API_KEY = DEFAULT_API_KEY;
if (Config.GetValueOf(AI_PROVIDER, out val))
{
providerUri = new Uri(val);
}
if (Config.GetValueOf(AI_PROVIDER_API_KEY, out val))
{
API_KEY = val;
}
OpenAIClientOptions options = new OpenAIClientOptions()
{
Endpoint = providerUri
};

_openAIClient = new OpenAIClient(new ApiKeyCredential(API_KEY), options);
}
public async Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string model, int dimensions, string input)
{
OpenAIEmbeddingCollection data = await GenerateEmbeddingAsync(model, dimensions, new List<string> { input });
return data.First().ToFloats();
}
public async Task<OpenAIEmbeddingCollection> GenerateEmbeddingAsync(string model, int dimensions, IEnumerable<string> input)
{
EmbeddingClient client = _openAIClient.GetEmbeddingClient(model);
EmbeddingGenerationOptions options = new EmbeddingGenerationOptions() { Dimensions = dimensions };
ClientResult<OpenAIEmbeddingCollection> clientResult = await client.GenerateEmbeddingsAsync(input, options);

return clientResult.Value;
}

}
internal class AgentService
{
private HttpClient _httpClient;
Expand Down Expand Up @@ -132,56 +87,28 @@ internal async Task<ChatCompletion> Assistant(string userMessage, string context
return response.Value;

}

}
internal class AIService : IAIService
{
//static readonly IGXLogger log = GXLoggerFactory.GetLogger<AIService>();
private static volatile OpenAiService m_EmbeddingInstance;
private static volatile AgentService m_AgentInstance;
private static volatile AgentService m_Instance;
private static object m_SyncRoot = new Object();

internal static OpenAiService EmbeddingHandlerInstance
{
get
{
if (m_EmbeddingInstance == null)
{
lock (m_SyncRoot)
{
if (m_EmbeddingInstance == null)
m_EmbeddingInstance = new OpenAiService();
}
}
return m_EmbeddingInstance;
}
}
internal static AgentService AgentHandlerInstance
{
get
{
if (m_AgentInstance == null)
if (m_Instance == null)
{
lock (m_SyncRoot)
{
if (m_AgentInstance == null)
m_AgentInstance = new AgentService();
if (m_Instance == null)
m_Instance = new AgentService();
}
}
return m_AgentInstance;
return m_Instance;
}
}
public async Task<OpenAIEmbeddingCollection> GenerateEmbeddingAsync(string model, int dimensions, IEnumerable<string> input)
{
return await EmbeddingHandlerInstance.GenerateEmbeddingAsync(model, dimensions, input);
}

public Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string model, int dimensions, string input)
{
return EmbeddingHandlerInstance.GenerateEmbeddingAsync(model, dimensions, input);
}

}
public class Variable

public class Variable
{
public string Key { get; set; }
public string Value { get; set; }
Expand Down
75 changes: 75 additions & 0 deletions dotnet/src/dotnetcore/Providers/AI/Services/EmbeddingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.ClientModel;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GeneXus.Configuration;
using GeneXus.Utils;
using OpenAI;
using OpenAI.Embeddings;
namespace GeneXus.AI
{
internal class EmbeddingService : IEmbeddingService
{
protected OpenAIClient _openAIClient;
protected string API_KEY;
protected virtual string DEFAULT_PROVIDER => "https://api.saia.ai/embeddings";
protected virtual string DEFAULT_API_KEY => "apitokenfortest_";
protected const string AI_PROVIDER = "AI_PROVIDER";
protected const string AI_PROVIDER_API_KEY = "AI_PROVIDER_API_KEY";
private static volatile EmbeddingService m_instance;
private static object m_SyncRoot = new Object();

internal EmbeddingService()
{
string val;
Uri providerUri = new Uri(DEFAULT_PROVIDER);
API_KEY = DEFAULT_API_KEY;
if (Config.GetValueOf(AI_PROVIDER, out val))
{
providerUri = new Uri(val);
}
if (Config.GetValueOf(AI_PROVIDER_API_KEY, out val))
{
API_KEY = val;
}
OpenAIClientOptions options = new OpenAIClientOptions()
{
Endpoint = providerUri
};

_openAIClient = new OpenAIClient(new ApiKeyCredential(API_KEY), options);
}
public async Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string model, int dimensions, string input)
{
OpenAIEmbeddingCollection data = await GenerateEmbeddingAsync(model, dimensions, new List<string> { input });
return data.First().ToFloats();
}
async Task<OpenAIEmbeddingCollection> GenerateEmbeddingAsync(string model, int dimensions, IEnumerable<string> input)
{
EmbeddingClient client = _openAIClient.GetEmbeddingClient(model);
EmbeddingGenerationOptions options = new EmbeddingGenerationOptions() { Dimensions = dimensions };
ClientResult<OpenAIEmbeddingCollection> clientResult = await client.GenerateEmbeddingsAsync(input, options);

return clientResult.Value;
}
internal static EmbeddingService Instance
{
get
{
if (m_instance == null)
{
lock (m_SyncRoot)
{
if (m_instance == null)
m_instance = new EmbeddingService();
}
}
return m_instance;
}
}


}

}
21 changes: 14 additions & 7 deletions dotnet/src/dotnetframework/GxClasses/Data/GXDataMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -872,13 +872,9 @@ public static object GetCopyFrom(object value)
return memStream;
}
#if NETCORE
else if (type == typeof(Pgvector.Vector))
else if (type.FullName == PGVECTOR_VECTOR_TYPE_NAME) // Avoid referencing Pgvector type if datastore is not Npgsql; prevents loading Pgvector DLL unnecessarily
{
Pgvector.Vector vector = (Pgvector.Vector)value;
float[] copyArray = new float[vector.Memory.Length];
vector.Memory.Span.CopyTo(copyArray);
Pgvector.Vector copyVector = new Pgvector.Vector(new ReadOnlyMemory<float>(copyArray));
return copyVector;
return CopyPgVectorValue(value);
}
#endif
else
Expand Down Expand Up @@ -907,8 +903,19 @@ public static object GetCopyFrom(object value)
}
}
}
#if NETCORE
const string PGVECTOR_VECTOR_TYPE_NAME = "Pgvector.Vector";
private static object CopyPgVectorValue(object value)
{
Pgvector.Vector vector = (Pgvector.Vector)value;
float[] copyArray = new float[vector.Memory.Length];
vector.Memory.Span.CopyTo(copyArray);
Pgvector.Vector copyVector = new Pgvector.Vector(new ReadOnlyMemory<float>(copyArray));
return copyVector;

#endregion
}
#endif
#endregion
}

}
2 changes: 1 addition & 1 deletion dotnet/test/DotNetCoreUnitTest/Domain/GxEmbeddingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class GxEmbeddingTest
[Fact(Skip ="Local test")]
public async Task EmbeddingTest()
{
IAIService embeddingService = AIServiceFactory.Instance;
IEmbeddingService embeddingService = AIEmbeddingactory.Instance;
ReadOnlyMemory<float> embedding = await embeddingService.GenerateEmbeddingAsync("openai/text-embedding-3-small", 512, "Hello World");
Assert.False(embedding.IsEmpty);
}
Expand Down

0 comments on commit f26685f

Please sign in to comment.