|
|
@@ -13,9 +13,24 @@ const configSchema = {
|
|
|
baseUrl: {
|
|
|
type: 'string',
|
|
|
title: 'Ollama Base URL',
|
|
|
- description: 'Base URL of the Ollama instance',
|
|
|
+ description: 'Base URL of the Ollama instance. Use https://ollama.com to call the hosted API directly instead of proxying through a local daemon.',
|
|
|
default: 'http://localhost:11434'
|
|
|
},
|
|
|
+ authType: {
|
|
|
+ type: 'string',
|
|
|
+ title: 'Authentication',
|
|
|
+ description: 'Hosted ollama.com requires a credential. A local daemon usually does not.',
|
|
|
+ enum: ['none', 'credential'],
|
|
|
+ enumLabels: ['None', 'Stored credential'],
|
|
|
+ default: 'none'
|
|
|
+ },
|
|
|
+ credentialId: {
|
|
|
+ type: 'string',
|
|
|
+ title: 'Credential',
|
|
|
+ description: 'Stored Bearer credential holding the ollama.com API key',
|
|
|
+ default: '',
|
|
|
+ showWhen: { field: 'authType', value: 'credential' }
|
|
|
+ },
|
|
|
model: {
|
|
|
type: 'string',
|
|
|
title: 'Model',
|
|
|
@@ -213,7 +228,21 @@ function findImage(input, override) {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-function callOllama(config, base64) {
|
|
|
+function buildHeaders(config) {
|
|
|
+ const headers = { 'Content-Type': 'application/json' };
|
|
|
+
|
|
|
+ if (config.authType === 'credential' && config.credentialId) {
|
|
|
+ const auth = smartbotic.credentials.get(config.credentialId);
|
|
|
+ if (!auth.success) {
|
|
|
+ throw new Error('Failed to load credential: ' + auth.error);
|
|
|
+ }
|
|
|
+ headers[auth.headerName] = auth.headerValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ return headers;
|
|
|
+}
|
|
|
+
|
|
|
+function callOllama(config, base64, headers) {
|
|
|
const messages = [];
|
|
|
if (config.systemPrompt && String(config.systemPrompt).trim().length > 0) {
|
|
|
messages.push({ role: 'system', content: String(config.systemPrompt) });
|
|
|
@@ -235,7 +264,7 @@ function callOllama(config, base64) {
|
|
|
const response = smartbotic.http.request({
|
|
|
method: 'POST',
|
|
|
url: String(config.baseUrl).replace(/\/+$/, '') + '/api/chat',
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
+ headers: headers,
|
|
|
body: JSON.stringify(payload),
|
|
|
timeout: Number(config.timeoutMs) || 120000
|
|
|
});
|
|
|
@@ -277,6 +306,10 @@ module.exports = {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Resolved once, outside the retry loop: a missing or broken credential is
|
|
|
+ // not transient, so retrying it with backoff only wastes time.
|
|
|
+ const headers = buildHeaders(config);
|
|
|
+
|
|
|
const wantJson = config.responseFormat === 'json';
|
|
|
const attempts = 1 + (Number(config.retryCount) > 0 ? Number(config.retryCount) : 0);
|
|
|
|
|
|
@@ -288,7 +321,7 @@ module.exports = {
|
|
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
|
used = attempt;
|
|
|
try {
|
|
|
- content = callOllama(config, image.base64);
|
|
|
+ content = callOllama(config, image.base64, headers);
|
|
|
if (wantJson) {
|
|
|
parsed = JSON.parse(stripFences(content));
|
|
|
}
|