Releases: pinecone-io/pinecone-dotnet-client
v4.0.1
Pinecone .NET SDK 4.0.1 Release Notes
This release adds the API for listing backups for a specific index.
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var backups = await pinecone.Backups.ListByIndexAsync("index-name", new ListBackupsByIndexRequest());
foreach (var backup in backups.Data)
{
Console.WriteLine($"BackupId: {backup.BackupId}");
Console.WriteLine($"Name: {backup.Name}");
Console.WriteLine($"CreatedAt: {backup.CreatedAt}");
Console.WriteLine($"Status: {backup.Status}");
Console.WriteLine($"RecordCount: {backup.RecordCount}");
}
Full Changelog: 4.0.0...4.0.1
v4.0.0
Pinecone .NET SDK 4.0.0 Release Notes
This release introduces APIs for namespace management, backup and restore for indexes, and listing of available models.
Breaking Changes
CreateIndexRequestMetric
has been renamed toMetricType
for better consistency.CreateIndexForModelRequestEmbedMetric
has been renamed toMetricType
ModelIndexEmbedMetric
has been renamed toMetricType
SparseEmbedding.SparseIndices
type has changed fromIEnumerable<int>
toIEnumerable<long>
New Features
Namespace Management
Namespaces provide logical separation of data within your indexes. You don't need to explicitly create namespaces before using them - they're automatically created when you upsert records.
List Namespaces
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var index = pinecone.Index("example-index");
var namespaces = await index.ListNamespacesAsync(new ListNamespacesRequest());
foreach(var @namespace in namespaces.Namespaces)
{
Console.WriteLine($"Namespace: {@namespace.Name}");
Console.WriteLine($"Record Count: {@namespace.RecordCount}");
}
Describe a Namespace
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var index = pinecone.Index("example-index");
var @namespace = await index.DescribeNamespaceAsync("namespace-name");
Console.WriteLine($"Namespace: {@namespace.Name}");
Console.WriteLine($"Record Count: {@namespace.RecordCount}");
Delete a Namespace
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var index = pinecone.Index("example-index");
await index.DeleteNamespaceAsync("namespace-name");
Backup and Restore Operations
New backup functionality enables data protection and migration scenarios.
Backup an Index
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var backup = await pinecone.Backups.BackupIndexAsync("index-name", new BackupIndexRequest());
Restore a Backup
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var response = await pinecone.Backups.CreateIndexFromBackupAsync("backup-id", new CreateIndexFromBackupRequest {
Name = "new-index-name"
});
Console.WriteLine($"Restore Job ID: {response.RestoreJobId}");
Console.WriteLine($"New Index ID: {response.IndexId}");
Get a Backup
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var backup = await pinecone.Backups.GetAsync("backup-id");
List Backups
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var backups = await pinecone.Backups.ListAsync();
foreach (var backup in backups.Data)
{
Console.WriteLine($"BackupId: {backup.BackupId}");
Console.WriteLine($"Name: {backup.Name}");
Console.WriteLine($"CreatedAt: {backup.CreatedAt}");
Console.WriteLine($"Status: {backup.Status}");
Console.WriteLine($"RecordCount: {backup.RecordCount}");
}
Delete Backup
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
await pinecone.Backups.DeleteAsync("backup-id");
Restore Jobs
Track and manage the progress of index restore operations.
List Restore Jobs
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var jobs = await pinecone.RestoreJobs.ListAsync(new ListRestoreJobsRequest());
foreach(var job in jobs.Data)
{
Console.WriteLine($"Restore Job ID: {job.RestoreJobId}");
Console.WriteLine($"Status: {job.Status}");
Console.WriteLine($"CreatedAt: {job.CreatedAt}");
Console.WriteLine($"TargetIndexName: {job.TargetIndexName}");
}
Get Restore Job
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var job = await pinecone.RestoreJobs.GetAsync("job-id");
Console.WriteLine($"Restore Job ID: {job.RestoreJobId}");
Console.WriteLine($"Status: {job.Status}");
Console.WriteLine($"CreatedAt: {job.CreatedAt}");
Console.WriteLine($"TargetIndexName: {job.TargetIndexName}");
Models
Access and manage embedding models directly through the SDK.
List Available Models
using Pinecone;
using Pinecone.Inference;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var models = await pinecone.Inference.Models.ListAsync(new ListModelsRequest());
foreach (var model in models.Models)
{
Console.WriteLine($"Name: {model.Model}");
Console.WriteLine($"Type: {model.Type}");
Console.WriteLine($"Vector type: {model.VectorType}");
}
Get Specific Model
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
var model = await pinecone.Inference.Models.GetAsync("pinecone-sparse-english-v0");
Console.WriteLine($"Name: {model.Model}");
Console.WriteLine($"Type: {model.Type}");
Console.WriteLine($"Vector type: {model.VectorType}");
Migration Guide
The renaming of various metric types to MetricType
requires updating any code that creates indexes. If you're using CreateIndexRequestMetric
, CreateIndexForModelRequestEmbedMetric
, or ModelIndexEmbedMetric
in your code, you'll need to update those to MetricType
.
Full Changelog: 3.1.0...4.0.0
v3.1.0
Pinecone .NET SDK 3.1.0 Release Notes
This release introduces new record-based APIs that simplify working with Pinecone indexes, especially for semantic search applications. These new APIs allow you to focus on your data rather than vector details, making the SDK more accessible for application developers.
New Features
Record-Based APIs
Two new endpoints have been added that offer a simplified interface for working with Pinecone:
UpsertRecordsAsync
await client.UpsertRecordsAsync(
namespace,
[
new UpsertRecord
{
Id = "rec1",
AdditionalProperties =
{
["chunk_text"] = "Your text content here",
["category"] = "Your category",
},
},
// Additional records...
]
);
- Allows direct insertion of records with structured data
- Automatically handles vector embedding behind the scenes
- Supports custom metadata through
AdditionalProperties
dictionary
SearchRecordsAsync
var response = await client.SearchRecordsAsync(
namespace,
new SearchRecordsRequest
{
Query = new SearchRecordsRequestQuery
{
TopK = 4,
Inputs = new Dictionary<string, object?> { { "text", "Your search query" } },
},
Fields = ["category", "chunk_text"],
}
);
- Search using natural language queries instead of vectors
- Specify which fields to include in the response
- Automatically handles query embedding and vector similarity search
Migration Guide
These new APIs are complementary to the existing vector-based APIs. If you're building semantic search applications, we recommend using these new record-based APIs for a more streamlined development experience.
Full Changelog: 3.0.0...3.1.0
v3.0.0 Release
This release updates the SDK for Pinecone API version 2025-01. It introduces new features, several breaking changes, and improvements to the Pinecone .NET Client.
New Features
Support for Sparse Indexes
Pinecone's API now lets you create sparse indexes.
Here's an example of how to use the Pinecone SDK with sparse vectors.
using Pinecone;
// Initialize Pinecone client with your API key
var pinecone = new PineconeClient(Environment.GetEnvironmentVariable("PINECONE_API_KEY"));
var indexName = "test-sparse-index";
// Create a sparse serverless index
Console.WriteLine("Creating serverless index...");
await pinecone.CreateIndexAsync(new CreateIndexRequest
{
Name = indexName,
VectorType = VectorType.Sparse,
Spec = new ServerlessIndexSpec
{
Serverless = new ServerlessSpec
{
Cloud = ServerlessSpecCloud.Aws,
Region = "us-east-1",
}
}
});
// Wait for index to be ready
Console.WriteLine("Waiting for index to be ready...");
while (true)
{
var indexInfo = await pinecone.DescribeIndexAsync(indexName);
if (indexInfo.Status.State == IndexModelStatusState.Ready)
{
break;
}
Console.WriteLine("Index not ready yet, waiting...");
await Task.Delay(TimeSpan.FromSeconds(2));
}
// Get index instance
var index = pinecone.Index(indexName);
// Prepare sparse vector data
var sparseIndices = new uint[] { 1, 2 };
var sparseValues = new ReadOnlyMemory<float>([1f, 2f]);
// Create vector with sparse values
var vector = new Vector
{
Id = "v1",
SparseValues = new SparseValues
{
Indices = sparseIndices,
Values = sparseValues
}
};
// Upsert vector
Console.WriteLine("Upserting vector...");
var upsertResponse = await index.UpsertAsync(new UpsertRequest
{
Vectors = new List<Vector> { vector }
});
Console.WriteLine($"Upserted {upsertResponse.UpsertedCount} vector");
// Query by vector ID
Console.WriteLine("Querying vector by ID...");
var queryResponse = await index.QueryAsync(new QueryRequest
{
Id = vector.Id,
TopK = 1,
IncludeValues = true,
});
// Note: It can take up to 1 minute before upserted records are available to query.
// https://docs.pinecone.io/guides/indexes/sparse-indexes#upsert-sparse-vectors
// Display query results
Console.WriteLine("Query results:");
foreach (var match in queryResponse.Matches)
{
Console.WriteLine($"ID: {match.Id}");
if (match.SparseValues != null)
{
var indicesString = string.Join(", ", match.SparseValues.Indices);
Console.WriteLine($"Sparse Indices: [{indicesString}]");
var valuesString = string.Join(", ", match.SparseValues.Values.ToArray());
Console.WriteLine($"Sparse Values: [{valuesString}]");
}
}
// Delete the index
Console.WriteLine("Deleting index...");
await pinecone.DeleteIndexAsync(indexName);
Console.WriteLine("Index deleted successfully");
New Operations
client.CreateIndexForModelAsync(CreateIndexForModelRequest { ... }) -> Index
Breaking Changes
Embedding Type Changes
The Embedding
type has changed from a simple object to a discriminated union, supporting both DenseEmbedding
and SparseEmbedding
. This change provides better type safety but requires code updates.
New helper methods available on the Embedding
type:
IsDense
&IsSparse
- Type checkingAsDense()
&AsSparse()
- Type conversionMatch()
andVisit()
- Pattern matching
Example of updating your code:
// Before
Embedding embedding = GetEmbedding();
foreach (var value in embedding.Values.Span)
{
Console.WriteLine(value);
}
// After
Embedding embedding = GetEmbedding();
switch (embedding.VectorType)
{
case VectorType.Dense:
DenseEmbedding denseEmbedding = embedding.AsDense();
foreach (var value in denseEmbedding.Values.Span)
{
Console.WriteLine(value);
}
break;
case VectorType.Sparse:
SparseEmbedding sparseEmbedding = embedding.AsSparse();
foreach (var value in sparseEmbedding.SparseValues.Span)
{
Console.WriteLine(value);
}
break;
}
Type Changes
EmbedRequest
The Parameters
property now uses Dictionary<string, object?>?
instead of EmbedRequestParameters
:
public record EmbedRequest
{
public Dictionary<string, object?>? Parameters { get; set; }
}
RankedDocument
The Document
property now uses Dictionary<string, object?>?
instead of Dictionary<string, string>?
:
public record RankedDocument
{
public Dictionary<string, object?>? Document { get; set; }
}
RerankRequest
Update property types for more flexibility:
public record RerankRequest
{
public IEnumerable<Dictionary<string, object?>> Documents { get; set; }
public Dictionary<string, object?>? Parameters { get; set; }
}
Optional Properties
The following properties are now nullable:
Index.Dimension
:int?
Index.Tags
:Dictionary<string, string>?
PodSpec.Replicas
:int?
PodSpec.Shards
:int?
PodSpec.Pods
:int?
Vector.Values
:int?
Migration Guide
- Update embedding handling code to use the new discriminated union pattern
- Replace
EmbedRequestParameters
usage withDictionary<string, object?>?
- Update code handling
RankedDocument
to work withobject
values - Review and update code that assumes non-nullable properties for Index and PodSpec
- Update RerankRequest implementations to use the new parameter types
Full Changelog: 2.1.0...3.0.0
v2.1.0 Release
This version of the Pinecone .NET Client introduces the ClientOptions.IsTlsEnabled
property which you are required to set to false
if you want to test the Client against an HTTP endpoint.
Features
IsTlsEnabled
Parts of the Client dynamically retrieve API hostnames to interact with which default to using HTTPS. To have the Client default to using HTTP instead, you can set the ClientOptions.IsTlsEnabled
to false
.
Now you can use the Pinecone Client against a local HTTP URL:
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY",
new ClientOptions
{
BaseUrl = "http://localhost:5080",
IsTlsEnabled = false
}
);
// Create serverless index
await pinecone.CreateIndexAsync(new CreateIndexRequest
{
Name = "serverless-index",
Dimension = 3,
Metric = CreateIndexRequestMetric.Cosine,
Spec = new ServerlessIndexSpec
{
Serverless = new ServerlessSpec
{
Cloud = ServerlessSpecCloud.Aws,
Region = "us-east-1",
}
},
DeletionProtection = DeletionProtection.Disabled
});
// Create pod index
await pinecone.CreateIndexAsync(new CreateIndexRequest
{
Name = "pod-index",
Dimension = 3,
Metric = CreateIndexRequestMetric.Cosine,
Spec = new PodIndexSpec
{
Pod = new PodSpec
{
Environment = "us-east-1-aws",
PodType = "p1.x1",
Pods = 1,
Replicas = 1,
Shards = 1,
}
},
DeletionProtection = DeletionProtection.Disabled
});
// Describe serverless index
await pinecone.DescribeIndexAsync("serverless-index");
// Describe pod index
await pinecone.DescribeIndexAsync("pod-index");
// Get index connection object for serverless-index
var serverlessIndexConnection = pinecone.Index("serverless-index");
// Get index connection object for pod-index
var podIndexConnection = pinecone.Index("pod-index");
// Upsert records into serverless index
await serverlessIndexConnection.UpsertAsync(new UpsertRequest()
{
Namespace = "v1",
Vectors = new List<Vector>
{
new Vector { Id = "1", Values = new ReadOnlyMemory<float>([1f, 2f, 3f]) }
}
});
// Upsert records into pod index
await podIndexConnection.UpsertAsync(new UpsertRequest()
{
Namespace = "v1",
Vectors = new List<Vector>
{
new Vector { Id = "1", Values = new ReadOnlyMemory<float>([1f, 2f, 3f]) }
}
});
// Query by vectorId from serverless index
await serverlessIndexConnection.QueryAsync(new QueryRequest
{
TopK = 10,
Id = "3",
Namespace = "v1",
});
// Query by vectorId from pod index
await podIndexConnection.QueryAsync(new QueryRequest
{
TopK = 10,
Id = "3",
Namespace = "v1",
});
// Delete serverless index
await pinecone.DeleteIndexAsync("serverless-index");
// Delete pod index
await pinecone.DeleteIndexAsync("pod-index");
What's Changed
- feat: Add
IsTlsEnabled
client option by @fern-support in #35 - 🌿 Fern Regeneration -- December 4, 2024 by @fern-api in #36
Full Changelog: 2.0.0...2.1.0
v2.0.0 Release
This version of the Pinecone .NET SDK introduces inference
and imports
. It also supports version 2024-10
of the Pinecone API. You can read more about versioning here.
Features
Embed
The Inference
has an operation called Embed
which allows users to generate embeddings for input data.
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
// Prepare input sentences to be embedded
List<EmbedRequestInputsItem> inputs =
[
new()
{
Text = "The quick brown fox jumps over the lazy dog."
},
new()
{
Text = "Lorem ipsum"
}
];
// Specify the embedding model and parameters
var embeddingModel = "multilingual-e5-large";
// Generate embeddings for the input data
var embeddings = await pinecone.Inference.EmbedAsync(new EmbedRequest()
{
Model = embeddingModel,
Inputs = inputs,
Parameters = new EmbedRequestParameters()
{
InputType = "query",
Truncate = "END"
}
});
// Get embedded data
var embeddedData = embeddings.Data;
Rerank
The Inference
has another operation called Rerank
which provides users the ability to rerank documents in descending relevance-order against a given query. Reranking documents is a common "second-pass" ranking strategy broadly used in retrieval applications.
using Pinecone;
var pinecone = new PineconeClient("PINECONE_API_KEY");
// The model to use for reranking
var model = "bge-reranker-v2-m3";
// The query to rerank documents against
var query = "The tech company Apple is known for its innovative products like the iPhone.";
// Add the documents to rerank
var documents = new List<Dictionary<string, string>>
{
new()
{
["id"] = "vec1",
["my_field"] = "Apple is a popular fruit known for its sweetness and crisp texture."
},
new()
{
["id"] = "vec2",
["my_field"] = "Many people enjoy eating apples as a healthy snack."
},
new()
{
["id"] = "vec3",
["my_field"] =
"Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces."
},
new()
{
["id"] = "vec4",
["my_field"] = "An apple a day keeps the doctor away, as the saying goes."
}
};
// The fields to rank the documents by. If not provided, the default is "text"
var rankFields = new List<string> { "my_field" };
// The number of results to return sorted by relevance. Defaults to the number of inputs
int topN = 2;
// Whether to return the documents in the response
bool returnDocuments = true;
// Additional model-specific parameters for the reranker
var parameters = new Dictionary<string, string>
{
["truncate"] = "END"
};
// Send ranking request
var result = await pinecone.Inference.RerankAsync(
new RerankRequest
{
Model = model,
Query = query,
Documents = documents,
RankFields = rankFields,
TopN = topN,
Parameters = parameters
});
// Get ranked data
var data = result.Data;
Import
Index
now exposes additional methods for working with import
operations. An import
is a long-running, asynchronous operation that gives users the ability to import vectors directly from object storage (e.g. S3) into a Pinecone index. It is intended to be used with large-scale jobs. For small-scale jobs (e.g. <1000 vectors), we recommend continuing to use upsert.
using Pinecone;
...
// Initialize pinecone object
var pinecone = new PineconeClient("PINECONE_API_KEY");
// Get async imports connection object
var index = pinecone.Index("PINECONE_INDEX_NAME");
// s3 uri
var uri = "s3://path/to/file.parquet";
// Start an import
var startImportResponse = await index.StartBulkImportAsync(new StartImportRequest
{
Uri = uri,
IntegrationId = "123-456-789",
ErrorMode = new ImportErrorMode { OnError = ImportErrorModeOnError.Continue }
});
// List imports
var listimportResponse = await index.ListBulkImportsAsync(new ListBulkImportsRequest
{
Limit = 100,
PaginationToken = "some-pagination-token"
});
// Describe import
var importDetails = await index.DescribeBulkImportAsync("1");
// Cancel import
var cancelResponse = await index.CancelBulkImportAsync("2");
v1.0.0 Release
This is the first public release of the Pinecone .NET client. The client supports both control and data plane operations for pods
, serverless
, and free-tier
indexes. Below are the supported operations:
Control Plane Operations:
- Index Operations:
- Create Index
- List Index
- Describe Index
- Delete Index
- Configure Index
- Collection Operations:
- Create Collection
- List Collection
- Describe Collection
- Delete Collection
Data Plane Operations:
- Upsert Vectors
- Query Vectors
- Fetch Vectors
- Update Vectors
- Delete Vectors
- List Vectors
- Get Index Stats
Please note that all data plane operations use gRPC, while control plane operations use REST. Users also have the ability to configure retries, timeouts, and the HTTP proxy. For detailed information on how to use the client, please refer to the README.
1.0.0-rc.1
Initial release.