Skip to content

Commit fefd1c8

Browse files
authored
Update Elastic.Clients.Elasticsearch and playground example (#8)
1 parent e0b232c commit fefd1c8

File tree

6 files changed

+576
-121
lines changed

6 files changed

+576
-121
lines changed

Directory.Packages.props

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
66
</PropertyGroup>
77
<ItemGroup>
8-
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.16.1" />
9-
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.29.0" />
8+
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="8.16.2" />
9+
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.30.0" />
1010
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
1111
<PackageVersion Include="MinVer" Version="6.0.0" />
1212
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
13+
<!-- Playground -->
14+
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
15+
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="1.30.0" />
16+
<PackageVersion Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" Version="1.30.0" />
1317
</ItemGroup>
1418
<ItemGroup>
1519
<GlobalPackageReference Include="Microsoft.Build.CopyOnWrite" Version="1.0.334" />

Elastic.SemanticKernel.Connectors.Elasticsearch/MockableElasticsearchClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ namespace Elastic.SemanticKernel.Connectors.Elasticsearch;
2323
#pragma warning disable CA1852 // TODO: Remove after using MockableElasticsearchClient in unit tests
2424

2525
/// <summary>
26-
/// Decorator class for <see cref="Elastic.Clients.Elasticsearch.ElasticsearchClient" /> that exposes the required
27-
/// methods as virtual allowing for mocking in unit tests.
26+
/// Decorator class for <see cref="ElasticsearchClient" /> that exposes the required methods as virtual allowing
27+
/// for mocking in unit tests.
2828
/// </summary>
2929
internal class MockableElasticsearchClient
3030
{

Elastic.SemanticKernel.Connectors.Elasticsearch/packages.lock.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
".NETStandard,Version=v2.0": {
55
"Elastic.Clients.Elasticsearch": {
66
"type": "Direct",
7-
"requested": "[8.16.1, )",
8-
"resolved": "8.16.1",
9-
"contentHash": "IIqNXo1hS15aXSfr3f+QzYJqj7k01vx075VJT4d4/oQdpN4HL3IRfnhcgPHMVQSuAw8bY5l3FSztGHT54AwgtQ==",
7+
"requested": "[8.16.2, )",
8+
"resolved": "8.16.2",
9+
"contentHash": "Fo1flOPjoQVmmjsZe0lPOjpL0WRSUbizgnC3fTqjIzkKSAs2wtzZACdjqILPZoYOXSlBXMLth3I0dwZUPN/eyw==",
1010
"dependencies": {
11-
"Elastic.Transport": "0.5.5"
11+
"Elastic.Transport": "0.5.6"
1212
}
1313
},
1414
"Microsoft.Build.CopyOnWrite": {
@@ -19,9 +19,9 @@
1919
},
2020
"Microsoft.SemanticKernel.Abstractions": {
2121
"type": "Direct",
22-
"requested": "[1.29.0, )",
23-
"resolved": "1.29.0",
24-
"contentHash": "PJIy7YAkaUtyp9uzRn1wO2lQYb/vuC9jtF7rmoPXe18ugrztnvY8sKyvv+LEMib48cZKfOhxOzQVE/dTa5TjrQ==",
22+
"requested": "[1.30.0, )",
23+
"resolved": "1.30.0",
24+
"contentHash": "JYac6hAUjqenVdfxqgW63/raaBqpk30cScmUg1ibuMY8CD56b1zgQ8pM35dQMY9sbLlWP+SuPNtWAn+gQl19Og==",
2525
"dependencies": {
2626
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
2727
"Microsoft.Bcl.HashCode": "1.1.1",
@@ -74,8 +74,8 @@
7474
},
7575
"Elastic.Transport": {
7676
"type": "Transitive",
77-
"resolved": "0.5.5",
78-
"contentHash": "sWuMp1yX4R2rHtmZhWVudt5l0zIaqSYCnUtcMrsmfiZxV2qY5WlbViPIH5KdYrI52p71vhScshVJbPDRezm10Q==",
77+
"resolved": "0.5.6",
78+
"contentHash": "4WGaVuBDvdLYgrXNchxXS0dLh92Vb5bH7dgVl0w6cHudXFKh67JZFY2se2LZ3SpUPsQWuv2UKQQRbJB/qbRJBQ==",
7979
"dependencies": {
8080
"Microsoft.CSharp": "4.7.0",
8181
"System.Buffers": "4.5.1",
@@ -219,11 +219,11 @@
219219
"net8.0": {
220220
"Elastic.Clients.Elasticsearch": {
221221
"type": "Direct",
222-
"requested": "[8.16.1, )",
223-
"resolved": "8.16.1",
224-
"contentHash": "IIqNXo1hS15aXSfr3f+QzYJqj7k01vx075VJT4d4/oQdpN4HL3IRfnhcgPHMVQSuAw8bY5l3FSztGHT54AwgtQ==",
222+
"requested": "[8.16.2, )",
223+
"resolved": "8.16.2",
224+
"contentHash": "Fo1flOPjoQVmmjsZe0lPOjpL0WRSUbizgnC3fTqjIzkKSAs2wtzZACdjqILPZoYOXSlBXMLth3I0dwZUPN/eyw==",
225225
"dependencies": {
226-
"Elastic.Transport": "0.5.5"
226+
"Elastic.Transport": "0.5.6"
227227
}
228228
},
229229
"Microsoft.Build.CopyOnWrite": {
@@ -234,9 +234,9 @@
234234
},
235235
"Microsoft.SemanticKernel.Abstractions": {
236236
"type": "Direct",
237-
"requested": "[1.29.0, )",
238-
"resolved": "1.29.0",
239-
"contentHash": "PJIy7YAkaUtyp9uzRn1wO2lQYb/vuC9jtF7rmoPXe18ugrztnvY8sKyvv+LEMib48cZKfOhxOzQVE/dTa5TjrQ==",
237+
"requested": "[1.30.0, )",
238+
"resolved": "1.30.0",
239+
"contentHash": "JYac6hAUjqenVdfxqgW63/raaBqpk30cScmUg1ibuMY8CD56b1zgQ8pM35dQMY9sbLlWP+SuPNtWAn+gQl19Og==",
240240
"dependencies": {
241241
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
242242
"Microsoft.Bcl.HashCode": "1.1.1",
@@ -272,8 +272,8 @@
272272
},
273273
"Elastic.Transport": {
274274
"type": "Transitive",
275-
"resolved": "0.5.5",
276-
"contentHash": "sWuMp1yX4R2rHtmZhWVudt5l0zIaqSYCnUtcMrsmfiZxV2qY5WlbViPIH5KdYrI52p71vhScshVJbPDRezm10Q=="
275+
"resolved": "0.5.6",
276+
"contentHash": "4WGaVuBDvdLYgrXNchxXS0dLh92Vb5bH7dgVl0w6cHudXFKh67JZFY2se2LZ3SpUPsQWuv2UKQQRbJB/qbRJBQ=="
277277
},
278278
"Microsoft.Bcl.AsyncInterfaces": {
279279
"type": "Transitive",

Elastic.SemanticKernel.Playground/Elastic.SemanticKernel.Playground.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
<ItemGroup>
1818
<PackageReference Include="Elastic.Clients.Elasticsearch" />
19+
<PackageReference Include="Microsoft.Extensions.Hosting" />
20+
<PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" />
21+
<PackageReference Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" />
1922
</ItemGroup>
2023

2124
<ItemGroup>
Lines changed: 89 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,132 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Text.Json.Serialization;
42
using System.Threading.Tasks;
53

64
using Elastic.Clients.Elasticsearch;
75
using Elastic.Transport;
86

9-
using Elastic.SemanticKernel.Connectors.Elasticsearch;
10-
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Hosting;
119
using Microsoft.Extensions.VectorData;
10+
using Microsoft.SemanticKernel;
11+
using Microsoft.SemanticKernel.Data;
12+
using Microsoft.SemanticKernel.Embeddings;
13+
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
1214

1315
namespace Elastic.SemanticKernel.Playground;
1416

15-
internal class Program
17+
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
18+
19+
internal sealed class Program
1620
{
17-
static async Task Main(string[] args)
21+
public static async Task Main(string[] args)
1822
{
19-
using var settings = new ElasticsearchClientSettings(new Uri("https://primary.es.europe-west3.gcp.cloud.es.io"))
20-
.Authentication(new BasicAuthentication("elastic", "g7wuRWPrJF2yAzytvd9w18s8"))
23+
#pragma warning disable SKEXP0010 // Some SK methods are still experimental
24+
25+
var builder = Host.CreateApplicationBuilder(args);
26+
27+
// Register AI services.
28+
var kernelBuilder = builder.Services.AddKernel();
29+
kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");
30+
kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");
31+
32+
// Register text search service.
33+
kernelBuilder.AddVectorStoreTextSearch<Hotel>();
34+
35+
// Register Elasticsearch vector store.
36+
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
37+
.Authentication(new BasicAuthentication("elastic", "my_password"))
2138
.DisableDirectStreaming()
2239
.EnableDebugMode(cd =>
2340
{
41+
//var request = System.Text.Encoding.Default.GetString(cd.RequestBodyInBytes);
2442
Console.WriteLine(cd.DebugInformation);
2543
});
44+
kernelBuilder.AddElasticsearchVectorStoreRecordCollection<string, Hotel>("skhotels", elasticsearchClientSettings);
2645

27-
var client = new ElasticsearchClient(settings);
46+
// Build the host.
47+
using var host = builder.Build();
2848

29-
var vectorStore = new ElasticsearchVectorStore(client);
49+
// For demo purposes, we access the services directly without using a DI context.
3050

31-
var collection = vectorStore.GetCollection<string, MyRecord>("sk");
51+
var kernel = host.Services.GetService<Kernel>()!;
52+
var embeddings = host.Services.GetService<ITextEmbeddingGenerationService>()!;
53+
var vectorStoreCollection = host.Services.GetService<IVectorStoreRecordCollection<string, Hotel>>()!;
3254

33-
await collection.CreateCollectionIfNotExistsAsync();
55+
// Register search plugin.
56+
var textSearch = host.Services.GetService<VectorStoreTextSearch<Hotel>>()!;
57+
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));
3458

35-
await collection.UpsertAsync(new MyRecord
36-
{
37-
MyKey = "40",
38-
Vec1 = new float[] { 2.1f, 2.0f, 2.2f },
39-
Vec2 = new float[] { 1.2f, 1.1f, 1.3f },
40-
Data1 = 1337,
41-
Data2 = DateTimeKind.Utc,
42-
Data3 = ["a", "b", "c"]
43-
});
59+
// Crate collection and ingest a few demo records.
60+
await vectorStoreCollection.CreateCollectionIfNotExistsAsync();
4461

45-
await collection.UpsertAsync(new MyRecord
62+
await vectorStoreCollection.UpsertAsync(new Hotel
4663
{
47-
MyKey = "41",
48-
Vec1 = new float[] { 0.1f, 0.1f, 1000.1f },
49-
Vec2 = new float[] { 1.2f, 1.1f, 1.3f },
50-
Data1 = 1338,
51-
Data2 = DateTimeKind.Utc,
52-
Data3 = ["a", "abba", "c"]
64+
HotelId = "1",
65+
HotelName = "First Hotel",
66+
Description = "The blue hotel.",
67+
DescriptionEmbedding = await embeddings.GenerateEmbeddingAsync("The blue hotel."),
68+
ReferenceLink = "Global Hotel Database, Entry 1337"
5369
});
5470

55-
var id = await collection.UpsertAsync(new MyRecord
71+
await vectorStoreCollection.UpsertAsync(new Hotel
5672
{
57-
MyKey = "42",
58-
Vec1 = new float[] { 2.2f, 2.1f, 2.3f, 0.0f },
59-
Vec2 = new float[] { 1.2f, 1.1f, 1.3f },
60-
Data1 = 1337,
61-
Data2 = DateTimeKind.Utc,
62-
Data3 = ["a", "b", "c"]
73+
HotelId = "2",
74+
HotelName = "Second Hotel",
75+
Description = "The green hotel.",
76+
DescriptionEmbedding = await embeddings.GenerateEmbeddingAsync("The green hotel."),
77+
ReferenceLink = "Global Hotel Database, Entry 4242"
6378
});
6479

65-
var record = await collection.GetAsync(id);
66-
67-
var search = await collection.VectorizedSearchAsync(new float[] { 2.2f, 2.1f, 2.3f }, new VectorSearchOptions
68-
{
69-
IncludeTotalCount = true,
70-
Filter = new VectorSearchFilter(new FilterClause[]
80+
// Invoke the LLM with a template that uses the search plugin to
81+
// 1. get related information to the user query from the vector store
82+
// 2. add the information to the LLM prompt.
83+
var response = await kernel.InvokePromptAsync(
84+
promptTemplate: """
85+
Please use this information to answer the question:
86+
{{#with (SearchPlugin-GetTextSearchResults question)}}
87+
{{#each this}}
88+
Name: {{Name}}
89+
Value: {{Value}}
90+
Source: {{Link}}
91+
-----------------
92+
{{/each}}
93+
{{/with}}
94+
95+
Include the source of relevant information in the response.
96+
97+
Question: {{question}}
98+
""",
99+
arguments: new KernelArguments
71100
{
72-
new AnyTagEqualToFilterClause(nameof(MyRecord.Data3), "abba"),
73-
new EqualToFilterClause(nameof(MyRecord.Data1), 1338) // TODO: Test char
74-
})
75-
});
101+
{ "question", "What is the name of the hotel that has the same color as grass?" },
102+
},
103+
templateFormat: "handlebars",
104+
promptTemplateFactory: new HandlebarsPromptTemplateFactory());
76105

77-
var genericCollection = vectorStore.GetCollection<string, VectorStoreGenericDataModel<string>>("sk", new VectorStoreRecordDefinition
78-
{
79-
Properties =
80-
[
81-
new VectorStoreRecordKeyProperty(nameof(MyRecord.MyKey), typeof(string)),
82-
new VectorStoreRecordVectorProperty(nameof(MyRecord.Vec1), typeof(ReadOnlyMemory<float>))
83-
{
84-
StoragePropertyName = "xxx"
85-
},
86-
new VectorStoreRecordVectorProperty(nameof(MyRecord.Vec2), typeof(ReadOnlyMemory<float>)),
87-
new VectorStoreRecordDataProperty(nameof(MyRecord.Data1), typeof(int)),
88-
new VectorStoreRecordDataProperty(nameof(MyRecord.Data2), typeof(DateTimeKind)),
89-
new VectorStoreRecordDataProperty(nameof(MyRecord.Data3), typeof(string[]))
90-
]
91-
});
92-
93-
var genericRecord = await genericCollection.GetAsync(id);
94-
genericRecord.Key = null;
95-
96-
var id2 = await genericCollection.UpsertAsync(genericRecord);
97-
var record2 = await collection.GetAsync(id2);
98-
99-
await collection.DeleteAsync(id);
100-
await collection.DeleteAsync(id2);
106+
Console.WriteLine(response.ToString());
101107

102-
await collection.DeleteCollectionAsync();
108+
// > The name of the hotel that has the same color as grass is "Second Hotel."
109+
// > This hotel is described as the green hotel. (Source: Global Hotel Database, Entry 4242)
103110
}
104111
}
105112

106-
public sealed class MyRecord
113+
public sealed record Hotel
107114
{
108115
[VectorStoreRecordKey]
109-
public required string? MyKey { get; init; }
110-
111-
[VectorStoreRecordVector(3)]
112-
[JsonPropertyName("xxx")]
113-
public required IReadOnlyCollection<float> Vec1 { get; init; }
114-
115-
[VectorStoreRecordVector(3)]
116-
public required ReadOnlyMemory<float> Vec2 { get; init; }
117-
118-
[VectorStoreRecordData(IsFilterable = true, IsFullTextSearchable = true)]
119-
public required int Data1 { get; init; }
120-
121-
[VectorStoreRecordData(IsFilterable = true)]
122-
public required DateTimeKind Data2 { get; init; }
116+
public required string HotelId { get; set; }
123117

118+
[TextSearchResultName]
124119
[VectorStoreRecordData(IsFilterable = true)]
120+
public required string HotelName { get; set; }
125121

126-
#pragma warning disable CA1819
122+
[TextSearchResultValue]
123+
[VectorStoreRecordData(IsFullTextSearchable = true)]
124+
public required string Description { get; set; }
127125

128-
public required string[] Data3 { get; init; }
126+
[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
127+
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
129128

130-
#pragma warning restore CA1819
129+
[TextSearchResultLink]
130+
[VectorStoreRecordData]
131+
public string? ReferenceLink { get; set; }
131132
}

0 commit comments

Comments
 (0)