Skip to content

Releases: pinecone-io/pinecone-java-client

v5.1.0 Release

05 Jun 17:00
36fa67b
Compare
Choose a tag to compare

This version of the Pinecone Java SDK introduces support for models api. You can now describe and list embedding models hosted by Pinecone. More details on models can be found here.

Features

Support to list and describe embedding models hosted by Pinecone.

Following methods are added:
1. listModels()
2. listModels(String type)
4. listModels(String type, String vectorType)
5. describeModel(String modelName)

Below code shows how to list and describe an embedding model:

import io.pinecone.clients.Inference;
import io.pinecone.clients.Pinecone;
import org.openapitools.inference.client.ApiException;
import org.openapitools.inference.client.model.ModelInfo;
import org.openapitools.inference.client.model.ModelInfoList;
...

Pinecone pinecone = new Pinecone
        .Builder(System.getenv("PINECONE_API_KEY"))
        .build();

Inference inference = pinecone.getInferenceClient();

// list models
ModelInfoList models = inference.listModels();
System.out.println(models);

// list models by filtering with type
models = inference.listModels("rerank");
System.out.println(models);

// list models by filtering with type and vectorType
models = inference.listModels("embed", "dense");
System.out.println(models);

// describe a model
ModelInfo modelInfo = inference.describeModel("llama-text-embed-v2");
System.out.println(modelInfo);

What's Changed

Full Changelog: v5.0.0...v5.1.0

v5.0.0 Release

19 May 15:47
cb00bc3
Compare
Choose a tag to compare

This version of the Pinecone Java SDK introduces indexes with integrated inference, backups and restore, and ability to work more explicitly with namespaces. It also supports version 2025-04 of the Pinecone API. You can read more about versioning here.

Features

Indexes with Integrated Inference

This release adds the following method for integrated inference. Together these methods provide a way for you to easily store your data and let us manage the process of creating embeddings. To learn about available models, see the Model Gallery.

  1. Create index for model i.e. create an index with an associated embedding model
  2. Configure an existing index to associate it with an embedding model
  3. Upsert records
  4. Search records by id
  5. Search records by vector
  6. Search records by text
import io.pinecone.clients.Index;
import io.pinecone.clients.Pinecone;
import io.pinecone.helpers.RandomStringBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.openapitools.db_control.client.model.CreateIndexForModelRequest;
import org.openapitools.db_control.client.model.CreateIndexForModelRequestEmbed;
import org.openapitools.db_control.client.model.DeletionProtection;
import org.openapitools.db_data.client.ApiException;
import org.openapitools.db_data.client.model.SearchRecordsRequestQuery;
import org.openapitools.db_data.client.model.SearchRecordsResponse;
import org.openapitools.db_data.client.model.UpsertRecord;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
...

Pinecone pinecone = new Pinecone.Builder(System.getenv("PINECONE_API_KEY")).build();
String indexName = RandomStringBuilder.build("inf", 8);

// Create index associated with a model
HashMap<String, String> fieldMap = new HashMap<>();
fieldMap.put("text", "chunk_text");
CreateIndexForModelRequestEmbed embed = new CreateIndexForModelRequestEmbed()
        .model("multilingual-e5-large")
        .fieldMap(fieldMap);
pinecone.createIndexForModel(indexName, CreateIndexForModelRequest.CloudEnum.AWS, "us-west-2", embed, DeletionProtection.DISABLED, new HashMap<>());

// Wait for index to be created
Thread.sleep(10000);

Index index = pinecone.getIndexConnection(indexName);

// Upsert records
HashMap<String, String> record1 = new HashMap<>();
record1.put("_id", "rec1");
record1.put("category", "digestive system");
record1.put("chunk_text", "Apples are a great source of dietary fiber, which supports digestion and helps maintain a healthy gut.");

HashMap<String, String> record2 = new HashMap<>();
record2.put("_id", "rec2");
record2.put("category", "cultivation");
record2.put("chunk_text", "Apples originated in Central Asia and have been cultivated for thousands of years, with over 7,500 varieties available today.");

HashMap<String, String> record3 = new HashMap<>();
record3.put("_id", "rec3");
record3.put("category", "immune system");
record3.put("chunk_text", "Rich in vitamin C and other antioxidants, apples contribute to immune health and may reduce the risk of chronic diseases.");

