Skip to content

Commit 18c7624

Browse files
authored
Merge pull request #14 from sharpninja/copilot/merge-upstream-and-audit-codebase
Sync vector store dimensions with embedding output across upstream and dotnet parity
2 parents 9dda2e3 + 88024f8 commit 18c7624

9 files changed

Lines changed: 210 additions & 3 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "patch",
3+
"description": "reconfigure vector store size by embedding model"
4+
}

docs/upstream-sync/upstream-3502c222.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
---
88

9-
Analysis unavailable: HTTP Error 401: Unauthorized
9+
Manual review complete.
1010

11-
Manual review of upstream commit `3502c222` is required.
11+
## Summary
12+
13+
Upstream commit `3502c222` updates Python config validation so that, after probing the configured embedding model, GraphRAG automatically realigns `vector_store.vector_size` and each index schema vector dimension to the actual embedding width.
14+
15+
## Dotnet parity
16+
17+
The dotnet codebase does not have a direct `validate_config.py` equivalent yet, so parity is implemented in the immutable configuration models:
18+
19+
- `GraphRagConfig.SyncVectorStoreDimensions(...)` now realigns vector-store dimensions when the configured embed-text model returns a different embedding width.
20+
- `VectorStoreConfig.WithVectorSize(...)` and `IndexSchema.WithVectorSize(...)` propagate the updated dimension consistently.
21+
22+
No additional missed Python parity changes were identified in this upstream commit beyond the vector-size synchronization behavior.

dotnet/src/GraphRag.Vectors/IndexSchema.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,16 @@ public sealed record IndexSchema
3232
/// Gets the mapping of field names to their types.
3333
/// </summary>
3434
public Dictionary<string, string>? Fields { get; init; }
35+
36+
/// <summary>
37+
/// Returns a copy of the schema with the specified vector size.
38+
/// </summary>
39+
/// <param name="vectorSize">The vector dimension to apply.</param>
40+
/// <returns>A copy of the schema with the updated vector size.</returns>
41+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="vectorSize"/> is less than or equal to zero.</exception>
42+
public IndexSchema WithVectorSize(int vectorSize)
43+
{
44+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(vectorSize);
45+
return this with { VectorSize = vectorSize };
46+
}
3547
}

dotnet/src/GraphRag.Vectors/VectorStoreConfig.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,21 @@ public sealed record VectorStoreConfig
5252
/// Gets the index schema configuration.
5353
/// </summary>
5454
public IndexSchema? IndexSchema { get; init; }
55+
56+
/// <summary>
57+
/// Returns a copy of the vector store configuration with the specified vector size.
58+
/// </summary>
59+
/// <param name="vectorSize">The vector dimension to apply.</param>
60+
/// <returns>A copy of the vector store configuration with the updated vector size.</returns>
61+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="vectorSize"/> is less than or equal to zero.</exception>
62+
public VectorStoreConfig WithVectorSize(int vectorSize)
63+
{
64+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(vectorSize);
65+
66+
return this with
67+
{
68+
VectorSize = vectorSize,
69+
IndexSchema = IndexSchema?.WithVectorSize(vectorSize),
70+
};
71+
}
5572
}

dotnet/src/GraphRag/Config/Models/GraphRagConfig.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using GraphRag.Config.Enums;
77
using GraphRag.Input;
88
using GraphRag.Llm.Config;
9+
using GraphRag.Llm.Types;
910
using GraphRag.Storage;
1011
using GraphRag.Storage.Tables;
1112
using GraphRag.Vectors;
@@ -131,4 +132,35 @@ public ModelConfig GetEmbeddingModelConfig(string? modelId = null)
131132

