From e5b56f190d40015a0968676790ad5f629e542060 Mon Sep 17 00:00:00 2001 From: h+ Date: Tue, 10 Jun 2025 19:47:51 +0200 Subject: [PATCH 1/2] feat: support for ollama prerendered agent block list and disable api key field --- apps/sim/blocks/blocks/agent.ts | 11 ++++++++++- apps/sim/providers/ollama/index.ts | 13 ++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index aac533f4baf..d7556f5ece1 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -141,10 +141,19 @@ export const AgentBlock: BlockConfig = { 'claude-opus-4-20250514', 'claude-3-7-sonnet-20250219', 'claude-3-5-sonnet-20240620', + ], not: true, // Show for all models EXCEPT those listed } - : undefined, // Show for all models in non-hosted environments + : { + field: 'model', + // Include all Ollama models for which we don't show the API key field + value: [ + // Ollama models + ...useOllamaStore.getState().models, + ], + not: true, // Show for all models EXCEPT those listed + }, // Show for all models in non-hosted environments }, { id: 'tools', diff --git a/apps/sim/providers/ollama/index.ts b/apps/sim/providers/ollama/index.ts index e71c1de4207..fb06af086e4 100644 --- a/apps/sim/providers/ollama/index.ts +++ b/apps/sim/providers/ollama/index.ts @@ -19,14 +19,16 @@ export const ollamaProvider: ProviderConfig = { // Initialize the provider by fetching available models async initialize() { - if (typeof window !== 'undefined') { - logger.info('Skipping Ollama initialization on client side to avoid CORS issues') - return - } + // Disabled too allow for client side initialization + // if (typeof window !== 'undefined') { + // logger.info('Skipping Ollama initialization on client side to avoid CORS issues') + // return + // } try { const response = await fetch(`${OLLAMA_HOST}/api/tags`) if (!response.ok) { + console.log('response', response) useOllamaStore.getState().setModels([]) logger.warn('Ollama service is not available. The provider will be disabled.') return @@ -34,6 +36,7 @@ export const ollamaProvider: ProviderConfig = { const data = (await response.json()) as ModelsObject this.models = data.models.map((model) => model.name) useOllamaStore.getState().setModels(this.models) + logger.info('Ollama models initialized', { models: this.models }) } catch (error) { logger.warn('Ollama model instantiation failed. The provider will be disabled.', { error: error instanceof Error ? error.message : 'Unknown error', @@ -58,8 +61,8 @@ export const ollamaProvider: ProviderConfig = { try { // Prepare messages array const ollama = new OpenAI({ - apiKey: 'empty', baseURL: `${OLLAMA_HOST}/v1`, + apiKey: 'ollama', }) // Start with an empty array for all messages From fa5a6436821c636af9aa7a11b4bc1c7e5ee6e5ef Mon Sep 17 00:00:00 2001 From: h+ Date: Wed, 18 Jun 2025 20:02:40 +0200 Subject: [PATCH 2/2] chore: api key validation & middleware handling for ollama models --- .../workflow-block/workflow-block.tsx | 22 +++++++--- apps/sim/blocks/blocks/agent.ts | 40 +++---------------- apps/sim/lib/env.ts | 2 + apps/sim/providers/ollama/index.ts | 11 ++--- apps/sim/providers/utils.ts | 7 +++- docker-compose.local.yml | 2 +- docker-compose.prod.yml | 2 +- 7 files changed, 35 insertions(+), 51 deletions(-) diff --git a/apps/sim/app/w/[id]/components/workflow-block/workflow-block.tsx b/apps/sim/app/w/[id]/components/workflow-block/workflow-block.tsx index 89309c40810..786a0fee76a 100644 --- a/apps/sim/app/w/[id]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/w/[id]/components/workflow-block/workflow-block.tsx @@ -9,6 +9,7 @@ import { parseCronToHumanReadable } from '@/lib/schedules/utils' import { cn, formatDateTime, validateName } from '@/lib/utils' import type { BlockConfig, SubBlockConfig } from '@/blocks/types' import { useExecutionStore } from '@/stores/execution/store' +import { useOllamaStore } from '@/stores/ollama/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { mergeSubblockState } from '@/stores/workflows/utils' import { useWorkflowStore } from '@/stores/workflows/workflow/store' @@ -279,6 +280,7 @@ export function WorkflowBlock({ id, data }: NodeProps) { } const isAdvancedMode = useWorkflowStore.getState().blocks[blockId]?.advancedMode ?? false + const ollamaModels = useOllamaStore.getState().models // Filter visible blocks and those that meet their conditions const visibleSubBlocks = subBlocks.filter((block) => { @@ -299,15 +301,25 @@ export function WorkflowBlock({ id, data }: NodeProps) { ? stateToUse[block.condition.and.field]?.value : undefined + // Special handling for Ollama models condition + let conditionValue = block.condition.value + if ( + block.condition.field === 'model' && + Array.isArray(conditionValue) && + conditionValue.length === 0 + ) { + conditionValue = ollamaModels + } + // Check if the condition value is an array - const isValueMatch = Array.isArray(block.condition.value) + const isValueMatch = Array.isArray(conditionValue) ? fieldValue != null && (block.condition.not - ? !block.condition.value.includes(fieldValue as string | number | boolean) - : block.condition.value.includes(fieldValue as string | number | boolean)) + ? !conditionValue.includes(fieldValue as string | number | boolean) + : conditionValue.includes(fieldValue as string | number | boolean)) : block.condition.not - ? fieldValue !== block.condition.value - : fieldValue === block.condition.value + ? fieldValue !== conditionValue + : fieldValue === conditionValue // Check both conditions if 'and' is present const isAndValueMatch = diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index d7556f5ece1..ff873b30043 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -1,5 +1,4 @@ import { AgentIcon } from '@/components/icons' -import { isHosted } from '@/lib/environment' import { createLogger } from '@/lib/logs/console-logger' import { MODELS_TEMP_RANGE_0_1, MODELS_TEMP_RANGE_0_2 } from '@/providers/model-capabilities' import { getAllModelProviders, getBaseModelProviders } from '@/providers/utils' @@ -121,39 +120,12 @@ export const AgentBlock: BlockConfig = { placeholder: 'Enter your API key', password: true, connectionDroppable: false, - // Hide API key for all OpenAI and Claude models when running on hosted version - condition: isHosted - ? { - field: 'model', - // Include all OpenAI models and Claude models for which we don't show the API key field - value: [ - // OpenAI models - 'gpt-4o', - 'o1', - 'o1-mini', - 'o1-preview', - 'o3', - 'o3-preview', - 'o4-mini', - 'gpt-4.1', - // Claude models - 'claude-sonnet-4-20250514', - 'claude-opus-4-20250514', - 'claude-3-7-sonnet-20250219', - 'claude-3-5-sonnet-20240620', - - ], - not: true, // Show for all models EXCEPT those listed - } - : { - field: 'model', - // Include all Ollama models for which we don't show the API key field - value: [ - // Ollama models - ...useOllamaStore.getState().models, - ], - not: true, // Show for all models EXCEPT those listed - }, // Show for all models in non-hosted environments + condition: { + // Hide API key for all OpenAI and Claude models when running on hosted version + field: 'model', // Include all Ollama models for which we don't show the API key field + value: useOllamaStore.getState().models, + not: true, // Show for all models EXCEPT those listed + }, // Show for all models in non-hosted environments }, { id: 'tools', diff --git a/apps/sim/lib/env.ts b/apps/sim/lib/env.ts index 30d875707e1..f5016de4c01 100644 --- a/apps/sim/lib/env.ts +++ b/apps/sim/lib/env.ts @@ -113,6 +113,7 @@ export const env = createEnv({ NEXT_PUBLIC_GOOGLE_CLIENT_ID: z.string().optional(), NEXT_PUBLIC_GOOGLE_API_KEY: z.string().optional(), NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER: z.string().optional(), + NEXT_PUBLIC_OLLAMA_URL: z.string().url().optional(), }, // Only need to define client variables, server variables are automatically handled @@ -123,5 +124,6 @@ export const env = createEnv({ NEXT_PUBLIC_GOOGLE_CLIENT_ID: getEnv('NEXT_PUBLIC_GOOGLE_CLIENT_ID'), NEXT_PUBLIC_GOOGLE_API_KEY: getEnv('NEXT_PUBLIC_GOOGLE_API_KEY'), NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER: getEnv('NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER'), + NEXT_PUBLIC_OLLAMA_URL: getEnv('NEXT_PUBLIC_OLLAMA_URL'), }, }) diff --git a/apps/sim/providers/ollama/index.ts b/apps/sim/providers/ollama/index.ts index fb06af086e4..3481c2808a4 100644 --- a/apps/sim/providers/ollama/index.ts +++ b/apps/sim/providers/ollama/index.ts @@ -7,7 +7,7 @@ import type { ProviderConfig, ProviderRequest, ProviderResponse, TimeSegment } f import type { ModelsObject } from './types' const logger = createLogger('OllamaProvider') -const OLLAMA_HOST = env.OLLAMA_URL || 'http://localhost:11434' +const OLLAMA_HOST = env.NEXT_PUBLIC_OLLAMA_URL || 'http://localhost:11434' export const ollamaProvider: ProviderConfig = { id: 'ollama', @@ -19,13 +19,9 @@ export const ollamaProvider: ProviderConfig = { // Initialize the provider by fetching available models async initialize() { - // Disabled too allow for client side initialization - // if (typeof window !== 'undefined') { - // logger.info('Skipping Ollama initialization on client side to avoid CORS issues') - // return - // } - + const OLLAMA_HOST = env.NEXT_PUBLIC_OLLAMA_URL || 'http://localhost:11434' try { + logger.info('Ollama host', { OLLAMA_HOST }) const response = await fetch(`${OLLAMA_HOST}/api/tags`) if (!response.ok) { console.log('response', response) @@ -45,7 +41,6 @@ export const ollamaProvider: ProviderConfig = { }, executeRequest: async (request: ProviderRequest): Promise => { - console.log(request) logger.info('Preparing Ollama request', { model: request.model, hasSystemPrompt: !!request.systemPrompt, diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index c6e7b91c5ec..5ba41959dd2 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -484,8 +484,9 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str // Use server key rotation for all OpenAI models and Anthropic's Claude models on the hosted platform const isOpenAIModel = provider === 'openai' const isClaudeModel = provider === 'anthropic' + const isOllamaModel = provider === 'ollama' - if (isHosted && (isOpenAIModel || isClaudeModel)) { + if (isHosted && (isOpenAIModel || (isClaudeModel && !isOllamaModel))) { try { // Import the key rotation function const { getRotatingApiKey } = require('@/lib/utils') @@ -496,7 +497,6 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str if (hasUserKey) { return userProvidedKey! } - // Otherwise, throw an error throw new Error(`No API key available for ${provider} ${model}`) } @@ -504,6 +504,9 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str // For all other cases, require user-provided key if (!hasUserKey) { + if (isOllamaModel) { + return 'ollama' + } throw new Error(`API key is required for ${provider} ${model}`) } diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 80b9d2106c7..eea5a3f4bb7 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -21,7 +21,7 @@ services: - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID:-placeholder} - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder} - RESEND_API_KEY=${RESEND_API_KEY:-placeholder} - - OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434} + - NEXT_PUBLIC_OLLAMA_URL=${NEXT_PUBLIC_OLLAMA_URL:-http://localhost:11434} depends_on: db: condition: service_healthy diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 1596b84d1c7..7fe62acba30 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -20,7 +20,7 @@ services: - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID:-placeholder} - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder} - RESEND_API_KEY=${RESEND_API_KEY:-placeholder} - - OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434} + - NEXT_PUBLIC_OLLAMA_URL=${NEXT_PUBLIC_OLLAMA_URL:-http://localhost:11434} depends_on: db: condition: service_healthy