Skip to content

Commit f05a22e

Browse files
authored
Merge pull request #239 from jmartisk/pinecone-improvements
Various Pinecone improvements
2 parents da167ca + 6bf9f70 commit f05a22e

13 files changed

+229
-18
lines changed

pinecone/runtime/src/main/java/io/quarkiverse/langchain4j/pinecone/PineconeEmbeddingStore.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import dev.langchain4j.store.embedding.EmbeddingMatch;
2626
import dev.langchain4j.store.embedding.EmbeddingStore;
2727
import dev.langchain4j.store.embedding.RelevanceScore;
28+
import io.quarkiverse.langchain4j.pinecone.runtime.CreateIndexPodSpec;
2829
import io.quarkiverse.langchain4j.pinecone.runtime.CreateIndexRequest;
30+
import io.quarkiverse.langchain4j.pinecone.runtime.CreateIndexSpec;
2931
import io.quarkiverse.langchain4j.pinecone.runtime.DistanceMetric;
3032
import io.quarkiverse.langchain4j.pinecone.runtime.PineconeIndexOperationsApi;
3133
import io.quarkiverse.langchain4j.pinecone.runtime.PineconeVectorOperationsApi;
@@ -55,11 +57,13 @@ public PineconeEmbeddingStore(String apiKey,
5557
String namespace,
5658
String textFieldName,
5759
Duration timeout,
58-
Integer dimension) {
60+
Integer dimension,
61+
String podType,
62+
Duration indexReadinessTimeout) {
5963
this.indexName = indexName;
6064
this.dimension = dimension;
6165
String baseUrl = "https://" + indexName + "-" + projectId + ".svc." + environment + ".pinecone.io";
62-
String baseUrlIndexOperations = "https://controller." + environment + ".pinecone.io";
66+
String baseUrlIndexOperations = "https://api.pinecone.io";
6367
try {
6468
ClientHeadersFactory clientHeadersFactory = new ClientHeadersFactory() {
6569
@Override
@@ -91,15 +95,18 @@ public MultivaluedMap<String, String> update(MultivaluedMap<String, String> inco
9195
this.indexExists = new LazyValue<>(new Supplier<Object>() {
9296
@Override
9397
public Object get() {
94-
if (indexOperations.listIndexes().contains(indexName)) {
98+
if (indexOperations.listIndexes().getIndexes().stream().anyMatch(i -> i.getName().equals(indexName))) {
9599
Log.info("Pinecone index " + indexName + " already exists");
96100
} else {
97101
if (dimension == null) {
98102
throw new IllegalArgumentException(
99103
"quarkus.langchain4j.pinecone.dimension must be specified when creating a new index");
100104
}
101-
indexOperations.createIndex(new CreateIndexRequest(indexName, dimension, DistanceMetric.COSINE));
102-
Log.info("Created Pinecone index " + indexName + " with dimension = " + dimension);
105+
CreateIndexSpec spec = new CreateIndexSpec(new CreateIndexPodSpec(environment, podType));
106+
indexOperations.createIndex(new CreateIndexRequest(indexName, dimension, DistanceMetric.COSINE, spec));
107+
Log.info("Created Pinecone index " + indexName + " with dimension = " + dimension + ", " +
108+
"now waiting for it to be become ready...");
109+
waitForIndexToBecomeReady(indexName, indexReadinessTimeout);
103110
}
104111
return new Object();
105112
}
@@ -199,4 +206,20 @@ private void addAllInternal(List<String> ids, List<Embedding> embeddings, List<T
199206
Log.debug("Added embeddings: " + response.getUpsertedCount());
200207
}
201208

209+
private void waitForIndexToBecomeReady(String indexName, Duration timeout) {
210+
long start = System.currentTimeMillis();
211+
while (System.currentTimeMillis() - start < timeout.toMillis()) {
212+
try {
213+
Thread.sleep(1000);
214+
} catch (InterruptedException e) {
215+
throw new RuntimeException(e);
216+
}
217+
if (indexOperations.describeIndex(indexName).getStatus().isReady()) {
218+
Log.info("Pinecone index " + indexName + " is now ready");
219+
return;
220+
}
221+
}
222+
throw new RuntimeException("Index " + indexName + " did not become ready within " + timeout);
223+
}
224+
202225
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
import io.quarkus.runtime.annotations.RegisterForReflection;
6+
7+
@RegisterForReflection
8+
public class CreateIndexPodSpec {
9+
10+
private final String environment;
11+
12+
@JsonProperty("pod_type")
13+
private final String podType;
14+
15+
public CreateIndexPodSpec(String environment, String podType) {
16+
this.environment = environment;
17+
this.podType = podType;
18+
}
19+
20+
public String getEnvironment() {
21+
return environment;
22+
}
23+
24+
public String getPodType() {
25+
return podType;
26+
}
27+
}

pinecone/runtime/src/main/java/io/quarkiverse/langchain4j/pinecone/runtime/CreateIndexRequest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ public class CreateIndexRequest {
1414
private final String name;
1515
private final Integer dimension;
1616
private final DistanceMetric metric;
17+
private final CreateIndexSpec spec;
1718

18-
public CreateIndexRequest(String name, Integer dimension, DistanceMetric metric) {
19+
public CreateIndexRequest(String name, Integer dimension, DistanceMetric metric, CreateIndexSpec spec) {
1920
this.name = name;
2021
this.dimension = dimension;
2122
this.metric = metric;
23+
this.spec = spec;
2224
}
2325

2426
public String getName() {
@@ -32,4 +34,8 @@ public Integer getDimension() {
3234
public DistanceMetric getMetric() {
3335
return metric;
3436
}
37+
38+
public CreateIndexSpec getSpec() {
39+
return spec;
40+
}
3541
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import io.quarkus.runtime.annotations.RegisterForReflection;
4+
5+
@RegisterForReflection
6+
public class CreateIndexSpec {
7+
8+
private final CreateIndexPodSpec pod;
9+
10+
public CreateIndexSpec(CreateIndexPodSpec pod) {
11+
this.pod = pod;
12+
}
13+
14+
public CreateIndexPodSpec getPod() {
15+
return pod;
16+
}
17+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
5+
public class DescribeIndexResponse {
6+
7+
private final String name;
8+
9+
private final DistanceMetric metric;
10+
11+
private final int dimension;
12+
13+
private final String host;
14+
15+
private final IndexStatus status;
16+
17+
@JsonCreator
18+
public DescribeIndexResponse(String name, DistanceMetric metric, int dimension, String host, IndexStatus status) {
19+
this.name = name;
20+
this.metric = metric;
21+
this.dimension = dimension;
22+
this.host = host;
23+
this.status = status;
24+
}
25+
26+
public String getName() {
27+
return name;
28+
}
29+
30+
public DistanceMetric getMetric() {
31+
return metric;
32+
}
33+
34+
public int getDimension() {
35+
return dimension;
36+
}
37+
38+
public String getHost() {
39+
return host;
40+
}
41+
42+
public IndexStatus getStatus() {
43+
return status;
44+
}
45+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package io.quarkiverse.langchain4j.pinecone.runtime;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
35
public enum DistanceMetric {
46

7+
@JsonProperty("euclidean")
58
EUCLIDEAN,
9+
@JsonProperty("cosine")
610
COSINE,
11+
@JsonProperty("dotproduct")
712
DOTPRODUCT
813

914
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
5+
import io.quarkus.runtime.annotations.RegisterForReflection;
6+
7+
@RegisterForReflection
8+
public class IndexStatus {
9+
10+
private final boolean ready;
11+
12+
private final String state;
13+
14+
@JsonCreator
15+
public IndexStatus(boolean ready, String state) {
16+
this.ready = ready;
17+
this.state = state;
18+
}
19+
20+
public boolean isReady() {
21+
return ready;
22+
}
23+
24+
public String getState() {
25+
return state;
26+
}
27+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.annotation.JsonCreator;
6+
7+
import io.quarkus.runtime.annotations.RegisterForReflection;
8+
9+
@RegisterForReflection
10+
public class ListIndexesResponse {
11+
12+
private final List<DescribeIndexResponse> indexes;
13+
14+
@JsonCreator
15+
public ListIndexesResponse(List<DescribeIndexResponse> indexes) {
16+
this.indexes = indexes;
17+
}
18+
19+
public List<DescribeIndexResponse> getIndexes() {
20+
return indexes;
21+
}
22+
}

pinecone/runtime/src/main/java/io/quarkiverse/langchain4j/pinecone/runtime/PineconeConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ public interface PineconeConfig {
3939
*/
4040
Optional<Integer> dimension();
4141

42+
/**
43+
* The type of the pod to use. This is only used if the index doesn't exist yet and needs to be created.
44+
* The format: One of `s1`, `p1`, or `p2` appended with `.` and one of `x1`, `x2`, `x4`, or `x8`.
45+
*/
46+
@WithDefault("s1.x1")
47+
String podType();
48+
49+
/**
50+
* The timeout duration for the index to become ready. Only relevant if the index doesn't exist yet and needs to be
51+
* created. If not specified, 1 minute will be used.
52+
*/
53+
Optional<Duration> indexReadinessTimeout();
54+
4255
/**
4356
* The namespace.
4457
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.quarkiverse.langchain4j.pinecone.runtime;
2+
3+
import jakarta.ws.rs.core.Response;
4+
5+
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
6+
7+
public class PineconeExceptionMapper implements ResponseExceptionMapper<RuntimeException> {
8+
@Override
9+
public RuntimeException toThrowable(Response response) {
10+
// FIXME: do this for some more status codes?
11+
if (response.getStatus() == 400) {
12+
return new RuntimeException("Pinecone returned 400, error: " + response.readEntity(String.class));
13+
}
14+
return null;
15+
}
16+
}

0 commit comments

Comments
 (0)