Skip to content

Commit

Permalink
Add Assistant code to RC branch (#322)
Browse files Browse the repository at this point in the history
## Description 

This PR adds the code necessary to use
[Assistant](https://docs.pinecone.io/guides/assistant/understanding-assistant)
via our Node SDK.

There are many moving parts here, so I will only call out the most impt:
- I have divided the Assistant code into control plane ops and data
plane ops, as we do with the core DB code. The one exception to this is
the [Evaluation
code](https://docs.pinecone.io/guides/assistant/understanding-evaluation),
which is spec-d in the OAS under the data plane, but architecturally
makes more sense under the control plane, due to it being independent of
any particular Assistant.
- There are unit tests and integration tests for every endpoint. The
control plane integration tests spin up Assistants and delete them
inplace, while the data plane integration tests use the `setup` and
`teardown` files to call/update a centralized Assistant in order to use
our resources wisely (just like the core DB does with its data plane
int. tests)
- The data plane Assistant ops make use of a Singleton
(`assistantHostSingleton`) much like the core DB code does with
`indexHostSingleton`
- There is a new function called `getHostUrl` in the above-mentioned
Singleton. This determines which host (between the `eu` URL and the `us`
URL) to route the data plane calls to. If unable to determine this host,
the Singleton defaults it to the `us` URL.

A few things to call out for the future: 
- The Evaluation service is likely to become its own API (as heard
through the grapevine) since it's quite orthogonal to the current
conception of an Assistant as we have it today
- Currently, `chat` and `chatCompletion` are essentially the same code
with different interface names. The feature team tells me this is
because `chat` is very likely to diverge in the near-time from
`chatCompletion` with the addition of Pinecone-specific features
- Currently, the latency between when a file is uploaded to an Assistant
vs when the Assistant is queryable is very high (we have to sleep 30
secs in our integration tests). It would be ideal to cut this down
somehow, although that work will probably lie with the feature team
- The [Describe file happy
path](https://github.com/pinecone-io/pinecone-ts-client/actions/runs/12699145653/job/35399192319)
int. test in particular is flakey ATM because it takes a variable amount
of time for an uploaded file to be available for describing. The test
waits 30s, and I'm wary of increasing that limit since I don't want to
bloat our whole pipeline just waiting for this 1 test.

Misc.:
This PR includes removing support for Typescript versions <`4.5`, since
we were running into errors with those versions and aim to maintain a
2-ish year sliding window for which versions we support. Everything
pre-`v4.5` was published in ~2021, so is quite old.

## Basic usage
### Control plane stuffs
```typescript
import {Pinecone} from '@pinecone-database/pinecone';
const pc = new Pinecone();
await pc.assistant.<something>
```
### Data plane stuffs
```typescript
import {Pinecone} from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test';
const assistant = pc.assistant.Assistant(assistantName);
await assistant.<something>
```


## Type of Change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] 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)
- [x] Non-code change (docs, etc)
- [ ] None of the above: (explain here)

## Test Plan

CI passes.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1208894748669518

---------

Co-authored-by: Austin DeNoble <[email protected]>
  • Loading branch information
aulorbe and austin-denoble committed Jan 23, 2025
1 parent e2b1dfe commit 9143308
Show file tree
Hide file tree
Showing 107 changed files with 8,647 additions and 30 deletions.
25 changes: 20 additions & 5 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jobs:
runs-on: ubuntu-latest
outputs:
serverlessIndexName: ${{ steps.step3.outputs.SERVERLESS_INDEX_NAME }}
assistantName: ${{ steps.step3.outputs.ASSISTANT_NAME }}
testFile: ${{ steps.step3.outputs.TEST_FILE }}
steps:
- name: Checkout code
id: step1
Expand All @@ -23,8 +25,13 @@ jobs:
env:
PINECONE_API_KEY: ${{ secrets.PINECONE_API_KEY }}
run: |
SERVERLESS_INDEX_NAME=$(npx ts-node ./src/integration/setup.ts | grep "SERVERLESS_INDEX_NAME=" | cut -d'=' -f2)
SETUP_OUTPUT=$(npx ts-node ./src/integration/setup.ts)
SERVERLESS_INDEX_NAME=$(echo "$SETUP_OUTPUT" | grep "SERVERLESS_INDEX_NAME=" | cut -d'=' -f2)
ASSISTANT_NAME=$(echo "$SETUP_OUTPUT" | grep "ASSISTANT_NAME=" | cut -d'=' -f2)
TEST_FILE=$(echo "$SETUP_OUTPUT" | grep "TEST_FILE=" | cut -d'=' -f2)
echo "SERVERLESS_INDEX_NAME=$SERVERLESS_INDEX_NAME" >> $GITHUB_OUTPUT
echo "ASSISTANT_NAME=$ASSISTANT_NAME" >> $GITHUB_OUTPUT
echo "TEST_FILE=$TEST_FILE" >> $GITHUB_OUTPUT
unit-tests:
needs: setup
Expand All @@ -48,6 +55,8 @@ jobs:
runs-on: ubuntu-latest
outputs:
serverlessIndexName: ${{ steps.runTests1.outputs.SERVERLESS_INDEX_NAME }}
assistantName: ${{ steps.runTests1.outputs.ASSISTANT_NAME }}
testFile: ${{ steps.runTests1.outputs.TEST_FILE }}
strategy:
fail-fast: false
max-parallel: 2
Expand Down Expand Up @@ -87,9 +96,13 @@ jobs:
CI: true
PINECONE_API_KEY: ${{ secrets.PINECONE_API_KEY }}
SERVERLESS_INDEX_NAME: ${{ needs.setup.outputs.serverlessIndexName}}
ASSISTANT_NAME: ${{ needs.setup.outputs.assistantName}}
TEST_FILE: ${{ needs.setup.outputs.testFile}}
run: |
${{ matrix.config.runner }} run test:integration:${{ matrix.config.jest_env }}
echo "SERVERLESS_INDEX_NAME=${{ needs.setup.outputs.serverlessIndexName}}" >> $GITHUB_OUTPUT
echo "ASSISTANT_NAME=${{ needs.setup.outputs.assistantName}}" >> $GITHUB_OUTPUT
echo "TEST_FILE=${{ needs.setup.outputs.testFile}}" >> $GITHUB_OUTPUT
- name: Run integration tests (Staging)
if: matrix.pinecone_env == 'staging'
Expand All @@ -115,6 +128,8 @@ jobs:
env:
PINECONE_API_KEY: ${{ secrets.PINECONE_API_KEY }}
SERVERLESS_INDEX_NAME: ${{ needs.integration-tests.outputs.serverlessIndexName}}
ASSISTANT_NAME: ${{ needs.integration-tests.outputs.assistantName}}
TEST_FILE: ${{ needs.integration-tests.outputs.testFile}}
run: |
npx ts-node ./src/integration/teardown.ts
Expand All @@ -126,10 +141,6 @@ jobs:
matrix:
tsVersion:
[
'~4.1.0',
'~4.2.0',
'~4.3.0',
'~4.4.0',
'~4.5.0',
'~4.6.0',
'~4.7.0',
Expand All @@ -138,6 +149,10 @@ jobs:
'~5.0.0',
'~5.1.0',
'~5.2.0',
'~5.3.0',
'~5.4.0',
'~5.5.0',
'~5.6.0',
'latest',
]
steps:
Expand Down
286 changes: 286 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,292 @@ console.log(response);
//}
```

## Pinecone Assistant

The [Pinecone Assistant API](https://docs.pinecone.io/guides/assistant/understanding-assistant) enables you to create and manage AI assistants powered by Pinecone's vector database
capabilities. These Assistants can be customized with specific instructions and metadata, and can interact with
files and engage in chat conversations.

### Create an Assistant

[Creates a new Assistant](https://docs.pinecone.io/guides/assistant/create-assistant) with specified configurations. You can define the Assistant's name, provide instructions
that guide its behavior, and attach metadata for organization and tracking purposes.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();

const assistant = await pc.createAssistant({
name: 'product-assistant',
instructions: 'You are a helpful product recommendation assistant.',
metadata: {
team: 'product',
version: '1.0',
},
});
```

### Delete an Assistant

[Deletes an Assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#delete-an-assistant) by name.

**Note:** Deleting an Assistant also deletes all associated files.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
await pc.deleteAssistant('test1');
```

### Get information about an Assistant

[Retrieves information](https://docs.pinecone.io/guides/assistant/manage-assistants#get-the-status-of-an-assistant) about an Assistant by name.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const test = await pc.describeAssistant('test1');
console.log(test);
// {
// name: 'test10',
// instructions: undefined,
// metadata: undefined,
// status: 'Ready',
// host: 'https://prod-1-data.ke.pinecone.io',
// createdAt: 2025-01-08T22:24:50.525Z,
// updatedAt: 2025-01-08T22:24:52.303Z
// }
```

### Update an Assistant

[Updates an Assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#add-instructions-to-an-assistant) by name. You can update the Assistant's name, instructions, and/or metadata.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
await pc.updateAssistant({
name: 'test1',
instructions: 'some new instructions!',
});
// {
// assistantName: test1,
// instructions: 'some new instructions!',
// metadata: undefined
// }
```

### List Assistants

Retrieves a [list of all Assistants](https://docs.pinecone.io/guides/assistant/manage-assistants) in your account. This method returns details about each Assistant including their
names, instructions, metadata, status, and host.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();

const assistants = await pc.listAssistants();
console.log(assistants);
// {
// assistants: [{
// name: 'product-assistant',
// instructions: 'You are a helpful product recommendation assistant.',
// metadata: { team: 'product', version: '1.0' },
// status: 'Ready',
// host: 'product-assistant-abc123.svc.pinecone.io'
// }]
// }
```

### Chat with an Assistant

You can [chat with Assistants](https://docs.pinecone.io/guides/assistant/chat-with-assistant) using either the `chat` method or the `chatCompletion` method. The latter's output is
compatible with [OpenAI's Chat Completion](https://platform.openai.com/docs/api-reference/chat) format.

**Note:** Your Assistant must contain files in order for chat to work.

The following example shows how to chat with an Assistant using the `chat` method:

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
const chatResp = await assistant.chat({
messages: [{ role: 'user', content: 'What is the capital of France?' }],
});
console.log(chatResp);
// {
// id: '000000000000000023e7fb015be9d0ad',
// finishReason: 'stop',
// message: {
// role: 'assistant',
// content: 'The capital of France is Paris.'
// },
// model: 'gpt-4o-2024-05-13',
// citations: [ { position: 209, references: [Array] } ],
// usage: { promptTokens: 493, completionTokens: 38, totalTokens: 531 }
// }
```

### Inspect context snippets associated with a chat

Returns [context snippets associated with a given query and an Assistant's response](https://docs.pinecone.io/guides/assistant/understanding-context-snippets). This is useful for understanding
how the Assistant arrived at its answer(s).

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
const response = await assistant.context({
query: 'What is the capital of France?',
});
console.log(response);
// {
// snippets: [
// {
// type: 'text',
// content: 'The capital of France is Paris.',
// score: 0.9978925,
// reference: [Object]
// },
// ],
// usage: { promptTokens: 527, completionTokens: 0, totalTokens: 527 }
// }
```

### Add files to an Assistant

You can [add files to an Assistant](https://docs.pinecone.io/guides/assistant/manage-files#upload-a-local-file) to enable it to interact with files during chat conversations. The following
example shows how to upload a local `test-file.txt` file to an Assistant.

**Note:** You must upload at least 1 file in order to chat with an Assistant.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
await assistant.uploadFile({
path: 'test-file.txt',
metadata: { 'test-key': 'test-value' },
});
// {
// name: 'test-file.txt',
// id: '921ad74c-2421-413a-8c86-fca81ceabc5c',
// metadata: { 'test-key': 'test-value' },
// createdOn: 2025-01-06T19:14:21.969Z,
// updatedOn: 2025-01-06T19:14:21.969Z,
// status: 'Processing',
// percentDone: null,
// signedUrl: null,
// errorMessage: null
// }
```

### List all files in an Assistant

[Lists all files](https://docs.pinecone.io/guides/assistant/manage-files#list-files-in-an-assistant) that have been uploaded to an Assistant. Optionally, you can pass a filter to list only files that
meet certain criteria.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
const files = await assistant.listFiles({
filter: { metadata: { key: 'value' } },
});
console.log(files);
// {
// files: [
// {
// name: 'test-file.txt',
// id: '1a56ddd0-c6d8-4295-80c0-9bfd6f5cb87b',
// metadata: [Object],
// createdOn: 2025-01-06T19:14:21.969Z,
// updatedOn: 2025-01-06T19:14:36.925Z,
// status: 'Available',
// percentDone: 1,
// signedUrl: undefined,
// errorMessage: undefined
// }
// ]
// }
```

### Get the status of a file in an Assistant

[Retrieves information about a file](https://docs.pinecone.io/guides/assistant/manage-files#get-the-status-of-a-file) in an Assistant by ID.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
const files = await assistant.listFiles();
let fileId: string;
if (files.files) {
fileId = files.files[0].id;
} else {
fileId = '';
}
const resp = await assistant.describeFile({ fileId: fileId });
console.log(resp);
// {
// name: 'test-file.txt',
// id: '1a56ddd0-c6d8-4295-80c0-9bfd6f5cb87b',
// metadata: undefined,
// createdOn: 2025-01-06T19:14:21.969Z,
// updatedOn: 2025-01-06T19:14:36.925Z,
// status: 'Available',
// percentDone: 1,
// signedUrl: undefined,
// errorMessage: undefined
// }
```

### Delete a file from an Assistant

[Deletes a file(s)](https://docs.pinecone.io/guides/assistant/manage-files#delete-a-file) from an Assistant by ID.

**Note:** Deleting files is a PERMANENT operation. Deleted files _cannot_ be recovered.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const assistantName = 'test1';
const assistant = pc.Assistant(assistantName);
const files = await assistant.listFiles();
let fileId: string;
if (files.files) {
fileId = files.files[0].id;
await assistant.deleteFile({ fileId: fileId });
}
```

### Evaluate answers

You can also use the Assistant API to [evaluate the accuracy of a question-answer pair, given a known-true answer](https://docs.pinecone.io/guides/assistant/evaluate-answers).
The API will return a set of metrics (`correctness`, `completeness`, and `alignment`) that indicate how well the
Assistant performed.

```typescript
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
await pc.evaluate({
question: 'What is the capital of France?',
answer: "Lyon is France's capital city",
groundTruth: 'Paris is the capital city of France',
});
// {
// metrics: { correctness: 0, completeness: 0, alignment: 0 }, // 0s across the board indicates incorrect
// reasoning: { evaluatedFacts: [ [Object] ] },
// usage: { promptTokens: 1134, completionTokens: 21, totalTokens: 1155 }
// }
```

## Testing

All testing takes place automatically in CI and is configured using Github actions
Expand Down
2 changes: 1 addition & 1 deletion codegen/apis
Submodule apis updated from 3e5739 to 934bd7
2 changes: 1 addition & 1 deletion codegen/build-oas.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -eux -o pipefail

version=$1 # e.g. 2024-07
modules=("db_control" "db_data" "inference")
modules=("db_control" "db_data" "inference" "assistant_control" "assistant_data" "assistant_evaluation")

destination="src/pinecone-generated-ts-fetch"
build_dir="build"
Expand Down
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9143308

Please sign in to comment.