Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Spice/Spice.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<PackageId>SpiceAI</PackageId>
<Version>0.3.0</Version>
<Version>0.4.0</Version>
<TargetFrameworks>netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>

<!-- NuGet Package Metadata -->
Expand Down
101 changes: 101 additions & 0 deletions docs/release_notes/v0.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Spice.ai .NET SDK v0.4.0 Release Notes

## New Features

### Health and Readiness Checks

New `SpiceClient.IsSpiceHealthyAsync` and `SpiceClient.IsSpiceReadyAsync` methods wrap the runtime's `/health` and `/v1/ready` endpoints, reusing the endpoint, credentials, TLS/mTLS and user agent already configured on the builder.

```csharp
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
if (!await client.IsSpiceHealthyAsync(cts.Token))
{
// runtime unreachable or unhealthy
}

bool ready = await client.IsSpiceReadyAsync();
```

An unreachable or unhealthy runtime is reported as `false` rather than thrown as an exception; caller-requested cancellation still propagates.

### Vector, Keyword, and Hybrid Search

New `SpiceClient.SearchAsync(SearchRequest, CancellationToken)` wraps `POST /v1/search` for datasets with an embedding column and a loaded embedding model.

```csharp
using Spice.Search;

var response = await client.SearchAsync(new SearchRequest("cozy mountain cabin")
{
Datasets = new[] { "listings" },
Limit = 10,
Where = "city = 'Tahoe'",
AdditionalColumns = new[] { "price" },
});

foreach (var match in response.Results)
{
Console.WriteLine($"{match.Dataset}: {match.Score}");
}
```

Setting `Keywords` pre-filters the embedding column with a lexical search before the vector search runs, making the search hybrid.

### Natural Language to SQL (Nsql)

New `SpiceClient.NsqlAsync(NsqlRequest, CancellationToken)` translates a natural-language question into SQL via the runtime's configured LLM and runs it, returning the rows alongside the generated SQL. `NsqlGenerateSqlAsync` generates the SQL without running it.

```csharp
using Spice.Nsql;

var response = await client.NsqlAsync(new NsqlRequest("how many taxi trips were there yesterday?"));
Console.WriteLine(response.SQL); // the SQL the model generated
Console.WriteLine(response.Data); // the rows it returned, decoded from JSON

// Inspect or edit the generated SQL without running it:
string sql = await client.NsqlGenerateSqlAsync(new NsqlRequest("how many taxi trips were there yesterday?"));
```

Requires an LLM model configured in the Spicepod.

### On-Demand Dataset Refresh Options

`RefreshDatasetAsync` accepts an optional `RefreshOptions` to override the dataset's configured refresh behavior for a single on-demand refresh.

```csharp
await client.RefreshDatasetAsync("taxi_trips", new RefreshOptions()
.WithRefreshSql("SELECT * FROM taxi_trips WHERE tip_amount > 10.0")
.WithRefreshMode(RefreshMode.Append)
.WithMaxJitter(TimeSpan.FromSeconds(10)));
```

Any option left unset falls back to the dataset's configured value. Applies to datasets accelerated with `full` or `append` refresh mode; datasets using `changes` mode are kept up to date by change data capture and are unaffected.

### Mutual TLS (mTLS) Client Certificate Support

`SpiceClientBuilder` now accepts a PEM-encoded client certificate and key for mTLS, in addition to the existing server-side TLS options.

```csharp
using var client = new SpiceClientBuilder()
.WithAddress("https://localhost:50051")
.WithTlsClientCertificate("client.pem", "client.key")
.WithTlsRootCertificate("ca.pem")
.Build();
```

`WithTlsClientCertificate` implies `WithTls(true)`. Applies to both the Flight (gRPC) and HTTP clients.

## Bug Fixes

- Fixed `AuthenticateAsync` throwing `System.InvalidOperationException: Can't get the call trailers because the call has not completed successfully` on a failed handshake, which masked the actual gRPC authentication error. The auth token is now read from response headers first, falling back to trailers only when headers carry none.
- Fixed mTLS client certificate handshakes failing on Windows.
- `PooledConnectionLifetime` is now set on the gRPC channel's `SocketsHttpHandler` so long-lived connections are periodically recycled and DNS is re-resolved. Previously, clients connecting through load-balanced endpoints (e.g. AWS ALBs) could get stuck on a stale backend IP after a rebalance.

## Dependencies

Added:
- `System.Text.Json` 9.0.9 (netstandard2.0 target only)

## Breaking Changes

None.