@adia-ai/llm 0.6.0 → 0.6.2

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 CHANGED
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  _No pending changes._
9
9
 
10
+ ## [0.6.2] - 2026-05-18
11
+
12
+ ### v0.6.2 — Lockstep ride-along
13
+
14
+ TS Phase 4: retrieval (§356) + mcp (§357) `.ts` sources + TS-MIG-001 closed.
15
+ No source changes in this package. Internal dep ranges stay at `^0.6.0`.
16
+
17
+
18
+
19
+ ## [0.6.1] - 2026-05-18
20
+
21
+ ### v0.6.1 §353 — TypeScript-first Phase 2: `@adia-ai/llm` source → `.ts`
22
+
23
+ Converts all npm-published source files to TypeScript in `packages/llm/src/`. Emits
24
+ `.js + .d.ts` via `tsconfig.build.json`. Replaces Phase 1 hotfix stubs with generated
25
+ declarations. Adds `dist-check.test.js` (25 tests) + `scripts/dev/llm-smoke.mjs`.
26
+ Published import paths unchanged. No runtime behaviour change.
27
+
28
+ **No BREAKING changes.** Internal dep ranges stay at `^0.6.0`.
29
+
30
+
31
+
10
32
  ## [0.6.0] - 2026-05-18
11
33
 
12
34
  _Lockstep ride-along (no source change in this package; companion to web-components v0.6.0 — see root CHANGELOG)._
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Anthropic Messages API adapter.
3
+ * Endpoint: https://api.anthropic.com/v1/messages
4
+ */
5
+ export interface AdapterRequest {
6
+ url: string;
7
+ headers: Record<string, string>;
8
+ body: Record<string, unknown>;
9
+ }
10
+ export interface AdapterUsage {
11
+ input: number;
12
+ output: number;
13
+ cacheCreation?: number;
14
+ cacheRead?: number;
15
+ }
16
+ export interface AdapterResponse {
17
+ text: string;
18
+ usage: AdapterUsage;
19
+ stopReason: string;
20
+ }
21
+ export type StreamChunk = {
22
+ type: 'text';
23
+ text: string;
24
+ snapshot: string;
25
+ } | {
26
+ type: 'thinking';
27
+ text: string;
28
+ } | {
29
+ type: 'done';
30
+ text: string;
31
+ usage: AdapterUsage;
32
+ stopReason: string;
33
+ } | {
34
+ type: 'error';
35
+ error: Error;
36
+ };
37
+ export interface BuildRequestOpts {
38
+ model: string;
39
+ messages: Array<{
40
+ role: string;
41
+ content: string;
42
+ }>;
43
+ apiKey: string;
44
+ system?: string | Array<{
45
+ type: string;
46
+ text: string;
47
+ cache_control?: {
48
+ type: string;
49
+ };
50
+ }>;
51
+ maxTokens?: number;
52
+ temperature?: number;
53
+ stream?: boolean;
54
+ cache?: boolean;
55
+ thinking?: boolean;
56
+ thinkingBudget?: number;
57
+ signal?: AbortSignal | null;
58
+ proxyUrl?: string;
59
+ provider?: string;
60
+ }
61
+ interface AnthropicUsage {
62
+ input_tokens?: number;
63
+ output_tokens?: number;
64
+ cache_creation_input_tokens?: number;
65
+ cache_read_input_tokens?: number;
66
+ }
67
+ interface AnthropicResponseBody {
68
+ content?: Array<{
69
+ type: string;
70
+ text?: string;
71
+ }>;
72
+ usage?: AnthropicUsage;
73
+ stop_reason?: string;
74
+ }
75
+ export declare const anthropic: {
76
+ name: "anthropic";
77
+ buildRequest(opts: BuildRequestOpts): AdapterRequest;
78
+ parseResponse(data: AnthropicResponseBody): AdapterResponse;
79
+ parseStream(response: Response): AsyncGenerator<StreamChunk>;
80
+ };
81
+ export {};
82
+ //# sourceMappingURL=anthropic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID,UAAU,cAAc;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,UAAU,qBAAqB;IAC7B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD,eAAO,MAAM,SAAS;;uBAGD,gBAAgB,GAAG,cAAc;wBA4BhC,qBAAqB,GAAG,eAAe;0BAc9B,QAAQ,GAAG,cAAc,CAAC,WAAW,CAAC;CA2CpE,CAAC"}
@@ -2,105 +2,103 @@
2
2
  * Anthropic Messages API adapter.
