Skip to content

Implement Inference Models and Namespaces APIs #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 30, 2025

Conversation

austin-denoble
Copy link
Contributor

@austin-denoble austin-denoble commented May 21, 2025

Problem

Inference Models and Namespaces are new API resources available in 2025-04. They need to be implemented in the Go client.

Additionally, there are a few client bugs which need to be fixed:

  • The Embed method and it's return type EmbedResponse need to be refactored to support both sparse and dense embedding responses, rather than just dense. Currently, embedding with a model that returns sparse values will cause errors.
  • The IndexConnection struct is not safe to reuse for performing operations across namespaces while reusing the existing gRPC connection for the index. This is because IndexConnection.Namespace is publicly exposed, and could be updated at any point.

Solution

Implement new namespaces and models API operations:

  • Namespace operations have been implemented on IndexConnection. They're exposed as methods which you can call via IndexConnection.ListNamespaces, IndexConnection.DescribeNamespace, or IndexConnection.DeleteNamespace.
  • Hosted model operations can be performed using the Client.Inference namespace (InferenceService struct) by calling client.Inference.DescribeModel or client.Inference.ListModels.

The InferenceService.Embed method now returns a different Embedding inside of EmbedResponse. Embedding has been refactored into a tagged union type which is basically just a struct that wraps either a SparseEmbedding or DenseEmbedding pointers. This seemed to be the best way to manage this type of thing in Go given the lack of explicit union types. I'm open to suggestions if anyone has something that may be a bit more ergonomic.

IndexConnection has been refactored to no longer expose IndexConnection.Namespace directly. Instead, Namespace is now a method which allows checking the currently targeted namespace. Users can now call IndexConnection.WithNamespace which will return a copy of the IndexConnection targeting the new namespace, but sharing the underlying gRPC connection. Again, I think this is a reasonable way of approaching this and allowing safely targeting multiple namespaces within an index, but I'm open to feedback.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
    • Breaking change for EmbedResponse on InferenceService.Embed
  • This change requires a documentation update
  • Infrastructure change (CI configs, etc)
  • Non-code change (docs, etc)
  • None of the above: (explain here)

Test Plan

CI - unit tests & integration tests

If you'd like to test the new APIs out yourself you can check the integration tests or README for more detailed examples:

Models:

	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)
        }

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

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, "my-namespace-1")
	if err != nil {
		log.Fatalf("Failed to describe namespace: %v: %v", "my-namespace-1", err)
	}

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

…r dense embeddings, add a few things for models API wiring
@austin-denoble austin-denoble changed the base branch from main to 2025-04 May 21, 2025 16:04
@austin-denoble austin-denoble marked this pull request as ready for review May 29, 2025 03:54
@@ -588,8 +588,10 @@ Pinecone indexes support working with vector data using operations such as upser

### Targeting an index

To perform data operations on an index, you target it using the `Index` method on a `Client` object. You will
need your index's `Host` value, which you can retrieve via `DescribeIndex` or `ListIndexes`.
To perform data operations on an index, you target it using the `Index` method on a `Client` object which returns a pointer to an `IndexConnection`. Calling `Index` will create and dial the index via a new gRPC connection. You can target a specific `Namespace` when calling `Index`, but if you want to reuse the connection with different namespaces, you can call `IndexConnection.WithNamespace`. If no `Namespace` is provided when establishing a new
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a bunch of cleaning up of README / doc comments which I noticed were either a bit dated, unclear, or needed changing due to some of the refactoring. There's a lot more to do, I need to do a real look over all of our examples and docs at some point.