HashMap<String, String> record4 = new HashMap<>();
record4.put("_id", "rec4");
record4.put("category", "endocrine system");
record4.put("chunk_text", "The high fiber content in apples can also help regulate blood sugar levels, making them a favorable snack for people with diabetes.");

upsertRecords.add(record1);
upsertRecords.add(record2);
upsertRecords.add(record3);
upsertRecords.add(record4);

index.upsertRecords("example-namespace", upsertRecords);

// Wait for vectors to be upserted
Thread.sleep(5000);

String namespace = "example-namespace";
List<String> fields = new ArrayList<>();
fields.add("category");
fields.add("chunk_text");

SearchRecordsRequestRerank rerank = new SearchRecordsRequestRerank()
    .model("bge-reranker-v2-m3")
    .topN(2)
    .rankFields(Arrays.asList("chunk_text"));

// Search records
SearchRecordsResponse recordsResponse = index.searchRecordsByText("Disease prevention", namespace, fields, 4, null, rerank);
System.out.println(recordsResponse);

Backups and restore

You can now create and manage backups of serverless indexes. It is a static, non-queryable copy of an index that represents a set of records. You can create a backup of a serverless index, and you can create a new serverless index from a backup. You can read more about backups here.

Following methods were added for Backups:

  1. createBackup(String indexName, String backupName, String description)
  2. listIndexBackups(String indexName)
  3. listIndexBackups(String indexName, Integer limit, String paginationToken)
  4. listProjectBackups()
  5. describeBackup(String backupId)
  6. deleteBackup(String backupId)
  7. createIndexFromBackup(String backupId,
  8. String indexName,
  9. Map<String, String> tags,
  10. DeletionProtection deletionProtection)
  11. CreateIndexFromBackupResponse createIndexFromBackup(String backupId, String indexName)

Following methods were added for Restore:

  1. describeRestoreJob(String jobId)
  2. listRestoreJobs(Integer limit)
  3. listRestoreJobs(String paginationToken)
  4. listRestoreJobs(Integer limit, String paginationToken)

Below code shows all of the methods added for backup and restore for a serverless index.

import io.pinecone.clients.Pinecone;
import org.openapitools.db_control.client.ApiException;
import org.openapitools.db_control.client.model.*;
...

Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();

String indexName1 = "test-index-1";
String indexName2 = "test-index-2";

// create a backup from index
BackupModel backupModel1 = pinecone.createBackup(indexName1, "backup-id-1", "description-for-backup-1");
System.out.println("backupModel1: " + backupModel1);

BackupModel backupModel2 = pinecone.createBackup(indexName2, "backup-id-2", "description-for-backup-2");
System.out.println("backupModel2: " + backupModel2);

// list all backups for an index
BackupList backupList = pinecone.listIndexBackups(indexName1);
System.out.println("backupList for index1: " + backupList);

// list all backups for a project
backupList = pinecone.listProjectBackups();
System.out.println("backupList for project: " + backupList);

// describe backup
backupModel1 = pinecone.describeBackup(backupModel1.getBackupId());
System.out.println("describe backup for index1: " + backupModel1);

backupModel2 = pinecone.describeBackup(backupModel2.getBackupId());
System.out.println("describe backup index2: " + backupModel2);

// delete backup
pinecone.deleteBackup(backupModel1.getBackupId());

// wait for the backup to be ready
Thread.sleep(10000);

// create index from a backup
CreateIndexFromBackupResponse backupResponse = pinecone.createIndexFromBackup(backupModel1.getBackupId(), "test-index-3");
System.out.println(backupResponse.getRestoreJobId());

// wait for index to be created
Thread.sleep(5000);

// describeRestoreJob
RestoreJobModel restoreJobModel = pinecone.describeRestoreJob(backupResponse.getRestoreJobId());
System.out.println("restore model: " + restoreJobModel);

// listRestoreJobs
RestoreJobList restoreJobList = pinecone.listRestoreJobs(2);
System.out.println("restore job list: " + restoreJobList);

Namespaces

You now have the ability to work more explicitly with namespaces that are associated with an index. There have been several namespace methods added to the Index class:

  1. listNamespaces()
  2. listNamespaces(String paginationToken)
  3. listNamespaces(String paginationToken, int limit)
  4. describeNamespace(String namespace)
  5. deleteNamespace(String namespace)

Following example shows listNamespaces(), describeNamespace(), and deleteNamespace() methods for both index and asyncIndex.

