Skip to content
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

Create serverless index functionality #3

Merged
merged 32 commits into from
Jun 14, 2024
Merged

Conversation

ericapywang
Copy link
Contributor

@ericapywang ericapywang commented Jun 6, 2024

Problem

The SDK needs to have a way to easily create a serverless index, but this was not implemented yet.

Solution

We wrote a create_serverless_index() function which takes in the index name, dimension, metric, cloud, and region. It then makes a call to the OpenAPI create_serverless_index() endpoint.

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)
  • This change requires a documentation update
  • Infrastructure change (CI configs, etc)
  • Non-code change (docs, etc)
  • None of the above: (explain here)

Test Plan

We have test cases which should pass.

@ericapywang ericapywang marked this pull request as ready for review June 7, 2024 15:52
Copy link
Contributor

@ssmith-pc ssmith-pc left a comment

Choose a reason for hiding this comment

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

Left a few comments throughout, but the bones are really good here 👍

pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
Comment on lines 9 to 39
pub async fn create_serverless_index(
&self,
params: CreateServerlessIndexRequest,
) -> Result<IndexModel, PineconeError> {
let create_index_request = self.create_serverless_index_req(params);
match openapi::apis::manage_indexes_api::create_index(
&self.openapi_config(),
create_index_request?,
)
.await
{
Ok(index) => Ok(index),
Err(e) => Err(PineconeError::CreateIndexError { openapi_error: e }),
}
}

pub fn create_serverless_index_req(
&self,
params: CreateServerlessIndexRequest,
) -> Result<CreateIndexRequest, PineconeError> {
let metric_enum = match &params.metric {
Some(metric) => match metric.as_str() {
"cosine" => Ok(Some(Metric::Cosine)),
"euclidean" => Ok(Some(Metric::Euclidean)),
"dotproduct" => Ok(Some(Metric::Dotproduct)),
_ => Err(PineconeError::InvalidMetricError {
metric: metric.clone(),
}),
},
None => Ok(Some(Metric::Cosine)),
}?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is create_serverless_index the function users would hit, and create_serverless_index_req is more of an internal function?

If so, at a minimum we should only make pub the one we want them to hit, and the other should be left private to the crate.

We might want to go further and separate the public and private functions completely, but I'd save that for later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good point -- create_serverless_index should be the only one that users see. Will change that! When you say completely separate the public and private functions, do you mean separate them into different files?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes into different files probably, but also just logically separate them, like maybe you have a separate type where the internal impls live

pinecone_sdk/src/control/create_index.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/utils/errors.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/control/create_index.rs Outdated Show resolved Hide resolved
@ericapywang ericapywang marked this pull request as draft June 7, 2024 20:59
@emily-emily emily-emily marked this pull request as ready for review June 10, 2024 19:10
Copy link
Contributor

@ssmith-pc ssmith-pc left a comment

Choose a reason for hiding this comment

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

I didn't have a chance to review thoroughly yet, but left a couple comments to think about

pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
Comment on lines 70 to 84
// constructs CreateIndexParams from CreateIndexParamsBuilder fields
pub fn build(self) -> Result<CreateIndexParams, PineconeError> {
// required parameters
let name = self.name.ok_or(PineconeError::MissingNameError)?;
let dimension = self.dimension.ok_or(PineconeError::MissingDimensionError)?;
let spec = self.spec.ok_or(PineconeError::MissingSpecError)?;

Ok(CreateIndexParams {
name,
dimension,
metric: self.metric.unwrap_or(Metric::Cosine),
spec,
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

re: the previous comment about builder

it's better for users to discover the "true" interface at compile time vs runtime

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 makes sense. Part of why I liked the builder was because I thought it provided flexibility for users to not need to worry about variable order in the new function, though I guess they can also just construct the struct directly. Do you think it would make sense to have all of these construction methods so the user can choose what is best for them, or is it better to just limit to one or two?

Copy link
Contributor

Choose a reason for hiding this comment

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

In my opinion, it's probably better to limit it to one way of doing things for now. If I'm understanding Silas, this would make things easier from an implementation perspective and allowing users to understand the interface at compile time.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it would make sense to have all of these construction methods so the user can choose what is best for them, or is it better to just limit to one or two

No, that kind of "choose your own adventure" interface can lead to confusion for someone trying to figure out how to use it.

At a high level, I think we should be opinionated and design the interface that we think best expresses and supports the semantics and requirements of the underlying operation.

Putting that into practice:

  • The interface is clearly defined, and its immediately apparent which parameters are required.
  • Rely on compiler/strong types to guide the customer to satisfy the interface (their code won't compile until they satisfy the req'd params with valid values)
  • Optional params are clear in the interface
  • It's easy for customers to discover optional params (either as builder functions or as Option params). Either way it should be easy for them to set the optional params to valid values with type support from the compiler

pinecone_sdk/src/pinecone.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@austin-denoble austin-denoble left a comment

Choose a reason for hiding this comment

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

Really coming along, nice work! Given @ssmith-pc's feedback around the builder pattern, I think we should maybe think about going functional arguments instead if that makes sense.

pinecone_sdk/src/pinecone.rs Outdated Show resolved Hide resolved
@ericapywang ericapywang marked this pull request as draft June 12, 2024 17:45
Copy link

@haruska haruska left a comment

Choose a reason for hiding this comment

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

Apologies for the conflicting code reviews. I see @ssmith-pc recommended the builder pattern in an earlier review. This isn't very idiomatic in Rust code.

There are some modules and structs here that seem unnecessary. I'd also consider flattening the Pinecone struct a bit to not have nested "Config" and "Configuration" structs in it. Just have the attributes directly on the struct itself.

It seems the "openapi configuration" can be derived from the "Config" of the Pinecone struct. It should just do that when needed instead of storing a separate attribute.

pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
spec,
}
}
}
Copy link

Choose a reason for hiding this comment

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

Why have a separate struct for params instead of these attributes directly on the create_index fn?

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 something we were discussing and we still aren't entirely sure what is better. Mainly we thought that passing all parameters directly into the function could be messy if there are a lot of parameters (such as for creating the pod index) and the user needs to keep track of the parameter order in the function, but I'm not sure how much of a problem this really is.

pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
pinecone_sdk/src/models/create_index_request_params.rs Outdated Show resolved Hide resolved
control_plane_host: None,
additional_headers: None,
source_tag: None,
}
Copy link

