diff --git a/src/Example/Program.cs b/src/Example/Program.cs index 52186e82..b16e8db1 100644 --- a/src/Example/Program.cs +++ b/src/Example/Program.cs @@ -82,7 +82,7 @@ static async Task Main() Name = "Cat", Description = "Lots of Cats of multiple breeds", Properties = Property.FromCollection(), - VectorConfig = Vector.Name("default"), + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecWeaviate()), }; collection = await weaviate.Collections.Create(catCollection); diff --git a/src/Weaviate.Client.Tests/Integration/NearText.cs b/src/Weaviate.Client.Tests/Integration/NearText.cs index 05f66f4e..4d39be40 100644 --- a/src/Weaviate.Client.Tests/Integration/NearText.cs +++ b/src/Weaviate.Client.Tests/Integration/NearText.cs @@ -14,10 +14,10 @@ public async Task NearTextSearch() null, "Test collection description", [Property.Text("value")], - vectorConfig: Vector - .Name("default") - .With(new VectorizerConfig.Text2VecContextionary()) - .From(t => t.Value) + vectorConfig: new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary() { Properties = ["value"] } + ) ); string[] values = ["Apple", "Mountain climbing", "apple cake", "cake"]; @@ -54,7 +54,7 @@ public async Task Test_Search_NearText_GroupBy() "", "Test collection description", [Property.Text("value")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.Text2VecContextionary()) + vectorConfig: new VectorConfig("default", new Vectorizer.Text2VecContextionary()) ); string[] values = ["Apple", "Mountain climbing", "apple cake", "cake"]; diff --git a/src/Weaviate.Client.Tests/Integration/SingleTargetRef.cs b/src/Weaviate.Client.Tests/Integration/SingleTargetRef.cs index c38b2bda..11389358 100644 --- a/src/Weaviate.Client.Tests/Integration/SingleTargetRef.cs +++ b/src/Weaviate.Client.Tests/Integration/SingleTargetRef.cs @@ -126,7 +126,7 @@ public async Task Test_SingleTargetReference_Complex() Property.Int("movie_id"), ], references: [Property.Reference("forMovie", targetCollection: movies.Name)], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.Text2VecContextionary()) + vectorConfig: new VectorConfig("default", new Vectorizer.Text2VecContextionary()) ); var moviesData = new[] diff --git a/src/Weaviate.Client.Tests/Integration/TestBatch.cs b/src/Weaviate.Client.Tests/Integration/TestBatch.cs index 3530b90b..e26fc83c 100644 --- a/src/Weaviate.Client.Tests/Integration/TestBatch.cs +++ b/src/Weaviate.Client.Tests/Integration/TestBatch.cs @@ -52,7 +52,7 @@ public async Task Test_Batch_ReferenceAddMany() // Setup referenced collection ("To") var refCollection = await CollectionFactory( name: "To", - vectorConfig: Vector.Name("default"), + vectorConfig: new VectorConfig("default"), properties: [Property.Int("number")] ); int numObjects = 10; @@ -69,7 +69,7 @@ public async Task Test_Batch_ReferenceAddMany() name: "From", properties: [Property.Int("num")], references: [Property.Reference("ref", refCollection.Name)], - vectorConfig: Vector.Name("default") + vectorConfig: new VectorConfig("default") ); // Insert objects into the main collection and get their UUIDs diff --git a/src/Weaviate.Client.Tests/Integration/TestBatchDelete.cs b/src/Weaviate.Client.Tests/Integration/TestBatchDelete.cs index e3e869a6..2ba1bc11 100644 --- a/src/Weaviate.Client.Tests/Integration/TestBatchDelete.cs +++ b/src/Weaviate.Client.Tests/Integration/TestBatchDelete.cs @@ -11,7 +11,7 @@ public async Task Test_Delete_Many_Return() { var collection = await CollectionFactory( properties: [Property.Text("Name")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); await collection.Data.InsertMany(batcher => @@ -34,7 +34,7 @@ public async Task Test_Delete_Many_Or() { var collection = await CollectionFactory( properties: [Property.Text("Name"), Property.Int("Age")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); await collection.Data.InsertMany(batcher => @@ -62,7 +62,7 @@ public async Task Test_Delete_Many_And() { var collection = await CollectionFactory( properties: [Property.Text("Name"), Property.Int("Age")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); await collection.Data.InsertMany(batcher => @@ -90,7 +90,7 @@ await collection.Data.DeleteMany( public async Task Test_Dry_Run(bool dryRun) { var collection = await CollectionFactory( - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); var uuid1 = await collection.Data.Insert(new { }); @@ -128,7 +128,7 @@ public async Task Test_Dry_Run(bool dryRun) public async Task Test_Verbosity(bool verbose) { var collection = await CollectionFactory( - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); var uuid1 = await collection.Data.Insert(new { }); diff --git a/src/Weaviate.Client.Tests/Integration/TestCollections.cs b/src/Weaviate.Client.Tests/Integration/TestCollections.cs index b7a2a57a..5e9a0ff1 100644 --- a/src/Weaviate.Client.Tests/Integration/TestCollections.cs +++ b/src/Weaviate.Client.Tests/Integration/TestCollections.cs @@ -15,17 +15,17 @@ public async Task Test_Collections_List() await CollectionFactory( name: "Collection1", properties: [Property.Text("Name")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: Configure.Vectors.None() ), await CollectionFactory( name: "Collection2", properties: [Property.Text("Lastname")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: Configure.Vectors.None() ), await CollectionFactory( name: "Collection3", properties: [Property.Text("Address")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: Configure.Vectors.None() ), }; @@ -46,7 +46,7 @@ public async Task Test_Collections_Exists() { var collection = await CollectionFactory( properties: [Property.Text("Name")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); bool exists = await _weaviate.Collections.Exists(collection.Name); @@ -69,7 +69,7 @@ public async Task Test_Collections_Export() name: "MyOwnSuffix", description: "My own description too", properties: [Property.Text("Name")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); var export = await _weaviate.Collections.Export(collection.Name); @@ -142,50 +142,50 @@ public async Task Test_Collections_Export() // VectorIndexConfig validation Assert.NotNull(defaultVectorConfig.VectorIndexConfig); - Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Identifier); - Assert.NotNull(defaultVectorConfig.VectorIndexConfig.Configuration); + Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Type); + Assert.NotNull(defaultVectorConfig.VectorIndexConfig); - var config = defaultVectorConfig.VectorIndexConfig.Configuration; + var config = defaultVectorConfig.VectorIndexConfig as VectorIndex.HNSW; // HNSW specific configuration assertions - Assert.Equal("cosine", config?.distance); - Assert.Equal(8, config?.dynamicEfFactor); - Assert.Equal(500, config?.dynamicEfMax); - Assert.Equal(100, config?.dynamicEfMin); - Assert.Equal(-1, config?.ef); - Assert.Equal(128, config?.efConstruction); - Assert.Equal("sweeping", config?.filterStrategy); - Assert.Equal(40000, config?.flatSearchCutoff); - Assert.Equal(32, config?.maxConnections); - Assert.Equal(300, config?.cleanupIntervalSeconds); - Assert.False(config?.skip); - Assert.Equal(1000000000000L, config?.vectorCacheMaxObjects); - - // Binary Quantization (bq) validation - Assert.NotNull(config?.bq); - Assert.False(config?.bq.enabled); - - // Product Quantization (pq) validation - Assert.NotNull(config?.pq); - Assert.False(config?.pq.enabled); - Assert.False(config?.pq.bitCompression); - Assert.Equal(256, config?.pq.centroids); - Assert.Equal(0, config?.pq.segments); - Assert.Equal(100000, config?.pq.trainingLimit); - Assert.NotNull(config?.pq.encoder); - Assert.Equal("log-normal", config?.pq.encoder.distribution); - Assert.Equal("kmeans", config?.pq.encoder.type); - - // Scalar Quantization (sq) validation - Assert.NotNull(config?.sq); - Assert.False(config?.sq.enabled); - Assert.Equal(20, config?.sq.rescoreLimit); - Assert.Equal(100000, config?.sq.trainingLimit); - - // Multivector validation - Assert.NotNull(config?.multivector); - Assert.False(config?.multivector.enabled); - Assert.Equal("maxSim", config?.multivector.aggregation); + Assert.Equal(VectorIndexConfig.VectorDistance.Cosine, config?.Distance); + Assert.Equal(8, config?.DynamicEfFactor); + Assert.Equal(500, config?.DynamicEfMax); + Assert.Equal(100, config?.DynamicEfMin); + Assert.Equal(-1, config?.Ef); + Assert.Equal(128, config?.EfConstruction); + Assert.Equal(VectorIndexConfig.VectorIndexFilterStrategy.Sweeping, config?.FilterStrategy); + Assert.Equal(40000, config?.FlatSearchCutoff); + Assert.Equal(32, config?.MaxConnections); + Assert.Equal(300, config?.CleanupIntervalSeconds); + Assert.False(config?.Skip); + Assert.Equal(1000000000000L, config?.VectorCacheMaxObjects); + + // TODO: Binary Quantization (bq) validation + // Assert.NotNull(config?.bq); + // Assert.False(config?.bq.enabled); + + // TODO: Product Quantization (pq) validation + // Assert.NotNull(config?.pq); + // Assert.False(config?.pq.enabled); + // Assert.False(config?.pq.bitCompression); + // Assert.Equal(256, config?.pq.centroids); + // Assert.Equal(0, config?.pq.segments); + // Assert.Equal(100000, config?.pq.trainingLimit); + // Assert.NotNull(config?.pq.encoder); + // Assert.Equal("log-normal", config?.pq.encoder.distribution); + // Assert.Equal("kmeans", config?.pq.encoder.type); + + // TODO: Scalar Quantization (sq) validation + // Assert.NotNull(config?.sq); + // Assert.False(config?.sq.enabled); + // Assert.Equal(20, config?.sq.rescoreLimit); + // Assert.Equal(100000, config?.sq.trainingLimit); + + // TODO: Multivector validation + // Assert.NotNull(config?.multivector); + // Assert.False(config?.multivector.enabled); + // Assert.Equal("maxSim", config?.multivector.aggregation); // Available from v1.31 // Assert.NotNull(config?.multivector.muvera); @@ -211,9 +211,10 @@ public async Task Test_Collections_Export_NonDefaultValues_Sharding() properties: [Property.Text("Name"), Property.Int("SomeNumber")], references: null, collectionNamePartSeparator: "", - vectorConfig: Vector - .Name("nondefault") - .With(new VectorizerConfig.Text2VecContextionary() { VectorizeClassName = false }), + vectorConfig: new VectorConfig( + "nondefault", + new Vectorizer.Text2VecContextionary() { VectorizeClassName = false } + ), invertedIndexConfig: new() { Bm25 = new() { B = 0.70f, K1 = 1.3f }, @@ -314,50 +315,50 @@ public async Task Test_Collections_Export_NonDefaultValues_Sharding() // VectorIndexConfig validation Assert.NotNull(defaultVectorConfig.VectorIndexConfig); - Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Identifier); - Assert.NotNull(defaultVectorConfig.VectorIndexConfig.Configuration); + Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Type); + Assert.NotNull(defaultVectorConfig.VectorIndexConfig); - var config = defaultVectorConfig.VectorIndexConfig.Configuration; + var config = defaultVectorConfig.VectorIndexConfig as VectorIndex.HNSW; // HNSW specific configuration assertions - Assert.Equal("cosine", config?.distance); - Assert.Equal(8, config?.dynamicEfFactor); - Assert.Equal(500, config?.dynamicEfMax); - Assert.Equal(100, config?.dynamicEfMin); - Assert.Equal(-1, config?.ef); - Assert.Equal(128, config?.efConstruction); - Assert.Equal("sweeping", config?.filterStrategy); - Assert.Equal(40000, config?.flatSearchCutoff); - Assert.Equal(32, config?.maxConnections); - Assert.Equal(300, config?.cleanupIntervalSeconds); - Assert.False(config?.skip); - Assert.Equal(1000000000000L, config?.vectorCacheMaxObjects); - - // Binary Quantization (bq) validation - Assert.NotNull(config?.bq); - Assert.False(config?.bq.enabled); - - // Product Quantization (pq) validation - Assert.NotNull(config?.pq); - Assert.False(config?.pq.enabled); - Assert.False(config?.pq.bitCompression); - Assert.Equal(256, config?.pq.centroids); - Assert.Equal(0, config?.pq.segments); - Assert.Equal(100000, config?.pq.trainingLimit); - Assert.NotNull(config?.pq.encoder); - Assert.Equal("log-normal", config?.pq.encoder.distribution); - Assert.Equal("kmeans", config?.pq.encoder.type); - - // Scalar Quantization (sq) validation - Assert.NotNull(config?.sq); - Assert.False(config?.sq.enabled); - Assert.Equal(20, config?.sq.rescoreLimit); - Assert.Equal(100000, config?.sq.trainingLimit); - - // Multivector validation - Assert.NotNull(config?.multivector); - Assert.False(config?.multivector.enabled); - Assert.Equal("maxSim", config?.multivector.aggregation); + Assert.Equal(VectorIndexConfig.VectorDistance.Cosine, config?.Distance); + Assert.Equal(8, config?.DynamicEfFactor); + Assert.Equal(500, config?.DynamicEfMax); + Assert.Equal(100, config?.DynamicEfMin); + Assert.Equal(-1, config?.Ef); + Assert.Equal(128, config?.EfConstruction); + Assert.Equal(VectorIndexConfig.VectorIndexFilterStrategy.Sweeping, config?.FilterStrategy); + Assert.Equal(40000, config?.FlatSearchCutoff); + Assert.Equal(32, config?.MaxConnections); + Assert.Equal(300, config?.CleanupIntervalSeconds); + Assert.False(config?.Skip); + Assert.Equal(1000000000000L, config?.VectorCacheMaxObjects); + + // TODO: Binary Quantization (bq) validation + // Assert.NotNull(config?.bq); + // Assert.False(config?.bq.enabled); + + // TODO: Product Quantization (pq) validation + // Assert.NotNull(config?.pq); + // Assert.False(config?.pq.enabled); + // Assert.False(config?.pq.bitCompression); + // Assert.Equal(256, config?.pq.centroids); + // Assert.Equal(0, config?.pq.segments); + // Assert.Equal(100000, config?.pq.trainingLimit); + // Assert.NotNull(config?.pq.encoder); + // Assert.Equal("log-normal", config?.pq.encoder.distribution); + // Assert.Equal("kmeans", config?.pq.encoder.type); + + // TODO: Scalar Quantization (sq) validation + // Assert.NotNull(config?.sq); + // Assert.False(config?.sq.enabled); + // Assert.Equal(20, config?.sq.rescoreLimit); + // Assert.Equal(100000, config?.sq.trainingLimit); + + // TODO: Multivector validation + // Assert.NotNull(config?.multivector); + // Assert.False(config?.multivector.enabled); + // Assert.Equal("maxSim", config?.multivector.aggregation); // Available from v1.31 // Assert.NotNull(config?.multivector.muvera); @@ -383,9 +384,10 @@ public async Task Test_Collections_Export_NonDefaultValues_MultiTenacy() properties: [Property.Text("Name"), Property.Int("SomeNumber")], references: null, collectionNamePartSeparator: "", - vectorConfig: Vector - .Name("nondefault") - .With(new VectorizerConfig.Text2VecContextionary() { VectorizeClassName = false }), + vectorConfig: new VectorConfig( + "nondefault", + new Vectorizer.Text2VecContextionary() { VectorizeClassName = false } + ), multiTenancyConfig: new() { AutoTenantActivation = true, @@ -484,50 +486,50 @@ public async Task Test_Collections_Export_NonDefaultValues_MultiTenacy() // VectorIndexConfig validation Assert.NotNull(defaultVectorConfig.VectorIndexConfig); - Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Identifier); - Assert.NotNull(defaultVectorConfig.VectorIndexConfig.Configuration); + Assert.Equal("hnsw", defaultVectorConfig.VectorIndexConfig.Type); + Assert.NotNull(defaultVectorConfig.VectorIndexConfig as VectorIndex.HNSW); - var config = defaultVectorConfig.VectorIndexConfig.Configuration; + var config = defaultVectorConfig.VectorIndexConfig as VectorIndex.HNSW; // HNSW specific configuration assertions - Assert.Equal("cosine", config?.distance); - Assert.Equal(8, config?.dynamicEfFactor); - Assert.Equal(500, config?.dynamicEfMax); - Assert.Equal(100, config?.dynamicEfMin); - Assert.Equal(-1, config?.ef); - Assert.Equal(128, config?.efConstruction); - Assert.Equal("sweeping", config?.filterStrategy); - Assert.Equal(40000, config?.flatSearchCutoff); - Assert.Equal(32, config?.maxConnections); - Assert.Equal(300, config?.cleanupIntervalSeconds); - Assert.False(config?.skip); - Assert.Equal(1000000000000L, config?.vectorCacheMaxObjects); - - // Binary Quantization (bq) validation - Assert.NotNull(config?.bq); - Assert.False(config?.bq.enabled); - - // Product Quantization (pq) validation - Assert.NotNull(config?.pq); - Assert.False(config?.pq.enabled); - Assert.False(config?.pq.bitCompression); - Assert.Equal(256, config?.pq.centroids); - Assert.Equal(0, config?.pq.segments); - Assert.Equal(100000, config?.pq.trainingLimit); - Assert.NotNull(config?.pq.encoder); - Assert.Equal("log-normal", config?.pq.encoder.distribution); - Assert.Equal("kmeans", config?.pq.encoder.type); - - // Scalar Quantization (sq) validation - Assert.NotNull(config?.sq); - Assert.False(config?.sq.enabled); - Assert.Equal(20, config?.sq.rescoreLimit); - Assert.Equal(100000, config?.sq.trainingLimit); - - // Multivector validation - Assert.NotNull(config?.multivector); - Assert.False(config?.multivector.enabled); - Assert.Equal("maxSim", config?.multivector.aggregation); + Assert.Equal(VectorIndexConfig.VectorDistance.Cosine, config?.Distance); + Assert.Equal(8, config?.DynamicEfFactor); + Assert.Equal(500, config?.DynamicEfMax); + Assert.Equal(100, config?.DynamicEfMin); + Assert.Equal(-1, config?.Ef); + Assert.Equal(128, config?.EfConstruction); + Assert.Equal(VectorIndexConfig.VectorIndexFilterStrategy.Sweeping, config?.FilterStrategy); + Assert.Equal(40000, config?.FlatSearchCutoff); + Assert.Equal(32, config?.MaxConnections); + Assert.Equal(300, config?.CleanupIntervalSeconds); + Assert.False(config?.Skip); + Assert.Equal(1000000000000L, config?.VectorCacheMaxObjects); + + // TODO: Binary Quantization (bq) validation + // Assert.NotNull(config?.bq); + // Assert.False(config?.bq.enabled); + + // TODO: Product Quantization (pq) validation + // Assert.NotNull(config?.pq); + // Assert.False(config?.pq.enabled); + // Assert.False(config?.pq.bitCompression); + // Assert.Equal(256, config?.pq.centroids); + // Assert.Equal(0, config?.pq.segments); + // Assert.Equal(100000, config?.pq.trainingLimit); + // Assert.NotNull(config?.pq.encoder); + // Assert.Equal("log-normal", config?.pq.encoder.distribution); + // Assert.Equal("kmeans", config?.pq.encoder.type); + + // TODO: Scalar Quantization (sq) validation + // Assert.NotNull(config?.sq); + // Assert.False(config?.sq.enabled); + // Assert.Equal(20, config?.sq.rescoreLimit); + // Assert.Equal(100000, config?.sq.trainingLimit); + + // TODO: Multivector validation + // Assert.NotNull(config?.multivector); + // Assert.False(config?.multivector.enabled); + // Assert.Equal("maxSim", config?.multivector.aggregation); // Available from v1.31 // Assert.NotNull(config?.multivector.muvera); diff --git a/src/Weaviate.Client.Tests/Integration/TestFilters.cs b/src/Weaviate.Client.Tests/Integration/TestFilters.cs index b053e816..52881a14 100644 --- a/src/Weaviate.Client.Tests/Integration/TestFilters.cs +++ b/src/Weaviate.Client.Tests/Integration/TestFilters.cs @@ -1,5 +1,4 @@ using Weaviate.Client.Models; -using Weaviate.Client.Models.Vectorizers; namespace Weaviate.Client.Tests.Integration; @@ -323,7 +322,7 @@ public async Task FilterArrayTypes(string key) // Arrange var collection = await CollectionFactory( - vectorConfig: Vector.Name("default"), + vectorConfig: new VectorConfig("default"), properties: [ Property.TextArray("texts"), @@ -378,7 +377,7 @@ public async Task FilterContains(string test) // Arrange var collection = await CollectionFactory( - vectorConfig: Vector.Name(Vector.DefaultVectorName), + vectorConfig: new VectorConfig("default"), properties: [ Property.Text("text"), diff --git a/src/Weaviate.Client.Tests/Integration/TestIterator.cs b/src/Weaviate.Client.Tests/Integration/TestIterator.cs index 7f46a62b..b906b282 100644 --- a/src/Weaviate.Client.Tests/Integration/TestIterator.cs +++ b/src/Weaviate.Client.Tests/Integration/TestIterator.cs @@ -1,5 +1,4 @@ using Weaviate.Client.Models; -using Weaviate.Client.Models.Vectorizers; namespace Weaviate.Client.Tests.Integration; @@ -10,7 +9,7 @@ public async Task Test_Iterator() { var collection = await CollectionFactory( properties: [Property.Text("name")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); await collection.Data.InsertMany(new { Name = "Name 1" }, new { Name = "Name 2" }); @@ -61,7 +60,7 @@ public async Task Test_Iterator_Arguments( { var collection = await CollectionFactory( properties: [Property.Int("data"), Property.Text("text")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.Text2VecContextionary()) + vectorConfig: new VectorConfig("default", new Vectorizer.Text2VecContextionary()) ); // Insert test data @@ -167,7 +166,7 @@ public async Task Test_Iterator_With_Default_Generic() { var collection = await CollectionFactory( properties: [Property.Text("this"), Property.Text("that")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); var insertData = Enumerable @@ -215,7 +214,7 @@ public async Task Test_Iterator_Basic(uint count) { var collection = await CollectionFactory( properties: [Property.Int("data")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); if (count > 0) @@ -261,7 +260,7 @@ public async Task Test_Iterator_With_After() { var collection = await CollectionFactory( properties: [Property.Int("data")], - vectorConfig: Vector.Name("default").With(new VectorizerConfig.None()) + vectorConfig: new VectorConfig("default", new Vectorizer.None()) ); var insertData = Enumerable.Range(0, 10).Select(i => new { data = i }).ToArray(); diff --git a/src/Weaviate.Client.Tests/Integration/_Integration.cs b/src/Weaviate.Client.Tests/Integration/_Integration.cs index 3c955d18..b74a7145 100644 --- a/src/Weaviate.Client.Tests/Integration/_Integration.cs +++ b/src/Weaviate.Client.Tests/Integration/_Integration.cs @@ -80,7 +80,7 @@ protected async Task> CollectionFactory( ArgumentException.ThrowIfNullOrEmpty(name); // Default is VectorizerConfig.None - vectorConfig ??= Vector.Name("default"); + vectorConfig ??= new VectorConfig("default"); references ??= []; diff --git a/src/Weaviate.Client.Tests/Unit/TestVectorizers.cs b/src/Weaviate.Client.Tests/Unit/TestVectorizers.cs index 011ef421..ce53fea7 100644 --- a/src/Weaviate.Client.Tests/Unit/TestVectorizers.cs +++ b/src/Weaviate.Client.Tests/Unit/TestVectorizers.cs @@ -1,41 +1,76 @@ using System.Text.Json; using Weaviate.Client.Models; using Weaviate.Client.Models.Vectorizers; +using Quantizers = Weaviate.Client.Models.VectorIndex.Quantizers; namespace Weaviate.Client.Tests; public partial class UnitTests { [Fact] - public void Test_VectorizerList_ImplicitConversion() + public void Test_VectorConfigList() { + var contextionaryVectorizer = Configure.Vectors.Text2VecContextionary(true); + // Arrange VectorConfigList ncList = new[] { - Vector.Name("default").With(new VectorizerConfig.Text2VecContextionary()).From("name"), - Vector.Name("fromSizes").With(new VectorizerConfig.Text2VecWeaviate()).From("size"), - Vector - .Name("location") - .With(new VectorizerConfig.Text2VecContextionary()) - .From("location"), - Vector - .Name("nein") - .With(new VectorizerConfig.None()) - .With(new VectorIndexConfig.HNSW()), - Vector.Name("built").With(new VectorizerConfig.None()).Build(), + new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary { Properties = ["breed", "color"] }, + new VectorIndex.HNSW() + { + Distance = VectorIndexConfig.VectorDistance.Cosine, + Quantizer = new Quantizers.PQConfig + { + Encoder = new Quantizers.PQConfig.EncoderConfig + { + Distribution = Quantizers.DistributionType.Normal, + Type = Quantizers.EncoderType.Kmeans, + }, + }, + } + ), + new VectorConfig( + "fromSizes", + new Vectorizer.Text2VecContextionary { Properties = ["size"] } + ), + new VectorConfig( + "location", + new Vectorizer.Text2VecContextionary { Properties = ["location"] } + ), + new VectorConfig("nein", new Vectorizer.None()), + contextionaryVectorizer.New("contextionary1", properties: ["breed"]), + contextionaryVectorizer.New("contextionary2", properties: ["color"]), + Configure + .Vectors.Text2VecWeaviate(vectorizeCollectionName: true) + .New("weaviate", properties: ["color"]), + Configure.Vectors.Img2VecNeural([]).New("neural", properties: ["color"]), }; // Act // Assert - Assert.Equal(["default", "fromSizes", "location", "nein", "built"], ncList.Keys); + Assert.Equal( + [ + "default", + "fromSizes", + "location", + "nein", + "contextionary1", + "contextionary2", + "weaviate", + "neural", + ], + ncList.Keys + ); } [Fact] public void Test_NamedVectorConfig_None_Has_No_Properties() { // Arrange - var vc = Vector.Name(Vector.DefaultVectorName).With(new VectorizerConfig.None()).Build(); + var vc = new VectorConfig("default", new Vectorizer.None()); // Act var dto = vc.Vectorizer?.ToDto() ?? default; @@ -62,10 +97,10 @@ public void Test_NamedVectorConfig_None_Deserialization() public void Test_NamedVectorConfig_Has_Properties() { // Arrange - var defaultVec = Vector - .Name("default") - .With(new VectorizerConfig.Text2VecContextionary()) - .From("name"); + var defaultVec = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary() { Properties = ["name"] } + ); // Build explicitely, when typing as VectorConfig is needed, // like when accessing the Vectorizer property. diff --git a/src/Weaviate.Client/Configure/Vectorizer.cs b/src/Weaviate.Client/Configure/Vectorizer.cs new file mode 100644 index 00000000..3ae3f031 --- /dev/null +++ b/src/Weaviate.Client/Configure/Vectorizer.cs @@ -0,0 +1,467 @@ +using Weaviate.Client.Models; + +namespace Weaviate.Client; + +public static partial class Configure +{ + public static class Vectors + { + public static VectorConfig None(string name = "default") => new(name); + + public class VectorConfigBuilder(VectorizerConfig Config) + { + public VectorConfig New( + string name, + VectorIndexConfig? indexConfig = null, + params string[] properties + ) => + new( + name, + vectorizer: Config with + { + Properties = properties, + }, + vectorIndexConfig: indexConfig + ); + } + + public static VectorConfigBuilder Img2VecNeural(string[] imageFields) => + new(new Vectorizer.Img2VecNeural { ImageFields = imageFields }); + + public static VectorConfigBuilder Text2VecContextionary(bool? vectorizeClassName = null) => + new(new Vectorizer.Text2VecContextionary() { VectorizeClassName = vectorizeClassName }); + + public static VectorConfigBuilder Text2VecWeaviate( + string? baseURL = null, + int? dimensions = null, + string? model = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecWeaviate + { + BaseURL = baseURL, + Dimensions = dimensions, + Model = model, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Multi2VecClip( + string[]? imageFields = null, + string? inferenceUrl = null, + string[]? textFields = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecClip + { + ImageFields = imageFields, + InferenceUrl = inferenceUrl, + TextFields = textFields, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Multi2VecCohere( + string? baseURL = null, + string[]? imageFields = null, + string? model = null, + string[]? textFields = null, + string? truncate = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecCohereWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecCohere + { + BaseURL = baseURL, + ImageFields = imageFields, + Model = model, + TextFields = textFields, + Truncate = truncate, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Multi2VecBind( + string[]? audioFields = null, + string[]? depthFields = null, + string[]? imageFields = null, + string[]? imuFields = null, + string[]? textFields = null, + string[]? thermalFields = null, + string[]? videoFields = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecBindWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecBind + { + AudioFields = audioFields, + DepthFields = depthFields, + ImageFields = imageFields, + IMUFields = imuFields, + TextFields = textFields, + ThermalFields = thermalFields, + VideoFields = videoFields, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Multi2VecGoogle( + string projectId, + string location, + string[]? imageFields = null, + string[]? textFields = null, + string[]? videoFields = null, + int? videoIntervalSeconds = null, + string? modelId = null, + int? dimensions = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecGoogleWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecGoogle + { + ProjectId = projectId, + Location = location, + ImageFields = imageFields, + TextFields = textFields, + VideoFields = videoFields, + VideoIntervalSeconds = videoIntervalSeconds, + ModelId = modelId, + Dimensions = dimensions, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + [Obsolete("Use Multi2VecGoogle instead.")] + public static VectorConfigBuilder Multi2VecPalm( + string projectId, + string location, + string[]? imageFields = null, + string[]? textFields = null, + string[]? videoFields = null, + int? videoIntervalSeconds = null, + string? modelId = null, + int? dimensions = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecGoogleWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecPalm + { + ProjectId = projectId, + Location = location, + ImageFields = imageFields, + TextFields = textFields, + VideoFields = videoFields, + VideoIntervalSeconds = videoIntervalSeconds, + ModelId = modelId, + Dimensions = dimensions, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Multi2VecJinaAI( + string? baseURL = null, + int? dimensions = null, + string[]? imageFields = null, + string? model = null, + string[]? textFields = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecJinaAIWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecJinaAI + { + BaseURL = baseURL, + Dimensions = dimensions, + ImageFields = imageFields, + Model = model, + TextFields = textFields, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Multi2VecVoyageAI( + string? baseURL = null, + string[]? imageFields = null, + string? model = null, + string? outputEncoding = null, + string[]? textFields = null, + bool? truncate = null, + bool? vectorizeCollectionName = null, + Vectorizer.Multi2VecVoyageAIWeights? weights = null + ) => + new( + new Vectorizer.Multi2VecVoyageAI + { + BaseURL = baseURL, + ImageFields = imageFields, + Model = model, + OutputEncoding = outputEncoding, + TextFields = textFields, + Truncate = truncate, + VectorizeCollectionName = vectorizeCollectionName, + Weights = weights, + } + ); + + public static VectorConfigBuilder Ref2VecCentroid( + string[] referenceProperties, + string method = "mean" + ) => + new( + new Vectorizer.Ref2VecCentroid + { + ReferenceProperties = referenceProperties, + Method = method, + } + ); + + public static VectorConfigBuilder Text2VecAWS( + string region, + string service, + string? endpoint = null, + string? model = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecAWS + { + Region = region, + Service = service, + Endpoint = endpoint, + Model = model, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecAzureOpenAI( + string deploymentId, + string resourceName, + string? baseURL = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecAzureOpenAI + { + DeploymentId = deploymentId, + ResourceName = resourceName, + BaseURL = baseURL, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecCohere( + string? baseURL = null, + string? model = null, + bool? truncate = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecCohere + { + BaseURL = baseURL, + Model = model, + Truncate = truncate, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecDatabricks( + string endpoint, + string? instruction = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecDatabricks + { + Endpoint = endpoint, + Instruction = instruction, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecGPT4All(bool? vectorizeCollectionName = null) => + new( + new Vectorizer.Text2VecGPT4All { VectorizeCollectionName = vectorizeCollectionName } + ); + + public static VectorConfigBuilder Text2VecHuggingFace( + string? endpointURL = null, + string? model = null, + string? passageModel = null, + string? queryModel = null, + bool? useCache = null, + bool? useGPU = null, + bool? waitForModel = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecHuggingFace + { + EndpointURL = endpointURL, + Model = model, + PassageModel = passageModel, + QueryModel = queryModel, + UseCache = useCache, + UseGPU = useGPU, + WaitForModel = waitForModel, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecJinaAI( + string? model = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecJinaAI + { + Model = model, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + [Obsolete("Use Text2VecJinaAI instead.")] + public static VectorConfigBuilder Text2VecJinaConfig( + string? model = null, + bool? vectorizeCollectionName = null + ) => new(new Vectorizer.Text2VecJinaConfig()); + + public static VectorConfigBuilder Text2VecNvidia( + string? baseURL = null, + string? model = null, + bool? truncate = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecNvidia + { + BaseURL = baseURL, + Model = model, + Truncate = truncate, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecMistral( + string? baseURL = null, + string? model = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecMistral + { + BaseURL = baseURL, + Model = model, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecOllama( + string? apiEndpoint = null, + string? model = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecOllama + { + ApiEndpoint = apiEndpoint, + Model = model, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecOpenAI( + string? baseURL = null, + int? dimensions = null, + string? model = null, + string? modelVersion = null, + string? type = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecOpenAI + { + BaseURL = baseURL, + Dimensions = dimensions, + Model = model, + ModelVersion = modelVersion, + Type = type, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + [Obsolete("Use Text2VecGoogle instead.")] + public static VectorConfigBuilder Text2VecPalm( + string? apiEndpoint = null, + string? modelId = null, + string? projectId = null, + string? titleProperty = null, + bool? vectorizeCollectionName = null + ) => new(new Vectorizer.Text2VecPalm()); + + public static VectorConfigBuilder Text2VecGoogle( + string? apiEndpoint = null, + string? modelId = null, + string? projectId = null, + string? titleProperty = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecGoogle + { + ApiEndpoint = apiEndpoint, + ModelId = modelId, + ProjectId = projectId, + TitleProperty = titleProperty, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecTransformers( + string? inferenceUrl = null, + string? passageInferenceUrl = null, + string? queryInferenceUrl = null, + string? poolingStrategy = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecTransformers + { + InferenceUrl = inferenceUrl, + PassageInferenceUrl = passageInferenceUrl, + QueryInferenceUrl = queryInferenceUrl, + PoolingStrategy = poolingStrategy, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + + public static VectorConfigBuilder Text2VecVoyageAI( + string? baseURL = null, + string? model = null, + bool? truncate = null, + bool? vectorizeCollectionName = null + ) => + new( + new Vectorizer.Text2VecVoyageAI + { + BaseURL = baseURL, + Model = model, + Truncate = truncate, + VectorizeCollectionName = vectorizeCollectionName, + } + ); + } +} diff --git a/src/Weaviate.Client/Extensions.cs b/src/Weaviate.Client/Extensions.cs index ad7adcbf..e5e8075c 100644 --- a/src/Weaviate.Client/Extensions.cs +++ b/src/Weaviate.Client/Extensions.cs @@ -34,7 +34,7 @@ internal static Rest.Dto.Class ToDto(this Collection collection) e => e.Name, e => new Rest.Dto.VectorConfig { - VectorIndexConfig = e.VectorIndexConfig?.Configuration, + VectorIndexConfig = e.VectorIndexConfig, VectorIndexType = e.VectorIndexType, Vectorizer = e.Vectorizer?.ToDto(), } @@ -123,7 +123,7 @@ internal static Collection ToModel(this Rest.Dto.Class collection) } } - return new VectorConfig(name) { Vectorizer = vc, VectorIndexConfig = vic }; + return new VectorConfig(name, vc, vic); }; var vectorConfig = new VectorConfigList( diff --git a/src/Weaviate.Client/Models/VectorConfig.cs b/src/Weaviate.Client/Models/VectorConfig.cs index 2249c8b8..cc68593d 100644 --- a/src/Weaviate.Client/Models/VectorConfig.cs +++ b/src/Weaviate.Client/Models/VectorConfig.cs @@ -1,21 +1,41 @@ -using Weaviate.Client.Models.Vectorizers; - namespace Weaviate.Client.Models; -public record VectorConfig(string Name) +/// +/// Represents a vector configuration with name, vectorizer, and index settings. +/// +public record VectorConfig { + /// The name of the vector configuration. + /// Configuration of a specific vectorizer used by this vector. + /// Vector-index config, that is specific to the type of index selected in vectorIndexType. + public VectorConfig( + string name, + VectorizerConfig? vectorizer = null, + VectorIndexConfig? vectorIndexConfig = null + ) + { + Name = name; + Vectorizer = vectorizer ?? new Vectorizer.None(); + VectorIndexConfig = vectorIndexConfig ?? new VectorIndex.HNSW(); + } + /// - /// Vector-index config, that is specific to the type of index selected in vectorIndexType. + /// Name of the vector index to use, eg. (HNSW). /// - public VectorIndexConfig? VectorIndexConfig { get; set; } + public string? VectorIndexType => VectorIndexConfig?.Type; /// - /// Name of the vector index to use, eg. (HNSW). + /// Name of the vector configuration. /// - public string? VectorIndexType => VectorIndexConfig?.Identifier; + public string Name { get; } /// /// Configuration of a specific vectorizer used by this vector. /// - public VectorizerConfig? Vectorizer { get; set; } + public VectorizerConfig? Vectorizer { get; } + + /// + /// Vector-index config, that is specific to the type of index selected in vectorIndexType. + /// + public VectorIndexConfig? VectorIndexConfig { get; } } diff --git a/src/Weaviate.Client/Models/VectorIndex.cs b/src/Weaviate.Client/Models/VectorIndex.cs new file mode 100644 index 00000000..ddddef48 --- /dev/null +++ b/src/Weaviate.Client/Models/VectorIndex.cs @@ -0,0 +1,182 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using static Weaviate.Client.Models.VectorIndexConfig; + +namespace Weaviate.Client.Models; + +public abstract record VectorIndexConfig() +{ + [JsonIgnore] + public abstract string Type { get; } + + internal static VectorIndexConfig Factory(string type, object? vectorIndexConfig) + { + VectorIndexConfig? result = null; + + JsonSerializerOptions options = new() + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true, // For readability + Converters = { new JsonStringEnumConverter(namingPolicy: JsonNamingPolicy.CamelCase) }, + }; + + if (vectorIndexConfig is JsonElement vic) + { + result = type switch + { + "hnsw" => JsonSerializer.Deserialize(vic.GetRawText(), options), + "flat" => JsonSerializer.Deserialize(vic.GetRawText(), options), + "dynamic" => JsonSerializer.Deserialize( + vic.GetRawText(), + options + ), + _ => null, + }; + } + + return result ?? throw new WeaviateException("Unable to create VectorIndexConfig"); + } + + public enum VectorDistance + { + Cosine, + Dot, + L2Squared, + Hamming, + } + + public enum VectorIndexFilterStrategy + { + Sweeping, + Acorn, + } + + public abstract class QuantizerConfig + { + public abstract string Type { get; } + } +} + +public static class VectorIndex +{ + public static class Quantizers + { + public enum DistributionType + { + LogNormal, + Normal, + } + + public enum EncoderType + { + Kmeans, + Tile, + } + + public class BQConfig : QuantizerConfig + { + public bool Cache { get; set; } + public int RescoreLimit { get; set; } + public override string Type => "bq"; + } + + public class SQConfig : QuantizerConfig + { + public int RescoreLimit { get; set; } + public int TrainingLimit { get; set; } + public override string Type => "sq"; + } + + public class PQConfig : QuantizerConfig + { + public class EncoderConfig + { + public EncoderType Type { get; set; } + public DistributionType Distribution { get; set; } + } + + public bool BitCompression { get; set; } + public int Centroids { get; set; } + public required EncoderConfig Encoder { get; set; } + public int Segments { get; set; } + public int TrainingLimit { get; set; } + + public override string Type => "pq"; + } + } + + public sealed record HNSW : VectorIndexConfig + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? CleanupIntervalSeconds { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public VectorDistance? Distance { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? DynamicEfMin { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? DynamicEfMax { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? DynamicEfFactor { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? EfConstruction { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Ef { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public VectorIndexFilterStrategy? FilterStrategy { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? FlatSearchCutoff { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxConnections { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public QuantizerConfig? Quantizer { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Skip { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public long? VectorCacheMaxObjects { get; set; } + + [JsonIgnore] + public override string Type => "hnsw"; + } + + public sealed record Flat : VectorIndexConfig + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public VectorDistance? Distance { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? VectorCacheMaxObjects { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Quantizers.BQConfig? Quantizer { get; set; } + + [JsonIgnore] + public override string Type => "flat"; + } + + public sealed record Dynamic : VectorIndexConfig + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public VectorDistance? Distance { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? Threshold { get; set; } + public required HNSW? Hnsw { get; set; } + public required Flat? Flat { get; set; } + + [JsonIgnore] + public override string Type => "dynamic"; + } +} diff --git a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.Declarations.cs b/src/Weaviate.Client/Models/Vectorizer.Declarations.cs similarity index 99% rename from src/Weaviate.Client/Models/Vectorizers/Vectorizer.Declarations.cs rename to src/Weaviate.Client/Models/Vectorizer.Declarations.cs index 9eb70deb..6acd157c 100644 --- a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.Declarations.cs +++ b/src/Weaviate.Client/Models/Vectorizer.Declarations.cs @@ -1,6 +1,6 @@ -namespace Weaviate.Client.Models.Vectorizers; +namespace Weaviate.Client.Models; -public abstract partial record VectorizerConfig +public static partial class Vectorizer { // All record constructors for derived types public partial record None : VectorizerConfig diff --git a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.Properties.cs b/src/Weaviate.Client/Models/Vectorizer.cs similarity index 99% rename from src/Weaviate.Client/Models/Vectorizers/Vectorizer.Properties.cs rename to src/Weaviate.Client/Models/Vectorizer.cs index 5f730cb3..f125f521 100644 --- a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.Properties.cs +++ b/src/Weaviate.Client/Models/Vectorizer.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace Weaviate.Client.Models.Vectorizers; +namespace Weaviate.Client.Models; -public abstract partial record VectorizerConfig +public static partial class Vectorizer { public partial record None { } diff --git a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.cs b/src/Weaviate.Client/Models/VectorizerConfig.cs similarity index 90% rename from src/Weaviate.Client/Models/Vectorizers/Vectorizer.cs rename to src/Weaviate.Client/Models/VectorizerConfig.cs index cd2b15b4..6f9f25aa 100644 --- a/src/Weaviate.Client/Models/Vectorizers/Vectorizer.cs +++ b/src/Weaviate.Client/Models/VectorizerConfig.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace Weaviate.Client.Models.Vectorizers; +namespace Weaviate.Client.Models; -public abstract partial record VectorizerConfig +public abstract record VectorizerConfig { private readonly string _identifier; protected HashSet _properties = new(); diff --git a/src/Weaviate.Client/Models/Vectorizers/Factory.cs b/src/Weaviate.Client/Models/Vectorizers/Factory.cs index 9b0a229f..0e50cf48 100644 --- a/src/Weaviate.Client/Models/Vectorizers/Factory.cs +++ b/src/Weaviate.Client/Models/Vectorizers/Factory.cs @@ -10,31 +10,31 @@ internal static class VectorizerConfigFactory { private static readonly Dictionary _configTypes = new() { - { "none", typeof(VectorizerConfig.None) }, - { "img2vec-neural", typeof(VectorizerConfig.Img2VecNeural) }, - { "multi2vec-clip", typeof(VectorizerConfig.Multi2VecClip) }, - { "multi2vec-cohere", typeof(VectorizerConfig.Multi2VecCohere) }, - { "multi2vec-bind", typeof(VectorizerConfig.Multi2VecBind) }, - { "multi2vec-palm", typeof(VectorizerConfig.Multi2VecGoogle) }, - { "multi2vec-jinaai", typeof(VectorizerConfig.Multi2VecJinaAI) }, - { "multi2vec-voyageai", typeof(VectorizerConfig.Multi2VecVoyageAI) }, - { "ref2vec-centroid", typeof(VectorizerConfig.Ref2VecCentroid) }, - { "text2vec-aws", typeof(VectorizerConfig.Text2VecAWS) }, - { "text2vec-azure-openai", typeof(VectorizerConfig.Text2VecAzureOpenAI) }, - { "text2vec-cohere", typeof(VectorizerConfig.Text2VecCohere) }, - { "text2vec-contextionary", typeof(VectorizerConfig.Text2VecContextionary) }, - { "text2vec-databricks", typeof(VectorizerConfig.Text2VecDatabricks) }, - { "text2vec-gpt4all", typeof(VectorizerConfig.Text2VecGPT4All) }, - { "text2vec-huggingface", typeof(VectorizerConfig.Text2VecHuggingFace) }, - { "text2vec-jinaai", typeof(VectorizerConfig.Text2VecJinaAI) }, - { "text2vec-nvidia", typeof(VectorizerConfig.Text2VecNvidia) }, - { "text2vec-mistral", typeof(VectorizerConfig.Text2VecMistral) }, - { "text2vec-ollama", typeof(VectorizerConfig.Text2VecOllama) }, - { "text2vec-openai", typeof(VectorizerConfig.Text2VecOpenAI) }, - { "text2vec-palm", typeof(VectorizerConfig.Text2VecGoogle) }, - { "text2vec-transformers", typeof(VectorizerConfig.Text2VecTransformers) }, - { "text2vec-voyageai", typeof(VectorizerConfig.Text2VecVoyageAI) }, - { "text2vec-weaviate", typeof(VectorizerConfig.Text2VecWeaviate) }, + { "none", typeof(Vectorizer.None) }, + { "img2vec-neural", typeof(Vectorizer.Img2VecNeural) }, + { "multi2vec-clip", typeof(Vectorizer.Multi2VecClip) }, + { "multi2vec-cohere", typeof(Vectorizer.Multi2VecCohere) }, + { "multi2vec-bind", typeof(Vectorizer.Multi2VecBind) }, + { "multi2vec-palm", typeof(Vectorizer.Multi2VecGoogle) }, + { "multi2vec-jinaai", typeof(Vectorizer.Multi2VecJinaAI) }, + { "multi2vec-voyageai", typeof(Vectorizer.Multi2VecVoyageAI) }, + { "ref2vec-centroid", typeof(Vectorizer.Ref2VecCentroid) }, + { "text2vec-aws", typeof(Vectorizer.Text2VecAWS) }, + { "text2vec-azure-openai", typeof(Vectorizer.Text2VecAzureOpenAI) }, + { "text2vec-cohere", typeof(Vectorizer.Text2VecCohere) }, + { "text2vec-contextionary", typeof(Vectorizer.Text2VecContextionary) }, + { "text2vec-databricks", typeof(Vectorizer.Text2VecDatabricks) }, + { "text2vec-gpt4all", typeof(Vectorizer.Text2VecGPT4All) }, + { "text2vec-huggingface", typeof(Vectorizer.Text2VecHuggingFace) }, + { "text2vec-jinaai", typeof(Vectorizer.Text2VecJinaAI) }, + { "text2vec-nvidia", typeof(Vectorizer.Text2VecNvidia) }, + { "text2vec-mistral", typeof(Vectorizer.Text2VecMistral) }, + { "text2vec-ollama", typeof(Vectorizer.Text2VecOllama) }, + { "text2vec-openai", typeof(Vectorizer.Text2VecOpenAI) }, + { "text2vec-palm", typeof(Vectorizer.Text2VecGoogle) }, + { "text2vec-transformers", typeof(Vectorizer.Text2VecTransformers) }, + { "text2vec-voyageai", typeof(Vectorizer.Text2VecVoyageAI) }, + { "text2vec-weaviate", typeof(Vectorizer.Text2VecWeaviate) }, }; /// @@ -55,7 +55,7 @@ public static VectorizerConfig Create(string type, object? parameters) if (type == "none") { - return new VectorizerConfig.None(); + return new Vectorizer.None(); } try @@ -91,125 +91,6 @@ public static VectorizerConfig Create(string type, object? parameters) } } - /// - /// Creates a VectorizerConfig instance based on the specified type and parameters using reflection. - /// This method provides more detailed error information but may be slower than the JSON approach. - /// - /// The vectorizer type string - /// Dynamic object containing the configuration parameters - /// A VectorizerConfig instance of the appropriate type - public static VectorizerConfig CreateWithReflection(string type, object? parameters) - { - if (string.IsNullOrWhiteSpace(type)) - throw new ArgumentException("Type cannot be null or empty", nameof(type)); - - if (!_configTypes.TryGetValue(type, out var configType)) - throw new ArgumentException($"Unsupported vectorizer type: {type}", nameof(type)); - - try - { - // Get constructor parameters - var constructors = configType.GetConstructors(); - var constructor = constructors - .OrderByDescending(c => c.GetParameters().Length) - .FirstOrDefault(); - if (constructor == null) - throw new InvalidOperationException($"No constructors found for {configType.Name}"); - - var constructorParams = constructor.GetParameters(); - - // Convert dynamic to dictionary for easier parameter mapping - Dictionary paramDict = ConvertDynamicToDictionary(parameters); - - // Prepare constructor arguments - var args = new object?[constructorParams.Length]; - for (int i = 0; i < constructorParams.Length; i++) - { - var param = constructorParams[i]; - var paramName = param.Name; - - if (paramName == null) - throw new InvalidOperationException( - $"Parameter name is null for constructor parameter at index {i} in {configType.Name}" - ); - - // Try to find matching parameter (case-insensitive) - var matchingKey = paramDict.Keys.FirstOrDefault(k => - string.Equals(k, paramName, StringComparison.OrdinalIgnoreCase) - ); - - if (matchingKey != null) - { - var value = paramDict[matchingKey]; - args[i] = ConvertValue(value, param.ParameterType); - } - else if (param.HasDefaultValue) - { - args[i] = param.DefaultValue; - } - else if ( - param.ParameterType.IsValueType - && Nullable.GetUnderlyingType(param.ParameterType) == null - ) - { - throw new InvalidOperationException( - $"Required parameter '{paramName}' not provided for {configType.Name}" - ); - } - else - { - args[i] = null; - } - } - - return (VectorizerConfig)Activator.CreateInstance(configType, args)!; - } - catch (Exception ex) when (!(ex is ArgumentException || ex is InvalidOperationException)) - { - throw new InvalidOperationException( - $"Failed to create {configType.Name} with provided parameters: {ex.Message}", - ex - ); - } - } - - /// - /// Gets all supported vectorizer types. - /// - /// An enumerable of supported type strings - public static IEnumerable GetSupportedTypes() - { - return _configTypes.Keys; - } - - /// - /// Checks if a vectorizer type is supported. - /// - /// The type string to check - /// True if the type is supported, false otherwise - public static bool IsTypeSupported(string? type) - { - return !string.IsNullOrWhiteSpace(type) && _configTypes.ContainsKey(type); - } - - private static Dictionary ConvertDynamicToDictionary(dynamic? obj) - { - if (obj is Dictionary dict) - return dict; - - var result = new Dictionary(); - - if (obj != null) - { - foreach (var property in obj.GetType().GetProperties()) - { - result[property.Name] = property.GetValue(obj); - } - } - - return result; - } - private static object? ConvertValue(object? value, Type targetType) { if (value == null) diff --git a/src/Weaviate.Client/Models/Vectorizers/Vector.cs b/src/Weaviate.Client/Models/Vectorizers/Vector.cs deleted file mode 100644 index 0c0c933b..00000000 --- a/src/Weaviate.Client/Models/Vectorizers/Vector.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Linq.Expressions; - -namespace Weaviate.Client.Models.Vectorizers; - -public abstract class Vector -{ - public static Builder Name(string name) - { - return new Builder(name); - } - - public sealed record Builder - { - private readonly string _name; - private VectorizerConfig? _vectorizerConfig; - private VectorIndexConfig? _vectorIndexConfig; - private string[] _properties = []; - - internal Builder(string namedVector) - { - _name = namedVector; - } - - // Copy constructor with additional behavior - Builder(Builder other) - { - _name = other._name; - _vectorizerConfig = other._vectorizerConfig; - _vectorIndexConfig = other._vectorIndexConfig; - _properties = other._properties; - - if (_vectorizerConfig is not null && _properties.Length > 0) - { - _vectorizerConfig.Properties = _properties; - } - } - - public Builder With(VectorIndexConfig indexConfig) => - new(this with { _vectorIndexConfig = indexConfig }); - - public Builder With(VectorizerConfig vectorizerConfig) => - new(this with { _vectorizerConfig = vectorizerConfig }); - - public Builder From(params string[] properties) => - new(this with { _properties = properties }); - - private static MemberExpression? GetMemberExpression( - Expression> expression - ) - { - // Handle direct member access: t => t.Property - if (expression.Body is MemberExpression memberExpr) - { - return memberExpr; - } - - // Handle value types that get boxed: t => t.IntProperty becomes t => (object)t.IntProperty - if ( - expression.Body is UnaryExpression unaryExpr - && unaryExpr.NodeType == ExpressionType.Convert - && unaryExpr.Operand is MemberExpression convertedMember - ) - { - return convertedMember; - } - - return null; - } - - public Builder From(params Expression>[] properties) - { - if (!properties.All(s => GetMemberExpression(s) != null)) - { - throw new ArgumentException( - "All expressions must be a member access", - nameof(properties) - ); - } - - var names = properties - .Select(GetMemberExpression) - .Where(me => me != null) - .Select(me => me!.Member) - .Select(mi => mi.Name.Decapitalize()) - .ToArray(); - - return From(names); - } - - public VectorConfig Build() - { - return new(_name) - { - Vectorizer = _vectorizerConfig ?? new VectorizerConfig.None(), - VectorIndexConfig = _vectorIndexConfig ?? VectorIndexConfig.Default, - }; - } - - public static implicit operator VectorConfig(Builder src) - { - return src.Build(); - } - - public static implicit operator VectorConfigList(Builder src) - { - return src.Build(); - } - } - - private static string defaultVectorName = "default"; - - public static string DefaultVectorName - { - get => defaultVectorName; - set => defaultVectorName = value; - } -} diff --git a/src/Weaviate.Client/Models/Vectorizers/VectorIndex.cs b/src/Weaviate.Client/Models/Vectorizers/VectorIndex.cs deleted file mode 100644 index af8e3a16..00000000 --- a/src/Weaviate.Client/Models/Vectorizers/VectorIndex.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json; - -namespace Weaviate.Client.Models.Vectorizers; - -public abstract record VectorIndexConfig(string Identifier, dynamic? Configuration) -{ - private static readonly Lazy _default = new(() => new HNSW()); - - public static VectorIndexConfig Default => _default.Value; - - internal static VectorIndexConfig Factory(string type, object? vectorIndexConfig) - { - if (vectorIndexConfig is JsonElement vic) - { - vectorIndexConfig = ObjectHelper.JsonElementToExpandoObject(vic); - } - - return type switch - { - "hnsw" => new HNSW() { Configuration = vectorIndexConfig }, - "flat" => new Flat() { Configuration = vectorIndexConfig }, - "dynamic" => new Dynamic() { Configuration = vectorIndexConfig }, - _ => VectorIndexConfig.Default, - }; - } - - public sealed record HNSW() : VectorIndexConfig("hnsw", new { }); - - public sealed record Flat() : VectorIndexConfig("flat", new { }); - - public sealed record Dynamic() : VectorIndexConfig("dynamic", new { }); -}; diff --git a/src/Weaviate.Client/Rest/Client.cs b/src/Weaviate.Client/Rest/Client.cs index 96647487..9b1c7ee0 100644 --- a/src/Weaviate.Client/Rest/Client.cs +++ b/src/Weaviate.Client/Rest/Client.cs @@ -92,8 +92,8 @@ public class WeaviateRestClient : IDisposable private JsonSerializerOptions _options = new() { - PropertyNameCaseInsensitive = true, // Case-insensitive property matching - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Convert JSON names to PascalCase (C# convention) + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, // For readability Converters = {