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.
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.
New SpiceClient.SearchAsync(SearchRequest, CancellationToken) wraps POST /v1/search for datasets with an embedding column and a loaded embedding model.
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.
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.
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.
RefreshDatasetAsync accepts an optional RefreshOptions to override the dataset's configured refresh behavior for a single on-demand refresh.
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.
SpiceClientBuilder now accepts a PEM-encoded client certificate and key for mTLS, in addition to the existing server-side TLS options.
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.
- Fixed
AuthenticateAsyncthrowingSystem.InvalidOperationException: Can't get the call trailers because the call has not completed successfullyon 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.
PooledConnectionLifetimeis now set on the gRPC channel'sSocketsHttpHandlerso 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.
Added:
System.Text.Json9.0.9 (netstandard2.0 target only)
None.