diff --git a/app/w/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/components/workflow-block/components/sub-block/sub-block.tsx index 176cdff96..cfba418dd 100644 --- a/app/w/components/workflow-block/components/sub-block/sub-block.tsx +++ b/app/w/components/workflow-block/components/sub-block/sub-block.tsx @@ -15,6 +15,10 @@ interface SubBlockProps { } export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) { + if (config.hidden) { + return null + } + const handleMouseDown = (e: React.MouseEvent) => { e.stopPropagation() } diff --git a/blocks/blocks/translate.ts b/blocks/blocks/translate.ts new file mode 100644 index 000000000..78d3443ef --- /dev/null +++ b/blocks/blocks/translate.ts @@ -0,0 +1,110 @@ +import { TranslateIcon } from '@/components/icons' +import { BlockConfig } from '../types' +import { ChatResponse } from '@/tools/openai/chat' + +const MODEL_TOOLS = { + 'gpt-4o': 'openai.chat', + 'o1': 'openai.chat', + 'o1-mini': 'openai.chat', + 'claude-3-5-sonnet-20241022': 'anthropic.chat', + 'gemini-pro': 'google.chat', +} as const + +const getTranslationPrompt = (targetLanguage: string) => `You are a highly skilled translator. Your task is to translate the given text into ${targetLanguage || 'English'} while: +1. Preserving the original meaning and nuance +2. Maintaining appropriate formality levels +3. Adapting idioms and cultural references appropriately +4. Preserving formatting and special characters +5. Handling technical terms accurately + +Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.` + +export const TranslateBlock: BlockConfig = { + type: 'translate', + toolbar: { + title: 'Translate', + description: 'Translate text to any language', + bgColor: '#FF4B4B', + icon: TranslateIcon, + category: 'basic', + }, + tools: { + access: ['openai.chat', 'anthropic.chat', 'google.chat'], + config: { + tool: (params: Record) => { + const model = params.model || 'gpt-4o' + + if (!model) { + throw new Error('No model selected') + } + + const tool = MODEL_TOOLS[model as keyof typeof MODEL_TOOLS] + + if (!tool) { + throw new Error(`Invalid model selected: ${model}`) + } + + return tool + } + } + }, + workflow: { + inputs: { + context: { type: 'string', required: true }, + targetLanguage: { type: 'string', required: true }, + apiKey: { type: 'string', required: true }, + systemPrompt: { type: 'string', required: true } + }, + outputs: { + response: { + type: { + content: 'string', + model: 'string', + tokens: 'any' + } + } + }, + subBlocks: [ + { + id: 'context', + title: 'Text to Translate', + type: 'long-input', + layout: 'full', + placeholder: 'Enter the text you want to translate' + }, + { + id: 'targetLanguage', + title: 'Translate To', + type: 'short-input', + layout: 'full', + placeholder: 'Enter language (e.g. Spanish, French, etc.)' + }, + { + id: 'model', + title: 'Model', + type: 'dropdown', + layout: 'half', + options: Object.keys(MODEL_TOOLS) + }, + { + id: 'apiKey', + title: "API Key", + type: "short-input", + layout: "full", + placeholder: "Enter your API key", + password: true, + connectionDroppable: false + }, + { + id: 'systemPrompt', + title: 'System Prompt', + type: 'code', + layout: 'full', + hidden: true, + value: (params: Record) => { + return getTranslationPrompt(params.targetLanguage || 'English') + } + } + ] + } +} \ No newline at end of file diff --git a/blocks/index.ts b/blocks/index.ts index d75f00ad5..3fbb08098 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -7,6 +7,7 @@ import { FunctionBlock } from './blocks/function' import { CrewAIVisionBlock } from './blocks/crewai' import { FirecrawlScrapeBlock } from './blocks/firecrawl' import { JinaBlock } from './blocks/jina' +import { TranslateBlock } from './blocks/translate' // Export blocks for ease of use export { @@ -15,7 +16,8 @@ export { FunctionBlock, CrewAIVisionBlock, FirecrawlScrapeBlock, - JinaBlock + JinaBlock, + TranslateBlock } // Registry of all block configurations @@ -25,7 +27,8 @@ const blocks: Record = { function: FunctionBlock, crewai_vision: CrewAIVisionBlock, firecrawl_scrape: FirecrawlScrapeBlock, - jina_reader: JinaBlock + jina_reader: JinaBlock, + translate: TranslateBlock } // Build a reverse mapping of tools to block types diff --git a/blocks/types.ts b/blocks/types.ts index 5ab5f2843..890213bb4 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -46,6 +46,8 @@ export interface SubBlockConfig { placeholder?: string password?: boolean connectionDroppable?: boolean + hidden?: boolean + value?: (params: Record) => string } export interface BlockConfig { diff --git a/components/icons.tsx b/components/icons.tsx index 105bb4304..c5f4c8a0d 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -956,3 +956,25 @@ export function JinaAIIcon(props: SVGProps) { ) } + +export const TranslateIcon = (props: SVGProps) => ( + + + + + + + + +) diff --git a/serializer/index.ts b/serializer/index.ts index 92ea54bac..db61c5dcb 100644 --- a/serializer/index.ts +++ b/serializer/index.ts @@ -58,10 +58,27 @@ export class Serializer { } private extractParams(block: BlockState): Record { + const blockConfig = getBlock(block.type) + if (!blockConfig) { + throw new Error(`Invalid block type: ${block.type}`) + } + const params: Record = {} + + // First collect all current values from subBlocks Object.entries(block.subBlocks).forEach(([id, subBlock]) => { params[id] = subBlock.value }) + + // Then check for any subBlocks with default values + blockConfig.workflow.subBlocks.forEach(subBlockConfig => { + const id = subBlockConfig.id + if (params[id] === null && subBlockConfig.value) { + // If the value is null and there's a default value function, use it + params[id] = subBlockConfig.value(params) + } + }) + return params }