From b37231650a1451e61b900a0164dd1205b8bbde92 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Tue, 24 Mar 2026 17:12:52 +0000 Subject: [PATCH 1/5] Add default expert URLs fixes #6895 --- forge/db/controllers/Assistant.js | 2 +- forge/expert/index.js | 2 +- forge/routes/api/assistant.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/forge/db/controllers/Assistant.js b/forge/db/controllers/Assistant.js index d0dbc30802..c1512f04e3 100644 --- a/forge/db/controllers/Assistant.js +++ b/forge/db/controllers/Assistant.js @@ -26,7 +26,7 @@ module.exports = { isTeamOnTrial = undefined }) => { const timeout = app.config.assistant?.service?.requestTimeout || 60000 - const serviceUrl = app.config.assistant?.service?.url + const serviceUrl = app.config.assistant?.service?.url || 'https://expert.flowfuse/v1/openai' const url = `${serviceUrl.replace(/\/+$/, '')}/${method.replace(/^\/+/, '')}` const headers = await module.exports.buildRequestHeaders(app, additionalHeaders, { diff --git a/forge/expert/index.js b/forge/expert/index.js index cb3b5ac4c3..937fe07bc8 100644 --- a/forge/expert/index.js +++ b/forge/expert/index.js @@ -5,7 +5,7 @@ const { LRUCache } = require('lru-cache') module.exports = fp(async function (app, _opts) { // Get the assistant service configuration const serviceEnabled = app.config.expert?.enabled === true - const expertUrl = app.config.expert?.service?.url + const expertUrl = app.config.expert?.service?.url || 'https://expert.flowfuse/v4/expert' const serviceToken = app.config.expert?.service?.token const requestTimeout = app.config.expert?.service?.requestTimeout || 60000 diff --git a/forge/routes/api/assistant.js b/forge/routes/api/assistant.js index d955ae592b..e0a887a786 100644 --- a/forge/routes/api/assistant.js +++ b/forge/routes/api/assistant.js @@ -25,7 +25,7 @@ module.exports = async function (app) { app.decorate('assistant', { assetCache, tablesSchemaCache }) // Get the assistant service configuration - const serviceUrl = app.config.assistant?.service?.url + const serviceUrl = app.config.assistant?.service?.url || 'https://expert.flowfuse/v1/openai' const serviceToken = app.config.assistant?.service?.token const serviceEnabled = app.config.assistant?.enabled !== false && serviceUrl const requestTimeout = app.config.assistant?.service?.requestTimeout || 60000 From 4a7c83cbeafd7eefefe9c1fa10431d8c52e57352 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Thu, 26 Mar 2026 11:04:18 +0000 Subject: [PATCH 2/5] Remove tests as there is now always a default URL --- test/unit/forge/routes/api/assistant_spec.js | 24 ++++++++++---------- test/unit/forge/routes/api/expert_spec.js | 24 ++++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/unit/forge/routes/api/assistant_spec.js b/test/unit/forge/routes/api/assistant_spec.js index 046197d863..c2d0f7f5a5 100644 --- a/test/unit/forge/routes/api/assistant_spec.js +++ b/test/unit/forge/routes/api/assistant_spec.js @@ -69,18 +69,18 @@ describe('Assistant API', async function () { }) response.statusCode.should.equal(501) }) - it('should return 501 if assistant service url is not set', async function () { - app2 = await setupApp({ assistant: { enabled: true, service: { url: null } } }) - const instance = app2.project - const token = (await instance.refreshAuthTokens()).token - const response = await app2.inject({ - method: 'POST', - url: '/api/v1/assistant/function', - headers: { authorization: 'Bearer ' + token }, - payload: { prompt: 'multiply by 5', transactionId: '1234' } - }) - response.statusCode.should.equal(501) - }) + // it('should return 501 if assistant service url is not set', async function () { + // app2 = await setupApp({ assistant: { enabled: true, service: { url: null } } }) + // const instance = app2.project + // const token = (await instance.refreshAuthTokens()).token + // const response = await app2.inject({ + // method: 'POST', + // url: '/api/v1/assistant/function', + // headers: { authorization: 'Bearer ' + token }, + // payload: { prompt: 'multiply by 5', transactionId: '1234' } + // }) + // response.statusCode.should.equal(501) + // }) }) describe('service enabled', async function () { diff --git a/test/unit/forge/routes/api/expert_spec.js b/test/unit/forge/routes/api/expert_spec.js index 6e6f4d47ab..b348fff52f 100644 --- a/test/unit/forge/routes/api/expert_spec.js +++ b/test/unit/forge/routes/api/expert_spec.js @@ -1497,17 +1497,17 @@ describe('Expert API', function () { }) response.statusCode.should.equal(501) }) - it('should return 501 if expert service url is not set', async function () { - app = await setupApp({ expert: { enabled: true, service: { url: null } } }) - const instance = app.project - const token = (await instance.refreshAuthTokens()).token - const response = await app.inject({ - method: 'POST', - url: '/api/v1/expert/chat', - headers: { authorization: 'Bearer ' + token }, - payload: { context: { team: 'teamid' }, query: 'test' } - }) - response.statusCode.should.equal(501) - }) + // it('should return 501 if expert service url is not set', async function () { + // app = await setupApp({ expert: { enabled: true, service: { url: null } } }) + // const instance = app.project + // const token = (await instance.refreshAuthTokens()).token + // const response = await app.inject({ + // method: 'POST', + // url: '/api/v1/expert/chat', + // headers: { authorization: 'Bearer ' + token }, + // payload: { context: { team: 'teamid' }, query: 'test' } + // }) + // response.statusCode.should.equal(501) + // }) }) }) From 2b7025cce0c5472519bc3bee3af5fb5cf725a498 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Wed, 1 Apr 2026 09:46:51 +0100 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> --- test/unit/forge/routes/api/assistant_spec.js | 12 ------------ test/unit/forge/routes/api/expert_spec.js | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/test/unit/forge/routes/api/assistant_spec.js b/test/unit/forge/routes/api/assistant_spec.js index c2d0f7f5a5..a936978480 100644 --- a/test/unit/forge/routes/api/assistant_spec.js +++ b/test/unit/forge/routes/api/assistant_spec.js @@ -69,18 +69,6 @@ describe('Assistant API', async function () { }) response.statusCode.should.equal(501) }) - // it('should return 501 if assistant service url is not set', async function () { - // app2 = await setupApp({ assistant: { enabled: true, service: { url: null } } }) - // const instance = app2.project - // const token = (await instance.refreshAuthTokens()).token - // const response = await app2.inject({ - // method: 'POST', - // url: '/api/v1/assistant/function', - // headers: { authorization: 'Bearer ' + token }, - // payload: { prompt: 'multiply by 5', transactionId: '1234' } - // }) - // response.statusCode.should.equal(501) - // }) }) describe('service enabled', async function () { diff --git a/test/unit/forge/routes/api/expert_spec.js b/test/unit/forge/routes/api/expert_spec.js index b348fff52f..c7ee5c6cfd 100644 --- a/test/unit/forge/routes/api/expert_spec.js +++ b/test/unit/forge/routes/api/expert_spec.js @@ -1497,17 +1497,5 @@ describe('Expert API', function () { }) response.statusCode.should.equal(501) }) - // it('should return 501 if expert service url is not set', async function () { - // app = await setupApp({ expert: { enabled: true, service: { url: null } } }) - // const instance = app.project - // const token = (await instance.refreshAuthTokens()).token - // const response = await app.inject({ - // method: 'POST', - // url: '/api/v1/expert/chat', - // headers: { authorization: 'Bearer ' + token }, - // payload: { context: { team: 'teamid' }, query: 'test' } - // }) - // response.statusCode.should.equal(501) - // }) }) }) From b7c27769073a11c4aa0e3c19c7e90969bdf248d1 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Wed, 1 Apr 2026 09:51:45 +0100 Subject: [PATCH 4/5] Move defaults to forge/config/index.js --- forge/config/index.js | 7 +++++++ forge/db/controllers/Assistant.js | 2 +- forge/expert/index.js | 2 +- forge/routes/api/assistant.js | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/forge/config/index.js b/forge/config/index.js index 5b710fa44c..1cef4e8acf 100644 --- a/forge/config/index.js +++ b/forge/config/index.js @@ -130,6 +130,13 @@ module.exports = { } } + if (config.assistant?.enabled === true && !config.assistant.service?.url) { + config.assistant.service.url = 'https://expert.flowfuse/v1/openai' + } + if (config.expert?.enabled === true && !config.expert.service?.url) { + config.expert.service.url = 'https://expert.flowfuse/v4/expert' + } + const defaultLogging = { level: 'info', http: 'warn', diff --git a/forge/db/controllers/Assistant.js b/forge/db/controllers/Assistant.js index c1512f04e3..d0dbc30802 100644 --- a/forge/db/controllers/Assistant.js +++ b/forge/db/controllers/Assistant.js @@ -26,7 +26,7 @@ module.exports = { isTeamOnTrial = undefined }) => { const timeout = app.config.assistant?.service?.requestTimeout || 60000 - const serviceUrl = app.config.assistant?.service?.url || 'https://expert.flowfuse/v1/openai' + const serviceUrl = app.config.assistant?.service?.url const url = `${serviceUrl.replace(/\/+$/, '')}/${method.replace(/^\/+/, '')}` const headers = await module.exports.buildRequestHeaders(app, additionalHeaders, { diff --git a/forge/expert/index.js b/forge/expert/index.js index 937fe07bc8..cb3b5ac4c3 100644 --- a/forge/expert/index.js +++ b/forge/expert/index.js @@ -5,7 +5,7 @@ const { LRUCache } = require('lru-cache') module.exports = fp(async function (app, _opts) { // Get the assistant service configuration const serviceEnabled = app.config.expert?.enabled === true - const expertUrl = app.config.expert?.service?.url || 'https://expert.flowfuse/v4/expert' + const expertUrl = app.config.expert?.service?.url const serviceToken = app.config.expert?.service?.token const requestTimeout = app.config.expert?.service?.requestTimeout || 60000 diff --git a/forge/routes/api/assistant.js b/forge/routes/api/assistant.js index e0a887a786..d955ae592b 100644 --- a/forge/routes/api/assistant.js +++ b/forge/routes/api/assistant.js @@ -25,7 +25,7 @@ module.exports = async function (app) { app.decorate('assistant', { assetCache, tablesSchemaCache }) // Get the assistant service configuration - const serviceUrl = app.config.assistant?.service?.url || 'https://expert.flowfuse/v1/openai' + const serviceUrl = app.config.assistant?.service?.url const serviceToken = app.config.assistant?.service?.token const serviceEnabled = app.config.assistant?.enabled !== false && serviceUrl const requestTimeout = app.config.assistant?.service?.requestTimeout || 60000 From 46163d521d8c5e0b1671ed13f101ad17decf2145 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Wed, 1 Apr 2026 10:07:17 +0100 Subject: [PATCH 5/5] Fix missing object --- forge/config/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forge/config/index.js b/forge/config/index.js index 1cef4e8acf..d4db276c5b 100644 --- a/forge/config/index.js +++ b/forge/config/index.js @@ -131,9 +131,11 @@ module.exports = { } if (config.assistant?.enabled === true && !config.assistant.service?.url) { + config.assistant.service = config.assistant.service || {} config.assistant.service.url = 'https://expert.flowfuse/v1/openai' } if (config.expert?.enabled === true && !config.expert.service?.url) { + config.expert.service = config.expert.service || {} config.expert.service.url = 'https://expert.flowfuse/v4/expert' }