Skip to content

Commit 110288b

Browse files
Merge pull request #25 from telerik/ani/ai-connector
AI Connector demo
2 parents 0790545 + afc7c01 commit 110288b

File tree

10 files changed

+285
-38
lines changed

10 files changed

+285
-38
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0-windows</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<UseWPF>true</UseWPF>
9+
</PropertyGroup>
10+
11+
<PropertyGroup>
12+
<DefineConstants>NETWINDOWS;$(DefineConstants)</DefineConstants>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<Compile Remove="OllamaEmbeddingsStorage.cs" />
17+
<Compile Remove="TextLoader.cs" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
22+
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />
23+
<PackageReference Include="Telerik.Windows.Documents.AIConnector" Version="2025.2.520" />
24+
<PackageReference Include="Telerik.Windows.Documents.Fixed" Version="2025.2.520" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<None Update="John Grisham.pdf">
29+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
30+
</None>
31+
</ItemGroup>
32+
33+
</Project>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<PropertyGroup>
11+
<DefineConstants>NETSTANDARD;$(DefineConstants)</DefineConstants>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="Azure.AI.OpenAI" Version="2.2.0-beta.2" />
16+
<PackageReference Include="LangChain" Version="0.17.1-dev.12" />
17+
<PackageReference Include="LangChain.Core" Version="0.17.1-dev.12" />
18+
<PackageReference Include="LangChain.Databases.Sqlite" Version="0.17.1-dev.5" />
19+
<PackageReference Include="LangChain.Providers.Ollama" Version="0.17.1-dev.20" />
20+
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25161.3" />
21+
<PackageReference Include="Telerik.Documents.AIConnector" Version="2025.2.520" />
22+
<PackageReference Include="Telerik.Documents.Fixed" Version="2025.2.520" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<None Update="John Grisham.pdf">
27+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
28+
</None>
29+
</ItemGroup>
30+
31+
</Project>
109 KB
Binary file not shown.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using LangChain.Databases;
2+
using LangChain.Databases.Sqlite;
3+
using LangChain.DocumentLoaders;
4+
using LangChain.Extensions;
5+
using LangChain.Providers;
6+
using LangChain.Providers.Ollama;
7+
using Telerik.Documents.AIConnector;
8+
9+
namespace AIConnectorDemo
10+
{
11+
// Necessary steps to get this working:
12+
// 1. Install Ollama: https://ollama.com/
13+
// 2. Pull the all-minilm model we'll use for embeddings: ollama pull all-minilm
14+
// 3. Ensure Ollama is running: ollama serve
15+
internal class OllamaEmbeddingsStorage : IEmbeddingsStorage
16+
{
17+
private const string AllMinilmEmbeddingModelName = "all-minilm";
18+
private const string DBName = "vectors.db";
19+
private const int DimensionsForAllMinilm = 384; // Should be 384 for all-minilm
20+
private static readonly string defaultCollectionName = "defaultName";
21+
22+
private readonly SqLiteVectorDatabase vectorDatabase;
23+
private readonly OllamaEmbeddingModel embeddingModel;
24+
25+
IVectorCollection vectorCollection;
26+
27+
public OllamaEmbeddingsStorage()
28+
{
29+
OllamaProvider provider = new OllamaProvider();
30+
this.embeddingModel = new OllamaEmbeddingModel(provider, id: AllMinilmEmbeddingModelName);
31+
this.vectorDatabase = new SqLiteVectorDatabase(dataSource: DBName);
32+
}
33+
34+
public async Task<string> GetQuestionContext(string question)
35+
{
36+
IReadOnlyCollection<Document> similarDocuments = await this.vectorCollection.GetSimilarDocuments(this.embeddingModel, question, amount: 5);
37+
38+
return similarDocuments.AsString();
39+
}
40+
41+
public void SetText(string text, PartialContextProcessorSettings settings)
42+
{
43+
MemoryStream memoryStream = new MemoryStream();
44+
StreamWriter writer = new StreamWriter(memoryStream);
45+
writer.Write(text);
46+
47+
if (this.vectorDatabase.IsCollectionExistsAsync(defaultCollectionName).Result)
48+
{
49+
this.vectorDatabase.DeleteCollectionAsync(defaultCollectionName).Wait();
50+
}
51+
52+
this.vectorCollection = this.vectorDatabase.AddDocumentsFromAsync<TextLoader>(
53+
this.embeddingModel,
54+
dimensions: DimensionsForAllMinilm,
55+
dataSource: DataSource.FromBytes(memoryStream.ToArray()),
56+
textSplitter: null,
57+
collectionName: defaultCollectionName,
58+
behavior: AddDocumentsToDatabaseBehavior.JustReturnCollectionIfCollectionIsAlreadyExists).Result;
59+
60+
}
61+
62+
public void Dispose()
63+
{
64+
this.vectorDatabase.Dispose();
65+
}
66+
}
67+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using Azure.AI.OpenAI;
2+
using Microsoft.Extensions.AI;
3+
using OpenAI.Chat;
4+
using System.IO;
5+
#if NETWINDOWS
6+
using Telerik.Windows.Documents.AIConnector;
7+
#else
8+
using Telerik.Documents.AIConnector;
9+
#endif
10+
using Telerik.Windows.Documents.Fixed.FormatProviders.Pdf;
11+
using Telerik.Windows.Documents.Fixed.Model;
12+
using Telerik.Windows.Documents.TextRepresentation;
13+
14+
namespace AIConnectorDemo
15+
{
16+
internal class Program
17+
{
18+
static int maxTokenCount = 128000;
19+
static IChatClient iChatClient;
20+
21+
static void Main(string[] args)
22+
{
23+
Console.WriteLine("Hello, World!");
24+
25+
CreateChatClient();
26+
27+
using (Stream input = File.OpenRead("John Grisham.pdf"))
28+
{
29+
PdfFormatProvider pdfFormatProvider = new PdfFormatProvider();
30+
RadFixedDocument inputPdf = pdfFormatProvider.Import(input, null);
31+
ISimpleTextDocument simpleDocument = inputPdf.ToSimpleTextDocument();
32+
33+
Summarize(simpleDocument);
34+
35+
Console.WriteLine("--------------------------------------------------");
36+
37+
AskQuestion(simpleDocument);
38+
39+
Console.WriteLine("--------------------------------------------------");
40+
41+
AskPartialContextQuestion(simpleDocument);
42+
}
43+
}
44+
45+
private static void CreateChatClient()
46+
{
47+
string key = Environment.GetEnvironmentVariable("AZUREOPENAI_KEY");
48+
string endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT");
49+
string model = "gpt-4o-mini";
50+
51+
AzureOpenAIClient azureClient = new(
52+
new Uri(endpoint),
53+
new Azure.AzureKeyCredential(key),
54+
new AzureOpenAIClientOptions());
55+
ChatClient chatClient = azureClient.GetChatClient(model);
56+
57+
iChatClient = new OpenAIChatClient(chatClient);
58+
}
59+
60+
private static void Summarize(ISimpleTextDocument simpleDocument)
61+
{
62+
SummarizationProcessor summarizationProcessor = new SummarizationProcessor(iChatClient, maxTokenCount);
63+
summarizationProcessor.Settings.PromptAddition = "Summarize the text in a few sentences. Be concise and clear.";
64+
summarizationProcessor.SummaryResourcesCalculated += SummarizationProcessor_SummaryResourcesCalculated;
65+
66+
string summary = summarizationProcessor.Summarize(simpleDocument).Result;
67+
Console.WriteLine(summary);
68+
}
69+
70+
private static void SummarizationProcessor_SummaryResourcesCalculated(object? sender, SummaryResourcesCalculatedEventArgs e)
71+
{
72+
Console.WriteLine($"The summary will require {e.EstimatedCallsRequired} calls and {e.EstimatedTokensRequired} tokens");
73+
e.ShouldContinueExecution = true;
74+
}
75+
76+
private static void AskQuestion(ISimpleTextDocument simpleDocument)
77+
{
78+
CompleteContextQuestionProcessor completeContextQuestionProcessor = new CompleteContextQuestionProcessor(iChatClient, maxTokenCount);
79+
80+
string question = "How many pages is the document and what is it about?";
81+
string answer = completeContextQuestionProcessor.AnswerQuestion(simpleDocument, question).Result;
82+
Console.WriteLine(question);
83+
Console.WriteLine(answer);
84+
}
85+
86+
private static void AskPartialContextQuestion(ISimpleTextDocument simpleDocument)
87+
{
88+
#if NETWINDOWS
89+
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, maxTokenCount, simpleDocument);
90+
#else
91+
IEmbeddingsStorage embeddingsStorage = new OllamaEmbeddingsStorage();
92+
PartialContextQuestionProcessor partialContextQuestionProcessor = new PartialContextQuestionProcessor(iChatClient, embeddingsStorage, maxTokenCount, simpleDocument);
93+
#endif
94+
string question = "What is the last book by John Grisham?";
95+
string answer = partialContextQuestionProcessor.AnswerQuestion(question).Result;
96+
Console.WriteLine(question);
97+
Console.WriteLine(answer);
98+
}
99+
}
100+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"profiles": {
3+
"AIConnectorDemo": {
4+
"commandName": "Project",
5+
"environmentVariables": {
6+
"AZUREOPENAI_KEY": "key",
7+
"AZUREOPENAI_ENDPOINT": "endpoint"
8+
}
9+
}
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
In order to run the project, you need to replace the key and endpoint with your Azure Open AI key and endpoint.
2+
They are located in the launchSettings.json file in the Properties folder.
3+
4+
In NetStandard you need to implement IEmbeddingsStorage if you would like to use the PartialContextQuestionProcessor.
5+
There is a sample implementation called OllamaEmbeddingsStorage in the NetStandard project.
6+
For it to work the Ollama server needs to be running locally. You can follow these steps to run it:
7+
1. Install Ollama: https://ollama.com/
8+
2. Pull the all-minilm model we'll use for embeddings: ollama pull all-minilm
9+
3. Ensure Ollama is running: ollama serve
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using LangChain.DocumentLoaders;
2+
3+
namespace AIConnectorDemo
4+
{
5+
internal class TextLoader : IDocumentLoader
6+
{
7+
public async Task<IReadOnlyCollection<Document>> LoadAsync(DataSource dataSource, DocumentLoaderSettings? settings = null, CancellationToken cancellationToken = default)
8+
{
9+
using (Stream inputStream = await dataSource.GetStreamAsync(cancellationToken))
10+
{
11+
StreamReader reader = new StreamReader(inputStream);
12+
string content = reader.ReadToEnd();
13+
14+
string[] pages = content.Split(["----------"], System.StringSplitOptions.RemoveEmptyEntries);
15+
16+
return pages.Select(x => new Document(x)).ToList();
17+
}
18+
}
19+
}
20+
}

PdfProcessing/PdfProcessing.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddWatermark", "AddWatermar
2727
EndProject
2828
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModifyBookmarks_WPF", "ModifyBookmarks\ModifyBookmarks_WPF.csproj", "{BD58EF11-6552-4279-A341-0E200480A201}"
2929
EndProject
30+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIConnectorDemo", "AIConnectorDemo\AIConnectorDemo.csproj", "{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}"
31+
EndProject
3032
Global
3133
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3234
Debug|Any CPU = Debug|Any CPU
@@ -81,6 +83,10 @@ Global
8183
{BD58EF11-6552-4279-A341-0E200480A201}.Debug|Any CPU.Build.0 = Debug|Any CPU
8284
{BD58EF11-6552-4279-A341-0E200480A201}.Release|Any CPU.ActiveCfg = Release|Any CPU
8385
{BD58EF11-6552-4279-A341-0E200480A201}.Release|Any CPU.Build.0 = Release|Any CPU
86+
{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
87+
{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
88+
{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
89+
{4AC38EDC-AE27-8F2B-5BCB-8E73F27E33F2}.Release|Any CPU.Build.0 = Release|Any CPU
8490
EndGlobalSection
8591
GlobalSection(SolutionProperties) = preSolution
8692
HideSolutionNode = FALSE

PdfProcessing/PdfProcessing_NetStandard.sln

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29709.97
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.13.35919.96 d17.13
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateDocumentWithImages_NetStandard", "CreateDocumentWithImages\CreateDocumentWithImages_NetStandard.csproj", "{D7CA01E8-E777-4D18-B960-AA0F62040F87}"
77
EndProject
@@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomJpegImageConverter_Ne
2323
EndProject
2424
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AddWatermark_NetStandard", "AddWatermark\AddWatermark_NetStandard.csproj", "{5E7AB5A2-E716-4FEE-A30B-5E7167C94013}"
2525
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIConnectorDemo_NetStandard", "AIConnectorDemo\AIConnectorDemo_NetStandard.csproj", "{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}"
27+
EndProject
2628
Global
2729
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2830
Debug|Any CPU = Debug|Any CPU
@@ -69,47 +71,15 @@ Global
6971
{5E7AB5A2-E716-4FEE-A30B-5E7167C94013}.Debug|Any CPU.Build.0 = Debug|Any CPU
7072
{5E7AB5A2-E716-4FEE-A30B-5E7167C94013}.Release|Any CPU.ActiveCfg = Release|Any CPU
7173
{5E7AB5A2-E716-4FEE-A30B-5E7167C94013}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75+
{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Debug|Any CPU.Build.0 = Debug|Any CPU
76+
{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{C948AB2A-A1D5-DE6C-0C36-8E9A4C475033}.Release|Any CPU.Build.0 = Release|Any CPU
7278
EndGlobalSection
7379
GlobalSection(SolutionProperties) = preSolution
7480
HideSolutionNode = FALSE
7581
EndGlobalSection
7682
GlobalSection(ExtensibilityGlobals) = postSolution
7783
SolutionGuid = {1206FC65-2429-4003-B167-FD0198015602}
7884
EndGlobalSection
79-
GlobalSection(TeamFoundationVersionControl) = preSolution
80-
SccNumberOfProjects = 11
81-
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
82-
SccTeamFoundationServer = https://tfsemea.progress.com/defaultcollection
83-
SccLocalPath0 = .
84-
SccProjectUniqueName1 = CreateDocumentWithImages\\CreateDocumentWithImages_NetStandard.csproj
85-
SccProjectName1 = CreateDocumentWithImages
86-
SccLocalPath1 = CreateDocumentWithImages
87-
SccProjectUniqueName2 = CreateInteractiveForms\\CreateInteractiveForms_NetStandard.csproj
88-
SccProjectName2 = CreateInteractiveForms
89-
SccLocalPath2 = CreateInteractiveForms
90-
SccProjectUniqueName3 = CreatePdfUsingRadFixedDocumentEditor\\CreatePdfUsingRadFixedDocumentEditor_NetStandard.csproj
91-
SccProjectName3 = CreatePdfUsingRadFixedDocumentEditor
92-
SccLocalPath3 = CreatePdfUsingRadFixedDocumentEditor
93-
SccProjectUniqueName4 = GenerateDocument\\GenerateDocument_NetStandard.csproj
94-
SccProjectName4 = GenerateDocument
95-
SccLocalPath4 = GenerateDocument
96-
SccProjectUniqueName5 = ManipulatePages\\ManipulatePages_NetStandard.csproj
97-
SccProjectName5 = ManipulatePages
98-
SccLocalPath5 = ManipulatePages
99-
SccProjectUniqueName6 = ModifyForms\\ModifyFormValues_NetStandard.csproj
100-
SccProjectName6 = ModifyForms
101-
SccLocalPath6 = ModifyForms
102-
SccProjectUniqueName7 = PdfStreamWriterPerformance\\PdfStreamWriterPerformance_NetStandard.csproj
103-
SccProjectName7 = PdfStreamWriterPerformance
104-
SccLocalPath7 = PdfStreamWriterPerformance
105-
SccProjectUniqueName8 = DrawHeaderFooter\\DrawHeaderFooter_NetStandard.csproj
106-
SccProjectName8 = DrawHeaderFooter
107-
SccLocalPath8 = DrawHeaderFooter
108-
SccProjectUniqueName9 = CustomJpegImageConverter\\CustomJpegImageConverter_NetStandard.csproj
109-
SccProjectName9 = CustomJpegImageConverter
110-
SccLocalPath9 = CustomJpegImageConverter
111-
SccProjectUniqueName10 = AddWatermark\\AddWatermark_NetStandard.csproj
112-
SccProjectName10 = AddWatermark
113-
SccLocalPath10 = AddWatermark
114-
EndGlobalSection
11585
EndGlobal

0 commit comments

Comments
 (0)