@dotsetlabs/bellwether 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +291 -0
- package/LICENSE +21 -0
- package/README.md +739 -0
- package/dist/auth/credentials.d.ts +64 -0
- package/dist/auth/credentials.js +218 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/keychain.d.ts +64 -0
- package/dist/auth/keychain.js +268 -0
- package/dist/baseline/ab-testing.d.ts +80 -0
- package/dist/baseline/ab-testing.js +236 -0
- package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
- package/dist/baseline/ai-compatibility-scorer.js +606 -0
- package/dist/baseline/calibration.d.ts +77 -0
- package/dist/baseline/calibration.js +136 -0
- package/dist/baseline/category-matching.d.ts +85 -0
- package/dist/baseline/category-matching.js +289 -0
- package/dist/baseline/change-impact-analyzer.d.ts +98 -0
- package/dist/baseline/change-impact-analyzer.js +592 -0
- package/dist/baseline/comparator.d.ts +64 -0
- package/dist/baseline/comparator.js +916 -0
- package/dist/baseline/confidence.d.ts +55 -0
- package/dist/baseline/confidence.js +122 -0
- package/dist/baseline/converter.d.ts +61 -0
- package/dist/baseline/converter.js +585 -0
- package/dist/baseline/dependency-analyzer.d.ts +89 -0
- package/dist/baseline/dependency-analyzer.js +567 -0
- package/dist/baseline/deprecation-tracker.d.ts +133 -0
- package/dist/baseline/deprecation-tracker.js +322 -0
- package/dist/baseline/diff.d.ts +55 -0
- package/dist/baseline/diff.js +1584 -0
- package/dist/baseline/documentation-scorer.d.ts +205 -0
- package/dist/baseline/documentation-scorer.js +466 -0
- package/dist/baseline/embeddings.d.ts +118 -0
- package/dist/baseline/embeddings.js +251 -0
- package/dist/baseline/error-analyzer.d.ts +198 -0
- package/dist/baseline/error-analyzer.js +721 -0
- package/dist/baseline/evaluation/evaluator.d.ts +42 -0
- package/dist/baseline/evaluation/evaluator.js +323 -0
- package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
- package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
- package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
- package/dist/baseline/evaluation/golden-dataset.js +717 -0
- package/dist/baseline/evaluation/index.d.ts +15 -0
- package/dist/baseline/evaluation/index.js +15 -0
- package/dist/baseline/evaluation/types.d.ts +186 -0
- package/dist/baseline/evaluation/types.js +8 -0
- package/dist/baseline/external-dependency-detector.d.ts +181 -0
- package/dist/baseline/external-dependency-detector.js +524 -0
- package/dist/baseline/golden-output.d.ts +162 -0
- package/dist/baseline/golden-output.js +636 -0
- package/dist/baseline/health-scorer.d.ts +174 -0
- package/dist/baseline/health-scorer.js +451 -0
- package/dist/baseline/incremental-checker.d.ts +97 -0
- package/dist/baseline/incremental-checker.js +174 -0
- package/dist/baseline/index.d.ts +31 -0
- package/dist/baseline/index.js +42 -0
- package/dist/baseline/migration-generator.d.ts +137 -0
- package/dist/baseline/migration-generator.js +554 -0
- package/dist/baseline/migrations.d.ts +60 -0
- package/dist/baseline/migrations.js +197 -0
- package/dist/baseline/performance-tracker.d.ts +214 -0
- package/dist/baseline/performance-tracker.js +577 -0
- package/dist/baseline/pr-comment-generator.d.ts +117 -0
- package/dist/baseline/pr-comment-generator.js +546 -0
- package/dist/baseline/response-fingerprint.d.ts +127 -0
- package/dist/baseline/response-fingerprint.js +728 -0
- package/dist/baseline/response-schema-tracker.d.ts +129 -0
- package/dist/baseline/response-schema-tracker.js +420 -0
- package/dist/baseline/risk-scorer.d.ts +54 -0
- package/dist/baseline/risk-scorer.js +434 -0
- package/dist/baseline/saver.d.ts +89 -0
- package/dist/baseline/saver.js +554 -0
- package/dist/baseline/scenario-generator.d.ts +151 -0
- package/dist/baseline/scenario-generator.js +905 -0
- package/dist/baseline/schema-compare.d.ts +86 -0
- package/dist/baseline/schema-compare.js +557 -0
- package/dist/baseline/schema-evolution.d.ts +189 -0
- package/dist/baseline/schema-evolution.js +467 -0
- package/dist/baseline/semantic.d.ts +203 -0
- package/dist/baseline/semantic.js +908 -0
- package/dist/baseline/synonyms.d.ts +60 -0
- package/dist/baseline/synonyms.js +386 -0
- package/dist/baseline/telemetry.d.ts +165 -0
- package/dist/baseline/telemetry.js +294 -0
- package/dist/baseline/test-pruner.d.ts +120 -0
- package/dist/baseline/test-pruner.js +387 -0
- package/dist/baseline/types.d.ts +449 -0
- package/dist/baseline/types.js +5 -0
- package/dist/baseline/version.d.ts +138 -0
- package/dist/baseline/version.js +206 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/response-cache.d.ts +151 -0
- package/dist/cache/response-cache.js +287 -0
- package/dist/ci/index.d.ts +60 -0
- package/dist/ci/index.js +342 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +352 -0
- package/dist/cli/commands/badge.d.ts +3 -0
- package/dist/cli/commands/badge.js +74 -0
- package/dist/cli/commands/baseline-accept.d.ts +15 -0
- package/dist/cli/commands/baseline-accept.js +178 -0
- package/dist/cli/commands/baseline-migrate.d.ts +12 -0
- package/dist/cli/commands/baseline-migrate.js +164 -0
- package/dist/cli/commands/baseline.d.ts +14 -0
- package/dist/cli/commands/baseline.js +449 -0
- package/dist/cli/commands/beta.d.ts +10 -0
- package/dist/cli/commands/beta.js +231 -0
- package/dist/cli/commands/check.d.ts +11 -0
- package/dist/cli/commands/check.js +820 -0
- package/dist/cli/commands/cloud/badge.d.ts +3 -0
- package/dist/cli/commands/cloud/badge.js +74 -0
- package/dist/cli/commands/cloud/diff.d.ts +6 -0
- package/dist/cli/commands/cloud/diff.js +79 -0
- package/dist/cli/commands/cloud/history.d.ts +6 -0
- package/dist/cli/commands/cloud/history.js +102 -0
- package/dist/cli/commands/cloud/link.d.ts +9 -0
- package/dist/cli/commands/cloud/link.js +119 -0
- package/dist/cli/commands/cloud/login.d.ts +7 -0
- package/dist/cli/commands/cloud/login.js +499 -0
- package/dist/cli/commands/cloud/projects.d.ts +6 -0
- package/dist/cli/commands/cloud/projects.js +44 -0
- package/dist/cli/commands/cloud/shared.d.ts +7 -0
- package/dist/cli/commands/cloud/shared.js +42 -0
- package/dist/cli/commands/cloud/teams.d.ts +8 -0
- package/dist/cli/commands/cloud/teams.js +169 -0
- package/dist/cli/commands/cloud/upload.d.ts +8 -0
- package/dist/cli/commands/cloud/upload.js +181 -0
- package/dist/cli/commands/contract.d.ts +11 -0
- package/dist/cli/commands/contract.js +280 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.js +82 -0
- package/dist/cli/commands/eval.d.ts +9 -0
- package/dist/cli/commands/eval.js +187 -0
- package/dist/cli/commands/explore.d.ts +11 -0
- package/dist/cli/commands/explore.js +437 -0
- package/dist/cli/commands/feedback.d.ts +9 -0
- package/dist/cli/commands/feedback.js +174 -0
- package/dist/cli/commands/golden.d.ts +12 -0
- package/dist/cli/commands/golden.js +407 -0
- package/dist/cli/commands/history.d.ts +10 -0
- package/dist/cli/commands/history.js +202 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +219 -0
- package/dist/cli/commands/interview.d.ts +3 -0
- package/dist/cli/commands/interview.js +903 -0
- package/dist/cli/commands/link.d.ts +10 -0
- package/dist/cli/commands/link.js +169 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +499 -0
- package/dist/cli/commands/preset.d.ts +33 -0
- package/dist/cli/commands/preset.js +297 -0
- package/dist/cli/commands/profile.d.ts +33 -0
- package/dist/cli/commands/profile.js +286 -0
- package/dist/cli/commands/registry.d.ts +11 -0
- package/dist/cli/commands/registry.js +146 -0
- package/dist/cli/commands/shared.d.ts +79 -0
- package/dist/cli/commands/shared.js +196 -0
- package/dist/cli/commands/teams.d.ts +8 -0
- package/dist/cli/commands/teams.js +169 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.js +500 -0
- package/dist/cli/commands/upload.d.ts +8 -0
- package/dist/cli/commands/upload.js +223 -0
- package/dist/cli/commands/validate-config.d.ts +6 -0
- package/dist/cli/commands/validate-config.js +35 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.js +283 -0
- package/dist/cli/commands/watch.d.ts +12 -0
- package/dist/cli/commands/watch.js +253 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +178 -0
- package/dist/cli/interactive.d.ts +47 -0
- package/dist/cli/interactive.js +216 -0
- package/dist/cli/output/terminal-reporter.d.ts +19 -0
- package/dist/cli/output/terminal-reporter.js +104 -0
- package/dist/cli/output.d.ts +226 -0
- package/dist/cli/output.js +438 -0
- package/dist/cli/utils/env.d.ts +5 -0
- package/dist/cli/utils/env.js +14 -0
- package/dist/cli/utils/progress.d.ts +59 -0
- package/dist/cli/utils/progress.js +206 -0
- package/dist/cli/utils/server-context.d.ts +10 -0
- package/dist/cli/utils/server-context.js +36 -0
- package/dist/cloud/auth.d.ts +144 -0
- package/dist/cloud/auth.js +374 -0
- package/dist/cloud/client.d.ts +24 -0
- package/dist/cloud/client.js +65 -0
- package/dist/cloud/http-client.d.ts +38 -0
- package/dist/cloud/http-client.js +215 -0
- package/dist/cloud/index.d.ts +23 -0
- package/dist/cloud/index.js +25 -0
- package/dist/cloud/mock-client.d.ts +107 -0
- package/dist/cloud/mock-client.js +545 -0
- package/dist/cloud/types.d.ts +515 -0
- package/dist/cloud/types.js +15 -0
- package/dist/config/defaults.d.ts +160 -0
- package/dist/config/defaults.js +169 -0
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.js +122 -0
- package/dist/config/template.d.ts +42 -0
- package/dist/config/template.js +647 -0
- package/dist/config/validator.d.ts +2112 -0
- package/dist/config/validator.js +658 -0
- package/dist/constants/cloud.d.ts +107 -0
- package/dist/constants/cloud.js +110 -0
- package/dist/constants/core.d.ts +521 -0
- package/dist/constants/core.js +556 -0
- package/dist/constants/testing.d.ts +1283 -0
- package/dist/constants/testing.js +1568 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/contract/index.d.ts +6 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/validator.d.ts +177 -0
- package/dist/contract/validator.js +574 -0
- package/dist/cost/index.d.ts +6 -0
- package/dist/cost/index.js +5 -0
- package/dist/cost/tracker.d.ts +134 -0
- package/dist/cost/tracker.js +313 -0
- package/dist/discovery/discovery.d.ts +16 -0
- package/dist/discovery/discovery.js +173 -0
- package/dist/discovery/types.d.ts +51 -0
- package/dist/discovery/types.js +2 -0
- package/dist/docs/agents.d.ts +3 -0
- package/dist/docs/agents.js +995 -0
- package/dist/docs/contract.d.ts +51 -0
- package/dist/docs/contract.js +1681 -0
- package/dist/docs/generator.d.ts +4 -0
- package/dist/docs/generator.js +4 -0
- package/dist/docs/html-reporter.d.ts +9 -0
- package/dist/docs/html-reporter.js +757 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/junit-reporter.d.ts +18 -0
- package/dist/docs/junit-reporter.js +210 -0
- package/dist/docs/report.d.ts +14 -0
- package/dist/docs/report.js +44 -0
- package/dist/docs/sarif-reporter.d.ts +19 -0
- package/dist/docs/sarif-reporter.js +335 -0
- package/dist/docs/shared.d.ts +35 -0
- package/dist/docs/shared.js +162 -0
- package/dist/docs/templates.d.ts +12 -0
- package/dist/docs/templates.js +76 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +6 -0
- package/dist/errors/retry.d.ts +92 -0
- package/dist/errors/retry.js +323 -0
- package/dist/errors/types.d.ts +321 -0
- package/dist/errors/types.js +584 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/interview/dependency-resolver.d.ts +11 -0
- package/dist/interview/dependency-resolver.js +32 -0
- package/dist/interview/interviewer.d.ts +232 -0
- package/dist/interview/interviewer.js +1939 -0
- package/dist/interview/mock-response-generator.d.ts +7 -0
- package/dist/interview/mock-response-generator.js +102 -0
- package/dist/interview/orchestrator.d.ts +237 -0
- package/dist/interview/orchestrator.js +1296 -0
- package/dist/interview/rate-limiter.d.ts +15 -0
- package/dist/interview/rate-limiter.js +55 -0
- package/dist/interview/response-validator.d.ts +10 -0
- package/dist/interview/response-validator.js +132 -0
- package/dist/interview/schema-inferrer.d.ts +8 -0
- package/dist/interview/schema-inferrer.js +71 -0
- package/dist/interview/schema-test-generator.d.ts +71 -0
- package/dist/interview/schema-test-generator.js +834 -0
- package/dist/interview/smart-value-generator.d.ts +155 -0
- package/dist/interview/smart-value-generator.js +554 -0
- package/dist/interview/stateful-test-runner.d.ts +19 -0
- package/dist/interview/stateful-test-runner.js +106 -0
- package/dist/interview/types.d.ts +561 -0
- package/dist/interview/types.js +2 -0
- package/dist/llm/anthropic.d.ts +41 -0
- package/dist/llm/anthropic.js +355 -0
- package/dist/llm/client.d.ts +123 -0
- package/dist/llm/client.js +42 -0
- package/dist/llm/factory.d.ts +38 -0
- package/dist/llm/factory.js +145 -0
- package/dist/llm/fallback.d.ts +140 -0
- package/dist/llm/fallback.js +379 -0
- package/dist/llm/index.d.ts +18 -0
- package/dist/llm/index.js +15 -0
- package/dist/llm/ollama.d.ts +37 -0
- package/dist/llm/ollama.js +330 -0
- package/dist/llm/openai.d.ts +25 -0
- package/dist/llm/openai.js +320 -0
- package/dist/llm/token-budget.d.ts +161 -0
- package/dist/llm/token-budget.js +395 -0
- package/dist/logging/logger.d.ts +70 -0
- package/dist/logging/logger.js +130 -0
- package/dist/metrics/collector.d.ts +106 -0
- package/dist/metrics/collector.js +547 -0
- package/dist/metrics/index.d.ts +7 -0
- package/dist/metrics/index.js +7 -0
- package/dist/metrics/prometheus.d.ts +20 -0
- package/dist/metrics/prometheus.js +241 -0
- package/dist/metrics/types.d.ts +209 -0
- package/dist/metrics/types.js +5 -0
- package/dist/persona/builtins.d.ts +54 -0
- package/dist/persona/builtins.js +219 -0
- package/dist/persona/index.d.ts +8 -0
- package/dist/persona/index.js +8 -0
- package/dist/persona/loader.d.ts +30 -0
- package/dist/persona/loader.js +190 -0
- package/dist/persona/types.d.ts +144 -0
- package/dist/persona/types.js +5 -0
- package/dist/persona/validation.d.ts +94 -0
- package/dist/persona/validation.js +332 -0
- package/dist/prompts/index.d.ts +5 -0
- package/dist/prompts/index.js +5 -0
- package/dist/prompts/templates.d.ts +180 -0
- package/dist/prompts/templates.js +431 -0
- package/dist/registry/client.d.ts +49 -0
- package/dist/registry/client.js +191 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +6 -0
- package/dist/registry/types.d.ts +140 -0
- package/dist/registry/types.js +6 -0
- package/dist/scenarios/evaluator.d.ts +43 -0
- package/dist/scenarios/evaluator.js +206 -0
- package/dist/scenarios/index.d.ts +10 -0
- package/dist/scenarios/index.js +9 -0
- package/dist/scenarios/loader.d.ts +20 -0
- package/dist/scenarios/loader.js +285 -0
- package/dist/scenarios/types.d.ts +153 -0
- package/dist/scenarios/types.js +8 -0
- package/dist/security/index.d.ts +17 -0
- package/dist/security/index.js +18 -0
- package/dist/security/payloads.d.ts +61 -0
- package/dist/security/payloads.js +268 -0
- package/dist/security/security-tester.d.ts +42 -0
- package/dist/security/security-tester.js +582 -0
- package/dist/security/types.d.ts +166 -0
- package/dist/security/types.js +8 -0
- package/dist/transport/base-transport.d.ts +59 -0
- package/dist/transport/base-transport.js +38 -0
- package/dist/transport/http-transport.d.ts +67 -0
- package/dist/transport/http-transport.js +238 -0
- package/dist/transport/mcp-client.d.ts +141 -0
- package/dist/transport/mcp-client.js +496 -0
- package/dist/transport/sse-transport.d.ts +88 -0
- package/dist/transport/sse-transport.js +316 -0
- package/dist/transport/stdio-transport.d.ts +43 -0
- package/dist/transport/stdio-transport.js +238 -0
- package/dist/transport/types.d.ts +125 -0
- package/dist/transport/types.js +16 -0
- package/dist/utils/concurrency.d.ts +123 -0
- package/dist/utils/concurrency.js +213 -0
- package/dist/utils/formatters.d.ts +16 -0
- package/dist/utils/formatters.js +37 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jsonpath.d.ts +87 -0
- package/dist/utils/jsonpath.js +326 -0
- package/dist/utils/markdown.d.ts +113 -0
- package/dist/utils/markdown.js +265 -0
- package/dist/utils/network.d.ts +14 -0
- package/dist/utils/network.js +17 -0
- package/dist/utils/sanitize.d.ts +92 -0
- package/dist/utils/sanitize.js +191 -0
- package/dist/utils/semantic.d.ts +194 -0
- package/dist/utils/semantic.js +1051 -0
- package/dist/utils/smart-truncate.d.ts +94 -0
- package/dist/utils/smart-truncate.js +361 -0
- package/dist/utils/timeout.d.ts +153 -0
- package/dist/utils/timeout.js +205 -0
- package/dist/utils/yaml-parser.d.ts +58 -0
- package/dist/utils/yaml-parser.js +86 -0
- package/dist/validation/index.d.ts +32 -0
- package/dist/validation/index.js +32 -0
- package/dist/validation/semantic-test-generator.d.ts +50 -0
- package/dist/validation/semantic-test-generator.js +176 -0
- package/dist/validation/semantic-types.d.ts +66 -0
- package/dist/validation/semantic-types.js +94 -0
- package/dist/validation/semantic-validator.d.ts +38 -0
- package/dist/validation/semantic-validator.js +340 -0
- package/dist/verification/index.d.ts +6 -0
- package/dist/verification/index.js +5 -0
- package/dist/verification/types.d.ts +133 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/verifier.d.ts +30 -0
- package/dist/verification/verifier.js +309 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +48 -0
- package/dist/workflow/auto-generator.d.ts +27 -0
- package/dist/workflow/auto-generator.js +513 -0
- package/dist/workflow/discovery.d.ts +40 -0
- package/dist/workflow/discovery.js +195 -0
- package/dist/workflow/executor.d.ts +82 -0
- package/dist/workflow/executor.js +611 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/loader.d.ts +24 -0
- package/dist/workflow/loader.js +194 -0
- package/dist/workflow/state-tracker.d.ts +98 -0
- package/dist/workflow/state-tracker.js +424 -0
- package/dist/workflow/types.d.ts +337 -0
- package/dist/workflow/types.js +5 -0
- package/package.json +94 -0
- package/schemas/bellwether-check.schema.json +651 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM provider factory - creates and auto-detects providers.
|
|
3
|
+
*/
|
|
4
|
+
import { DEFAULT_MODELS } from './client.js';
|
|
5
|
+
import { OpenAIClient } from './openai.js';
|
|
6
|
+
import { AnthropicClient } from './anthropic.js';
|
|
7
|
+
import { OllamaClient } from './ollama.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create an LLM client from configuration.
|
|
10
|
+
*/
|
|
11
|
+
export function createLLMClient(config) {
|
|
12
|
+
const apiKey = resolveApiKey(config);
|
|
13
|
+
switch (config.provider) {
|
|
14
|
+
case 'openai':
|
|
15
|
+
return new OpenAIClient({
|
|
16
|
+
apiKey,
|
|
17
|
+
model: config.model,
|
|
18
|
+
baseURL: config.baseUrl,
|
|
19
|
+
onUsage: config.onUsage,
|
|
20
|
+
});
|
|
21
|
+
case 'anthropic':
|
|
22
|
+
return new AnthropicClient({
|
|
23
|
+
apiKey,
|
|
24
|
+
model: config.model,
|
|
25
|
+
baseURL: config.baseUrl,
|
|
26
|
+
onUsage: config.onUsage,
|
|
27
|
+
});
|
|
28
|
+
case 'ollama':
|
|
29
|
+
return new OllamaClient({
|
|
30
|
+
baseUrl: config.baseUrl,
|
|
31
|
+
model: config.model,
|
|
32
|
+
onUsage: config.onUsage,
|
|
33
|
+
});
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Unknown LLM provider: ${config.provider}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve API key from config or environment.
|
|
40
|
+
*/
|
|
41
|
+
function resolveApiKey(config) {
|
|
42
|
+
// Direct API key takes precedence
|
|
43
|
+
if (config.apiKey) {
|
|
44
|
+
return config.apiKey;
|
|
45
|
+
}
|
|
46
|
+
// Check specified env var
|
|
47
|
+
if (config.apiKeyEnvVar) {
|
|
48
|
+
const key = process.env[config.apiKeyEnvVar];
|
|
49
|
+
if (!key) {
|
|
50
|
+
throw new Error(`Environment variable ${config.apiKeyEnvVar} is not set`);
|
|
51
|
+
}
|
|
52
|
+
return key;
|
|
53
|
+
}
|
|
54
|
+
// Default env vars per provider
|
|
55
|
+
const defaultEnvVars = {
|
|
56
|
+
openai: 'OPENAI_API_KEY',
|
|
57
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
58
|
+
ollama: '', // Ollama doesn't need an API key
|
|
59
|
+
};
|
|
60
|
+
const defaultVar = defaultEnvVars[config.provider];
|
|
61
|
+
if (defaultVar) {
|
|
62
|
+
return process.env[defaultVar];
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Auto-detect the best available provider based on environment.
|
|
68
|
+
*/
|
|
69
|
+
export function detectProvider() {
|
|
70
|
+
// Check for API keys in order of preference
|
|
71
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
72
|
+
return 'anthropic';
|
|
73
|
+
}
|
|
74
|
+
if (process.env.OPENAI_API_KEY) {
|
|
75
|
+
return 'openai';
|
|
76
|
+
}
|
|
77
|
+
// Fall back to Ollama (local, no API key needed)
|
|
78
|
+
return 'ollama';
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create an LLM client with auto-detection.
|
|
82
|
+
* Uses environment variables to determine the best provider.
|
|
83
|
+
*/
|
|
84
|
+
export function createAutoClient(modelOverride) {
|
|
85
|
+
const provider = detectProvider();
|
|
86
|
+
return createLLMClient({
|
|
87
|
+
provider,
|
|
88
|
+
model: modelOverride ?? DEFAULT_MODELS[provider],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check which providers are available.
|
|
93
|
+
*/
|
|
94
|
+
export async function checkProviderAvailability() {
|
|
95
|
+
const results = [];
|
|
96
|
+
// Check OpenAI
|
|
97
|
+
if (process.env.OPENAI_API_KEY) {
|
|
98
|
+
results.push({ provider: 'openai', available: true });
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
results.push({
|
|
102
|
+
provider: 'openai',
|
|
103
|
+
available: false,
|
|
104
|
+
reason: 'OPENAI_API_KEY not set',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Check Anthropic
|
|
108
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
109
|
+
results.push({ provider: 'anthropic', available: true });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
results.push({
|
|
113
|
+
provider: 'anthropic',
|
|
114
|
+
available: false,
|
|
115
|
+
reason: 'ANTHROPIC_API_KEY not set',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Check Ollama
|
|
119
|
+
const ollama = new OllamaClient();
|
|
120
|
+
const ollamaAvailable = await ollama.isAvailable();
|
|
121
|
+
if (ollamaAvailable) {
|
|
122
|
+
results.push({ provider: 'ollama', available: true });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
results.push({
|
|
126
|
+
provider: 'ollama',
|
|
127
|
+
available: false,
|
|
128
|
+
reason: 'Ollama not running (start with: ollama serve)',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return results;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get the default model for a provider.
|
|
135
|
+
*/
|
|
136
|
+
export function getDefaultModel(provider) {
|
|
137
|
+
return DEFAULT_MODELS[provider];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* List all supported providers.
|
|
141
|
+
*/
|
|
142
|
+
export function getSupportedProviders() {
|
|
143
|
+
return ['openai', 'anthropic', 'ollama'];
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback LLM client that tries multiple providers in sequence.
|
|
3
|
+
* Provides resilience through provider failover and model fallback.
|
|
4
|
+
*/
|
|
5
|
+
import type { LLMClient, Message, CompletionOptions, ProviderInfo, LLMConfig, LLMProviderId, StreamingOptions, StreamingResult } from './client.js';
|
|
6
|
+
/**
|
|
7
|
+
* Health status for a provider.
|
|
8
|
+
*/
|
|
9
|
+
export interface ProviderHealth {
|
|
10
|
+
provider: LLMProviderId;
|
|
11
|
+
healthy: boolean;
|
|
12
|
+
lastChecked: Date;
|
|
13
|
+
lastError?: string;
|
|
14
|
+
consecutiveFailures: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Configuration for fallback behavior.
|
|
18
|
+
*/
|
|
19
|
+
export interface FallbackConfig {
|
|
20
|
+
/** Primary provider configurations in priority order */
|
|
21
|
+
providers: LLMConfig[];
|
|
22
|
+
/** Whether to use Ollama as final fallback (default: true) */
|
|
23
|
+
useOllamaFallback?: boolean;
|
|
24
|
+
/** Ollama model to use for fallback */
|
|
25
|
+
ollamaModel?: string;
|
|
26
|
+
/** Health check interval in ms (default: 60000) */
|
|
27
|
+
healthCheckIntervalMs?: number;
|
|
28
|
+
/** Max consecutive failures before marking unhealthy (default: 3) */
|
|
29
|
+
maxConsecutiveFailures?: number;
|
|
30
|
+
/** Time to wait before retrying unhealthy provider in ms (default: 300000) */
|
|
31
|
+
unhealthyRetryDelayMs?: number;
|
|
32
|
+
/** Callback for usage tracking (applied to all providers) */
|
|
33
|
+
onUsage?: (inputTokens: number, outputTokens: number) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Fallback result with metadata.
|
|
37
|
+
*/
|
|
38
|
+
export interface FallbackResult {
|
|
39
|
+
response: string;
|
|
40
|
+
provider: LLMProviderId;
|
|
41
|
+
model: string;
|
|
42
|
+
attemptedProviders: LLMProviderId[];
|
|
43
|
+
failedProviders: Array<{
|
|
44
|
+
provider: LLMProviderId;
|
|
45
|
+
error: string;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* LLM client with automatic provider failover.
|
|
50
|
+
*/
|
|
51
|
+
export declare class FallbackLLMClient implements LLMClient {
|
|
52
|
+
private clients;
|
|
53
|
+
private providerOrder;
|
|
54
|
+
private health;
|
|
55
|
+
private config;
|
|
56
|
+
constructor(config: FallbackConfig);
|
|
57
|
+
/**
|
|
58
|
+
* Initialize all provider clients.
|
|
59
|
+
*/
|
|
60
|
+
private initializeClients;
|
|
61
|
+
/**
|
|
62
|
+
* Get combined provider info.
|
|
63
|
+
*/
|
|
64
|
+
getProviderInfo(): ProviderInfo;
|
|
65
|
+
/**
|
|
66
|
+
* Get the current primary (first healthy) client.
|
|
67
|
+
*/
|
|
68
|
+
private getPrimaryClient;
|
|
69
|
+
/**
|
|
70
|
+
* Check if a provider is currently healthy.
|
|
71
|
+
*/
|
|
72
|
+
private isProviderHealthy;
|
|
73
|
+
/**
|
|
74
|
+
* Mark a provider as failed.
|
|
75
|
+
*/
|
|
76
|
+
private markProviderFailed;
|
|
77
|
+
/**
|
|
78
|
+
* Mark a provider as successful.
|
|
79
|
+
*/
|
|
80
|
+
private markProviderSuccess;
|
|
81
|
+
/**
|
|
82
|
+
* Determine if an error should trigger failover to next provider.
|
|
83
|
+
*/
|
|
84
|
+
private shouldFailover;
|
|
85
|
+
/**
|
|
86
|
+
* Execute a completion with fallback.
|
|
87
|
+
*/
|
|
88
|
+
private executeWithFallback;
|
|
89
|
+
/**
|
|
90
|
+
* Chat completion with fallback.
|
|
91
|
+
*/
|
|
92
|
+
chat(messages: Message[], options?: CompletionOptions): Promise<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Single prompt completion with fallback.
|
|
95
|
+
*/
|
|
96
|
+
complete(prompt: string, options?: CompletionOptions): Promise<string>;
|
|
97
|
+
/**
|
|
98
|
+
* Parse JSON from response.
|
|
99
|
+
*/
|
|
100
|
+
parseJSON<T>(response: string): T;
|
|
101
|
+
/**
|
|
102
|
+
* Stream completion from a single prompt with fallback.
|
|
103
|
+
*/
|
|
104
|
+
stream(prompt: string, options?: StreamingOptions): Promise<StreamingResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Stream chat completion with fallback.
|
|
107
|
+
*/
|
|
108
|
+
streamChat(messages: Message[], options?: StreamingOptions): Promise<StreamingResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Get health status for all providers.
|
|
111
|
+
*/
|
|
112
|
+
getProviderHealth(): ProviderHealth[];
|
|
113
|
+
/**
|
|
114
|
+
* Get list of available providers in priority order.
|
|
115
|
+
*/
|
|
116
|
+
getProviderOrder(): LLMProviderId[];
|
|
117
|
+
/**
|
|
118
|
+
* Check health of all providers (for Ollama this pings the server).
|
|
119
|
+
*/
|
|
120
|
+
checkHealth(): Promise<ProviderHealth[]>;
|
|
121
|
+
/**
|
|
122
|
+
* Manually mark a provider as unhealthy.
|
|
123
|
+
*/
|
|
124
|
+
disableProvider(providerId: LLMProviderId): void;
|
|
125
|
+
/**
|
|
126
|
+
* Manually mark a provider as healthy.
|
|
127
|
+
*/
|
|
128
|
+
enableProvider(providerId: LLMProviderId): void;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create a fallback LLM client from available providers.
|
|
132
|
+
* Automatically detects available API keys and sets up fallback chain.
|
|
133
|
+
*/
|
|
134
|
+
export declare function createFallbackClient(options?: {
|
|
135
|
+
preferredOrder?: LLMProviderId[];
|
|
136
|
+
useOllamaFallback?: boolean;
|
|
137
|
+
ollamaModel?: string;
|
|
138
|
+
onUsage?: (inputTokens: number, outputTokens: number) => void;
|
|
139
|
+
}): FallbackLLMClient;
|
|
140
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback LLM client that tries multiple providers in sequence.
|
|
3
|
+
* Provides resilience through provider failover and model fallback.
|
|
4
|
+
*/
|
|
5
|
+
import { parseJSONResponse, DEFAULT_MODELS } from './client.js';
|
|
6
|
+
import { createLLMClient } from './factory.js';
|
|
7
|
+
import { OllamaClient } from './ollama.js';
|
|
8
|
+
import { getLogger } from '../logging/logger.js';
|
|
9
|
+
import { LLMAuthError, LLMQuotaError, LLMConnectionError, LLMRateLimitError, } from '../errors/types.js';
|
|
10
|
+
import { TIME_CONSTANTS, RETRY } from '../constants.js';
|
|
11
|
+
const logger = getLogger('fallback-llm');
|
|
12
|
+
/**
|
|
13
|
+
* LLM client with automatic provider failover.
|
|
14
|
+
*/
|
|
15
|
+
export class FallbackLLMClient {
|
|
16
|
+
clients = new Map();
|
|
17
|
+
providerOrder = [];
|
|
18
|
+
health = new Map();
|
|
19
|
+
config;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = {
|
|
22
|
+
providers: config.providers,
|
|
23
|
+
useOllamaFallback: config.useOllamaFallback ?? true,
|
|
24
|
+
ollamaModel: config.ollamaModel ?? DEFAULT_MODELS.ollama,
|
|
25
|
+
healthCheckIntervalMs: config.healthCheckIntervalMs ?? TIME_CONSTANTS.HEALTH_CHECK_INTERVAL,
|
|
26
|
+
maxConsecutiveFailures: config.maxConsecutiveFailures ?? RETRY.DEFAULT_ATTEMPTS,
|
|
27
|
+
unhealthyRetryDelayMs: config.unhealthyRetryDelayMs ?? TIME_CONSTANTS.UNHEALTHY_RETRY_DELAY,
|
|
28
|
+
onUsage: config.onUsage,
|
|
29
|
+
};
|
|
30
|
+
this.initializeClients();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Initialize all provider clients.
|
|
34
|
+
*/
|
|
35
|
+
initializeClients() {
|
|
36
|
+
for (const providerConfig of this.config.providers) {
|
|
37
|
+
try {
|
|
38
|
+
// Apply shared onUsage callback
|
|
39
|
+
const configWithUsage = {
|
|
40
|
+
...providerConfig,
|
|
41
|
+
onUsage: this.config.onUsage,
|
|
42
|
+
};
|
|
43
|
+
const client = createLLMClient(configWithUsage);
|
|
44
|
+
this.clients.set(providerConfig.provider, client);
|
|
45
|
+
this.providerOrder.push(providerConfig.provider);
|
|
46
|
+
this.health.set(providerConfig.provider, {
|
|
47
|
+
provider: providerConfig.provider,
|
|
48
|
+
healthy: true,
|
|
49
|
+
lastChecked: new Date(),
|
|
50
|
+
consecutiveFailures: 0,
|
|
51
|
+
});
|
|
52
|
+
logger.debug({ provider: providerConfig.provider }, 'Initialized provider');
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger.warn({ provider: providerConfig.provider, error: String(error) }, 'Failed to initialize provider');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Add Ollama as final fallback if enabled
|
|
59
|
+
if (this.config.useOllamaFallback && !this.clients.has('ollama')) {
|
|
60
|
+
try {
|
|
61
|
+
const ollamaClient = new OllamaClient({
|
|
62
|
+
model: this.config.ollamaModel,
|
|
63
|
+
});
|
|
64
|
+
this.clients.set('ollama', ollamaClient);
|
|
65
|
+
this.providerOrder.push('ollama');
|
|
66
|
+
this.health.set('ollama', {
|
|
67
|
+
provider: 'ollama',
|
|
68
|
+
healthy: true, // Assume healthy until checked
|
|
69
|
+
lastChecked: new Date(),
|
|
70
|
+
consecutiveFailures: 0,
|
|
71
|
+
});
|
|
72
|
+
logger.debug('Added Ollama as fallback provider');
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
logger.warn({ error: String(error) }, 'Failed to initialize Ollama fallback');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (this.clients.size === 0) {
|
|
79
|
+
throw new Error('No LLM providers could be initialized');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get combined provider info.
|
|
84
|
+
*/
|
|
85
|
+
getProviderInfo() {
|
|
86
|
+
const primaryClient = this.getPrimaryClient();
|
|
87
|
+
const info = primaryClient.getProviderInfo();
|
|
88
|
+
return {
|
|
89
|
+
...info,
|
|
90
|
+
id: `fallback(${this.providerOrder.join(',')})`,
|
|
91
|
+
name: `Fallback: ${info.name}`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the current primary (first healthy) client.
|
|
96
|
+
*/
|
|
97
|
+
getPrimaryClient() {
|
|
98
|
+
for (const providerId of this.providerOrder) {
|
|
99
|
+
if (this.isProviderHealthy(providerId)) {
|
|
100
|
+
const client = this.clients.get(providerId);
|
|
101
|
+
if (client)
|
|
102
|
+
return client;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Return first client as fallback (guaranteed to exist from constructor check)
|
|
106
|
+
const firstClient = this.clients.values().next().value;
|
|
107
|
+
if (!firstClient) {
|
|
108
|
+
throw new Error('No LLM clients available');
|
|
109
|
+
}
|
|
110
|
+
return firstClient;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if a provider is currently healthy.
|
|
114
|
+
*/
|
|
115
|
+
isProviderHealthy(providerId) {
|
|
116
|
+
const health = this.health.get(providerId);
|
|
117
|
+
if (!health)
|
|
118
|
+
return false;
|
|
119
|
+
// If marked unhealthy, check if retry delay has passed
|
|
120
|
+
if (!health.healthy) {
|
|
121
|
+
const timeSinceCheck = Date.now() - health.lastChecked.getTime();
|
|
122
|
+
if (timeSinceCheck >= this.config.unhealthyRetryDelayMs) {
|
|
123
|
+
// Reset to allow retry
|
|
124
|
+
health.healthy = true;
|
|
125
|
+
health.consecutiveFailures = 0;
|
|
126
|
+
logger.info({ provider: providerId }, 'Resetting unhealthy provider for retry');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return health.healthy;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Mark a provider as failed.
|
|
133
|
+
*/
|
|
134
|
+
markProviderFailed(providerId, error) {
|
|
135
|
+
const health = this.health.get(providerId);
|
|
136
|
+
if (!health)
|
|
137
|
+
return;
|
|
138
|
+
health.consecutiveFailures++;
|
|
139
|
+
health.lastError = error.message;
|
|
140
|
+
health.lastChecked = new Date();
|
|
141
|
+
// Check if we should mark as unhealthy
|
|
142
|
+
if (health.consecutiveFailures >= this.config.maxConsecutiveFailures) {
|
|
143
|
+
health.healthy = false;
|
|
144
|
+
logger.warn({
|
|
145
|
+
provider: providerId,
|
|
146
|
+
consecutiveFailures: health.consecutiveFailures,
|
|
147
|
+
error: error.message,
|
|
148
|
+
}, 'Marking provider as unhealthy');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Mark a provider as successful.
|
|
153
|
+
*/
|
|
154
|
+
markProviderSuccess(providerId) {
|
|
155
|
+
const health = this.health.get(providerId);
|
|
156
|
+
if (!health)
|
|
157
|
+
return;
|
|
158
|
+
health.healthy = true;
|
|
159
|
+
health.consecutiveFailures = 0;
|
|
160
|
+
health.lastChecked = new Date();
|
|
161
|
+
health.lastError = undefined;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Determine if an error should trigger failover to next provider.
|
|
165
|
+
*/
|
|
166
|
+
shouldFailover(error) {
|
|
167
|
+
// Auth and quota errors are terminal for that provider - failover
|
|
168
|
+
if (error instanceof LLMAuthError || error instanceof LLMQuotaError) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
// Connection errors - failover
|
|
172
|
+
if (error instanceof LLMConnectionError) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
// Rate limits - failover (they have their own retry logic)
|
|
176
|
+
if (error instanceof LLMRateLimitError) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
// Check error message for common failover conditions
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
const message = error.message.toLowerCase();
|
|
182
|
+
if (message.includes('econnrefused') ||
|
|
183
|
+
message.includes('econnreset') ||
|
|
184
|
+
message.includes('network') ||
|
|
185
|
+
message.includes('timeout') ||
|
|
186
|
+
message.includes('unavailable') ||
|
|
187
|
+
message.includes('503') ||
|
|
188
|
+
message.includes('502')) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Execute a completion with fallback.
|
|
196
|
+
*/
|
|
197
|
+
async executeWithFallback(operation, operationName) {
|
|
198
|
+
const attemptedProviders = [];
|
|
199
|
+
const errors = [];
|
|
200
|
+
for (const providerId of this.providerOrder) {
|
|
201
|
+
if (!this.isProviderHealthy(providerId)) {
|
|
202
|
+
logger.debug({ provider: providerId }, 'Skipping unhealthy provider');
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const client = this.clients.get(providerId);
|
|
206
|
+
if (!client)
|
|
207
|
+
continue;
|
|
208
|
+
attemptedProviders.push(providerId);
|
|
209
|
+
try {
|
|
210
|
+
logger.debug({ provider: providerId, operation: operationName }, 'Attempting operation');
|
|
211
|
+
const result = await operation(client);
|
|
212
|
+
this.markProviderSuccess(providerId);
|
|
213
|
+
return { result, provider: providerId };
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
217
|
+
errors.push({ provider: providerId, error: err });
|
|
218
|
+
this.markProviderFailed(providerId, err);
|
|
219
|
+
logger.warn({
|
|
220
|
+
provider: providerId,
|
|
221
|
+
operation: operationName,
|
|
222
|
+
error: err.message,
|
|
223
|
+
shouldFailover: this.shouldFailover(error),
|
|
224
|
+
}, 'Provider operation failed');
|
|
225
|
+
if (!this.shouldFailover(error)) {
|
|
226
|
+
// Non-failover error - throw immediately
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
// Continue to next provider
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// All providers failed
|
|
233
|
+
const errorSummary = errors
|
|
234
|
+
.map(({ provider, error }) => `${provider}: ${error.message}`)
|
|
235
|
+
.join('; ');
|
|
236
|
+
throw new Error(`All LLM providers failed for ${operationName}. Errors: ${errorSummary}`);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Chat completion with fallback.
|
|
240
|
+
*/
|
|
241
|
+
async chat(messages, options) {
|
|
242
|
+
const { result } = await this.executeWithFallback((client) => client.chat(messages, options), 'chat');
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Single prompt completion with fallback.
|
|
247
|
+
*/
|
|
248
|
+
async complete(prompt, options) {
|
|
249
|
+
const { result } = await this.executeWithFallback((client) => client.complete(prompt, options), 'complete');
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Parse JSON from response.
|
|
254
|
+
*/
|
|
255
|
+
parseJSON(response) {
|
|
256
|
+
return parseJSONResponse(response);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Stream completion from a single prompt with fallback.
|
|
260
|
+
*/
|
|
261
|
+
async stream(prompt, options) {
|
|
262
|
+
const { result } = await this.executeWithFallback((client) => client.stream(prompt, options), 'stream');
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Stream chat completion with fallback.
|
|
267
|
+
*/
|
|
268
|
+
async streamChat(messages, options) {
|
|
269
|
+
const { result } = await this.executeWithFallback((client) => client.streamChat(messages, options), 'streamChat');
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get health status for all providers.
|
|
274
|
+
*/
|
|
275
|
+
getProviderHealth() {
|
|
276
|
+
return Array.from(this.health.values());
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get list of available providers in priority order.
|
|
280
|
+
*/
|
|
281
|
+
getProviderOrder() {
|
|
282
|
+
return [...this.providerOrder];
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Check health of all providers (for Ollama this pings the server).
|
|
286
|
+
*/
|
|
287
|
+
async checkHealth() {
|
|
288
|
+
const results = [];
|
|
289
|
+
for (const providerId of this.providerOrder) {
|
|
290
|
+
const client = this.clients.get(providerId);
|
|
291
|
+
const health = this.health.get(providerId);
|
|
292
|
+
if (!client || !health)
|
|
293
|
+
continue;
|
|
294
|
+
try {
|
|
295
|
+
// For Ollama, use isAvailable()
|
|
296
|
+
if (providerId === 'ollama' && client instanceof OllamaClient) {
|
|
297
|
+
const available = await client.isAvailable();
|
|
298
|
+
health.healthy = available;
|
|
299
|
+
health.lastChecked = new Date();
|
|
300
|
+
if (!available) {
|
|
301
|
+
health.lastError = 'Ollama server not responding';
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
health.lastError = undefined;
|
|
305
|
+
health.consecutiveFailures = 0;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
// For API providers, we assume healthy if we have credentials
|
|
310
|
+
// Actual health is determined by request success/failure
|
|
311
|
+
health.lastChecked = new Date();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
health.healthy = false;
|
|
316
|
+
health.lastError = error instanceof Error ? error.message : String(error);
|
|
317
|
+
health.lastChecked = new Date();
|
|
318
|
+
}
|
|
319
|
+
results.push({ ...health });
|
|
320
|
+
}
|
|
321
|
+
return results;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Manually mark a provider as unhealthy.
|
|
325
|
+
*/
|
|
326
|
+
disableProvider(providerId) {
|
|
327
|
+
const health = this.health.get(providerId);
|
|
328
|
+
if (health) {
|
|
329
|
+
health.healthy = false;
|
|
330
|
+
health.lastChecked = new Date();
|
|
331
|
+
logger.info({ provider: providerId }, 'Provider manually disabled');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Manually mark a provider as healthy.
|
|
336
|
+
*/
|
|
337
|
+
enableProvider(providerId) {
|
|
338
|
+
const health = this.health.get(providerId);
|
|
339
|
+
if (health) {
|
|
340
|
+
health.healthy = true;
|
|
341
|
+
health.consecutiveFailures = 0;
|
|
342
|
+
health.lastError = undefined;
|
|
343
|
+
health.lastChecked = new Date();
|
|
344
|
+
logger.info({ provider: providerId }, 'Provider manually enabled');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Create a fallback LLM client from available providers.
|
|
350
|
+
* Automatically detects available API keys and sets up fallback chain.
|
|
351
|
+
*/
|
|
352
|
+
export function createFallbackClient(options) {
|
|
353
|
+
const providers = [];
|
|
354
|
+
const order = options?.preferredOrder ?? ['anthropic', 'openai', 'ollama'];
|
|
355
|
+
for (const providerId of order) {
|
|
356
|
+
if (providerId === 'anthropic' && process.env.ANTHROPIC_API_KEY) {
|
|
357
|
+
providers.push({
|
|
358
|
+
provider: 'anthropic',
|
|
359
|
+
model: DEFAULT_MODELS.anthropic,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
else if (providerId === 'openai' && process.env.OPENAI_API_KEY) {
|
|
363
|
+
providers.push({
|
|
364
|
+
provider: 'openai',
|
|
365
|
+
model: DEFAULT_MODELS.openai,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
else if (providerId === 'ollama') {
|
|
369
|
+
// Ollama will be added as fallback if useOllamaFallback is true
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return new FallbackLLMClient({
|
|
373
|
+
providers,
|
|
374
|
+
useOllamaFallback: options?.useOllamaFallback ?? true,
|
|
375
|
+
ollamaModel: options?.ollamaModel,
|
|
376
|
+
onUsage: options?.onUsage,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM module - multi-provider language model support.
|
|
3
|
+
*/
|
|
4
|
+
export type { LLMClient, Message, CompletionOptions, ProviderInfo, LLMConfig, LLMProviderId, StreamingOptions, StreamingResult, } from './client.js';
|
|
5
|
+
export { DEFAULT_MODELS, PREMIUM_MODELS, parseJSONResponse } from './client.js';
|
|
6
|
+
export { OpenAIClient } from './openai.js';
|
|
7
|
+
export type { OpenAIClientOptions } from './openai.js';
|
|
8
|
+
export { AnthropicClient } from './anthropic.js';
|
|
9
|
+
export type { AnthropicClientOptions } from './anthropic.js';
|
|
10
|
+
export { OllamaClient } from './ollama.js';
|
|
11
|
+
export type { OllamaClientOptions } from './ollama.js';
|
|
12
|
+
export { createLLMClient, createAutoClient, detectProvider, checkProviderAvailability, getDefaultModel, getSupportedProviders, } from './factory.js';
|
|
13
|
+
export type { ProviderAvailability } from './factory.js';
|
|
14
|
+
export { FallbackLLMClient, createFallbackClient } from './fallback.js';
|
|
15
|
+
export type { FallbackConfig, ProviderHealth, FallbackResult } from './fallback.js';
|
|
16
|
+
export { estimateTokens, estimateMessagesTokens, estimateWithContext, getContextWindow, truncateMessages, truncateText, TokenBudgetTracker, BudgetEnforcedLLMClient, TokenBudgetExceededError, withTokenBudget, } from './token-budget.js';
|
|
17
|
+
export type { TokenBudgetOptions, TokenEstimate, BudgetStatus, } from './token-budget.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM module - multi-provider language model support.
|
|
3
|
+
*/
|
|
4
|
+
export { DEFAULT_MODELS, PREMIUM_MODELS, parseJSONResponse } from './client.js';
|
|
5
|
+
// Provider implementations
|
|
6
|
+
export { OpenAIClient } from './openai.js';
|
|
7
|
+
export { AnthropicClient } from './anthropic.js';
|
|
8
|
+
export { OllamaClient } from './ollama.js';
|
|
9
|
+
// Factory functions
|
|
10
|
+
export { createLLMClient, createAutoClient, detectProvider, checkProviderAvailability, getDefaultModel, getSupportedProviders, } from './factory.js';
|
|
11
|
+
// Fallback client
|
|
12
|
+
export { FallbackLLMClient, createFallbackClient } from './fallback.js';
|
|
13
|
+
// Token budget enforcement
|
|
14
|
+
export { estimateTokens, estimateMessagesTokens, estimateWithContext, getContextWindow, truncateMessages, truncateText, TokenBudgetTracker, BudgetEnforcedLLMClient, TokenBudgetExceededError, withTokenBudget, } from './token-budget.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|