Skip to content

Commit 3f873c1

Browse files
mpartipiloclaude
andcommitted
feat: add X-Weaviate-Client-Integration header support
AddWeaviateContext() automatically appends "weaviate-client-csharp-managed/version" to the X-Weaviate-Client-Integration header via IConfigureOptions<WeaviateOptions>, so the Weaviate server can identify managed ORM traffic in metrics. SDK consumers building higher-level frameworks on top of the managed client should append their own identity at the core DI layer: services.AddWeaviate(opts => opts.AddIntegration("my-framework/1.0")); For non-DI usage, ClientConfiguration.WithManagedIntegrationHeader() sets the header on the configuration before passing it to WeaviateClient. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ab234c5 commit 3f873c1

11 files changed

Lines changed: 152 additions & 6 deletions

File tree

docs/ADVANCED.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,35 @@ foreach (var s in summaries)
581581

582582
---
583583

584+
## SDK Integration Header
585+
586+
When using the managed client, the `X-Weaviate-Client-Integration` header is automatically sent with every request so the Weaviate server can identify and track managed client traffic in metrics.
587+
588+
### DI path
589+
590+
`AddWeaviateContext` sets the header automatically. If you are building a higher-level framework on top of the managed client, append your own identity at the core DI layer:
591+
592+
```csharp
593+
// X-Weaviate-Client-Integration: weaviate-client-csharp-managed/1.x.x my-framework/2.3.0
594+
builder.Services.AddWeaviate(opts =>
595+
opts.AddIntegration("my-framework/2.3.0"));
596+
builder.Services.AddWeaviateContext<MyContext>();
597+
```
598+
599+
### Non-DI path
600+
601+
When constructing `WeaviateContext` directly (without DI), call `WithManagedIntegrationHeader()` on your `ClientConfiguration`:
602+
603+
```csharp
604+
var config = new ClientConfiguration("localhost")
605+
.WithManagedIntegrationHeader();
606+
607+
var client = new WeaviateClient(config);
608+
var context = new MyContext(client);
609+
```
610+
611+
---
612+
584613
## Collection Lifecycle Hooks
585614

586615
The `OnCollectionConfig` pattern allows intercepting collection creation.