132133
throw new KeyNotFoundException($"Embedding model '{key}' not found in configuration.");
133134
}
135+
136+
/// <summary>
137+
/// Returns a copy of the configuration with vector store dimensions synchronized to an embedding response.
138+
/// </summary>
139+
/// <param name="embeddingModelId">The embedding model that produced the response.</param>
140+
/// <param name="response">The embedding response to inspect.</param>
141+
/// <returns>
142+
/// The current configuration when the response is empty, already aligned, or produced by a different embedding model;
143+
/// otherwise a copy with the vector store dimensions updated to match the response.
144+
/// </returns>
145+
public GraphRagConfig SyncVectorStoreDimensions(string embeddingModelId, LlmEmbeddingResponse response)
146+
{
147+
ArgumentNullException.ThrowIfNull(embeddingModelId);
148+
ArgumentNullException.ThrowIfNull(response);
149+
150+
if (!string.Equals(embeddingModelId, EmbedText.EmbeddingModelId, StringComparison.Ordinal))
151+
{
152+
return this;
153+
}
154+
155+
var detectedVectorSize = response.FirstEmbedding.Count;
156+
if (detectedVectorSize == 0 || detectedVectorSize == VectorStore.VectorSize)
157+
{
158+
return this;
159+
}
160+
161+
return this with
162+
{
163+
VectorStore = VectorStore.WithVectorSize(detectedVectorSize),
164+
};
165+
}
134166
}

dotnet/tests/GraphRag.Tests.Unit/Config/GraphRagConfigMethodTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using GraphRag.Config.Errors;
77
using GraphRag.Config.Models;
88
using GraphRag.Llm.Config;
9+
using GraphRag.Llm.Types;
10+
using GraphRag.Vectors;
911

1012
namespace GraphRag.Tests.Unit.Config;
1113

@@ -133,4 +135,57 @@ public void Workflows_CanBeSet()
133135

134136
config.Workflows.Should().BeEquivalentTo(workflows);
135137
}
138+
139+
[Fact]
140+
public void SyncVectorStoreDimensions_UpdatesVectorStoreAndSchema_ForConfiguredEmbeddingModel()
141+
{
142+
var config = new GraphRagConfig
143+
{
144+
EmbedText = new EmbedTextConfig { EmbeddingModelId = "embed-model" },
145+
VectorStore = new VectorStoreConfig
146+
{
147+
Type = "azure_ai_search",
148+
VectorSize = 3072,
149+
IndexSchema = new IndexSchema { IndexName = "entities", VectorSize = 3072 },
150+
},
151+
};
152+
var response = new LlmEmbeddingResponse([[1.0f, 2.0f, 3.0f]]);
153+
154+
var result = config.SyncVectorStoreDimensions("embed-model", response);
155+
156+
result.Should().NotBeSameAs(config);
157+
result.VectorStore.VectorSize.Should().Be(3);
158+
result.VectorStore.IndexSchema.Should().NotBeNull();
159+
result.VectorStore.IndexSchema!.VectorSize.Should().Be(3);
160+
config.VectorStore.VectorSize.Should().Be(3072);
161+
config.VectorStore.IndexSchema!.VectorSize.Should().Be(3072);
162+
}
163+
164+
[Fact]
165+
public void SyncVectorStoreDimensions_ReturnsSameConfig_WhenEmbeddingModelDoesNotMatch()
166+
{
167+
var config = new GraphRagConfig
168+
{
169+
EmbedText = new EmbedTextConfig { EmbeddingModelId = "embed-model" },
170+
};
171+
var response = new LlmEmbeddingResponse([[1.0f, 2.0f, 3.0f]]);
172+
173+
var result = config.SyncVectorStoreDimensions("different-model", response);
174+
175+
result.Should().BeSameAs(config);
176+
}
177+
178+
[Fact]
179+
public void SyncVectorStoreDimensions_ReturnsSameConfig_WhenResponseIsEmpty()
180+
{
181+
var config = new GraphRagConfig
182+
{
183+
EmbedText = new EmbedTextConfig { EmbeddingModelId = "embed-model" },
184+
};
185+
var response = new LlmEmbeddingResponse([]);
186+
187+
var result = config.SyncVectorStoreDimensions("embed-model", response);
188+
189+
result.Should().BeSameAs(config);
190+
}
136191
}

