From 91acd6c110c0584d485ca4314ae3538eff465524 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Wed, 6 Mar 2024 21:38:40 +0400 Subject: [PATCH] fix: Fixed Together issues. --- README.md | 23 +-- src/Meta/test/OpenAiTests.cs | 89 +++++----- src/Meta/test/ReadmeTests.cs | 166 ++++++++---------- .../Abstractions/src/Chat/ChatRequest.cs | 10 +- .../Anyscale/src/AnyscaleProvider.cs | 2 +- .../src/Embedding/OpenAiEmbeddingSettings.cs | 5 +- 6 files changed, 139 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index c7ecc746..e18f88a0 100644 --- a/README.md +++ b/README.md @@ -60,25 +60,26 @@ var answer = await llm.GenerateAsync( """, cancellationToken: CancellationToken.None).ConfigureAwait(false); Console.WriteLine($"LLM answer: {answer}"); // The cloaked figure. -Console.WriteLine($"LLM usage: {llm.Usage}"); -Console.WriteLine($"Embeddings usage: {embeddings.Usage}"); // 2. Chains -var promptText = +var promptTemplate = @"Use the following pieces of context to answer the question at the end. If the answer is not in context then just say that you don't know, don't try to make up an answer. Keep the answer as short as possible. Always quote the context in your answer. {context} Question: {text} Helpful Answer:"; var chain = - Set("Who was drinking a unicorn blood?") // set the question - | RetrieveDocuments(index, amount: 5) // take 5 most similar documents - | StuffDocuments(outputKey: "context") // combine documents together and put them into context - | Template(promptText) // replace context and question in the prompt with their values - | LLM(llm.UseConsoleForDebug()); // send the result to the language model -var chainAnswer = await chain.Run("text"); // get chain result - -Console.WriteLine("Answer:"+ chainAnswer); // print the result + Set("Who was drinking a unicorn blood?") // set the question (default key is "text") + | RetrieveSimilarDocuments(index, amount: 5) // take 5 most similar documents + | CombineDocuments(outputKey: "context") // combine documents together and put them into context + | Template(promptTemplate) // replace context and question in the prompt with their values + | LLM(llm.UseConsoleForDebug()); // send the result to the language model +var chainAnswer = await chain.Run("text"); // get chain result + +Console.WriteLine("Chain Answer:"+ chainAnswer); // print the result + +Console.WriteLine($"LLM usage: {llm.Usage}"); // Print usage and price +Console.WriteLine($"Embeddings usage: {embeddings.Usage}"); // Print usage and price ``` ## Contributors diff --git a/src/Meta/test/OpenAiTests.cs b/src/Meta/test/OpenAiTests.cs index 76133488..abff5112 100644 --- a/src/Meta/test/OpenAiTests.cs +++ b/src/Meta/test/OpenAiTests.cs @@ -1,41 +1,48 @@ -// using FluentAssertions; -// using LangChain.Base; -// using LangChain.Callback; -// using LangChain.Chains.LLM; -// using LangChain.LLMS.OpenAi; -// using LangChain.Prompts; -// using LangChain.Schema; -// -// namespace LangChain.IntegrationTests; -// -// public class OpenAiTests -// { -// [Fact] -// public async Task TestOpenAi_WithValidInput_ShouldReturnResponse() -// { -// var model = new OpenAi(); -// -// var result = await model.Call("What is a good name for a company that sells colourful socks?"); -// -// result.Should().NotBeEmpty(); -// } -// -// [Fact] -// public async Task TestOpenAi_WithChain_ShouldReturnResponse() -// { -// var llm = new OpenAi(); -// -// var template = "What is a good name for a company that makes {product}?"; -// var prompt = new PromptTemplate(new PromptTemplateInput(template, new List(1){"product"})); -// -// var chain = new LlmChain(new LlmChainInput(llm, prompt)); -// -// var result = await chain.Call(new ChainValues(new Dictionary(1) -// { -// { "product", "colourful socks" } -// })); -// -// // The result is an object with a `text` property. -// result.Value["text"].ToString().Should().NotBeEmpty(); -// } -// } \ No newline at end of file +using LangChain.Chains.LLM; +using LangChain.Prompts; +using LangChain.Providers.OpenAI.Predefined; +using LangChain.Schema; + +namespace LangChain.IntegrationTests; + +[TestFixture] +[Explicit] +public class OpenAiTests +{ + [Test] + public async Task TestOpenAi_WithValidInput_ShouldReturnResponse() + { + var model = new Gpt35TurboModel( + Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? + throw new InconclusiveException("OPENAI_API_KEY is not set")); + + string result = await model.GenerateAsync("What is a good name for a company that sells colourful socks?"); + + result.Should().NotBeEmpty(); + + Console.WriteLine(result); + } + + [Test] + public async Task TestOpenAi_WithChain_ShouldReturnResponse() + { + var llm = new Gpt35TurboModel( + Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? + throw new InconclusiveException("OPENAI_API_KEY is not set")); + + var template = "What is a good name for a company that makes {product}?"; + var prompt = new PromptTemplate(new PromptTemplateInput(template, new List(1){"product"})); + + var chain = new LlmChain(new LlmChainInput(llm, prompt)); + + var result = await chain.CallAsync(new ChainValues(new Dictionary(1) + { + { "product", "colourful socks" } + })); + + // The result is an object with a `text` property. + result.Value["text"].ToString().Should().NotBeEmpty(); + + Console.WriteLine(result.Value["text"]); + } +} \ No newline at end of file diff --git a/src/Meta/test/ReadmeTests.cs b/src/Meta/test/ReadmeTests.cs index 6010b685..9d59adea 100644 --- a/src/Meta/test/ReadmeTests.cs +++ b/src/Meta/test/ReadmeTests.cs @@ -1,20 +1,20 @@ using LangChain.Databases; using LangChain.Databases.InMemory; using LangChain.Sources; -using LangChain.Indexes; using LangChain.Providers; +using LangChain.Providers.Anyscale; +using LangChain.Providers.Anyscale.Predefined; using LangChain.Providers.OpenAI; using LangChain.Providers.OpenAI.Predefined; -using LangChain.Splitters.Text; using LangChain.VectorStores; using static LangChain.Chains.Chain; namespace LangChain.IntegrationTests; [TestFixture] +[Explicit] public class ReadmeTests { - [Explicit] [Test] public async Task Chains1() { @@ -61,71 +61,11 @@ Human will provide you with sentence about pet. You need to answer with pet name result.Should().Be("Bob"); } - /// - /// Price to run from zero(create embedding and request to LLM): 0,015$ - /// Price to re-run if database is exists: 0,0004$ - /// - /// - [Explicit] - [Test] - public async Task RagWithOpenAiUsingChains() - { - var gpt35 = new Gpt35TurboModel( - Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? - throw new InconclusiveException("OPENAI_API_KEY is not set")); - var embeddings = new TextEmbeddingV3SmallModel( - Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? - throw new InconclusiveException("OPENAI_API_KEY is not set")); - - // Create database with embeddings from Harry Potter book pdf - if (!File.Exists("vectors.db")) - { - await using var stream = H.Resources.HarryPotterBook1_pdf.AsStream(); - var documents = await PdfPigPdfSource.FromStreamAsync(stream); - - await SQLiteVectorStore.CreateIndexFromDocuments( - embeddings: embeddings, - documents: documents, - filename: "vectors.db", - tableName: "vectors", - textSplitter: new RecursiveCharacterTextSplitter( - chunkSize: 200, - chunkOverlap: 50)); - } - - var vectorStore = new SQLiteVectorStore("vectors.db", "vectors", embeddings); - var index = new VectorStoreIndexWrapper(vectorStore); - var chain = - // set the question - Set("Who was drinking a unicorn blood?", outputKey: "question") | - // take 5 most similar documents - RetrieveSimilarDocuments(index, inputKey: "question", outputKey: "documents", amount: 5) | - // combine documents together and put them into context - CombineDocuments(inputKey: "documents", outputKey: "context") | - // replace context and question in the prompt with their values - Template(@"Use the following pieces of context to answer the question at the end. -If the answer is not in context then just say that you don't know, don't try to make up an answer. -Keep the answer as short as possible. - -{context} - -Question: {question} -Helpful Answer:") | - // send the result to the language model - LargeLanguageModel(gpt35); - - var result = await chain.Run("text"); - - Console.WriteLine($"LLM answer: {result}"); - Console.WriteLine($"Total usage: {gpt35.Usage}"); - } - /// /// Price to run from zero(create embeddings and request to LLM): 0,015$ /// Price to re-run if database is exists: 0,0004$ /// /// - [Explicit] [Test] public async Task Readme() { @@ -161,46 +101,86 @@ Keep the answer as short as possible. """, cancellationToken: CancellationToken.None).ConfigureAwait(false); Console.WriteLine($"LLM answer: {answer}"); // The cloaked figure. - Console.WriteLine($"LLM usage: {llm.Usage}"); - Console.WriteLine($"Embeddings usage: {embeddings.Usage}"); // 2. Chains - var promptText = + var promptTemplate = @"Use the following pieces of context to answer the question at the end. If the answer is not in context then just say that you don't know, don't try to make up an answer. Keep the answer as short as possible. Always quote the context in your answer. {context} Question: {text} Helpful Answer:"; var chain = - Set("Who was drinking a unicorn blood?") // set the question - | RetrieveDocuments(index, amount: 5) // take 5 most similar documents - | StuffDocuments(outputKey: "context") // combine documents together and put them into context - | Template(promptText) // replace context and question in the prompt with their values - | LLM(llm.UseConsoleForDebug()); // send the result to the language model - var chainAnswer = await chain.Run("text"); // get chain result + Set("Who was drinking a unicorn blood?") // set the question (default key is "text") + | RetrieveSimilarDocuments(index, amount: 5) // take 5 most similar documents + | CombineDocuments(outputKey: "context") // combine documents together and put them into context + | Template(promptTemplate) // replace context and question in the prompt with their values + | LLM(llm.UseConsoleForDebug()); // send the result to the language model + var chainAnswer = await chain.Run("text"); // get chain result + + Console.WriteLine("Chain Answer:"+ chainAnswer); // print the result - Console.WriteLine("Answer:"+ chainAnswer); // print the result + Console.WriteLine($"LLM usage: {llm.Usage}"); // Print usage and price + Console.WriteLine($"Embeddings usage: {embeddings.Usage}"); // Print usage and price } - [Explicit] + private enum ProviderType + { + OpenAi, + Together, + Anyscale, + } + + private (IChatModel ChatModel, IEmbeddingModel EmbeddingModel) GetModels(ProviderType providerType) + { + switch (providerType) + { + case ProviderType.OpenAi: + { + var provider = new OpenAiProvider( + Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? + throw new InconclusiveException("OPENAI_API_KEY is not set")); + var llm = new Gpt35TurboModel(provider); + var embeddings = new TextEmbeddingV3SmallModel(provider); + + return (llm, embeddings); + } + case ProviderType.Together: + { + // https://www.together.ai/blog/embeddings-endpoint-release + var provider = new OpenAiProvider( + apiKey: Environment.GetEnvironmentVariable("TOGETHER_API_KEY") ?? + throw new InconclusiveException("TOGETHER_API_KEY is not set"), + customEndpoint: "api.together.xyz"); + var llm = new OpenAiChatModel(provider, id: "meta-llama/Llama-2-70b-chat-hf"); + var embeddings = new OpenAiEmbeddingModel(provider, id: "togethercomputer/m2-bert-80M-2k-retrieval"); + + return (llm, embeddings); + } + case ProviderType.Anyscale: + { + // https://app.endpoints.anyscale.com/ + var provider = new AnyscaleProvider( + apiKey: Environment.GetEnvironmentVariable("ANYSCALE_API_KEY") ?? + throw new InconclusiveException("ANYSCALE_API_KEY is not set")); + var llm = new Llama2LargeModel(provider); + + // Use OpenAI embeddings for now because Anyscale doesn't have embeddings yet + var embeddings = new TextEmbeddingV3SmallModel( + Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? + throw new InconclusiveException("OPENAI_API_KEY is not set")); + + return (llm, embeddings); + } + + default: + throw new ArgumentOutOfRangeException(); + } + } + [Test] - public async Task SimpleDocuments() + public async Task SimpleTestUsingAsync() { - // https://www.together.ai/blog/embeddings-endpoint-release - // var together = new OpenAiModel(new OpenAiConfiguration - // { - // ApiKey = Environment.GetEnvironmentVariable("TOGETHER_API_KEY") ?? - // throw new InconclusiveException("TOGETHER_API_KEY is not set"), - // Endpoint = "api.together.xyz", - // ModelId = "togethercomputer/Qwen-7B", - // EmbeddingModelId = "togethercomputer/m2-bert-80M-32k-retrieval", - // }); - var gpt35 = new Gpt35TurboModel( - Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? - throw new InconclusiveException("OPENAI_API_KEY is not set")); - var embeddings = new TextEmbeddingV3SmallModel( - Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? - throw new InconclusiveException("OPENAI_API_KEY is not set")); + var (llm, embeddings) = GetModels(ProviderType.OpenAi); var database = await InMemoryVectorStore.CreateIndexFromDocuments(embeddings, new[] { @@ -213,7 +193,9 @@ public async Task SimpleDocuments() const string question = "What is the good name for a pet?"; var similarDocuments = await database.Store.GetSimilarDocuments(question, amount: 1); - var petNameResponse = await gpt35.GenerateAsync( + Console.WriteLine($"Similar Documents: {similarDocuments.AsString()}"); + + var petNameResponse = await llm.GenerateAsync( $""" Human will provide you with sentence about pet. You need to answer with pet name. @@ -227,7 +209,7 @@ Human will provide you with sentence about pet. You need to answer with pet name """, cancellationToken: CancellationToken.None).ConfigureAwait(false); Console.WriteLine($"LLM answer: {petNameResponse}"); - Console.WriteLine($"Total usage: {gpt35.Usage}"); + Console.WriteLine($"Total usage: {llm.Usage}"); petNameResponse.ToString().Should().Be("Bob"); } diff --git a/src/Providers/Abstractions/src/Chat/ChatRequest.cs b/src/Providers/Abstractions/src/Chat/ChatRequest.cs index 1ed03989..d3f5c3fd 100644 --- a/src/Providers/Abstractions/src/Chat/ChatRequest.cs +++ b/src/Providers/Abstractions/src/Chat/ChatRequest.cs @@ -44,10 +44,7 @@ public static implicit operator ChatRequest(Message[] messages) /// public static ChatRequest ToChatRequest(string message) { - return new ChatRequest - { - Messages = new[] { message.AsSystemMessage() }, - }; + return ToChatRequest(message.AsHumanMessage()); } /// @@ -57,10 +54,7 @@ public static ChatRequest ToChatRequest(string message) /// public static ChatRequest ToChatRequest(Message message) { - return new ChatRequest - { - Messages = [message], - }; + return ToChatRequest([message]); } /// diff --git a/src/Providers/Anyscale/src/AnyscaleProvider.cs b/src/Providers/Anyscale/src/AnyscaleProvider.cs index 61135f21..c0eb4042 100644 --- a/src/Providers/Anyscale/src/AnyscaleProvider.cs +++ b/src/Providers/Anyscale/src/AnyscaleProvider.cs @@ -8,7 +8,7 @@ public AnyscaleProvider(AnyscaleConfiguration configuration) : base(configuratio { } - public AnyscaleProvider(string apiKey) : base(apiKey, customEndpoint: "https://api.endpoints.anyscale.com/v1") + public AnyscaleProvider(string apiKey) : base(apiKey, customEndpoint: "api.endpoints.anyscale.com") { } } \ No newline at end of file diff --git a/src/Providers/OpenAI/src/Embedding/OpenAiEmbeddingSettings.cs b/src/Providers/OpenAI/src/Embedding/OpenAiEmbeddingSettings.cs index 20a09dd4..47655fee 100644 --- a/src/Providers/OpenAI/src/Embedding/OpenAiEmbeddingSettings.cs +++ b/src/Providers/OpenAI/src/Embedding/OpenAiEmbeddingSettings.cs @@ -11,7 +11,7 @@ public class OpenAiEmbeddingSettings : EmbeddingSettings /// public new static OpenAiEmbeddingSettings Default { get; } = new() { - User = string.Empty, + User = null, }; /// @@ -42,8 +42,7 @@ public static OpenAiEmbeddingSettings Calculate( requestSettingsCasted?.User ?? modelSettingsCasted?.User ?? providerSettingsCasted?.User ?? - Default.User ?? - throw new InvalidOperationException("Default User is not set."), + Default.User, }; } } \ No newline at end of file