chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import type { PineconeDeleteVectorsParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
import { parseJsonParam } from '@/tools/pinecone/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteVectorsTool: ToolConfig<PineconeDeleteVectorsParams, PineconeResponse> = {
|
||||
id: 'pinecone_delete_vectors',
|
||||
name: 'Pinecone Delete Vectors',
|
||||
description: 'Delete vectors from a Pinecone namespace by IDs, by metadata filter, or delete all',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to delete vectors from (e.g., "documents", "embeddings")',
|
||||
},
|
||||
ids: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Vector IDs to delete (1-1000 items). Mutually exclusive with deleteAll and filter',
|
||||
},
|
||||
deleteAll: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete all vectors in the namespace. Mutually exclusive with ids and filter',
|
||||
},
|
||||
filter: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Metadata filter selecting vectors to delete (e.g., {"category": {"$eq": "product"}}). Mutually exclusive with ids and deleteAll',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/vectors/delete`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.namespace) {
|
||||
body.namespace = params.namespace
|
||||
}
|
||||
if (params.ids != null && params.ids !== '') {
|
||||
const ids = parseJsonParam<unknown>(params.ids, 'ids')
|
||||
if (Array.isArray(ids) && ids.length > 0) {
|
||||
body.ids = ids
|
||||
}
|
||||
}
|
||||
if (params.deleteAll === true || params.deleteAll === 'true') {
|
||||
body.deleteAll = true
|
||||
}
|
||||
if (params.filter != null && params.filter !== '') {
|
||||
body.filter = parseJsonParam(params.filter, 'filter')
|
||||
}
|
||||
|
||||
const selectorCount = [body.ids, body.deleteAll, body.filter].filter(
|
||||
(selector) => selector !== undefined
|
||||
).length
|
||||
if (selectorCount === 0) {
|
||||
throw new Error('Provide exactly one of ids, deleteAll, or filter to delete vectors')
|
||||
}
|
||||
if (selectorCount > 1) {
|
||||
throw new Error('ids, deleteAll, and filter are mutually exclusive — provide only one')
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
statusText: response.status === 200 ? 'Deleted' : response.statusText,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
statusText: {
|
||||
type: 'string',
|
||||
description: 'Status of the delete operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import type {
|
||||
PineconeDescribeIndexParams,
|
||||
PineconeIndexModel,
|
||||
PineconeResponse,
|
||||
} from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const describeIndexTool: ToolConfig<PineconeDescribeIndexParams, PineconeResponse> = {
|
||||
id: 'pinecone_describe_index',
|
||||
name: 'Pinecone Describe Index',
|
||||
description: 'Get the configuration and status of a Pinecone index by name',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the index to describe',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: (params) =>
|
||||
`https://api.pinecone.io/indexes/${encodeURIComponent(params.indexName.trim())}`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
Accept: 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const index: PineconeIndexModel = {
|
||||
name: data.name,
|
||||
dimension: data.dimension ?? null,
|
||||
metric: data.metric ?? null,
|
||||
host: data.host ?? null,
|
||||
vectorType: data.vectorType ?? data.vector_type ?? null,
|
||||
deletionProtection: data.deletionProtection ?? data.deletion_protection ?? null,
|
||||
tags: data.tags ?? null,
|
||||
spec: data.spec ?? null,
|
||||
status: data.status ?? null,
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { index },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
index: {
|
||||
type: 'object',
|
||||
description: 'Index configuration and status',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Index name' },
|
||||
dimension: { type: 'number', description: 'Vector dimensionality' },
|
||||
metric: { type: 'string', description: 'Distance metric (cosine, euclidean, dotproduct)' },
|
||||
host: { type: 'string', description: 'Index host URL for data-plane operations' },
|
||||
vectorType: { type: 'string', description: 'Vector type (dense or sparse)' },
|
||||
deletionProtection: {
|
||||
type: 'string',
|
||||
description: 'Deletion protection (enabled or disabled)',
|
||||
},
|
||||
tags: { type: 'object', description: 'Custom user tags on the index' },
|
||||
spec: { type: 'object', description: 'Index spec (serverless or pod configuration)' },
|
||||
status: { type: 'object', description: 'Index status with ready and state' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import type { PineconeDescribeIndexStatsParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
import { parseJsonParam } from '@/tools/pinecone/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const describeIndexStatsTool: ToolConfig<
|
||||
PineconeDescribeIndexStatsParams,
|
||||
PineconeResponse
|
||||
> = {
|
||||
id: 'pinecone_describe_index_stats',
|
||||
name: 'Pinecone Describe Index Stats',
|
||||
description: 'Get statistics about a Pinecone index, including per-namespace vector counts',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
filter: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Metadata filter to limit which vectors are counted (pod-based indexes only, e.g., {"category": {"$eq": "product"}})',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/describe_index_stats`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.filter != null && params.filter !== '') {
|
||||
body.filter = parseJsonParam(params.filter, 'filter')
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const rawNamespaces = (data.namespaces ?? {}) as Record<
|
||||
string,
|
||||
{ vectorCount?: number; vector_count?: number }
|
||||
>
|
||||
const namespaces: Record<string, { vectorCount: number | null }> = {}
|
||||
for (const [name, summary] of Object.entries(rawNamespaces)) {
|
||||
namespaces[name] = { vectorCount: summary.vectorCount ?? summary.vector_count ?? null }
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
namespaces,
|
||||
dimension: data.dimension ?? null,
|
||||
indexFullness: data.indexFullness ?? data.index_fullness ?? null,
|
||||
totalVectorCount: data.totalVectorCount ?? data.total_vector_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
namespaces: {
|
||||
type: 'json',
|
||||
description: 'Map of namespace name to its summary including vectorCount',
|
||||
},
|
||||
dimension: {
|
||||
type: 'number',
|
||||
description: 'Dimensionality of the indexed vectors',
|
||||
},
|
||||
indexFullness: {
|
||||
type: 'number',
|
||||
description: 'Fullness of the index (pod-based indexes only)',
|
||||
},
|
||||
totalVectorCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of vectors across all namespaces',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type { PineconeFetchParams, PineconeResponse, PineconeVector } from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const fetchTool: ToolConfig<PineconeFetchParams, PineconeResponse> = {
|
||||
id: 'pinecone_fetch',
|
||||
name: 'Pinecone Fetch',
|
||||
description: 'Fetch vectors by ID from a Pinecone index',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
ids: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of vector IDs to fetch (e.g., ["vec-001", "vec-002"])',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to fetch vectors from (e.g., "documents", "embeddings")',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: (params) => {
|
||||
const baseUrl = `${params.indexHost}/vectors/fetch`
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('ids', params.ids.join(','))
|
||||
if (params.namespace) {
|
||||
queryParams.append('namespace', params.namespace)
|
||||
}
|
||||
return `${baseUrl}?${queryParams.toString()}`
|
||||
},
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const vectors = data.vectors as Record<string, PineconeVector>
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
matches: Object.entries(vectors).map(([id, vector]) => ({
|
||||
id,
|
||||
values: vector.values,
|
||||
metadata: vector.metadata,
|
||||
score: 1.0, // Fetch returns exact matches
|
||||
})),
|
||||
data: Object.values(vectors).map((vector) => ({
|
||||
values: vector.values,
|
||||
vector_type: 'dense' as const,
|
||||
})),
|
||||
usage: {
|
||||
total_tokens: data.usage?.readUnits || 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
matches: {
|
||||
type: 'array',
|
||||
description: 'Fetched vectors with ID, values, metadata, and score',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Vector ID' },
|
||||
values: { type: 'array', description: 'Vector values' },
|
||||
metadata: { type: 'object', description: 'Associated metadata' },
|
||||
score: { type: 'number', description: 'Match score (1.0 for exact matches)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
type: 'array',
|
||||
description: 'Vector data with values and vector type',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
values: { type: 'array', description: 'Vector values' },
|
||||
vector_type: { type: 'string', description: 'Vector type (dense/sparse)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
usage: {
|
||||
type: 'object',
|
||||
description: 'Usage statistics including total read units',
|
||||
properties: {
|
||||
total_tokens: { type: 'number', description: 'Read units consumed' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { PineconeGenerateEmbeddingsParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const generateEmbeddingsTool: ToolConfig<
|
||||
PineconeGenerateEmbeddingsParams,
|
||||
PineconeResponse
|
||||
> = {
|
||||
id: 'pinecone_generate_embeddings',
|
||||
name: 'Pinecone Generate Embeddings',
|
||||
description: "Generate embeddings from text using Pinecone's hosted models",
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
model: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Model to use for generating embeddings',
|
||||
},
|
||||
inputs: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of text inputs to generate embeddings for',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: () => 'https://api.pinecone.io/embed',
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => ({
|
||||
model: params.model,
|
||||
inputs: params.inputs,
|
||||
parameters: params.parameters || {
|
||||
input_type: 'passage',
|
||||
truncate: 'END',
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
data: data.data,
|
||||
model: data.model,
|
||||
vector_type: data.vector_type,
|
||||
usage: data.usage,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
data: {
|
||||
type: 'array',
|
||||
description: 'Generated embeddings data with values and vector type',
|
||||
},
|
||||
model: {
|
||||
type: 'string',
|
||||
description: 'Model used for generating embeddings',
|
||||
},
|
||||
vector_type: {
|
||||
type: 'string',
|
||||
description: 'Type of vector generated (dense/sparse)',
|
||||
},
|
||||
usage: {
|
||||
type: 'object',
|
||||
description: 'Usage statistics for embeddings generation',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { deleteVectorsTool } from '@/tools/pinecone/delete_vectors'
|
||||
import { describeIndexTool } from '@/tools/pinecone/describe_index'
|
||||
import { describeIndexStatsTool } from '@/tools/pinecone/describe_index_stats'
|
||||
import { fetchTool } from '@/tools/pinecone/fetch'
|
||||
import { generateEmbeddingsTool } from '@/tools/pinecone/generate_embeddings'
|
||||
import { listIndexesTool } from '@/tools/pinecone/list_indexes'
|
||||
import { listVectorIdsTool } from '@/tools/pinecone/list_vector_ids'
|
||||
import { searchTextTool } from '@/tools/pinecone/search_text'
|
||||
import { searchVectorTool } from '@/tools/pinecone/search_vector'
|
||||
import { updateVectorTool } from '@/tools/pinecone/update_vector'
|
||||
import { upsertTextTool } from '@/tools/pinecone/upsert_text'
|
||||
|
||||
export const pineconeDeleteVectorsTool = deleteVectorsTool
|
||||
export const pineconeDescribeIndexTool = describeIndexTool
|
||||
export const pineconeDescribeIndexStatsTool = describeIndexStatsTool
|
||||
export const pineconeFetchTool = fetchTool
|
||||
export const pineconeGenerateEmbeddingsTool = generateEmbeddingsTool
|
||||
export const pineconeListIndexesTool = listIndexesTool
|
||||
export const pineconeListVectorIdsTool = listVectorIdsTool
|
||||
export const pineconeSearchTextTool = searchTextTool
|
||||
export const pineconeSearchVectorTool = searchVectorTool
|
||||
export const pineconeUpdateVectorTool = updateVectorTool
|
||||
export const pineconeUpsertTextTool = upsertTextTool
|
||||
@@ -0,0 +1,84 @@
|
||||
import type {
|
||||
PineconeIndexModel,
|
||||
PineconeListIndexesParams,
|
||||
PineconeResponse,
|
||||
} from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/** Map a raw Pinecone index model to a clean output shape. */
|
||||
function mapIndex(index: any): PineconeIndexModel {
|
||||
return {
|
||||
name: index.name,
|
||||
dimension: index.dimension ?? null,
|
||||
metric: index.metric ?? null,
|
||||
host: index.host ?? null,
|
||||
vectorType: index.vectorType ?? index.vector_type ?? null,
|
||||
deletionProtection: index.deletionProtection ?? index.deletion_protection ?? null,
|
||||
tags: index.tags ?? null,
|
||||
spec: index.spec ?? null,
|
||||
status: index.status ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export const listIndexesTool: ToolConfig<PineconeListIndexesParams, PineconeResponse> = {
|
||||
id: 'pinecone_list_indexes',
|
||||
name: 'Pinecone List Indexes',
|
||||
description: 'List all Pinecone indexes in the project',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: () => 'https://api.pinecone.io/indexes',
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
Accept: 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
indexes: (data.indexes ?? []).map(mapIndex),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
indexes: {
|
||||
type: 'array',
|
||||
description: 'List of indexes with name, dimension, metric, host, spec, and status',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Index name' },
|
||||
dimension: { type: 'number', description: 'Vector dimensionality' },
|
||||
metric: {
|
||||
type: 'string',
|
||||
description: 'Distance metric (cosine, euclidean, dotproduct)',
|
||||
},
|
||||
host: { type: 'string', description: 'Index host URL for data-plane operations' },
|
||||
vectorType: { type: 'string', description: 'Vector type (dense or sparse)' },
|
||||
deletionProtection: {
|
||||
type: 'string',
|
||||
description: 'Deletion protection (enabled or disabled)',
|
||||
},
|
||||
tags: { type: 'object', description: 'Custom user tags on the index' },
|
||||
spec: { type: 'object', description: 'Index spec (serverless or pod configuration)' },
|
||||
status: { type: 'object', description: 'Index status with ready and state' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { PineconeListVectorIdsParams, PineconeResponse } from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listVectorIdsTool: ToolConfig<PineconeListVectorIdsParams, PineconeResponse> = {
|
||||
id: 'pinecone_list_vector_ids',
|
||||
name: 'Pinecone List Vector IDs',
|
||||
description: 'List vector IDs in a Pinecone namespace by prefix (serverless indexes only)',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to list vector IDs from (e.g., "documents", "embeddings")',
|
||||
},
|
||||
prefix: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter vector IDs by a common prefix (e.g., "doc1#")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of IDs to return per page (default 100)',
|
||||
},
|
||||
paginationToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination token from a previous response to fetch the next page',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('namespace', params.namespace)
|
||||
if (params.prefix) {
|
||||
queryParams.append('prefix', params.prefix)
|
||||
}
|
||||
if (params.limit != null && String(params.limit) !== '') {
|
||||
queryParams.append('limit', String(params.limit))
|
||||
}
|
||||
if (params.paginationToken) {
|
||||
queryParams.append('paginationToken', params.paginationToken)
|
||||
}
|
||||
return `${params.indexHost}/vectors/list?${queryParams.toString()}`
|
||||
},
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
Accept: 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
vectorIds: (data.vectors ?? []).map((vector: { id: string }) => vector.id),
|
||||
pagination: data.pagination ?? null,
|
||||
namespace: data.namespace ?? null,
|
||||
usage: {
|
||||
total_tokens: data.usage?.readUnits ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
vectorIds: {
|
||||
type: 'array',
|
||||
description: 'Vector IDs in the namespace',
|
||||
items: { type: 'string', description: 'Vector ID' },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination info with a next token when more results exist',
|
||||
properties: {
|
||||
next: { type: 'string', description: 'Token to fetch the next page' },
|
||||
},
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
description: 'Namespace the IDs were listed from',
|
||||
},
|
||||
usage: {
|
||||
type: 'object',
|
||||
description: 'Usage statistics including read units',
|
||||
properties: {
|
||||
total_tokens: { type: 'number', description: 'Read units consumed' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import type {
|
||||
PineconeResponse,
|
||||
PineconeSearchHit,
|
||||
PineconeSearchTextParams,
|
||||
} from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const searchTextTool: ToolConfig<PineconeSearchTextParams, PineconeResponse> = {
|
||||
id: 'pinecone_search_text',
|
||||
name: 'Pinecone Search Text',
|
||||
description: 'Search for similar text in a Pinecone index',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to search in (e.g., "documents", "embeddings")',
|
||||
},
|
||||
searchQuery: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Text to search for',
|
||||
},
|
||||
topK: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "10", "25")',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Fields to return in the results',
|
||||
},
|
||||
filter: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter to apply to the search (e.g., {"category": "tech", "year": {"$gte": 2020}})',
|
||||
},
|
||||
rerank: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Reranking parameters',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/records/namespaces/${params.namespace}/search`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => {
|
||||
// Format the query object
|
||||
const query = {
|
||||
inputs: { text: params.searchQuery },
|
||||
top_k: params.topK ? Number(params.topK) : 10,
|
||||
}
|
||||
|
||||
// Build the request body
|
||||
const body: any = {
|
||||
query,
|
||||
}
|
||||
|
||||
// Add optional parameters if provided
|
||||
if (params.fields) {
|
||||
body.fields = typeof params.fields === 'string' ? JSON.parse(params.fields) : params.fields
|
||||
}
|
||||
|
||||
if (params.filter) {
|
||||
body.query.filter =
|
||||
typeof params.filter === 'string' ? JSON.parse(params.filter) : params.filter
|
||||
}
|
||||
|
||||
if (params.rerank) {
|
||||
body.rerank = typeof params.rerank === 'string' ? JSON.parse(params.rerank) : params.rerank
|
||||
// If rerank query is not specified, use the search query
|
||||
if (!body.rerank.query) {
|
||||
body.rerank.query = { text: params.searchQuery }
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
matches: data.result.hits.map((hit: PineconeSearchHit) => ({
|
||||
id: hit._id,
|
||||
score: hit._score,
|
||||
metadata: hit.fields,
|
||||
})),
|
||||
usage: {
|
||||
total_tokens: data.usage.embed_total_tokens || 0,
|
||||
read_units: data.usage.read_units,
|
||||
rerank_units: data.usage.rerank_units,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
matches: {
|
||||
type: 'array',
|
||||
description: 'Search results with ID, score, and metadata',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Vector ID' },
|
||||
score: { type: 'number', description: 'Similarity score' },
|
||||
metadata: { type: 'object', description: 'Associated metadata' },
|
||||
},
|
||||
},
|
||||
},
|
||||
usage: {
|
||||
type: 'object',
|
||||
description: 'Usage statistics including tokens, read units, and rerank units',
|
||||
properties: {
|
||||
total_tokens: { type: 'number', description: 'Total tokens used for embedding' },
|
||||
read_units: { type: 'number', description: 'Read units consumed' },
|
||||
rerank_units: { type: 'number', description: 'Rerank units used' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { PineconeResponse, PineconeSearchVectorParams } from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const searchVectorTool: ToolConfig<PineconeSearchVectorParams, PineconeResponse> = {
|
||||
id: 'pinecone_search_vector',
|
||||
name: 'Pinecone Search Vector',
|
||||
description: 'Search for similar vectors in a Pinecone index',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to search in (e.g., "documents", "embeddings")',
|
||||
},
|
||||
vector: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Vector to search for',
|
||||
},
|
||||
topK: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., 10, 25)',
|
||||
},
|
||||
filter: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter to apply to the search (e.g., {"category": "tech", "year": {"$gte": 2020}})',
|
||||
},
|
||||
includeValues: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Include vector values in response',
|
||||
},
|
||||
includeMetadata: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Include metadata in response (true/false)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/query`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
namespace: params.namespace,
|
||||
vector: typeof params.vector === 'string' ? JSON.parse(params.vector) : params.vector,
|
||||
topK: params.topK ? Number(params.topK) : 10,
|
||||
filter: params.filter
|
||||
? typeof params.filter === 'string'
|
||||
? JSON.parse(params.filter)
|
||||
: params.filter
|
||||
: undefined,
|
||||
includeValues: true, //TODO: Make this dynamic
|
||||
includeMetadata: true, //TODO: Make this dynamic
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
matches: data.matches.map((match: any) => ({
|
||||
id: match.id,
|
||||
score: match.score,
|
||||
values: match.values,
|
||||
metadata: match.metadata,
|
||||
})),
|
||||
namespace: data.namespace,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
matches: {
|
||||
type: 'array',
|
||||
description: 'Vector search results with ID, score, values, and metadata',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
description: 'Namespace where the search was performed',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// Base Pinecone params shared across all operations
|
||||
interface PineconeBaseParams {
|
||||
indexHost: string
|
||||
namespace: string
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
// Response types
|
||||
interface PineconeMatchResponse {
|
||||
id: string
|
||||
score: number
|
||||
values?: number[]
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
export interface PineconeIndexModel {
|
||||
name: string
|
||||
dimension?: number | null
|
||||
metric?: string | null
|
||||
host?: string | null
|
||||
vectorType?: string | null
|
||||
deletionProtection?: string | null
|
||||
tags?: Record<string, string> | null
|
||||
spec?: Record<string, any> | null
|
||||
status?: { ready?: boolean; state?: string } | null
|
||||
}
|
||||
|
||||
export interface PineconeResponse extends ToolResponse {
|
||||
output: {
|
||||
matches?: PineconeMatchResponse[]
|
||||
statusText?: string
|
||||
data?: Array<{
|
||||
values: number[]
|
||||
vector_type: 'dense' | 'sparse'
|
||||
}>
|
||||
model?: string
|
||||
vector_type?: 'dense' | 'sparse'
|
||||
namespace?: string | null
|
||||
usage?: {
|
||||
total_tokens: number
|
||||
}
|
||||
indexes?: PineconeIndexModel[]
|
||||
index?: PineconeIndexModel
|
||||
namespaces?: Record<string, { vectorCount: number | null }>
|
||||
dimension?: number | null
|
||||
indexFullness?: number | null
|
||||
totalVectorCount?: number | null
|
||||
vectorIds?: string[]
|
||||
pagination?: { next?: string } | null
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Embeddings
|
||||
export interface PineconeGenerateEmbeddingsParams {
|
||||
apiKey: string
|
||||
model: string
|
||||
inputs: { text: string }[]
|
||||
parameters?: {
|
||||
input_type?: 'passage'
|
||||
truncate?: 'END'
|
||||
}
|
||||
}
|
||||
|
||||
// Upsert Text
|
||||
export interface PineconeUpsertTextRecord {
|
||||
_id: string
|
||||
chunk_text: string
|
||||
category?: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface PineconeUpsertTextParams extends PineconeBaseParams {
|
||||
records: PineconeUpsertTextRecord | PineconeUpsertTextRecord[]
|
||||
}
|
||||
|
||||
// Upsert Vectors
|
||||
interface PineconeUpsertVectorsParams extends PineconeBaseParams {
|
||||
vectors: {
|
||||
id: string
|
||||
values: number[]
|
||||
metadata?: Record<string, any>
|
||||
sparseValues?: {
|
||||
indices: number[]
|
||||
values: number[]
|
||||
}
|
||||
}[]
|
||||
}
|
||||
|
||||
// Search Text
|
||||
interface PineconeSearchQuery {
|
||||
inputs?: { text: string }
|
||||
vector?: {
|
||||
values: number[]
|
||||
sparse_values?: number[]
|
||||
sparse_indices?: number[]
|
||||
}
|
||||
id?: string
|
||||
top_k: number
|
||||
filter?: Record<string, any>
|
||||
}
|
||||
|
||||
interface PineconeRerank {
|
||||
model: string
|
||||
rank_fields: string[]
|
||||
top_n?: number
|
||||
parameters?: Record<string, any>
|
||||
query?: { text: string }
|
||||
}
|
||||
|
||||
export interface PineconeSearchTextParams extends PineconeBaseParams {
|
||||
searchQuery: string
|
||||
topK?: string
|
||||
fields?: string[] | string
|
||||
filter?: Record<string, any> | string
|
||||
rerank?: PineconeRerank | string
|
||||
}
|
||||
|
||||
export interface PineconeSearchHit {
|
||||
_id: string
|
||||
_score: number
|
||||
fields?: Record<string, any>
|
||||
}
|
||||
|
||||
interface PineconeSearchResponse {
|
||||
result: {
|
||||
hits: PineconeSearchHit[]
|
||||
}
|
||||
usage: {
|
||||
read_units: number
|
||||
embed_total_tokens?: number
|
||||
rerank_units?: number
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Vectors
|
||||
export interface PineconeFetchParams extends PineconeBaseParams {
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
export interface PineconeVector {
|
||||
id: string
|
||||
values: number[]
|
||||
metadata?: Record<string, any>
|
||||
}
|
||||
|
||||
interface PineconeUsage {
|
||||
readUnits: number
|
||||
}
|
||||
|
||||
interface PineconeFetchResponse {
|
||||
vectors: Record<string, PineconeVector>
|
||||
namespace?: string
|
||||
usage: PineconeUsage
|
||||
}
|
||||
|
||||
interface PineconeParams {
|
||||
apiKey: string
|
||||
indexHost: string
|
||||
operation: 'query' | 'upsert' | 'delete'
|
||||
// Query operation
|
||||
queryVector?: number[]
|
||||
topK?: number
|
||||
includeMetadata?: boolean
|
||||
includeValues?: boolean
|
||||
// Upsert operation
|
||||
vectors?: Array<{
|
||||
id: string
|
||||
values: number[]
|
||||
metadata?: Record<string, any>
|
||||
}>
|
||||
}
|
||||
|
||||
// Search Vector
|
||||
export interface PineconeSearchVectorParams extends PineconeBaseParams {
|
||||
vector: number[] | string
|
||||
topK?: number | string
|
||||
filter?: Record<string, any> | string
|
||||
includeValues?: boolean
|
||||
includeMetadata?: boolean
|
||||
}
|
||||
|
||||
export interface PineconeDeleteVectorsParams {
|
||||
apiKey: string
|
||||
indexHost: string
|
||||
namespace?: string
|
||||
ids?: string[] | string
|
||||
deleteAll?: boolean | string
|
||||
filter?: Record<string, any> | string
|
||||
}
|
||||
|
||||
export interface PineconeUpdateVectorParams {
|
||||
apiKey: string
|
||||
indexHost: string
|
||||
id: string
|
||||
namespace?: string
|
||||
values?: number[] | string
|
||||
sparseValues?: { indices: number[]; values: number[] } | string
|
||||
setMetadata?: Record<string, any> | string
|
||||
}
|
||||
|
||||
export interface PineconeDescribeIndexStatsParams {
|
||||
apiKey: string
|
||||
indexHost: string
|
||||
filter?: Record<string, any> | string
|
||||
}
|
||||
|
||||
export interface PineconeListIndexesParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface PineconeDescribeIndexParams {
|
||||
apiKey: string
|
||||
indexName: string
|
||||
}
|
||||
|
||||
export interface PineconeListVectorIdsParams {
|
||||
apiKey: string
|
||||
indexHost: string
|
||||
namespace: string
|
||||
prefix?: string
|
||||
limit?: number | string
|
||||
paginationToken?: string
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { PineconeResponse, PineconeUpdateVectorParams } from '@/tools/pinecone/types'
|
||||
import { parseJsonParam } from '@/tools/pinecone/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateVectorTool: ToolConfig<PineconeUpdateVectorParams, PineconeResponse> = {
|
||||
id: 'pinecone_update_vector',
|
||||
name: 'Pinecone Update Vector',
|
||||
description: 'Update the values, sparse values, or metadata of a vector in a Pinecone namespace',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique ID of the vector to update',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace containing the vector (e.g., "documents", "embeddings")',
|
||||
},
|
||||
values: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New dense vector values to overwrite the existing values',
|
||||
},
|
||||
sparseValues: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New sparse vector values with indices and values arrays',
|
||||
},
|
||||
setMetadata: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Metadata key-value pairs to add or overwrite on the vector',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/vectors/update`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = { id: params.id.trim() }
|
||||
if (params.namespace) {
|
||||
body.namespace = params.namespace
|
||||
}
|
||||
if (params.values != null && params.values !== '') {
|
||||
body.values = parseJsonParam(params.values, 'values')
|
||||
}
|
||||
if (params.sparseValues != null && params.sparseValues !== '') {
|
||||
body.sparseValues = parseJsonParam(params.sparseValues, 'sparseValues')
|
||||
}
|
||||
if (params.setMetadata != null && params.setMetadata !== '') {
|
||||
body.setMetadata = parseJsonParam(params.setMetadata, 'setMetadata')
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
statusText: response.status === 200 ? 'Updated' : response.statusText,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
statusText: {
|
||||
type: 'string',
|
||||
description: 'Status of the update operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import type {
|
||||
PineconeResponse,
|
||||
PineconeUpsertTextParams,
|
||||
PineconeUpsertTextRecord,
|
||||
} from '@/tools/pinecone/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const upsertTextTool: ToolConfig<PineconeUpsertTextParams, PineconeResponse> = {
|
||||
id: 'pinecone_upsert_text',
|
||||
name: 'Pinecone Upsert Text',
|
||||
description: 'Insert or update text records in a Pinecone index',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
indexHost: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full Pinecone index host URL (e.g., "https://my-index-abc123.svc.pinecone.io")',
|
||||
},
|
||||
namespace: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Namespace to upsert records into (e.g., "documents", "embeddings")',
|
||||
},
|
||||
records: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Record or array of records to upsert, each containing _id, text, and optional metadata',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Pinecone API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => `${params.indexHost}/records/namespaces/${params.namespace}/upsert`,
|
||||
headers: (params) => ({
|
||||
'Api-Key': params.apiKey,
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'X-Pinecone-API-Version': '2025-01',
|
||||
}),
|
||||
body: (params) => {
|
||||
// If records is a string, parse it line by line
|
||||
let records: PineconeUpsertTextRecord[]
|
||||
if (typeof params.records === 'string') {
|
||||
// Split by newlines and parse each line
|
||||
records = (params.records as string)
|
||||
.split('\n')
|
||||
.filter((line: string) => line.trim()) // Remove empty lines
|
||||
.map((line: string) => {
|
||||
// Clean and parse each line
|
||||
const cleanJson = line.trim().replace(/'\\''/g, "'")
|
||||
return JSON.parse(cleanJson) as PineconeUpsertTextRecord
|
||||
})
|
||||
} else {
|
||||
records = Array.isArray(params.records) ? params.records : [params.records]
|
||||
}
|
||||
|
||||
// Convert to NDJSON format
|
||||
const ndjson = records.map((r: PineconeUpsertTextRecord) => JSON.stringify(r)).join('\n')
|
||||
return { body: ndjson }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
// Pinecone upsert returns 201 Created with empty body on success
|
||||
return {
|
||||
success: response.status === 201,
|
||||
output: {
|
||||
statusText: response.status === 201 ? 'Created' : response.statusText,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
statusText: {
|
||||
type: 'string',
|
||||
description: 'Status of the upsert operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Parse a Pinecone param that may arrive as a JSON string (from a block input or
|
||||
* agent tool-call) or as an already-parsed value. Throws a descriptive error when
|
||||
* a provided string is not valid JSON, instead of letting `JSON.parse` crash.
|
||||
*/
|
||||
export function parseJsonParam<T>(value: unknown, fieldName: string): T {
|
||||
if (typeof value !== 'string') {
|
||||
return value as T
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value) as T
|
||||
} catch {
|
||||
throw new Error(`${fieldName} must be valid JSON`)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user