import io.pinecone.clients.AsyncIndex;
import io.pinecone.clients.Index;
import io.pinecone.clients.Pinecone;
import io.pinecone.proto.ListNamespacesResponse;
import io.pinecone.proto.NamespaceDescription;

import java.util.concurrent.ExecutionException;
...

String indexName = "PINECONE_INDEX_NAME";
Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();

//// Sync index example
Index index = pinecone.getIndexConnection(indexName);

// list namespaces without pagination token and limit (if no limit is passed, it'll default to 100)
ListNamespacesResponse listNamespacesResponse = index.listNamespaces();

// list namespaces with pagination token
listNamespacesResponse = index.listNamespaces("some-pagination-token");

// list namespaces with pagination token and a custom limit of 5
listNamespacesResponse = index.listNamespaces("some-pagination-token", 5);

// describe a namespace
NamespaceDescription namespaceDescription = index.describeNamespace("namespace");

// delete a namespace
index.deleteNamespace("namespace");


//// AsyncIndex example
AsyncIndex asyncIndex = pinecone.getAsyncIndexConnection(indexName);

// list namespaces without pagination token and limit (if no limit is passed, it'll default to 100)
ListNamespacesResponse asyncListNamespacesResponse = index.listNamespaces();

// list namespaces with pagination token
asyncListNamespacesResponse = asyncIndex.listNamespaces("some-pagination-token").get();

// list namespaces with pagination token and a custom limit of 5
asyncListNamespacesResponse = asyncIndex.listNamespaces("some-pagination-token", 5).get();

// describe a namespace
NamespaceDescription asyncNamespaceDescription = asyncIndex.describeNamespace("some-namespace").get();

// delete a namespace
asyncIndex.deleteNamespace("some-namespace");

What's Changed

Read more

v4.0.1 Release

14 Feb 20:09
c83ea24
Compare
Choose a tag to compare

Fixed issue #173: Thread safety issue for getIndexConnection() and getAsyncIndexConnection()

The getIndexConnection() and getAsyncIndexConnection() methods were not thread-safe. The underlying connectionsMap, which maintains a mapping of index names (String) to their corresponding PineconeConnection instances, was incorrectly updating all entries to reference the most recently created connection. This fix ensures that each index maintains its own distinct connection, preventing unintended overwrites in multi-threaded environments.

What's Changed

Full Changelog: v4.0.0...v4.0.1

v4.0.0 Release

03 Feb 19:47
6ed8d46
Compare
Choose a tag to compare

This version of the Pinecone Java SDK introduces Sparse Indexes. It also supports version 2025-01 of the Pinecone API. You can read more about versioning here.

Features

Sparse Index

Following is an example that shows how to create sparse index, upsert and query data into the index, and finally deleting the index.

import io.pinecone.proto.UpsertResponse;
import io.pinecone.unsigned_indices_model.QueryResponseWithUnsignedIndices;
import org.openapitools.db_control.client.model.DeletionProtection;
import org.openapitools.db_control.client.model.IndexModel;

import java.util.*;

public class SparseIndexExample {
    public static void main(String[] args) {
        // Instantiate Pinecone class
        Pinecone pinecone = new Pinecone
                .Builder(System.getenv("PINECONE_API_KEY"))
                .withSourceTag("pinecone_test")
                .build();

        // Create sparse Index
        String indexName = "example-index";
        String cloud = "aws";
        String region = "us-east-1";
        String vectorType = "sparse";
        Map<String, String> tags = new HashMap<>();
        tags.put("env", "test");
        pinecone.createSparseServelessIndex(indexName,
                cloud,
                region,
                DeletionProtection.ENABLED,
                tags,
                vectorType);

        // Wait for index to be ready
        Thread.sleep(10000);
        IndexModel indexModel = pinecone.describeIndex(indexName);
        System.out.println(indexModel.getStatus());

        // Upsert vectors
        Index index = pinecone.getIndexConnection(indexName);
        String id = "v1";
        ArrayList<Long> indices = new ArrayList<>();
        indices.add(1L);
        indices.add(2L);

        ArrayList<Float> values = new ArrayList<>();
        values.add(1f);
        values.add(2f);

        UpsertResponse upsertResponse = index.upsert("v1", Collections.emptyList(), indices, values, null, "");
        System.out.println(upsertResponse);

        // 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

        // Query by vector id
        QueryResponseWithUnsignedIndices queryResponse = index.queryByVectorId(1, id, true, false);
        System.out.println(queryResponse);

        // Delete sparse index
        pinecone.deleteIndex(indexName);
    }
}

