Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions PdfProcessing/AIConnectorDemo/AIConnectorDemo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
</PropertyGroup>

<ItemGroup>
<Compile Remove="OllamaEmbeddingsStorage.cs" />
<Compile Remove="CustomOpenAIEmbedder.cs" />
<Compile Remove="TextLoader.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />
<PackageReference Include="Telerik.Windows.Documents.AIConnector" Version="2025.2.520" />
<PackageReference Include="Telerik.Windows.Documents.Fixed" Version="2025.2.520" />
<PackageReference Include="Telerik.Windows.Documents.AIConnector" Version="2025.4.1104" />
<PackageReference Include="Telerik.Windows.Documents.Fixed" Version="2025.4.1104" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
<PackageReference Include="LangChain" Version="0.17.1-dev.12" />
<PackageReference Include="LangChain.Core" Version="0.17.1-dev.12" />
<PackageReference Include="LangChain.Databases.Sqlite" Version="0.17.1-dev.5" />
<PackageReference Include="LangChain.Providers.Ollama" Version="0.17.1-dev.20" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />
<PackageReference Include="Telerik.Documents.AIConnector" Version="2025.2.520" />
<PackageReference Include="Telerik.Documents.Fixed" Version="2025.2.520" />
<PackageReference Include="Telerik.Documents.AIConnector" Version="2025.4.1104" />
<PackageReference Include="Telerik.Documents.Fixed" Version="2025.4.1104" />
</ItemGroup>

<ItemGroup>
Expand Down
99 changes: 99 additions & 0 deletions PdfProcessing/AIConnectorDemo/CustomOpenAIEmbedder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using Telerik.Documents.AI.Core;

namespace AIConnectorDemo
{
public class CustomOpenAIEmbedder : IEmbedder
{
private readonly HttpClient httpClient;

public CustomOpenAIEmbedder()
{
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(5);
string endpoint = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_ENDPOINT");
string apiKey = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_KEY");

httpClient.BaseAddress = new Uri(endpoint);
httpClient.DefaultRequestHeaders.Add("api-key", apiKey);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

this.httpClient = httpClient;
}

public async Task<IList<Embedding>> EmbedAsync(IList<IFragment> fragments)
{
AzureEmbeddingsRequest requestBody = new AzureEmbeddingsRequest
{
Input = fragments.Select(p => p.ToEmbeddingText()).ToArray(),
Dimensions = 3072
};

string json = JsonSerializer.Serialize(requestBody);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

string apiVersion = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_APIVERSION");
string deploymentName = Environment.GetEnvironmentVariable("AZUREEMBEDDINGOPENAI_DEPLOYMENT");
string url = $"openai/deployments/{deploymentName}/embeddings?api-version={apiVersion}";
using HttpResponseMessage response = await this.httpClient.PostAsync(url, content, CancellationToken.None);

Embedding[] embeddings = new Embedding[fragments.Count];

string responseJson = await response.Content.ReadAsStringAsync(CancellationToken.None);
AzureEmbeddingsResponse responseObj = JsonSerializer.Deserialize<AzureEmbeddingsResponse>(responseJson);

List<EmbeddingData> sorted = responseObj.Data.OrderBy(d => d.Index).ToList();
List<float[]> result = new List<float[]>(sorted.Count);

for (int i = 0; i < sorted.Count; i++)
{
EmbeddingData item = sorted[i];
embeddings[i] = new Embedding(fragments[i], item.Embedding);
}

return embeddings;
}

private sealed class AzureEmbeddingsRequest
{
[System.Text.Json.Serialization.JsonPropertyName("input")]
public string[] Input { get; set; } = Array.Empty<string>();

[System.Text.Json.Serialization.JsonPropertyName("dimensions")]
public int? Dimensions { get; set; }
}

private sealed class AzureEmbeddingsResponse
{
[System.Text.Json.Serialization.JsonPropertyName("data")]
public EmbeddingData[] Data { get; set; } = Array.Empty<EmbeddingData>();

[System.Text.Json.Serialization.JsonPropertyName("model")]
public string? Model { get; set; }

[System.Text.Json.Serialization.JsonPropertyName("usage")]
public UsageInfo? Usage { get; set; }
}

private sealed class UsageInfo
{
[System.Text.Json.Serialization.JsonPropertyName("prompt_tokens")]
public int PromptTokens { get; set; }

[System.Text.Json.Serialization.JsonPropertyName("total_tokens")]
public int TotalTokens { get; set; }
}

private sealed class EmbeddingData
{
[System.Text.Json.Serialization.JsonPropertyName("embedding")]
public float[] Embedding { get; set; } = Array.Empty<float>();

[System.Text.Json.Serialization.JsonPropertyName("index")]
public int Index { get; set; }
}
}
}
67 changes: 0 additions & 67 deletions PdfProcessing/AIConnectorDemo/OllamaEmbeddingsStorage.cs

This file was deleted.

37 changes: 21 additions & 16 deletions PdfProcessing/AIConnectorDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Microsoft.Extensions.AI;
using OpenAI.Chat;
using System.IO;
using Telerik.Documents.AI.Core;

