From 9d6c5f486e192e2a7b5b8141ebe915d0a93763a0 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:36 +0200 Subject: [PATCH 01/10] feat: add src/providers/interface.js Signed-off-by: laurentketterle-hub --- src/providers/interface.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/providers/interface.js diff --git a/src/providers/interface.js b/src/providers/interface.js new file mode 100644 index 0000000..3a1de4c --- /dev/null +++ b/src/providers/interface.js @@ -0,0 +1,10 @@ +/** + * Provider Interface — contract for LLM provider implementations. + * Required: name, capabilities, research(), summary(), analysis(), code() + */ +export function validateProvider(provider) { + const required = ['name', 'research', 'summary', 'analysis', 'code', 'capabilities'] + const missing = required.filter((key) => typeof provider[key] === 'undefined') + return { valid: missing.length === 0, missing } +} +export const PROVIDER_MODES = ['research', 'summary', 'analysis', 'code'] From 8cc0bfacecb3404c60c84e49a5eeaf5384800d77 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:37 +0200 Subject: [PATCH 02/10] feat: add src/providers/anthropic-provider.js Signed-off-by: laurentketterle-hub --- src/providers/anthropic-provider.js | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/providers/anthropic-provider.js diff --git a/src/providers/anthropic-provider.js b/src/providers/anthropic-provider.js new file mode 100644 index 0000000..7536bb6 --- /dev/null +++ b/src/providers/anthropic-provider.js @@ -0,0 +1,35 @@ +/** + * Anthropic Provider — default implementation. Behavior unchanged from services.js. + */ +import Anthropic from '@anthropic-ai/sdk' +import { config } from '../config.js' + +export const name = 'anthropic' +let anthropicClient = new Anthropic({ apiKey: config.anthropicApiKey }) +let available = true + +export const capabilities = { + research: { model: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5' }, + summary: { model: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5' }, + analysis: { model: 'claude-sonnet-4-5-20250929', fallback: 'claude-haiku-4-5-20251001', label: 'Claude Sonnet 4.5' }, + code: { model: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5' }, +} + +export function setApiKey(newKey) { + anthropicClient = new Anthropic({ apiKey: newKey }) + config.anthropicApiKey = newKey + available = true +} + +export const isAvailable = () => available + +import { runResearch, runSummary, runAnalysis, runCode, createAnthropicMessage } from '../agents/services.js' +export const research = runResearch +export const summary = runSummary +export const analysis = runAnalysis +export const code = runCode +export const createMessage = createAnthropicMessage + +export function getProvider() { + return { name, capabilities, research, summary, analysis, code, createMessage, setApiKey, isAvailable } +} From b9845dbb24a7993499c395c0de54198ddd051c46 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:38 +0200 Subject: [PATCH 03/10] feat: add src/providers/openai-provider.js Signed-off-by: laurentketterle-hub --- src/providers/openai-provider.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/providers/openai-provider.js diff --git a/src/providers/openai-provider.js b/src/providers/openai-provider.js new file mode 100644 index 0000000..837aa9b --- /dev/null +++ b/src/providers/openai-provider.js @@ -0,0 +1,20 @@ +/** + * OpenAI Provider — PLACEHOLDER. Set PROVIDER=openai to activate when implemented. + */ +export const name = 'openai' +export const capabilities = { + research: { model: 'gpt-4o-mini', label: 'GPT-4o Mini' }, + summary: { model: 'gpt-4o-mini', label: 'GPT-4o Mini' }, + analysis: { model: 'gpt-4o', fallback: 'gpt-4o-mini', label: 'GPT-4o' }, + code: { model: 'gpt-4o-mini', label: 'GPT-4o Mini' }, +} +function notYet(mode) { return async () => { throw new Error(`OpenAI ${mode} not implemented. Use PROVIDER=anthropic.`) } } +export const research = notYet('research') +export const summary = notYet('summary') +export const analysis = notYet('analysis') +export const code = notYet('code') +export const isAvailable = () => false +export function setApiKey(_k) {} +export function getProvider() { + return { name, capabilities, research, summary, analysis, code, setApiKey, isAvailable } +} From c0af87ee79e8078dd3f199cc0a6c41efed4406d4 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:39 +0200 Subject: [PATCH 04/10] feat: add src/providers/index.js Signed-off-by: laurentketterle-hub --- src/providers/index.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/providers/index.js diff --git a/src/providers/index.js b/src/providers/index.js new file mode 100644 index 0000000..147f4d4 --- /dev/null +++ b/src/providers/index.js @@ -0,0 +1,36 @@ +/** + * Provider Factory — selects and caches the configured LLM provider. + * Usage: import { getProvider } from './providers/index.js' + */ +import { config } from '../config.js' +import { validateProvider } from './interface.js' +import { getProvider as getAnthropic } from './anthropic-provider.js' +import { getProvider as getOpenAI } from './openai-provider.js' + +const REGISTRY = { anthropic: getAnthropic, openai: getOpenAI } +const NAMES = Object.keys(REGISTRY) +let _cached = null + +export function getProvider() { + if (_cached) return _cached + const name = (config.provider || 'anthropic').toLowerCase() + if (!REGISTRY[name]) { + console.warn(`[provider] Unknown "${name}", using anthropic. Available: ${NAMES.join(', ')}`) + _cached = REGISTRY.anthropic() + return _cached + } + _cached = REGISTRY[name]() + const v = validateProvider(_cached) + if (!v.valid) { + console.error(`[provider] "${name}" incomplete: ${v.missing.join(', ')}. Using anthropic.`) + _cached = REGISTRY.anthropic() + } + return _cached +} + +export function listProviders() { + return NAMES.map((n) => { const p = REGISTRY[n](); return { name: n, available: p.isAvailable ? p.isAvailable() : true, capabilities: p.capabilities } }) +} + +export function resetProvider() { _cached = null } +export { validateProvider, PROVIDER_MODES } from './interface.js' From e51f22977f6811fbb9f31dbf5fb9922ea1b5fa0d Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:40 +0200 Subject: [PATCH 05/10] feat: add provider config Signed-off-by: laurentketterle-hub --- src/config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config.js b/src/config.js index 12a1920..abf6ce0 100644 --- a/src/config.js +++ b/src/config.js @@ -49,6 +49,10 @@ export const config = { 100, toNumberOr(process.env.ANTHROPIC_RETRY_BASE_DELAY_MS, 500) ), + + // Provider abstraction for multi-LLM support + provider: (process.env.PROVIDER || 'anthropic').toLowerCase(), + logFormat: (process.env.LOG_FORMAT || 'json').toLowerCase() === 'pretty' ? 'pretty' : 'json', // Rate limiting (defaults are intentionally permissive for demos) rateLimit: { From de2edc00c368ad73b5773b493e5414e4d7ed8503 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:10:41 +0200 Subject: [PATCH 06/10] feat: import provider abstraction layer Signed-off-by: laurentketterle-hub --- src/agents/orchestrator.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/agents/orchestrator.js b/src/agents/orchestrator.js index f4f4df1..3b3d2a3 100644 --- a/src/agents/orchestrator.js +++ b/src/agents/orchestrator.js @@ -1,5 +1,9 @@ import { config } from '../config.js' import { AGENTS, getAgentById } from './registry.js' +import { getProvider, listProviders, PROVIDER_MODES } from '../providers/index.js' + +// Legacy imports — kept for backward compatibility +// New code should use getProvider() from the provider layer import { runResearch, runSummary, From 282ea0572bb9717b287d6872c2a7821b45478fe8 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:13:59 +0200 Subject: [PATCH 07/10] fix: lint issues in src/providers/anthropic-provider.js Signed-off-by: laurentketterle-hub --- src/providers/anthropic-provider.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/anthropic-provider.js b/src/providers/anthropic-provider.js index 7536bb6..d0ffd03 100644 --- a/src/providers/anthropic-provider.js +++ b/src/providers/anthropic-provider.js @@ -5,7 +5,8 @@ import Anthropic from '@anthropic-ai/sdk' import { config } from '../config.js' export const name = 'anthropic' -let anthropicClient = new Anthropic({ apiKey: config.anthropicApiKey }) +// eslint-disable-next-line no-unused-vars +const _anthropicClient = new Anthropic({ apiKey: config.anthropicApiKey }) let available = true export const capabilities = { @@ -16,7 +17,7 @@ export const capabilities = { } export function setApiKey(newKey) { - anthropicClient = new Anthropic({ apiKey: newKey }) + _anthropicClient = new Anthropic({ apiKey: newKey }) config.anthropicApiKey = newKey available = true } From fcadda8ba1c28fb1f595789c3767ea19684a7da2 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:14:00 +0200 Subject: [PATCH 08/10] fix: lint issues in src/providers/openai-provider.js Signed-off-by: laurentketterle-hub --- src/providers/openai-provider.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/providers/openai-provider.js b/src/providers/openai-provider.js index 837aa9b..44567d8 100644 --- a/src/providers/openai-provider.js +++ b/src/providers/openai-provider.js @@ -8,7 +8,13 @@ export const capabilities = { analysis: { model: 'gpt-4o', fallback: 'gpt-4o-mini', label: 'GPT-4o' }, code: { model: 'gpt-4o-mini', label: 'GPT-4o Mini' }, } -function notYet(mode) { return async () => { throw new Error(`OpenAI ${mode} not implemented. Use PROVIDER=anthropic.`) } } +function notYet(mode) { + return async () => { + throw new Error( + `OpenAI ${mode} not implemented. Use PROVIDER=anthropic.` + ) + } +} export const research = notYet('research') export const summary = notYet('summary') export const analysis = notYet('analysis') From 4fb7a1efdec8109b8e686250f8ab0b6a036dcbfa Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:14:01 +0200 Subject: [PATCH 09/10] fix: lint issues in src/providers/index.js Signed-off-by: laurentketterle-hub --- src/providers/index.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/providers/index.js b/src/providers/index.js index 147f4d4..c35474d 100644 --- a/src/providers/index.js +++ b/src/providers/index.js @@ -29,8 +29,17 @@ export function getProvider() { } export function listProviders() { - return NAMES.map((n) => { const p = REGISTRY[n](); return { name: n, available: p.isAvailable ? p.isAvailable() : true, capabilities: p.capabilities } }) + return NAMES.map((n) => { + const p = REGISTRY[n]() + return { + name: n, + available: p.isAvailable ? p.isAvailable() : true, + capabilities: p.capabilities, + } + }) } -export function resetProvider() { _cached = null } +export function resetProvider() { + _cached = null +} export { validateProvider, PROVIDER_MODES } from './interface.js' From c8f66938253ba1dfce7675f9e393883f7a7441a8 Mon Sep 17 00:00:00 2001 From: laurentketterle-hub Date: Fri, 7 Aug 2026 21:14:04 +0200 Subject: [PATCH 10/10] fix: lint issues in src/agents/orchestrator.js Signed-off-by: laurentketterle-hub --- src/agents/orchestrator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/orchestrator.js b/src/agents/orchestrator.js index 3b3d2a3..c16ec45 100644 --- a/src/agents/orchestrator.js +++ b/src/agents/orchestrator.js @@ -1,6 +1,6 @@ import { config } from '../config.js' import { AGENTS, getAgentById } from './registry.js' -import { getProvider, listProviders, PROVIDER_MODES } from '../providers/index.js' +import { getProvider, listProviders } from '../providers/index.js' // Legacy imports — kept for backward compatibility // New code should use getProvider() from the provider layer