Breaking Changes

Embed endpoint

  • parameters now accepts Map<String, Object> instead of EmbedRequestParameters.
  • The response class Embeddings has dense and sparse i.e. users will now have to use getDenseEmbedding() or getSparseEmbedding().
    For example: embeddings.getData().get(0).getValues() -> embeddings.getData().get(0).getDenseEmbedding().getValues().

Rerank endpoint

  • documents now accepts List<Map<String, Object>> instead of List<Map<String, String>>
  • parameters now accepts Map<String, Object> instead of Map<String, String>

What's Changed

Full Changelog: v3.1.0...v4.0.0

v3.1.0 Release

27 Nov 00:56
Compare
Choose a tag to compare

This version of the Pinecone Java SDK introduces support for specifying a base URL for control and data plane operations.

Features

Support to pass base url for control and data plane operations

Users can now specify the base URL (host:port) for both control and data plane operations using the Builder inner class of the Pinecone class. This is achieved with the withHost() method, supporting both HTTP and HTTPS endpoints. For HTTP endpoints on the data plane, users must also disable TLS using the withTlsEnabled() method.

The following example demonstrates how to perform control and data plane operations against a locally running emulator:

import io.pinecone.clients.Index;
import io.pinecone.clients.Pinecone;
import io.pinecone.configs.PineconeConfig;
import org.openapitools.db_control.client.ApiClient;
import org.openapitools.db_control.client.api.ManageIndexesApi;
import org.openapitools.db_control.client.model.DeletionProtection;
import java.util.Arrays;

...

String host = "http://localhost:5080";
Pinecone pinecone = new Pinecone.Builder("apiKey")
        .withHost(host)     // set host to localhost and port to 5080
        .withTlsEnabled(false)  // disable TLS for data plane operations
        .build();

// Create serverless index
pinecone.createServerlessIndex("serverless-index", "cosine", 3, "aws","us-east-1", DeletionProtection.DISABLED);
// Create pod index
pinecone.createPodsIndex("pod-index", 3, "us-east-1-aws", "p1.x1");

// Describe serverless index
pinecone.describeIndex("serverless-index");
// Describe pod index
pinecone.describeIndex("pod-index");

// Get index connection object for serverless-index
Index serverlessIndexConnection = pinecone.getIndexConnection("serverless-index");
// Get index connection object for pod-index
Index podIndexConnection = pinecone.getIndexConnection("pod-index");

// Upsert records into serverless index
serverlessIndexConnection.upsert("v1", Arrays.asList(1f, 2f, 3f));
// Upsert records into pod index
podIndexConnection.upsert("v1", Arrays.asList(1f, 2f, 3f));

// Query by vectorId from serverless index
serverlessIndexConnection.queryByVectorId(3, "v1");
// Query by vectorId from pod index
podIndexConnection.queryByVectorId(3, "v1");

// Delete serverless index
pinecone.deleteIndex("serverless-index");
// Delete pod index
pinecone.deleteIndex("pod-index");

What's Changed

Full Changelog: v2.1.0...v3.0.0

What's Changed

New Contributors

v3.0.0 Release

24 Oct 16:14
Compare
Choose a tag to compare

This version of the Pinecone Java SDK introduces Inference and Import. 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.

import io.pinecone.clients.Inference;
import io.pinecone.clients.Pinecone;
import org.openapitools.inference.client.ApiException;
import org.openapitools.inference.client.model.Embedding;
import org.openapitools.inference.client.model.EmbeddingsList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
...

Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();
Inference inference = pinecone.getInferenceClient();

// Prepare input sentences to be embedded
List<String> inputs = new ArrayList<>();
inputs.add("The quick brown fox jumps over the lazy dog.");
inputs.add("Lorem ipsum");

// Specify the embedding model and parameters
String embeddingModel = "multilingual-e5-large";

Map<String, Object> parameters = new HashMap<>();
parameters.put("input_type", "query");
parameters.put("truncate", "END");

// Generate embeddings for the input data
EmbeddingsList embeddings = inference.embed(embeddingModel, parameters, inputs);

// Get embedded data
List<Embedding> embeddedData = embeddings.getData();

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.

