diff --git a/blocks/blocks/pinecone.ts b/blocks/blocks/pinecone.ts new file mode 100644 index 0000000000..5633f93810 --- /dev/null +++ b/blocks/blocks/pinecone.ts @@ -0,0 +1,153 @@ +import { PineconeIcon } from '@/components/icons' +// You'll need to create this icon +import { PineconeResponse } from '@/tools/pinecone/types' +import { BlockConfig } from '../types' + +export const PineconeBlock: BlockConfig = { + type: 'pinecone', + name: 'Pinecone', + description: 'Interact with Pinecone vector database', + category: 'tools', + bgColor: '#0D1117', + icon: PineconeIcon, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Query', id: 'query' }, + { label: 'Upsert', id: 'upsert' }, + { label: 'Delete', id: 'delete' }, + ], + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Your Pinecone API key', + password: true, + }, + { + id: 'environment', + title: 'Environment', + type: 'short-input', + layout: 'full', + placeholder: 'gcp-starter', + }, + { + id: 'indexName', + title: 'Index Name', + type: 'short-input', + layout: 'full', + placeholder: 'my-index', + }, + // Query operation fields + { + id: 'queryVector', + title: 'Query Vector', + type: 'long-input', + layout: 'full', + placeholder: '[0.1, 0.2, 0.3, ...]', + condition: { field: 'operation', value: 'query' }, + }, + { + id: 'topK', + title: 'Top K Results', + type: 'short-input', + layout: 'half', + placeholder: '10', + condition: { field: 'operation', value: 'query' }, + }, + { + id: 'includeMetadata', + title: 'Include Metadata', + type: 'switch', + layout: 'half', + value: () => 'true', + condition: { field: 'operation', value: 'query' }, + }, + { + id: 'includeValues', + title: 'Include Values', + type: 'switch', + layout: 'half', + value: () => 'false', + condition: { field: 'operation', value: 'query' }, + }, + // Upsert operation fields + { + id: 'vectors', + title: 'Vectors', + type: 'long-input', + layout: 'full', + placeholder: '[{"id": "vec1", "values": [0.1, 0.2, 0.3], "metadata": {"key": "value"}}]', + condition: { field: 'operation', value: 'upsert' }, + }, + // Delete operation fields + { + id: 'ids', + title: 'Vector IDs', + type: 'long-input', + layout: 'full', + placeholder: '["vec1", "vec2", ...]', + condition: { field: 'operation', value: 'delete' }, + }, + { + id: 'deleteAll', + title: 'Delete All Vectors', + type: 'switch', + layout: 'half', + value: () => 'false', + condition: { field: 'operation', value: 'delete' }, + }, + ], + + tools: { + access: ['pinecone_query', 'pinecone_upsert', 'pinecone_delete'], + config: { + tool: (params) => { + switch (params.operation) { + case 'query': + return 'pinecone_query' + case 'upsert': + return 'pinecone_upsert' + case 'delete': + return 'pinecone_delete' + default: + throw new Error('Invalid operation selected') + } + }, + }, + }, + + inputs: { + operation: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + environment: { type: 'string', required: true }, + indexName: { type: 'string', required: true }, + // Query operation inputs + queryVector: { type: 'json', required: false }, + topK: { type: 'number', required: false }, + includeMetadata: { type: 'boolean', required: false }, + includeValues: { type: 'boolean', required: false }, + // Upsert operation inputs + vectors: { type: 'json', required: false }, + // Delete operation inputs + ids: { type: 'json', required: false }, + deleteAll: { type: 'boolean', required: false }, + }, + + outputs: { + response: { + type: { + matches: 'any', + upsertedCount: 'any', + deletedCount: 'any', + }, + }, + }, +} diff --git a/blocks/index.ts b/blocks/index.ts index 4d2ea71cd4..ffdc6ace3c 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -10,6 +10,7 @@ import { GitHubBlock } from './blocks/github' import { GmailBlock } from './blocks/gmail' import { JinaBlock } from './blocks/jina' import { NotionBlock } from './blocks/notion' +import { PineconeBlock } from './blocks/pinecone' import { RouterBlock } from './blocks/router' import { SerperBlock } from './blocks/serper' import { SlackBlock } from './blocks/slack' @@ -41,6 +42,7 @@ export { GmailBlock, XBlock, StarterBlock, + PineconeBlock, } // Registry of all block configurations @@ -64,6 +66,7 @@ const blocks: Record = { notion: NotionBlock, gmail: GmailBlock, x: XBlock, + pinecone: PineconeBlock, } // Helper functions diff --git a/components/icons.tsx b/components/icons.tsx index 70d918186f..79635a302d 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -1388,3 +1388,23 @@ export function StartIcon(props: SVGProps) { ) } + +export function PineconeIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/tools/index.ts b/tools/index.ts index 78be57bbd9..8f1c5d8195 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -15,6 +15,9 @@ import { readUrlTool } from './jina/reader' import { notionReadTool } from './notion/read' import { notionWriteTool } from './notion/write' import { chatTool as openAIChat } from './openai/chat' +import { deleteTool as pineconeDeleteTool } from './pinecone/delete' +import { queryTool as pineconeQueryTool } from './pinecone/query' +import { upsertTool as pineconeUpsertTool } from './pinecone/upsert' import { opportunitiesTool as salesforceOpportunities } from './salesforce/opportunities' import { searchTool as serperSearch } from './serper/search' import { slackMessageTool } from './slack/message' @@ -58,6 +61,9 @@ export const tools: Record = { x_read: xRead, x_search: xSearch, x_user: xUser, + pinecone_query: pineconeQueryTool, + pinecone_upsert: pineconeUpsertTool, + pinecone_delete: pineconeDeleteTool, } // Get a tool by its ID diff --git a/tools/pinecone/delete.ts b/tools/pinecone/delete.ts new file mode 100644 index 0000000000..d5049d1a2d --- /dev/null +++ b/tools/pinecone/delete.ts @@ -0,0 +1,42 @@ +import { ToolConfig } from '../types' +import { PineconeParams, PineconeResponse } from './types' + +export const deleteTool: ToolConfig = { + id: 'pinecone_delete', + name: 'Pinecone Delete', + description: 'Delete vectors from Pinecone index', + version: '1.0', + + params: { + apiKey: { type: 'string', required: true, description: 'Pinecone API key' }, + environment: { type: 'string', required: true, description: 'Pinecone environment' }, + indexName: { type: 'string', required: true, description: 'Name of the Pinecone index' }, + ids: { type: 'array', required: false, description: 'Array of vector IDs to delete' }, + deleteAll: { type: 'boolean', required: false, description: 'Delete all vectors in the index' }, + }, + + request: { + method: 'POST', + url: (params) => + `https://${params.indexName}-${params.environment}.svc.${params.environment}.pinecone.io/vectors/delete`, + headers: (params) => ({ + 'Api-Key': params.apiKey, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + ids: params.ids, + deleteAll: params.deleteAll, + }), + }, + + transformResponse: async (response) => { + return { + success: true, + output: { + deletedCount: response.ok ? 1 : 0, // Pinecone doesn't return count, so we estimate + }, + } + }, + + transformError: (error) => `Pinecone delete failed: ${error.message}`, +} diff --git a/tools/pinecone/query.ts b/tools/pinecone/query.ts new file mode 100644 index 0000000000..b0b7c76396 --- /dev/null +++ b/tools/pinecone/query.ts @@ -0,0 +1,62 @@ +import { ToolConfig } from '../types' +import { PineconeParams, PineconeResponse } from './types' + +export const queryTool: ToolConfig = { + id: 'pinecone_query', + name: 'Pinecone Query', + description: 'Query vectors from Pinecone index', + version: '1.0', + + params: { + apiKey: { type: 'string', required: true, description: 'Pinecone API key' }, + environment: { type: 'string', required: true, description: 'Pinecone environment' }, + indexName: { type: 'string', required: true, description: 'Name of the Pinecone index' }, + queryVector: { type: 'array', required: true, description: 'Vector to query' }, + topK: { + type: 'number', + required: false, + default: 10, + description: 'Number of results to return', + }, + includeMetadata: { + type: 'boolean', + required: false, + default: true, + description: 'Include metadata in results', + }, + includeValues: { + type: 'boolean', + required: false, + default: false, + description: 'Include vector values in results', + }, + }, + + request: { + method: 'POST', + url: (params) => + `https://${params.indexName}-${params.environment}.svc.${params.environment}.pinecone.io/vectors/query`, + headers: (params) => ({ + 'Api-Key': params.apiKey, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + vector: params.queryVector, + topK: params.topK || 10, + includeMetadata: params.includeMetadata ?? true, + includeValues: params.includeValues ?? false, + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + return { + success: true, + output: { + matches: data.matches || [], + }, + } + }, + + transformError: (error) => `Pinecone query failed: ${error.message}`, +} diff --git a/tools/pinecone/types.ts b/tools/pinecone/types.ts new file mode 100644 index 0000000000..c728ffbb23 --- /dev/null +++ b/tools/pinecone/types.ts @@ -0,0 +1,35 @@ +import { ToolResponse } from '../types' + +export interface PineconeParams { + apiKey: string + environment: string + indexName: 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 + }> + // Delete operation + ids?: string[] + deleteAll?: boolean +} + +export interface PineconeResponse extends ToolResponse { + output: { + matches?: Array<{ + id: string + score: number + values?: number[] + metadata?: Record + }> + upsertedCount?: number + deletedCount?: number + } +} diff --git a/tools/pinecone/upsert.ts b/tools/pinecone/upsert.ts new file mode 100644 index 0000000000..be646a83b7 --- /dev/null +++ b/tools/pinecone/upsert.ts @@ -0,0 +1,45 @@ +import { ToolConfig } from '../types' +import { PineconeParams, PineconeResponse } from './types' + +export const upsertTool: ToolConfig = { + id: 'pinecone_upsert', + name: 'Pinecone Upsert', + description: 'Upsert vectors into Pinecone index', + version: '1.0', + + params: { + apiKey: { type: 'string', required: true, description: 'Pinecone API key' }, + environment: { type: 'string', required: true, description: 'Pinecone environment' }, + indexName: { type: 'string', required: true, description: 'Name of the Pinecone index' }, + vectors: { + type: 'array', + required: true, + description: 'Array of vectors to upsert, each with id, values, and optional metadata', + }, + }, + + request: { + method: 'POST', + url: (params) => + `https://${params.indexName}-${params.environment}.svc.${params.environment}.pinecone.io/vectors/upsert`, + headers: (params) => ({ + 'Api-Key': params.apiKey, + 'Content-Type': 'application/json', + }), + body: (params) => ({ + vectors: params.vectors, + }), + }, + + transformResponse: async (response) => { + const data = await response.json() + return { + success: true, + output: { + upsertedCount: data.upsertedCount || 0, + }, + } + }, + + transformError: (error) => `Pinecone upsert failed: ${error.message}`, +}