From a12f234a7155622bbc0a1bf8dbd5d1ef8cc994fd Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 20 Mar 2025 14:29:55 -0700 Subject: [PATCH] added perplexity tool (#129) --- sim/app/blocks/blocks/perplexity.ts | 116 +++++++++++++++++++++++ sim/app/blocks/index.ts | 3 + sim/app/tools/index.ts | 2 + sim/app/tools/perplexity/chat.ts | 138 ++++++++++++++++++++++++++++ sim/app/tools/perplexity/index.ts | 3 + sim/app/tools/perplexity/types.ts | 29 ++++++ 6 files changed, 291 insertions(+) create mode 100644 sim/app/blocks/blocks/perplexity.ts create mode 100644 sim/app/tools/perplexity/chat.ts create mode 100644 sim/app/tools/perplexity/index.ts create mode 100644 sim/app/tools/perplexity/types.ts diff --git a/sim/app/blocks/blocks/perplexity.ts b/sim/app/blocks/blocks/perplexity.ts new file mode 100644 index 0000000000..0e44f3448a --- /dev/null +++ b/sim/app/blocks/blocks/perplexity.ts @@ -0,0 +1,116 @@ +import { PerplexityIcon } from '@/components/icons' +import { ToolResponse } from '@/tools/types' +import { BlockConfig } from '../types' + +interface PerplexityChatResponse extends ToolResponse { + output: { + content: string + model: string + usage: { + prompt_tokens: number + completion_tokens: number + total_tokens: number + } + } +} + +export const PerplexityBlock: BlockConfig = { + type: 'perplexity', + name: 'Perplexity', + description: 'Use Perplexity AI chat models', + longDescription: + 'Generate completions using Perplexity AI models with real-time knowledge and search capabilities. Create responses, answer questions, and generate content with customizable parameters.', + category: 'tools', + bgColor: '#20808D', // Perplexity turquoise color + icon: PerplexityIcon, + subBlocks: [ + { + id: 'system', + title: 'System Prompt', + type: 'long-input', + layout: 'full', + placeholder: 'Optional system prompt to guide the model behavior...', + }, + { + id: 'prompt', + title: 'User Prompt', + type: 'long-input', + layout: 'full', + placeholder: 'Enter your prompt here...', + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: [ + { label: 'Sonar', id: 'sonar' }, + { label: 'Mistral', id: 'mistral' }, + { label: 'Claude-3-Opus', id: 'claude-3-opus' }, + { label: 'Claude-3-Sonnet', id: 'claude-3-sonnet' }, + { label: 'Command-R', id: 'command-r' }, + { label: 'GPT-4o', id: 'gpt-4o' }, + ], + value: () => 'sonar', + }, + { + id: 'temperature', + title: 'Temperature', + type: 'slider', + layout: 'half', + min: 0, + max: 1, + value: () => '0.7', + }, + { + id: 'max_tokens', + title: 'Max Tokens', + type: 'short-input', + layout: 'half', + placeholder: 'Maximum number of tokens', + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + layout: 'full', + placeholder: 'Enter your Perplexity API key', + password: true, + }, + ], + tools: { + access: ['perplexity_chat'], + config: { + tool: () => 'perplexity_chat', + params: (params) => { + const toolParams = { + apiKey: params.apiKey, + model: params.model, + prompt: params.prompt, + system: params.system, + max_tokens: params.max_tokens ? parseInt(params.max_tokens) : undefined, + temperature: params.temperature ? parseFloat(params.temperature) : undefined, + } + + return toolParams + }, + }, + }, + inputs: { + prompt: { type: 'string', required: true }, + system: { type: 'string', required: false }, + model: { type: 'string', required: true }, + max_tokens: { type: 'string', required: false }, + temperature: { type: 'string', required: false }, + apiKey: { type: 'string', required: true }, + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + usage: 'json', + }, + }, + }, +} diff --git a/sim/app/blocks/index.ts b/sim/app/blocks/index.ts index 75b8dac6e7..65755b02d8 100644 --- a/sim/app/blocks/index.ts +++ b/sim/app/blocks/index.ts @@ -14,6 +14,7 @@ import { GuestyBlock } from './blocks/guesty' import { JinaBlock } from './blocks/jina' import { NotionBlock } from './blocks/notion' import { OpenAIBlock } from './blocks/openai' +import { PerplexityBlock } from './blocks/perplexity' import { PineconeBlock } from './blocks/pinecone' import { RedditBlock } from './blocks/reddit' import { RouterBlock } from './blocks/router' @@ -61,6 +62,7 @@ export { GoogleDocsBlock, WhatsAppBlock, GoogleSheetsBlock, + PerplexityBlock, } // Registry of all block configurations, alphabetically sorted @@ -81,6 +83,7 @@ const blocks: Record = { jina: JinaBlock, notion: NotionBlock, openai: OpenAIBlock, + perplexity: PerplexityBlock, pinecone: PineconeBlock, reddit: RedditBlock, router: RouterBlock, diff --git a/sim/app/tools/index.ts b/sim/app/tools/index.ts index 7648d87659..6a248d4f30 100644 --- a/sim/app/tools/index.ts +++ b/sim/app/tools/index.ts @@ -14,6 +14,7 @@ import { contactsTool as hubspotContacts } from './hubspot/contacts' import { readUrlTool } from './jina/reader' import { notionReadTool, notionWriteTool } from './notion' import { embeddingsTool as openAIEmbeddings } from './openai/embeddings' +import { perplexityChatTool } from './perplexity' import { pineconeFetchTool, pineconeGenerateEmbeddingsTool, @@ -90,6 +91,7 @@ export const tools: Record = { google_sheets_update: sheetsUpdateTool, guesty_reservation: guestyReservationTool, guesty_guest: guestyGuestTool, + perplexity_chat: perplexityChatTool, } // Get a tool by its ID diff --git a/sim/app/tools/perplexity/chat.ts b/sim/app/tools/perplexity/chat.ts new file mode 100644 index 0000000000..9b0408c092 --- /dev/null +++ b/sim/app/tools/perplexity/chat.ts @@ -0,0 +1,138 @@ +import { ToolConfig } from '../types' +import { PerplexityChatParams, PerplexityChatResponse } from './types' + +export const chatTool: ToolConfig = { + id: 'perplexity_chat', + name: 'Perplexity Chat', + description: 'Generate completions using Perplexity AI chat models', + version: '1.0', + + params: { + apiKey: { + type: 'string', + required: true, + requiredForToolCall: true, + description: 'Perplexity API key', + }, + model: { + type: 'string', + required: true, + description: 'Model to use for chat completions (e.g., sonar, mistral)', + }, + messages: { + type: 'array', + required: true, + description: 'Array of message objects with role and content', + }, + max_tokens: { + type: 'number', + required: false, + description: 'Maximum number of tokens to generate', + }, + temperature: { + type: 'number', + required: false, + description: 'Sampling temperature between 0 and 1', + }, + }, + + request: { + method: 'POST', + url: () => 'https://api.perplexity.ai/chat/completions', + headers: (params) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + let messages = params.messages + + if (!messages && (params.prompt || params.system)) { + messages = [] + + // Add system message if provided + if (params.system && typeof params.system === 'string' && params.system.trim() !== '') { + messages.push({ + role: 'system', + content: params.system, + }) + } + + // Add user message + if (params.prompt && typeof params.prompt === 'string' && params.prompt.trim() !== '') { + messages.push({ + role: 'user', + content: params.prompt, + }) + } + } + + // Validate that each message has role and content + for (const msg of messages!) { + if (!msg.role || !msg.content) { + throw new Error('Each message must have role and content properties') + } + } + + const body: Record = { + model: params.model, + messages: messages, + } + + if (params.max_tokens !== undefined) { + body.max_tokens = params.max_tokens + } + + if (params.temperature !== undefined) { + body.temperature = params.temperature + } + + return body + }, + }, + + transformResponse: async (response, params) => { + try { + // Check if the response was successful + if (!response.ok) { + const errorData = await response.json().catch(() => null) + console.error('Perplexity API error:', { + status: response.status, + statusText: response.statusText, + errorData, + }) + + const errorMessage = errorData + ? JSON.stringify(errorData) + : `API error: ${response.status} ${response.statusText}` + + throw new Error(errorMessage) + } + + const data = await response.json() + + // Validate response structure + if (!data.choices || !data.choices[0] || !data.choices[0].message) { + console.error('Invalid Perplexity response format:', data) + throw new Error('Invalid response format from Perplexity API') + } + + return { + success: true, + output: { + content: data.choices[0].message.content, + model: data.model, + usage: { + prompt_tokens: data.usage.prompt_tokens, + completion_tokens: data.usage.completion_tokens, + total_tokens: data.usage.total_tokens, + }, + }, + } + } catch (error: any) { + console.error('Failed to process Perplexity response:', error) + throw error + } + }, + + transformError: (error) => `Perplexity chat completion failed: ${error.message}`, +} diff --git a/sim/app/tools/perplexity/index.ts b/sim/app/tools/perplexity/index.ts new file mode 100644 index 0000000000..d4776ce508 --- /dev/null +++ b/sim/app/tools/perplexity/index.ts @@ -0,0 +1,3 @@ +import { chatTool } from './chat' + +export const perplexityChatTool = chatTool diff --git a/sim/app/tools/perplexity/types.ts b/sim/app/tools/perplexity/types.ts new file mode 100644 index 0000000000..813f52a42a --- /dev/null +++ b/sim/app/tools/perplexity/types.ts @@ -0,0 +1,29 @@ +import { ToolResponse } from '../types' + +export interface PerplexityMessage { + role: string + content: string +} + +export interface PerplexityChatParams { + apiKey: string + model: string + messages?: PerplexityMessage[] + max_tokens?: number + temperature?: number + + prompt?: string + system?: string +} + +export interface PerplexityChatResponse extends ToolResponse { + output: { + content: string + model: string + usage: { + prompt_tokens: number + completion_tokens: number + total_tokens: number + } + } +}