Skip to content

Release v4.0.0

Compare
Choose a tag to compare
@austin-denoble austin-denoble released this 12 Jun 06:37
· 4 commits to main since this release

This version of the Pinecone Go SDK depends on version 2025-04 of the Pinecone API. You can read more about versioning here. This v4 SDK release line should continue to receive fixes as long as the 2025-04 API version is in support.

Features

Namespaces

You can now work directly with namespaces associated with a specific index. There have been three new methods added to the IndexConnection class allowing you to DescribeNamespace, ListNamespaces within an index, and DeleteNamespace. Currently, namespaces are still created implicitly when calling Upsert against a specific IndexConnection targeting a namespace.

Note: You can now use IndexConnection.WithNamespace to create a shallow copy of the gRPC connection targeting a different namespace which reuses the underlying connection internals. See "Bug fixes & Breaking Changes" below for more details.

import (
	"context"
	"log"

	"github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func Namespaces() {
	ctx := context.Background()

	pc, err := pinecone.NewClient(pinecone.NewClientParams{
		ApiKey: "YOUR_API_KEY",
	})
	if err != nil {
		log.Fatalf("Failed to create Client: %v", err)
	}

	idx, err := pc.DescribeIndex(ctx, "example-index")
	if err != nil {
		log.Fatalf("Failed to describe index \"%v\": %v", idx.Name, err)
	}

	idxConnection, err := pc.Index(pinecone.NewIndexConnParams{Host: idx.Host})
	if err != nil {
		log.Fatalf("Failed to create IndexConnection for Host: %v: %v", idx.Host, err)
	}

	// list namespaces
	limit := uint32(10)
	namespaces, err := idxConnection.ListNamespaces(ctx, &pinecone.ListNamespacesParams{
		Limit: &limit,
	})
	if err != nil {
		log.Fatalf("Failed to list namespaces for Host: %v: %v", idx.Host, err)
	}

	// describe a namespace
	namespace1, err := idxConnection.DescribeNamespace(ctx, namespaces.Namespaces[0].Name)
	if err != nil {
		log.Fatalf("Failed to describe namespace: %v: %v", namespaces.Namespaces[0].Name, err)
	}

	// delete a namespace
	err = idxConnection.DeleteNamespace(namespace1.Name)
	if err != nil {
		log.Fatalf("Failed to delete namespace: %v: %v", "my-namespace-1", err)
	}

	// target a new specific namespace
	idxConnectionNs2 := idxConnection.WithNamespace(namespaces.Namespaces[1].Name)
}

Backups and Restore Jobs

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.

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func BackupsAndRestore() {
	ctx := context.Background()

	clientParams := pinecone.NewClientParams{
		ApiKey: os.Getenv("PINECONE_API_KEY"),
	}

	pc, err := pinecone.NewClient(clientParams)
	if err != nil {
		log.Fatalf("Failed to create Client: %w", err)
	}

	indexName := "my-index"
	backupName := fmt.Sprintf("backup-%s")
	backupDesc := fmt.Sprintf("Backup created for index %s", indexName)
	fmt.Printf("Creating backup: %s for index: %s\n", backupName, indexName)

	backup, err := pc.CreateBackup(ctx, &pinecone.CreateBackupParams{
		IndexName:   indexName,
		Name:        &backupName,
		Description: &backupDesc,
	})
	if err != nil {
		log.Fatalf("Failed to create backup: %w", err)
	}

	backup, err = pc.DescribeBackup(ctx, backup.BackupId)
	if err != nil {
		log.Fatalf("Failed to describe backup: %w", err)
	}

	// wait for backup to be "Complete" before triggering a restore job
	log.Printf("Backup status: %v", backup.Status)

	limit := 10
	backups, err := pc.ListBackups(ctx, &pinecone.ListBackupsParams{
		Limit:     &limit,
		IndexName: &indexName,
	})
	if err != nil {
		log.Fatalf("Failed to list backups: %w", err)
	}

	// create a new serverless index from the backup
	restoredIndexName := indexName + "-from-backup"
	restoredIndexTags := pinecone.IndexTags{"restored_on": time.Now().Format("2006-01-02 15:04")}
	createIndexFromBackupResp, err := pc.CreateIndexFromBackup(context.Background(), &pinecone.CreateIndexFromBackupParams{
		BackupId: ts.backupId,
		Name:     restoredIndexName,
		Tags:     &restoredIndexTags,
	})

	// check the status of the index restoration
	restoreJob, err := pc.DescribeRestoreJob(ctx, restoreJob.RestoreJobId)
	if err != nil {
		log.Fatalf("Failed to describe restore job: %w", err)
	}
}