Choose a reason for hiding this comment

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

This is equivalent to PineconeBuilder::default() if you derive Default on the struct.

control_plane_host: Option<String>,
additional_headers: Option<HashMap<String, String>>,
source_tag: Option<String>,
}
Copy link

Choose a reason for hiding this comment

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

I saw @ssmith-pc mention the builder pattern in an earlier review and apologies for contradicting here.

This pattern isn't very idiomatic in Rust code. Users should just create the Pinecone struct directly with the required attributes.

@haruska
Copy link

haruska commented Jun 12, 2024

I'd also just add, use modules a bit more sparingly. There probably shouldn't be modules for every action. It's ok to put the implementations in the "client" module (or whatever name.)

I'm also guessing the overall lib name for this sdk will be "pinecone"? If so, a Pinecone struct might get confusing. Maybe PineconeClient or some such.

@ericapywang ericapywang requested a review from haruska June 13, 2024 17:29
@ericapywang ericapywang marked this pull request as ready for review June 13, 2024 17:29
Copy link
Contributor

@ssmith-pc ssmith-pc left a comment

Choose a reason for hiding this comment

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

I think this looks good to move on

Copy link
Contributor

@austin-denoble austin-denoble left a comment

Choose a reason for hiding this comment

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

Agreed with @ssmith-pc , I think this looks good in terms of moving forward. Thanks for all the iteration and attention to detail around feedback!

Err(e) => Err(PineconeError::CreateIndexError { openapi_error: e }),
}
}
/// Lists all indexes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice job on the docstring. 👍

@emily-emily emily-emily merged commit 89d1e5b into main Jun 14, 2024
2 checks passed
@emily-emily emily-emily deleted the ericapywang/create-index branch June 14, 2024 14:18
Copy link

@haruska haruska left a comment

Choose a reason for hiding this comment

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

👍

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.

5 participants