@@ -7,12 +7,12 @@ document.addEventListener('DOMContentLoaded', function() {
77 const connectionTooltip = document . getElementById ( 'connection-tooltip' ) ;
88 const emptyCanvasHelp = document . getElementById ( 'empty-canvas-help' ) ;
99
10- // Mark non-OpenAI agents and all tools as "coming soon"
10+ // Mark non-OpenAI or Ollama agents and all tools as "coming soon"
1111 const markComingSoonFeatures = ( ) => {
12- // Apply to agent types (except OpenAI)
12+ // Apply to agent types (except OpenAI & Ollama )
1313 const agentCards = document . querySelectorAll ( '.agent-card' ) ;
1414 agentCards . forEach ( card => {
15- if ( card . getAttribute ( 'data-type' ) !== 'openai' ) {
15+ if ( ! [ 'openai' , 'ollama' ] . includes ( card . getAttribute ( 'data-type' ) ) ) {
1616 card . classList . add ( 'feature-showcase' ) ;
1717
1818 // Add the badge with icon
@@ -91,7 +91,7 @@ document.addEventListener('DOMContentLoaded', function() {
9191 const newAgentTypeSelect = document . getElementById ( 'new-agent-type' ) ;
9292 if ( newAgentTypeSelect ) {
9393 Array . from ( newAgentTypeSelect . options ) . forEach ( option => {
94- if ( option . value !== 'openai' ) {
94+ if ( ! [ 'openai' , 'ollama' ] . includes ( option . value ) ) {
9595 option . disabled = true ;
9696 option . style . color = 'rgba(160, 160, 160, 0.6)' ;
9797 option . style . fontStyle = 'italic' ;
@@ -479,6 +479,9 @@ document.addEventListener('DOMContentLoaded', function() {
479479 case 'openai' :
480480 typeName = 'OpenAI' ;
481481 break ;
482+ case 'ollama' :
483+ typeName = 'Ollama' ;
484+ break ;
482485 case 'anthropic' :
483486 typeName = 'Claude' ;
484487 break ;
@@ -1413,6 +1416,11 @@ document.addEventListener('DOMContentLoaded', function() {
14131416 document . getElementById ( 'openai-api-key' ) . value = nodeData . config . apiKey || '' ;
14141417 document . getElementById ( 'openai-model' ) . value = nodeData . config . model || 'gpt-4o' ;
14151418 document . getElementById ( 'openai-system-message' ) . value = nodeData . config . systemMessage || 'You are a helpful AI assistant.' ;
1419+ } else if ( nodeData . subType === 'ollama' ) {
1420+ document . getElementById ( 'ollama-api-url' ) . value = nodeData . config . apiUrl || '' ;
1421+ document . getElementById ( 'ollama-api-key' ) . value = nodeData . config . apiKey || '' ;
1422+ document . getElementById ( 'ollama-model' ) . value = nodeData . config . model || 'deepseek-r1:latest' ;
1423+ document . getElementById ( 'ollama-system-message' ) . value = nodeData . config . systemMessage || 'You are a helpful AI assistant.' ;
14161424 } else if ( nodeData . subType === 'anthropic' ) {
14171425 document . getElementById ( 'anthropic-api-key' ) . value = nodeData . config . apiKey || '' ;
14181426 document . getElementById ( 'anthropic-model' ) . value = nodeData . config . model || 'claude-3-opus' ;
@@ -2599,6 +2607,31 @@ document.addEventListener('DOMContentLoaded', function() {
25992607 selectedNode . config . model = model ;
26002608 selectedNode . config . systemMessage = systemMessage ;
26012609 }
2610+ } else if ( selectedNode . subType === 'ollama' ) {
2611+ const apiUrl = document . getElementById ( 'ollama-api-url' ) . value ;
2612+ const apiKey = document . getElementById ( 'ollama-api-key' ) . value ;
2613+ const model = document . getElementById ( 'ollama-model' ) . value ;
2614+ const systemMessage = document . getElementById ( 'ollama-system-message' ) . value ;
2615+
2616+ if ( ! apiUrl || apiUrl . trim ( ) === '' ) {
2617+ validationErrors . push ( 'Ollama API url is required' ) ;
2618+ }
2619+
2620+ if ( ! apiKey || apiKey . trim ( ) === '' ) {
2621+ validationErrors . push ( 'Ollama API Key is required' ) ;
2622+ }
2623+
2624+ if ( ! model ) {
2625+ validationErrors . push ( 'Please define a model' ) ;
2626+ }
2627+
2628+ // Store values if validation passes
2629+ if ( validationErrors . length === 0 ) {
2630+ selectedNode . config . apiUrl = apiUrl ;
2631+ selectedNode . config . apiKey = apiKey ;
2632+ selectedNode . config . model = model ;
2633+ selectedNode . config . systemMessage = systemMessage ;
2634+ }
26022635 } else if ( selectedNode . subType === 'anthropic' ) {
26032636 const apiKey = document . getElementById ( 'anthropic-api-key' ) . value ;
26042637 const model = document . getElementById ( 'anthropic-model' ) . value ;
@@ -2824,6 +2857,8 @@ document.addEventListener('DOMContentLoaded', function() {
28242857
28252858 if ( selectedNode . subType === 'openai' && selectedNode . config . model ) {
28262859 modelText = selectedNode . config . model ;
2860+ } else if ( selectedNode . subType === 'ollama' && selectedNode . config . model ) {
2861+ modelText = selectedNode . config . model ;
28272862 } else if ( selectedNode . subType === 'anthropic' && selectedNode . config . model ) {
28282863 modelText = selectedNode . config . model ;
28292864 } else if ( selectedNode . subType === 'bedrock' && selectedNode . config . model ) {
@@ -2891,6 +2926,21 @@ document.addEventListener('DOMContentLoaded', function() {
28912926 </select>
28922927 </div>
28932928 ` ;
2929+ } else if ( agentType === 'ollama' ) {
2930+ configHtml = `
2931+ <div class="form-group">
2932+ <label for="new-ollama-api-url">Ollama API url</label>
2933+ <input type="text" id="new-ollama-api-url" class="form-control" placeholder="http://localhost:11434">
2934+ </div>
2935+ <div class="form-group">
2936+ <label for="new-ollama-api-key">Ollama API Key</label>
2937+ <input type="password" id="new-ollama-api-key" class="form-control" placeholder="sk-...">
2938+ </div>
2939+ <div class="form-group">
2940+ <label for="new-ollama-model">Model</label>
2941+ <input type="text" id="new-ollama-api-model" class="form-control" placeholder="deepseek-r1:latest">
2942+ </div>
2943+ ` ;
28942944 } else if ( agentType === 'anthropic' ) {
28952945 configHtml = `
28962946 <div class="form-group">
@@ -2982,6 +3032,10 @@ document.addEventListener('DOMContentLoaded', function() {
29823032 if ( agentType === 'openai' ) {
29833033 nodeData . config . apiKey = document . getElementById ( 'new-openai-api-key' ) ?. value || '' ;
29843034 nodeData . config . model = document . getElementById ( 'new-openai-model' ) ?. value || 'gpt-4o' ;
3035+ } else if ( agentType === 'ollama' ) {
3036+ nodeData . config . apiUrl = document . getElementById ( 'new-ollama-api-url' ) ?. value || '' ;
3037+ nodeData . config . apiKey = document . getElementById ( 'new-ollama-api-key' ) ?. value || '' ;
3038+ nodeData . config . model = document . getElementById ( 'new-ollama-model' ) ?. value || 'deepseek-r1:latest' ;
29853039 } else if ( agentType === 'anthropic' ) {
29863040 nodeData . config . apiKey = document . getElementById ( 'new-anthropic-api-key' ) ?. value || '' ;
29873041 nodeData . config . model = document . getElementById ( 'new-anthropic-model' ) ?. value || 'claude-3-opus' ;
@@ -3009,6 +3063,8 @@ document.addEventListener('DOMContentLoaded', function() {
30093063
30103064 if ( agentType === 'openai' && nodeData . config . model ) {
30113065 modelText = nodeData . config . model ;
3066+ } if ( agentType === 'ollama' && nodeData . config . model ) {
3067+ modelText = nodeData . config . model ;
30123068 } else if ( agentType === 'anthropic' && nodeData . config . model ) {
30133069 modelText = nodeData . config . model ;
30143070 } else if ( agentType === 'bedrock' && nodeData . config . model ) {
@@ -3334,13 +3390,17 @@ document.addEventListener('DOMContentLoaded', function() {
33343390 // Validate each agent node has necessary configuration
33353391 const unconfiguredAgents = [ ] ;
33363392 agentNodes . forEach ( agent => {
3337- // Only check OpenAI agents since others are marked as "under development"
3338- if ( agent . subType === 'openai' ) {
3393+ // Only check OpenAI & Ollama agents since others are marked as "under development"
3394+ if ( [ 'openai' , 'ollama' ] . includes ( agent . subType ) ) {
33393395 // Check if the agent has the required configuration
33403396 if ( ! agent . config ||
33413397 ! agent . config . apiKey ||
33423398 ! agent . config . model ) {
3343- unconfiguredAgents . push ( agent ) ;
3399+ if ( agent . subType === 'ollama' && ! agent . config . apiUrl ) {
3400+ unconfiguredAgents . push ( agent ) ;
3401+ } else {
3402+ unconfiguredAgents . push ( agent ) ;
3403+ }
33443404 }
33453405 }
33463406 } ) ;
@@ -3563,6 +3623,9 @@ document.addEventListener('DOMContentLoaded', function() {
35633623 if ( agentType === 'openai' && ( ! config . apiKey || ! config . model ) ) {
35643624 return false ;
35653625 }
3626+ else if ( agentType === 'ollama' && ( ! config . apiUrl || ! config . apiKey || ! config . model ) ) {
3627+ return false ;
3628+ }
35663629 else if ( agentType === 'anthropic' && ( ! config . apiKey || ! config . model ) ) {
35673630 return false ;
35683631 }
@@ -4697,6 +4760,17 @@ document.addEventListener('DOMContentLoaded', function() {
46974760 errors . push ( `OpenAI agent ${ config . name || 'Unnamed' } is missing model selection` ) ;
46984761 }
46994762 }
4763+ else if ( agentType === 'ollama' ) {
4764+ if ( ! config . apiUrl || config . apiUrl . trim ( ) === '' ) {
4765+ errors . push ( `Ollama agent ${ config . name || 'Unnamed' } is missing API url` ) ;
4766+ }
4767+ if ( ! config . apiKey || config . apiKey . trim ( ) === '' ) {
4768+ errors . push ( `Ollama agent ${ config . name || 'Unnamed' } is missing API key` ) ;
4769+ }
4770+ if ( ! config . model ) {
4771+ errors . push ( `Ollama agent ${ config . name || 'Unnamed' } is missing model definition` ) ;
4772+ }
4773+ }
47004774 else if ( agentType === 'anthropic' ) {
47014775 if ( ! config . apiKey || config . apiKey . trim ( ) === '' ) {
47024776 errors . push ( `Claude agent ${ config . name || 'Unnamed' } is missing API key` ) ;
0 commit comments