Inference Models

You can now use the InferenceService class within Client.Inference to browse models hosted by Pinecone, including detailed configuration options for each model.

You can list all available models, with the options of filtering by model Type ("embed", "rerank"), and VectorType ("sparse", "dense") for models with Type "embed".

import (
	"context"
	"log"

	"github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func InferenceModels() {
	ctx := context.Background()

	pc, err := pinecone.NewClient(pinecone.NewClientParams{
		ApiKey: "YOUR_API_KEY",
	})
	if err != nil {
		log.Fatalf("Failed to create Client: %v", err)
	}

	embed := "embed"
	rerank := "rerank"

	embedModels, err := pc.Inference.ListModels(ctx, &pinecone.ListModelsParams{
		Type: &embed,
	})
	if err != nil {
		log.Fatalf("Failed to list embedding models: %v", err)
	}

	rerankModels, err := pc.Inference.ListModels(ctx, &pinecone.ListModelsParams{
		Type: &rerank,
	})
	if err != nil {
		log.Fatalf("Failed to list reranking models: %v", err)
	}
}

You can also describe a single model by name:

import (
	"context"
	"fmt"
	"log"

	"github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func InferenceModel() {
	ctx := context.Background()

	pc, err := pinecone.NewClient(pinecone.NewClientParams{
		ApiKey: "YOUR_API_KEY",
	})
	if err != nil {
		log.Fatalf("Failed to create Client: %v", err)
	}

	model, err := pc.Inference.DescribeModel(ctx, "multilingual-e5-large")
	if err != nil {
		log.Fatalf("Failed to get model: %v", err)
	}

	fmt.Printf("Model (multilingual-e5-large): %+v\n", model)
}

Bug fixes & Breaking Changes

IndexConnection now properly supports targeting multiple namespaces in an index while reusing the underlying gRPC connection instead of needing to re-dial every time. you want to target a new namespace in your index. IndexConnection used to expose Namespace as a field, which could be updated at any time, and was originally set when creating a new IndexConnection through Client using NewIndexConnParams. Now, you can re-use IndexConnection against a new namespace by using IndexConnection.WithNamespace, which will return a new pointer to a shallow copy of the struct.

package main

import (
	"context"
	"log"

	"github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func WithNamespace() {
	ctx := context.Background()

	pc, err := pinecone.NewClient(pinecone.NewClientParams{
		ApiKey: "YOUR_API_KEY",
	})
	if err != nil {
		log.Fatalf("Failed to create Client: %v", err)
	}

	idx, err := pc.DescribeIndex(ctx, "example-index")
	if err != nil {
		log.Fatalf("Failed to describe index \"%v\": %v", idx.Name, err)
	}

	idxConnectionNs1, err := pc.Index(pinecone.NewIndexConnParams{Host: idx.Host, Namespace: "namespace-1"})
	if err != nil {
		log.Fatalf("Failed to create IndexConnection for Host: %v: %v", idx.Host, err)
	}

	// This will reuse the gRPC connection while targeting a different namespace
	idxConnectionNs2 := idxConnectionNs1.WithNamespace("namespace-2")

	vectors := []*pinecone.Vector{
		{
			Id:     "A",
			Values: []float32{0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
		},
		{
			Id:     "B",
			Values: []float32{0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2},
		},
		{
			Id:     "C",
			Values: []float32{0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3},
		},
		{
			Id:     "D",
			Values: []float32{0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4},
		},
	}

	countNs1, err := idxConnectionNs1.UpsertVectors(ctx, vectors)
	if err != nil {
		log.Fatalf("Failed to upsert vectors to \"example-index\" in \"namespace-1\": %w", err)
	}

	countNs2, err := idxConnectionNs2.UpsertVectors(ctx, vectors)
	if err != nil {
		log.Fatalf("Failed to upsert vectors to \"example-index\" in \"namespace-1\": %w", err)
	}
}

The Embed operation on InferenceService now properly returns different structs when you're embedding using a sparse or dense model. Previously, only dense embedding values would be returned successfully. To handle this, the Embedding type has been updated to a tagged union holding one of either two pointers to a SparseEmbedding, or DenseEmbedding.

What's Changed

New Contributors

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