@@ -1537,16 +1541,9 @@ type EmbedRequest struct {
// [EmbedParameters] contains model-specific parameters that can be used for generating embeddings.
//
// Fields:
//
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaning up some weirdly formatted comments.

err := json.NewDecoder(resBody).Decode(&embeddingsList)
if err != nil {
return nil, fmt.Errorf("failed to decode embeddings response: %w", err)
func decodeEmbedResponse(resBody io.ReadCloser) (*EmbedResponse, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a portion of the mentioned refactor to support both sparse and dense embedding responses. Check the models.go file for the associated types.

require.Equal(ts.T(), true, allDenseModels, "Expected all listed models to be of vector type 'dense'")
}

func (ts *IntegrationTests) TestGenerateEmbeddingsDense() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration tests for generating sparse and dense embeddings.

// - additionalMetadata: Additional metadata to be sent with each RPC request.
// - dataClient: The gRPC client for the index.
// - grpcConn: The gRPC connection.
type IndexConnection struct {
Namespace string
namespace string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this a private field on IndexConnection.

//
// Returns the number of vectors upserted or an error if the request fails.
// [IndexConnection.Namespace] allows returning the namespace the instance of [IndexConnection] is targeting.
func (idx *IndexConnection) Namespace() string {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposing a getter for the current namespace.

// if err != nil {
// log.Fatalf("Failed to upsert vectors in %s. Error: %v", idxConnNs2.Namespace, err)
// }
func (idx *IndexConnection) WithNamespace(namespace string) *IndexConnection {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes a shallow copy of the IndexConnection, which re-shares the underlying gRPC connection. This should be threadsafe from what I understand, while allowing people to reuse the connection.

//
// idxConnection, err := pc.Index(pinecone.NewIndexConnParams{Host: idx.Host})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of doc comment cleanup, apologies.

@@ -1581,6 +1777,42 @@ func (idx *IndexConnection) delete(ctx context.Context, req *db_data_grpc.Delete
return err
}

func decodeSearchRecordsResponse(body io.ReadCloser) (*SearchRecordsResponse, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of this is reshuffling things around plus some minor cleanup.

@@ -232,7 +281,7 @@ const (
Pending ImportStatus = "Pending"
)

// ImportErrorMode specifies how errors are handled during an [Import].
// [ImportErrorMode] specifies how errors are handled during an [Import].
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More doc comment nitpicky stuff - these just make these hyperlinks in the resulting Go docs which we do in most other places.

log.Fatalf("Failed to upsert vectors in SetupSuite: %v", err)
// Upsert vectors into each namespace
for _, ns := range namespaces {
idxConnNamespaced := ts.idxConn.WithNamespace(ns)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using WithNamespace here to seed the integration test index with multiple namespaces rather than one. This is leveraged in integration tests around the namespace operations.

@@ -77,8 +85,6 @@ func (ts *IntegrationTests) SetupSuite() {
}

fmt.Printf("\n %s set up suite completed successfully\n", ts.indexType)

// Poll for data freshness
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed from the test setup at some point.

Copy link
Contributor

@jhamon jhamon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being diligent on docs

@austin-denoble austin-denoble merged commit 4c42c76 into 2025-04 May 30, 2025
2 checks passed
@austin-denoble austin-denoble deleted the adenoble/implement-models-and-namespaces-apis branch May 30, 2025 21:28
austin-denoble added a commit that referenced this pull request Jun 11, 2025
## Problem
Inference Models and Namespaces are new API resources available in
`2025-04`. They need to be implemented in the Go client.

Additionally, there are a few client bugs which need to be fixed: 
- The `Embed` method and it's return type `EmbedResponse` need to be
refactored to support both sparse and dense embedding responses, rather
than just dense. Currently, embedding with a model that returns sparse
values will cause errors.
- The `IndexConnection` struct is not safe to reuse for performing
operations across namespaces while reusing the existing gRPC connection
for the index. This is because `IndexConnection.Namespace` is publicly
exposed, and could be updated at any point.

## Solution
Implement new namespaces and models API operations:
- Namespace operations have been implemented on `IndexConnection`.
They're exposed as methods which you can call via
`IndexConnection.ListNamespaces`, `IndexConnection.DescribeNamespace`,
or `IndexConnection.DeleteNamespace`.
- Hosted model operations can be performed using the `Client.Inference`
namespace (`InferenceService` struct) by calling
`client.Inference.DescribeModel` or `client.Inference.ListModels`.

The `InferenceService.Embed` method now returns a different `Embedding`
inside of `EmbedResponse`. `Embedding` has been refactored into a tagged
union type which is basically just a struct that wraps either a
`SparseEmbedding` or `DenseEmbedding` pointers. This seemed to be the
best way to manage this type of thing in Go given the lack of explicit
union types. I'm open to suggestions if anyone has something that may be
a bit more ergonomic.

`IndexConnection` has been refactored to no longer expose
`IndexConnection.Namespace` directly. Instead, `Namespace` is now a
method which allows checking the currently targeted namespace. Users can
now call `IndexConnection.WithNamespace` which will return a copy of the
`IndexConnection` targeting the new namespace, but sharing the
underlying gRPC connection. Again, I think this is a reasonable way of
approaching this and allowing safely targeting multiple namespaces
within an index, but I'm open to feedback.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [X] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
  - Breaking change for `EmbedResponse` on `InferenceService.Embed` 
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
CI - unit tests & integration tests

If you'd like to test the new APIs out yourself you can check the
integration tests or README for more detailed examples:

Models:
```go
	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)
        }

       multilingualModel, := pc.Inference.DescribeModel(ctx, "multilingual-e5-large")
	if err != nil {
		log.Fatalf("Failed to describe models: %v", err)
        }
```
Namespaces:
```go
	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, "my-namespace-1")
	if err != nil {
		log.Fatalf("Failed to describe namespace: %v: %v", "my-namespace-1", err)
	}

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


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210238631289243
  - https://app.asana.com/0/0/1209828518477630
austin-denoble added a commit that referenced this pull request Jun 11, 2025
## Problem
Inference Models and Namespaces are new API resources available in
`2025-04`. They need to be implemented in the Go client.

Additionally, there are a few client bugs which need to be fixed: 
- The `Embed` method and it's return type `EmbedResponse` need to be
refactored to support both sparse and dense embedding responses, rather
than just dense. Currently, embedding with a model that returns sparse
values will cause errors.
- The `IndexConnection` struct is not safe to reuse for performing
operations across namespaces while reusing the existing gRPC connection
for the index. This is because `IndexConnection.Namespace` is publicly
exposed, and could be updated at any point.

## Solution
Implement new namespaces and models API operations:
- Namespace operations have been implemented on `IndexConnection`.
They're exposed as methods which you can call via
`IndexConnection.ListNamespaces`, `IndexConnection.DescribeNamespace`,
or `IndexConnection.DeleteNamespace`.
- Hosted model operations can be performed using the `Client.Inference`
namespace (`InferenceService` struct) by calling
`client.Inference.DescribeModel` or `client.Inference.ListModels`.

The `InferenceService.Embed` method now returns a different `Embedding`
inside of `EmbedResponse`. `Embedding` has been refactored into a tagged
union type which is basically just a struct that wraps either a
`SparseEmbedding` or `DenseEmbedding` pointers. This seemed to be the
best way to manage this type of thing in Go given the lack of explicit
union types. I'm open to suggestions if anyone has something that may be
a bit more ergonomic.

`IndexConnection` has been refactored to no longer expose
`IndexConnection.Namespace` directly. Instead, `Namespace` is now a
method which allows checking the currently targeted namespace. Users can
now call `IndexConnection.WithNamespace` which will return a copy of the
`IndexConnection` targeting the new namespace, but sharing the
underlying gRPC connection. Again, I think this is a reasonable way of
approaching this and allowing safely targeting multiple namespaces
within an index, but I'm open to feedback.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [X] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
  - Breaking change for `EmbedResponse` on `InferenceService.Embed` 
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan
CI - unit tests & integration tests

If you'd like to test the new APIs out yourself you can check the
integration tests or README for more detailed examples:

Models:
```go
	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)
        }

       multilingualModel, := pc.Inference.DescribeModel(ctx, "multilingual-e5-large")
	if err != nil {
		log.Fatalf("Failed to describe models: %v", err)
        }
```
Namespaces:
```go
	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, "my-namespace-1")
	if err != nil {
		log.Fatalf("Failed to describe namespace: %v: %v", "my-namespace-1", err)
	}

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


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210238631289243
  - https://app.asana.com/0/0/1209828518477630
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants