diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts index 427796925..02b5e1216 100644 --- a/app/api/proxy/route.ts +++ b/app/api/proxy/route.ts @@ -1,10 +1,44 @@ import { NextResponse } from 'next/server' import { getTool } from '@/tools' +import { anthropicProvider } from '@/providers/anthropic' +import { openaiProvider } from '@/providers/openai' +import { ProviderConfig } from '@/providers/types' + +const providers: Record = { + 'anthropic/chat': anthropicProvider, + 'openai/chat': openaiProvider +} export async function POST(request: Request) { try { const { toolId, params } = await request.json() + // Check if this is a provider chat request + const provider = providers[toolId] + if (provider) { + const { apiKey, ...restParams } = params + if (!apiKey) { + throw new Error('API key is required') + } + + const response = await fetch(provider.baseUrl, { + method: 'POST', + headers: provider.headers(apiKey), + body: JSON.stringify(restParams) + }) + + if (!response.ok) { + const error = await response.json() + throw new Error(error.error?.message || `${toolId} API error`) + } + + return NextResponse.json({ + success: true, + output: await response.json() + }) + } + + // Handle regular tool requests const tool = getTool(toolId) if (!tool) { throw new Error(`Tool not found: ${toolId}`) diff --git a/blocks/blocks/agent.ts b/blocks/blocks/agent.ts index 71c8b33c7..c87485be1 100644 --- a/blocks/blocks/agent.ts +++ b/blocks/blocks/agent.ts @@ -1,34 +1,49 @@ -import { AgentIcon } from '@/components/icons' import { BlockConfig } from '../types' -import { ChatResponse } from '@/tools/openai/chat' +import { AgentIcon } from '@/components/icons' import { MODEL_TOOLS, ModelType } from '../consts' +import { ToolResponse } from '@/tools/types' -export const AgentBlock: BlockConfig = { +interface AgentResponse extends ToolResponse { + output: { + content: string + model: string + tokens?: { + prompt?: number + completion?: number + total?: number + } + toolCalls?: { + list: Array<{ + name: string + arguments: Record + }> + count: number + } + } +} + +export const AgentBlock: BlockConfig = { type: 'agent', toolbar: { title: 'Agent', - description: 'Use any LLM', + description: 'Add an AI agent with tool access', bgColor: '#7F2FFF', icon: AgentIcon, category: 'blocks', }, tools: { - access: ['openai.chat', 'anthropic.chat', 'google.chat', 'xai.chat', 'deepseek.chat', 'deepseek.reasoner'], + access: ['openai_chat', 'anthropic_chat', 'google_chat', 'xai_chat', 'deepseek_chat', 'deepseek_reasoner'], config: { tool: (params: Record) => { const model = params.model || 'gpt-4o' - if (!model) { throw new Error('No model selected') } - const tool = MODEL_TOOLS[model as ModelType] - if (!tool) { throw new Error(`Invalid model selected: ${model}`) } - - return tool + return tool } } }, @@ -36,6 +51,7 @@ export const AgentBlock: BlockConfig = { inputs: { systemPrompt: { type: 'string', required: true }, context: { type: 'string', required: false }, + model: { type: 'string', required: true }, apiKey: { type: 'string', required: true }, responseFormat: { type: 'json', required: false }, temperature: { type: 'number', required: false }, @@ -47,7 +63,7 @@ export const AgentBlock: BlockConfig = { content: 'string', model: 'string', tokens: 'any', - reasoning_tokens: 'any' + toolCalls: 'any' } } }, @@ -57,14 +73,14 @@ export const AgentBlock: BlockConfig = { title: 'System Prompt', type: 'long-input', layout: 'full', - placeholder: 'Enter prompt' + placeholder: 'Enter system prompt...' }, { id: 'context', title: 'Context', type: 'short-input', layout: 'full', - placeholder: 'Enter text' + placeholder: 'Enter context or user message...' }, { id: 'model', diff --git a/blocks/blocks/api.ts b/blocks/blocks/api.ts index 6f1e0ec4f..7ddac7e7c 100644 --- a/blocks/blocks/api.ts +++ b/blocks/blocks/api.ts @@ -12,7 +12,7 @@ export const ApiBlock: BlockConfig = { category: 'blocks', }, tools: { - access: ['http.request'] + access: ['http_request'] }, workflow: { inputs: { diff --git a/blocks/blocks/crewai.ts b/blocks/blocks/crewai.ts index 2b149220e..a1d850358 100644 --- a/blocks/blocks/crewai.ts +++ b/blocks/blocks/crewai.ts @@ -12,7 +12,7 @@ export const CrewAIVisionBlock: BlockConfig = { category: 'tools' }, tools: { - access: ['crewai.vision'] + access: ['crewai_vision'] }, workflow: { inputs: { diff --git a/blocks/blocks/firecrawl.ts b/blocks/blocks/firecrawl.ts index fcb99556f..d6bcfc555 100644 --- a/blocks/blocks/firecrawl.ts +++ b/blocks/blocks/firecrawl.ts @@ -12,7 +12,7 @@ export const FirecrawlScrapeBlock: BlockConfig = { category: 'tools' }, tools: { - access: ['firecrawl.scrape'] + access: ['firecrawl_scrape'] }, workflow: { inputs: { diff --git a/blocks/blocks/function.ts b/blocks/blocks/function.ts index 6bc040c58..608e0761b 100644 --- a/blocks/blocks/function.ts +++ b/blocks/blocks/function.ts @@ -12,7 +12,7 @@ export const FunctionBlock: BlockConfig = { category: 'blocks', }, tools: { - access: ['function.execute'] + access: ['function_execute'] }, workflow: { inputs: { diff --git a/blocks/blocks/github.ts b/blocks/blocks/github.ts index 46b7b4309..a766b4804 100644 --- a/blocks/blocks/github.ts +++ b/blocks/blocks/github.ts @@ -12,7 +12,7 @@ export const GitHubBlock: BlockConfig = { category: 'tools', }, tools: { - access: ['github.repoinfo'] + access: ['github_repoinfo'] }, workflow: { inputs: { diff --git a/blocks/blocks/jina.ts b/blocks/blocks/jina.ts index 8f8e84191..429205386 100644 --- a/blocks/blocks/jina.ts +++ b/blocks/blocks/jina.ts @@ -12,7 +12,7 @@ export const JinaBlock: BlockConfig = { category: 'tools', }, tools: { - access: ['jina.readurl'] + access: ['jina_readurl'] }, workflow: { inputs: { diff --git a/blocks/blocks/slack.ts b/blocks/blocks/slack.ts index 47f3eca2a..8229a63c4 100644 --- a/blocks/blocks/slack.ts +++ b/blocks/blocks/slack.ts @@ -12,7 +12,7 @@ export const SlackMessageBlock: BlockConfig = { category: 'tools' }, tools: { - access: ['slack.message'] + access: ['slack_message'] }, workflow: { inputs: { diff --git a/blocks/blocks/translate.ts b/blocks/blocks/translate.ts index 76f2ee5fc..d9f09132a 100644 --- a/blocks/blocks/translate.ts +++ b/blocks/blocks/translate.ts @@ -22,7 +22,7 @@ export const TranslateBlock: BlockConfig = { category: 'tools', }, tools: { - access: ['openai.chat', 'anthropic.chat', 'google.chat'], + access: ['openai_chat', 'anthropic_chat', 'google_chat'], config: { tool: (params: Record) => { const model = params.model || 'gpt-4o' diff --git a/blocks/consts.ts b/blocks/consts.ts index c67cc63b9..20c9b5329 100644 --- a/blocks/consts.ts +++ b/blocks/consts.ts @@ -1,12 +1,12 @@ export const MODEL_TOOLS = { - 'gpt-4o': 'openai.chat', - 'o1': 'openai.chat', - 'o1-mini': 'openai.chat', - 'deepseek-v3': 'deepseek.chat', - 'deepseek-r1': 'deepseek.reasoner', - 'claude-3-5-sonnet-20241022': 'anthropic.chat', - 'gemini-pro': 'google.chat', - 'grok-2-latest': 'xai.chat' + 'gpt-4o': 'openai_chat', + 'o1': 'openai_chat', + 'o1-mini': 'openai_chat', + 'deepseek-v3': 'deepseek_chat', + 'deepseek-r1': 'deepseek_reasoner', + 'claude-3-5-sonnet-20241022': 'anthropic_chat', + 'gemini-pro': 'google_chat', + 'grok-2-latest': 'xai_chat' } as const export type ModelType = keyof typeof MODEL_TOOLS diff --git a/blocks/types.ts b/blocks/types.ts index 4f951c479..5f6c755f8 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -1,17 +1,30 @@ import type { SVGProps } from 'react' import type { JSX } from 'react' import { ToolResponse } from '@/tools/types' -import { ExtractToolOutput, ToolOutputToValueType } from './utils' + +// Tool output type utilities +export type ExtractToolOutput = T extends ToolResponse + ? T['output'] + : never + +export type ToolOutputToValueType = T extends Record + ? { + [K in keyof T]: T[K] extends string ? 'string' + : T[K] extends number ? 'number' + : T[K] extends boolean ? 'boolean' + : T[K] extends object ? 'json' + : 'any' + } + : never export type BlockIcon = (props: SVGProps) => JSX.Element export type BlockCategory = 'blocks' | 'tools' -export type PrimitiveValueType = 'string' | 'number' | 'json' | 'boolean' | 'any' -export type ValueType = PrimitiveValueType | Record +export type PrimitiveValueType = 'string' | 'number' | 'boolean' | 'json' | 'any' export type BlockOutput = - | PrimitiveValueType - | { [key: string]: BlockOutput } + | PrimitiveValueType + | { [key: string]: PrimitiveValueType | Record } export type ParamType = 'string' | 'number' | 'boolean' | 'json' @@ -63,4 +76,15 @@ export interface BlockConfig { } } } +} + +export interface OutputConfig { + type: BlockOutput + dependsOn?: { + subBlockId: string + condition: { + whenEmpty: BlockOutput + whenFilled: BlockOutput + } + } } \ No newline at end of file diff --git a/blocks/utils.ts b/blocks/utils.ts index 5f8679954..e73df5a81 100644 --- a/blocks/utils.ts +++ b/blocks/utils.ts @@ -1,27 +1,11 @@ import { BlockState, SubBlockState } from '@/stores/workflow/types' -import { BlockOutput, OutputConfig, PrimitiveValueType } from '@/blocks/types' -import { ToolResponse } from '@/tools/types' +import { BlockOutput, OutputConfig } from '@/blocks/types' interface CodeLine { id: string content: string } -// Tool output type utilities -export type ExtractToolOutput = T extends ToolResponse - ? T['output'] - : never - -export type ToolOutputToValueType = T extends Record - ? { - [K in keyof T]: T[K] extends string ? 'string' - : T[K] extends number ? 'number' - : T[K] extends boolean ? 'boolean' - : T[K] extends object ? 'json' - : 'any' - } - : never - function isEmptyValue(value: SubBlockState['value']): boolean { if (value === null || value === undefined) return true if (typeof value === 'string') return value.trim() === '' diff --git a/executor/index.ts b/executor/index.ts index ad42fa0cf..bf521b848 100644 --- a/executor/index.ts +++ b/executor/index.ts @@ -17,7 +17,10 @@ import { ExecutionResult, BlockLog } from './types' -import { tools, executeTool } from '@/tools' +import { tools, executeTool, getTool } from '@/tools' +import { executeProviderRequest } from '@/providers/service' +import { getAllBlocks } from '@/blocks' +import { BlockConfig } from '@/blocks/types' export class Executor { constructor( @@ -209,7 +212,93 @@ export class Executor { block: SerializedBlock, inputs: Record, context: ExecutionContext - ): Promise { + ): Promise<{ response: Record }> { + // Special handling for agent blocks that use providers + if (block.metadata?.type === 'agent') { + const model = inputs.model || 'gpt-4o' + const providerId = model.startsWith('gpt') || model.startsWith('o1') + ? 'openai' + : model.startsWith('claude') + ? 'anthropic' + : model.startsWith('gemini') + ? 'google' + : model.startsWith('grok') + ? 'xai' + : 'deepseek' + + // Format tools if they exist + const tools = Array.isArray(inputs.tools) ? inputs.tools.map((tool: any) => { + // Get the tool ID from the block type + const block = getAllBlocks().find((b: BlockConfig) => b.type === tool.type) + const toolId = block?.tools.access[0] + if (!toolId) return null + + // Get the tool configuration + const toolConfig = getTool(toolId) + if (!toolConfig) return null + + // Return the tool configuration with parameters + return { + id: toolConfig.id, + name: toolConfig.name, + description: toolConfig.description, + // Store the actual parameters from the tool input + params: tool.params || {}, + parameters: { + type: 'object', + properties: Object.entries(toolConfig.params).reduce((acc, [key, config]) => ({ + ...acc, + [key]: { + type: config.type === 'json' ? 'object' : config.type, + description: config.description || '', + ...(key in (tool.params || {}) && { default: tool.params[key] }) + } + }), {}), + required: Object.entries(toolConfig.params) + .filter(([_, config]) => config.required) + .map(([key]) => key) + } + } + }).filter((t): t is NonNullable => t !== null) : [] + + const requestPayload = { + model, + systemPrompt: inputs.systemPrompt, + context: inputs.context, + tools: tools.length > 0 ? tools : undefined, + temperature: inputs.temperature, + maxTokens: inputs.maxTokens, + apiKey: inputs.apiKey + } + + // Log the request payload for debugging + console.log('Provider Request:', { + providerId, + model, + tools: requestPayload.tools, + }) + + const response = await executeProviderRequest(providerId, requestPayload) + + // Return the actual response values + return { + response: { + content: response.content, + model: response.model, + tokens: response.tokens || { + prompt: 0, + completion: 0, + total: 0 + }, + toolCalls: { + list: response.toolCalls || [], + count: response.toolCalls?.length || 0 + } + } + } + } + + // Regular tool execution for non-agent blocks const toolId = block.config.tool if (!toolId) { throw new Error(`Block "${block.id}" does not specify a tool`) diff --git a/providers/anthropic/index.ts b/providers/anthropic/index.ts new file mode 100644 index 000000000..5e759818b --- /dev/null +++ b/providers/anthropic/index.ts @@ -0,0 +1,115 @@ +import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest, Message } from '../types' +import { ToolConfig } from '@/tools/types' + +export const anthropicProvider: ProviderConfig = { + id: 'anthropic', + name: 'Anthropic', + description: 'Anthropic\'s Claude models', + version: '1.0.0', + models: ['claude-3-5-sonnet-20241022'], + defaultModel: 'claude-3-5-sonnet-20241022', + + baseUrl: 'https://api.anthropic.com/v1/messages', + headers: (apiKey: string) => ({ + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01' + }), + + createRequest: (request: ProviderRequest, functions?: any) => ({ + model: request.model || anthropicProvider.defaultModel, + messages: [ + ...(request.context ? [{ role: 'user', content: request.context }] : []) + ], + system: request.systemPrompt, + temperature: request.temperature, + max_tokens: request.maxTokens, + ...(functions && { + tools: functions + }) + }), + + extractResponse: (response: any) => { + const data = response.output || response + const textContent = data.content?.find((item: any) => item.type === 'text') + + return { + content: textContent?.text || '', + tokens: { + prompt: data.usage?.input_tokens, + completion: data.usage?.output_tokens, + total: data.usage?.input_tokens + data.usage?.output_tokens + } + } + }, + + handleToolCall: (response: any) => { + const data = response.output || response + const hasToolUse = data.content?.some((item: any) => item.type === 'tool_use') + + if (!hasToolUse) { + const textContent = data.content?.find((item: any) => item.type === 'text') + return { + hasFunctionCall: false, + content: textContent?.text || '' + } + } + + return { + hasFunctionCall: true + } + }, + + createToolCallMessage: (functionCall: FunctionCallResponse, result: any): Message => ({ + role: 'assistant', + content: [ + { + type: 'tool_use', + name: functionCall.name, + input: functionCall.arguments + } + ] + }), + + transformToolsToFunctions: (tools: ProviderToolConfig[]) => { + return tools.map(tool => ({ + type: 'function', + name: tool.id, + description: tool.description, + parameters: { + type: 'object', + properties: Object.entries(tool.params).reduce((acc, [key, config]) => { + acc[key] = { + type: config.type, + description: config.description, + ...(config.default && { default: config.default }) + } + return acc + }, {} as Record), + required: Object.entries(tool.params) + .filter(([_, config]) => config.required) + .map(([key]) => key) + } + })) + }, + + transformFunctionCallResponse: (response: any): FunctionCallResponse => { + const content = response.output ? response.output.content : response.content + + if (!content || !Array.isArray(content)) { + throw new Error('Invalid response format: content is missing or not an array') + } + + const toolUse = content.find(item => item.type === 'tool_use') + if (!toolUse) { + throw new Error('No tool use found in response') + } + + return { + name: toolUse.name, + arguments: typeof toolUse.input === 'string' + ? JSON.parse(toolUse.input) + : toolUse.input + } + } +} \ No newline at end of file diff --git a/providers/openai/index.ts b/providers/openai/index.ts new file mode 100644 index 000000000..4a1cdbdab --- /dev/null +++ b/providers/openai/index.ts @@ -0,0 +1,60 @@ +import { ProviderConfig, FunctionCallResponse, ProviderToolConfig } from '../types' +import { ToolConfig } from '@/tools/types' + +export const openaiProvider: ProviderConfig = { + id: 'openai', + name: 'OpenAI', + description: 'OpenAI\'s GPT models', + version: '1.0.0', + models: ['gpt-4o', 'o1', 'o1-mini'], + defaultModel: 'gpt-4o', + + baseUrl: 'https://api.openai.com/v1/chat/completions', + headers: (apiKey: string) => ({ + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` + }), + + transformToolsToFunctions: (tools: ProviderToolConfig[]) => { + if (!tools || tools.length === 0) { + return undefined + } + + return tools.map(tool => ({ + name: tool.id, + description: tool.description || '', + parameters: { + ...tool.parameters, + properties: Object.entries(tool.parameters.properties).reduce((acc, [key, value]) => ({ + ...acc, + [key]: { + ...value, + ...(key in tool.params && { default: tool.params[key] }) + } + }), {}) + } + })) + }, + + transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => { + const functionCall = response.choices?.[0]?.message?.function_call + if (!functionCall) { + throw new Error('No function call found in response') + } + + const args = typeof functionCall.arguments === 'string' + ? JSON.parse(functionCall.arguments) + : functionCall.arguments + + const tool = tools?.find(t => t.id === functionCall.name) + const toolParams = tool?.params || {} + + return { + name: functionCall.name, + arguments: { + ...toolParams, // First spread the stored params to ensure they're used as defaults + ...args // Then spread any overrides from the function call + } + } + } +} diff --git a/providers/service.ts b/providers/service.ts new file mode 100644 index 000000000..5559502f7 --- /dev/null +++ b/providers/service.ts @@ -0,0 +1,215 @@ +import { ProviderConfig, ProviderRequest, ProviderResponse, Message } from './types' +import { openaiProvider } from './openai' +import { anthropicProvider } from './anthropic' +import { ToolConfig } from '@/tools/types' +import { getTool, executeTool } from '@/tools' + +// Register providers +const providers: Record = { + openai: openaiProvider, + anthropic: anthropicProvider, + // Add other providers here as they're implemented +} + +export async function executeProviderRequest( + providerId: string, + request: ProviderRequest +): Promise { + const provider = providers[providerId] + if (!provider) { + throw new Error(`Provider not found: ${providerId}`) + } + + // Only transform tools if they are provided and non-empty + const functions = request.tools && request.tools.length > 0 + ? provider.transformToolsToFunctions(request.tools) + : undefined + + // Base payload that's common across providers + const basePayload = { + model: request.model || provider.defaultModel, + messages: [ + { role: 'system' as const, content: request.systemPrompt }, + ...(request.context ? [{ role: 'user' as const, content: request.context }] : []) + ] as Message[], + temperature: request.temperature, + max_tokens: request.maxTokens + } + + // Provider-specific payload adjustments + let payload + switch (providerId) { + case 'openai': + payload = { + ...basePayload, + ...(functions && { + functions, + function_call: 'auto' + }) + } + break + case 'anthropic': + payload = { + ...basePayload, + system: request.systemPrompt, + messages: request.context ? [{ role: 'user', content: request.context }] : [], + ...(functions && { + tools: functions + }) + } + break + default: + payload = { + ...basePayload, + ...(functions && { functions }) + } + } + + // Make the API request through the proxy + const response = await fetch('/api/proxy', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + toolId: `${providerId}/chat`, + params: { + ...payload, + apiKey: request.apiKey + } + }) + }) + + if (!response.ok) { + const error = await response.json() + throw new Error(error.error || 'Provider API error') + } + + const { output: data } = await response.json() + + // Extract content and tokens based on provider + let content = '' + let tokens = undefined + + switch (providerId) { + case 'anthropic': + content = data.content?.[0]?.text || '' + tokens = { + prompt: data.usage?.input_tokens, + completion: data.usage?.output_tokens, + total: data.usage?.input_tokens + data.usage?.output_tokens + } + break + default: + content = data.choices?.[0]?.message?.content || '' + tokens = data.usage && { + prompt: data.usage.prompt_tokens, + completion: data.usage.completion_tokens, + total: data.usage.total_tokens + } + } + + // Check for function calls + let toolCalls = [] + let toolResults = [] + let currentMessages = [...basePayload.messages] + + try { + let currentResponse = data + let hasMoreCalls = true + + while (hasMoreCalls) { + const hasFunctionCall = + (providerId === 'openai' && currentResponse.choices?.[0]?.message?.function_call) || + (providerId === 'anthropic' && currentResponse.content?.some((item: any) => item.type === 'function_call')) + + if (!hasFunctionCall) { + // No more function calls, use the content from the current response + content = currentResponse.choices?.[0]?.message?.content || '' + hasMoreCalls = false + continue + } + + const functionCall = provider.transformFunctionCallResponse(currentResponse, request.tools) + if (!functionCall) { + hasMoreCalls = false + continue + } + + // Execute the tool + const tool = getTool(functionCall.name) + if (!tool) { + throw new Error(`Tool not found: ${functionCall.name}`) + } + + const result = await executeTool(functionCall.name, functionCall.arguments) + if (result.success) { + toolResults.push(result.output) + toolCalls.push(functionCall) + + // Add the assistant's function call and the function result to the message history + currentMessages.push({ + role: 'assistant', + content: null, + function_call: { + name: functionCall.name, + arguments: JSON.stringify(functionCall.arguments) + } + }) + currentMessages.push({ + role: 'function', + name: functionCall.name, + content: JSON.stringify(result.output) + }) + + // Make the next call through the proxy + const nextResponse = await fetch('/api/proxy', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + toolId: `${providerId}/chat`, + params: { + ...basePayload, + messages: currentMessages, + ...(functions && { functions, function_call: 'auto' }), + apiKey: request.apiKey + } + }) + }) + + if (!nextResponse.ok) { + const error = await nextResponse.json() + throw new Error(error.error || 'Provider API error') + } + + const { output: nextData } = await nextResponse.json() + currentResponse = nextData + + // Update tokens + if (nextData.usage) { + tokens = { + prompt: (tokens?.prompt || 0) + nextData.usage.prompt_tokens, + completion: (tokens?.completion || 0) + nextData.usage.completion_tokens, + total: (tokens?.total || 0) + nextData.usage.total_tokens + } + } + } else { + hasMoreCalls = false + } + } + } catch (error: any) { + console.error('Error executing tool:', error) + throw error + } + + return { + content, + model: data.model, + tokens, + toolCalls: toolCalls.length > 0 ? toolCalls : undefined, + toolResults: toolResults.length > 0 ? toolResults : undefined + } +} + diff --git a/providers/types.ts b/providers/types.ts new file mode 100644 index 000000000..849b98a8b --- /dev/null +++ b/providers/types.ts @@ -0,0 +1,74 @@ +import { ToolConfig } from '@/tools/types' + +export interface ProviderConfig { + id: string + name: string + description: string + version: string + models: string[] + defaultModel: string + + // Provider-specific configuration + baseUrl: string + headers: (apiKey: string) => Record + + // Tool calling support + transformToolsToFunctions: (tools: ProviderToolConfig[]) => any + transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]) => FunctionCallResponse + + // Internal state for tool name mapping + _toolNameMapping?: Map +} + +export interface FunctionCallResponse { + name: string + arguments: Record +} + +export interface ProviderResponse { + content: string + model: string + tokens?: { + prompt?: number + completion?: number + total?: number + } + toolCalls?: FunctionCallResponse[] + toolResults?: any[] +} + +export interface ProviderToolConfig { + id: string + name: string + description: string + params: Record + parameters: { + type: string + properties: Record + required: string[] + } +} + +export interface Message { + role: 'system' | 'user' | 'assistant' | 'function' + content: string | null + name?: string + function_call?: { + name: string + arguments: string + } +} + +export interface ProviderRequest { + model: string + systemPrompt: string + context?: string + tools?: ProviderToolConfig[] + temperature?: number + maxTokens?: number + apiKey: string + messages?: Message[] +} + +// Map of provider IDs to their configurations +export const providers: Record = {} diff --git a/tools/anthropic/chat.ts b/tools/anthropic/chat.ts index dc11f0f1c..708ad66e3 100644 --- a/tools/anthropic/chat.ts +++ b/tools/anthropic/chat.ts @@ -20,7 +20,7 @@ export interface ChatResponse extends ToolResponse { } export const chatTool: ToolConfig = { - id: 'anthropic.chat', + id: 'anthropic_chat', name: 'Anthropic Chat', description: 'Chat with Anthropic Claude models', version: '1.0.0', diff --git a/tools/crewai/vision.ts b/tools/crewai/vision.ts index 0aa72a0a9..551bad7e3 100644 --- a/tools/crewai/vision.ts +++ b/tools/crewai/vision.ts @@ -16,7 +16,7 @@ export interface VisionResponse extends ToolResponse { } export const visionTool: ToolConfig = { - id: 'crewai.vision', + id: 'crewai_vision', name: 'Vision Analysis', description: 'Analyze images using vision models', version: '1.0.0', diff --git a/tools/deepseek/chat.ts b/tools/deepseek/chat.ts index 60273cb84..08f2c6516 100644 --- a/tools/deepseek/chat.ts +++ b/tools/deepseek/chat.ts @@ -23,7 +23,7 @@ export interface ChatResponse extends ToolResponse { } export const chatTool: ToolConfig = { - id: 'deepseek.chat', + id: 'deepseek_chat', name: 'DeepSeek Chat', description: 'Chat with DeepSeek-v3 model', version: '1.0.0', diff --git a/tools/deepseek/reasoner.ts b/tools/deepseek/reasoner.ts index ec73fd07c..c4a0d424a 100644 --- a/tools/deepseek/reasoner.ts +++ b/tools/deepseek/reasoner.ts @@ -22,7 +22,7 @@ export interface ChatResponse extends ToolResponse { } export const reasonerTool: ToolConfig = { - id: 'deepseek.reasoner', + id: 'deepseek_reasoner', name: 'DeepSeek Reasoner', description: 'Chat with DeepSeek-R1 reasoning model', version: '1.0.0', diff --git a/tools/firecrawl/scrape.ts b/tools/firecrawl/scrape.ts index eff054955..8257c2253 100644 --- a/tools/firecrawl/scrape.ts +++ b/tools/firecrawl/scrape.ts @@ -32,7 +32,7 @@ export interface ScrapeResponse extends ToolResponse { } export const scrapeTool: ToolConfig = { - id: 'firecrawl.scrape', + id: 'firecrawl_scrape', name: 'Firecrawl Website Scraper', description: 'Extract clean content from any webpage in markdown format', version: '1.0.0', diff --git a/tools/function/execute.ts b/tools/function/execute.ts index e614ea1da..aabf4eea1 100644 --- a/tools/function/execute.ts +++ b/tools/function/execute.ts @@ -13,7 +13,7 @@ export interface CodeExecutionOutput extends ToolResponse { } export const functionExecuteTool: ToolConfig = { - id: 'function.execute', + id: 'function_execute', name: 'Function Execute', description: 'Execute code in a sandboxed environment', version: '1.0.0', diff --git a/tools/github/repo.ts b/tools/github/repo.ts index e5148da32..d7fb5483a 100644 --- a/tools/github/repo.ts +++ b/tools/github/repo.ts @@ -18,7 +18,7 @@ export interface RepoInfoResponse extends ToolResponse { } export const repoInfoTool: ToolConfig = { - id: 'github.repoinfo', + id: 'github_repoinfo', name: 'GitHub Repository Info', description: 'Fetch detailed information about a GitHub repository', version: '1.0.0', diff --git a/tools/google/chat.ts b/tools/google/chat.ts index de0846701..be7ca31ad 100644 --- a/tools/google/chat.ts +++ b/tools/google/chat.ts @@ -21,7 +21,7 @@ export interface ChatResponse extends ToolResponse { } export const chatTool: ToolConfig = { - id: 'google.chat', + id: 'google_chat', name: 'Google Chat', description: 'Chat with Google Gemini models', version: '1.0.0', diff --git a/tools/http/request.ts b/tools/http/request.ts index 8e1355997..8bf5864ca 100644 --- a/tools/http/request.ts +++ b/tools/http/request.ts @@ -21,7 +21,7 @@ export interface RequestResponse extends ToolResponse { } export const requestTool: ToolConfig = { - id: 'http.request', + id: 'http_request', name: 'HTTP Request', description: 'Make HTTP requests to any endpoint with support for CRUD operations', version: '1.0.0', diff --git a/tools/hubspot/contacts.ts b/tools/hubspot/contacts.ts index ed968d656..b9226e4e6 100644 --- a/tools/hubspot/contacts.ts +++ b/tools/hubspot/contacts.ts @@ -27,7 +27,7 @@ export interface ContactsResponse extends ToolResponse { } export const contactsTool: ToolConfig = { - id: 'hubspot.contacts', + id: 'hubspot_contacts', name: 'HubSpot Contacts', description: 'Manage HubSpot contacts - create, search, and update contact records', version: '1.0.0', diff --git a/tools/index.ts b/tools/index.ts index 594a0c084..74ffedd96 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -18,29 +18,29 @@ import { repoInfoTool } from './github/repo' // Registry of all available tools export const tools: Record = { // AI Models - 'openai.chat': openAIChat, - 'anthropic.chat': anthropicChat, - 'google.chat': googleChat, - 'xai.chat': xaiChat, - 'deepseek.chat': deepseekChat, - 'deepseek.reasoner': deepseekReasoner, + 'openai_chat': openAIChat, + 'anthropic_chat': anthropicChat, + 'google_chat': googleChat, + 'xai_chat': xaiChat, + 'deepseek_chat': deepseekChat, + 'deepseek_reasoner': deepseekReasoner, // HTTP - 'http.request': httpRequest, + 'http_request': httpRequest, // CRM Tools - 'hubspot.contacts': hubspotContacts, - 'salesforce.opportunities': salesforceOpportunities, + 'hubspot_contacts': hubspotContacts, + 'salesforce_opportunities': salesforceOpportunities, // Function Tools - 'function.execute': functionExecute, + 'function_execute': functionExecute, // CrewAI Tools - 'crewai.vision': crewAIVision, + 'crewai_vision': crewAIVision, // Firecrawl Tools - 'firecrawl.scrape': scrapeTool, + 'firecrawl_scrape': scrapeTool, // Jina Tools - 'jina.readurl': readUrlTool, + 'jina_readurl': readUrlTool, // Slack Tools - 'slack.message': slackMessageTool, + 'slack_message': slackMessageTool, // GitHub Tools - 'github.repoinfo': repoInfoTool + 'github_repoinfo': repoInfoTool } // Get a tool by its ID diff --git a/tools/jina/reader.ts b/tools/jina/reader.ts index fdc4d644b..6b6546049 100644 --- a/tools/jina/reader.ts +++ b/tools/jina/reader.ts @@ -16,7 +16,7 @@ export interface ReadUrlResponse extends ToolResponse { } export const readUrlTool: ToolConfig = { - id: 'jina.readurl', + id: 'jina_readurl', name: 'Jina Reader', description: 'Convert any URL to LLM-friendly text using Jina AI Reader', version: '1.0.0', diff --git a/tools/openai/chat.ts b/tools/openai/chat.ts index bb8010b59..891e232a4 100644 --- a/tools/openai/chat.ts +++ b/tools/openai/chat.ts @@ -24,7 +24,7 @@ export interface ChatResponse extends ToolResponse { } export const chatTool: ToolConfig = { - id: 'openai.chat', + id: 'openai_chat', name: 'OpenAI Chat', description: 'Chat with OpenAI models', version: '1.0.0', diff --git a/tools/salesforce/opportunities.ts b/tools/salesforce/opportunities.ts index 33ae45d4a..fe0f4ee2a 100644 --- a/tools/salesforce/opportunities.ts +++ b/tools/salesforce/opportunities.ts @@ -28,7 +28,7 @@ export interface OpportunityResponse extends ToolResponse { } export const opportunitiesTool: ToolConfig = { - id: 'salesforce.opportunities', + id: 'salesforce_opportunities', name: 'Salesforce Opportunities', description: 'Manage Salesforce opportunities - create, query, and update opportunity records', version: '1.0.0', diff --git a/tools/slack/message.ts b/tools/slack/message.ts index b5acdbee3..826e0d0c6 100644 --- a/tools/slack/message.ts +++ b/tools/slack/message.ts @@ -14,7 +14,7 @@ export interface SlackMessageResponse extends ToolResponse { } export const slackMessageTool: ToolConfig = { - id: 'slack.message', + id: 'slack_message', name: 'Slack Message', description: 'Send a message to a Slack channel', version: '1.0.0', diff --git a/tools/xai/chat.ts b/tools/xai/chat.ts index fd69da3bc..89c1642f4 100644 --- a/tools/xai/chat.ts +++ b/tools/xai/chat.ts @@ -22,7 +22,7 @@ export interface ChatResponse extends ToolResponse { } export const chatTool: ToolConfig = { - id: 'xai.chat', + id: 'xai_chat', name: 'xAI Chat', description: 'Chat with xAI models', version: '1.0.0',