feat: add Ollama (local AI) integration for AIUI

- Create Ollama provider plugin in AIUI (ollama-provider.ts)
- Register Ollama alongside Claude in plugin system
- Auto-detect installed models via /api/tags endpoint
- Add Ollama proxy in mock backend (forwards to localhost:11434)
- Add nginx proxy rules for /aiui/api/ollama/ (both HTTP and HTTPS)
- Rebuild AIUI dist with Ollama provider included
- Qwen 2.5 Coder 3B installed on dev server via Ollama

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 01:48:23 +00:00
co-authored by Claude Opus 4.6
parent 00ad7a42f5
commit 825d082003
82 changed files with 246 additions and 135 deletions
+12
View File
@@ -86,6 +86,18 @@ http {
proxy_set_header X-Real-IP $remote_addr;
}
# Proxy Ollama (local AI) requests to backend
location /aiui/api/ollama/ {
proxy_pass http://neode-backend:5959;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
proxy_set_header Connection "";
}
# Proxy Claude API requests to backend (which handles API key + streaming)
location /aiui/api/claude/ {
proxy_pass http://neode-backend:5959;
+68
View File
@@ -1087,6 +1087,74 @@ app.post('/aiui/api/claude/*', (req, res) => {
proxyReq.end()
})
// Ollama (local AI) proxy — forwards to localhost:11434
app.all('/aiui/api/ollama/*', (req, res) => {
const ollamaPath = '/' + req.params[0]
const bodyStr = JSON.stringify(req.body)
const options = {
hostname: '127.0.0.1',
port: 11434,
path: ollamaPath,
method: req.method,
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(bodyStr),
},
}
const proxyReq = http.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.pipe(res)
})
proxyReq.on('error', (err) => {
const msg = err.message || err.code || 'Ollama not available'
console.error('[Ollama Proxy] Error:', msg)
if (!res.headersSent) {
res.status(502).json({ error: msg })
}
})
if (req.method !== 'GET' && req.method !== 'HEAD') {
proxyReq.write(bodyStr)
}
proxyReq.end()
})
// =============================================================================
// Ollama Local AI Proxy (forwards to Ollama on localhost:11434)
// =============================================================================
app.all('/api/ollama/*', (req, res) => {
const ollamaPath = '/' + req.params[0]
const isPost = req.method === 'POST'
const bodyStr = isPost ? JSON.stringify(req.body) : null
const options = {
hostname: '127.0.0.1',
port: 11434,
path: ollamaPath,
method: req.method,
headers: { 'Content-Type': 'application/json' },
}
const proxyReq = http.request(options, (proxyRes) => {
res.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.pipe(res)
})
proxyReq.on('error', (err) => {
const msg = err.message || err.code || 'Ollama not available'
console.error('[Ollama Proxy] Error:', msg)
if (!res.headersSent) {
res.status(502).json({ error: msg })
}
})
if (bodyStr) proxyReq.write(bodyStr)
proxyReq.end()
})
// Web search stub (no search engine configured in demo)
app.get('/api/web-search', (req, res) => {
res.json({ results: [] })