import io.pinecone.clients.Inference;
import io.pinecone.clients.Pinecone;
import org.openapitools.inference.client.ApiException;
import org.openapitools.inference.client.model.RerankResult;

import java.util.*;
...

Pinecone pinecone = new Pinecone.Builder(System.getenv("PINECONE_API_KEY")).build();
Inference inference = pinecone.getInferenceClient();

// The model to use for reranking
String model = "bge-reranker-v2-m3";

// The query to rerank documents against
String query = "The tech company Apple is known for its innovative products like the iPhone.";

// Add the documents to rerank
List<Map<String, String>> documents = new ArrayList<>();
Map<String, String> doc1 = new HashMap<>();
doc1.put("id", "vec1");
doc1.put("my_field", "Apple is a popular fruit known for its sweetness and crisp texture.");
documents.add(doc1);

Map<String, String> doc2 = new HashMap<>();
doc2.put("id", "vec2");
doc2.put("my_field", "Many people enjoy eating apples as a healthy snack.");
documents.add(doc2);

Map<String, String> doc3 = new HashMap<>();
doc3.put("id", "vec3");
doc3.put("my_field", "Apple Inc. has revolutionized the tech industry with its sleek designs and user-friendly interfaces.");
documents.add(doc3);

Map<String, String> doc4 = new HashMap<>();
doc4.put("id", "vec4");
doc4.put("my_field", "An apple a day keeps the doctor away, as the saying goes.");
documents.add(doc4);