#if NETWINDOWS
using Telerik.Windows.Documents.AIConnector;
#else
Expand All @@ -16,19 +18,22 @@ namespace AIConnectorDemo
internal class Program
{
static int maxTokenCount = 128000;
static int maxNumberOfEmbeddingsSent = 50000;
static IChatClient iChatClient;
static string tokenizationEncoding = "cl100k_base";
static string model = "gpt-4o-mini";
static string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY");
static string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT");

static void Main(string[] args)
{
Console.WriteLine("Hello, World!");

CreateChatClient();

using (Stream input = File.OpenRead("John Grisham.pdf"))
{
PdfFormatProvider pdfFormatProvider = new PdfFormatProvider();
RadFixedDocument inputPdf = pdfFormatProvider.Import(input, null);
ISimpleTextDocument simpleDocument = inputPdf.ToSimpleTextDocument();
SimpleTextDocument simpleDocument = inputPdf.ToSimpleTextDocument(TimeSpan.FromSeconds(10));

Summarize(simpleDocument);

Expand All @@ -44,10 +49,6 @@ static void Main(string[] args)

private static void CreateChatClient()
{
string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY");
string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT");
string model = "gpt-4o-mini";

AzureOpenAIClient azureClient = new(
new Uri(endpoint),
new Azure.AzureKeyCredential(key),
Expand All @@ -57,10 +58,12 @@ private static void CreateChatClient()
iChatClient = new OpenAIChatClient(chatClient);
}

private static void Summarize(ISimpleTextDocument simpleDocument)
private static void Summarize(SimpleTextDocument simpleDocument)
{
SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, maxTokenCount);
summarizationProcessor.Settings.PromptAddition = "Summarize the text in a few sentences. Be concise and clear.";
string additionalPrompt = "Summarize the text in a few sentences. Be concise and clear.";
SummarizationProcessorSettings summarizationProcessorSettings = new SummarizationProcessorSettings(maxTokenCount, additionalPrompt);
SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, summarizationProcessorSettings);

summarizationProcessor.SummaryResourcesCalculated += SummarizationProcessor_SummaryResourcesCalculated;

string summary = summarizationProcessor.Summarize(simpleDocument).Result;
Expand All @@ -73,23 +76,25 @@ private static void SummarizationProcessor_SummaryResourcesCalculated(object? se
e.ShouldContinueExecution = true;
}

private static void AskQuestion(ISimpleTextDocument simpleDocument)
private static void AskQuestion(SimpleTextDocument simpleDocument)
{
CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, maxTokenCount);
CompleteContextProcessorSettings completeContextProcessorSettings = new CompleteContextProcessorSettings(maxTokenCount, model, tokenizationEncoding, false);
CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, completeContextProcessorSettings);

string question = "How many pages is the document and what is it about?";
string answer = completeContextQuestionProcessor.AnswerQuestion(simpleDocument, question).Result;
Console.WriteLine(question);
Console.WriteLine(answer);
}

private static void AskPartialContextQuestion(ISimpleTextDocument simpleDocument)
private static void AskPartialContextQuestion(SimpleTextDocument simpleDocument)
{
var settings = EmbeddingSettingsFactory.CreateSettingsForTextDocuments(maxTokenCount, model, tokenizationEncoding, maxNumberOfEmbeddingsSent);
#if NETWINDOWS
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, maxTokenCount, simpleDocument);
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, settings, simpleDocument);
#else
IEmbeddingsStorage embeddingsStorage = new OllamaEmbeddingsStorage();
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embeddingsStorage, maxTokenCount, simpleDocument);
IEmbedder embedder = new CustomOpenAIEmbedder();
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embedder, settings, simpleDocument);
#endif
string question = "What is the last book by John Grisham?";
string answer = partialContextQuestionProcessor.AnswerQuestion(question).Result;
Expand Down
8 changes: 2 additions & 6 deletions PdfProcessing/AIConnectorDemo/ReadMe.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
In order to run the project, you need to replace the key and endpoint with your Azure Open AI key and endpoint.
They are located in the launchSettings.json file in the Properties folder.

In NetStandard you need to implement IEmbeddingsStorage if you would like to use the PartialContextQuestionProcessor.
There is a sample implementation called OllamaEmbeddingsStorage in the NetStandard project.
For it to work the Ollama server needs to be running locally. You can follow these steps to run it:
1. Install Ollama: https://ollama.com/
2. Pull the all-minilm model we'll use for embeddings: ollama pull all-minilm
3. Ensure Ollama is running: ollama serve
In NetStandard you need to implement IEmbedder if you would like to use the PartialContextQuestionProcessor.
There is a sample implementation called CustomOpenAIEmbedder in the NetStandard project.
20 changes: 0 additions & 20 deletions PdfProcessing/AIConnectorDemo/TextLoader.cs

This file was deleted.

2 changes: 1 addition & 1 deletion PdfProcessing/PdfProcessing_NetStandard.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35919.96 d17.13
VisualStudioVersion = 17.13.35919.96
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateDocumentWithImages_NetStandard", "CreateDocumentWithImages\CreateDocumentWithImages_NetStandard.csproj", "{D7CA01E8-E777-4D18-B960-AA0F62040F87}"
EndProject
Expand Down
33 changes: 33 additions & 0 deletions WordsProcessing/AIConnectorDemo/AIConnectorDemo.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>

<PropertyGroup>
<DefineConstants>NETWINDOWS;$(DefineConstants)</DefineConstants>
</PropertyGroup>

<ItemGroup>
<Compile Remove="CustomOpenAIEmbedder.cs" />
<Compile Remove="TextLoader.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />
<PackageReference Include="Telerik.Windows.Documents.AIConnector" Version="2025.4.1104" />
<PackageReference Include="Telerik.Windows.Documents.Flow" Version="2025.4.1104" />
</ItemGroup>

<ItemGroup>
<None Update="GenAI Document Insights Test Document.docx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading