@unerr-ai/unerr 0.2.1 → 0.2.3
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/README.md +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local Chat Provider — BYO-LLM streaming chat for the REPL.
|
|
3
|
-
*
|
|
4
|
-
* Implements the ChatProvider interface by routing to Ollama (/api/chat),
|
|
5
|
-
* LM Studio / OpenAI-compatible (/v1/chat/completions), or anthropic-direct.
|
|
6
|
-
* All providers use Server-Sent Events or NDJSON streaming.
|
|
7
|
-
*
|
|
8
|
-
* SECURITY: API keys are NEVER logged or included in MCP responses.
|
|
9
|
-
*/
|
|
10
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
11
|
-
// ── Convert unerr Tool → ChatToolDef ────────────────────────
|
|
12
|
-
export function toChatToolDefs(tools) {
|
|
13
|
-
return tools.map((t) => ({
|
|
14
|
-
type: "function",
|
|
15
|
-
function: {
|
|
16
|
-
name: t.name,
|
|
17
|
-
description: t.description,
|
|
18
|
-
parameters: t.inputSchema,
|
|
19
|
-
},
|
|
20
|
-
}));
|
|
21
|
-
}
|
|
22
|
-
// ── Anthropic Chat Provider ─────────────────────────────────
|
|
23
|
-
export class AnthropicChatProvider {
|
|
24
|
-
providerName = "anthropic";
|
|
25
|
-
modelId;
|
|
26
|
-
client;
|
|
27
|
-
constructor(apiKey, model) {
|
|
28
|
-
this.client = new Anthropic({ apiKey });
|
|
29
|
-
this.modelId = model;
|
|
30
|
-
}
|
|
31
|
-
async streamChat(messages, tools, systemPrompt, maxTokens, onChunk) {
|
|
32
|
-
// Convert ChatMessage[] → Anthropic format
|
|
33
|
-
const anthropicMessages = this.toAnthropicMessages(messages);
|
|
34
|
-
const anthropicTools = tools.map((t) => ({
|
|
35
|
-
name: t.function.name,
|
|
36
|
-
description: t.function.description,
|
|
37
|
-
input_schema: t.function.parameters,
|
|
38
|
-
}));
|
|
39
|
-
const stream = this.client.messages.stream({
|
|
40
|
-
model: this.modelId,
|
|
41
|
-
max_tokens: maxTokens,
|
|
42
|
-
system: systemPrompt,
|
|
43
|
-
messages: anthropicMessages,
|
|
44
|
-
tools: anthropicTools.length > 0 ? anthropicTools : undefined,
|
|
45
|
-
});
|
|
46
|
-
let text = "";
|
|
47
|
-
stream.on("text", (t) => {
|
|
48
|
-
text += t;
|
|
49
|
-
onChunk({ type: "text_delta", text: t });
|
|
50
|
-
});
|
|
51
|
-
const message = await stream.finalMessage();
|
|
52
|
-
const toolCalls = [];
|
|
53
|
-
for (const block of message.content) {
|
|
54
|
-
if (block.type === "tool_use") {
|
|
55
|
-
toolCalls.push({
|
|
56
|
-
id: block.id,
|
|
57
|
-
name: block.name,
|
|
58
|
-
args: block.input,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
onChunk({ type: "done" });
|
|
63
|
-
return {
|
|
64
|
-
text,
|
|
65
|
-
toolCalls,
|
|
66
|
-
inputTokens: message.usage.input_tokens,
|
|
67
|
-
outputTokens: message.usage.output_tokens,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
toAnthropicMessages(messages) {
|
|
71
|
-
const result = [];
|
|
72
|
-
for (const msg of messages) {
|
|
73
|
-
if (msg.role === "system")
|
|
74
|
-
continue; // System prompt handled separately
|
|
75
|
-
if (msg.role === "tool") {
|
|
76
|
-
result.push({
|
|
77
|
-
role: "user",
|
|
78
|
-
content: [
|
|
79
|
-
{
|
|
80
|
-
type: "tool_result",
|
|
81
|
-
tool_use_id: msg.tool_call_id ?? "",
|
|
82
|
-
content: msg.content,
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
else if (msg.role === "assistant" && msg.tool_calls?.length) {
|
|
88
|
-
const blocks = [];
|
|
89
|
-
if (msg.content)
|
|
90
|
-
blocks.push({ type: "text", text: msg.content });
|
|
91
|
-
for (const tc of msg.tool_calls) {
|
|
92
|
-
blocks.push({
|
|
93
|
-
type: "tool_use",
|
|
94
|
-
id: tc.id,
|
|
95
|
-
name: tc.function.name,
|
|
96
|
-
input: JSON.parse(tc.function.arguments),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
result.push({ role: "assistant", content: blocks });
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
result.push({
|
|
103
|
-
role: msg.role,
|
|
104
|
-
content: msg.content,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// ── Local Chat Provider ─────────────────────────────────────
|
|
112
|
-
const DEFAULT_BASE_URLS = {
|
|
113
|
-
ollama: "http://localhost:11434",
|
|
114
|
-
"lm-studio": "http://localhost:1234",
|
|
115
|
-
"openai-compatible": "http://localhost:8080",
|
|
116
|
-
"anthropic-direct": "https://api.anthropic.com",
|
|
117
|
-
};
|
|
118
|
-
export class LocalChatProvider {
|
|
119
|
-
providerName;
|
|
120
|
-
modelId;
|
|
121
|
-
baseUrl;
|
|
122
|
-
apiKey;
|
|
123
|
-
useOllamaApi;
|
|
124
|
-
constructor(config) {
|
|
125
|
-
this.providerName = config.provider;
|
|
126
|
-
this.modelId = config.chatModel;
|
|
127
|
-
this.baseUrl = (config.baseUrl ??
|
|
128
|
-
DEFAULT_BASE_URLS[config.provider] ??
|
|
129
|
-
"http://localhost:8080").replace(/\/+$/, "");
|
|
130
|
-
this.apiKey = config.apiKey;
|
|
131
|
-
this.useOllamaApi = config.provider === "ollama";
|
|
132
|
-
}
|
|
133
|
-
async streamChat(messages, tools, systemPrompt, maxTokens, onChunk) {
|
|
134
|
-
// anthropic-direct uses the Anthropic SDK directly
|
|
135
|
-
if (this.providerName === "anthropic-direct") {
|
|
136
|
-
return this.streamAnthropicDirect(messages, tools, systemPrompt, maxTokens, onChunk);
|
|
137
|
-
}
|
|
138
|
-
if (this.useOllamaApi) {
|
|
139
|
-
return this.streamOllama(messages, tools, systemPrompt, maxTokens, onChunk);
|
|
140
|
-
}
|
|
141
|
-
return this.streamOpenAiCompatible(messages, tools, systemPrompt, maxTokens, onChunk);
|
|
142
|
-
}
|
|
143
|
-
async streamAnthropicDirect(messages, tools, systemPrompt, maxTokens, onChunk) {
|
|
144
|
-
if (!this.apiKey) {
|
|
145
|
-
throw new Error("[LocalChatProvider] anthropic-direct requires an API key. " +
|
|
146
|
-
"Set localLlm.apiKey in settings.json.");
|
|
147
|
-
}
|
|
148
|
-
const provider = new AnthropicChatProvider(this.apiKey, this.modelId);
|
|
149
|
-
return provider.streamChat(messages, tools, systemPrompt, maxTokens, onChunk);
|
|
150
|
-
}
|
|
151
|
-
// ── Ollama: POST /api/chat with NDJSON streaming ──────────
|
|
152
|
-
async streamOllama(messages, tools, systemPrompt, _maxTokens, onChunk) {
|
|
153
|
-
const ollamaMessages = [
|
|
154
|
-
{ role: "system", content: systemPrompt },
|
|
155
|
-
...messages.map((m) => ({
|
|
156
|
-
role: m.role === "tool" ? "user" : m.role,
|
|
157
|
-
content: m.content,
|
|
158
|
-
})),
|
|
159
|
-
];
|
|
160
|
-
const body = {
|
|
161
|
-
model: this.modelId,
|
|
162
|
-
messages: ollamaMessages,
|
|
163
|
-
stream: true,
|
|
164
|
-
};
|
|
165
|
-
// Ollama supports tools via the OpenAI-compatible format
|
|
166
|
-
if (tools.length > 0) {
|
|
167
|
-
body.tools = tools;
|
|
168
|
-
}
|
|
169
|
-
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
170
|
-
method: "POST",
|
|
171
|
-
headers: { "Content-Type": "application/json" },
|
|
172
|
-
body: JSON.stringify(body),
|
|
173
|
-
});
|
|
174
|
-
if (!response.ok) {
|
|
175
|
-
const errText = await response.text().catch(() => "");
|
|
176
|
-
throw new Error(`[LocalChatProvider] Ollama chat failed: ${response.status} ${response.statusText}${errText ? ` — ${errText.slice(0, 200)}` : ""}`);
|
|
177
|
-
}
|
|
178
|
-
return this.parseNdjsonStream(response, onChunk);
|
|
179
|
-
}
|
|
180
|
-
async parseNdjsonStream(response, onChunk) {
|
|
181
|
-
const reader = response.body?.getReader();
|
|
182
|
-
if (!reader)
|
|
183
|
-
throw new Error("[LocalChatProvider] No response body");
|
|
184
|
-
const decoder = new TextDecoder();
|
|
185
|
-
let text = "";
|
|
186
|
-
const toolCalls = [];
|
|
187
|
-
let inputTokens = 0;
|
|
188
|
-
let outputTokens = 0;
|
|
189
|
-
let buffer = "";
|
|
190
|
-
while (true) {
|
|
191
|
-
const { done, value } = await reader.read();
|
|
192
|
-
if (done)
|
|
193
|
-
break;
|
|
194
|
-
buffer += decoder.decode(value, { stream: true });
|
|
195
|
-
const lines = buffer.split("\n");
|
|
196
|
-
buffer = lines.pop() ?? "";
|
|
197
|
-
for (const line of lines) {
|
|
198
|
-
if (!line.trim())
|
|
199
|
-
continue;
|
|
200
|
-
try {
|
|
201
|
-
const chunk = JSON.parse(line);
|
|
202
|
-
if (chunk.message?.content) {
|
|
203
|
-
text += chunk.message.content;
|
|
204
|
-
onChunk({ type: "text_delta", text: chunk.message.content });
|
|
205
|
-
}
|
|
206
|
-
if (chunk.message?.tool_calls) {
|
|
207
|
-
for (const tc of chunk.message.tool_calls) {
|
|
208
|
-
toolCalls.push({
|
|
209
|
-
id: tc.id ?? `tool_${Date.now()}`,
|
|
210
|
-
name: tc.function?.name ?? "",
|
|
211
|
-
args: typeof tc.function?.arguments === "string"
|
|
212
|
-
? JSON.parse(tc.function.arguments)
|
|
213
|
-
: (tc.function?.arguments ?? {}),
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (chunk.eval_count)
|
|
218
|
-
outputTokens = chunk.eval_count;
|
|
219
|
-
if (chunk.prompt_eval_count)
|
|
220
|
-
inputTokens = chunk.prompt_eval_count;
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// Skip malformed JSON lines
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
onChunk({ type: "done" });
|
|
228
|
-
return { text, toolCalls, inputTokens, outputTokens };
|
|
229
|
-
}
|
|
230
|
-
// ── OpenAI-Compatible: POST /v1/chat/completions with SSE ──
|
|
231
|
-
async streamOpenAiCompatible(messages, tools, systemPrompt, maxTokens, onChunk) {
|
|
232
|
-
const openaiMessages = [
|
|
233
|
-
{ role: "system", content: systemPrompt },
|
|
234
|
-
...messages.map((m) => {
|
|
235
|
-
if (m.tool_calls) {
|
|
236
|
-
return { role: m.role, content: m.content, tool_calls: m.tool_calls };
|
|
237
|
-
}
|
|
238
|
-
if (m.tool_call_id) {
|
|
239
|
-
return {
|
|
240
|
-
role: m.role,
|
|
241
|
-
content: m.content,
|
|
242
|
-
tool_call_id: m.tool_call_id,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
return { role: m.role, content: m.content };
|
|
246
|
-
}),
|
|
247
|
-
];
|
|
248
|
-
const body = {
|
|
249
|
-
model: this.modelId,
|
|
250
|
-
messages: openaiMessages,
|
|
251
|
-
max_tokens: maxTokens,
|
|
252
|
-
stream: true,
|
|
253
|
-
};
|
|
254
|
-
if (tools.length > 0) {
|
|
255
|
-
body.tools = tools;
|
|
256
|
-
}
|
|
257
|
-
const headers = {
|
|
258
|
-
"Content-Type": "application/json",
|
|
259
|
-
};
|
|
260
|
-
if (this.apiKey) {
|
|
261
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
262
|
-
}
|
|
263
|
-
const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
264
|
-
method: "POST",
|
|
265
|
-
headers,
|
|
266
|
-
body: JSON.stringify(body),
|
|
267
|
-
});
|
|
268
|
-
if (!response.ok) {
|
|
269
|
-
const errText = await response.text().catch(() => "");
|
|
270
|
-
throw new Error(`[LocalChatProvider] Chat request failed: ${response.status} ${response.statusText}${errText ? ` — ${errText.slice(0, 200)}` : ""}`);
|
|
271
|
-
}
|
|
272
|
-
return this.parseSseStream(response, onChunk);
|
|
273
|
-
}
|
|
274
|
-
async parseSseStream(response, onChunk) {
|
|
275
|
-
const reader = response.body?.getReader();
|
|
276
|
-
if (!reader)
|
|
277
|
-
throw new Error("[LocalChatProvider] No response body");
|
|
278
|
-
const decoder = new TextDecoder();
|
|
279
|
-
let text = "";
|
|
280
|
-
const toolCalls = [];
|
|
281
|
-
const pendingToolArgs = new Map();
|
|
282
|
-
let buffer = "";
|
|
283
|
-
while (true) {
|
|
284
|
-
const { done, value } = await reader.read();
|
|
285
|
-
if (done)
|
|
286
|
-
break;
|
|
287
|
-
buffer += decoder.decode(value, { stream: true });
|
|
288
|
-
const lines = buffer.split("\n");
|
|
289
|
-
buffer = lines.pop() ?? "";
|
|
290
|
-
for (const line of lines) {
|
|
291
|
-
if (!line.startsWith("data: "))
|
|
292
|
-
continue;
|
|
293
|
-
const data = line.slice(6).trim();
|
|
294
|
-
if (data === "[DONE]")
|
|
295
|
-
continue;
|
|
296
|
-
try {
|
|
297
|
-
const chunk = JSON.parse(data);
|
|
298
|
-
const delta = chunk.choices?.[0]?.delta;
|
|
299
|
-
if (!delta)
|
|
300
|
-
continue;
|
|
301
|
-
if (delta.content) {
|
|
302
|
-
text += delta.content;
|
|
303
|
-
onChunk({ type: "text_delta", text: delta.content });
|
|
304
|
-
}
|
|
305
|
-
if (delta.tool_calls) {
|
|
306
|
-
for (const tc of delta.tool_calls) {
|
|
307
|
-
const idx = tc.index ?? 0;
|
|
308
|
-
if (tc.id) {
|
|
309
|
-
pendingToolArgs.set(idx, {
|
|
310
|
-
id: tc.id,
|
|
311
|
-
name: tc.function?.name ?? "",
|
|
312
|
-
args: tc.function?.arguments ?? "",
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
else if (pendingToolArgs.has(idx) && tc.function?.arguments) {
|
|
316
|
-
const pending = pendingToolArgs.get(idx);
|
|
317
|
-
if (pending)
|
|
318
|
-
pending.args += tc.function.arguments;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
catch {
|
|
324
|
-
// Skip malformed SSE data
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
// Finalize pending tool calls
|
|
329
|
-
for (const [, pending] of pendingToolArgs) {
|
|
330
|
-
try {
|
|
331
|
-
toolCalls.push({
|
|
332
|
-
id: pending.id,
|
|
333
|
-
name: pending.name,
|
|
334
|
-
args: pending.args ? JSON.parse(pending.args) : {},
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
catch {
|
|
338
|
-
toolCalls.push({
|
|
339
|
-
id: pending.id,
|
|
340
|
-
name: pending.name,
|
|
341
|
-
args: {},
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
onChunk({ type: "done" });
|
|
346
|
-
// OpenAI-compatible endpoints don't always return token counts in streamed responses
|
|
347
|
-
return { text, toolCalls, inputTokens: 0, outputTokens: 0 };
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// ── AI SDK Chat Provider ─────────────────────────────────────
|
|
351
|
-
/**
|
|
352
|
-
* Chat provider backed by Vercel AI SDK streamText().
|
|
353
|
-
*
|
|
354
|
-
* Works with any AI SDK-compatible LanguageModel (Anthropic, OpenAI, Google,
|
|
355
|
-
* Ollama, any OpenAI-compatible). This is the recommended provider for new
|
|
356
|
-
* integrations — it replaces both AnthropicChatProvider and LocalChatProvider.
|
|
357
|
-
*/
|
|
358
|
-
export class AiSdkChatProvider {
|
|
359
|
-
providerName;
|
|
360
|
-
modelId;
|
|
361
|
-
model;
|
|
362
|
-
constructor(model, providerName, modelId) {
|
|
363
|
-
this.model = model;
|
|
364
|
-
this.providerName = providerName;
|
|
365
|
-
this.modelId = modelId;
|
|
366
|
-
}
|
|
367
|
-
async streamChat(messages, tools, systemPrompt, maxTokens, onChunk) {
|
|
368
|
-
const { streamText } = await import("ai");
|
|
369
|
-
const modelMessages = this.toModelMessages(messages);
|
|
370
|
-
const aiTools = this.toAiTools(tools);
|
|
371
|
-
const result = streamText({
|
|
372
|
-
model: this.model,
|
|
373
|
-
system: systemPrompt,
|
|
374
|
-
messages: modelMessages,
|
|
375
|
-
tools: Object.keys(aiTools).length > 0 ? aiTools : undefined,
|
|
376
|
-
maxOutputTokens: maxTokens,
|
|
377
|
-
toolChoice: tools.length > 0 ? "auto" : undefined,
|
|
378
|
-
});
|
|
379
|
-
let text = "";
|
|
380
|
-
const collectedToolCalls = [];
|
|
381
|
-
for await (const part of result.fullStream) {
|
|
382
|
-
if (part.type === "text-delta") {
|
|
383
|
-
const delta = part.text ??
|
|
384
|
-
part.textDelta ??
|
|
385
|
-
"";
|
|
386
|
-
text += delta;
|
|
387
|
-
onChunk({ type: "text_delta", text: delta });
|
|
388
|
-
}
|
|
389
|
-
else if (part.type === "tool-call") {
|
|
390
|
-
const tc = part;
|
|
391
|
-
const toolCallId = tc.toolCallId ?? "";
|
|
392
|
-
const toolName = tc.toolName ?? "";
|
|
393
|
-
const args = tc.args ?? tc.input ?? {};
|
|
394
|
-
onChunk({ type: "tool_use_start", toolCallId, toolName });
|
|
395
|
-
onChunk({
|
|
396
|
-
type: "tool_use_delta",
|
|
397
|
-
toolCallId,
|
|
398
|
-
toolArgs: JSON.stringify(args),
|
|
399
|
-
});
|
|
400
|
-
onChunk({ type: "tool_use_end", toolCallId });
|
|
401
|
-
collectedToolCalls.push({
|
|
402
|
-
id: toolCallId,
|
|
403
|
-
name: toolName,
|
|
404
|
-
args: args,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
let inputTokens = 0;
|
|
409
|
-
let outputTokens = 0;
|
|
410
|
-
try {
|
|
411
|
-
const usage = await result.usage;
|
|
412
|
-
const u = usage;
|
|
413
|
-
inputTokens = u?.promptTokens ?? 0;
|
|
414
|
-
outputTokens = u?.completionTokens ?? 0;
|
|
415
|
-
}
|
|
416
|
-
catch {
|
|
417
|
-
/* token counts unavailable for some providers */
|
|
418
|
-
}
|
|
419
|
-
onChunk({ type: "done" });
|
|
420
|
-
return { text, toolCalls: collectedToolCalls, inputTokens, outputTokens };
|
|
421
|
-
}
|
|
422
|
-
toModelMessages(messages) {
|
|
423
|
-
const result = [];
|
|
424
|
-
for (const msg of messages) {
|
|
425
|
-
if (msg.role === "system")
|
|
426
|
-
continue;
|
|
427
|
-
if (msg.role === "user") {
|
|
428
|
-
result.push({ role: "user", content: msg.content });
|
|
429
|
-
}
|
|
430
|
-
else if (msg.role === "tool") {
|
|
431
|
-
result.push({
|
|
432
|
-
role: "tool",
|
|
433
|
-
content: [
|
|
434
|
-
{
|
|
435
|
-
type: "tool-result",
|
|
436
|
-
toolCallId: msg.tool_call_id ?? "",
|
|
437
|
-
result: msg.content,
|
|
438
|
-
},
|
|
439
|
-
],
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
else if (msg.role === "assistant") {
|
|
443
|
-
if (msg.tool_calls?.length) {
|
|
444
|
-
const parts = [];
|
|
445
|
-
if (msg.content) {
|
|
446
|
-
parts.push({ type: "text", text: msg.content });
|
|
447
|
-
}
|
|
448
|
-
for (const tc of msg.tool_calls) {
|
|
449
|
-
parts.push({
|
|
450
|
-
type: "tool-call",
|
|
451
|
-
toolCallId: tc.id,
|
|
452
|
-
toolName: tc.function.name,
|
|
453
|
-
args: JSON.parse(tc.function.arguments),
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
result.push({ role: "assistant", content: parts });
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
result.push({ role: "assistant", content: msg.content });
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return result;
|
|
464
|
-
}
|
|
465
|
-
toAiTools(tools) {
|
|
466
|
-
const result = {};
|
|
467
|
-
for (const t of tools) {
|
|
468
|
-
result[t.function.name] = {
|
|
469
|
-
description: t.function.description,
|
|
470
|
-
parameters: t.function.parameters,
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
return result;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provider Factory — creates AI SDK provider from settings configuration.
|
|
3
|
-
*
|
|
4
|
-
* Reads the LLM configuration (provider, model, apiKey, baseUrl) and
|
|
5
|
-
* returns a ready-to-use LanguageModel for streamText().
|
|
6
|
-
*
|
|
7
|
-
* Supports two config shapes:
|
|
8
|
-
* 1. Direct ProviderConfig: { provider, model, apiKey, baseUrl }
|
|
9
|
-
* 2. Settings-based: reads from loadSettings().llm
|
|
10
|
-
*/
|
|
11
|
-
import { createLanguageModel, } from "./providers.js";
|
|
12
|
-
const DEFAULT_MODELS = {
|
|
13
|
-
anthropic: "claude-sonnet-4-20250514",
|
|
14
|
-
openai: "gpt-4o",
|
|
15
|
-
google: "gemini-2.0-flash",
|
|
16
|
-
ollama: "llama3",
|
|
17
|
-
"openai-compatible": "default",
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Resolve a ProviderConfig from loose settings.
|
|
21
|
-
* Fills in defaults for missing fields.
|
|
22
|
-
*/
|
|
23
|
-
export function resolveProviderConfig(settings) {
|
|
24
|
-
const provider = (settings.provider ?? "anthropic");
|
|
25
|
-
const model = settings.model ?? DEFAULT_MODELS[provider] ?? "default";
|
|
26
|
-
return {
|
|
27
|
-
provider,
|
|
28
|
-
model,
|
|
29
|
-
apiKey: settings.apiKey,
|
|
30
|
-
baseUrl: settings.baseUrl,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Create a LanguageModel from settings configuration.
|
|
35
|
-
* This is the primary entry point for the rest of the codebase.
|
|
36
|
-
*/
|
|
37
|
-
export async function createModelFromSettings(settings) {
|
|
38
|
-
const config = resolveProviderConfig(settings);
|
|
39
|
-
return createLanguageModel(config);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Create a LanguageModel from environment variables.
|
|
43
|
-
* Falls back to Anthropic with claude-sonnet-4-20250514.
|
|
44
|
-
*/
|
|
45
|
-
export async function createModelFromEnv() {
|
|
46
|
-
const provider = (process.env.UNERR_LLM_PROVIDER ??
|
|
47
|
-
"anthropic");
|
|
48
|
-
const model = process.env.UNERR_MODEL ?? DEFAULT_MODELS[provider] ?? "default";
|
|
49
|
-
return createLanguageModel({
|
|
50
|
-
provider,
|
|
51
|
-
model,
|
|
52
|
-
apiKey: process.env.ANTHROPIC_API_KEY ?? process.env.OPENAI_API_KEY,
|
|
53
|
-
baseUrl: process.env.UNERR_LLM_BASE_URL,
|
|
54
|
-
});
|
|
55
|
-
}
|
package/dist/core/providers.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Provider Registry — unified provider abstraction via Vercel AI SDK.
|
|
3
|
-
*
|
|
4
|
-
* Supports: Anthropic, OpenAI, Google Gemini, Ollama (local), any OpenAI-compatible.
|
|
5
|
-
* Each provider returns an AI SDK LanguageModel that plugs into streamText().
|
|
6
|
-
*
|
|
7
|
-
* Design: the registry is a pure function map — no state, no singletons.
|
|
8
|
-
* Provider creation is lazy (only when called) to avoid importing unused SDKs.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Create an AI SDK LanguageModel from provider configuration.
|
|
12
|
-
* Throws on invalid config or missing API keys (except Ollama).
|
|
13
|
-
*/
|
|
14
|
-
export async function createLanguageModel(config) {
|
|
15
|
-
switch (config.provider) {
|
|
16
|
-
case "anthropic":
|
|
17
|
-
return createAnthropicModel(config);
|
|
18
|
-
case "openai":
|
|
19
|
-
return createOpenAIModel(config);
|
|
20
|
-
case "google":
|
|
21
|
-
return createGoogleModel(config);
|
|
22
|
-
case "ollama":
|
|
23
|
-
return createOllamaModel(config);
|
|
24
|
-
case "openai-compatible":
|
|
25
|
-
return createOpenAICompatibleModel(config);
|
|
26
|
-
default: {
|
|
27
|
-
const _exhaustive = config.provider;
|
|
28
|
-
throw new Error(`Unknown provider: ${config.provider}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function createAnthropicModel(config) {
|
|
33
|
-
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
34
|
-
const anthropic = createAnthropic({
|
|
35
|
-
apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY,
|
|
36
|
-
});
|
|
37
|
-
return anthropic(config.model);
|
|
38
|
-
}
|
|
39
|
-
async function createOpenAIModel(config) {
|
|
40
|
-
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
41
|
-
const openai = createOpenAI({
|
|
42
|
-
apiKey: config.apiKey ?? process.env.OPENAI_API_KEY,
|
|
43
|
-
...(config.baseUrl ? { baseURL: config.baseUrl } : {}),
|
|
44
|
-
});
|
|
45
|
-
return openai(config.model);
|
|
46
|
-
}
|
|
47
|
-
async function createGoogleModel(config) {
|
|
48
|
-
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
49
|
-
const google = createGoogleGenerativeAI({
|
|
50
|
-
apiKey: config.apiKey ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
51
|
-
});
|
|
52
|
-
return google(config.model);
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Ollama uses the OpenAI-compatible endpoint with a local baseURL.
|
|
56
|
-
* No API key required — Ollama runs locally.
|
|
57
|
-
*/
|
|
58
|
-
async function createOllamaModel(config) {
|
|
59
|
-
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
60
|
-
const ollama = createOpenAI({
|
|
61
|
-
baseURL: config.baseUrl ?? "http://localhost:11434/v1",
|
|
62
|
-
apiKey: "ollama",
|
|
63
|
-
});
|
|
64
|
-
return ollama(config.model);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Any OpenAI-compatible API (LM Studio, vLLM, Together, Fireworks, etc.)
|
|
68
|
-
* Requires baseUrl to be set.
|
|
69
|
-
*/
|
|
70
|
-
async function createOpenAICompatibleModel(config) {
|
|
71
|
-
if (!config.baseUrl) {
|
|
72
|
-
throw new Error("openai-compatible provider requires baseUrl to be set");
|
|
73
|
-
}
|
|
74
|
-
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
75
|
-
const compatible = createOpenAI({
|
|
76
|
-
baseURL: config.baseUrl,
|
|
77
|
-
apiKey: config.apiKey ?? "no-key",
|
|
78
|
-
});
|
|
79
|
-
return compatible(config.model);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* List of all supported provider names.
|
|
83
|
-
*/
|
|
84
|
-
export const SUPPORTED_PROVIDERS = [
|
|
85
|
-
"anthropic",
|
|
86
|
-
"openai",
|
|
87
|
-
"google",
|
|
88
|
-
"ollama",
|
|
89
|
-
"openai-compatible",
|
|
90
|
-
];
|