Skip to content

Commit dd0488d

Browse files
committed
Add Agent state persistance
1 parent d8b3812 commit dd0488d

9 files changed

Lines changed: 353 additions & 27 deletions

Agent_Llama.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ le</ImplicitUsings>
88
<Nullable>enable</Nullable>
99
</PropertyGroup>
1010
<ItemGroup>
11+
<PackageReference Include="MemoryPack" Version="1.21.4" />
1112
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0" />
1213
<PackageReference Include="Microsoft.Agents.AI.A2A" Version="1.3.0-preview.260423.1" />
1314
<PackageReference Include="Microsoft.Extensions.AI" Version="10.4.1" />

DotNetAI.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ public static async Task<string> GetBitcoinPrice(
9999
}
100100

101101

102-
103-
104-
105102
[Description("Get the weather for a given location.")]
106103
public static string GetWeather([Description("The location to get the weather for.")] string location)
107104
=> $"The weather in {location} is ...";

PDF_AI_Summariser.cs

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
using Microsoft.SemanticKernel;
1+
using MemoryPack;
2+
using Microsoft.Extensions.AI;
3+
using Microsoft.SemanticKernel;
4+
using Microsoft.SemanticKernel.Connectors.Ollama;
25
using Microsoft.SemanticKernel.Embeddings;
36
using OllamaSharp;
47
using OllamaSharp.Models;
8+
using OpenAI.Chat;
9+
using PdfReader;
510
using System;
611
using System.Collections.Generic;
712
using System.IO;
13+
using System.Net.Http;
814
using System.Threading.Tasks;
15+
using UglyToad.PdfPig.Graphics;
916

1017
#pragma warning disable CA1861 // Avoid constant arrays as arguments
1118
#pragma warning disable SKEXP0070 // AddOllamaTextGeneration
19+
#pragma warning disable SKEXP0010
1220

1321
namespace Agent_Llama;
1422

