Skip to content

Commit 55c2864

Browse files
committed
feat: Add Ollama support in UI
1 parent dc4b818 commit 55c2864

File tree

2 files changed

+109
-7
lines changed

2 files changed

+109
-7
lines changed

python_a2a/agent_flow/server/static/js/flow-builder.js

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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`);

python_a2a/agent_flow/server/templates/index.html

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ <h3>OpenAI Agent</h3>
6262
<p>GPT-based agent</p>
6363
</div>
6464
</div>
65+
<div class="agent-card" draggable="true" data-type="ollama">
66+
<div class="card-icon"><i class="bi bi-robot"></i></div>
67+
<div class="card-content">
68+
<h3>Ollama Agent</h3>
69+
<p>Ollama models</p>
70+
</div>
71+
</div>
6572
<div class="agent-card" draggable="true" data-type="anthropic">
6673
<div class="card-icon"><i class="bi bi-robot"></i></div>
6774
<div class="card-content">
@@ -203,6 +210,26 @@ <h2 id="config-title">Configure Agent</h2>
203210
<textarea id="openai-system-message" class="form-control" placeholder="You are a helpful AI assistant..." rows="3" autocomplete="off"></textarea>
204211
</div>
205212
</div>
213+
214+
<!-- Ollama Config -->
215+
<div id="ollama-config" class="agent-specific-config">
216+
<div class="form-group">
217+
<label for="ollama-api-url">Ollama API url</label>
218+
<input type="text" id="ollama-api-url" class="form-control" placeholder="http://localhost:11434" autocomplete="off">
219+
</div>
220+
<div class="form-group">
221+
<label for="ollama-api-key">Ollama API Key</label>
222+
<input type="password" id="ollama-api-key" class="form-control" placeholder="sk-..." autocomplete="off">
223+
</div>
224+
<div class="form-group">
225+
<label for="ollama-model">Ollama model</label>
226+
<input type="text" id="ollama-model" class="form-control" placeholder="deepseek-r1:latest" autocomplete="off">
227+
</div>
228+
<div class="form-group">
229+
<label for="ollama-system-message">System Message</label>
230+
<textarea id="ollama-system-message" class="form-control" placeholder="You are a helpful AI assistant..." rows="3" autocomplete="off"></textarea>
231+
</div>
232+
</div>
206233

207234
<!-- Anthropic Config -->
208235
<div id="anthropic-config" class="agent-specific-config">
@@ -362,6 +389,7 @@ <h2>Create New Agent</h2>
362389
<label for="new-agent-type">Agent Type</label>
363390
<select id="new-agent-type" class="form-control">
364391
<option value="openai">OpenAI</option>
392+
<option value="ollama">Ollama</option>
365393
<option value="anthropic">Anthropic Claude</option>
366394
<option value="bedrock">AWS Bedrock</option>
367395
<option value="custom">Custom</option>

0 commit comments

Comments
 (0)