diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 9bb47dd1..54b133f1 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -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 @@ -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 @@ -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 @@ -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' @@ -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 @@ -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', @@ -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: diff --git a/README.md b/README.md index 5d516c18..3f4a0935 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/codegen/apis b/codegen/apis index 3e5739b4..934bd7bd 160000 --- a/codegen/apis +++ b/codegen/apis @@ -1 +1 @@ -Subproject commit 3e5739b49a9592a0a7da935e4247e377bd5d1e8a +Subproject commit 934bd7bdc3e0eb8d8ab9c96d058bc572648ea867 diff --git a/codegen/build-oas.sh b/codegen/build-oas.sh index e432f537..01b7ebac 100755 --- a/codegen/build-oas.sh +++ b/codegen/build-oas.sh @@ -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" diff --git a/package-lock.json b/package-lock.json index 546fb3b4..9a8d9c70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2146,9 +2146,10 @@ "peer": true }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", diff --git a/src/assistant/assistantHostSingleton.ts b/src/assistant/assistantHostSingleton.ts new file mode 100644 index 00000000..5134f039 --- /dev/null +++ b/src/assistant/assistantHostSingleton.ts @@ -0,0 +1,90 @@ +import type { PineconeConfiguration } from '../data'; +import { normalizeUrl } from '../utils'; +import { asstControlOperationsBuilder } from './control/asstControlOperationsBuilder'; +import { describeAssistant } from './control'; + +export const AssistantHostSingleton = (function () { + const hostUrls = {}; // map of apiKey-assistantName to hostUrl + + function ensureAssistantPath(url) { + if (!url.endsWith('/assistant')) { + // Append "/assistant" if it doesn't already end with it + url = url.endsWith('/') ? `${url}assistant` : `${url}/assistant`; + } + return url; + } + + const _describeAssistant = async ( + config: PineconeConfiguration, + assistantName: string + ): Promise => { + const assistantControlApi = asstControlOperationsBuilder(config); + const describeResponse = await describeAssistant(assistantControlApi)( + assistantName + ); + const host = describeResponse?.host; + + if (!host) { + // if the host is empty for some reason, default based on region + let defaultHost = 'https://prod-1-data.ke.pinecone.io'; + // If 'eu' is specified use that, otherwise default to 'us' + if ( + config.assistantRegion && + config.assistantRegion.toLowerCase() === 'eu' + ) { + defaultHost = 'https://prod-eu-data.ke.pinecone.io'; + } + return defaultHost; + } else { + return host; + } + }; + + const _key = (config: PineconeConfiguration, assistantName: string) => + `${config.apiKey}-${assistantName}`; + + // singleton object + const singleton = { + getHostUrl: async ( + config: PineconeConfiguration, + assistantName: string + ) => { + const cacheKey = _key(config, assistantName); + if (cacheKey in hostUrls) { + return hostUrls[cacheKey]; + } else { + const hostUrl = await _describeAssistant(config, assistantName); + hostUrls[cacheKey] = normalizeUrl(ensureAssistantPath(hostUrl)); + } + return hostUrls[cacheKey]; + }, + + _reset: () => { + for (const key of Object.keys(hostUrls)) { + delete hostUrls[key]; + } + }, + + _set: ( + config: PineconeConfiguration, + assistantName: string, + hostUrl: string + ) => { + const normalizedHostUrl = normalizeUrl(ensureAssistantPath(hostUrl)); + // prevent adding an empty hostUrl to the cache + if (!hostUrl || !normalizedHostUrl) { + return; + } + + const cacheKey = _key(config, assistantName); + hostUrls[cacheKey] = normalizedHostUrl; + }, + + _delete: (config: PineconeConfiguration, assistantName: string) => { + const cacheKey = _key(config, assistantName); + delete hostUrls[cacheKey]; + }, + }; + + return singleton; +})(); diff --git a/src/assistant/control/__tests__/assistantHostSingleton.test.ts b/src/assistant/control/__tests__/assistantHostSingleton.test.ts new file mode 100644 index 00000000..7222ab0a --- /dev/null +++ b/src/assistant/control/__tests__/assistantHostSingleton.test.ts @@ -0,0 +1,225 @@ +import { AssistantHostSingleton } from '../../assistantHostSingleton'; + +const mockDescribeAsst = jest.fn(); +const mockAsstOperationsBuilder = jest.fn(); + +jest.mock('../../control', () => { + const realControl = jest.requireActual('../../control'); + return { + ...realControl, + describeAssistant: () => mockDescribeAsst, + assistantOperationsBuilder: (config) => mockAsstOperationsBuilder(config), + }; +}); + +describe('AssistantHostSingleton', () => { + afterEach(() => { + AssistantHostSingleton._reset(); + mockDescribeAsst.mockReset(); + mockAsstOperationsBuilder.mockReset(); + }); + + test('returns default host URL when no region is set', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + const hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('returns correct host URL for US region', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + const hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('returns correct host URL for EU region', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-eu-data.ke.pinecone.io' + ); + const hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-eu-data.ke.pinecone.io/assistant'); + }); + + test('caches host URL per apiKey and assistantName combination', async () => { + const pineconeConfig1 = { apiKey: 'test-key-1' }; + const pineconeConfig2 = { apiKey: 'test-key-2' }; + + AssistantHostSingleton._set( + pineconeConfig1, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + AssistantHostSingleton._set( + pineconeConfig2, + 'assistant-1', + 'https://prod-eu-data.ke.pinecone.io' + ); + + const hostUrl1 = await AssistantHostSingleton.getHostUrl( + pineconeConfig1, + 'assistant-1' + ); + const hostUrl2 = await AssistantHostSingleton.getHostUrl( + pineconeConfig2, + 'assistant-1' + ); + + expect(hostUrl1).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + expect(hostUrl2).toEqual('https://prod-eu-data.ke.pinecone.io/assistant'); + }); + + test('_delete removes cached host URL', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + + let hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + + AssistantHostSingleton._delete(pineconeConfig, 'assistant-1'); + hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('_reset clears all cached host URLs', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-2', + 'https://prod-eu-data.ke.pinecone.io' + ); + + AssistantHostSingleton._reset(); + + const hostUrl1 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + const hostUrl2 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-2' + ); + + expect(hostUrl1).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + expect(hostUrl2).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('_set does not cache empty hostUrl values', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set(pineconeConfig, 'assistant-1', ''); + + const hostUrl = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + expect(hostUrl).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('returns same host URL instance for same apiKey and assistantName combination', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + + const hostUrl1 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + const hostUrl2 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + + expect(hostUrl1).toBe(hostUrl2); // Using .toBe() to check instance equality + expect(hostUrl1).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + }); + + test('creates different host URL instances for different apiKeys', async () => { + const pineconeConfig1 = { apiKey: 'test-key-1' }; + const pineconeConfig2 = { apiKey: 'test-key-2' }; + + AssistantHostSingleton._set( + pineconeConfig1, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + AssistantHostSingleton._set( + pineconeConfig2, + 'assistant-1', + 'https://prod-eu-data.ke.pinecone.io' + ); + + const hostUrl1 = await AssistantHostSingleton.getHostUrl( + pineconeConfig1, + 'assistant-1' + ); + const hostUrl2 = await AssistantHostSingleton.getHostUrl( + pineconeConfig2, + 'assistant-1' + ); + + expect(hostUrl1).not.toBe(hostUrl2); + expect(hostUrl1).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + expect(hostUrl2).toEqual('https://prod-eu-data.ke.pinecone.io/assistant'); + }); + + test('creates different host URL instances for different assistant names', async () => { + const pineconeConfig = { apiKey: 'test-key' }; + + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-1', + 'https://prod-1-data.ke.pinecone.io' + ); + AssistantHostSingleton._set( + pineconeConfig, + 'assistant-2', + 'https://prod-eu-data.ke.pinecone.io' + ); + + const hostUrl1 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-1' + ); + const hostUrl2 = await AssistantHostSingleton.getHostUrl( + pineconeConfig, + 'assistant-2' + ); + + expect(hostUrl1).not.toBe(hostUrl2); + expect(hostUrl1).toEqual('https://prod-1-data.ke.pinecone.io/assistant'); + expect(hostUrl2).toEqual('https://prod-eu-data.ke.pinecone.io/assistant'); + }); +}); diff --git a/src/assistant/control/__tests__/createAssistant.test.ts b/src/assistant/control/__tests__/createAssistant.test.ts new file mode 100644 index 00000000..c8d7a296 --- /dev/null +++ b/src/assistant/control/__tests__/createAssistant.test.ts @@ -0,0 +1,67 @@ +import { + Assistant, + CreateAssistantOperationRequest, + ManageAssistantsApi, +} from '../../../pinecone-generated-ts-fetch/assistant_control'; +import { createAssistant } from '../createAssistant'; + +const setupManageAssistantsApi = () => { + const fakeCreateAssistant: ( + req: CreateAssistantOperationRequest + ) => Promise = jest + .fn() + .mockImplementation(() => Promise.resolve({})); + + const MAP = { + createAssistant: fakeCreateAssistant, + } as ManageAssistantsApi; + return MAP; +}; + +describe('AssistantCtrlPlane', () => { + let manageAssistantsApi: ManageAssistantsApi; + + beforeEach(() => { + manageAssistantsApi = setupManageAssistantsApi(); + }); + + describe('createAssistant', () => { + test('throws error when invalid region is provided', async () => { + const invalidRequest = { + name: 'test-assistant', + region: 'invalid-region', + sourceCollection: 'test-collection', + model: 'test-model', + }; + + await expect( + createAssistant(manageAssistantsApi)(invalidRequest) + ).rejects.toThrow( + 'Invalid region specified. Must be one of "us" or "eu"' + ); + }); + + test('accepts valid regions in different cases', async () => { + const validRequests = [ + { + name: 'test-assistant-1', + region: 'US', + sourceCollection: 'test-collection', + model: 'test-model', + }, + { + name: 'test-assistant-2', + region: 'eu', + sourceCollection: 'test-collection', + model: 'test-model', + }, + ]; + + for (const request of validRequests) { + await expect( + createAssistant(manageAssistantsApi)(request) + ).resolves.not.toThrow(); + } + }); + }); +}); diff --git a/src/assistant/control/__tests__/evaluate.test.ts b/src/assistant/control/__tests__/evaluate.test.ts new file mode 100644 index 00000000..40fb7d87 --- /dev/null +++ b/src/assistant/control/__tests__/evaluate.test.ts @@ -0,0 +1,55 @@ +import { evaluate } from '../evaluate'; +import { + AlignmentResponse, + MetricsAlignmentRequest, + MetricsApi, +} from '../../../pinecone-generated-ts-fetch/assistant_evaluation'; + +const setupMetricsApi = () => { + const fakeMetricsAlignment: ( + req: MetricsAlignmentRequest + ) => Promise = jest + .fn() + .mockImplementation(() => Promise.resolve({})); + + const MAP = { + metricsAlignment: fakeMetricsAlignment, + } as MetricsApi; + return MAP; +}; + +describe('AssistantCtrlPlane', () => { + let metricsApi: MetricsApi; + + beforeAll(() => { + metricsApi = setupMetricsApi(); + }); + + describe('evaluate', () => { + test('throws error when empty strings are provided', async () => { + const emptyRequests = [ + { + question: '', + answer: 'test-answer', + groundTruth: 'test-ground-truth', + }, + { + question: 'test-question', + answer: '', + groundTruth: 'test-ground-truth', + }, + { + question: 'test-question', + answer: 'test-answer', + groundTruth: '', + }, + ]; + + for (const request of emptyRequests) { + await expect(evaluate(metricsApi)(request)).rejects.toThrow( + 'Invalid input. Question, answer, and groundTruth must be non-empty strings.' + ); + } + }); + }); +}); diff --git a/src/assistant/control/asstControlOperationsBuilder.ts b/src/assistant/control/asstControlOperationsBuilder.ts new file mode 100644 index 00000000..7f21e99f --- /dev/null +++ b/src/assistant/control/asstControlOperationsBuilder.ts @@ -0,0 +1,37 @@ +import type { PineconeConfiguration } from '../../data'; +import { + ManageAssistantsApi as ManageAssistantsControlApi, + Configuration, + type ConfigurationParameters as AssistantOperationsApiConfigurationParameters, + X_PINECONE_API_VERSION, +} from '../../pinecone-generated-ts-fetch/assistant_control'; +import { + buildUserAgent, + getFetch, + normalizeUrl, + queryParamsStringify, +} from '../../utils'; +import { middleware } from '../../utils/middleware'; + +export const asstControlOperationsBuilder = ( + config: PineconeConfiguration +): ManageAssistantsControlApi => { + const { apiKey } = config; + const controllerPath = + normalizeUrl(config.controllerHostUrl) || + 'https://api.pinecone.io/assistant'; + const headers = config.additionalHeaders || null; + const apiConfig: AssistantOperationsApiConfigurationParameters = { + basePath: controllerPath, + apiKey, + queryParamsStringify, + headers: { + 'User-Agent': buildUserAgent(config), + 'X-Pinecone-Api-Version': X_PINECONE_API_VERSION, + ...headers, + }, + fetchApi: getFetch(config), + middleware, + }; + return new ManageAssistantsControlApi(new Configuration(apiConfig)); +}; diff --git a/src/assistant/control/asstMetricsOperationsBuilder.ts b/src/assistant/control/asstMetricsOperationsBuilder.ts new file mode 100644 index 00000000..f58f13b8 --- /dev/null +++ b/src/assistant/control/asstMetricsOperationsBuilder.ts @@ -0,0 +1,36 @@ +import type { PineconeConfiguration } from '../../data'; +import { + Configuration, + type ConfigurationParameters as AssistantOperationsApiConfigurationParameters, + X_PINECONE_API_VERSION, + MetricsApi, +} from '../../pinecone-generated-ts-fetch/assistant_evaluation'; +import { buildUserAgent, getFetch, queryParamsStringify } from '../../utils'; +import { middleware } from '../../utils/middleware'; + +export const asstMetricsOperationsBuilder = ( + config: PineconeConfiguration +): MetricsApi => { + const { apiKey } = config; + let hostUrl = 'https://prod-1-data.ke.pinecone.io/assistant'; + // If 'eu' is specified use that, otherwise default to 'us' + if (config.assistantRegion && config.assistantRegion.toLowerCase() === 'eu') { + hostUrl = 'https://prod-eu-data.ke.pinecone.io/assistant'; + } + + const headers = config.additionalHeaders || null; + const apiConfig: AssistantOperationsApiConfigurationParameters = { + basePath: hostUrl, + apiKey, + queryParamsStringify, + headers: { + 'User-Agent': buildUserAgent(config), + 'X-Pinecone-Api-Version': X_PINECONE_API_VERSION, + ...headers, + }, + fetchApi: getFetch(config), + middleware, + }; + + return new MetricsApi(new Configuration(apiConfig)); +}; diff --git a/src/assistant/control/createAssistant.ts b/src/assistant/control/createAssistant.ts new file mode 100644 index 00000000..17fa34fe --- /dev/null +++ b/src/assistant/control/createAssistant.ts @@ -0,0 +1,60 @@ +import { + type Assistant, + CreateAssistantRequestRegionEnum, + ManageAssistantsApi as ManageAssistantsControlApi, +} from '../../pinecone-generated-ts-fetch/assistant_control'; + +/** + * The `CreateAssistantOptions` interface describes the name and optional configurations that can be + * passed when creating an Assistant. + */ +export interface CreateAssistantOptions { + /** + * The name of the assistant. Resource name must be 1-45 characters long, start and end with an alphanumeric character, and consist only of lower case alphanumeric characters or '-'. + */ + name: string; + /** + * Optional instructions for the Assistant. Instructions can [customize tone, role, and focus]https://www.pinecone.io/learn/assistant-api-deep-dive/#Custom-Instructions. + */ + instructions?: string; + /** + * Optional metadata for the Assistant. + */ + metadata?: Record; + /** + * Optional region for the Assistant. The region is where the Assistant is deployed. The default region is 'us'. + * The other option is 'eu'. + */ + region?: string; +} + +export const createAssistant = (api: ManageAssistantsControlApi) => { + return async (options: CreateAssistantOptions): Promise => { + if (options.region) { + let region: CreateAssistantRequestRegionEnum = + CreateAssistantRequestRegionEnum.Us; + if ( + !Object.values(CreateAssistantRequestRegionEnum) + .toString() + .includes(options.region.toLowerCase()) + ) { + throw new Error( + 'Invalid region specified. Must be one of "us" or "eu"' + ); + } else { + region = + options.region.toLowerCase() as CreateAssistantRequestRegionEnum; + } + options.region = region; + } + + return await api.createAssistant({ + createAssistantRequest: { + name: options.name, + instructions: options?.instructions, + metadata: options?.metadata, + region: options?.region as CreateAssistantRequestRegionEnum, + }, + }); + }; +}; diff --git a/src/assistant/control/deleteAssistant.ts b/src/assistant/control/deleteAssistant.ts new file mode 100644 index 00000000..11e9759d --- /dev/null +++ b/src/assistant/control/deleteAssistant.ts @@ -0,0 +1,9 @@ +import { ManageAssistantsApi as ManageAssistantsControlApi } from '../../pinecone-generated-ts-fetch/assistant_control'; + +export const deleteAssistant = (api: ManageAssistantsControlApi) => { + return async (assistantName: string): Promise => { + return await api.deleteAssistant({ + assistantName: assistantName, + }); + }; +}; diff --git a/src/assistant/control/describeAssistant.ts b/src/assistant/control/describeAssistant.ts new file mode 100644 index 00000000..d015c3e3 --- /dev/null +++ b/src/assistant/control/describeAssistant.ts @@ -0,0 +1,12 @@ +import { + type Assistant, + ManageAssistantsApi as ManageAssistantsControlApi, +} from '../../pinecone-generated-ts-fetch/assistant_control'; + +export const describeAssistant = (api: ManageAssistantsControlApi) => { + return async (assistantName: string): Promise => { + return await api.getAssistant({ + assistantName: assistantName, + }); + }; +}; diff --git a/src/assistant/control/evaluate.ts b/src/assistant/control/evaluate.ts new file mode 100644 index 00000000..d07baf16 --- /dev/null +++ b/src/assistant/control/evaluate.ts @@ -0,0 +1,45 @@ +import { + MetricsApi, + MetricsAlignmentRequest, +} from '../../pinecone-generated-ts-fetch/assistant_evaluation'; + +/** + * The `AssistantEval` interface defines the structure of the input object for the `evaluate` method. + */ +export interface AssistantEval { + /** + * The question to evaluate. + */ + question: string; + /** + * The answer to evaluate. + */ + answer: string; + /** + * The ground truth answer against which evaluate the question-answer pair. + */ + groundTruth: string; +} + +export const evaluate = (metricsApi: MetricsApi) => { + return async (options: AssistantEval) => { + if ( + options.question == '' || + options.answer == '' || + options.groundTruth == '' + ) { + throw new Error( + 'Invalid input. Question, answer, and groundTruth must be non-empty strings.' + ); + } + + const request = { + alignmentRequest: { + question: options.question, + answer: options.answer, + groundTruthAnswer: options.groundTruth, + }, + } as MetricsAlignmentRequest; + return metricsApi.metricsAlignment(request); + }; +}; diff --git a/src/assistant/control/index.ts b/src/assistant/control/index.ts new file mode 100644 index 00000000..5572096a --- /dev/null +++ b/src/assistant/control/index.ts @@ -0,0 +1,8 @@ +export { createAssistant, CreateAssistantOptions } from './createAssistant'; +export { deleteAssistant } from './deleteAssistant'; +export { describeAssistant } from './describeAssistant'; +export { listAssistants } from './listAssistants'; +export { updateAssistant, UpdateAssistantOptions } from './updateAssistant'; +export { evaluate, AssistantEval } from './evaluate'; +export { asstControlOperationsBuilder } from './asstControlOperationsBuilder'; +export { asstMetricsOperationsBuilder } from './asstMetricsOperationsBuilder'; diff --git a/src/assistant/control/listAssistants.ts b/src/assistant/control/listAssistants.ts new file mode 100644 index 00000000..1883891e --- /dev/null +++ b/src/assistant/control/listAssistants.ts @@ -0,0 +1,10 @@ +import { + type ListAssistants200Response, + ManageAssistantsApi as ManageAssistantsControlApi, +} from '../../pinecone-generated-ts-fetch/assistant_control'; + +export const listAssistants = (api: ManageAssistantsControlApi) => { + return async (): Promise => { + return await api.listAssistants(); + }; +}; diff --git a/src/assistant/control/updateAssistant.ts b/src/assistant/control/updateAssistant.ts new file mode 100644 index 00000000..eb232929 --- /dev/null +++ b/src/assistant/control/updateAssistant.ts @@ -0,0 +1,43 @@ +import { + ManageAssistantsApi as ManageAssistantsControlApi, + type UpdateAssistant200Response, +} from '../../pinecone-generated-ts-fetch/assistant_control'; + +/** + * Options for updating an assistant's properties. + */ +export interface UpdateAssistantOptions { + /** + * The name of the assistant to be updated. + */ + assistantName: string; + + /** + * Optional instructions for the assistant to apply to all responses. + */ + instructions?: string; + + /** + * Optional metadata associated with the assistant. + */ + metadata?: Record; +} + +export const updateAssistant = (api: ManageAssistantsControlApi) => { + return async ( + options: UpdateAssistantOptions + ): Promise => { + const resp = await api.updateAssistant({ + assistantName: options.assistantName, + updateAssistantRequest: { + instructions: options?.instructions, + metadata: options?.metadata, + }, + }); + return { + name: options.assistantName, + instructions: resp.instructions, + metadata: resp.metadata, + } as UpdateAssistant200Response; + }; +}; diff --git a/src/assistant/data/__tests__/chatAndChatCompletion.test.ts b/src/assistant/data/__tests__/chatAndChatCompletion.test.ts new file mode 100644 index 00000000..77b4180a --- /dev/null +++ b/src/assistant/data/__tests__/chatAndChatCompletion.test.ts @@ -0,0 +1,110 @@ +import { + messagesValidation, + modelValidation, + ChatRequest, + chat, +} from '../chat'; +import { chatCompletion, ChatCompletionRequest } from '../chatCompletion'; +import { + ChatModelEnum, + ManageAssistantsApi, +} from '../../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from '../asstDataOperationsProvider'; + +const endpoints = ['chat', 'chatCompletion']; + +endpoints.forEach((endpoint) => { + describe(`${endpoint} validation`, () => { + describe('messagesValidation', () => { + test('converts string array to MessageModel array', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: ['Hello', 'How are you?'], + }; + + const expected = [ + { role: 'user', content: 'Hello' }, + { role: 'user', content: 'How are you?' }, + ]; + + expect(messagesValidation(input)).toEqual(expected); + }); + + test('validates message objects with role and content', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: [ + { role: 'user', content: 'Hello' }, + { role: 'assistant', content: 'Hi there!' }, + ], + }; + + expect(messagesValidation(input)).toEqual(input.messages); + }); + + test('throws error when role is missing', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: [{ content: 'Hello' }], + }; + + expect(() => messagesValidation(input)).toThrow( + 'Message object must have exactly two keys: "role" and "content"' + ); + }); + + test('throws error when object has invalid keys', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: [{ role: 'user', content: 'Hello', extra: 'field' }], + }; + + expect(() => messagesValidation(input)).toThrow('exactly two keys'); + }); + }); + + describe('modelValidation', () => { + test('returns default GPT-4 model when no model specified', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: ['Hello'], + }; + + expect(modelValidation(input)).toBe(ChatModelEnum.Gpt4o); + }); + + test('validates correct model string', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: ['Hello'], + model: 'gpt-4o', + }; + + expect(modelValidation(input)).toBe(ChatModelEnum.Gpt4o); + }); + + test('throws error for invalid model', () => { + const input: ChatRequest | ChatCompletionRequest = { + messages: ['Hello'], + model: 'invalid-model', + }; + + expect(() => modelValidation(input)).toThrow('Invalid model specified'); + }); + + test('throws error when no messages provided', async () => { + const AsstDataOperationsProvider = { + provideData: async () => new ManageAssistantsApi(), + } as AsstDataOperationsProvider; + + const chatFn = chat('test-assistant', AsstDataOperationsProvider); + const chatCompletionFn = chatCompletion( + 'test-assistant', + AsstDataOperationsProvider + ); + + const input = {} as ChatRequest; + const inputCompletion = {} as ChatCompletionRequest; + + await expect(chatFn(input)).rejects.toThrow('No messages passed'); + await expect(chatCompletionFn(inputCompletion)).rejects.toThrow( + 'No messages passed' + ); + }); + }); + }); +}); diff --git a/src/assistant/data/__tests__/context.test.ts b/src/assistant/data/__tests__/context.test.ts new file mode 100644 index 00000000..5465a029 --- /dev/null +++ b/src/assistant/data/__tests__/context.test.ts @@ -0,0 +1,81 @@ +import { context } from '../context'; +import { + ContextAssistantRequest, + ContextModel, + ManageAssistantsApi, +} from '../../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from '../asstDataOperationsProvider'; + +const setupApiProvider = () => { + const fakeContextAssistant: ( + req: ContextAssistantRequest + ) => Promise = jest + .fn() + .mockImplementation(() => Promise.resolve({})); + + const MAP = { + contextAssistant: fakeContextAssistant, + } as ManageAssistantsApi; + const AsstDataOperationsProvider = { + provideData: async () => MAP, + } as AsstDataOperationsProvider; + return { MAP, AsstDataOperationsProvider }; +}; + +describe('contextClosed', () => { + let mockApi: ManageAssistantsApi; + let asstOperationsProvider: AsstDataOperationsProvider; + + beforeEach(() => { + const { MAP, AsstDataOperationsProvider } = setupApiProvider(); + mockApi = MAP; + asstOperationsProvider = AsstDataOperationsProvider; + }); + + test('creates a context function that calls the API with correct parameters', async () => { + const assistantName = 'test-assistant'; + const contextFn = context(assistantName, asstOperationsProvider); + + const options = { + query: 'test query', + filter: { key: 'value' }, + }; + + await contextFn(options); + + expect(mockApi.contextAssistant).toHaveBeenCalledWith({ + assistantName, + contextRequest: { + query: options.query, + filter: options.filter, + }, + }); + }); + + test('throws error when query is empty', async () => { + const contextFn = context('test-assistant', asstOperationsProvider); + + await expect(contextFn({ query: '' })).rejects.toThrow( + 'Must provide a query' + ); + }); + + test('works without filter parameter', async () => { + const assistantName = 'test-assistant'; + const contextFn = context(assistantName, asstOperationsProvider); + + const options = { + query: 'test query', + }; + + await contextFn(options); + + expect(mockApi.contextAssistant).toHaveBeenCalledWith({ + assistantName, + contextRequest: { + query: options.query, + filter: undefined, + }, + }); + }); +}); diff --git a/src/assistant/data/__tests__/uploadFile.test.ts b/src/assistant/data/__tests__/uploadFile.test.ts new file mode 100644 index 00000000..85bd8d5b --- /dev/null +++ b/src/assistant/data/__tests__/uploadFile.test.ts @@ -0,0 +1,125 @@ +import { uploadFile } from '../uploadFile'; +import fs from 'fs'; +import path from 'path'; +import { AsstDataOperationsProvider } from '../asstDataOperationsProvider'; + +const mockFetch = jest.fn(); +jest.mock('fs'); +jest.mock('path'); +jest.mock('../../../utils', () => ({ + getFetch: () => mockFetch, + buildUserAgent: () => 'TestUserAgent', +})); + +const buildMockFetchResponse = ( + isSuccess: boolean, + status: number, + body: string +) => + mockFetch.mockResolvedValue({ + ok: isSuccess ? true : false, + status: status, + json: async () => JSON.parse(body), + }); + +describe('uploadFileInternal', () => { + const mockConfig = { + apiKey: 'test-api-key', + additionalHeaders: { + 'Custom-Header': 'test', + }, + }; + const mockAssistantName = 'test-assistant'; + const mockFileContent = Buffer.from('test file content'); + const mockResponse = { + data: { + name: 'test.txt', + id: 'test-id', + createdOn: new Date().toISOString(), + updatedOn: new Date().toISOString(), + status: 'ready', + }, + }; + const mockApiProvider = { + provideHostUrl: async () => 'https://prod-1-data.ke.pinecone.io/assistant', + } as AsstDataOperationsProvider; + + beforeEach(() => { + jest.clearAllMocks(); + jest.resetAllMocks(); + (fs.readFileSync as jest.Mock).mockResolvedValue(mockFileContent); + (path.basename as jest.Mock).mockReturnValue('test.txt'); + }); + + test('throws error when file path is not provided', async () => { + const upload = uploadFile(mockAssistantName, mockApiProvider, mockConfig); + await expect(upload({ path: '' })).rejects.toThrow('File path is required'); + }); + + test('correctly builds URL without metadata', async () => { + buildMockFetchResponse(true, 200, JSON.stringify(mockResponse)); + const upload = uploadFile(mockAssistantName, mockApiProvider, mockConfig); + await upload({ path: 'test.txt' }); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://prod-1-data.ke.pinecone.io/assistant/files/test-assistant', + expect.objectContaining({ + method: 'POST', + body: expect.any(FormData), + headers: expect.objectContaining({ + 'Api-Key': 'test-api-key', + 'User-Agent': 'TestUserAgent', + 'X-Pinecone-Api-Version': expect.any(String), + }), + }) + ); + }); + + test('correctly builds URL with metadata', async () => { + buildMockFetchResponse(true, 200, JSON.stringify(mockResponse)); + const metadata = { key: 'value' }; + const upload = uploadFile(mockAssistantName, mockApiProvider, mockConfig); + await upload({ path: 'test.txt', metadata }); + + const encodedMetadata = encodeURIComponent(JSON.stringify(metadata)); + expect(mockFetch).toHaveBeenCalledWith( + `https://prod-1-data.ke.pinecone.io/assistant/files/test-assistant?metadata=${encodedMetadata}`, + expect.objectContaining({ + method: 'POST', + body: expect.any(FormData), + headers: expect.objectContaining({ + 'Api-Key': 'test-api-key', + 'User-Agent': 'TestUserAgent', + 'X-Pinecone-Api-Version': expect.any(String), + }), + }) + ); + }); + + test('includes correct headers in request', async () => { + buildMockFetchResponse(true, 200, JSON.stringify(mockResponse)); + const upload = uploadFile(mockAssistantName, mockApiProvider, mockConfig); + await upload({ path: 'test.txt' }); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://prod-1-data.ke.pinecone.io/assistant/files/test-assistant', + expect.objectContaining({ + method: 'POST', + body: expect.any(FormData), + headers: expect.objectContaining({ + 'Api-Key': 'test-api-key', + 'User-Agent': 'TestUserAgent', + 'X-Pinecone-Api-Version': expect.any(String), + }), + }) + ); + }); + + test('creates form data with file stream', async () => { + buildMockFetchResponse(true, 200, JSON.stringify(mockResponse)); + const upload = uploadFile(mockAssistantName, mockApiProvider, mockConfig); + await upload({ path: 'test.txt' }); + + expect(fs.readFileSync).toHaveBeenCalledWith('test.txt'); + }); +}); diff --git a/src/assistant/data/assistant.ts b/src/assistant/data/assistant.ts new file mode 100644 index 00000000..fd41b356 --- /dev/null +++ b/src/assistant/data/assistant.ts @@ -0,0 +1,291 @@ +import { chat, ChatRequest } from './chat'; +import { ListFiles, listFiles } from './listFiles'; +import { DescribeFile, describeFile } from './describeFile'; +import { DeleteFile, deleteFile } from './deleteFile'; +// import { UploadFile, uploadFileClosed } from './uploadFile'; +import { UploadFile } from './uploadFile'; +import { uploadFile } from './uploadFile'; +import { PineconeConfiguration } from '../../data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; +import { Context, context } from './context'; +import { chatCompletion, ChatCompletionRequest } from './chatCompletion'; + +/** + * The `Assistant` class holds the data plane methods for interacting with + * [Assistants](https://docs.pinecone.io/guides/assistant/understanding-assistant). + * + * This class's methods are instantiated on a Pinecone object and are used to interact with the data plane of an Assistant. + * + * @example + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * const pc = new Pinecone(); + * const assistant = pc.Assistant('assistant-name'); + * ``` + */ +export class Assistant { + private config: PineconeConfiguration; + + readonly _chat: ReturnType; + readonly _chatCompletions: ReturnType; + readonly _listFiles: ReturnType; + readonly _describeFile: ReturnType; + readonly _uploadFile: ReturnType; + readonly _deleteFile: ReturnType; + readonly _context: ReturnType; + + assistantName: string; + + /** + * Creates an instance of the `Assistant` class. + * + * @param assistantName - The name of the Assistant. + * @param config - The Pinecone configuration object containing an API key and other configuration parameters + * needed for API calls. + * + * @throws An error if no assistant name is provided. + */ + constructor(assistantName: string, config: PineconeConfiguration) { + if (!assistantName) { + throw new Error('No assistant name provided'); + } + this.config = config; + const asstDataOperationsProvider = new AsstDataOperationsProvider( + this.config, + assistantName + ); + this.assistantName = assistantName; + + this._chat = chat(this.assistantName, asstDataOperationsProvider); + this._chatCompletions = chatCompletion( + this.assistantName, + asstDataOperationsProvider + ); + this._listFiles = listFiles(this.assistantName, asstDataOperationsProvider); + this._describeFile = describeFile( + this.assistantName, + asstDataOperationsProvider + ); + this._uploadFile = uploadFile( + this.assistantName, + asstDataOperationsProvider, + this.config + ); + this._deleteFile = deleteFile( + this.assistantName, + asstDataOperationsProvider + ); + this._context = context(this.assistantName, asstDataOperationsProvider); + } + + // --------- Chat methods --------- + + /** + * Sends a message to the Assistant and receives a response. Retries the request if the server fails. + * + * @example + * ```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?"}]}); + * // { + * // 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 } + * } + * ``` + * + * @param options - A {@link ChatRequest} object containing the message and optional parameters to send to the + * Assistant. + * @returns A promise that resolves to a {@link ChatModel} object containing the response from the Assistant. + */ + chat(options: ChatRequest) { + return this._chat(options); + } + + /** + * Sends a message to the Assistant and receives a response. Response is compatible with + * [OpenAI's Chat Completion API](https://platform.openai.com/docs/guides/text-generation. Retries the request if the server fails. + * + * See {@link chat} for example usage. + * + * @param options - A {@link ChatCompletionRequest} object containing the message and optional parameters to send + * to an Assistant. + */ + chatCompletions(options: ChatCompletionRequest) { + return this._chatCompletions(options); + } + + // --------- File methods --------- + + /** + * Lists files (with optional filter) uploaded to an Assistant. + * + * @example + * ```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: 'temp-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 + * // } + * // ] + * // } + * ``` + * + * @param options - A {@link ListFiles} object containing optional parameters to filter the list of files. + * @returns A promise that resolves to a {@link ListFiles200Response} object containing a list of files. + */ + listFiles(options?: ListFiles) { + if (!options) { + options = {}; + } + return this._listFiles(options); + } + + /** + * Describes a file uploaded to an Assistant. + * + * @example + * ```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 + * // } + * ``` + * + * @param options - A {@link DescribeFile} object containing the file ID to describe. + * @returns A promise that resolves to a {@link AssistantFileModel} object containing the file details. + */ + describeFile(options: DescribeFile) { + return this._describeFile(options); + } + + /** + * Uploads a file to an Assistant. + * + * Note: This method does *not* use the generated code from the OpenAPI spec. + * + * @example + * ```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: Invalid Date, // Note: these dates resolve in seconds + * // updatedOn: Invalid Date, + * // status: 'Processing', + * // percentDone: null, + * // signedUrl: null, + * // errorMessage: null + * // } + * ``` + * + * @param options - A {@link UploadFile} object containing the file path and optional metadata. + * @returns A promise that resolves to a {@link AssistantFileModel} object containing the file details. + */ + uploadFile(options: UploadFile) { + return this._uploadFile(options); + } + + /** + * Deletes a file uploaded to an Assistant by ID. + * + * @example + * ```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}); + * } + * ``` + * + * @param options - A {@link DeleteFile} object containing the file ID to delete. + */ + deleteFile(options: DeleteFile) { + return this._deleteFile(options); + } + + /** + * Retrieves [the context snippets](https://docs.pinecone.io/guides/assistant/understanding-context-snippets) used + * by an Assistant during the retrieval process. + * + * @example + * ```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 } + * // } + * ``` + * + * @param options + * @returns A promise that resolves to a {@link Context} object containing the context snippets. + */ + context(options: Context) { + return this._context(options); + } +} diff --git a/src/assistant/data/asstDataOperationsProvider.ts b/src/assistant/data/asstDataOperationsProvider.ts new file mode 100644 index 00000000..e58fe82c --- /dev/null +++ b/src/assistant/data/asstDataOperationsProvider.ts @@ -0,0 +1,81 @@ +import type { PineconeConfiguration } from '../../data'; +import { + ManageAssistantsApi as ManageAssistantsDataApi, + Configuration as DataConfiguration, + type ConfigurationParameters as AssistantOperationsApiConfigurationParameters, + X_PINECONE_API_VERSION, + HTTPHeaders, +} from '../../pinecone-generated-ts-fetch/assistant_data'; +import { + buildUserAgent, + getFetch, + queryParamsStringify, + normalizeUrl, +} from '../../utils'; +import { middleware } from '../../utils/middleware'; +import { AssistantHostSingleton } from '../assistantHostSingleton'; + +export class AsstDataOperationsProvider { + private readonly config: PineconeConfiguration; + private readonly asstName: string; + private asstHostUrl?: string; + private asstDataOperations?: ManageAssistantsDataApi; + private additionalHeaders?: HTTPHeaders; + + constructor( + config: PineconeConfiguration, + asstName: string, + asstHostUrl?: string, + additionalHeaders?: HTTPHeaders + ) { + this.config = config; + this.asstName = asstName; + this.asstHostUrl = normalizeUrl(asstHostUrl); + this.additionalHeaders = additionalHeaders; + } + + async provideData() { + if (this.asstDataOperations) { + return this.asstDataOperations; + } else { + this.asstHostUrl = await AssistantHostSingleton.getHostUrl( + this.config, + this.asstName + ); + this.asstDataOperations = this.buildAsstDataOperationsConfig(); + } + return this.asstDataOperations; + } + + async provideHostUrl() { + if (this.asstHostUrl) { + return this.asstHostUrl; + } else { + this.asstHostUrl = await AssistantHostSingleton.getHostUrl( + this.config, + this.asstName + ); + } + return this.asstHostUrl; + } + + buildAsstDataOperationsConfig() { + const { apiKey } = this.config; + const hostUrl = this.asstHostUrl; + const headers = this.additionalHeaders || null; + const apiConfig: AssistantOperationsApiConfigurationParameters = { + basePath: hostUrl, + apiKey, + queryParamsStringify, + headers: { + 'User-Agent': buildUserAgent(this.config), + 'X-Pinecone-Api-Version': X_PINECONE_API_VERSION, + ...headers, + }, + fetchApi: getFetch(this.config), + middleware, + }; + + return new ManageAssistantsDataApi(new DataConfiguration(apiConfig)); + } +} diff --git a/src/assistant/data/chat.ts b/src/assistant/data/chat.ts new file mode 100644 index 00000000..7dc23e59 --- /dev/null +++ b/src/assistant/data/chat.ts @@ -0,0 +1,165 @@ +import { + ChatAssistantRequest, + type ChatModel, + ChatModelEnum, + MessageModel, +} from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; +import { RetryOnServerFailure } from '../../utils'; + +/** + * Validates the messages passed to the Assistant. + * + * @param options - A {@link ChatRequest} object containing the messages to send to the Assistant. + * @throws An Error `role` key is not one of `user` or `assistant`. + * @throws An Error if the message object does not have exactly two keys: `role` and `content`. + * @returns An array of {@link MessageModel} objects containing the messages to send to the Assistant. + */ +export const messagesValidation = (options: ChatRequest): MessageModel[] => { + let messages: MessageModel[] = []; + + // If messages are passed as a list of strings: + if (options.messages && typeof options.messages[0] == 'string') { + // role defaults to user if not specified + messages = options.messages.map((message) => { + return { role: 'user', content: message }; + }); + } + // If messages are passed as a list of objects: + if ( + Array.isArray(options.messages) && + typeof options.messages[0] === 'object' + ) { + if (options.messages[0]['role']) { + if ( + options.messages[0]['role'].toLowerCase() !== 'user' && + options.messages[0]['role'].toLowerCase() !== 'assistant' + ) { + throw new Error( + 'No role specified in message object. Must be one of "user" or "assistant"' + ); + } + } + + // Extract unique keys from all messages + const keys: string[] = Array.from( + new Set(options.messages.flatMap((message) => Object.keys(message))) + ); + + if (keys.length !== 2) { + throw new Error( + 'Message object must have exactly two keys: "role" and "content"' + ); + } + + // Cast messages after validating keys + return (messages = options.messages as MessageModel[]); + } + + return messages; +}; + +/** + * Validates the model passed to the Assistant. + * + * @param options - A {@link ChatRequest} object containing the model to use for the Assistant. + * @throws An Error if the model is not one of the available models as outlined in {@link ChatModelEnum}. + */ +export const modelValidation = (options: ChatRequest) => { + // Make sure passed string for 'model' matches one of the Enum values; default to Gpt4o + let model: ChatModelEnum = ChatModelEnum.Gpt4o; + if (options.model) { + if (!Object.values(ChatModelEnum).toString().includes(options.model)) { + throw new Error( + 'Invalid model specified. Must be one of "gpt-4o" or "claude-3-5-sonnet"' + ); + } else { + model = options.model as ChatModelEnum; + } + } + return model; +}; + +/** + * The `ChatRequest` interface describes the request format for sending a message to an Assistant. + */ +export interface ChatRequest { + /** + * The messages to send to the Assistant. Can be a list of strings or a list of objects. If sent as a list of + * objects, must have exactly two keys: `role` and `content`. The `role` key can only be one of `user` or `assistant`. + */ + messages: string[] | Array<{ [key: string]: string }>; + /** + * If false, the Assistant will return a single JSON response. If true, the Assistant will return a stream of responses. + */ + stream?: boolean; + /** + * The large language model to use for answer generation. Must be one outlined in {@link ChatModelEnum}. + */ + model?: string; + /** + * A filter against which documents can be retrieved. + */ + filter?: object; +} + +/** + * Sends a message to the Assistant and receives a response. Retries the request if the server fails. + * + * @example + * ```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 } + * // } + * ``` + * + * @param assistantName - The name of the Assistant to send the message to. + * @param api - The API object to use to send the request. + * @throws An Error if no messages are provided. + * @throws an Error if the message object is not formatted correctly. + * @throws an Error if the model is not one of the available models. + * @returns A promise that resolves to a {@link ChatModel} object containing the response from the Assistant. + */ +export const chat = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async (options: ChatRequest): Promise => { + if (!options.messages) { + throw new Error('No messages passed to Assistant'); + } + const api = await apiProvider.provideData(); + + const messages = messagesValidation(options) as MessageModel[]; + const model = modelValidation(options); + const request: ChatAssistantRequest = { + assistantName: assistantName, + chat: { + messages: messages, + stream: options.stream, + model: model, + filter: options.filter, + }, + }; + + const retryWrapper = new RetryOnServerFailure(() => + api.chatAssistant(request) + ); + + return retryWrapper.execute(); + }; +}; diff --git a/src/assistant/data/chatCompletion.ts b/src/assistant/data/chatCompletion.ts new file mode 100644 index 00000000..72533c34 --- /dev/null +++ b/src/assistant/data/chatCompletion.ts @@ -0,0 +1,58 @@ +import { + ChatCompletionAssistantRequest, + ChatCompletionModel, + MessageModel, +} from '../../pinecone-generated-ts-fetch/assistant_data'; +import { messagesValidation, modelValidation } from './chat'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; +import { RetryOnServerFailure } from '../../utils'; + +export interface ChatCompletionRequest { + messages: string[] | Array<{ [key: string]: string }>; + stream?: boolean; + model?: string; + filter?: object; +} + +/** + * Sends a message to the Assistant and receives a response. Response is compatible with + * [OpenAI's Chat Completion API](https://platform.openai.com/docs/guides/text-generation. Retries the request if the server fails. + * + * See {@link chat} for example usage. + * + * @param assistantName - The name of the Assistant to send the message to. + * @param api - The API object to use to send the request. + * @throws An Error if no messages are provided. + * @throws an Error if the message object is not formatted correctly. + * @throws an Error if the model is not one of the available models. + * @returns A promise that resolves to a {@link ChatCompletionModel} object containing the response from the Assistant. + */ +export const chatCompletion = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async ( + options: ChatCompletionRequest + ): Promise => { + if (!options.messages) { + throw new Error('No messages passed to Assistant'); + } + const api = await apiProvider.provideData(); + const messages = messagesValidation(options) as MessageModel[]; + const model = modelValidation(options); + const request: ChatCompletionAssistantRequest = { + assistantName: assistantName, + searchCompletions: { + messages: messages, + stream: options.stream, + model: model, + filter: options.filter, + }, + }; + const retryWrapper = new RetryOnServerFailure(() => + api.chatCompletionAssistant(request) + ); + + return retryWrapper.execute(); + }; +}; diff --git a/src/assistant/data/context.ts b/src/assistant/data/context.ts new file mode 100644 index 00000000..8bf5fe90 --- /dev/null +++ b/src/assistant/data/context.ts @@ -0,0 +1,66 @@ +import { ContextAssistantRequest } from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; + +/** + * The `Context` interface describes the query and optional filter to retrieve context snippets from an Assistant. + */ +export interface Context { + /** + * The query to retrieve context snippets for. + */ + query: string; + /** + * Optional filter to apply to the context snippets. + */ + filter?: Record; +} + +/** + * Retrieves [the context snippets](https://docs.pinecone.io/guides/assistant/understanding-context-snippets) used + * by an Assistant during the retrieval process. + * + * @example + * ```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 } + * // } + * ``` + * + * @param assistantName - The name of the Assistant to retrieve the context snippets from. + * @param api - The Pinecone API object. + * @throws An error if a query is not provided. + * @returns A promise that resolves to a {@link Context} object containing the context snippets. + */ +export const context = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async (options: Context) => { + if (!options.query) { + throw new Error('Must provide a query'); + } + const api = await apiProvider.provideData(); + const request = { + assistantName: assistantName, + contextRequest: { + query: options.query, + filter: options.filter, + }, + } as ContextAssistantRequest; + return api.contextAssistant(request); + }; +}; diff --git a/src/assistant/data/deleteFile.ts b/src/assistant/data/deleteFile.ts new file mode 100644 index 00000000..c6ba9a87 --- /dev/null +++ b/src/assistant/data/deleteFile.ts @@ -0,0 +1,46 @@ +import { DeleteFileRequest } from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; + +/** + * The `DeleteFile` interface describes the file ID for deleting a file uploaded to an Assistant. + */ +export interface DeleteFile { + /** + * The ID of the file to delete. + */ + fileId: string; +} + +/** + * Deletes a file uploaded to an Assistant by ID. + * + * @example + * ```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}); + * } + * ``` + * + * @param assistantName - The name of the Assistant to delete the file from. + * @param api - The Pinecone API object. + */ +export const deleteFile = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async (options: DeleteFile) => { + const api = await apiProvider.provideData(); + const request = { + assistantName: assistantName, + assistantFileId: options.fileId, + } as DeleteFileRequest; + return api.deleteFile(request); + }; +}; diff --git a/src/assistant/data/describeFile.ts b/src/assistant/data/describeFile.ts new file mode 100644 index 00000000..eec8210a --- /dev/null +++ b/src/assistant/data/describeFile.ts @@ -0,0 +1,66 @@ +import { DescribeFileRequest } from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; + +/** + * The `DescribeFile` interface describes the options that can be passed to the `describeFile` method. + */ +export interface DescribeFile { + /** + * The ID of the file to describe. + */ + fileId: string; + /** + * Whether to include a signed URL for the file. Points to a GCP bucket if `true`. + */ + includeUrl?: boolean; +} + +/** + * Describes a file uploaded to an Assistant. + * + * @example + * ```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 + * // } + * ``` + * + * @param assistantName - The name of the Assistant that the file is uploaded to. + * @param api - The API object to use to send the request. + * @returns A promise that resolves to a {@link AssistantFileModel} object containing the file details. + */ +export const describeFile = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async (options: DescribeFile) => { + const api = await apiProvider.provideData(); + const request = { + assistantName: assistantName, + assistantFileId: options.fileId, + includeUrl: options.includeUrl, + } as DescribeFileRequest; + return api.describeFile(request); + }; +}; diff --git a/src/assistant/data/index.ts b/src/assistant/data/index.ts new file mode 100644 index 00000000..b6f968f2 --- /dev/null +++ b/src/assistant/data/index.ts @@ -0,0 +1,10 @@ +export { ChatRequest, chat } from './chat'; +export { ChatCompletionRequest, chatCompletion } from './chatCompletion'; +export { DeleteFile, deleteFile } from './deleteFile'; +export { DescribeFile, describeFile } from './describeFile'; +export { ListFiles, listFiles } from './listFiles'; +export { UploadFile, uploadFile } from './uploadFile'; +export { Context, context } from './context'; +export { AssistantEval, evaluate } from '../control/evaluate'; +export { Assistant } from './assistant'; +export { AsstDataOperationsProvider } from './asstDataOperationsProvider'; diff --git a/src/assistant/data/listFiles.ts b/src/assistant/data/listFiles.ts new file mode 100644 index 00000000..ea2cb703 --- /dev/null +++ b/src/assistant/data/listFiles.ts @@ -0,0 +1,56 @@ +import { ListFilesRequest } from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; + +/** + * The `ListFiles` interface describes the options (a single filter) that can be passed to the `listFiles` method. + */ +export interface ListFiles { + /** + * A filter object to filter the files returned by the `listFiles` method. + */ + filter?: object; +} + +/** + * Lists files (with optional filter) uploaded to an Assistant. + * + * @example + * ```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 + * // } + * // ] + * // } + * ``` + * @param assistantName - The name of the Assistant that the files are uploaded to. + * @param api - The API object to use to send the request. + */ +export const listFiles = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider +) => { + return async (options: ListFiles) => { + const api = await apiProvider.provideData(); + const request = { + assistantName: assistantName, + filter: options.filter, + } as ListFilesRequest; + return api.listFiles(request); + }; +}; diff --git a/src/assistant/data/uploadFile.ts b/src/assistant/data/uploadFile.ts new file mode 100644 index 00000000..8c16ad44 --- /dev/null +++ b/src/assistant/data/uploadFile.ts @@ -0,0 +1,99 @@ +import { + AssistantFileModelFromJSON, + X_PINECONE_API_VERSION, + JSONApiResponse, + ResponseError, +} from '../../pinecone-generated-ts-fetch/assistant_data'; +import { AsstDataOperationsProvider } from './asstDataOperationsProvider'; +import { handleApiError, PineconeArgumentError } from '../../errors'; +import { PineconeConfiguration } from '../../data'; +import { buildUserAgent, getFetch } from '../../utils'; +import fs from 'fs'; +import path from 'path'; + +/** + * The `UploadFile` interface describes the file path for uploading a file to an Assistant and optional metadata. + */ +export interface UploadFile { + /** + * The (local) path to the file to upload. + */ + path: string; + /** + * Optional metadata to attach to the file. + */ + metadata?: Record; +} + +export const uploadFile = ( + assistantName: string, + apiProvider: AsstDataOperationsProvider, + config: PineconeConfiguration +) => { + return async (req: UploadFile) => { + const fetch = getFetch(config); + if (!req.path) { + throw new PineconeArgumentError('File path is required'); + } + const fileBuffer = fs.readFileSync(req.path); + const fileName = path.basename(req.path); + const mimeType = getMimeType(fileName); + const fileBlob = new Blob([fileBuffer], { type: mimeType }); + const formData = new FormData(); + formData.append('file', fileBlob, fileName); + const hostUrl = await apiProvider.provideHostUrl(); + let filesUrl = `${hostUrl}/files/${assistantName}`; + + const requestHeaders = { + 'Api-Key': config.apiKey, + 'User-Agent': buildUserAgent(config), + 'X-Pinecone-Api-Version': X_PINECONE_API_VERSION, + }; + + if (req.metadata) { + const encodedMetadata = encodeURIComponent(JSON.stringify(req.metadata)); + filesUrl += `?metadata=${encodedMetadata}`; + } + + const response = await fetch(filesUrl, { + method: 'POST', + headers: requestHeaders, + body: formData, + }); + + if (response.ok) { + const assistantFileModel = new JSONApiResponse(response, (jsonValue) => + AssistantFileModelFromJSON(jsonValue) + ).value(); + return assistantFileModel; + } else { + const err = await handleApiError( + new ResponseError(response, 'Response returned an error'), + undefined, + filesUrl + ); + throw err; + } + }; +}; + +// get mime types for accepted file types +function getMimeType(filePath: string) { + const extensionToMimeType = { + pdf: 'application/pdf', + json: 'application/json', + txt: 'text/plain', + md: 'text/markdown', + }; + + // Extract file extension and ensure it's lowercase + const parts = filePath.split('.'); + if (parts.length < 2) { + return 'application/octet-stream'; // Default for files without extensions + } + const ext = parts.pop(); + const extension = ext ? ext.toLowerCase() : ''; + + // Return the MIME type or a default value for unsupported types + return extensionToMimeType[extension]; +} diff --git a/src/data/vectors/types.ts b/src/data/vectors/types.ts index 8163c7a0..ad22fefd 100644 --- a/src/data/vectors/types.ts +++ b/src/data/vectors/types.ts @@ -36,6 +36,12 @@ export type PineconeConfiguration = { * Optional configuration field for specifying the maximum number of retries for a request. Defaults to 3. */ maxRetries?: number; + + /** + * Optional configuration field for specifying a region to use with the assistant APIs. If not specified, the default + * region of "us" is used. + */ + assistantRegion?: string; }; // Properties for validation to ensure no unknown/invalid properties are passed, no req'd properties are missing diff --git a/src/external-app/README.md b/src/external-app/README.md old mode 100644 new mode 100755 diff --git a/src/external-app/assertResponse.ts b/src/external-app/assertResponse.ts index d5c04fdf..34e44467 100644 --- a/src/external-app/assertResponse.ts +++ b/src/external-app/assertResponse.ts @@ -33,15 +33,42 @@ class EdgeExternalAppTest { }; } -const apiKey = process.env['PINECONE_API_KEY']; -if (!apiKey) { - throw new Error('PINECONE_API_KEY key is required'); -} +// const apiKey = process.env['PINECONE_API_KEY']; +// if (!apiKey) { +// throw new Error('PINECONE_API_KEY key is required'); +// } + +// const url = process.argv[2]; // Get local URL from the command line arg +// console.log('URL COMING FROM PROCESS: ', url); +// const { assertOnResponse } = new EdgeExternalAppTest(apiKey, url); + +// assertOnResponse().then((indexName) => { +// console.log(indexName); +// }); + +async function main() { + try { + const apiKey = process.env.PINECONE_API_KEY; + if (!apiKey) { + throw new Error('PINECONE_API_KEY environment variable is required.'); + } -const url = process.argv[2]; // Get local URL from the command line arg + const url = process.argv[2]; + if (!url) { + throw new Error('A valid URL argument is required.'); + } + + console.log('Testing against URL:', url); + + const tester = new EdgeExternalAppTest(apiKey, url); + const indexName = await tester.assertOnResponse(); -const { assertOnResponse } = new EdgeExternalAppTest(apiKey, url); + console.log('Test passed. Index name:', indexName); + } catch (error) { + console.error('Test failed:', error); + process.exit(1); + } +} -assertOnResponse().then((indexName) => { - console.log(indexName); -}); +// Run the script +main(); diff --git a/src/external-app/local-external-app.sh b/src/external-app/local-external-app.sh index 6f72ebdc..0a05f331 100755 --- a/src/external-app/local-external-app.sh +++ b/src/external-app/local-external-app.sh @@ -36,6 +36,19 @@ pushd "ts-client-test-external-app" next dev & # `&` runs the command in the background popd +# Wait for the server to be ready +echo "Waiting for Next.js server to start..." +max_attempts=30 +attempt=0 +until curl --silent --head http://localhost:3000/ > /dev/null; do + if [ $attempt -eq $max_attempts ]; then + echo "Next.js server failed to start within the expected time." + exit 1 + fi + attempt=$((attempt+1)) + sleep 2 +done + # Run test file echo "Running tests..." localUrl='http://localhost:3000/api/createSeedQuery' # TODO: parameterize later for different endpoints diff --git a/src/index.ts b/src/index.ts index f92e6354..cdaafe9c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,21 @@ export { RerankResultUsage, RankedDocument, } from './pinecone-generated-ts-fetch/inference'; +export type { ListResponse } from './pinecone-generated-ts-fetch/db_data'; +export { + UpdateAssistantOperationRequest, + UpdateAssistantRequest, +} from './pinecone-generated-ts-fetch/assistant_control'; +export { CreateAssistantOptions } from './assistant/control/createAssistant'; +export { UpdateAssistantOptions } from './assistant/control/updateAssistant'; +export { ChatRequest } from './assistant/data/chat'; +export { ChatCompletionRequest } from './assistant/data/chatCompletion'; +export { DeleteFile } from './assistant/data/deleteFile'; +export { DescribeFile } from './assistant/data/describeFile'; +export { ListFiles } from './assistant/data/listFiles'; +export { UploadFile } from './assistant/data/uploadFile'; +export { Context } from './assistant/data/context'; +export { AssistantEval } from './assistant/control/evaluate'; // Type exports export type { @@ -48,7 +63,6 @@ export type { RecordValues, ScoredPineconeRecord, } from './data'; - export type { CollectionList, CollectionModel, @@ -67,4 +81,3 @@ export type { PodSpec, PodSpecMetadataConfig, } from './pinecone-generated-ts-fetch/db_control'; -export type { ListResponse } from './pinecone-generated-ts-fetch/db_data'; diff --git a/src/integration/assistant-control/createAssistant.test.ts b/src/integration/assistant-control/createAssistant.test.ts new file mode 100644 index 00000000..bed9065e --- /dev/null +++ b/src/integration/assistant-control/createAssistant.test.ts @@ -0,0 +1,79 @@ +import { Pinecone } from '../../pinecone'; +import { randomString, sleep } from '../test-helpers'; + +let pinecone: Pinecone; + +beforeAll(async () => { + pinecone = new Pinecone(); +}); + +describe('createAssistant happy path', () => { + test('simple create', async () => { + const assistantName = randomString(5); + await pinecone.createAssistant({ + name: assistantName, + instructions: 'test-instructions', + metadata: { key: 'value', keyTwo: 'valueTwo' }, + region: 'us', + }); + await sleep(2000); + const description = await pinecone.describeAssistant(assistantName); + expect(description.name).toEqual(assistantName); + expect(description.instructions).toEqual('test-instructions'); + expect(description.metadata).toEqual({ key: 'value', keyTwo: 'valueTwo' }); + + await pinecone.deleteAssistant(assistantName); + }); +}); + +describe('createAssistant error paths', () => { + test('createAssistant with too much metadata', async () => { + const assistantName = randomString(5); + const throwError = async () => { + await pinecone.createAssistant({ + name: assistantName, + metadata: { key: 'a'.repeat(1000000) }, + }); + }; + await expect(throwError()).rejects.toThrow('Metadata is too large'); + }); + + test('createAssistant with invalid region', async () => { + const assistantName = randomString(5); + const throwError = async () => { + await pinecone.createAssistant({ + name: assistantName, + region: 'invalid-region', + }); + }; + await expect(throwError()).rejects.toThrow( + 'Invalid region specified. Must be one of "us" or "eu"' + ); + }); + + test('createAssistant with empty assistant name', async () => { + const assistantName = ''; + const throwError = async () => { + await pinecone.createAssistant({ + name: assistantName, + }); + }; + await expect(throwError()).rejects.toThrow('Invalid assistant name'); + }); + + test('createAssistant with duplicate name', async () => { + const assistantName = randomString(5); + await pinecone.createAssistant({ + name: assistantName, + }); + + const throwError = async () => { + await pinecone.createAssistant({ + name: assistantName, + }); + }; + await expect(throwError()).rejects.toThrow(); + + await pinecone.deleteAssistant(assistantName); + }); +}); diff --git a/src/integration/assistant-control/deleteAssistant.test.ts b/src/integration/assistant-control/deleteAssistant.test.ts new file mode 100644 index 00000000..364c8114 --- /dev/null +++ b/src/integration/assistant-control/deleteAssistant.test.ts @@ -0,0 +1,37 @@ +import { Pinecone } from '../../pinecone'; +import { randomString, sleep } from '../test-helpers'; +import { PineconeNotFoundError } from '../../errors'; + +let pinecone: Pinecone; + +beforeAll(async () => { + pinecone = new Pinecone(); +}); + +describe('deleteAssistant happy path', () => { + test('simple delete', async () => { + const assistantName = randomString(5); + + await pinecone.createAssistant({ + name: assistantName, + }); + + await pinecone.deleteAssistant(assistantName); + await sleep(3000); + + const noAssistant = async () => { + await pinecone.describeAssistant(assistantName); + }; + + await expect(noAssistant()).rejects.toThrow(PineconeNotFoundError); + }); +}); + +describe('deleteAssistant error paths', () => { + test('delete non-existent assistant', async () => { + const throwError = async () => { + await pinecone.deleteAssistant('non-existent-assistant'); + }; + await expect(throwError()).rejects.toThrow(PineconeNotFoundError); + }); +}); diff --git a/src/integration/assistant-control/evaluate.test.ts b/src/integration/assistant-control/evaluate.test.ts new file mode 100644 index 00000000..f3fff813 --- /dev/null +++ b/src/integration/assistant-control/evaluate.test.ts @@ -0,0 +1,38 @@ +import { Pinecone } from '../../pinecone'; + +describe('evaluate', () => { + const pc = new Pinecone(); + + test('Evaluate happy path', async () => { + const response = await pc.evaluate({ + question: "What's the capital of France?", + answer: "Paris is France's capital city", + groundTruth: 'Paris is the capital city of France', + }); + + expect(response).toBeDefined(); + expect(response.metrics).toBeDefined(); + expect(response.metrics['correctness']).toBeDefined(); + expect(response.metrics['completeness']).toBeDefined(); + expect(response.metrics['alignment']).toBeDefined(); + expect(response.usage).toBeDefined(); + expect(response.usage['promptTokens']).toBeDefined(); + expect(response.usage['completionTokens']).toBeDefined(); + expect(response.usage['totalTokens']).toBeDefined(); + expect(response.reasoning).toBeDefined(); + expect(response.reasoning['evaluatedFacts']).toBeDefined(); + }); + + test('Evaluate with wrong answer', async () => { + const response = await pc.evaluate({ + question: "What's the capital of France?", + answer: "Lyon is France's capital city", + groundTruth: 'Paris is the capital city of France', + }); + + expect(response).toBeDefined(); + expect(response.metrics['correctness']).toEqual(0); // B/c answer is wrong + expect(response.metrics['completeness']).toEqual(0); + expect(response.metrics['alignment']).toEqual(0); + }); +}); diff --git a/src/integration/assistant-control/getAssistant.test.ts b/src/integration/assistant-control/getAssistant.test.ts new file mode 100644 index 00000000..0ee76c00 --- /dev/null +++ b/src/integration/assistant-control/getAssistant.test.ts @@ -0,0 +1,39 @@ +import { Pinecone } from '../../pinecone'; +import { randomString, sleep } from '../test-helpers'; +import { PineconeNotFoundError } from '../../errors'; + +let pinecone: Pinecone; +let assistantName: string; + +beforeAll(async () => { + pinecone = new Pinecone(); + assistantName = randomString(5); + await pinecone.createAssistant({ name: assistantName }); + await sleep(2000); +}); + +afterAll(async () => { + await pinecone.deleteAssistant(assistantName); +}); + +describe('getAssistant happy path', () => { + test('simple get', async () => { + const assistantInfo = await pinecone.describeAssistant(assistantName); + expect(assistantInfo.name).toEqual(assistantName); + expect(assistantInfo.instructions).toBeUndefined(); + expect(assistantInfo.metadata).toBeUndefined(); + expect(assistantInfo.status).toBeDefined(); + expect(assistantInfo.host).toBeDefined(); + expect(assistantInfo.createdAt).toBeDefined(); + expect(assistantInfo.updatedAt).toBeDefined(); + }); +}); + +describe('getAssistant error paths', () => { + test('get non-existent assistant', async () => { + const throwError = async () => { + await pinecone.describeAssistant('non-existent-assistant'); + }; + await expect(throwError()).rejects.toThrow(PineconeNotFoundError); + }); +}); diff --git a/src/integration/assistant-control/listAssistants.test.ts b/src/integration/assistant-control/listAssistants.test.ts new file mode 100644 index 00000000..956ba197 --- /dev/null +++ b/src/integration/assistant-control/listAssistants.test.ts @@ -0,0 +1,33 @@ +import { Pinecone } from '../../pinecone'; +import { randomString } from '../test-helpers'; + +let pinecone: Pinecone; +let assistantNameOne: string; +let assistantNameTwo: string; + +beforeAll(async () => { + pinecone = new Pinecone(); + assistantNameOne = randomString(5); + assistantNameTwo = randomString(5); + await pinecone.createAssistant({ name: assistantNameOne }); + await pinecone.createAssistant({ name: assistantNameTwo }); +}); + +afterAll(async () => { + await pinecone.deleteAssistant(assistantNameOne); + await pinecone.deleteAssistant(assistantNameTwo); +}); + +describe('listAssistant happy path', () => { + test('list existing Assistants', async () => { + const assistants = await pinecone.listAssistants(); + expect(assistants.assistants).toBeDefined(); + if (assistants.assistants) { + const assistantNames = assistants.assistants.map( + (assistant) => assistant.name + ); + expect(assistantNames).toContain(assistantNameOne); + expect(assistantNames).toContain(assistantNameTwo); + } + }); +}); diff --git a/src/integration/assistant-control/updateAssistant.test.ts b/src/integration/assistant-control/updateAssistant.test.ts new file mode 100644 index 00000000..2348b142 --- /dev/null +++ b/src/integration/assistant-control/updateAssistant.test.ts @@ -0,0 +1,66 @@ +import { Pinecone } from '../../pinecone'; +import { randomString } from '../test-helpers'; +import { PineconeNotFoundError } from '../../errors'; + +let pinecone: Pinecone; + +beforeAll(async () => { + pinecone = new Pinecone(); +}); + +describe('updateAssistant inplace updates, happy path', () => { + test('simple update', async () => { + const assistantName = randomString(5); + await pinecone.createAssistant({ + name: assistantName, + instructions: 'test-instructions', + metadata: { key: 'value', keyTwo: 'valueTwo' }, + region: 'us', + }); + + await pinecone.updateAssistant({ + assistantName: assistantName, + instructions: 'new-instructions', + metadata: { key: 'newValue', keyTwo: 'newValueTwo' }, + }); + + const description = await pinecone.describeAssistant(assistantName); + expect(description.instructions).toEqual('new-instructions'); + expect(description.metadata).toEqual({ + key: 'newValue', + keyTwo: 'newValueTwo', + }); + + await pinecone.deleteAssistant(assistantName); + }); + + // todo: change this test if product says this is not the desired behavior + test('updateAssistant with new metadata key:value pair', async () => { + const assistantName = randomString(5); + await pinecone.createAssistant({ + name: assistantName, + metadata: { key: 'value', keyTwo: 'valueTwo' }, + }); + + await pinecone.updateAssistant({ + assistantName: assistantName, + metadata: { keyThree: 'valueThree' }, + }); + + const description = await pinecone.describeAssistant(assistantName); + expect(description.metadata).toEqual({ keyThree: 'valueThree' }); + + await pinecone.deleteAssistant(assistantName); + }); +}); + +describe('updateAssistant error paths', () => { + test('Update non-existent assistant', async () => { + const throwError = async () => { + await pinecone.updateAssistant({ + assistantName: 'non-existent-assistant', + }); + }; + await expect(throwError()).rejects.toThrow(PineconeNotFoundError); + }); +}); diff --git a/src/integration/assistant-data/chat.test.ts b/src/integration/assistant-data/chat.test.ts new file mode 100644 index 00000000..ac32ace0 --- /dev/null +++ b/src/integration/assistant-data/chat.test.ts @@ -0,0 +1,103 @@ +import { Pinecone } from '../../pinecone'; +import { Assistant } from '../../assistant/data'; +import { PineconeBadRequestError } from '../../errors'; +import { ChatModel } from '../../pinecone-generated-ts-fetch/assistant_data'; + +let pinecone: Pinecone; +let assistant: Assistant; +let assistantName: string; + +if (!process.env.ASSISTANT_NAME) { + throw new Error('ASSISTANT_NAME environment variable is not set'); +} else { + assistantName = process.env.ASSISTANT_NAME; +} + +beforeAll(async () => { + pinecone = new Pinecone(); + assistant = pinecone.Assistant(assistantName); +}); + +describe('Chat happy path', () => { + test('Chat with messages', async () => { + let response: ChatModel; + try { + response = await assistant.chat({ + messages: [{ role: 'user', content: 'Hello' }], + }); + } catch (error) { + const errorResponse = error as PineconeBadRequestError; + if (errorResponse.name == 'PineconeBadRequestError') { + console.log( + 'Assistant not ready to chat yet, trying again ', + errorResponse.message + ); + response = await assistant.chat({ + messages: [{ role: 'user', content: 'Hello' }], + }); + } else { + throw error; + } + } + expect(response).toBeDefined(); + expect(response.message).toBeDefined(); + expect(response.id).toBeDefined(); + expect(response.model).toBeDefined(); + expect(response.usage).toBeDefined(); + expect(response.finishReason).toBeDefined(); + }); +}); + +describe('Chat error paths', () => { + test('Chat with empty messages', async () => { + const throwError = async () => { + await assistant.chat({ messages: [] }); + }; + await expect(throwError()).rejects.toThrow('Must have at least 1 message'); + }); + + test('Chat with invalid role type', async () => { + const throwError = async () => { + await assistant.chat({ + messages: [{ role: 'invalid', content: 'Hello' }], + }); + }; + await expect(throwError()).rejects.toThrow( + 'No role specified in message object. Must be one of "user" or "assistant"' + ); + }); + + test('Chat with no role key', async () => { + const throwError = async () => { + await assistant.chat({ + messages: [{}], + }); + }; + await expect(throwError()).rejects.toThrow( + 'Message object must have exactly two keys: "role" and "content"' + ); + }); + + test('Chat with invalid model', async () => { + const throwError = async () => { + await assistant.chat({ + messages: [{ role: 'user', content: 'Hello' }], + model: 'invalid', + }); + }; + await expect(throwError()).rejects.toThrow( + 'Invalid model specified. Must be one of "gpt-4o" or "claude-3-5-sonnet"' + ); + }); + + test('Chat with nonexistent assistant', async () => { + const throwError = async () => { + await pinecone.Assistant('nonexistent').chat({ + messages: [{ role: 'user', content: 'Hello' }], + }); + }; + await expect(throwError()).rejects.toThrow( + 'A call to https://api.pinecone.io/assistant/assistants/nonexistent returned HTTP status 404.' + ); + }); +}); diff --git a/src/integration/assistant-data/context.test.ts b/src/integration/assistant-data/context.test.ts new file mode 100644 index 00000000..9f336a91 --- /dev/null +++ b/src/integration/assistant-data/context.test.ts @@ -0,0 +1,56 @@ +import { Pinecone } from '../../pinecone'; +import { Assistant } from '../../assistant/data'; + +let pinecone: Pinecone; +let assistant: Assistant; +let assistantName: string; + +if (!process.env.ASSISTANT_NAME) { + throw new Error('ASSISTANT_NAME environment variable is not set'); +} else { + assistantName = process.env.ASSISTANT_NAME; +} + +beforeAll(async () => { + pinecone = new Pinecone(); + assistant = pinecone.Assistant(assistantName); +}); + +describe('Context happy path', () => { + test('Get context with valid query', async () => { + const response = await assistant.context({ + query: 'What is in the uploaded file?', + }); + expect(response).toBeDefined(); + expect(response.snippets).toBeDefined(); + expect(Array.isArray(response.snippets)).toBe(true); + }); + + test('Get context with query and filter', async () => { + const response = await assistant.context({ + query: 'What is in the uploaded file?', + filter: { key: 'valueOne' }, + }); + expect(response).toBeDefined(); + expect(response.snippets).toBeDefined(); + expect(Array.isArray(response.snippets)).toBe(true); + }); +}); + +describe('Context error paths', () => { + test('Context with empty query', async () => { + const throwError = async () => { + await assistant.context({ query: '' }); + }; + await expect(throwError()).rejects.toThrow('Must provide a query'); + }); + + test('Context with nonexistent assistant', async () => { + const throwError = async () => { + await pinecone.Assistant('nonexistent').context({ + query: 'What is in the file?', + }); + }; + await expect(throwError()).rejects.toThrow(/404/); + }); +}); diff --git a/src/integration/assistant-data/describeFile.test.ts b/src/integration/assistant-data/describeFile.test.ts new file mode 100644 index 00000000..4d330aeb --- /dev/null +++ b/src/integration/assistant-data/describeFile.test.ts @@ -0,0 +1,71 @@ +import { Pinecone } from '../../pinecone'; +import { Assistant } from '../../assistant/data'; + +let pinecone: Pinecone; +let assistant: Assistant; +let assistantName: string; +let fileId: string; + +if (!process.env.ASSISTANT_NAME) { + throw new Error('ASSISTANT_NAME environment variable is not set'); +} else { + assistantName = process.env.ASSISTANT_NAME; +} + +beforeAll(async () => { + pinecone = new Pinecone(); + assistant = pinecone.Assistant(assistantName); + const files = await assistant.listFiles(); + if (files.files) { + fileId = files.files.map((f) => f.id)[0]; + } else { + throw new Error('No files found'); + } +}); + +describe('Describe file happy path', () => { + test('Describe file', async () => { + const response = await assistant.describeFile({ + fileId: fileId, + }); + expect(response).toBeDefined(); + expect(response.status).toBeDefined(); + expect(response.id).toBeDefined(); + expect(response.metadata).toBeDefined(); + if (response.metadata) { + expect(response.metadata['key']).toBeDefined(); + expect(response.metadata['key']).toEqual('valueOne'); + expect(response.metadata['keyTwo']).toBeDefined(); + expect(response.metadata['keyTwo']).toEqual('valueTwo'); + } + }); +}); + +describe('Describe file error paths', () => { + test('Describe with nonexistent fileId', async () => { + const throwError = async () => { + await assistant.describeFile({ + fileId: 'nonexistent-file-id', + }); + }; + await expect(throwError()).rejects.toThrow(/404/); + }); + + test('Describe with empty fileId', async () => { + const throwError = async () => { + await assistant.describeFile({ + fileId: '', + }); + }; + await expect(throwError()).rejects.toThrow(); + }); + + test('Describe file with nonexistent assistant', async () => { + const throwError = async () => { + await pinecone.Assistant('nonexistent').describeFile({ + fileId: fileId, + }); + }; + await expect(throwError()).rejects.toThrow(/404/); + }); +}); diff --git a/src/integration/assistant-data/uploadAndDeleteFile.test.ts b/src/integration/assistant-data/uploadAndDeleteFile.test.ts new file mode 100644 index 00000000..541972a2 --- /dev/null +++ b/src/integration/assistant-data/uploadAndDeleteFile.test.ts @@ -0,0 +1,154 @@ +import { Pinecone } from '../../pinecone'; +import { Assistant } from '../../assistant/data'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { sleep } from '../test-helpers'; + +let pinecone: Pinecone; +let assistant: Assistant; +let assistantName: string; +let tempFile: string; +let tempFilePath: string; +let tempFileWithMetadata: string; +let tempFileWithMetadataPath: string; + +beforeAll(async () => { + if (!process.env.ASSISTANT_NAME) { + throw new Error('ASSISTANT_NAME environment variable is not set'); + } else { + assistantName = process.env.ASSISTANT_NAME; + } + + pinecone = new Pinecone(); + assistant = pinecone.Assistant(assistantName); + + // Create two temporary test files + const content = 'This is test content for file upload'; + // 1: file without metadata + tempFile = `test-upload-${Date.now()}.txt`; + tempFilePath = path.join(os.tmpdir(), tempFile); + try { + fs.writeFileSync(tempFilePath, content); + console.log('File written:', tempFilePath); + } catch (err) { + console.error('Error writing file:', err); + } + + // 2: file with metadata + tempFileWithMetadata = `test-upload-metadata-${Date.now()}.txt`; + tempFileWithMetadataPath = path.join(os.tmpdir(), tempFileWithMetadata); + + try { + fs.writeFileSync(tempFileWithMetadataPath, content); + console.log('File written:', tempFileWithMetadataPath); + } catch (err) { + console.error('Error writing file:', err); + } + + // Add a small delay to ensure file system sync + await sleep(5000); + + if (!fs.existsSync(tempFilePath)) { + throw new Error(`Temporary file was not created: ${tempFilePath}`); + } + if (!fs.existsSync(tempFileWithMetadataPath)) { + throw new Error( + `Temporary file was not created: ${tempFileWithMetadataPath}` + ); + } +}); + +afterAll(() => { + // Cleanup: remove temporary test files + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + if (fs.existsSync(tempFileWithMetadataPath)) { + fs.unlinkSync(tempFileWithMetadataPath); + } +}); + +describe('Upload file happy path', () => { + test('Upload file without metadata', async () => { + const response = await assistant.uploadFile({ + path: tempFilePath, + }); + await sleep(30000); // Crazy long sleep necessary; need to optimize (+ technically we already know this works + // b/c of setup.ts + expect(response).toBeDefined(); + expect(response.name).toEqual(tempFile); + expect(response.id).toBeDefined(); + expect(response.createdOn).toBeDefined(); // Sometimes these dates populate as "Invalid" at first, but then get updated + expect(response.updatedOn).toBeDefined(); + expect(response.status).toBeDefined(); + + // Delete file happy path test: + expect(await assistant.deleteFile({ fileId: response.id })).toBeUndefined(); + }); + + test('Upload file with metadata', async () => { + const response = await assistant.uploadFile({ + path: tempFileWithMetadataPath, + metadata: { + description: 'Test file', + category: 'integration-test', + }, + }); + await sleep(25000); // Crazy long sleep necessary; need to optimize + expect(response).toBeDefined(); + expect(response.name).toEqual(tempFileWithMetadata); + expect(response.id).toBeDefined(); + expect(response.createdOn).toBeDefined(); + expect(response.updatedOn).toBeDefined(); + expect(response.status).toBeDefined(); + expect(response.metadata).toBeDefined(); + if (response.metadata) { + expect(response.metadata['description']).toEqual('Test file'); + expect(response.metadata['category']).toEqual('integration-test'); + } + + // Delete file happy path test: + expect(await assistant.deleteFile({ fileId: response.id })).toBeUndefined(); + }); +}); + +describe('Upload file error paths', () => { + test('Upload with nonexistent file path', async () => { + const throwError = async () => { + await assistant.uploadFile({ + path: 'nonexistent-file.txt', + }); + }; + await expect(throwError()).rejects.toThrow(); + }); + + test('Upload to nonexistent assistant', async () => { + const throwError = async () => { + await pinecone.Assistant('nonexistent').uploadFile({ + path: tempFileWithMetadataPath, + }); + }; + await expect(throwError()).rejects.toThrow(/404/); + }); + + test('Upload with empty file path', async () => { + const throwError = async () => { + await assistant.uploadFile({ + path: '', + }); + }; + await expect(throwError()).rejects.toThrow('File path is required'); + }); +}); + +describe('Delete file error paths', () => { + test('Delete non-existent file', async () => { + const throwError = async () => { + await assistant.deleteFile({ + fileId: 'nonexistent-file-id', + }); + }; + await expect(throwError()).rejects.toThrow(/404/); + }); +}); diff --git a/src/integration/integrationRunner.js b/src/integration/integrationRunner.js old mode 100644 new mode 100755 index fd8d7c5a..109ed4d3 --- a/src/integration/integrationRunner.js +++ b/src/integration/integrationRunner.js @@ -2,7 +2,9 @@ const { execSync } = require('child_process'); -let SERVERLESS_INDEX_NAME; // Declare outside the try block +let SERVERLESS_INDEX_NAME; +let ASSISTANT_NAME; +let TEST_FILE; try { // Step 1: Run setup script and capture SERVERLESS_INDEX_NAME @@ -11,26 +13,34 @@ try { encoding: 'utf-8', }); SERVERLESS_INDEX_NAME = setupOutput.match(/SERVERLESS_INDEX_NAME=(\S+)/)[1]; + ASSISTANT_NAME = setupOutput.match(/ASSISTANT_NAME=(\S+)/)[1]; + TEST_FILE = setupOutput.match(/TEST_FILE=(\S+)/)[1]; const TEST_ENV = process.env.TEST_ENV || 'node'; // Step 2: Run Jest tests console.log(`Executing integration tests in Jest environment: "${TEST_ENV}"`); execSync( - `SERVERLESS_INDEX_NAME=${SERVERLESS_INDEX_NAME} TEST_ENV=${TEST_ENV} jest src/integration -c jest.config.integration-node.js --runInBand --bail`, + `SERVERLESS_INDEX_NAME=${SERVERLESS_INDEX_NAME} ASSISTANT_NAME=${ASSISTANT_NAME} TEST_FILE=${TEST_FILE} TEST_ENV=${TEST_ENV} jest src/integration -c jest.config.integration-node.js --runInBand --bail`, { stdio: 'inherit' } ); +} catch (error) { + console.error('Setup script failed with error:'); + console.error(error.stdout?.toString() || 'No stdout'); + console.error(error.stderr?.toString() || 'No stderr'); + process.exit(1); // Ensure the script fails visibly in CI/CD } finally { - // Step 3: Pass SERVERLESS_INDEX_NAME to teardown script, ensuring teardown runs even if tests fail - if (SERVERLESS_INDEX_NAME) { + // Step 3: Pass SERVERLESS_INDEX_NAME and ASSISTANT_NAME to teardown script, ensuring teardown runs even if tests fail + if (SERVERLESS_INDEX_NAME && ASSISTANT_NAME && TEST_FILE) { console.log('Running teardown script'); execSync( - `SERVERLESS_INDEX_NAME=${SERVERLESS_INDEX_NAME} npx ts-node ./src/integration/teardown.ts`, + `SERVERLESS_INDEX_NAME=${SERVERLESS_INDEX_NAME} ASSISTANT_NAME=${ASSISTANT_NAME} TEST_FILE=${TEST_FILE} npx ts-node ./src/integration/teardown.ts`, { stdio: 'inherit' } ); } else { console.warn( - 'SERVERLESS_INDEX_NAME is undefined. Teardown script may not behave as expected.' + 'Either SERVERLESS_INDEX_NAME or ASSISTANT_NAME or TEST_FILE is undefined. Teardown script may not behave as' + + ' expected.' ); } } diff --git a/src/integration/setup.ts b/src/integration/setup.ts index 269fa65f..a4660142 100644 --- a/src/integration/setup.ts +++ b/src/integration/setup.ts @@ -4,9 +4,14 @@ import { generateRecords, globalNamespaceOne, prefix, + randomString, sleep, } from './test-helpers'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; + // todo: refactor to make conditions & loops more efficient const setup = async () => { @@ -102,6 +107,61 @@ const setup = async () => { } // Capture output in GITHUB_OUTPUT env var when run in CI; necessary to pass across tests console.log(`SERVERLESS_INDEX_NAME=${randomIndexName}`); + + // Set up an Assistant and upload a file to it + const assistantName = randomString(5); + await pc.createAssistant({ + name: assistantName, + instructions: 'test-instructions', + metadata: { key: 'valueOne', keyTwo: 'valueTwo' }, + region: 'us', + }); + await sleep(2000); + + try { + await pc.describeAssistant(assistantName); + } catch (e) { + console.log('Error getting assistant:', e); + } + + const assistant = pc.Assistant(assistantName); + + // Capture output in GITHUB_OUTPUT env var when run in CI; necessary to pass across tests + console.log(`ASSISTANT_NAME=${assistantName}`); + + const tempFileName = path.join(os.tmpdir(), `tempfile-${Date.now()}.txt`); + + // Capture output in GITHUB_OUTPUT env var when run in CI; necessary to pass across tests + console.log(`TEST_FILE=${tempFileName}`); + + try { + const data = 'This is some temporary data'; + fs.writeFileSync(tempFileName, data); + console.log(`Temporary file created: ${tempFileName}`); + } catch (err) { + console.error('Error writing file:', err); + } + // Add a small delay to ensure file system sync + await sleep(1000); + + // Upload file to assistant so chat works + const file = await assistant.uploadFile({ + path: tempFileName, + metadata: { key: 'valueOne', keyTwo: 'valueTwo' }, + }); + + console.log('File uploaded:', file); + + // Another sleep b/c it currently takes a *long* time for a file to be available + await sleep(30000); + + // Delete file from local file system + try { + fs.unlinkSync(path.resolve(process.cwd(), tempFileName)); + console.log(`Temporary file deleted: ${tempFileName}`); + } catch (err) { + console.error('Error deleting file:', err); + } }; setup(); diff --git a/src/integration/teardown.ts b/src/integration/teardown.ts index f9a67eda..20443fa5 100644 --- a/src/integration/teardown.ts +++ b/src/integration/teardown.ts @@ -8,14 +8,13 @@ export const teardown = async () => { } else { apiKey = process.env['PINECONE_API_KEY']; } + const pc = new Pinecone({ apiKey: apiKey }); if (!process.env.SERVERLESS_INDEX_NAME) { throw new Error('SERVERLESS_INDEX_NAME environment variable is not set'); } else { const serverlessIndexName = process.env.SERVERLESS_INDEX_NAME; - const pc = new Pinecone({ apiKey: apiKey }); - const indexes = await pc.listIndexes(); if ( indexes && @@ -26,6 +25,13 @@ export const teardown = async () => { await pc.deleteIndex(serverlessIndexName); } + + if (!process.env.ASSISTANT_NAME) { + throw new Error('ASSISTANT_NAME environment variable is not set'); + } else { + const assistantName = process.env.ASSISTANT_NAME; + await pc.deleteAssistant(assistantName); + } }; teardown(); diff --git a/src/pinecone-generated-ts-fetch/assistant_control/api_version.ts b/src/pinecone-generated-ts-fetch/assistant_control/api_version.ts new file mode 100644 index 00000000..87013d2b --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/api_version.ts @@ -0,0 +1 @@ +export const X_PINECONE_API_VERSION = '2025-01'; diff --git a/src/pinecone-generated-ts-fetch/assistant_control/apis/ManageAssistantsApi.ts b/src/pinecone-generated-ts-fetch/assistant_control/apis/ManageAssistantsApi.ts new file mode 100644 index 00000000..ed3b9992 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/apis/ManageAssistantsApi.ts @@ -0,0 +1,247 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + Assistant, + CreateAssistantRequest, + ErrorResponse, + ListAssistants200Response, + UpdateAssistant200Response, + UpdateAssistantRequest, +} from '../models/index'; +import { + AssistantFromJSON, + AssistantToJSON, + CreateAssistantRequestFromJSON, + CreateAssistantRequestToJSON, + ErrorResponseFromJSON, + ErrorResponseToJSON, + ListAssistants200ResponseFromJSON, + ListAssistants200ResponseToJSON, + UpdateAssistant200ResponseFromJSON, + UpdateAssistant200ResponseToJSON, + UpdateAssistantRequestFromJSON, + UpdateAssistantRequestToJSON, +} from '../models/index'; + +export interface CreateAssistantOperationRequest { + createAssistantRequest: CreateAssistantRequest; +} + +export interface DeleteAssistantRequest { + assistantName: string; +} + +export interface GetAssistantRequest { + assistantName: string; +} + +export interface UpdateAssistantOperationRequest { + assistantName: string; + updateAssistantRequest: UpdateAssistantRequest; +} + +/** + * + */ +export class ManageAssistantsApi extends runtime.BaseAPI { + + /** + * The `create_assistant` endpoint [creates a Pinecone Assistant](https://docs.pinecone.io/guides/assistant/create-assistant). This is where you specify the underlying training model, which cloud provider you would like to deploy with, and more. + * Create an assistant + */ + async createAssistantRaw(requestParameters: CreateAssistantOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.createAssistantRequest === null || requestParameters.createAssistantRequest === undefined) { + throw new runtime.RequiredError('createAssistantRequest','Required parameter requestParameters.createAssistantRequest was null or undefined when calling createAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/assistants`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: CreateAssistantRequestToJSON(requestParameters.createAssistantRequest), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AssistantFromJSON(jsonValue)); + } + + /** + * The `create_assistant` endpoint [creates a Pinecone Assistant](https://docs.pinecone.io/guides/assistant/create-assistant). This is where you specify the underlying training model, which cloud provider you would like to deploy with, and more. + * Create an assistant + */ + async createAssistant(requestParameters: CreateAssistantOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.createAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `delete_assistant` endpoint [deletes an existing assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#delete-an-assistant). + * Delete an assistant + */ + async deleteAssistantRaw(requestParameters: DeleteAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling deleteAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/assistants/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * The `delete_assistant` endpoint [deletes an existing assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#delete-an-assistant). + * Delete an assistant + */ + async deleteAssistant(requestParameters: DeleteAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.deleteAssistantRaw(requestParameters, initOverrides); + } + + /** + * The `get_assistant` endpoint [gets the status](https://docs.pinecone.io/guides/assistant/manage-assistants#get-the-status-of-an-assistant) of an assistant. + * Check assistant status + */ + async getAssistantRaw(requestParameters: GetAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling getAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/assistants/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AssistantFromJSON(jsonValue)); + } + + /** + * The `get_assistant` endpoint [gets the status](https://docs.pinecone.io/guides/assistant/manage-assistants#get-the-status-of-an-assistant) of an assistant. + * Check assistant status + */ + async getAssistant(requestParameters: GetAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * This operation returns a list of all assistants in a project. + * List assistants + */ + async listAssistantsRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/assistants`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ListAssistants200ResponseFromJSON(jsonValue)); + } + + /** + * This operation returns a list of all assistants in a project. + * List assistants + */ + async listAssistants(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listAssistantsRaw(initOverrides); + return await response.value(); + } + + /** + * The `update_assistant` endpoint [updates an existing assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#update-an-existing-assistant). You can modify the assistant\'s instructions and metadata. + * Update an assistant + */ + async updateAssistantRaw(requestParameters: UpdateAssistantOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling updateAssistant.'); + } + + if (requestParameters.updateAssistantRequest === null || requestParameters.updateAssistantRequest === undefined) { + throw new runtime.RequiredError('updateAssistantRequest','Required parameter requestParameters.updateAssistantRequest was null or undefined when calling updateAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/assistants/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: UpdateAssistantRequestToJSON(requestParameters.updateAssistantRequest), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => UpdateAssistant200ResponseFromJSON(jsonValue)); + } + + /** + * The `update_assistant` endpoint [updates an existing assistant](https://docs.pinecone.io/guides/assistant/manage-assistants#update-an-existing-assistant). You can modify the assistant\'s instructions and metadata. + * Update an assistant + */ + async updateAssistant(requestParameters: UpdateAssistantOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/pinecone-generated-ts-fetch/assistant_control/apis/index.ts b/src/pinecone-generated-ts-fetch/assistant_control/apis/index.ts new file mode 100644 index 00000000..c2756ab4 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './ManageAssistantsApi'; diff --git a/src/pinecone-generated-ts-fetch/assistant_control/index.ts b/src/pinecone-generated-ts-fetch/assistant_control/index.ts new file mode 100644 index 00000000..91e0ae58 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/index.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; +export * from './api_version'; diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/Assistant.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/Assistant.ts new file mode 100644 index 00000000..396994eb --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/Assistant.ts @@ -0,0 +1,128 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The AssistantModel describes the configuration and status of a Pinecone Assistant. + * @export + * @interface Assistant + */ +export interface Assistant { + /** + * The name of the assistant. Resource name must be 1-45 characters long, start and end with an alphanumeric character, and consist only of lower case alphanumeric characters or '-'. + * @type {string} + * @memberof Assistant + */ + name: string; + /** + * Description or directive for the assistant to apply to all responses. + * @type {string} + * @memberof Assistant + */ + instructions?: string | null; + /** + * + * @type {object} + * @memberof Assistant + */ + metadata?: object | null; + /** + * + * @type {string} + * @memberof Assistant + */ + status: AssistantStatusEnum; + /** + * The host where the assistant is deployed. + * @type {string} + * @memberof Assistant + */ + host?: string; + /** + * + * @type {Date} + * @memberof Assistant + */ + createdAt?: Date; + /** + * + * @type {Date} + * @memberof Assistant + */ + updatedAt?: Date; +} + + +/** + * @export + */ +export const AssistantStatusEnum = { + Initializing: 'Initializing', + Failed: 'Failed', + Ready: 'Ready', + Terminating: 'Terminating' +} as const; +export type AssistantStatusEnum = typeof AssistantStatusEnum[keyof typeof AssistantStatusEnum]; + + +/** + * Check if a given object implements the Assistant interface. + */ +export function instanceOfAssistant(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "name" in value; + isInstance = isInstance && "status" in value; + + return isInstance; +} + +export function AssistantFromJSON(json: any): Assistant { + return AssistantFromJSONTyped(json, false); +} + +export function AssistantFromJSONTyped(json: any, ignoreDiscriminator: boolean): Assistant { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'instructions': !exists(json, 'instructions') ? undefined : json['instructions'], + 'metadata': !exists(json, 'metadata') ? undefined : json['metadata'], + 'status': json['status'], + 'host': !exists(json, 'host') ? undefined : json['host'], + 'createdAt': !exists(json, 'created_at') ? undefined : (new Date(json['created_at'])), + 'updatedAt': !exists(json, 'updated_at') ? undefined : (new Date(json['updated_at'])), + }; +} + +export function AssistantToJSON(value?: Assistant | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'instructions': value.instructions, + 'metadata': value.metadata, + 'status': value.status, + 'host': value.host, + 'created_at': value.createdAt === undefined ? undefined : (value.createdAt.toISOString()), + 'updated_at': value.updatedAt === undefined ? undefined : (value.updatedAt.toISOString()), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/CreateAssistantRequest.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/CreateAssistantRequest.ts new file mode 100644 index 00000000..575fcb75 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/CreateAssistantRequest.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The configuration needed to create an assistant. + * @export + * @interface CreateAssistantRequest + */ +export interface CreateAssistantRequest { + /** + * The name of the assistant. Resource name must be 1-45 characters long, start and end with an alphanumeric character, and consist only of lower case alphanumeric characters or '-'. + * @type {string} + * @memberof CreateAssistantRequest + */ + name: string; + /** + * Description or directive for the assistant to apply to all responses. + * @type {string} + * @memberof CreateAssistantRequest + */ + instructions?: string | null; + /** + * + * @type {object} + * @memberof CreateAssistantRequest + */ + metadata?: object; + /** + * The region to deploy the assistant in. Our current options are either us or eu. Defaults to us. + * @type {string} + * @memberof CreateAssistantRequest + */ + region?: CreateAssistantRequestRegionEnum; +} + + +/** + * @export + */ +export const CreateAssistantRequestRegionEnum = { + Us: 'us', + Eu: 'eu' +} as const; +export type CreateAssistantRequestRegionEnum = typeof CreateAssistantRequestRegionEnum[keyof typeof CreateAssistantRequestRegionEnum]; + + +/** + * Check if a given object implements the CreateAssistantRequest interface. + */ +export function instanceOfCreateAssistantRequest(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "name" in value; + + return isInstance; +} + +export function CreateAssistantRequestFromJSON(json: any): CreateAssistantRequest { + return CreateAssistantRequestFromJSONTyped(json, false); +} + +export function CreateAssistantRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateAssistantRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'instructions': !exists(json, 'instructions') ? undefined : json['instructions'], + 'metadata': !exists(json, 'metadata') ? undefined : json['metadata'], + 'region': !exists(json, 'region') ? undefined : json['region'], + }; +} + +export function CreateAssistantRequestToJSON(value?: CreateAssistantRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'instructions': value.instructions, + 'metadata': value.metadata, + 'region': value.region, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponse.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponse.ts new file mode 100644 index 00000000..ffed9d48 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponse.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ErrorResponseError } from './ErrorResponseError'; +import { + ErrorResponseErrorFromJSON, + ErrorResponseErrorFromJSONTyped, + ErrorResponseErrorToJSON, +} from './ErrorResponseError'; + +/** + * The response shape used for all error responses. + * @export + * @interface ErrorResponse + */ +export interface ErrorResponse { + /** + * The HTTP status code of the error. + * @type {number} + * @memberof ErrorResponse + */ + status: number; + /** + * + * @type {ErrorResponseError} + * @memberof ErrorResponse + */ + error: ErrorResponseError; +} + +/** + * Check if a given object implements the ErrorResponse interface. + */ +export function instanceOfErrorResponse(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "status" in value; + isInstance = isInstance && "error" in value; + + return isInstance; +} + +export function ErrorResponseFromJSON(json: any): ErrorResponse { + return ErrorResponseFromJSONTyped(json, false); +} + +export function ErrorResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'status': json['status'], + 'error': ErrorResponseErrorFromJSON(json['error']), + }; +} + +export function ErrorResponseToJSON(value?: ErrorResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'status': value.status, + 'error': ErrorResponseErrorToJSON(value.error), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponseError.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponseError.ts new file mode 100644 index 00000000..7f3f0814 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/ErrorResponseError.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Detailed information about the error that occurred. + * @export + * @interface ErrorResponseError + */ +export interface ErrorResponseError { + /** + * + * @type {string} + * @memberof ErrorResponseError + */ + code: ErrorResponseErrorCodeEnum; + /** + * + * @type {string} + * @memberof ErrorResponseError + */ + message: string; + /** + * Additional information about the error. This field is not guaranteed to be present. + * @type {object} + * @memberof ErrorResponseError + */ + details?: object; +} + + +/** + * @export + */ +export const ErrorResponseErrorCodeEnum = { + Ok: 'OK', + Unknown: 'UNKNOWN', + InvalidArgument: 'INVALID_ARGUMENT', + DeadlineExceeded: 'DEADLINE_EXCEEDED', + QuotaExceeded: 'QUOTA_EXCEEDED', + NotFound: 'NOT_FOUND', + AlreadyExists: 'ALREADY_EXISTS', + PermissionDenied: 'PERMISSION_DENIED', + Unauthenticated: 'UNAUTHENTICATED', + ResourceExhausted: 'RESOURCE_EXHAUSTED', + FailedPrecondition: 'FAILED_PRECONDITION', + Aborted: 'ABORTED', + OutOfRange: 'OUT_OF_RANGE', + Unimplemented: 'UNIMPLEMENTED', + Internal: 'INTERNAL', + Unavailable: 'UNAVAILABLE', + DataLoss: 'DATA_LOSS', + Forbidden: 'FORBIDDEN' +} as const; +export type ErrorResponseErrorCodeEnum = typeof ErrorResponseErrorCodeEnum[keyof typeof ErrorResponseErrorCodeEnum]; + + +/** + * Check if a given object implements the ErrorResponseError interface. + */ +export function instanceOfErrorResponseError(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "code" in value; + isInstance = isInstance && "message" in value; + + return isInstance; +} + +export function ErrorResponseErrorFromJSON(json: any): ErrorResponseError { + return ErrorResponseErrorFromJSONTyped(json, false); +} + +export function ErrorResponseErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponseError { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'code': json['code'], + 'message': json['message'], + 'details': !exists(json, 'details') ? undefined : json['details'], + }; +} + +export function ErrorResponseErrorToJSON(value?: ErrorResponseError | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'code': value.code, + 'message': value.message, + 'details': value.details, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/ListAssistants200Response.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/ListAssistants200Response.ts new file mode 100644 index 00000000..9d7e8e7d --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/ListAssistants200Response.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { Assistant } from './Assistant'; +import { + AssistantFromJSON, + AssistantFromJSONTyped, + AssistantToJSON, +} from './Assistant'; + +/** + * The list of assistants that exist in the project. + * @export + * @interface ListAssistants200Response + */ +export interface ListAssistants200Response { + /** + * + * @type {Array} + * @memberof ListAssistants200Response + */ + assistants?: Array; +} + +/** + * Check if a given object implements the ListAssistants200Response interface. + */ +export function instanceOfListAssistants200Response(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ListAssistants200ResponseFromJSON(json: any): ListAssistants200Response { + return ListAssistants200ResponseFromJSONTyped(json, false); +} + +export function ListAssistants200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListAssistants200Response { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'assistants': !exists(json, 'assistants') ? undefined : ((json['assistants'] as Array).map(AssistantFromJSON)), + }; +} + +export function ListAssistants200ResponseToJSON(value?: ListAssistants200Response | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'assistants': value.assistants === undefined ? undefined : ((value.assistants as Array).map(AssistantToJSON)), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistant200Response.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistant200Response.ts new file mode 100644 index 00000000..c509d1c9 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistant200Response.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface UpdateAssistant200Response + */ +export interface UpdateAssistant200Response { + /** + * + * @type {string} + * @memberof UpdateAssistant200Response + */ + assistantName?: string; + /** + * Description or directive for the assistant to apply to all responses. + * @type {string} + * @memberof UpdateAssistant200Response + */ + instructions?: string; + /** + * + * @type {object} + * @memberof UpdateAssistant200Response + */ + metadata?: object; +} + +/** + * Check if a given object implements the UpdateAssistant200Response interface. + */ +export function instanceOfUpdateAssistant200Response(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function UpdateAssistant200ResponseFromJSON(json: any): UpdateAssistant200Response { + return UpdateAssistant200ResponseFromJSONTyped(json, false); +} + +export function UpdateAssistant200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateAssistant200Response { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'assistantName': !exists(json, 'assistant_name') ? undefined : json['assistant_name'], + 'instructions': !exists(json, 'instructions') ? undefined : json['instructions'], + 'metadata': !exists(json, 'metadata') ? undefined : json['metadata'], + }; +} + +export function UpdateAssistant200ResponseToJSON(value?: UpdateAssistant200Response | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'assistant_name': value.assistantName, + 'instructions': value.instructions, + 'metadata': value.metadata, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistantRequest.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistantRequest.ts new file mode 100644 index 00000000..1729f17e --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/UpdateAssistantRequest.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The configuration updates for the assistant. + * @export + * @interface UpdateAssistantRequest + */ +export interface UpdateAssistantRequest { + /** + * Description or directive for the assistant to apply to all responses. + * @type {string} + * @memberof UpdateAssistantRequest + */ + instructions?: string | null; + /** + * + * @type {object} + * @memberof UpdateAssistantRequest + */ + metadata?: object | null; +} + +/** + * Check if a given object implements the UpdateAssistantRequest interface. + */ +export function instanceOfUpdateAssistantRequest(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function UpdateAssistantRequestFromJSON(json: any): UpdateAssistantRequest { + return UpdateAssistantRequestFromJSONTyped(json, false); +} + +export function UpdateAssistantRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateAssistantRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'instructions': !exists(json, 'instructions') ? undefined : json['instructions'], + 'metadata': !exists(json, 'metadata') ? undefined : json['metadata'], + }; +} + +export function UpdateAssistantRequestToJSON(value?: UpdateAssistantRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'instructions': value.instructions, + 'metadata': value.metadata, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_control/models/index.ts b/src/pinecone-generated-ts-fetch/assistant_control/models/index.ts new file mode 100644 index 00000000..8d3c021c --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/models/index.ts @@ -0,0 +1,9 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './Assistant'; +export * from './CreateAssistantRequest'; +export * from './ErrorResponse'; +export * from './ErrorResponseError'; +export * from './ListAssistants200Response'; +export * from './UpdateAssistant200Response'; +export * from './UpdateAssistantRequest'; diff --git a/src/pinecone-generated-ts-fetch/assistant_control/runtime.ts b/src/pinecone-generated-ts-fetch/assistant_control/runtime.ts new file mode 100644 index 00000000..df0a4283 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_control/runtime.ts @@ -0,0 +1,431 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Control Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports creating and managing assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "https://api.pinecone.io/assistant".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/src/pinecone-generated-ts-fetch/assistant_data/api_version.ts b/src/pinecone-generated-ts-fetch/assistant_data/api_version.ts new file mode 100644 index 00000000..87013d2b --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/api_version.ts @@ -0,0 +1 @@ +export const X_PINECONE_API_VERSION = '2025-01'; diff --git a/src/pinecone-generated-ts-fetch/assistant_data/apis/ManageAssistantsApi.ts b/src/pinecone-generated-ts-fetch/assistant_data/apis/ManageAssistantsApi.ts new file mode 100644 index 00000000..0bbd991e --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/apis/ManageAssistantsApi.ts @@ -0,0 +1,420 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + AssistantFileModel, + Chat, + ChatCompletionAssistant200Response, + ChatCompletionModel, + ChatModel, + ContextModel, + ContextRequest, + ErrorResponse, + ListFiles200Response, + SearchCompletions, +} from '../models/index'; +import { + AssistantFileModelFromJSON, + AssistantFileModelToJSON, + ChatFromJSON, + ChatToJSON, + ChatCompletionAssistant200ResponseFromJSON, + ChatCompletionAssistant200ResponseToJSON, + ChatCompletionModelFromJSON, + ChatCompletionModelToJSON, + ChatModelFromJSON, + ChatModelToJSON, + ContextModelFromJSON, + ContextModelToJSON, + ContextRequestFromJSON, + ContextRequestToJSON, + ErrorResponseFromJSON, + ErrorResponseToJSON, + ListFiles200ResponseFromJSON, + ListFiles200ResponseToJSON, + SearchCompletionsFromJSON, + SearchCompletionsToJSON, +} from '../models/index'; + +export interface ChatAssistantRequest { + assistantName: string; + chat: Chat; +} + +export interface ChatCompletionAssistantRequest { + assistantName: string; + searchCompletions: SearchCompletions; +} + +export interface ContextAssistantRequest { + assistantName: string; + contextRequest: ContextRequest; +} + +export interface DeleteFileRequest { + assistantName: string; + assistantFileId: string; +} + +export interface DescribeFileRequest { + assistantName: string; + assistantFileId: string; + includeUrl?: DescribeFileIncludeUrlEnum; +} + +export interface ListFilesRequest { + assistantName: string; + filter?: string; +} + +export interface UploadFileRequest { + assistantName: string; + file: Blob; + metadata?: string; +} + +/** + * + */ +export class ManageAssistantsApi extends runtime.BaseAPI { + + /** + * The `chat_assistant` endpoint allows you to [chat with an assistant](https://docs.pinecone.io/guides/assistant/chat-with-assistant) and get back citations in structured form. This is the recommended way to chat with an assistant, as it offers more functionality and control over the assistant\'s responses and references than the `chat_completion_assistant` endpoint. + * Chat with an assistant + */ + async chatAssistantRaw(requestParameters: ChatAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling chatAssistant.'); + } + + if (requestParameters.chat === null || requestParameters.chat === undefined) { + throw new runtime.RequiredError('chat','Required parameter requestParameters.chat was null or undefined when calling chatAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/chat/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ChatToJSON(requestParameters.chat), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ChatModelFromJSON(jsonValue)); + } + + /** + * The `chat_assistant` endpoint allows you to [chat with an assistant](https://docs.pinecone.io/guides/assistant/chat-with-assistant) and get back citations in structured form. This is the recommended way to chat with an assistant, as it offers more functionality and control over the assistant\'s responses and references than the `chat_completion_assistant` endpoint. + * Chat with an assistant + */ + async chatAssistant(requestParameters: ChatAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.chatAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `chat_completion_assistant` endpoint is used to [chat with an assistant](https://docs.pinecone.io/guides/assistant/chat-with-assistant). This endpoint is based on the OpenAI Chat Completion API, a commonly used and adopted API. It is useful if you need inline citations or OpenAI-compatible responses, but has limited functionality compared to the [`chat_assistant`](https://docs.pinecone.io/reference/api/2024-07/assistant/chat_assistant) operation. + * Chat through an OpenAI-compatible interface + */ + async chatCompletionAssistantRaw(requestParameters: ChatCompletionAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling chatCompletionAssistant.'); + } + + if (requestParameters.searchCompletions === null || requestParameters.searchCompletions === undefined) { + throw new runtime.RequiredError('searchCompletions','Required parameter requestParameters.searchCompletions was null or undefined when calling chatCompletionAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/chat/{assistant_name}/chat/completions`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: SearchCompletionsToJSON(requestParameters.searchCompletions), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ChatCompletionModelFromJSON(jsonValue)); + } + + /** + * The `chat_completion_assistant` endpoint is used to [chat with an assistant](https://docs.pinecone.io/guides/assistant/chat-with-assistant). This endpoint is based on the OpenAI Chat Completion API, a commonly used and adopted API. It is useful if you need inline citations or OpenAI-compatible responses, but has limited functionality compared to the [`chat_assistant`](https://docs.pinecone.io/reference/api/2024-07/assistant/chat_assistant) operation. + * Chat through an OpenAI-compatible interface + */ + async chatCompletionAssistant(requestParameters: ChatCompletionAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.chatCompletionAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `context_assistant` endpoint allows you to retrieve context from an assistant that might be used as part of RAG or any agentic flow. + * Retrieve context from an assistant + */ + async contextAssistantRaw(requestParameters: ContextAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling contextAssistant.'); + } + + if (requestParameters.contextRequest === null || requestParameters.contextRequest === undefined) { + throw new runtime.RequiredError('contextRequest','Required parameter requestParameters.contextRequest was null or undefined when calling contextAssistant.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/chat/{assistant_name}/context`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ContextRequestToJSON(requestParameters.contextRequest), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ContextModelFromJSON(jsonValue)); + } + + /** + * The `context_assistant` endpoint allows you to retrieve context from an assistant that might be used as part of RAG or any agentic flow. + * Retrieve context from an assistant + */ + async contextAssistant(requestParameters: ContextAssistantRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.contextAssistantRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `delete_file` endpoint [deletes an uploaded file](https://docs.pinecone.io/guides/assistant/manage-files#delete-a-file) from an assistant. + * Delete an uploaded file + */ + async deleteFileRaw(requestParameters: DeleteFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling deleteFile.'); + } + + if (requestParameters.assistantFileId === null || requestParameters.assistantFileId === undefined) { + throw new runtime.RequiredError('assistantFileId','Required parameter requestParameters.assistantFileId was null or undefined when calling deleteFile.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/files/{assistant_name}/{assistant_file_id}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))).replace(`{${"assistant_file_id"}}`, encodeURIComponent(String(requestParameters.assistantFileId))), + method: 'DELETE', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + * The `delete_file` endpoint [deletes an uploaded file](https://docs.pinecone.io/guides/assistant/manage-files#delete-a-file) from an assistant. + * Delete an uploaded file + */ + async deleteFile(requestParameters: DeleteFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + await this.deleteFileRaw(requestParameters, initOverrides); + } + + /** + * The `describe_file` endpoint provides the [current status and metadata of a file](https://docs.pinecone.io/guides/assistant/manage-files#get-the-status-of-a-file) uploaded to an assistant. + * Describe a file upload + */ + async describeFileRaw(requestParameters: DescribeFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling describeFile.'); + } + + if (requestParameters.assistantFileId === null || requestParameters.assistantFileId === undefined) { + throw new runtime.RequiredError('assistantFileId','Required parameter requestParameters.assistantFileId was null or undefined when calling describeFile.'); + } + + const queryParameters: any = {}; + + if (requestParameters.includeUrl !== undefined) { + queryParameters['include_url'] = requestParameters.includeUrl; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/files/{assistant_name}/{assistant_file_id}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))).replace(`{${"assistant_file_id"}}`, encodeURIComponent(String(requestParameters.assistantFileId))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AssistantFileModelFromJSON(jsonValue)); + } + + /** + * The `describe_file` endpoint provides the [current status and metadata of a file](https://docs.pinecone.io/guides/assistant/manage-files#get-the-status-of-a-file) uploaded to an assistant. + * Describe a file upload + */ + async describeFile(requestParameters: DescribeFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.describeFileRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `list_files` endpoint returns a [list of all files in an assistant](https://docs.pinecone.io//guides/assistant/manage-files#list-files-in-an-assistant), with an option to filter files with metadata. + * List Files + */ + async listFilesRaw(requestParameters: ListFilesRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling listFiles.'); + } + + const queryParameters: any = {}; + + if (requestParameters.filter !== undefined) { + queryParameters['filter'] = requestParameters.filter; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/files/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ListFiles200ResponseFromJSON(jsonValue)); + } + + /** + * The `list_files` endpoint returns a [list of all files in an assistant](https://docs.pinecone.io//guides/assistant/manage-files#list-files-in-an-assistant), with an option to filter files with metadata. + * List Files + */ + async listFiles(requestParameters: ListFilesRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listFilesRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * The `upload_file` endpoint [uploads a file](https://docs.pinecone.io/guides/assistant/upload-file) to the specified assistant. + * Upload file to assistant + */ + async uploadFileRaw(requestParameters: UploadFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.assistantName === null || requestParameters.assistantName === undefined) { + throw new runtime.RequiredError('assistantName','Required parameter requestParameters.assistantName was null or undefined when calling uploadFile.'); + } + + if (requestParameters.file === null || requestParameters.file === undefined) { + throw new runtime.RequiredError('file','Required parameter requestParameters.file was null or undefined when calling uploadFile.'); + } + + const queryParameters: any = {}; + + if (requestParameters.metadata !== undefined) { + queryParameters['metadata'] = requestParameters.metadata; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const consumes: runtime.Consume[] = [ + { contentType: 'multipart/form-data' }, + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + // use FormData to transmit files using content-type "multipart/form-data" + useForm = canConsumeForm; + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + if (requestParameters.file !== undefined) { + formParams.append('file', requestParameters.file as any); + } + + const response = await this.request({ + path: `/files/{assistant_name}`.replace(`{${"assistant_name"}}`, encodeURIComponent(String(requestParameters.assistantName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: formParams, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AssistantFileModelFromJSON(jsonValue)); + } + + /** + * The `upload_file` endpoint [uploads a file](https://docs.pinecone.io/guides/assistant/upload-file) to the specified assistant. + * Upload file to assistant + */ + async uploadFile(requestParameters: UploadFileRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.uploadFileRaw(requestParameters, initOverrides); + return await response.value(); + } + +} + +/** + * @export + */ +export const DescribeFileIncludeUrlEnum = { + True: 'true', + False: 'false' +} as const; +export type DescribeFileIncludeUrlEnum = typeof DescribeFileIncludeUrlEnum[keyof typeof DescribeFileIncludeUrlEnum]; diff --git a/src/pinecone-generated-ts-fetch/assistant_data/apis/index.ts b/src/pinecone-generated-ts-fetch/assistant_data/apis/index.ts new file mode 100644 index 00000000..c2756ab4 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './ManageAssistantsApi'; diff --git a/src/pinecone-generated-ts-fetch/assistant_data/index.ts b/src/pinecone-generated-ts-fetch/assistant_data/index.ts new file mode 100644 index 00000000..91e0ae58 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/index.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; +export * from './api_version'; diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/AssistantFileModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/AssistantFileModel.ts new file mode 100644 index 00000000..2d8dc6ec --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/AssistantFileModel.ts @@ -0,0 +1,144 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * AssistantFileModel is the response format to a successful file upload request. + * @export + * @interface AssistantFileModel + */ +export interface AssistantFileModel { + /** + * + * @type {string} + * @memberof AssistantFileModel + */ + name: string; + /** + * + * @type {string} + * @memberof AssistantFileModel + */ + id: string; + /** + * + * @type {object} + * @memberof AssistantFileModel + */ + metadata?: object | null; + /** + * + * @type {Date} + * @memberof AssistantFileModel + */ + createdOn?: Date; + /** + * + * @type {Date} + * @memberof AssistantFileModel + */ + updatedOn?: Date; + /** + * + * @type {string} + * @memberof AssistantFileModel + */ + status?: AssistantFileModelStatusEnum; + /** + * The percentage of the file that has been processed + * @type {number} + * @memberof AssistantFileModel + */ + percentDone?: number | null; + /** + * A signed url that gives you access to the underlying file + * @type {string} + * @memberof AssistantFileModel + */ + signedUrl?: string | null; + /** + * A message describing any error during file processing, provided only if an error occurs. + * @type {string} + * @memberof AssistantFileModel + */ + errorMessage?: string | null; +} + + +/** + * @export + */ +export const AssistantFileModelStatusEnum = { + Processing: 'Processing', + Available: 'Available', + Deleting: 'Deleting', + ProcessingFailed: 'ProcessingFailed' +} as const; +export type AssistantFileModelStatusEnum = typeof AssistantFileModelStatusEnum[keyof typeof AssistantFileModelStatusEnum]; + + +/** + * Check if a given object implements the AssistantFileModel interface. + */ +export function instanceOfAssistantFileModel(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "name" in value; + isInstance = isInstance && "id" in value; + + return isInstance; +} + +export function AssistantFileModelFromJSON(json: any): AssistantFileModel { + return AssistantFileModelFromJSONTyped(json, false); +} + +export function AssistantFileModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): AssistantFileModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'id': json['id'], + 'metadata': !exists(json, 'metadata') ? undefined : json['metadata'], + 'createdOn': !exists(json, 'created_on') ? undefined : (new Date(json['created_on'])), + 'updatedOn': !exists(json, 'updated_on') ? undefined : (new Date(json['updated_on'])), + 'status': !exists(json, 'status') ? undefined : json['status'], + 'percentDone': !exists(json, 'percent_done') ? undefined : json['percent_done'], + 'signedUrl': !exists(json, 'signed_url') ? undefined : json['signed_url'], + 'errorMessage': !exists(json, 'error_message') ? undefined : json['error_message'], + }; +} + +export function AssistantFileModelToJSON(value?: AssistantFileModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'id': value.id, + 'metadata': value.metadata, + 'created_on': value.createdOn === undefined ? undefined : (value.createdOn.toISOString()), + 'updated_on': value.updatedOn === undefined ? undefined : (value.updatedOn.toISOString()), + 'status': value.status, + 'percent_done': value.percentDone, + 'signed_url': value.signedUrl, + 'error_message': value.errorMessage, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/Chat.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/Chat.ts new file mode 100644 index 00000000..60dff5d5 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/Chat.ts @@ -0,0 +1,116 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { MessageModel } from './MessageModel'; +import { + MessageModelFromJSON, + MessageModelFromJSONTyped, + MessageModelToJSON, +} from './MessageModel'; + +/** + * The list of queries / chats to chat an assistant + * @export + * @interface Chat + */ +export interface Chat { + /** + * + * @type {Array} + * @memberof Chat + */ + messages: Array; + /** + * If false, the assistant will return a single JSON response. If true, the assistant will return a stream of responses. + * @type {boolean} + * @memberof Chat + */ + stream?: boolean; + /** + * The large language model to use for answer generation + * @type {string} + * @memberof Chat + */ + model?: ChatModelEnum; + /** + * Optionally filter which documents can be retrieved using the following metadata fields. + * @type {object} + * @memberof Chat + */ + filter?: object; + /** + * If true, the assistant will be instructed to return a JSON response. Cannot be used with streaming. + * @type {boolean} + * @memberof Chat + */ + jsonResponse?: boolean; +} + + +/** + * @export + */ +export const ChatModelEnum = { + Gpt4o: 'gpt-4o', + Claude35Sonnet: 'claude-3-5-sonnet' +} as const; +export type ChatModelEnum = typeof ChatModelEnum[keyof typeof ChatModelEnum]; + + +/** + * Check if a given object implements the Chat interface. + */ +export function instanceOfChat(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "messages" in value; + + return isInstance; +} + +export function ChatFromJSON(json: any): Chat { + return ChatFromJSONTyped(json, false); +} + +export function ChatFromJSONTyped(json: any, ignoreDiscriminator: boolean): Chat { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'messages': ((json['messages'] as Array).map(MessageModelFromJSON)), + 'stream': !exists(json, 'stream') ? undefined : json['stream'], + 'model': !exists(json, 'model') ? undefined : json['model'], + 'filter': !exists(json, 'filter') ? undefined : json['filter'], + 'jsonResponse': !exists(json, 'json_response') ? undefined : json['json_response'], + }; +} + +export function ChatToJSON(value?: Chat | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'messages': ((value.messages as Array).map(MessageModelToJSON)), + 'stream': value.stream, + 'model': value.model, + 'filter': value.filter, + 'json_response': value.jsonResponse, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200Response.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200Response.ts new file mode 100644 index 00000000..c8422d1a --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200Response.ts @@ -0,0 +1,88 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ChatCompletionAssistant200ResponseChoicesInner } from './ChatCompletionAssistant200ResponseChoicesInner'; +import { + ChatCompletionAssistant200ResponseChoicesInnerFromJSON, + ChatCompletionAssistant200ResponseChoicesInnerFromJSONTyped, + ChatCompletionAssistant200ResponseChoicesInnerToJSON, +} from './ChatCompletionAssistant200ResponseChoicesInner'; + +/** + * The ChatCompletionModel describes the response format of a chat request. + * @export + * @interface ChatCompletionAssistant200Response + */ +export interface ChatCompletionAssistant200Response { + /** + * + * @type {string} + * @memberof ChatCompletionAssistant200Response + */ + id?: string; + /** + * + * @type {Array} + * @memberof ChatCompletionAssistant200Response + */ + choices?: Array; + /** + * + * @type {string} + * @memberof ChatCompletionAssistant200Response + */ + model?: string; +} + +/** + * Check if a given object implements the ChatCompletionAssistant200Response interface. + */ +export function instanceOfChatCompletionAssistant200Response(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChatCompletionAssistant200ResponseFromJSON(json: any): ChatCompletionAssistant200Response { + return ChatCompletionAssistant200ResponseFromJSONTyped(json, false); +} + +export function ChatCompletionAssistant200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatCompletionAssistant200Response { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'id': !exists(json, 'id') ? undefined : json['id'], + 'choices': !exists(json, 'choices') ? undefined : ((json['choices'] as Array).map(ChatCompletionAssistant200ResponseChoicesInnerFromJSON)), + 'model': !exists(json, 'model') ? undefined : json['model'], + }; +} + +export function ChatCompletionAssistant200ResponseToJSON(value?: ChatCompletionAssistant200Response | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'id': value.id, + 'choices': value.choices === undefined ? undefined : ((value.choices as Array).map(ChatCompletionAssistant200ResponseChoicesInnerToJSON)), + 'model': value.model, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInner.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInner.ts new file mode 100644 index 00000000..85a29bb8 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInner.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ChatCompletionAssistant200ResponseChoicesInnerDelta } from './ChatCompletionAssistant200ResponseChoicesInnerDelta'; +import { + ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSON, + ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSONTyped, + ChatCompletionAssistant200ResponseChoicesInnerDeltaToJSON, +} from './ChatCompletionAssistant200ResponseChoicesInnerDelta'; + +/** + * The ChoiceChunkModel describes a single choice in a chat completion response. + * @export + * @interface ChatCompletionAssistant200ResponseChoicesInner + */ +export interface ChatCompletionAssistant200ResponseChoicesInner { + /** + * + * @type {string} + * @memberof ChatCompletionAssistant200ResponseChoicesInner + */ + finishReason?: ChatCompletionAssistant200ResponseChoicesInnerFinishReasonEnum; + /** + * + * @type {number} + * @memberof ChatCompletionAssistant200ResponseChoicesInner + */ + index?: number; + /** + * + * @type {ChatCompletionAssistant200ResponseChoicesInnerDelta} + * @memberof ChatCompletionAssistant200ResponseChoicesInner + */ + delta?: ChatCompletionAssistant200ResponseChoicesInnerDelta; +} + + +/** + * @export + */ +export const ChatCompletionAssistant200ResponseChoicesInnerFinishReasonEnum = { + Stop: 'stop', + Length: 'length', + ContentFilter: 'content_filter', + FunctionCall: 'function_call' +} as const; +export type ChatCompletionAssistant200ResponseChoicesInnerFinishReasonEnum = typeof ChatCompletionAssistant200ResponseChoicesInnerFinishReasonEnum[keyof typeof ChatCompletionAssistant200ResponseChoicesInnerFinishReasonEnum]; + + +/** + * Check if a given object implements the ChatCompletionAssistant200ResponseChoicesInner interface. + */ +export function instanceOfChatCompletionAssistant200ResponseChoicesInner(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChatCompletionAssistant200ResponseChoicesInnerFromJSON(json: any): ChatCompletionAssistant200ResponseChoicesInner { + return ChatCompletionAssistant200ResponseChoicesInnerFromJSONTyped(json, false); +} + +export function ChatCompletionAssistant200ResponseChoicesInnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatCompletionAssistant200ResponseChoicesInner { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'finishReason': !exists(json, 'finish_reason') ? undefined : json['finish_reason'], + 'index': !exists(json, 'index') ? undefined : json['index'], + 'delta': !exists(json, 'delta') ? undefined : ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSON(json['delta']), + }; +} + +export function ChatCompletionAssistant200ResponseChoicesInnerToJSON(value?: ChatCompletionAssistant200ResponseChoicesInner | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'finish_reason': value.finishReason, + 'index': value.index, + 'delta': ChatCompletionAssistant200ResponseChoicesInnerDeltaToJSON(value.delta), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInnerDelta.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInnerDelta.ts new file mode 100644 index 00000000..ca82032e --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionAssistant200ResponseChoicesInnerDelta.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Chat completion message + * @export + * @interface ChatCompletionAssistant200ResponseChoicesInnerDelta + */ +export interface ChatCompletionAssistant200ResponseChoicesInnerDelta { + /** + * + * @type {string} + * @memberof ChatCompletionAssistant200ResponseChoicesInnerDelta + */ + role?: string; + /** + * + * @type {string} + * @memberof ChatCompletionAssistant200ResponseChoicesInnerDelta + */ + content?: string; +} + +/** + * Check if a given object implements the ChatCompletionAssistant200ResponseChoicesInnerDelta interface. + */ +export function instanceOfChatCompletionAssistant200ResponseChoicesInnerDelta(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSON(json: any): ChatCompletionAssistant200ResponseChoicesInnerDelta { + return ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSONTyped(json, false); +} + +export function ChatCompletionAssistant200ResponseChoicesInnerDeltaFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatCompletionAssistant200ResponseChoicesInnerDelta { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'role': !exists(json, 'role') ? undefined : json['role'], + 'content': !exists(json, 'content') ? undefined : json['content'], + }; +} + +export function ChatCompletionAssistant200ResponseChoicesInnerDeltaToJSON(value?: ChatCompletionAssistant200ResponseChoicesInnerDelta | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'role': value.role, + 'content': value.content, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionModel.ts new file mode 100644 index 00000000..b993b87c --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatCompletionModel.ts @@ -0,0 +1,102 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ChoiceModel } from './ChoiceModel'; +import { + ChoiceModelFromJSON, + ChoiceModelFromJSONTyped, + ChoiceModelToJSON, +} from './ChoiceModel'; +import type { UsageModel } from './UsageModel'; +import { + UsageModelFromJSON, + UsageModelFromJSONTyped, + UsageModelToJSON, +} from './UsageModel'; + +/** + * The ChatCompletionModel describes the response format of a chat request. + * @export + * @interface ChatCompletionModel + */ +export interface ChatCompletionModel { + /** + * + * @type {string} + * @memberof ChatCompletionModel + */ + id?: string; + /** + * + * @type {Array} + * @memberof ChatCompletionModel + */ + choices?: Array; + /** + * + * @type {string} + * @memberof ChatCompletionModel + */ + model?: string; + /** + * + * @type {UsageModel} + * @memberof ChatCompletionModel + */ + usage?: UsageModel; +} + +/** + * Check if a given object implements the ChatCompletionModel interface. + */ +export function instanceOfChatCompletionModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChatCompletionModelFromJSON(json: any): ChatCompletionModel { + return ChatCompletionModelFromJSONTyped(json, false); +} + +export function ChatCompletionModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatCompletionModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'id': !exists(json, 'id') ? undefined : json['id'], + 'choices': !exists(json, 'choices') ? undefined : ((json['choices'] as Array).map(ChoiceModelFromJSON)), + 'model': !exists(json, 'model') ? undefined : json['model'], + 'usage': !exists(json, 'usage') ? undefined : UsageModelFromJSON(json['usage']), + }; +} + +export function ChatCompletionModelToJSON(value?: ChatCompletionModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'id': value.id, + 'choices': value.choices === undefined ? undefined : ((value.choices as Array).map(ChoiceModelToJSON)), + 'model': value.model, + 'usage': UsageModelToJSON(value.usage), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChatModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatModel.ts new file mode 100644 index 00000000..c687105b --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChatModel.ts @@ -0,0 +1,137 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { CitationModel } from './CitationModel'; +import { + CitationModelFromJSON, + CitationModelFromJSONTyped, + CitationModelToJSON, +} from './CitationModel'; +import type { MessageModel } from './MessageModel'; +import { + MessageModelFromJSON, + MessageModelFromJSONTyped, + MessageModelToJSON, +} from './MessageModel'; +import type { UsageModel } from './UsageModel'; +import { + UsageModelFromJSON, + UsageModelFromJSONTyped, + UsageModelToJSON, +} from './UsageModel'; + +/** + * The ChatModel describes the response format of a chat request from the citation api. + * @export + * @interface ChatModel + */ +export interface ChatModel { + /** + * + * @type {string} + * @memberof ChatModel + */ + id?: string; + /** + * + * @type {string} + * @memberof ChatModel + */ + finishReason?: ChatModelFinishReasonEnum; + /** + * + * @type {MessageModel} + * @memberof ChatModel + */ + message?: MessageModel; + /** + * + * @type {string} + * @memberof ChatModel + */ + model?: string; + /** + * + * @type {Array} + * @memberof ChatModel + */ + citations?: Array; + /** + * + * @type {UsageModel} + * @memberof ChatModel + */ + usage?: UsageModel; +} + + +/** + * @export + */ +export const ChatModelFinishReasonEnum = { + Stop: 'stop', + Length: 'length', + ContentFilter: 'content_filter', + FunctionCall: 'function_call' +} as const; +export type ChatModelFinishReasonEnum = typeof ChatModelFinishReasonEnum[keyof typeof ChatModelFinishReasonEnum]; + + +/** + * Check if a given object implements the ChatModel interface. + */ +export function instanceOfChatModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChatModelFromJSON(json: any): ChatModel { + return ChatModelFromJSONTyped(json, false); +} + +export function ChatModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'id': !exists(json, 'id') ? undefined : json['id'], + 'finishReason': !exists(json, 'finish_reason') ? undefined : json['finish_reason'], + 'message': !exists(json, 'message') ? undefined : MessageModelFromJSON(json['message']), + 'model': !exists(json, 'model') ? undefined : json['model'], + 'citations': !exists(json, 'citations') ? undefined : ((json['citations'] as Array).map(CitationModelFromJSON)), + 'usage': !exists(json, 'usage') ? undefined : UsageModelFromJSON(json['usage']), + }; +} + +export function ChatModelToJSON(value?: ChatModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'id': value.id, + 'finish_reason': value.finishReason, + 'message': MessageModelToJSON(value.message), + 'model': value.model, + 'citations': value.citations === undefined ? undefined : ((value.citations as Array).map(CitationModelToJSON)), + 'usage': UsageModelToJSON(value.usage), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ChoiceModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ChoiceModel.ts new file mode 100644 index 00000000..185e6b7a --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ChoiceModel.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { MessageModel } from './MessageModel'; +import { + MessageModelFromJSON, + MessageModelFromJSONTyped, + MessageModelToJSON, +} from './MessageModel'; + +/** + * The ChoiceModel describes a single choice in a chat completion response + * @export + * @interface ChoiceModel + */ +export interface ChoiceModel { + /** + * + * @type {string} + * @memberof ChoiceModel + */ + finishReason?: ChoiceModelFinishReasonEnum; + /** + * + * @type {number} + * @memberof ChoiceModel + */ + index?: number; + /** + * + * @type {MessageModel} + * @memberof ChoiceModel + */ + message?: MessageModel; +} + + +/** + * @export + */ +export const ChoiceModelFinishReasonEnum = { + Stop: 'stop', + Length: 'length', + ContentFilter: 'content_filter', + FunctionCall: 'function_call' +} as const; +export type ChoiceModelFinishReasonEnum = typeof ChoiceModelFinishReasonEnum[keyof typeof ChoiceModelFinishReasonEnum]; + + +/** + * Check if a given object implements the ChoiceModel interface. + */ +export function instanceOfChoiceModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChoiceModelFromJSON(json: any): ChoiceModel { + return ChoiceModelFromJSONTyped(json, false); +} + +export function ChoiceModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChoiceModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'finishReason': !exists(json, 'finish_reason') ? undefined : json['finish_reason'], + 'index': !exists(json, 'index') ? undefined : json['index'], + 'message': !exists(json, 'message') ? undefined : MessageModelFromJSON(json['message']), + }; +} + +export function ChoiceModelToJSON(value?: ChoiceModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'finish_reason': value.finishReason, + 'index': value.index, + 'message': MessageModelToJSON(value.message), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/CitationModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/CitationModel.ts new file mode 100644 index 00000000..615ad072 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/CitationModel.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ReferenceModel } from './ReferenceModel'; +import { + ReferenceModelFromJSON, + ReferenceModelFromJSONTyped, + ReferenceModelToJSON, +} from './ReferenceModel'; + +/** + * The CitationModel describes a single cited source returned by a chat request. + * @export + * @interface CitationModel + */ +export interface CitationModel { + /** + * The index position of the citation in the complete text response. + * @type {number} + * @memberof CitationModel + */ + position?: number; + /** + * + * @type {Array} + * @memberof CitationModel + */ + references?: Array; +} + +/** + * Check if a given object implements the CitationModel interface. + */ +export function instanceOfCitationModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function CitationModelFromJSON(json: any): CitationModel { + return CitationModelFromJSONTyped(json, false); +} + +export function CitationModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): CitationModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'position': !exists(json, 'position') ? undefined : json['position'], + 'references': !exists(json, 'references') ? undefined : ((json['references'] as Array).map(ReferenceModelFromJSON)), + }; +} + +export function CitationModelToJSON(value?: CitationModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'position': value.position, + 'references': value.references === undefined ? undefined : ((value.references as Array).map(ReferenceModelToJSON)), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ContextModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ContextModel.ts new file mode 100644 index 00000000..110336cb --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ContextModel.ts @@ -0,0 +1,88 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { SnippetModel } from './SnippetModel'; +import { + SnippetModelFromJSON, + SnippetModelFromJSONTyped, + SnippetModelToJSON, +} from './SnippetModel'; +import type { UsageModel } from './UsageModel'; +import { + UsageModelFromJSON, + UsageModelFromJSONTyped, + UsageModelToJSON, +} from './UsageModel'; + +/** + * The response format containing the context from an assistant. + * @export + * @interface ContextModel + */ +export interface ContextModel { + /** + * + * @type {Array} + * @memberof ContextModel + */ + snippets: Array; + /** + * + * @type {UsageModel} + * @memberof ContextModel + */ + usage: UsageModel; +} + +/** + * Check if a given object implements the ContextModel interface. + */ +export function instanceOfContextModel(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "snippets" in value; + isInstance = isInstance && "usage" in value; + + return isInstance; +} + +export function ContextModelFromJSON(json: any): ContextModel { + return ContextModelFromJSONTyped(json, false); +} + +export function ContextModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ContextModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'snippets': ((json['snippets'] as Array).map(SnippetModelFromJSON)), + 'usage': UsageModelFromJSON(json['usage']), + }; +} + +export function ContextModelToJSON(value?: ContextModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'snippets': ((value.snippets as Array).map(SnippetModelToJSON)), + 'usage': UsageModelToJSON(value.usage), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ContextRequest.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ContextRequest.ts new file mode 100644 index 00000000..953b8343 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ContextRequest.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Parameters to retrieve context from an assistant. + * @export + * @interface ContextRequest + */ +export interface ContextRequest { + /** + * The query that is used to generate the context. + * @type {string} + * @memberof ContextRequest + */ + query: string; + /** + * Optionally filter which documents can be retrieved using the following metadata fields. + * @type {object} + * @memberof ContextRequest + */ + filter?: object; +} + +/** + * Check if a given object implements the ContextRequest interface. + */ +export function instanceOfContextRequest(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "query" in value; + + return isInstance; +} + +export function ContextRequestFromJSON(json: any): ContextRequest { + return ContextRequestFromJSONTyped(json, false); +} + +export function ContextRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ContextRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'query': json['query'], + 'filter': !exists(json, 'filter') ? undefined : json['filter'], + }; +} + +export function ContextRequestToJSON(value?: ContextRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'query': value.query, + 'filter': value.filter, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponse.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponse.ts new file mode 100644 index 00000000..4b474687 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponse.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { ErrorResponseError } from './ErrorResponseError'; +import { + ErrorResponseErrorFromJSON, + ErrorResponseErrorFromJSONTyped, + ErrorResponseErrorToJSON, +} from './ErrorResponseError'; + +/** + * The response shape used for all error responses. + * @export + * @interface ErrorResponse + */ +export interface ErrorResponse { + /** + * The HTTP status code of the error. + * @type {number} + * @memberof ErrorResponse + */ + status: number; + /** + * + * @type {ErrorResponseError} + * @memberof ErrorResponse + */ + error: ErrorResponseError; +} + +/** + * Check if a given object implements the ErrorResponse interface. + */ +export function instanceOfErrorResponse(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "status" in value; + isInstance = isInstance && "error" in value; + + return isInstance; +} + +export function ErrorResponseFromJSON(json: any): ErrorResponse { + return ErrorResponseFromJSONTyped(json, false); +} + +export function ErrorResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'status': json['status'], + 'error': ErrorResponseErrorFromJSON(json['error']), + }; +} + +export function ErrorResponseToJSON(value?: ErrorResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'status': value.status, + 'error': ErrorResponseErrorToJSON(value.error), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponseError.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponseError.ts new file mode 100644 index 00000000..c40ca5a3 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ErrorResponseError.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Detailed information about the error that occurred. + * @export + * @interface ErrorResponseError + */ +export interface ErrorResponseError { + /** + * + * @type {string} + * @memberof ErrorResponseError + */ + code: ErrorResponseErrorCodeEnum; + /** + * + * @type {string} + * @memberof ErrorResponseError + */ + message: string; + /** + * Additional information about the error. This field is not guaranteed to be present. + * @type {object} + * @memberof ErrorResponseError + */ + details?: object; +} + + +/** + * @export + */ +export const ErrorResponseErrorCodeEnum = { + Ok: 'OK', + Unknown: 'UNKNOWN', + InvalidArgument: 'INVALID_ARGUMENT', + DeadlineExceeded: 'DEADLINE_EXCEEDED', + QuotaExceeded: 'QUOTA_EXCEEDED', + NotFound: 'NOT_FOUND', + AlreadyExists: 'ALREADY_EXISTS', + PermissionDenied: 'PERMISSION_DENIED', + Unauthenticated: 'UNAUTHENTICATED', + ResourceExhausted: 'RESOURCE_EXHAUSTED', + FailedPrecondition: 'FAILED_PRECONDITION', + Aborted: 'ABORTED', + OutOfRange: 'OUT_OF_RANGE', + Unimplemented: 'UNIMPLEMENTED', + Internal: 'INTERNAL', + Unavailable: 'UNAVAILABLE', + DataLoss: 'DATA_LOSS', + Forbidden: 'FORBIDDEN' +} as const; +export type ErrorResponseErrorCodeEnum = typeof ErrorResponseErrorCodeEnum[keyof typeof ErrorResponseErrorCodeEnum]; + + +/** + * Check if a given object implements the ErrorResponseError interface. + */ +export function instanceOfErrorResponseError(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "code" in value; + isInstance = isInstance && "message" in value; + + return isInstance; +} + +export function ErrorResponseErrorFromJSON(json: any): ErrorResponseError { + return ErrorResponseErrorFromJSONTyped(json, false); +} + +export function ErrorResponseErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponseError { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'code': json['code'], + 'message': json['message'], + 'details': !exists(json, 'details') ? undefined : json['details'], + }; +} + +export function ErrorResponseErrorToJSON(value?: ErrorResponseError | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'code': value.code, + 'message': value.message, + 'details': value.details, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ListFiles200Response.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ListFiles200Response.ts new file mode 100644 index 00000000..fea68cc9 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ListFiles200Response.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { AssistantFileModel } from './AssistantFileModel'; +import { + AssistantFileModelFromJSON, + AssistantFileModelFromJSONTyped, + AssistantFileModelToJSON, +} from './AssistantFileModel'; + +/** + * The list of files that exist in the assistant + * @export + * @interface ListFiles200Response + */ +export interface ListFiles200Response { + /** + * + * @type {Array} + * @memberof ListFiles200Response + */ + files?: Array; +} + +/** + * Check if a given object implements the ListFiles200Response interface. + */ +export function instanceOfListFiles200Response(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ListFiles200ResponseFromJSON(json: any): ListFiles200Response { + return ListFiles200ResponseFromJSONTyped(json, false); +} + +export function ListFiles200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ListFiles200Response { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'files': !exists(json, 'files') ? undefined : ((json['files'] as Array).map(AssistantFileModelFromJSON)), + }; +} + +export function ListFiles200ResponseToJSON(value?: ListFiles200Response | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'files': value.files === undefined ? undefined : ((value.files as Array).map(AssistantFileModelToJSON)), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/MessageModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/MessageModel.ts new file mode 100644 index 00000000..edb38834 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/MessageModel.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The MessageModel describes the format of a message in a chat. + * @export + * @interface MessageModel + */ +export interface MessageModel { + /** + * Role of the message such as 'user' or 'assistant' + * @type {string} + * @memberof MessageModel + */ + role?: string; + /** + * Content of the message + * @type {string} + * @memberof MessageModel + */ + content?: string; +} + +/** + * Check if a given object implements the MessageModel interface. + */ +export function instanceOfMessageModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function MessageModelFromJSON(json: any): MessageModel { + return MessageModelFromJSONTyped(json, false); +} + +export function MessageModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): MessageModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'role': !exists(json, 'role') ? undefined : json['role'], + 'content': !exists(json, 'content') ? undefined : json['content'], + }; +} + +export function MessageModelToJSON(value?: MessageModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'role': value.role, + 'content': value.content, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/ReferenceModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/ReferenceModel.ts new file mode 100644 index 00000000..dd86c012 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/ReferenceModel.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { AssistantFileModel } from './AssistantFileModel'; +import { + AssistantFileModelFromJSON, + AssistantFileModelFromJSONTyped, + AssistantFileModelToJSON, +} from './AssistantFileModel'; + +/** + * The ReferenceModel describes a single reference in a citation. + * @export + * @interface ReferenceModel + */ +export interface ReferenceModel { + /** + * + * @type {AssistantFileModel} + * @memberof ReferenceModel + */ + file?: AssistantFileModel; + /** + * + * @type {Array} + * @memberof ReferenceModel + */ + pages?: Array; +} + +/** + * Check if a given object implements the ReferenceModel interface. + */ +export function instanceOfReferenceModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ReferenceModelFromJSON(json: any): ReferenceModel { + return ReferenceModelFromJSONTyped(json, false); +} + +export function ReferenceModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): ReferenceModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'file': !exists(json, 'file') ? undefined : AssistantFileModelFromJSON(json['file']), + 'pages': !exists(json, 'pages') ? undefined : json['pages'], + }; +} + +export function ReferenceModelToJSON(value?: ReferenceModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'file': AssistantFileModelToJSON(value.file), + 'pages': value.pages, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/SearchCompletions.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/SearchCompletions.ts new file mode 100644 index 00000000..8330a7c5 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/SearchCompletions.ts @@ -0,0 +1,108 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { MessageModel } from './MessageModel'; +import { + MessageModelFromJSON, + MessageModelFromJSONTyped, + MessageModelToJSON, +} from './MessageModel'; + +/** + * The list of queries / chats to chat an assistant + * @export + * @interface SearchCompletions + */ +export interface SearchCompletions { + /** + * + * @type {Array} + * @memberof SearchCompletions + */ + messages: Array; + /** + * If false, the assistant will return a single JSON response. If true, the assistant will return a stream of responses. + * @type {boolean} + * @memberof SearchCompletions + */ + stream?: boolean; + /** + * The large language model to use for answer generation + * @type {string} + * @memberof SearchCompletions + */ + model?: SearchCompletionsModelEnum; + /** + * Optionally filter which documents can be retrieved using the following metadata fields. + * @type {object} + * @memberof SearchCompletions + */ + filter?: object; +} + + +/** + * @export + */ +export const SearchCompletionsModelEnum = { + Gpt4o: 'gpt-4o', + Claude35Sonnet: 'claude-3-5-sonnet' +} as const; +export type SearchCompletionsModelEnum = typeof SearchCompletionsModelEnum[keyof typeof SearchCompletionsModelEnum]; + + +/** + * Check if a given object implements the SearchCompletions interface. + */ +export function instanceOfSearchCompletions(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "messages" in value; + + return isInstance; +} + +export function SearchCompletionsFromJSON(json: any): SearchCompletions { + return SearchCompletionsFromJSONTyped(json, false); +} + +export function SearchCompletionsFromJSONTyped(json: any, ignoreDiscriminator: boolean): SearchCompletions { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'messages': ((json['messages'] as Array).map(MessageModelFromJSON)), + 'stream': !exists(json, 'stream') ? undefined : json['stream'], + 'model': !exists(json, 'model') ? undefined : json['model'], + 'filter': !exists(json, 'filter') ? undefined : json['filter'], + }; +} + +export function SearchCompletionsToJSON(value?: SearchCompletions | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'messages': ((value.messages as Array).map(MessageModelToJSON)), + 'stream': value.stream, + 'model': value.model, + 'filter': value.filter, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/SnippetModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/SnippetModel.ts new file mode 100644 index 00000000..f1e4b091 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/SnippetModel.ts @@ -0,0 +1,102 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The SnippetModel represents a part of a document that is relevant to the user query. + * @export + * @interface SnippetModel + */ +export interface SnippetModel { + /** + * + * @type {string} + * @memberof SnippetModel + */ + type?: SnippetModelTypeEnum; + /** + * + * @type {string} + * @memberof SnippetModel + */ + content: string; + /** + * + * @type {number} + * @memberof SnippetModel + */ + score: number; + /** + * The TypedReferenceModel represents a reference for the information provided. + * @type {object} + * @memberof SnippetModel + */ + reference: object; +} + + +/** + * @export + */ +export const SnippetModelTypeEnum = { + Text: 'text' +} as const; +export type SnippetModelTypeEnum = typeof SnippetModelTypeEnum[keyof typeof SnippetModelTypeEnum]; + + +/** + * Check if a given object implements the SnippetModel interface. + */ +export function instanceOfSnippetModel(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "content" in value; + isInstance = isInstance && "score" in value; + isInstance = isInstance && "reference" in value; + + return isInstance; +} + +export function SnippetModelFromJSON(json: any): SnippetModel { + return SnippetModelFromJSONTyped(json, false); +} + +export function SnippetModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): SnippetModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'type': !exists(json, 'type') ? undefined : json['type'], + 'content': json['content'], + 'score': json['score'], + 'reference': json['reference'], + }; +} + +export function SnippetModelToJSON(value?: SnippetModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'type': value.type, + 'content': value.content, + 'score': value.score, + 'reference': value.reference, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/UsageModel.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/UsageModel.ts new file mode 100644 index 00000000..a11bdc4f --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/UsageModel.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The UsageModel describes the usage of a chat completion. + * @export + * @interface UsageModel + */ +export interface UsageModel { + /** + * + * @type {number} + * @memberof UsageModel + */ + promptTokens?: number; + /** + * + * @type {number} + * @memberof UsageModel + */ + completionTokens?: number; + /** + * + * @type {number} + * @memberof UsageModel + */ + totalTokens?: number; +} + +/** + * Check if a given object implements the UsageModel interface. + */ +export function instanceOfUsageModel(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function UsageModelFromJSON(json: any): UsageModel { + return UsageModelFromJSONTyped(json, false); +} + +export function UsageModelFromJSONTyped(json: any, ignoreDiscriminator: boolean): UsageModel { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'promptTokens': !exists(json, 'prompt_tokens') ? undefined : json['prompt_tokens'], + 'completionTokens': !exists(json, 'completion_tokens') ? undefined : json['completion_tokens'], + 'totalTokens': !exists(json, 'total_tokens') ? undefined : json['total_tokens'], + }; +} + +export function UsageModelToJSON(value?: UsageModel | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'prompt_tokens': value.promptTokens, + 'completion_tokens': value.completionTokens, + 'total_tokens': value.totalTokens, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_data/models/index.ts b/src/pinecone-generated-ts-fetch/assistant_data/models/index.ts new file mode 100644 index 00000000..694c53f2 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/models/index.ts @@ -0,0 +1,21 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './AssistantFileModel'; +export * from './Chat'; +export * from './ChatCompletionAssistant200Response'; +export * from './ChatCompletionAssistant200ResponseChoicesInner'; +export * from './ChatCompletionAssistant200ResponseChoicesInnerDelta'; +export * from './ChatCompletionModel'; +export * from './ChatModel'; +export * from './ChoiceModel'; +export * from './CitationModel'; +export * from './ContextModel'; +export * from './ContextRequest'; +export * from './ErrorResponse'; +export * from './ErrorResponseError'; +export * from './ListFiles200Response'; +export * from './MessageModel'; +export * from './ReferenceModel'; +export * from './SearchCompletions'; +export * from './SnippetModel'; +export * from './UsageModel'; diff --git a/src/pinecone-generated-ts-fetch/assistant_data/runtime.ts b/src/pinecone-generated-ts-fetch/assistant_data/runtime.ts new file mode 100644 index 00000000..c1bf0938 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_data/runtime.ts @@ -0,0 +1,431 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Pinecone Assistant Data Plane API + * Pinecone Assistant Engine is a context engine to store and retrieve relevant knowledge from millions of documents at scale. This API supports interactions with assistants. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "https://unknown".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/api_version.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/api_version.ts new file mode 100644 index 00000000..87013d2b --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/api_version.ts @@ -0,0 +1 @@ +export const X_PINECONE_API_VERSION = '2025-01'; diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/MetricsApi.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/MetricsApi.ts new file mode 100644 index 00000000..5f35e994 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/MetricsApi.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + AlignmentRequest, + AlignmentResponse, + BasicErrorResponse, +} from '../models/index'; +import { + AlignmentRequestFromJSON, + AlignmentRequestToJSON, + AlignmentResponseFromJSON, + AlignmentResponseToJSON, + BasicErrorResponseFromJSON, + BasicErrorResponseToJSON, +} from '../models/index'; + +export interface MetricsAlignmentRequest { + alignmentRequest: AlignmentRequest; +} + +/** + * + */ +export class MetricsApi extends runtime.BaseAPI { + + /** + * The `metrics_alignment` endpoint [evaluates](https://docs.pinecone.io/guides/assistant/understanding-evaluation) the correctness, completeness, and alignment of a generated answer with respect to a question and a ground truth answer. The correctness and completeness are evaluated based on the precision and recall of the generated answer with respect to the ground truth answer facts. Alignment is the harmonic mean of correctness and completeness. + * Evaluate an answer + */ + async metricsAlignmentRaw(requestParameters: MetricsAlignmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters.alignmentRequest === null || requestParameters.alignmentRequest === undefined) { + throw new runtime.RequiredError('alignmentRequest','Required parameter requestParameters.alignmentRequest was null or undefined when calling metricsAlignment.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Api-Key"] = this.configuration.apiKey("Api-Key"); // ApiKeyAuth authentication + } + + const response = await this.request({ + path: `/evaluation/metrics/alignment`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: AlignmentRequestToJSON(requestParameters.alignmentRequest), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AlignmentResponseFromJSON(jsonValue)); + } + + /** + * The `metrics_alignment` endpoint [evaluates](https://docs.pinecone.io/guides/assistant/understanding-evaluation) the correctness, completeness, and alignment of a generated answer with respect to a question and a ground truth answer. The correctness and completeness are evaluated based on the precision and recall of the generated answer with respect to the ground truth answer facts. Alignment is the harmonic mean of correctness and completeness. + * Evaluate an answer + */ + async metricsAlignment(requestParameters: MetricsAlignmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.metricsAlignmentRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/index.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/index.ts new file mode 100644 index 00000000..df88ff17 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './MetricsApi'; diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/index.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/index.ts new file mode 100644 index 00000000..91e0ae58 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/index.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; +export * from './api_version'; diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentRequest.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentRequest.ts new file mode 100644 index 00000000..514d4171 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentRequest.ts @@ -0,0 +1,84 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The request for the alignment evaluation. + * @export + * @interface AlignmentRequest + */ +export interface AlignmentRequest { + /** + * The question for which the answer was generated. + * @type {string} + * @memberof AlignmentRequest + */ + question: string; + /** + * The generated answer. + * @type {string} + * @memberof AlignmentRequest + */ + answer: string; + /** + * The ground truth answer to the question. + * @type {string} + * @memberof AlignmentRequest + */ + groundTruthAnswer: string; +} + +/** + * Check if a given object implements the AlignmentRequest interface. + */ +export function instanceOfAlignmentRequest(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "question" in value; + isInstance = isInstance && "answer" in value; + isInstance = isInstance && "groundTruthAnswer" in value; + + return isInstance; +} + +export function AlignmentRequestFromJSON(json: any): AlignmentRequest { + return AlignmentRequestFromJSONTyped(json, false); +} + +export function AlignmentRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): AlignmentRequest { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'question': json['question'], + 'answer': json['answer'], + 'groundTruthAnswer': json['ground_truth_answer'], + }; +} + +export function AlignmentRequestToJSON(value?: AlignmentRequest | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'question': value.question, + 'answer': value.answer, + 'ground_truth_answer': value.groundTruthAnswer, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentResponse.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentResponse.ts new file mode 100644 index 00000000..d8c438ac --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/AlignmentResponse.ts @@ -0,0 +1,103 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { Metrics } from './Metrics'; +import { + MetricsFromJSON, + MetricsFromJSONTyped, + MetricsToJSON, +} from './Metrics'; +import type { Reasoning } from './Reasoning'; +import { + ReasoningFromJSON, + ReasoningFromJSONTyped, + ReasoningToJSON, +} from './Reasoning'; +import type { TokenCounts } from './TokenCounts'; +import { + TokenCountsFromJSON, + TokenCountsFromJSONTyped, + TokenCountsToJSON, +} from './TokenCounts'; + +/** + * The response for the alignment evaluation. + * @export + * @interface AlignmentResponse + */ +export interface AlignmentResponse { + /** + * + * @type {Metrics} + * @memberof AlignmentResponse + */ + metrics: Metrics; + /** + * + * @type {Reasoning} + * @memberof AlignmentResponse + */ + reasoning: Reasoning; + /** + * + * @type {TokenCounts} + * @memberof AlignmentResponse + */ + usage: TokenCounts; +} + +/** + * Check if a given object implements the AlignmentResponse interface. + */ +export function instanceOfAlignmentResponse(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "metrics" in value; + isInstance = isInstance && "reasoning" in value; + isInstance = isInstance && "usage" in value; + + return isInstance; +} + +export function AlignmentResponseFromJSON(json: any): AlignmentResponse { + return AlignmentResponseFromJSONTyped(json, false); +} + +export function AlignmentResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): AlignmentResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'metrics': MetricsFromJSON(json['metrics']), + 'reasoning': ReasoningFromJSON(json['reasoning']), + 'usage': TokenCountsFromJSON(json['usage']), + }; +} + +export function AlignmentResponseToJSON(value?: AlignmentResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'metrics': MetricsToJSON(value.metrics), + 'reasoning': ReasoningToJSON(value.reasoning), + 'usage': TokenCountsToJSON(value.usage), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/BasicErrorResponse.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/BasicErrorResponse.ts new file mode 100644 index 00000000..7a27f8a8 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/BasicErrorResponse.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * A basic error response that contains a message. + * @export + * @interface BasicErrorResponse + */ +export interface BasicErrorResponse { + /** + * + * @type {string} + * @memberof BasicErrorResponse + */ + message: string; +} + +/** + * Check if a given object implements the BasicErrorResponse interface. + */ +export function instanceOfBasicErrorResponse(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "message" in value; + + return isInstance; +} + +export function BasicErrorResponseFromJSON(json: any): BasicErrorResponse { + return BasicErrorResponseFromJSONTyped(json, false); +} + +export function BasicErrorResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): BasicErrorResponse { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'message': json['message'], + }; +} + +export function BasicErrorResponseToJSON(value?: BasicErrorResponse | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'message': value.message, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Entailment.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Entailment.ts new file mode 100644 index 00000000..edbe1c35 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Entailment.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * The entailment of a fact. + * @export + */ +export const Entailment = { + Entailed: 'entailed', + Contradicted: 'contradicted', + Neutral: 'neutral' +} as const; +export type Entailment = typeof Entailment[keyof typeof Entailment]; + + +export function EntailmentFromJSON(json: any): Entailment { + return EntailmentFromJSONTyped(json, false); +} + +export function EntailmentFromJSONTyped(json: any, ignoreDiscriminator: boolean): Entailment { + return json as Entailment; +} + +export function EntailmentToJSON(value?: Entailment | null): any { + return value as any; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/EvaluatedFact.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/EvaluatedFact.ts new file mode 100644 index 00000000..7d218533 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/EvaluatedFact.ts @@ -0,0 +1,88 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { Entailment } from './Entailment'; +import { + EntailmentFromJSON, + EntailmentFromJSONTyped, + EntailmentToJSON, +} from './Entailment'; +import type { Fact } from './Fact'; +import { + FactFromJSON, + FactFromJSONTyped, + FactToJSON, +} from './Fact'; + +/** + * A fact that was evaluated. + * @export + * @interface EvaluatedFact + */ +export interface EvaluatedFact { + /** + * + * @type {Fact} + * @memberof EvaluatedFact + */ + fact: Fact; + /** + * + * @type {Entailment} + * @memberof EvaluatedFact + */ + entailment: Entailment; +} + +/** + * Check if a given object implements the EvaluatedFact interface. + */ +export function instanceOfEvaluatedFact(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "fact" in value; + isInstance = isInstance && "entailment" in value; + + return isInstance; +} + +export function EvaluatedFactFromJSON(json: any): EvaluatedFact { + return EvaluatedFactFromJSONTyped(json, false); +} + +export function EvaluatedFactFromJSONTyped(json: any, ignoreDiscriminator: boolean): EvaluatedFact { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'fact': FactFromJSON(json['fact']), + 'entailment': EntailmentFromJSON(json['entailment']), + }; +} + +export function EvaluatedFactToJSON(value?: EvaluatedFact | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'fact': FactToJSON(value.fact), + 'entailment': EntailmentToJSON(value.entailment), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Fact.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Fact.ts new file mode 100644 index 00000000..e3d0c704 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Fact.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * A fact + * @export + * @interface Fact + */ +export interface Fact { + /** + * The content of the fact. + * @type {string} + * @memberof Fact + */ + content: string; +} + +/** + * Check if a given object implements the Fact interface. + */ +export function instanceOfFact(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "content" in value; + + return isInstance; +} + +export function FactFromJSON(json: any): Fact { + return FactFromJSONTyped(json, false); +} + +export function FactFromJSONTyped(json: any, ignoreDiscriminator: boolean): Fact { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'content': json['content'], + }; +} + +export function FactToJSON(value?: Fact | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'content': value.content, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Metrics.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Metrics.ts new file mode 100644 index 00000000..8777dc5d --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Metrics.ts @@ -0,0 +1,84 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * The metrics returned for the alignment evaluation. + * @export + * @interface Metrics + */ +export interface Metrics { + /** + * The precision of the generated answer. + * @type {number} + * @memberof Metrics + */ + correctness: number; + /** + * The recall of the generated answer. + * @type {number} + * @memberof Metrics + */ + completeness: number; + /** + * The harmonic mean of correctness and completeness. + * @type {number} + * @memberof Metrics + */ + alignment: number; +} + +/** + * Check if a given object implements the Metrics interface. + */ +export function instanceOfMetrics(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "correctness" in value; + isInstance = isInstance && "completeness" in value; + isInstance = isInstance && "alignment" in value; + + return isInstance; +} + +export function MetricsFromJSON(json: any): Metrics { + return MetricsFromJSONTyped(json, false); +} + +export function MetricsFromJSONTyped(json: any, ignoreDiscriminator: boolean): Metrics { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'correctness': json['correctness'], + 'completeness': json['completeness'], + 'alignment': json['alignment'], + }; +} + +export function MetricsToJSON(value?: Metrics | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'correctness': value.correctness, + 'completeness': value.completeness, + 'alignment': value.alignment, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Reasoning.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Reasoning.ts new file mode 100644 index 00000000..c024ab3d --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/Reasoning.ts @@ -0,0 +1,73 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import type { EvaluatedFact } from './EvaluatedFact'; +import { + EvaluatedFactFromJSON, + EvaluatedFactFromJSONTyped, + EvaluatedFactToJSON, +} from './EvaluatedFact'; + +/** + * The reasoning behind the alignment evaluation. + * @export + * @interface Reasoning + */ +export interface Reasoning { + /** + * The facts that were evaluated. + * @type {Array} + * @memberof Reasoning + */ + evaluatedFacts: Array; +} + +/** + * Check if a given object implements the Reasoning interface. + */ +export function instanceOfReasoning(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "evaluatedFacts" in value; + + return isInstance; +} + +export function ReasoningFromJSON(json: any): Reasoning { + return ReasoningFromJSONTyped(json, false); +} + +export function ReasoningFromJSONTyped(json: any, ignoreDiscriminator: boolean): Reasoning { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'evaluatedFacts': ((json['evaluated_facts'] as Array).map(EvaluatedFactFromJSON)), + }; +} + +export function ReasoningToJSON(value?: Reasoning | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'evaluated_facts': ((value.evaluatedFacts as Array).map(EvaluatedFactToJSON)), + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/TokenCounts.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/TokenCounts.ts new file mode 100644 index 00000000..44b8e643 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/TokenCounts.ts @@ -0,0 +1,84 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Token counts for the input prompt and completion. + * @export + * @interface TokenCounts + */ +export interface TokenCounts { + /** + * + * @type {number} + * @memberof TokenCounts + */ + promptTokens: number; + /** + * + * @type {number} + * @memberof TokenCounts + */ + completionTokens: number; + /** + * + * @type {number} + * @memberof TokenCounts + */ + totalTokens: number; +} + +/** + * Check if a given object implements the TokenCounts interface. + */ +export function instanceOfTokenCounts(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "promptTokens" in value; + isInstance = isInstance && "completionTokens" in value; + isInstance = isInstance && "totalTokens" in value; + + return isInstance; +} + +export function TokenCountsFromJSON(json: any): TokenCounts { + return TokenCountsFromJSONTyped(json, false); +} + +export function TokenCountsFromJSONTyped(json: any, ignoreDiscriminator: boolean): TokenCounts { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'promptTokens': json['prompt_tokens'], + 'completionTokens': json['completion_tokens'], + 'totalTokens': json['total_tokens'], + }; +} + +export function TokenCountsToJSON(value?: TokenCounts | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'prompt_tokens': value.promptTokens, + 'completion_tokens': value.completionTokens, + 'total_tokens': value.totalTokens, + }; +} + diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/models/index.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/index.ts new file mode 100644 index 00000000..892b0ed1 --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/models/index.ts @@ -0,0 +1,11 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './AlignmentRequest'; +export * from './AlignmentResponse'; +export * from './BasicErrorResponse'; +export * from './Entailment'; +export * from './EvaluatedFact'; +export * from './Fact'; +export * from './Metrics'; +export * from './Reasoning'; +export * from './TokenCounts'; diff --git a/src/pinecone-generated-ts-fetch/assistant_evaluation/runtime.ts b/src/pinecone-generated-ts-fetch/assistant_evaluation/runtime.ts new file mode 100644 index 00000000..a28fec5f --- /dev/null +++ b/src/pinecone-generated-ts-fetch/assistant_evaluation/runtime.ts @@ -0,0 +1,431 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Evaluation API + * Provides endpoints for evaluating RAG systems using various metrics. + * + * The version of the OpenAPI document: 2025-01 + * Contact: support@pinecone.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "https://prod-1-data.ke.pinecone.io/assistant".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/src/pinecone.ts b/src/pinecone.ts index 5fde1965..2dff49dc 100644 --- a/src/pinecone.ts +++ b/src/pinecone.ts @@ -13,6 +13,18 @@ import { indexOperationsBuilder, CollectionName, } from './control'; +import { + createAssistant, + CreateAssistantOptions, + deleteAssistant, + describeAssistant, + updateAssistant, + UpdateAssistantOptions, + listAssistants, + evaluate, + AssistantEval, +} from './assistant/control'; +import { AssistantHostSingleton } from './assistant/assistantHostSingleton'; import type { ConfigureIndexRequest, CreateCollectionRequest, @@ -30,6 +42,9 @@ import { inferenceOperationsBuilder } from './inference/inferenceOperationsBuild import { isBrowser } from './utils/environment'; import { ValidateProperties } from './utils/validateProperties'; import { PineconeConfigurationProperties } from './data/vectors/types'; +import { asstControlOperationsBuilder } from './assistant/control/asstControlOperationsBuilder'; +import { asstMetricsOperationsBuilder } from './assistant/control/asstMetricsOperationsBuilder'; +import { Assistant } from './assistant/data/assistant'; /** * The `Pinecone` class is the main entrypoint to this sdk. You will use @@ -92,6 +107,18 @@ export class Pinecone { private _listCollections: ReturnType; /** @hidden */ private _listIndexes: ReturnType; + /** @hidden */ + private _createAssistant: ReturnType; + /** @hidden */ + private _deleteAssistant: ReturnType; + /** @hidden */ + private _updateAssistant: ReturnType; + /** @hidden */ + private _evaluate: ReturnType; + /** @hidden */ + private _describeAssistant: ReturnType; + /** @hidden */ + private _listAssistants: ReturnType; public inference: Inference; @@ -127,6 +154,8 @@ export class Pinecone { const api = indexOperationsBuilder(this.config); const infApi = inferenceOperationsBuilder(this.config); + const asstControlApi = asstControlOperationsBuilder(this.config); + const asstEvalApi = asstMetricsOperationsBuilder(this.config); this._configureIndex = configureIndex(api); this._createCollection = createCollection(api); @@ -137,6 +166,14 @@ export class Pinecone { this._deleteIndex = deleteIndex(api); this._listCollections = listCollections(api); this._listIndexes = listIndexes(api); + + this._createAssistant = createAssistant(asstControlApi); + this._deleteAssistant = deleteAssistant(asstControlApi); + this._updateAssistant = updateAssistant(asstControlApi); + this._evaluate = evaluate(asstEvalApi); + this._describeAssistant = describeAssistant(asstControlApi); + this._listAssistants = listAssistants(asstControlApi); + this.inference = new Inference(infApi); } @@ -563,6 +600,194 @@ export class Pinecone { return this._describeCollection(collectionName); } + /** + * Creates a new Assistant. + * + * @example + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * const pc = new Pinecone(); + * await pc.createAssistant({name: 'test1'}); + * // { + * // name: 'test11', + * // instructions: undefined, + * // metadata: undefined, + * // status: 'Initializing', + * // host: 'https://prod-1-data.ke.pinecone.io', + * // createdAt: 2025-01-08T22:52:49.652Z, + * // updatedAt: 2025-01-08T22:52:49.652Z + * // } + * ``` + * + * @param options - A {@link CreateAssistantOptions} object containing the `name` of the Assistant to be created. + * Optionally, users can also specify instructions, metadata, and host region. Region must be one of "us" or "eu" + * and determines where the Assistant will be hosted. + * @throws Error if the Assistant API is not initialized. + * @throws Error if an invalid region is provided. + * @returns A Promise that resolves to an {@link Assistant} model. + */ + async createAssistant(options: CreateAssistantOptions) { + const assistant = await this._createAssistant(options); + + if (assistant.host) { + AssistantHostSingleton._set(this.config, assistant.name, assistant.host); + } + + return Promise.resolve(assistant); + } + + /** + * Deletes an Assistant by name. + * + * @example + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * const pc = new Pinecone(); + * await pc.deleteAssistant('test1'); + * ``` + * + * @param assistantName - The name of the Assistant to be deleted. + * @throws Error if the Assistant API is not initialized. + */ + async deleteAssistant(assistantName: string) { + await this._deleteAssistant(assistantName); + AssistantHostSingleton._delete(this.config, assistantName); + return Promise.resolve(); + } + + /** + * Retrieves information about an Assistant by name. + * + * @example + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * const pc = new Pinecone(); + * const test = await pc.describeAssistant('test1'); + * console.log(test); + * // { + * // name: 'test1', + * // 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 + * // } + * ``` + * + * @param assistantName - The name of the Assistant to retrieve. + * @throws Error if the Assistant API is not initialized. + * @returns A Promise that resolves to an {@link Assistant} model. + */ + async describeAssistant(assistantName: string) { + const assistant = await this._describeAssistant(assistantName); + + if (assistant.host) { + AssistantHostSingleton._set(this.config, assistantName, assistant.host); + } + + return Promise.resolve(assistant); + } + + /** + * Retrieves a list of all Assistants for a given Pinecone API key. + * + * @example + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * const pc = new Pinecone(); + * const assistants = await pc.listAssistants(); + * console.log(assistants); + * // { + * // assistants: [ + * // { + * // name: 'test2', + * // instructions: 'test-instructions', + * // metadata: [Object], + * // status: 'Ready', + * // host: 'https://prod-1-data.ke.pinecone.io', + * // createdAt: 2025-01-06T19:14:18.633Z, + * // updatedAt: 2025-01-06T19:14:36.977Z + * // }, + * // ] + * // } + * ``` + * + * @throws Error if the Assistant API is not initialized. + * @returns A Promise that resolves to an object containing an array of {@link Assistant} models. + */ + async listAssistants() { + const assistantList = await this._listAssistants(); + + // For any listAssistants calls we want to update the AssistantHostSingleton cache. + // This prevents unneeded calls to describeAssistant for resolving the host for assistant operations. + if (assistantList.assistants && assistantList.assistants.length > 0) { + for (let i = 0; i < assistantList.assistants.length; i++) { + const assistant = assistantList.assistants[i]; + if (assistant.host) { + AssistantHostSingleton._set( + this.config, + assistant.name, + assistant.host + ); + } + } + } + + return Promise.resolve(assistantList); + } + + /** + * Updates an Assistant by name. + * + * @example + * ```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 + * // } + * ``` + * + * @param options - An {@link updateAssistant} object containing the name of the Assistant to be updated and + * optional instructions and metadata. + * @throws Error if the Assistant API is not initialized. + * @returns A Promise that resolves to an {@link UpdateAssistant200Response} object. + */ + updateAssistant(options: UpdateAssistantOptions) { + return this._updateAssistant(options); + } + + /** + * Evaluates a question against a given answer and a ground truth answer. + * + * @example + * ```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 } + * // } + * ``` + * @param options - An {@link AssistantEval} object containing the question, the answer, and a ground truth answer to + * evaluate. + * @throws Error if the Evaluation API is not initialized. + * @returns A Promise that resolves to an {@link AlignmentResponse} object. + */ + evaluate(options: AssistantEval) { + return this._evaluate(options); + } + /** @internal */ _checkForBrowser() { if (isBrowser()) { @@ -672,4 +897,63 @@ export class Pinecone { ) { return this.index(indexName, indexHostUrl, additionalHeaders); } + + /** + * Targets a specific assistant for performing operations. + * + * Once an assistant is targeted, you can perform operations such as uploading files, + * updating instructions, and chatting. + * + * ```typescript + * import { Pinecone } from '@pinecone-database/pinecone'; + * + * const pc = new Pinecone(); + * const assistant = pc.Assistant('my-assistant'); + * + * // Upload a file to the assistant + * await assistant.uploadFile({ + * path: 'test-file.txt', + * metadata: { description: 'Sample test file' } + * }); + * + * // Retrieve assistant details + * const details = await assistant.describe(); + * console.log('Assistant details:', details); + * + * // Update assistant instructions + * await assistant.update({ + * instructions: 'Provide concise responses only.', + * }); + * + * 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 } + * // } + * ``` + * + * @param assistantName - The name of the assistant to target. + * @returns An {@link Assistant} object that can be used to perform assistant-related operations. + */ + assistant(assistantName: string) { + return new Assistant(assistantName, this.config); + } + + /** + * {@inheritDoc assistant} + */ + // Alias method + Assistant(assistantName: string) { + return this.assistant(assistantName); + } } diff --git a/src/utils/__tests__/fetch.test.ts b/src/utils/__tests__/fetch.test.ts index d3996e1e..4c83c4e7 100644 --- a/src/utils/__tests__/fetch.test.ts +++ b/src/utils/__tests__/fetch.test.ts @@ -4,7 +4,6 @@ import type { PineconeConfiguration } from '../../data'; describe('getFetch', () => { afterEach(() => { - console.log('resetting global.fetch'); // Reset global.fetch after each test to avoid affecting other tests delete (global as any).fetch; }); diff --git a/src/utils/retries.ts b/src/utils/retries.ts index 34b7d9fa..6482ea2d 100644 --- a/src/utils/retries.ts +++ b/src/utils/retries.ts @@ -1,5 +1,8 @@ import { mapHttpStatusError, PineconeMaxRetriesExceededError } from '../errors'; +// TODO: Parameterize this class to allow for custom error handling (e.g. only retry 400 errors on Chat endpoint, +// but not globally + /* Retry asynchronous operations. * * @param maxRetries - The maximum number of retries to attempt. @@ -61,6 +64,9 @@ export class RetryOnServerFailure { } isRetryError(response): boolean { + if (!response) { + return false; + } if (response) { if ( response.name && diff --git a/src/version.json b/src/version.json old mode 100644 new mode 100755 diff --git a/utils/cleanupResources.ts b/utils/cleanupResources.ts index a419c1a0..d086e398 100644 --- a/utils/cleanupResources.ts +++ b/utils/cleanupResources.ts @@ -38,6 +38,14 @@ for (const envVar of ['PINECONE_API_KEY']) { await p.deleteIndex(index.name); } } - process.exit(); } + + const assistants = await p.listAssistants(); + if (assistants.assistants.length > 0) { + for (const assistant of assistants.assistants) { + console.log(`Deleting assistant ${assistant.name}`); + await p.deleteAssistant(assistant.name); + } + } + process.exit(); })();