Skip to content

Commit e1fd611

Browse files
feat(pinecone): Add rerank task for Pinecone component (#773)
Because - Rerank API is introduced from Pinecone - Resolves instill-ai/instill-core#1114 This commit - Added Pinecone Rerank Support. - Inputs: Documents and Query input. We can restrict output to n documents by setting optional field (top-n) - Output: Reranked Documents and Scores - Made URL in Pinecone Setup Optional as for rerank input URL is not required. For others, this will still need to be input. --------- Co-authored-by: chuang8511 <a0961192312@gmail.com>
1 parent 12619c6 commit e1fd611

File tree

6 files changed

+240
-11
lines changed

6 files changed

+240
-11
lines changed

pkg/component/data/pinecone/v0/README.mdx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The Pinecone component is a data component that allows users to build and search
99
It can carry out the following tasks:
1010
- [Query](#query)
1111
- [Upsert](#upsert)
12+
- [Rerank](#rerank)
1213

1314

1415

@@ -40,7 +41,7 @@ ${connection.<my-connection-id>}`.
4041
| Field | Field ID | Type | Note |
4142
| :--- | :--- | :--- | :--- |
4243
| API Key (required) | `api-key` | string | Fill in your Pinecone AI API key. You can create an api key in Pinecone Console. |
43-
| Pinecone Base URL (required) | `url` | string | Fill in your Pinecone base URL. It is in the form. |
44+
| Pinecone Index URL | `url` | string | Fill in your Pinecone index URL. It is in the form. |
4445

4546
</div>
4647

@@ -127,4 +128,32 @@ Writes vectors into a namespace. If a new value is upserted for an existing vect
127128
</div>
128129

129130

131+
### Rerank
132+
133+
Rerank documents, such as text passages, according to their relevance to a query. The input is a list of documents and a query. The output is a list of documents, sorted by relevance to the query.
134+
135+
<div class="markdown-col-no-wrap" data-col-1 data-col-2>
136+
137+
| Input | ID | Type | Description |
138+
| :--- | :--- | :--- | :--- |
139+
| Task ID (required) | `task` | string | `TASK_RERANK` |
140+
| Query (required) | `query` | string | The query to rerank the documents. |
141+
| Documents (required) | `documents` | array[string] | The documents to rerank. |
142+
| Top N | `top-n` | integer | The number of results to return sorted by relevance. Defaults to the number of inputs. |
143+
</div>
144+
145+
146+
147+
148+
149+
150+
<div class="markdown-col-no-wrap" data-col-1 data-col-2>
151+
152+
| Output | ID | Type | Description |
153+
| :--- | :--- | :--- | :--- |
154+
| Reranked Documents. | `documents` | array[string] | Reranked documents. |
155+
| Scores | `scores` | array[number] | The relevance score of the documents normalized between 0 and 1. |
156+
</div>
157+
158+
130159

pkg/component/data/pinecone/v0/config/definition.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"availableTasks": [
33
"TASK_QUERY",
4-
"TASK_UPSERT"
4+
"TASK_UPSERT",
5+
"TASK_RERANK"
56
],
67
"custom": false,
78
"documentationUrl": "https://www.instill.tech/docs/component/data/pinecone",

pkg/component/data/pinecone/v0/config/setup.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"type": "string"
1717
},
1818
"url": {
19-
"description": "Fill in your Pinecone base URL. It is in the form.",
19+
"description": "Fill in your Pinecone index URL. It is in the form.",
2020
"instillUpstreamTypes": [
2121
"value"
2222
],
@@ -25,17 +25,15 @@
2525
],
2626
"instillSecret": false,
2727
"instillUIOrder": 1,
28-
"title": "Pinecone Base URL",
28+
"title": "Pinecone Index URL",
2929
"type": "string"
3030
}
3131
},
3232
"required": [
33-
"api-key",
34-
"url"
33+
"api-key"
3534
],
3635
"instillEditOnNodeFields": [
37-
"api-key",
38-
"url"
36+
"api-key"
3937
],
4038
"title": "Pinecone Connection",
4139
"type": "object"

pkg/component/data/pinecone/v0/config/tasks.json

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,5 +291,97 @@
291291
"title": "Output",
292292
"type": "object"
293293
}
294+
},
295+
"TASK_RERANK": {
296+
"instillShortDescription": "Rerank documents, such as text passages, according to their relevance to a query.",
297+
"description": "Rerank documents, such as text passages, according to their relevance to a query. The input is a list of documents and a query. The output is a list of documents, sorted by relevance to the query.",
298+
"input": {
299+
"instillUIOrder": 0,
300+
"properties": {
301+
"query": {
302+
"description": "The query to rerank the documents.",
303+
"instillAcceptFormats": [
304+
"string"
305+
],
306+
"instullUIMultiline": false,
307+
"instillUIOrder": 0,
308+
"instillUpstreamTypes": [
309+
"value",
310+
"reference",
311+
"template"
312+
],
313+
"title": "Query",
314+
"type": "string"
315+
},
316+
"documents": {
317+
"description": "The documents to rerank.",
318+
"instillUIOrder": 1,
319+
"instillUpstreamTypes": [
320+
"value",
321+
"reference"
322+
],
323+
"items": {
324+
"type": "string"
325+
},
326+
"minItems": 1,
327+
"title": "Documents",
328+
"type": "array"
329+
},
330+
"top-n": {
331+
"description": "The number of results to return sorted by relevance. Defaults to the number of inputs.",
332+
"instillAcceptFormats": [
333+
"integer"
334+
],
335+
"instillUIOrder": 2,
336+
"instillUpstreamTypes": [
337+
"value",
338+
"reference"
339+
],
340+
"title": "Top N",
341+
"type": "integer"
342+
}
343+
},
344+
"required": [
345+
"query",
346+
"documents"
347+
],
348+
"title": "Input",
349+
"type": "object"
350+
},
351+
"output": {
352+
"instillUIOrder": 0,
353+
"properties": {
354+
"documents": {
355+
"description": "Reranked documents.",
356+
"instillFormat": "array:string",
357+
"items": {
358+
"instillFormat": "string",
359+
"title": "Documents",
360+
"type": "string"
361+
},
362+
"instillUIOrder": 0,
363+
"title": "Reranked documents.",
364+
"type": "array"
365+
},
366+
"scores": {
367+
"description": "The relevance score of the documents normalized between 0 and 1.",
368+
"instillFormat": "array:number",
369+
"items": {
370+
"instillFormat": "number",
371+
"title": "Score",
372+
"type": "number"
373+
},
374+
"instillUIOrder": 1,
375+
"title": "Scores",
376+
"type": "array"
377+
}
378+
},
379+
"required": [
380+
"documents",
381+
"scores"
382+
],
383+
"title": "Output",
384+
"type": "object"
385+
}
294386
}
295387
}

pkg/component/data/pinecone/v0/main.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import (
1717
const (
1818
taskQuery = "TASK_QUERY"
1919
taskUpsert = "TASK_UPSERT"
20+
taskRerank = "TASK_RERANK"
2021

2122
upsertPath = "/vectors/upsert"
2223
queryPath = "/query"
24+
rerankPath = "/rerank"
2325
)
2426

2527
//go:embed config/definition.json
@@ -59,7 +61,8 @@ func (c *component) CreateExecution(x base.ComponentExecution) (base.IExecution,
5961
}, nil
6062
}
6163

62-
func newClient(setup *structpb.Struct, logger *zap.Logger) *httpclient.Client {
64+
// newIndexClient creates a new httpclient.Client with the index URL provided in setup
65+
func newIndexClient(setup *structpb.Struct, logger *zap.Logger) *httpclient.Client {
6366
c := httpclient.New("Pinecone", getURL(setup),
6467
httpclient.WithLogger(logger),
6568
httpclient.WithEndUserError(new(errBody)),
@@ -71,6 +74,23 @@ func newClient(setup *structpb.Struct, logger *zap.Logger) *httpclient.Client {
7174
return c
7275
}
7376

77+
// newBaseClient creates a new httpclient.Client with the default Pinecone API URL.
78+
func newBaseClient(setup *structpb.Struct, logger *zap.Logger) *httpclient.Client {
79+
c := httpclient.New("Pinecone", "https://api.pinecone.io",
80+
httpclient.WithLogger(logger),
81+
httpclient.WithEndUserError(new(errBody)),
82+
)
83+
84+
c.SetHeader("Api-Key", getAPIKey(setup))
85+
c.SetHeader("User-Agent", "source_tag=instillai")
86+
87+
// Currently, by default Pinecone API redirects request to OLDEST stable version i.e. 2024-04 right now and does not support Rerank
88+
// It is recommended by Pinecone to specify API version to use: https://docs.pinecone.io/reference/api/versioning#specify-an-api-version
89+
c.SetHeader("X-Pinecone-API-Version", "2024-10")
90+
91+
return c
92+
}
93+
7494
func getAPIKey(setup *structpb.Struct) string {
7595
return setup.GetFields()["api-key"].GetStringValue()
7696
}
@@ -81,8 +101,6 @@ func getURL(setup *structpb.Struct) string {
81101

82102
func (e *execution) Execute(ctx context.Context, jobs []*base.Job) error {
83103

84-
req := newClient(e.Setup, e.GetLogger()).R()
85-
86104
for _, job := range jobs {
87105
input, err := job.Input.Read(ctx)
88106
if err != nil {
@@ -92,6 +110,8 @@ func (e *execution) Execute(ctx context.Context, jobs []*base.Job) error {
92110
var output *structpb.Struct
93111
switch e.Task {
94112
case taskQuery:
113+
req := newIndexClient(e.Setup, e.GetLogger()).R()
114+
95115
inputStruct := queryInput{}
96116
err := base.ConvertFromStructpb(input, &inputStruct)
97117
if err != nil {
@@ -122,6 +142,8 @@ func (e *execution) Execute(ctx context.Context, jobs []*base.Job) error {
122142
continue
123143
}
124144
case taskUpsert:
145+
req := newIndexClient(e.Setup, e.GetLogger()).R()
146+
125147
v := upsertInput{}
126148
err := base.ConvertFromStructpb(input, &v)
127149
if err != nil {
@@ -145,7 +167,34 @@ func (e *execution) Execute(ctx context.Context, jobs []*base.Job) error {
145167
job.Error.Error(ctx, err)
146168
continue
147169
}
170+
case taskRerank:
171+
// rerank task does not need index URL, so using the base client with the default pinecone API URL.
172+
req := newBaseClient(e.Setup, e.GetLogger()).R()
173+
174+
// parse input struct
175+
inputStruct := rerankInput{}
176+
err := base.ConvertFromStructpb(input, &inputStruct)
177+
if err != nil {
178+
job.Error.Error(ctx, err)
179+
continue
180+
}
181+
182+
// make API request to rerank task
183+
resp := rerankResp{}
184+
req.SetResult(&resp).SetBody(inputStruct.asRequest())
185+
if _, err := req.Post(rerankPath); err != nil {
186+
job.Error.Error(ctx, httpclient.WrapURLError(err))
187+
continue
188+
}
189+
190+
// convert response to output struct
191+
output, err = base.ConvertToStructpb(resp.toOutput())
192+
if err != nil {
193+
job.Error.Error(ctx, err)
194+
continue
195+
}
148196
}
197+
149198
err = job.Output.Write(ctx, output)
150199
if err != nil {
151200
job.Error.Error(ctx, err)

pkg/component/data/pinecone/v0/structs.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,66 @@ type upsertOutput struct {
8383
RecordsUpserted int64 `json:"upserted-count"`
8484
}
8585

86+
type Document struct {
87+
Text string `json:"text"`
88+
}
89+
90+
type rerankInput struct {
91+
// not taking model as input for now as only one model is supported for rerank task: https://docs.pinecone.io/guides/inference/understanding-inference#models
92+
//ModelName string `json:"model-name"`
93+
Query string `json:"query"`
94+
Documents []string `json:"documents"`
95+
TopN int `json:"top-n"`
96+
}
97+
98+
func (r *rerankInput) asRequest() *rerankReq {
99+
reqDocuments := make([]Document, 0, len(r.Documents))
100+
for _, doc := range r.Documents {
101+
reqDocuments = append(reqDocuments, Document{Text: doc})
102+
}
103+
104+
// TODO: make model configurable in tasks.json
105+
return &rerankReq{
106+
Model: "bge-reranker-v2-m3",
107+
Query: r.Query,
108+
TopN: r.TopN,
109+
Documents: reqDocuments,
110+
}
111+
}
112+
113+
type rerankReq struct {
114+
Model string `json:"model"`
115+
Query string `json:"query"`
116+
TopN int `json:"top_n,omitempty"`
117+
Documents []Document `json:"documents"`
118+
}
119+
120+
type rerankResp struct {
121+
Data []struct {
122+
Index int `json:"index"`
123+
Document Document `json:"document"`
124+
Score float64 `json:"score"`
125+
} `json:"data"`
126+
}
127+
128+
func (r *rerankResp) toOutput() rerankOutput {
129+
documents := make([]string, 0, len(r.Data))
130+
scores := make([]float64, 0, len(r.Data))
131+
for _, d := range r.Data {
132+
documents = append(documents, d.Document.Text)
133+
scores = append(scores, d.Score)
134+
}
135+
return rerankOutput{
136+
Documents: documents,
137+
Scores: scores,
138+
}
139+
}
140+
141+
type rerankOutput struct {
142+
Documents []string `json:"documents"`
143+
Scores []float64 `json:"scores"`
144+
}
145+
86146
type errBody struct {
87147
Msg string `json:"message"`
88148
}

0 commit comments

Comments
 (0)