3
3
  * Endpoint: https://api.anthropic.com/v1/messages
4
4
  */
5
-
6
5
  import { readSSE } from './sse.js';
7
-
8
6
  const API_URL = 'https://api.anthropic.com/v1/messages';
9
7
  const API_VERSION = '2023-06-01';
10
- const DEFAULT_MAX_TOKENS = 4096;
11
-
8
+ const DEFAULT_MAX_TOKENS = 32768;
9
+ // ── Adapter ───────────────────────────────────────────────────────────────
12
10
  export const anthropic = {
13
- name: 'anthropic',
14
-
15
- buildRequest(opts) {
16
- const body = {
17
- model: opts.model,
18
- max_tokens: opts.maxTokens || DEFAULT_MAX_TOKENS,
19
- messages: opts.messages,
20
- stream: !!opts.stream,
21
- };
22
- if (opts.system) {
23
- // Prompt caching: the AdiaUI system prompt is ~23KB and constant across
24
- // a session. Emitting it as a cached block marks it as a cache breakpoint
25
- // (ephemeral, ~5 min TTL). First call = cache write (+25% cost), every
26
- // subsequent call in the window = cache read (−90% cost). No-op below
27
- // the model's minimum cacheable size (1024 tok Sonnet/Opus, 2048 Haiku).
28
- body.system = opts.cache
29
- ? [{ type: 'text', text: opts.system, cache_control: { type: 'ephemeral' } }]
30
- : opts.system;
31
- }
32
- if (opts.temperature != null) body.temperature = opts.temperature;
33
- if (opts.thinking) {
34
- body.thinking = { type: 'enabled', budget_tokens: opts.thinkingBudget || 10000 };
35
- }
36
-
37
- return {
38
- url: API_URL,
39
- headers: {
40
- 'content-type': 'application/json',
41
- 'x-api-key': opts.apiKey,
42
- 'anthropic-version': API_VERSION,
43
- },
44
- body,
45
- };
46
- },
47
-
48
- parseResponse(data) {
49
- const text = data.content?.find(b => b.type === 'text')?.text ?? '';
50
- return {
51
- text,
52
- usage: {
53
- input: data.usage?.input_tokens ?? 0,
54
- output: data.usage?.output_tokens ?? 0,
55
- // Cache telemetry: non-zero cacheRead on turn 2+ is the signal that
56
- // caching is actually kicking in. Recorded per-turn for hit-rate analysis.
57
- cacheCreation: data.usage?.cache_creation_input_tokens ?? 0,
58
- cacheRead: data.usage?.cache_read_input_tokens ?? 0,
59
- },
60
- stopReason: data.stop_reason ?? 'end',
61
- };
62
- },
63
-
64
- async *parseStream(response) {
65
- let snapshot = '';
66
- let usage = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
67
- let stopReason = 'end';
68
-
69
- for await (const event of readSSE(response.body)) {
70
- if (event.done) break;
71
- let data;
72
- try { data = JSON.parse(event.data); } catch { continue; }
73
- const eventType = event.event ?? data.type;
74
-
75
- switch (eventType) {
76
- case 'message_start':
77
- if (data.message?.usage) {
78
- usage.input = data.message.usage.input_tokens ?? 0;
79
- usage.cacheCreation = data.message.usage.cache_creation_input_tokens ?? 0;
80
- usage.cacheRead = data.message.usage.cache_read_input_tokens ?? 0;
81
- }
82
- break;
83
- case 'content_block_delta': {
84
- const delta = data.delta;
85
- if (delta?.type === 'text_delta') {
86
- snapshot += delta.text;
87
- yield { type: 'text', text: delta.text, snapshot };
88
- } else if (delta?.type === 'thinking_delta') {
89
- yield { type: 'thinking', text: delta.thinking };
90
- }
91
- break;
11
+ name: 'anthropic',
12
+ buildRequest(opts) {
13
+ const body = {
14
+ model: opts.model,
15
+ max_tokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
16
+ messages: opts.messages,
17
+ stream: !!opts.stream,
18
+ };
19
+ if (opts.system != null) {
20
+ body['system'] = opts.cache
21
+ ? [{ type: 'text', text: opts.system, cache_control: { type: 'ephemeral' } }]
22
+ : opts.system;
92
23
  }
93
- case 'message_delta':
94
- if (data.delta?.stop_reason) stopReason = data.delta.stop_reason;
95
- if (data.usage) usage.output = data.usage.output_tokens ?? 0;
96
- break;
97
- case 'message_stop':
98
- yield { type: 'done', text: snapshot, usage, stopReason };
99
- break;
100
- case 'error':
101
- yield { type: 'error', error: new Error(data.error?.message ?? 'Stream error') };
102
- break;
103
- }
104
- }
105
- },
24
+ if (opts.temperature != null)
25
+ body['temperature'] = opts.temperature;
26
+ if (opts.thinking) {
27
+ body['thinking'] = { type: 'enabled', budget_tokens: opts.thinkingBudget ?? 10000 };
28
+ }
29
+ return {
30
+ url: API_URL,
31
+ headers: {
32
+ 'content-type': 'application/json',
33
+ 'x-api-key': opts.apiKey,
34
+ 'anthropic-version': API_VERSION,
35
+ },
36
+ body,
37
+ };
38
+ },
39
+ parseResponse(data) {
40
+ const text = data.content?.find(b => b.type === 'text')?.text ?? '';
41
+ return {
42
+ text,
43
+ usage: {
44
+ input: data.usage?.input_tokens ?? 0,
45
+ output: data.usage?.output_tokens ?? 0,
46
+ cacheCreation: data.usage?.cache_creation_input_tokens ?? 0,
47
+ cacheRead: data.usage?.cache_read_input_tokens ?? 0,
48
+ },
49
+ stopReason: data.stop_reason ?? 'end',
50
+ };
51
+ },
52
+ async *parseStream(response) {
53
+ if (!response.body)
54
+ throw new Error('Response body is null');
55
+ let snapshot = '';
56
+ let usage = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
57
+ let stopReason = 'end';
58
+ for await (const event of readSSE(response.body)) {
59
+ if (event.done)
60
+ break;
61
+ let data;
62
+ try {
63
+ data = JSON.parse(event.data);
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ const eventType = event.event ?? data.type;
69
+ switch (eventType) {
70
+ case 'message_start':
71
+ if (data.message?.usage) {
72
+ usage.input = data.message.usage.input_tokens ?? 0;
73
+ usage.cacheCreation = data.message.usage.cache_creation_input_tokens ?? 0;
74
+ usage.cacheRead = data.message.usage.cache_read_input_tokens ?? 0;
75
+ }
76
+ break;
77
+ case 'content_block_delta': {
78
+ const delta = data.delta;
79
+ if (delta?.type === 'text_delta' && delta.text != null) {
80
+ snapshot += delta.text;
81
+ yield { type: 'text', text: delta.text, snapshot };
82
+ }
83
+ else if (delta?.type === 'thinking_delta' && delta.thinking != null) {
84
+ yield { type: 'thinking', text: delta.thinking };
85
+ }
86
+ break;
87
+ }
88
+ case 'message_delta':
89
+ if (data.delta?.stop_reason)
90
+ stopReason = data.delta.stop_reason;
91
+ if (data.usage)
92
+ usage.output = data.usage.output_tokens ?? 0;
93
+ break;
94
+ case 'message_stop':
95
+ yield { type: 'done', text: snapshot, usage, stopReason };
96
+ break;
97
+ case 'error':
98
+ yield { type: 'error', error: new Error(data.error?.message ?? 'Stream error') };
99
+ break;
100
+ }
101
+ }
102
+ },
106
103
  };
104
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../src/adapters/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Google Gemini generateContent API adapter.
3
+ * Endpoint: https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent
4
+ * Streaming: .../{model}:streamGenerateContent?alt=sse
5
+ */
6
+ import type { AdapterRequest, AdapterResponse, StreamChunk, BuildRequestOpts } from './anthropic.js';
7
+ interface GeminiUsageMetadata {
8
+ promptTokenCount?: number;
9
+ candidatesTokenCount?: number;
10
+ }
11
+ interface GeminiPart {
12
+ text?: string;
13
+ }
14
+ interface GeminiContent {
15
+ parts?: GeminiPart[];
16
+ }
17
+ interface GeminiCandidate {
18
+ content?: GeminiContent;
19
+ finishReason?: string;
20
+ }
21
+ interface GeminiResponseBody {
22
+ candidates?: GeminiCandidate[];
23
+ usageMetadata?: GeminiUsageMetadata;
24
+ }
25
+ export declare const gemini: {
26
+ name: "gemini";
27
+ buildRequest(opts: BuildRequestOpts): AdapterRequest;
28
+ parseResponse(data: GeminiResponseBody): AdapterResponse;
29
+ parseStream(response: Response): AsyncGenerator<StreamChunk>;
30
+ };
31
+ export {};
32
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAgB,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOnH,UAAU,mBAAmB;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,UAAU,UAAU;IAAG,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AACtC,UAAU,aAAa;IAAG,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;CAAE;AAChD,UAAU,eAAe;IACvB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAID,eAAO,MAAM,MAAM;;uBAGE,gBAAgB,GAAG,cAAc;wBA+BhC,kBAAkB,GAAG,eAAe;0BAa3B,QAAQ,GAAG,cAAc,CAAC,WAAW,CAAC;CAgCpE,CAAC"}
@@ -3,97 +3,84 @@
3
3
  * Endpoint: https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent
4
4
  * Streaming: .../{model}:streamGenerateContent?alt=sse
5
5
  */
6
-
7
6
  import { readSSE } from './sse.js';
8
-
9
7
  const API_URL = 'https://generativelanguage.googleapis.com/v1beta/models';
10
- const DEFAULT_MAX_TOKENS = 4096;
11
-
8
+ const DEFAULT_MAX_TOKENS = 32768;
9
+ // ── Adapter ────────────────────────────────────────────────────────────────
12
10
  export const gemini = {
13
- name: 'gemini',
14
-
15
- buildRequest(opts) {
16
- const model = opts.model;
17
- const contents = [];
18
- for (const msg of opts.messages) {
19
- contents.push({
20
- role: msg.role === 'assistant' ? 'model' : 'user',
21
- parts: [{ text: msg.content }],
22
- });
23
- }
24
-
25
- const body = { contents };
26
-
27
- if (opts.system) {
28
- body.systemInstruction = { parts: [{ text: opts.system }] };
29
- }
30
-
31
- const generationConfig = {
32
- maxOutputTokens: opts.maxTokens || DEFAULT_MAX_TOKENS,
33
- };
34
- if (opts.temperature != null) generationConfig.temperature = opts.temperature;
35
- body.generationConfig = generationConfig;
36
-
37
- const action = opts.stream
38
- ? `streamGenerateContent?alt=sse`
39
- : 'generateContent';
40
-
41
- return {
42
- url: `${API_URL}/${model}:${action}`,
43
- headers: {
44
- 'content-type': 'application/json',
45
- 'x-goog-api-key': opts.apiKey,
46
- },
47
- body,
48
- };
49
- },
50
-
51
- parseResponse(data) {
52
- const parts = data.candidates?.[0]?.content?.parts ?? [];
53
- const text = parts.map(p => p.text ?? '').join('');
54
- return {
55
- text,
56
- usage: {
57
- input: data.usageMetadata?.promptTokenCount ?? 0,
58
- output: data.usageMetadata?.candidatesTokenCount ?? 0,
59
- },
60
- stopReason: data.candidates?.[0]?.finishReason === 'STOP' ? 'end' : 'end',
61
- };
62
- },
63
-
64
- async *parseStream(response) {
65
- let snapshot = '';
66
- let usage = { input: 0, output: 0 };
67
- let stopReason = 'end';
68
-
69
- for await (const event of readSSE(response.body)) {
70
- if (event.done) break;
71
- let data;
72
- try { data = JSON.parse(event.data); } catch { continue; }
73
-
74
- if (data.usageMetadata) {
75
- usage.input = data.usageMetadata.promptTokenCount ?? 0;
76
- usage.output = data.usageMetadata.candidatesTokenCount ?? 0;
77
- }
78
-
79
- const candidate = data.candidates?.[0];
80
- if (!candidate) continue;
81
-
82
- if (candidate.finishReason && candidate.finishReason !== 'STOP') {
83
- stopReason = candidate.finishReason;
84
- }
85
-
86
- const parts = candidate.content?.parts;
87
- if (!parts?.length) continue;
88
-
89
- for (const part of parts) {
90
- if (part.text != null) {
91
- snapshot += part.text;
92
- yield { type: 'text', text: part.text, snapshot };
11
+ name: 'gemini',
12
+ buildRequest(opts) {
13
+ const model = opts.model;
14
+ const contents = opts.messages.map(msg => ({
15
+ role: msg.role === 'assistant' ? 'model' : 'user',
16
+ parts: [{ text: msg.content }],
17
+ }));
18
+ const body = { contents };
19
+ if (opts.system != null) {
20
+ body['systemInstruction'] = { parts: [{ text: opts.system }] };
93
21
  }
94
- }
95
- }
96
-
97
- yield { type: 'done', text: snapshot, usage, stopReason };
98
- },
22
+ const generationConfig = {
23
+ maxOutputTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
24
+ };
25
+ if (opts.temperature != null)
26
+ generationConfig['temperature'] = opts.temperature;
27
+ body['generationConfig'] = generationConfig;
28
+ const action = opts.stream ? 'streamGenerateContent?alt=sse' : 'generateContent';
29
+ return {
30
+ url: `${API_URL}/${model}:${action}`,
31
+ headers: {
32
+ 'content-type': 'application/json',
33
+ 'x-goog-api-key': opts.apiKey,
34
+ },
35
+ body,
36
+ };
37
+ },
38
+ parseResponse(data) {
39
+ const parts = data.candidates?.[0]?.content?.parts ?? [];
40
+ const text = parts.map(p => p.text ?? '').join('');
41
+ return {
42
+ text,
43
+ usage: {
44
+ input: data.usageMetadata?.promptTokenCount ?? 0,
45
+ output: data.usageMetadata?.candidatesTokenCount ?? 0,
46
+ },
47
+ stopReason: 'end',
48
+ };
49
+ },
50
+ async *parseStream(response) {
51
+ if (!response.body)
52
+ throw new Error('Response body is null');
53
+ let snapshot = '';
54
+ let usage = { input: 0, output: 0 };
55
+ const stopReason = 'end';
56
+ for await (const event of readSSE(response.body)) {
57
+ if (event.done)
58
+ break;
59
+ let data;
60
+ try {
61
+ data = JSON.parse(event.data);
62
+ }
63
+ catch {
64
+ continue;
65
+ }
66
+ if (data.usageMetadata) {
67
+ usage.input = data.usageMetadata.promptTokenCount ?? 0;
68
+ usage.output = data.usageMetadata.candidatesTokenCount ?? 0;
69
+ }
70
+ const candidate = data.candidates?.[0];
71
+ if (!candidate)
72
+ continue;
73
+ const parts = candidate.content?.parts;
74
+ if (!parts?.length)
75
+ continue;
76
+ for (const part of parts) {
77
+ if (part.text != null) {
78
+ snapshot += part.text;
79
+ yield { type: 'text', text: part.text, snapshot };
80
+ }
81
+ }
82
+ }
83
+ yield { type: 'done', text: snapshot, usage, stopReason };
84
+ },
99
85
  };
86
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../src/adapters/gemini.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * LLM Client — Provider-agnostic chat interface.
3
+ *
4
+ * Usage:
5
+ * import { createClient, chat, streamChat } from './llm/index.js';
6
+ *
7
+ * // Quick use (provider auto-detected from model name)
8
+ * const reply = await chat({
9
+ * apiKey: 'sk-ant-...',
10
+ * model: 'claude-sonnet-4-20250514',
11
+ * messages: [{ role: 'user', content: 'Hello' }],
12
+ * });
13
+ *
14
+ * for await (const chunk of streamChat({
15
+ * apiKey: 'sk-...',
16
+ * model: 'gpt-4o',
17
+ * messages: [{ role: 'user', content: 'Hello' }],
18
+ * })) {
19
+ * if (chunk.type === 'text') process.stdout.write(chunk.text);
20
+ * }
21
+ *
22
+ * // Explicit provider
23
+ * const reply = await chat({ provider: 'gemini', apiKey: '...', model: 'gemini-2.5-flash', ... });
24
+ *
25
+ * // Reusable client instance
26
+ * const client = createClient({ provider: 'anthropic', apiKey: '...' });
27
+ * const reply = await client.chat({ model: 'claude-sonnet-4-20250514', messages: [...] });
28
+ * for await (const chunk of client.stream({ model: '...', messages: [...] })) { ... }
29
+ *
30
+ * Chunk types (streaming):
31
+ * { type: 'text', text: 'delta', snapshot: 'full text so far' }
32
+ * { type: 'thinking', text: 'thinking delta' }
33
+ * { type: 'done', text: 'full response', usage: { input, output }, stopReason }
34
+ * { type: 'error', error: Error }
35
+ */
36
+ import { anthropic } from './anthropic.js';
37
+ import { openai } from './openai.js';
38
+ import { gemini } from './gemini.js';
39
+ import type { AdapterResponse, AdapterUsage, StreamChunk, BuildRequestOpts } from './anthropic.js';
40
+ export type { StreamChunk, AdapterUsage, AdapterResponse, BuildRequestOpts };
41
+ export interface ChatOpts {
42
+ model: string;
43
+ messages: Array<{
44
+ role: string;
45
+ content: string;
46
+ }>;
47
+ apiKey: string;
48
+ provider?: string;
49
+ system?: string | Array<{
50
+ type: string;
51
+ text: string;
52
+ cache_control?: {
53
+ type: string;
54
+ };
55
+ }>;
56
+ maxTokens?: number;
57
+ temperature?: number;
58
+ stream?: boolean;
59
+ thinking?: boolean;
60
+ thinkingBudget?: number;
61
+ signal?: AbortSignal | null;
62
+ proxyUrl?: string;
63
+ cache?: boolean;
64
+ }
65
+ export interface ChatResult {
66
+ text: string;
67
+ usage: AdapterUsage;
68
+ stopReason: string;
69
+ }
70
+ export interface LLMClient {
71
+ chat(opts: Partial<ChatOpts>): Promise<ChatResult>;
72
+ stream(opts: Partial<ChatOpts>): AsyncGenerator<StreamChunk>;
73
+ }
74
+ /**
75
+ * Non-streaming chat completion.
76
+ */
77
+ export declare function chat(opts: ChatOpts): Promise<ChatResult>;
78
+ /**
79
+ * Streaming chat — yields chunks as they arrive.
80
+ */
81
+ export declare function streamChat(opts: ChatOpts): AsyncGenerator<StreamChunk>;
82
+ /**
83
+ * Create a reusable client instance with defaults baked in.
84
+ *
85
+ * @param defaults
86
+ * @param defaults.provider — 'anthropic' | 'openai' | 'gemini'
87
+ * @param defaults.apiKey
88
+ * @param defaults.model — default model
89
+ * @param defaults.proxyUrl — proxy URL (for CORS)
90
+ * @param defaults.system — default system prompt
91
+ */
92
+ export declare function createClient(defaults?: Partial<ChatOpts>): LLMClient;
93
+ export { anthropic, openai, gemini };
94
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEnG,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC;AAI7E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;CAC9D;AAqFD;;GAEG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB9D;AAED;;GAEG;AACH,wBAAuB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,WAAW,CAAC,CA0B7E;AAID;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,GAAE,OAAO,CAAC,QAAQ,CAAM,GAAG,SAAS,CAKxE;AAGD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC"}