dotnet/tests/GraphRag.Tests.Unit/Vectors/IndexSchemaTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,16 @@ public void DefaultValues_AreCorrect()
2121
schema.VectorSize.Should().Be(3072);
2222
schema.Fields.Should().BeNull();
2323
}
24+
25+
[Fact]
26+
public void WithVectorSize_ReturnsUpdatedCopy()
27+
{
28+
var schema = new IndexSchema { IndexName = "test", VectorSize = 3072 };
29+
30+
var updated = schema.WithVectorSize(1536);
31+
32+
updated.Should().NotBeSameAs(schema);
33+
updated.VectorSize.Should().Be(1536);
34+
schema.VectorSize.Should().Be(3072);
35+
}
2436
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) 2025 Microsoft Corporation.
2+
// Licensed under the MIT License
3+
4+
using FluentAssertions;
5+
using GraphRag.Vectors;
6+
7+
namespace GraphRag.Tests.Unit.Vectors;
8+
9+
/// <summary>
10+
/// Unit tests for <see cref="VectorStoreConfig"/>.
11+
/// </summary>
12+
public class VectorStoreConfigTests
13+
{
14+
[Fact]
15+
public void WithVectorSize_ReturnsUpdatedCopy_AndSynchronizesSchema()
16+
{
17+
var config = new VectorStoreConfig
18+
{
19+
Type = "azure_ai_search",
20+
VectorSize = 3072,
21+
IndexSchema = new IndexSchema { IndexName = "entities", VectorSize = 3072 },
22+
};
23+
24+
var updated = config.WithVectorSize(1536);
25+
26+
updated.Should().NotBeSameAs(config);
27+
updated.VectorSize.Should().Be(1536);
28+
updated.IndexSchema.Should().NotBeNull();
29+
updated.IndexSchema!.VectorSize.Should().Be(1536);
30+
config.VectorSize.Should().Be(3072);
31+
config.IndexSchema!.VectorSize.Should().Be(3072);
32+
}
33+
}

packages/graphrag/graphrag/index/validate_config.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
import asyncio
77
import logging
88
import sys
9+
from typing import TYPE_CHECKING
910

1011
from graphrag_llm.completion import create_completion
1112
from graphrag_llm.embedding import create_embedding
1213

1314
from graphrag.config.models.graph_rag_config import GraphRagConfig
1415

16+
if TYPE_CHECKING:
17+
from graphrag_llm.types import LLMEmbeddingResponse
18+
1519
logger = logging.getLogger(__name__)
1620

1721

@@ -29,13 +33,40 @@ def validate_config_names(parameters: GraphRagConfig) -> None:
2933
for id, config in parameters.embedding_models.items():
3034
embed_llm = create_embedding(config)
3135
try:
32-
asyncio.run(
36+
response = asyncio.run(
3337
embed_llm.embedding_async(
3438
input=["This is an LLM Embedding Test String"]
3539
)
3640
)
3741
logger.info("Embedding LLM Config Params Validated")
42+
43+
if id == parameters.embed_text.embedding_model_id:
44+
_sync_vector_store_dimensions(parameters, response)
3845
except Exception as e: # noqa: BLE001
3946
logger.error(f"Embedding configuration error detected.\n{e}") # noqa
4047
print(f"Failed to validate embedding model ({id}) params", e) # noqa: T201
4148
sys.exit(1)
49+
50+
51+
def _sync_vector_store_dimensions(
52+
parameters: GraphRagConfig,
53+
response: "LLMEmbeddingResponse",
54+
) -> None:
55+
"""Sync vector store dimensions to match the actual embedding model output."""
56+
detected = len(response.first_embedding)
57+
if detected == 0:
58+
return
59+
60+
configured = parameters.vector_store.vector_size
61+
if detected == configured:
62+
return
63+
64+
logger.warning(
65+
"Embedding model produces %d-dimensional vectors but vector_size is "
66+
"configured as %d. Overriding vector_size to match the model.",
67+
detected,
68+
configured,
69+
)
70+
parameters.vector_store.vector_size = detected
71+
for schema in parameters.vector_store.index_schema.values():
72+
schema.vector_size = detected

0 commit comments

Comments
 (0)