docs/DEPENDENCY_INJECTION.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ builder.Services.AddWeaviateContext<BlogContext>(
7878
);
7979
```
8080

81+
### SDK Integrations (X-Weaviate-Client-Integration Header)
82+
83+
When `AddWeaviateContext` is called, it automatically sets the `X-Weaviate-Client-Integration` header so the Weaviate server can identify traffic from the managed client.
84+
85+
If you are building a higher-level SDK or framework on top of `Weaviate.Client.Managed`, append your own identity at the core DI layer via `AddWeaviate()`. Multiple tokens are space-separated in the header value:
86+
87+
```csharp
88+
// Results in: X-Weaviate-Client-Integration: weaviate-client-csharp-managed/1.x.x my-framework/2.3.0
89+
builder.Services.AddWeaviate(opts =>
90+
opts.AddIntegration("my-framework/2.3.0"));
91+
builder.Services.AddWeaviateContext<BlogContext>();
92+
```
93+
8194
### OnConfiguring Override
8295

8396
Context instances can override `OnConfiguring()` to set defaults, which take precedence over DI configuration:

nuget.config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<packageSources>
4+
<add key="local" value="/tmp/local-nuget" />
5+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
6+
</packageSources>
7+
</configuration>

src/Example/packages.lock.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,8 @@
362362
},
363363
"Weaviate.Client": {
364364
"type": "Transitive",
365-
"resolved": "1.0.1",
366-
"contentHash": "Ff7/q5DpshU532DDZJ5Zh86G1nxnlO6zwTqzo4x/UOBY1s3zLRfbNyArQ012gMHtxx/odpULgi2Gld7zfwVI5A==",
365+
"resolved": "1.0.2",
366+
"contentHash": "B/QmsqYDf/R2m3/GJtg+SuKStg/mQ6Dkv1C5KcROLyqpyt96gH3RWLUFGB6SKFxhJAAyBEp/+Hk+eSFOcoShSg==",
367367
"dependencies": {
368368
"Duende.IdentityModel": "7.1.0",
369369
"Google.Protobuf": "3.30.2",
@@ -383,7 +383,7 @@
383383
"Microsoft.Extensions.DependencyInjection.Abstractions": "[9.0.8, )",
384384
"Microsoft.Extensions.Hosting.Abstractions": "[9.0.8, )",
385385
"Microsoft.Extensions.Options": "[9.0.8, )",
386-
"Weaviate.Client": "[1.0.1, )"
386+
"Weaviate.Client": "[1.0.2, )"
387387
}
388388
}
389389
}

src/Weaviate.Client.Managed.Tests/DependencyInjection/WeaviateContextDITests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Options;
23
using Weaviate.Client.DependencyInjection;
34
using Xunit;
45

@@ -233,6 +234,24 @@ public void AddWeaviateContext_ExposesUnderlyingClient()
233234
Assert.Same(client, context.Client);
234235
}
235236

237+
[Fact]
238+
public void AddWeaviateContext_SetsIntegrationHeader()
239+
{
240+
var services = new ServiceCollection();
241+
services.AddWeaviateLocal(eagerInitialization: false);
242+
services.AddWeaviateContext<TestStoreContext>();
243+
244+
var provider = services.BuildServiceProvider();
245+
var options = provider.GetRequiredService<IOptions<WeaviateOptions>>().Value;
246+
247+
Assert.NotNull(options.Headers);
248+
Assert.True(options.Headers.ContainsKey("X-Weaviate-Client-Integration"));
249+
Assert.Matches(
250+
@"^weaviate-client-csharp-managed/\d+",
251+
options.Headers["X-Weaviate-Client-Integration"]
252+
);
253+
}
254+
236255
#region Test Types
237256

238257
[WeaviateCollection("Products")]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Weaviate.Client.Managed.Extensions;
2+
using Xunit;
3+
4+
namespace Weaviate.Client.Managed.Tests.Extensions;
5+
6+
public class WeaviateClientExtensionsTests
7+
{
8+
[Fact]
9+
public void WithManagedIntegrationHeader_AddsHeader()
10+
{
11+
var config = new ClientConfiguration();
12+
var result = config.WithManagedIntegrationHeader();
13+
14+
Assert.NotNull(result.Headers);
15+
Assert.True(result.Headers.ContainsKey(WeaviateDefaults.IntegrationHeader));
16+
Assert.Matches(
17+
@"^weaviate-client-csharp-managed/\d+",
18+
result.Headers[WeaviateDefaults.IntegrationHeader]
19+
);
20+
}
21+
22+
[Fact]
23+
public void WithManagedIntegrationHeader_DoesNotOverwriteExistingValue()
24+
{
25+
var config = new ClientConfiguration(
26+
Headers: new Dictionary<string, string>
27+
{
28+
[WeaviateDefaults.IntegrationHeader] = "existing/1.0",
29+
}
30+
);
31+
var result = config.WithManagedIntegrationHeader();
32+
33+
var value = result.Headers![WeaviateDefaults.IntegrationHeader];
34+
// Existing value is preserved; managed segment is appended
35+
Assert.Contains("existing/1.0", value);
36+
Assert.Contains("weaviate-client-csharp-managed/", value);
37+
}
38+
39+
[Fact]
40+
public void WithManagedIntegrationHeader_DoesNotMutateOriginal()
41+
{
42+
var config = new ClientConfiguration();
43+
var result = config.WithManagedIntegrationHeader();
44+
45+
// Original is unchanged (record with syntax returns new instance)
46+
Assert.Null(config.Headers);
47+
Assert.NotNull(result.Headers);
48+
}
49+
}

src/Weaviate.Client.Managed.Tests/Weaviate.Client.Managed.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
<ItemGroup>
2727
<ProjectReference Include="..\Weaviate.Client.Managed\Weaviate.Client.Managed.csproj" />
28-
<PackageReference Include="Weaviate.Client" Version="1.0.1" />
28+
<PackageReference Include="Weaviate.Client" Version="1.0.2" />
2929
</ItemGroup>
3030

3131
</Project>

src/Weaviate.Client.Managed/DependencyInjection/WeaviateManagedServiceCollectionExtensions.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using Microsoft.Extensions.DependencyInjection.Extensions;
44
using Microsoft.Extensions.Hosting;
55
using Microsoft.Extensions.Options;
6-
using Weaviate.Client.Managed.Context;
6+
using Weaviate.Client.DependencyInjection;
77

88
namespace Weaviate.Client.Managed.DependencyInjection;
99

@@ -12,6 +12,11 @@ namespace Weaviate.Client.Managed.DependencyInjection;
1212
/// </summary>
1313
public static class WeaviateManagedServiceCollectionExtensions
1414
{
15+
/// <summary>
16+
/// The integration package name for the managed client.
17+
/// </summary>
18+
internal const string IntegrationName = "weaviate-client-csharp-managed";
19+
1520
/// <summary>
1621
/// Registers a <see cref="WeaviateContext"/> subclass with the dependency injection container.
1722
/// </summary>
@@ -32,6 +37,14 @@ public static IServiceCollection AddWeaviateContext<TContext>(
3237
)
3338
where TContext : WeaviateContext
3439
{
40+
// Append managed client identity to X-Weaviate-Client-Integration via IConfigureOptions
41+
// so it is applied before WeaviateClient constructs its HttpClient/gRPC client.
42+
services
43+
.AddOptions<WeaviateOptions>()
44+
.Configure(opts =>
45+
opts.AddIntegration(WeaviateDefaults.IntegrationAgent(IntegrationName))
46+
);
47+
3548
// Configure typed options for this context type
3649
if (configureOptions != null)
3750
{

src/Weaviate.Client.Managed/Extensions/WeaviateClientExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ namespace Weaviate.Client.Managed.Extensions;
88
/// </summary>
99
public static class WeaviateClientExtensions
1010
{
11+
/// <summary>
12+
/// Returns a new <see cref="ClientConfiguration"/> with the
13+
/// <c>X-Weaviate-Client-Integration</c> header set for the managed client.
14+
/// Use this when constructing <see cref="WeaviateContext"/> without dependency injection.
15+
/// </summary>
16+
/// <param name="config">The base configuration.</param>
17+
public static ClientConfiguration WithManagedIntegrationHeader(
18+
this ClientConfiguration config
19+
) =>
20+
config.WithIntegration(
21+
WeaviateDefaults.IntegrationAgent(
22+
DependencyInjection.WeaviateManagedServiceCollectionExtensions.IntegrationName
23+
)
24+
);
25+
1126
/// <summary>
1227
/// Creates a Weaviate collection from a C# class decorated with ORM attributes.
1328
/// The class must have a [WeaviateCollection] attribute or the class name will be used as the collection name.

src/Weaviate.Client.Managed/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,4 @@ static Weaviate.Client.Managed.Query.WeaviateQueryableExtensions.WithMetadata<T>
162162
static Weaviate.Client.Managed.Query.WeaviateQueryableExtensions.WithReferences<T>(this System.Linq.IQueryable<T!>! source, params System.Linq.Expressions.Expression<System.Func<T!, object!>!>![]! references) -> Weaviate.Client.Managed.Query.WeaviateQueryable<T!>!
163163
static Weaviate.Client.Managed.Query.WeaviateQueryableExtensions.WithCancellation<T>(this System.Linq.IQueryable<T!>! source, System.Threading.CancellationToken cancellationToken) -> Weaviate.Client.Managed.Query.WeaviateQueryable<T!>!
164164
static Weaviate.Client.Managed.Query.WeaviateQueryableExtensions.WithVectors<T>(this System.Linq.IQueryable<T!>! source, params System.Linq.Expressions.Expression<System.Func<T!, object!>!>![]! vectors) -> Weaviate.Client.Managed.Query.WeaviateQueryable<T!>!
165+
static Weaviate.Client.Managed.Extensions.WeaviateClientExtensions.WithManagedIntegrationHeader(this Weaviate.Client.ClientConfiguration! config) -> Weaviate.Client.ClientConfiguration!

0 commit comments

Comments
 (0)