@@ -49,7 +57,7 @@ public async Task SummarizeFileUsingPdfContentPlugin(string PDF_filename = @"C:\
4957
var builder = Kernel.CreateBuilder()
5058
.AddOllamaTextGeneration(
5159
endpoint: new Uri(ollamaEndpoint), // Use named argument for 'endpoint'
52-
modelId: ollamaModel // Use named argument for 'modelId'
60+
modelId: ollamaModel // Use named argument for 'modelId
5361
);
5462

5563
// Build the kernel instance
@@ -62,6 +70,8 @@ public async Task SummarizeFileUsingPdfContentPlugin(string PDF_filename = @"C:\
6270
var pdfContentPlugin = kernel.CreatePluginFromObject(new PdfContentPlugin());
6371
Console.WriteLine("PdfContentPlugin loaded successfully.");
6472

73+
74+
6575
// --- Define the path to the text file ---
6676
//string filePath = Path.GetFullPath(sampleFileName);
6777
//Console.WriteLine($"Attempting to summarize file: {filePath}");
@@ -91,6 +101,8 @@ public async Task SummarizeFileUsingPdfContentPlugin(string PDF_filename = @"C:\
91101
var gen = ollamaClient.AsTextEmbeddingGenerationService();
92102
var embeds = await gen.GenerateEmbeddingsAsync(Input);
93103

104+
System.Console.WriteLine($"Generated {embeds.Count} embeddings from Ollama for PDF: {PDF_filename_local}" );
105+
94106
// Create the embedding service
95107
//var embeddingService = OllamaApiClient.AsEmbeddingGenerationService(ollamaClient, "nomic-embed-text");
96108

@@ -136,6 +148,122 @@ public async Task SummarizeFileUsingPdfContentPlugin(string PDF_filename = @"C:\
136148
//Console.Read();
137149
}
138150

151+
152+
public async Task SummarizeFileWithPdfContentPlugin(string PDF_filename = @"C:\Users\risto\source\repos\PDF_Llama\PDFs\VN.pdf")
153+
{
154+
// --- Configuration ---
155+
const string PDF_filename_local = @"VN.pdf";
156+
157+
var httpClient = new HttpClient()
158+
{
159+
BaseAddress = ModelEndpoint,
160+
Timeout = TimeSpan.FromMinutes(20)
161+
};
162+
163+
try
164+
{
165+
var builder = Kernel.CreateBuilder()
166+
.AddOllamaTextGeneration(
167+
endpoint: ModelEndpoint,
168+
modelId: ModelName
169+
);
170+
171+
var kernel = builder.Build();
172+
173+
Console.WriteLine($"Kernel initialized with Ollama model: {ModelName} at {ModelEndpoint}");
174+
175+
176+
// --- Import your custom plugin ---
177+
// The KernelPluginFactory.CreateFromType<T>() method is used to discover kernel functions defined within the FileContentPlugin class.
178+
//var pdfContentPlugin = kernel.CreatePluginFromObject(new PdfContentPlugin());
179+
//Console.WriteLine("PdfContentPlugin loaded successfully.");
180+
181+
182+
builder.Services.AddOllamaTextGeneration(
183+
ModelName,
184+
ModelEndpoint
185+
);
186+
187+
var Input = new List<string> { "your text to embed" };
188+
189+
Reader reader = new Reader();
190+
var pdftxtlist = reader.ReadPdfToList(PDF_filename);
191+
192+
193+
// Assuming you have an IHttpClientFactory and a properly configured OllamaApiClient
194+
//var ollamaClient = new OllamaApiClient(ModelEndpoint, ModelName);
195+
196+
var ollamaClient = new OllamaApiClient(httpClient)
197+
{
198+
SelectedModel = ModelName
199+
};
200+
201+
//var gen = ollamaClient.AsTextEmbeddingGenerationService();
202+
var embeds = await ollamaClient.AsTextEmbeddingGenerationService().GenerateEmbeddingsAsync(pdftxtlist);
203+
204+
// Serialize
205+
byte[] bytearr = MemoryPackSerializer.Serialize(embeds);
206+
File.WriteAllBytes(@"C:\tmp\embedding.bin", bytearr);
207+
208+
// Deserialize
209+
ReadOnlyMemory<float> embeds_from_file = MemoryPackSerializer.Deserialize<ReadOnlyMemory<float>>(bytearr);
210+
211+
212+
System.Console.WriteLine($"Generated {embeds.Count}, seralised {embeds_from_file.Length} embeddings from Ollama for PDF: {PDF_filename_local}");
213+
214+
215+
216+
//var prompt = $"Summarize the following text in one sentence: {documentText}";
217+
//var response = await chatClient.GetResponseAsync(prompt);
218+
219+
//Console.WriteLine($"\nSummary:\n{response.Message.Text}");
220+
221+
// Create the embedding service
222+
//var embeddingService = OllamaApiClient.AsEmbeddingGenerationService(ollamaClient, "nomic-embed-text");
223+
224+
// Generate embeddings for a text
225+
//var text = "This is a sample sentence.";
226+
//var embedding = await embeddingService.GenerateEmbeddingAsync(text);
227+
228+
229+
var embeddingRequest = new EmbedRequest
230+
{
231+
Input = Input,
232+
};
233+
234+
var embeddingGenerator = new OllamaApiClient(ModelEndpoint, ModelName)
235+
.EmbedAsync(embeddingRequest);
236+
237+
//OllamaApiClientExtensions
238+
// .AddOllamaTextEmbeddingGeneration(
239+
// builder.Services,
240+
241+
// modelId: ollamaModel,
242+
// endpoint: new Uri(ollamaEndpoint)
243+
// );
244+
245+
//var embeddingGenerator = new OllamaEmbeddingGenerator(
246+
// modelId: ollamaModel,
247+
// endpoint: new Uri(ollamaEndpoint)
248+
// );
249+
250+
//var embeddingGenerator = new AddOllamaTextEmbeddingGeneration(ollamaEndpoint, ollamaModel)
251+
// .GetEmbeddingClient("your chosen model")
252+
// .AsIEmbeddingGenerator();
253+
254+
}
255+
catch (Exception ex)
256+
{
257+
Console.WriteLine($"An error occurred: {ex.Message}");
258+
Console.WriteLine("Please ensure Ollama is running and the specified model is downloaded.");
259+
Console.WriteLine($"Check your Ollama endpoint: {ModelEndpoint.AbsoluteUri} and model: {ModelName}");
260+
}
261+
262+
Console.WriteLine("Press any key to exit.");
263+
}
264+
265+
266+
139267
/// <summary>
140268
/// Creates a sample text file with some dummy content for testing.
141269
/// </summary>

PdfContentPlugin.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@ public async Task<string> SummarizeFile(
4242
Reader reader = new Reader(PDFPath);
4343
pdfpath = PDFPath + pdfFileName;
4444
string filePath = Path.GetFullPath(pdfpath);
45-
var pdftxt = reader.ReadPdf(filePath); // Ensure the file can be read before proceeding.
46-
//json_chunks = reader.ReadPdfBlocks(pdfpath);
47-
// Create a prompt for the AI model.Instruct the model to summarize the provided text.
45+
var pdftxt = reader.ReadPdf(filePath);
46+
47+
48+
// Ensure the file can be read before proceeding.
49+
//json_chunks = reader.ReadPdfBlocks(pdfpath);
50+
// Create a prompt for the AI model.Instruct the model to summarize the provided text.
51+
4852
prompt = @$"Summarize the following text concisely and accurately.
4953
If the text is too short or doesn't contain meaningful information, state that.
5054

PdfReader.cs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,35 @@ public Reader(string directoryPath)
3333
}
3434
}
3535

36+
// get text and blocks
37+
public string ReadPdf(string filePath)
38+
{
39+
StringBuilder text = new StringBuilder();
40+
using (var document = UglyToad.PdfPig.PdfDocument.Open(filePath))
41+
{
42+
foreach (var page in document.GetPages())
43+
{
44+
var pageText = ContentOrderTextExtractor.GetText(page);
45+
text.AppendLine(pageText);
46+
}
47+
}
48+
return text.ToString();
49+
}
50+
51+
public List<string> ReadPdfToList(string filePath)
52+
{
53+
List<string> pages = new List<string>();
54+
using (var document = UglyToad.PdfPig.PdfDocument.Open(filePath))
55+
{
56+
foreach (var page in document.GetPages())
57+
{
58+
var pageText = ContentOrderTextExtractor.GetText(page);
59+
pages.Add(pageText);
60+
}
61+
}
62+
return pages;
63+
}
64+
3665
public List<string> Read_PDF_Blocks_AsJSON(string filePath)
3766
{
3867
List<string> jsonList = new List<string>();
@@ -97,20 +126,6 @@ public List<string> ReadDirectorysPdfsAsJSON()
97126
}
98127
return pdf_json_list;
99128
}
100-
// get text and blocks
101-
public string ReadPdf(string filePath)
102-
{
103-
StringBuilder text = new StringBuilder();
104-
using (var document = UglyToad.PdfPig.PdfDocument.Open(filePath))
105-
{
106-
foreach (var page in document.GetPages())
107-
{
108-
var pageText = ContentOrderTextExtractor.GetText(page);
109-
text.AppendLine(pageText);
110-
}
111-
}
112-
return text.ToString();
113-
}
114129

115130
// C:\Users\risto\Downloads\Vacancy Notice EEA-AD-2024-17.pdf
116131
// VACANCY NOTICE EEA-AD-2024-17.pdf

Program.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ static async Task Main(string[] args)
6868
//var config = configurationManager.GetRequiredSection("appSettings");
6969

7070
var configvalue1 = configurationManager.Sources; // ("ModelEndpoint"); //.AppSettings["countoffiles"];
71-
// write title
7271

7372
SpectreConsoleOutput.DisplayTitleH3($"Use Semantic Kernel Plugin; PDF Summariser - Get response from Ollama IChatClient");
7473

@@ -83,25 +82,30 @@ static async Task Main(string[] args)
8382
AgentStructuredOutput agent_struct = new(starts.ModelEndpoint, starts.ModelName);
8483

8584
NestEd nested = new(starts.ModelEndpoint, starts.ModelName);
85+
Embed embed = new(starts.ModelEndpoint, starts.ModelName);
8686

8787
////var cardResolver = new A2ACardResolver(starts.ModelEndpoint);
8888
//var agentCard = await cardResolver.GetAgentCardAsync();
8989

9090
// present
9191
switch (scenario)
9292
{
93-
// NestedDictionary
93+
case "Embed":
94+
await embed.CreateAgent("", "");
95+
break;
96+
9497
case "Nested":
9598
await nested.CreateAgent("","");
9699
break;
100+
97101
case "AtoA":
98102
await a2a.CreateAgent("What is the second largest city in France?",
99103
"What is the third largest city in italy?");
100104
break;
101105

102106
case "PDF AI Summariser":
103107
PDF_AI_Summariser pdf_AI_Summariser = new(starts.ModelEndpoint, starts.ModelName);
104-
await pdf_AI_Summariser.SummarizeFileUsingPdfContentPlugin();
108+
await pdf_AI_Summariser.SummarizeFileWithPdfContentPlugin();
105109
break;
106110

107111
case "Long Agent Task":

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Agent_Llama
2-
##### Based on PDF_Llama solution, uses Microsoft Agent Framework.
32

43
This repository demonstrates using Ollama (local LLM) with Microsoft Agent Framework
54
to **summarize PDF documents** and generate embeddings from documents for future use.
5+
It is Based on PDF_Llama. Uses Microsoft Agent Framework.
66

77
## Usage scenarios
88

@@ -20,7 +20,16 @@ this helpful piggy gives us the ability to
2020
AI analyse documents images, links and metadata.
2121

2222
### Working on atm...
23-
- Use gemma3:4b model to add image analysis
23+
- Using gemma3:4b model to add image analysis
24+
- Serach for 'gemma3'
25+
26+
### Choosing an embedding model
27+
- Model accuracy and dimensionality.
28+
- What is the size of the vectors generated by the model,
29+
- Is it configurable, as this will affect your vector storage cost.
30+
- What type of elements does the generated vectors contain, e.g. float32, float16 - cost again.
31+
- How fast does it generate vectors?
32+
2433

2534
### Future enhancements
2635

0 commit comments

Comments
 (0)