// The fields to rank the documents by. If not provided, the default is "text"
List<String> rankFields = Arrays.asList("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
boolean returnDocuments = true;

// Additional model-specific parameters for the reranker
Map<String, String> parameters = new HashMap<>();
parameters.put("truncate", "END");

// Send ranking request
RerankResult result = inference.rerank(model, query, documents, rankFields, topN, returnDocuments, parameters);

// Get ranked data
System.out.println(result.getData());

Import

AsyncIndex 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.

import org.openapitools.db_data.client.ApiException;
import org.openapitools.db_data.client.model.ImportErrorMode;
import org.openapitools.db_data.client.model.StartImportResponse;
...

// Initialize pinecone object
Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();
// Get async imports connection object
AsyncIndex asyncIndex = pinecone.getAsyncIndexConnection("PINECONE_INDEX_NAME");

// s3 uri
String uri = "s3://path/to/file.parquet";

// Start an import
StartImportResponse response = asyncIndex.startImport(uri, "123-456-789", ImportErrorMode.OnErrorEnum.CONTINUE);

// List imports
ListImportsResponse response = asyncIndex.listImports(100, "some-pagination-token");

// Describe import
ImportModel importDetails = asyncIndex.describeImport("1");

// Cancel import
asyncIndex.cancelImport("2");

Breaking Changes

Import Statements Update:

In this version, we have made changes to the openAPI generated code. Packagecontrol is now renamed to db_control so the import path for model classes for control plane such as ApiException, IndexModel, CollectionModel, etc. are now changed. Please ensure that you modify your code to reflect the new import paths as follows:

- import org.openapitools.control.client.ApiException;
+ import org.openapitools.db_control.client.ApiException;

- import org.openapitools.control.client.model.IndexModel;
+ import org.openapitools.db_control.client.model.IndexModel; 

- import org.openapitools.client.model.CollectionModel;
+ import org.openapitools.db_control.client.model.CollectionModel;

- import org.openapitools.client.model.IndexList;
+ import org.openapitools.db_control.client.model.IndexList;

What's Changed

Full Changelog: v2.1.0...v3.0.0

v2.1.0 Release

18 Sep 19:12
27ea488
Compare
Choose a tag to compare

Added: Support to disable TLS for data plane operations

This release adds the support for users to disable TLS verification for data plane operations. Users can disable it by setting enableTLS parameter of PineconeConfig class to false. We do not recommend going to production with TLS verification disabled. Following example shows how to disable TLS verification:

import io.pinecone.clients.Index;
import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.pinecone.unsigned_indices_model.QueryResponseWithUnsignedIndices;
import io.pinecone.proto.UpsertResponse;
import java.util.Arrays;

public class DisableTLSExample {
    public static void main(String[] args) {
        PineconeConfig config = new PineconeConfig("api");
        config.setHost("localhost:5081");
        config.setTLSEnabled(false);
        PineconeConnection connection = new PineconeConnection(config);
        Index index = new Index(connection, "example-index");
        
        // Data plane operations
        // 1. Upsert data
        UpsertResponse upsertResponse = index.upsert("v1", Arrays.asList(1f, 2f, 3f));
        // 2. Query data
        QueryResponseWithUnsignedIndices queryResponse = index.queryByVectorId(1, "v1", true, true);
    }
}

What's Changed

New Contributors

Full Changelog: v2.0.0...v2.1.0

v2.0.0 Release

19 Jul 19:52
e5f79bb
Compare
Choose a tag to compare

Added: API versioning

This updated release of the Pinecone Java SDK depends on API version 2024-07. This v2 SDK release line should continue to receive fixes as long as the 2024-07 API version is in support.

Added: Deletion Protection

Use deletion protection to prevent your most important indexes from accidentally being deleted. This feature is available for both serverless and pod indexes.

To enable this feature for existing pod indexes, use configurePodsindex()

import io.pinecone.clients.Pinecone;
import org.openapitools.control.client.model.DeletionProtection;
...
        
Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();
pinecone.configurePodsIndex(indexName, DeletionProtection.ENABLED);

When deletion protection is enabled, calls to deleteIndex() will fail until you first disable the deletion protection.

# To disable deletion protection for pods index
pinecone.configurePodsIndex(indexName, DeletionProtection.DISABLED);

If you want to enable this feature at the time of index creation, createIndex now accepts as an enum argument. The feature is disabled by default.

import io.pinecone.clients.Pinecone;
import org.openapitools.client.model.IndexModel;
import org.openapitools.control.client.model.DeletionProtection;

...

Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build();
        
String indexName = "example-index";
String similarityMetric = "cosine";
int dimension = 1538;
String cloud = "aws";
String region = "us-west-2";

IndexModel indexModel = pinecone.createServerlessIndex(indexName, similarityMetric, dimension, cloud, region, DeletionProtection.ENABLED);

What's Changed

Full Changelog: v1.2.2...v2.0.0

v1.2.2 Release

28 Jun 18:36
8034dab
Compare
Choose a tag to compare

Added: Support for proxy configuration using proxyHost and proxyPort

Users can now configure proxy settings without the need to manually instantiate network handler for both data and control plane operations. Until now, users had to instantiate multiple Pinecone classes and pass customManagedChannel for data plane operations and configure OkHttpClient separately for control plane operations, involving more complex setup steps.

The update simplifies proxy configuration within the SDK, ensuring easier setup by allowing users to specify proxyHost and proxyPort.

Note:
Users need to set up certificate authorities (CAs) to establish secure connections. Certificates verify server identities and encrypt data exchanged between the SDK and servers

Example

  1. The following examples demonstrates how to configure proxy for data Plane operations via NettyChannelBuilder vs. using the newly added proxy config support:

Before:

import io.grpc.HttpConnectProxiedSocketAddress;
import io.grpc.ManagedChannel;
import io.grpc.ProxiedSocketAddress;
import io.grpc.ProxyDetector;
import io.pinecone.clients.Index;
import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.NegotiationType;
import io.grpc.netty.NettyChannelBuilder;
import io.pinecone.exceptions.PineconeException;

import javax.net.ssl.SSLException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;

import java.util.Arrays;

...
String apiKey = System.getenv("PINECONE_API_KEY");
String proxyHost = System.getenv("PROXY_HOST");
int proxyPort = Integer.parseInt(System.getenv("PROXY_PORT"));

PineconeConfig config = new PineconeConfig(apiKey);
String endpoint = System.getenv("PINECONE_HOST");
NettyChannelBuilder builder = NettyChannelBuilder.forTarget(endpoint);

ProxyDetector proxyDetector = new ProxyDetector() {
    @Override
    public ProxiedSocketAddress proxyFor(SocketAddress targetServerAddress) {
        SocketAddress proxyAddress = new InetSocketAddress(proxyHost, proxyPort);
        
        return HttpConnectProxiedSocketAddress.newBuilder()
                .setTargetAddress((InetSocketAddress) targetServerAddress)
                .setProxyAddress(proxyAddress)
                .build();
    }
};

// Create custom channel
try {
    builder = builder.overrideAuthority(endpoint)
            .negotiationType(NegotiationType.TLS)
            .keepAliveTimeout(5, TimeUnit.SECONDS)
            .sslContext(GrpcSslContexts.forClient().build())
            .proxyDetector(proxyDetector);
} catch (SSLException e) {
    throw new PineconeException("SSL error opening gRPC channel", e);
}

// Build the managed channel with the configured options
ManagedChannel channel = builder.build();
config.setCustomManagedChannel(channel);
PineconeConnection connection = new PineconeConnection(config);
Index index = new Index(connection, "PINECONE_INDEX_NAME");
// Data plane operations
// 1. Upsert data
System.out.println(index.upsert("v1", Arrays.asList(1F, 2F, 3F, 4F)));
// 2. Describe index stats
System.out.println(index.describeIndexStats());

After:

import io.pinecone.clients.Index;
import io.pinecone.clients.Pinecone;

...
String apiKey = System.getenv("PINECONE_API_KEY");
String proxyHost = System.getenv("PROXY_HOST");
int proxyPort = Integer.parseInt(System.getenv("PROXY_PORT"));

Pinecone pinecone = new Pinecone.Builder(apiKey)
        .withProxy(proxyHost, proxyPort)
        .build();

Index index = pinecone.getIndexConnection("PINECONE_INDEX_NAME");
// Data plane operation routed through the proxy server
// 1. Upsert data
System.out.println(index.upsert("v1", Arrays.asList(1F, 2F, 3F, 4F)));
// 2. Describe index stats
System.out.println(index.describeIndexStats());
  1. The following examples demonstrates how to configure proxy for control Plane operations via OkHttpClient vs. using the newly added proxy config support:
    Before:
import io.pinecone.clients.Pinecone;
import okhttp3.OkHttpClient;
import java.net.InetSocketAddress;
import java.net.Proxy;

...
String apiKey = System.getenv("PINECONE_API_KEY");
String proxyHost = System.getenv("PROXY_HOST");
int proxyPort = Integer.parseInt(System.getenv("PROXY_PORT"));

// Instantiate OkHttpClient instance and configure the proxy
OkHttpClient client = new OkHttpClient.Builder()
        .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
        .build();

// Instantiate Pinecone class with the custom OkHttpClient object
Pinecone pinecone = new Pinecone.Builder(apiKey)
        .withOkHttpClient(client)
        .build();

// Control plane operation routed through the proxy server
System.out.println(pinecone.describeIndex("PINECONE_INDEX"));

After:

import io.pinecone.clients.Pinecone;

...
String apiKey = System.getenv("PINECONE_API_KEY");
String proxyHost = System.getenv("PROXY_HOST");
int proxyPort = Integer.parseInt(System.getenv("PROXY_PORT"));

Pinecone pinecone = new Pinecone.Builder(apiKey)
        .withProxy(proxyHost, proxyPort)
        .build();

// Control plane operation routed through the proxy server
System.out.println(pinecone.describeIndex("PINECONE_INDEX"));

Fixed: Adding source tag and setting custom user agent string for gRPC

The user agent string was not correctly setup for gRPC calls and instead of using the custom sourceTag + user agent string, the user agent string would always default to "netty-java-grpc/<grpc_version>". This update fixes this issue so if the source tag is set by the users, it should be appended to the custom user agent string.

What's Changed

  • Allow : in the source tag and add pinecone_test as a source tag for all integration tests by @rohanshah18 in #137
  • Add proxy configuration for OkHTTPClient and NettyChannelBuilder by @rohanshah18 in #136
  • Fix useragent for grpc by @rohanshah18 in #138
  • Update pinecone client version to 1.2.2 and remove redundant client version declaration by @rohanshah18 in #139

Full Changelog: v1.2.1...v1.2.2

v1.2.1 Release

31 May 21:56
c7c02ee
Compare
Choose a tag to compare

Fixed: Uber jar

The META-INF/services directory contains service provider configuration files. It wasn't shaded correctly so the users were seeing NameResolverProvider error when running a data plane operation such as upsert using the uber jar.
Error:
Exception in thread "main" java.lang.IllegalArgumentException: Could not find a NameResolverProvider for index-name-somehost.pinecone.io.
The error is now fixed and users can use the uber jar for data plane operations successfully.

Example

The following example demonstrates how to use the uber jar in a pom.xml for a maven project:

<dependencies>
   <dependency>
      <groupId>io.pinecone</groupId>
      <artifactId>pinecone-client</artifactId>
      <version>1.2.1</version>
      <classifier>all</classifier>
   </dependency>
</dependencies>

What's Changed

Full Changelog: v1.2.0...v1.2.1