@usejarvis/brain 0.1.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/LICENSE +153 -0
- package/README.md +278 -0
- package/bin/jarvis.ts +413 -0
- package/package.json +74 -0
- package/scripts/ensure-bun.cjs +8 -0
- package/src/actions/README.md +421 -0
- package/src/actions/app-control/desktop-controller.test.ts +26 -0
- package/src/actions/app-control/desktop-controller.ts +438 -0
- package/src/actions/app-control/interface.ts +64 -0
- package/src/actions/app-control/linux.ts +273 -0
- package/src/actions/app-control/macos.ts +54 -0
- package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
- package/src/actions/app-control/sidecar-launcher.ts +286 -0
- package/src/actions/app-control/windows.ts +44 -0
- package/src/actions/browser/cdp.ts +138 -0
- package/src/actions/browser/chrome-launcher.ts +252 -0
- package/src/actions/browser/session.ts +437 -0
- package/src/actions/browser/stealth.ts +49 -0
- package/src/actions/index.ts +20 -0
- package/src/actions/terminal/executor.ts +157 -0
- package/src/actions/terminal/wsl-bridge.ts +126 -0
- package/src/actions/test.ts +93 -0
- package/src/actions/tools/agents.ts +321 -0
- package/src/actions/tools/builtin.ts +846 -0
- package/src/actions/tools/commitments.ts +192 -0
- package/src/actions/tools/content.ts +217 -0
- package/src/actions/tools/delegate.ts +147 -0
- package/src/actions/tools/desktop.test.ts +55 -0
- package/src/actions/tools/desktop.ts +305 -0
- package/src/actions/tools/goals.ts +376 -0
- package/src/actions/tools/local-tools-guard.ts +20 -0
- package/src/actions/tools/registry.ts +171 -0
- package/src/actions/tools/research.ts +111 -0
- package/src/actions/tools/sidecar-list.ts +57 -0
- package/src/actions/tools/sidecar-route.ts +105 -0
- package/src/actions/tools/workflows.ts +216 -0
- package/src/agents/agent.ts +132 -0
- package/src/agents/delegation.ts +107 -0
- package/src/agents/hierarchy.ts +113 -0
- package/src/agents/index.ts +19 -0
- package/src/agents/messaging.ts +125 -0
- package/src/agents/orchestrator.ts +576 -0
- package/src/agents/role-discovery.ts +61 -0
- package/src/agents/sub-agent-runner.ts +307 -0
- package/src/agents/task-manager.ts +151 -0
- package/src/authority/approval-delivery.ts +59 -0
- package/src/authority/approval.ts +196 -0
- package/src/authority/audit.ts +158 -0
- package/src/authority/authority.test.ts +519 -0
- package/src/authority/deferred-executor.ts +103 -0
- package/src/authority/emergency.ts +66 -0
- package/src/authority/engine.ts +297 -0
- package/src/authority/index.ts +12 -0
- package/src/authority/learning.ts +111 -0
- package/src/authority/tool-action-map.ts +74 -0
- package/src/awareness/analytics.ts +466 -0
- package/src/awareness/awareness.test.ts +332 -0
- package/src/awareness/capture-engine.ts +305 -0
- package/src/awareness/context-graph.ts +130 -0
- package/src/awareness/context-tracker.ts +349 -0
- package/src/awareness/index.ts +25 -0
- package/src/awareness/intelligence.ts +321 -0
- package/src/awareness/ocr-engine.ts +88 -0
- package/src/awareness/service.ts +528 -0
- package/src/awareness/struggle-detector.ts +342 -0
- package/src/awareness/suggestion-engine.ts +476 -0
- package/src/awareness/types.ts +201 -0
- package/src/cli/autostart.ts +241 -0
- package/src/cli/deps.ts +449 -0
- package/src/cli/doctor.ts +230 -0
- package/src/cli/helpers.ts +401 -0
- package/src/cli/onboard.ts +580 -0
- package/src/comms/README.md +329 -0
- package/src/comms/auth-error.html +48 -0
- package/src/comms/channels/discord.ts +228 -0
- package/src/comms/channels/signal.ts +56 -0
- package/src/comms/channels/telegram.ts +316 -0
- package/src/comms/channels/whatsapp.ts +60 -0
- package/src/comms/channels.test.ts +173 -0
- package/src/comms/desktop-notify.ts +114 -0
- package/src/comms/example.ts +129 -0
- package/src/comms/index.ts +129 -0
- package/src/comms/streaming.ts +142 -0
- package/src/comms/voice.test.ts +152 -0
- package/src/comms/voice.ts +291 -0
- package/src/comms/websocket.test.ts +409 -0
- package/src/comms/websocket.ts +473 -0
- package/src/config/README.md +387 -0
- package/src/config/index.ts +6 -0
- package/src/config/loader.test.ts +137 -0
- package/src/config/loader.ts +142 -0
- package/src/config/types.ts +260 -0
- package/src/daemon/README.md +232 -0
- package/src/daemon/agent-service-interface.ts +9 -0
- package/src/daemon/agent-service.ts +600 -0
- package/src/daemon/api-routes.ts +2119 -0
- package/src/daemon/background-agent-service.ts +396 -0
- package/src/daemon/background-agent.test.ts +78 -0
- package/src/daemon/channel-service.ts +201 -0
- package/src/daemon/commitment-executor.ts +297 -0
- package/src/daemon/event-classifier.ts +239 -0
- package/src/daemon/event-coalescer.ts +123 -0
- package/src/daemon/event-reactor.ts +214 -0
- package/src/daemon/health.ts +220 -0
- package/src/daemon/index.ts +1004 -0
- package/src/daemon/llm-settings.ts +316 -0
- package/src/daemon/observer-service.ts +150 -0
- package/src/daemon/pid.ts +98 -0
- package/src/daemon/research-queue.ts +155 -0
- package/src/daemon/services.ts +175 -0
- package/src/daemon/ws-service.ts +788 -0
- package/src/goals/accountability.ts +240 -0
- package/src/goals/awareness-bridge.ts +185 -0
- package/src/goals/estimator.ts +185 -0
- package/src/goals/events.ts +28 -0
- package/src/goals/goals.test.ts +400 -0
- package/src/goals/integration.test.ts +329 -0
- package/src/goals/nl-builder.test.ts +220 -0
- package/src/goals/nl-builder.ts +256 -0
- package/src/goals/rhythm.test.ts +177 -0
- package/src/goals/rhythm.ts +275 -0
- package/src/goals/service.test.ts +135 -0
- package/src/goals/service.ts +348 -0
- package/src/goals/types.ts +106 -0
- package/src/goals/workflow-bridge.ts +96 -0
- package/src/integrations/google-api.ts +134 -0
- package/src/integrations/google-auth.ts +175 -0
- package/src/llm/README.md +291 -0
- package/src/llm/anthropic.ts +386 -0
- package/src/llm/gemini.ts +371 -0
- package/src/llm/index.ts +19 -0
- package/src/llm/manager.ts +153 -0
- package/src/llm/ollama.ts +307 -0
- package/src/llm/openai.ts +350 -0
- package/src/llm/provider.test.ts +231 -0
- package/src/llm/provider.ts +60 -0
- package/src/llm/test.ts +87 -0
- package/src/observers/README.md +278 -0
- package/src/observers/calendar.ts +113 -0
- package/src/observers/clipboard.ts +136 -0
- package/src/observers/email.ts +109 -0
- package/src/observers/example.ts +58 -0
- package/src/observers/file-watcher.ts +124 -0
- package/src/observers/index.ts +159 -0
- package/src/observers/notifications.ts +197 -0
- package/src/observers/observers.test.ts +203 -0
- package/src/observers/processes.ts +225 -0
- package/src/personality/README.md +61 -0
- package/src/personality/adapter.ts +196 -0
- package/src/personality/index.ts +20 -0
- package/src/personality/learner.ts +209 -0
- package/src/personality/model.ts +132 -0
- package/src/personality/personality.test.ts +236 -0
- package/src/roles/README.md +252 -0
- package/src/roles/authority.ts +119 -0
- package/src/roles/example-usage.ts +198 -0
- package/src/roles/index.ts +42 -0
- package/src/roles/loader.ts +143 -0
- package/src/roles/prompt-builder.ts +194 -0
- package/src/roles/test-multi.ts +102 -0
- package/src/roles/test-role.yaml +77 -0
- package/src/roles/test-utils.ts +93 -0
- package/src/roles/test.ts +106 -0
- package/src/roles/tool-guide.ts +190 -0
- package/src/roles/types.ts +36 -0
- package/src/roles/utils.ts +200 -0
- package/src/scripts/google-setup.ts +168 -0
- package/src/sidecar/connection.ts +179 -0
- package/src/sidecar/index.ts +6 -0
- package/src/sidecar/manager.ts +542 -0
- package/src/sidecar/protocol.ts +85 -0
- package/src/sidecar/rpc.ts +161 -0
- package/src/sidecar/scheduler.ts +136 -0
- package/src/sidecar/types.ts +112 -0
- package/src/sidecar/validator.ts +144 -0
- package/src/vault/README.md +110 -0
- package/src/vault/awareness.ts +341 -0
- package/src/vault/commitments.ts +299 -0
- package/src/vault/content-pipeline.ts +260 -0
- package/src/vault/conversations.ts +173 -0
- package/src/vault/entities.ts +180 -0
- package/src/vault/extractor.test.ts +356 -0
- package/src/vault/extractor.ts +345 -0
- package/src/vault/facts.ts +190 -0
- package/src/vault/goals.ts +477 -0
- package/src/vault/index.ts +87 -0
- package/src/vault/keychain.ts +99 -0
- package/src/vault/observations.ts +115 -0
- package/src/vault/relationships.ts +178 -0
- package/src/vault/retrieval.test.ts +126 -0
- package/src/vault/retrieval.ts +227 -0
- package/src/vault/schema.ts +658 -0
- package/src/vault/settings.ts +38 -0
- package/src/vault/vectors.ts +92 -0
- package/src/vault/workflows.ts +403 -0
- package/src/workflows/auto-suggest.ts +290 -0
- package/src/workflows/engine.ts +366 -0
- package/src/workflows/events.ts +24 -0
- package/src/workflows/executor.ts +207 -0
- package/src/workflows/nl-builder.ts +198 -0
- package/src/workflows/nodes/actions/agent-task.ts +73 -0
- package/src/workflows/nodes/actions/calendar-action.ts +85 -0
- package/src/workflows/nodes/actions/code-execution.ts +73 -0
- package/src/workflows/nodes/actions/discord.ts +77 -0
- package/src/workflows/nodes/actions/file-write.ts +73 -0
- package/src/workflows/nodes/actions/gmail.ts +69 -0
- package/src/workflows/nodes/actions/http-request.ts +117 -0
- package/src/workflows/nodes/actions/notification.ts +85 -0
- package/src/workflows/nodes/actions/run-tool.ts +55 -0
- package/src/workflows/nodes/actions/send-message.ts +82 -0
- package/src/workflows/nodes/actions/shell-command.ts +76 -0
- package/src/workflows/nodes/actions/telegram.ts +60 -0
- package/src/workflows/nodes/builtin.ts +119 -0
- package/src/workflows/nodes/error/error-handler.ts +37 -0
- package/src/workflows/nodes/error/fallback.ts +47 -0
- package/src/workflows/nodes/error/retry.ts +82 -0
- package/src/workflows/nodes/logic/delay.ts +42 -0
- package/src/workflows/nodes/logic/if-else.ts +41 -0
- package/src/workflows/nodes/logic/loop.ts +90 -0
- package/src/workflows/nodes/logic/merge.ts +38 -0
- package/src/workflows/nodes/logic/race.ts +40 -0
- package/src/workflows/nodes/logic/switch.ts +59 -0
- package/src/workflows/nodes/logic/template-render.ts +53 -0
- package/src/workflows/nodes/logic/variable-get.ts +37 -0
- package/src/workflows/nodes/logic/variable-set.ts +59 -0
- package/src/workflows/nodes/registry.ts +99 -0
- package/src/workflows/nodes/transform/aggregate.ts +99 -0
- package/src/workflows/nodes/transform/csv-parse.ts +70 -0
- package/src/workflows/nodes/transform/json-parse.ts +63 -0
- package/src/workflows/nodes/transform/map-filter.ts +84 -0
- package/src/workflows/nodes/transform/regex-match.ts +89 -0
- package/src/workflows/nodes/triggers/calendar.ts +33 -0
- package/src/workflows/nodes/triggers/clipboard.ts +32 -0
- package/src/workflows/nodes/triggers/cron.ts +40 -0
- package/src/workflows/nodes/triggers/email.ts +40 -0
- package/src/workflows/nodes/triggers/file-change.ts +45 -0
- package/src/workflows/nodes/triggers/git.ts +46 -0
- package/src/workflows/nodes/triggers/manual.ts +23 -0
- package/src/workflows/nodes/triggers/poll.ts +81 -0
- package/src/workflows/nodes/triggers/process.ts +44 -0
- package/src/workflows/nodes/triggers/screen-event.ts +37 -0
- package/src/workflows/nodes/triggers/webhook.ts +39 -0
- package/src/workflows/safe-eval.ts +139 -0
- package/src/workflows/template.ts +118 -0
- package/src/workflows/triggers/cron.ts +311 -0
- package/src/workflows/triggers/manager.ts +285 -0
- package/src/workflows/triggers/observer-bridge.ts +172 -0
- package/src/workflows/triggers/poller.ts +201 -0
- package/src/workflows/triggers/screen-condition.ts +218 -0
- package/src/workflows/triggers/triggers.test.ts +740 -0
- package/src/workflows/triggers/webhook.ts +191 -0
- package/src/workflows/types.ts +133 -0
- package/src/workflows/variables.ts +72 -0
- package/src/workflows/workflows.test.ts +383 -0
- package/src/workflows/yaml.ts +104 -0
- package/ui/dist/index-j75njzc1.css +1199 -0
- package/ui/dist/index-p2zh407q.js +80603 -0
- package/ui/dist/index.html +13 -0
- package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
- package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
- package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
- package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
- package/ui/public/ort/ort-wasm-simd-threaded.wasm +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google OAuth2 Authentication
|
|
3
|
+
*
|
|
4
|
+
* Manages OAuth2 tokens for Google APIs (Gmail, Calendar).
|
|
5
|
+
* Uses raw fetch() — no googleapis package needed.
|
|
6
|
+
* Tokens stored at ~/.jarvis/google-tokens.json
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token';
|
|
14
|
+
const AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
15
|
+
|
|
16
|
+
export type GoogleTokens = {
|
|
17
|
+
access_token: string;
|
|
18
|
+
refresh_token: string;
|
|
19
|
+
expiry_date: number;
|
|
20
|
+
token_type: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class GoogleAuth {
|
|
24
|
+
private clientId: string;
|
|
25
|
+
private clientSecret: string;
|
|
26
|
+
private tokens: GoogleTokens | null = null;
|
|
27
|
+
private tokensPath: string;
|
|
28
|
+
private redirectUri: string;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
clientId: string,
|
|
32
|
+
clientSecret: string,
|
|
33
|
+
opts?: { tokensPath?: string; redirectUri?: string }
|
|
34
|
+
) {
|
|
35
|
+
this.clientId = clientId;
|
|
36
|
+
this.clientSecret = clientSecret;
|
|
37
|
+
this.tokensPath = opts?.tokensPath ?? path.join(os.homedir(), '.jarvis', 'google-tokens.json');
|
|
38
|
+
this.redirectUri = opts?.redirectUri ?? 'http://localhost:3142/api/auth/google/callback';
|
|
39
|
+
this.loadTokens();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load saved tokens from disk.
|
|
44
|
+
*/
|
|
45
|
+
loadTokens(): GoogleTokens | null {
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(this.tokensPath)) return null;
|
|
48
|
+
const text = readFileSync(this.tokensPath, 'utf-8');
|
|
49
|
+
const data = JSON.parse(text);
|
|
50
|
+
if (data.access_token && data.refresh_token) {
|
|
51
|
+
this.tokens = data as GoogleTokens;
|
|
52
|
+
return this.tokens;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// No tokens file or invalid
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save tokens to disk.
|
|
62
|
+
*/
|
|
63
|
+
async saveTokens(tokens: GoogleTokens): Promise<void> {
|
|
64
|
+
this.tokens = tokens;
|
|
65
|
+
await Bun.write(this.tokensPath, JSON.stringify(tokens, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if we have valid tokens.
|
|
70
|
+
*/
|
|
71
|
+
isAuthenticated(): boolean {
|
|
72
|
+
return this.tokens !== null && !!this.tokens.refresh_token;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get a valid access token. Auto-refreshes if expired.
|
|
77
|
+
*/
|
|
78
|
+
async getAccessToken(): Promise<string> {
|
|
79
|
+
if (!this.tokens) {
|
|
80
|
+
throw new Error('Not authenticated. Run: bun run src/scripts/google-setup.ts');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if token is expired (with 5 min buffer)
|
|
84
|
+
if (this.tokens.expiry_date && Date.now() > this.tokens.expiry_date - 5 * 60_000) {
|
|
85
|
+
await this.refreshAccessToken();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return this.tokens.access_token;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generate OAuth2 consent URL.
|
|
93
|
+
*/
|
|
94
|
+
getAuthUrl(scopes: string[]): string {
|
|
95
|
+
const params = new URLSearchParams({
|
|
96
|
+
client_id: this.clientId,
|
|
97
|
+
redirect_uri: this.redirectUri,
|
|
98
|
+
response_type: 'code',
|
|
99
|
+
scope: scopes.join(' '),
|
|
100
|
+
access_type: 'offline',
|
|
101
|
+
prompt: 'consent',
|
|
102
|
+
});
|
|
103
|
+
return `${AUTH_ENDPOINT}?${params.toString()}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Exchange authorization code for tokens.
|
|
108
|
+
*/
|
|
109
|
+
async exchangeCode(code: string): Promise<GoogleTokens> {
|
|
110
|
+
const resp = await fetch(TOKEN_ENDPOINT, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
113
|
+
body: new URLSearchParams({
|
|
114
|
+
code,
|
|
115
|
+
client_id: this.clientId,
|
|
116
|
+
client_secret: this.clientSecret,
|
|
117
|
+
redirect_uri: this.redirectUri,
|
|
118
|
+
grant_type: 'authorization_code',
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!resp.ok) {
|
|
123
|
+
const err = await resp.text();
|
|
124
|
+
throw new Error(`Token exchange failed: ${err}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const data = await resp.json() as any;
|
|
128
|
+
|
|
129
|
+
const tokens: GoogleTokens = {
|
|
130
|
+
access_token: data.access_token,
|
|
131
|
+
refresh_token: data.refresh_token,
|
|
132
|
+
expiry_date: Date.now() + (data.expires_in ?? 3600) * 1000,
|
|
133
|
+
token_type: data.token_type ?? 'Bearer',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
await this.saveTokens(tokens);
|
|
137
|
+
return tokens;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Refresh the access token using the refresh token.
|
|
142
|
+
*/
|
|
143
|
+
private async refreshAccessToken(): Promise<void> {
|
|
144
|
+
if (!this.tokens?.refresh_token) {
|
|
145
|
+
throw new Error('No refresh token available');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const resp = await fetch(TOKEN_ENDPOINT, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
151
|
+
body: new URLSearchParams({
|
|
152
|
+
refresh_token: this.tokens.refresh_token,
|
|
153
|
+
client_id: this.clientId,
|
|
154
|
+
client_secret: this.clientSecret,
|
|
155
|
+
grant_type: 'refresh_token',
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (!resp.ok) {
|
|
160
|
+
const err = await resp.text();
|
|
161
|
+
throw new Error(`Token refresh failed: ${err}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const data = await resp.json() as any;
|
|
165
|
+
|
|
166
|
+
this.tokens = {
|
|
167
|
+
...this.tokens,
|
|
168
|
+
access_token: data.access_token,
|
|
169
|
+
expiry_date: Date.now() + (data.expires_in ?? 3600) * 1000,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
await this.saveTokens(this.tokens);
|
|
173
|
+
console.log('[GoogleAuth] Token refreshed successfully');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# J.A.R.V.I.S. LLM Provider System
|
|
2
|
+
|
|
3
|
+
A unified abstraction layer for multiple LLM providers with automatic fallback support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Unified Interface**: Single API for Anthropic Claude, OpenAI GPT, and Ollama (local models)
|
|
8
|
+
- **Automatic Fallback**: Seamlessly switches to backup providers if primary fails
|
|
9
|
+
- **Streaming Support**: Real-time response streaming for all providers
|
|
10
|
+
- **Tool Calling**: Unified tool/function calling across providers
|
|
11
|
+
- **Zero Dependencies**: Uses native `fetch` API (no SDKs required)
|
|
12
|
+
- **Type-Safe**: Full TypeScript types for all operations
|
|
13
|
+
|
|
14
|
+
## Supported Providers
|
|
15
|
+
|
|
16
|
+
### 1. Anthropic Claude
|
|
17
|
+
- **Models**: Claude Opus 4.6, Sonnet 4.5, Claude 3.5, Claude 3
|
|
18
|
+
- **Default**: `claude-sonnet-4-5-20250929`
|
|
19
|
+
- **Features**: Text generation, streaming, tool use
|
|
20
|
+
- **API**: https://api.anthropic.com/v1/messages
|
|
21
|
+
|
|
22
|
+
### 2. OpenAI GPT
|
|
23
|
+
- **Models**: GPT-4o, GPT-4 Turbo, GPT-3.5 Turbo
|
|
24
|
+
- **Default**: `gpt-4o`
|
|
25
|
+
- **Features**: Text generation, streaming, function calling
|
|
26
|
+
- **API**: https://api.openai.com/v1/chat/completions
|
|
27
|
+
|
|
28
|
+
### 3. Ollama (Local)
|
|
29
|
+
- **Models**: Llama 3, Llama 2, Mistral, Mixtral, CodeLlama, etc.
|
|
30
|
+
- **Default**: `llama3`
|
|
31
|
+
- **Features**: Local inference, streaming, tool use
|
|
32
|
+
- **API**: http://localhost:11434/api/chat
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Basic Setup
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { LLMManager, AnthropicProvider, OpenAIProvider, OllamaProvider } from './llm/index.ts';
|
|
40
|
+
|
|
41
|
+
const manager = new LLMManager();
|
|
42
|
+
|
|
43
|
+
// Register providers
|
|
44
|
+
const anthropic = new AnthropicProvider('sk-ant-...', 'claude-sonnet-4-5-20250929');
|
|
45
|
+
manager.registerProvider(anthropic);
|
|
46
|
+
|
|
47
|
+
const openai = new OpenAIProvider('sk-...', 'gpt-4o');
|
|
48
|
+
manager.registerProvider(openai);
|
|
49
|
+
|
|
50
|
+
const ollama = new OllamaProvider('http://localhost:11434', 'llama3');
|
|
51
|
+
manager.registerProvider(ollama);
|
|
52
|
+
|
|
53
|
+
// Set primary and fallbacks
|
|
54
|
+
manager.setPrimary('anthropic');
|
|
55
|
+
manager.setFallbackChain(['openai', 'ollama']);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Simple Chat
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const messages = [
|
|
62
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
63
|
+
{ role: 'user', content: 'What is TypeScript?' },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const response = await manager.chat(messages);
|
|
67
|
+
console.log(response.content);
|
|
68
|
+
console.log('Tokens:', response.usage);
|
|
69
|
+
console.log('Model:', response.model);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Streaming Responses
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
for await (const event of manager.stream(messages)) {
|
|
76
|
+
if (event.type === 'text') {
|
|
77
|
+
process.stdout.write(event.text);
|
|
78
|
+
} else if (event.type === 'done') {
|
|
79
|
+
console.log('\nCompleted!');
|
|
80
|
+
console.log('Total tokens:', event.response.usage);
|
|
81
|
+
} else if (event.type === 'error') {
|
|
82
|
+
console.error('Error:', event.error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Tool Calling
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const tools = [
|
|
91
|
+
{
|
|
92
|
+
name: 'get_weather',
|
|
93
|
+
description: 'Get the current weather in a location',
|
|
94
|
+
parameters: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
location: { type: 'string', description: 'City name' },
|
|
98
|
+
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
|
|
99
|
+
},
|
|
100
|
+
required: ['location'],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const messages = [
|
|
106
|
+
{ role: 'user', content: 'What is the weather in Paris?' },
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const response = await manager.chat(messages, { tools });
|
|
110
|
+
|
|
111
|
+
if (response.tool_calls.length > 0) {
|
|
112
|
+
for (const call of response.tool_calls) {
|
|
113
|
+
console.log('Tool:', call.name);
|
|
114
|
+
console.log('Arguments:', call.arguments);
|
|
115
|
+
// Execute tool and continue conversation...
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Advanced Options
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const response = await manager.chat(messages, {
|
|
124
|
+
model: 'claude-opus-4-6', // Override default model
|
|
125
|
+
temperature: 0.7, // Control randomness (0-1)
|
|
126
|
+
max_tokens: 2000, // Limit response length
|
|
127
|
+
tools: [...], // Tool definitions
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
Load from `~/.jarvis/config.yaml`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { loadConfig } from './config/index.ts';
|
|
137
|
+
|
|
138
|
+
const config = await loadConfig();
|
|
139
|
+
|
|
140
|
+
// Auto-configure providers from config
|
|
141
|
+
if (config.llm.anthropic?.api_key) {
|
|
142
|
+
const anthropic = new AnthropicProvider(
|
|
143
|
+
config.llm.anthropic.api_key,
|
|
144
|
+
config.llm.anthropic.model
|
|
145
|
+
);
|
|
146
|
+
manager.registerProvider(anthropic);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
manager.setPrimary(config.llm.primary);
|
|
150
|
+
manager.setFallbackChain(config.llm.fallback);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Provider-Specific Usage
|
|
154
|
+
|
|
155
|
+
### Direct Provider Access
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const provider = manager.getProvider('anthropic');
|
|
159
|
+
if (provider) {
|
|
160
|
+
const response = await provider.chat(messages);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### List Available Models
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const anthropic = new AnthropicProvider('sk-ant-...');
|
|
168
|
+
const models = await anthropic.listModels();
|
|
169
|
+
console.log('Available models:', models);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Response Types
|
|
173
|
+
|
|
174
|
+
### LLMResponse
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
{
|
|
178
|
+
content: string; // Generated text
|
|
179
|
+
tool_calls: LLMToolCall[]; // Tool calls made (if any)
|
|
180
|
+
usage: {
|
|
181
|
+
input_tokens: number; // Prompt tokens
|
|
182
|
+
output_tokens: number; // Completion tokens
|
|
183
|
+
};
|
|
184
|
+
model: string; // Model used
|
|
185
|
+
finish_reason: 'stop' | 'tool_use' | 'length' | 'error';
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### LLMStreamEvent
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Text delta
|
|
193
|
+
{ type: 'text'; text: string }
|
|
194
|
+
|
|
195
|
+
// Tool call completed
|
|
196
|
+
{ type: 'tool_call'; tool_call: LLMToolCall }
|
|
197
|
+
|
|
198
|
+
// Stream finished
|
|
199
|
+
{ type: 'done'; response: LLMResponse }
|
|
200
|
+
|
|
201
|
+
// Error occurred
|
|
202
|
+
{ type: 'error'; error: string }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Error Handling
|
|
206
|
+
|
|
207
|
+
The manager automatically tries fallback providers on failure:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
try {
|
|
211
|
+
const response = await manager.chat(messages);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
// All providers failed
|
|
214
|
+
console.error('All LLM providers failed:', err.message);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Individual provider errors are logged but don't throw unless all providers fail.
|
|
219
|
+
|
|
220
|
+
## Testing
|
|
221
|
+
|
|
222
|
+
Run the test file:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
bun run src/llm/test.ts
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Make sure you have a valid config at `~/.jarvis/config.yaml` with API keys set.
|
|
229
|
+
|
|
230
|
+
## Implementation Details
|
|
231
|
+
|
|
232
|
+
### Message Conversion
|
|
233
|
+
|
|
234
|
+
Each provider has different message formats:
|
|
235
|
+
- **Anthropic**: Separates system message, converts roles
|
|
236
|
+
- **OpenAI**: Standard chat format
|
|
237
|
+
- **Ollama**: Chat API format
|
|
238
|
+
|
|
239
|
+
The abstraction layer handles all conversions automatically.
|
|
240
|
+
|
|
241
|
+
### Tool Calling Formats
|
|
242
|
+
|
|
243
|
+
- **Anthropic**: `tools` array with `input_schema`
|
|
244
|
+
- **OpenAI**: `tools` array with `function` wrapper
|
|
245
|
+
- **Ollama**: OpenAI-compatible function calling
|
|
246
|
+
|
|
247
|
+
All converted to unified `LLMToolCall` format in responses.
|
|
248
|
+
|
|
249
|
+
### Streaming Implementation
|
|
250
|
+
|
|
251
|
+
- **Anthropic**: Server-Sent Events (SSE) with `data: {...}` format
|
|
252
|
+
- **OpenAI**: SSE with delta chunks
|
|
253
|
+
- **Ollama**: Newline-delimited JSON streaming
|
|
254
|
+
|
|
255
|
+
All providers return the same `LLMStreamEvent` types.
|
|
256
|
+
|
|
257
|
+
## Architecture
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
LLMManager
|
|
261
|
+
├── AnthropicProvider (implements LLMProvider)
|
|
262
|
+
├── OpenAIProvider (implements LLMProvider)
|
|
263
|
+
└── OllamaProvider (implements LLMProvider)
|
|
264
|
+
|
|
265
|
+
Each provider:
|
|
266
|
+
- Implements chat() for single responses
|
|
267
|
+
- Implements stream() for streaming responses
|
|
268
|
+
- Implements listModels() for available models
|
|
269
|
+
- Handles provider-specific API formats
|
|
270
|
+
- Converts to/from unified types
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Best Practices
|
|
274
|
+
|
|
275
|
+
1. **Always use LLMManager**: Don't instantiate providers directly unless needed
|
|
276
|
+
2. **Set up fallbacks**: Configure multiple providers for reliability
|
|
277
|
+
3. **Handle stream errors**: Check for `error` events in stream loops
|
|
278
|
+
4. **Use config system**: Load API keys from config, never hardcode
|
|
279
|
+
5. **Monitor usage**: Track token usage from responses for cost control
|
|
280
|
+
6. **Test locally first**: Use Ollama for development before hitting paid APIs
|
|
281
|
+
|
|
282
|
+
## Future Enhancements
|
|
283
|
+
|
|
284
|
+
- [ ] Response caching
|
|
285
|
+
- [ ] Rate limiting
|
|
286
|
+
- [ ] Usage tracking/analytics
|
|
287
|
+
- [ ] Model capability detection
|
|
288
|
+
- [ ] Automatic model selection based on task
|
|
289
|
+
- [ ] Retry with exponential backoff
|
|
290
|
+
- [ ] Cost estimation per request
|
|
291
|
+
- [ ] Multi-turn conversation helpers
|