@thinwrap/llm 1.0.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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cjs/base/base.connector.d.ts +44 -0
  4. package/dist/cjs/base/base.connector.js +216 -0
  5. package/dist/cjs/facades/chat.facade.d.ts +17 -0
  6. package/dist/cjs/facades/chat.facade.js +63 -0
  7. package/dist/cjs/facades/embeddings.facade.d.ts +23 -0
  8. package/dist/cjs/facades/embeddings.facade.js +63 -0
  9. package/dist/cjs/index.d.ts +15 -0
  10. package/dist/cjs/index.js +35 -0
  11. package/dist/cjs/package.json +1 -0
  12. package/dist/cjs/providers/_shared/openai-compat.connector.d.ts +23 -0
  13. package/dist/cjs/providers/_shared/openai-compat.connector.js +320 -0
  14. package/dist/cjs/providers/_shared/openai-compat.embeddings.connector.d.ts +18 -0
  15. package/dist/cjs/providers/_shared/openai-compat.embeddings.connector.js +105 -0
  16. package/dist/cjs/providers/_shared/spec.d.ts +99 -0
  17. package/dist/cjs/providers/_shared/spec.js +84 -0
  18. package/dist/cjs/providers/anthropic/anthropic.config.d.ts +17 -0
  19. package/dist/cjs/providers/anthropic/anthropic.config.js +2 -0
  20. package/dist/cjs/providers/anthropic/anthropic.connector.d.ts +30 -0
  21. package/dist/cjs/providers/anthropic/anthropic.connector.js +427 -0
  22. package/dist/cjs/providers/anthropic/index.d.ts +2 -0
  23. package/dist/cjs/providers/anthropic/index.js +5 -0
  24. package/dist/cjs/providers/bedrock/bedrock.config.d.ts +18 -0
  25. package/dist/cjs/providers/bedrock/bedrock.config.js +2 -0
  26. package/dist/cjs/providers/bedrock/bedrock.connector.d.ts +61 -0
  27. package/dist/cjs/providers/bedrock/bedrock.connector.js +479 -0
  28. package/dist/cjs/providers/bedrock/index.d.ts +2 -0
  29. package/dist/cjs/providers/bedrock/index.js +5 -0
  30. package/dist/cjs/providers/gemini/gemini.config.d.ts +12 -0
  31. package/dist/cjs/providers/gemini/gemini.config.js +2 -0
  32. package/dist/cjs/providers/gemini/gemini.connector.d.ts +28 -0
  33. package/dist/cjs/providers/gemini/gemini.connector.js +434 -0
  34. package/dist/cjs/providers/gemini/index.d.ts +2 -0
  35. package/dist/cjs/providers/gemini/index.js +5 -0
  36. package/dist/cjs/types/chat.types.d.ts +115 -0
  37. package/dist/cjs/types/chat.types.js +2 -0
  38. package/dist/cjs/types/config-map.type.d.ts +29 -0
  39. package/dist/cjs/types/config-map.type.js +2 -0
  40. package/dist/cjs/types/embeddings.types.d.ts +29 -0
  41. package/dist/cjs/types/embeddings.types.js +2 -0
  42. package/dist/cjs/types/error.types.d.ts +20 -0
  43. package/dist/cjs/types/error.types.js +22 -0
  44. package/dist/cjs/types/index.d.ts +7 -0
  45. package/dist/cjs/types/index.js +23 -0
  46. package/dist/cjs/types/passthrough.type.d.ts +15 -0
  47. package/dist/cjs/types/passthrough.type.js +2 -0
  48. package/dist/cjs/types/provider-id.enum.d.ts +8 -0
  49. package/dist/cjs/types/provider-id.enum.js +29 -0
  50. package/dist/cjs/types/provider.interface.d.ts +7 -0
  51. package/dist/cjs/types/provider.interface.js +2 -0
  52. package/dist/esm/base/base.connector.d.ts +44 -0
  53. package/dist/esm/base/base.connector.js +209 -0
  54. package/dist/esm/facades/chat.facade.d.ts +17 -0
  55. package/dist/esm/facades/chat.facade.js +59 -0
  56. package/dist/esm/facades/embeddings.facade.d.ts +23 -0
  57. package/dist/esm/facades/embeddings.facade.js +59 -0
  58. package/dist/esm/index.d.ts +15 -0
  59. package/dist/esm/index.js +9 -0
  60. package/dist/esm/package.json +1 -0
  61. package/dist/esm/providers/_shared/openai-compat.connector.d.ts +23 -0
  62. package/dist/esm/providers/_shared/openai-compat.connector.js +316 -0
  63. package/dist/esm/providers/_shared/openai-compat.embeddings.connector.d.ts +18 -0
  64. package/dist/esm/providers/_shared/openai-compat.embeddings.connector.js +101 -0
  65. package/dist/esm/providers/_shared/spec.d.ts +99 -0
  66. package/dist/esm/providers/_shared/spec.js +81 -0
  67. package/dist/esm/providers/anthropic/anthropic.config.d.ts +17 -0
  68. package/dist/esm/providers/anthropic/anthropic.config.js +1 -0
  69. package/dist/esm/providers/anthropic/anthropic.connector.d.ts +30 -0
  70. package/dist/esm/providers/anthropic/anthropic.connector.js +423 -0
  71. package/dist/esm/providers/anthropic/index.d.ts +2 -0
  72. package/dist/esm/providers/anthropic/index.js +1 -0
  73. package/dist/esm/providers/bedrock/bedrock.config.d.ts +18 -0
  74. package/dist/esm/providers/bedrock/bedrock.config.js +1 -0
  75. package/dist/esm/providers/bedrock/bedrock.connector.d.ts +61 -0
  76. package/dist/esm/providers/bedrock/bedrock.connector.js +474 -0
  77. package/dist/esm/providers/bedrock/index.d.ts +2 -0
  78. package/dist/esm/providers/bedrock/index.js +1 -0
  79. package/dist/esm/providers/gemini/gemini.config.d.ts +12 -0
  80. package/dist/esm/providers/gemini/gemini.config.js +1 -0
  81. package/dist/esm/providers/gemini/gemini.connector.d.ts +28 -0
  82. package/dist/esm/providers/gemini/gemini.connector.js +430 -0
  83. package/dist/esm/providers/gemini/index.d.ts +2 -0
  84. package/dist/esm/providers/gemini/index.js +1 -0
  85. package/dist/esm/types/chat.types.d.ts +115 -0
  86. package/dist/esm/types/chat.types.js +1 -0
  87. package/dist/esm/types/config-map.type.d.ts +29 -0
  88. package/dist/esm/types/config-map.type.js +1 -0
  89. package/dist/esm/types/embeddings.types.d.ts +29 -0
  90. package/dist/esm/types/embeddings.types.js +1 -0
  91. package/dist/esm/types/error.types.d.ts +20 -0
  92. package/dist/esm/types/error.types.js +18 -0
  93. package/dist/esm/types/index.d.ts +7 -0
  94. package/dist/esm/types/index.js +7 -0
  95. package/dist/esm/types/passthrough.type.d.ts +15 -0
  96. package/dist/esm/types/passthrough.type.js +1 -0
  97. package/dist/esm/types/provider-id.enum.d.ts +8 -0
  98. package/dist/esm/types/provider-id.enum.js +26 -0
  99. package/dist/esm/types/provider.interface.d.ts +7 -0
  100. package/dist/esm/types/provider.interface.js +1 -0
  101. package/package.json +73 -0
  102. package/src/providers/anthropic/README.md +64 -0
  103. package/src/providers/azure-openai/README.md +62 -0
  104. package/src/providers/bedrock/README.md +71 -0
  105. package/src/providers/cloudflare/README.md +62 -0
  106. package/src/providers/deepinfra/README.md +61 -0
  107. package/src/providers/deepseek/README.md +50 -0
  108. package/src/providers/fireworks/README.md +62 -0
  109. package/src/providers/gemini/README.md +67 -0
  110. package/src/providers/groq/README.md +50 -0
  111. package/src/providers/lmstudio/README.md +62 -0
  112. package/src/providers/mistral/README.md +61 -0
  113. package/src/providers/ollama/README.md +62 -0
  114. package/src/providers/openai/README.md +62 -0
  115. package/src/providers/openrouter/README.md +62 -0
  116. package/src/providers/perplexity/README.md +50 -0
  117. package/src/providers/together/README.md +61 -0
  118. package/src/providers/vllm/README.md +62 -0
  119. package/src/providers/xai/README.md +50 -0
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./error.types"), exports);
18
+ __exportStar(require("./passthrough.type"), exports);
19
+ __exportStar(require("./chat.types"), exports);
20
+ __exportStar(require("./embeddings.types"), exports);
21
+ __exportStar(require("./provider-id.enum"), exports);
22
+ __exportStar(require("./config-map.type"), exports);
23
+ __exportStar(require("./provider.interface"), exports);
@@ -0,0 +1,15 @@
1
+ export interface Passthrough {
2
+ /**
3
+ * Merged into the outbound request JSON body; overrides connector-built fields
4
+ * on key collision. Never transformed or validated — if you populate this from
5
+ * untrusted input, sanitize it yourself (you own the keys that reach the wire).
6
+ */
7
+ body?: Record<string, unknown>;
8
+ /** Merged into request headers. */
9
+ headers?: Record<string, string>;
10
+ /** Appended to the request URL query string. */
11
+ query?: Record<string, string>;
12
+ }
13
+ export type WithPassthrough<T> = T & {
14
+ _passthrough?: Passthrough;
15
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Provider ids implemented so far — the 15 first-class OpenAI-compatible
3
+ * providers (one shared connector) plus native-adapter providers as they land
4
+ * (`anthropic`). Remaining natives (bedrock, gemini, vertex, cohere) are added
5
+ * when their adapters ship.
6
+ */
7
+ export declare const LLM_PROVIDER_IDS: readonly ["openai", "azure-openai", "openrouter", "groq", "together", "fireworks", "deepseek", "xai", "mistral", "perplexity", "deepinfra", "cloudflare", "vllm", "ollama", "lmstudio", "anthropic", "bedrock", "gemini"];
8
+ export type LlmProviderId = (typeof LLM_PROVIDER_IDS)[number];
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LLM_PROVIDER_IDS = void 0;
4
+ /**
5
+ * Provider ids implemented so far — the 15 first-class OpenAI-compatible
6
+ * providers (one shared connector) plus native-adapter providers as they land
7
+ * (`anthropic`). Remaining natives (bedrock, gemini, vertex, cohere) are added
8
+ * when their adapters ship.
9
+ */
10
+ exports.LLM_PROVIDER_IDS = [
11
+ 'openai',
12
+ 'azure-openai',
13
+ 'openrouter',
14
+ 'groq',
15
+ 'together',
16
+ 'fireworks',
17
+ 'deepseek',
18
+ 'xai',
19
+ 'mistral',
20
+ 'perplexity',
21
+ 'deepinfra',
22
+ 'cloudflare',
23
+ 'vllm',
24
+ 'ollama',
25
+ 'lmstudio',
26
+ 'anthropic',
27
+ 'bedrock',
28
+ 'gemini',
29
+ ];
@@ -0,0 +1,7 @@
1
+ import type { ChatInput, ChatResult, ChatStreamDelta } from './chat.types';
2
+ /** The contract every chat connector satisfies — shared-compat or native adapter. */
3
+ export interface IChatConnector {
4
+ readonly id: string;
5
+ complete(input: ChatInput): Promise<ChatResult>;
6
+ stream(input: ChatInput): AsyncIterable<ChatStreamDelta>;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,44 @@
1
+ import { ConnectorError } from '../types/index.js';
2
+ /**
3
+ * Shared HTTP plumbing for every connector. Holds the BYO `fetch` and maps
4
+ * transport-level failures (abort, network) to `ConnectorError`. Applies no
5
+ * automatic key transformation and holds no state (umbrella invariants).
6
+ */
7
+ export declare abstract class BaseConnector {
8
+ protected readonly fetchImpl: typeof fetch;
9
+ protected constructor(fetchImpl?: typeof fetch);
10
+ protected sendPostJson(url: string, body: unknown, headers: Record<string, string>, signal?: AbortSignal): Promise<Response>;
11
+ protected invokeFetch(url: string, init: RequestInit): Promise<Response>;
12
+ /**
13
+ * Guard a decoded 2xx body: a `null` here means the body was empty or not
14
+ * JSON (a proxy/captive-portal HTML page, a truncated read). Parsing that into
15
+ * an empty "successful" result is silent data loss, so raise instead.
16
+ */
17
+ protected requireDecodedBody<T>(json: T | null, status: number): T;
18
+ }
19
+ /**
20
+ * Map a caught transport-layer failure to a `ConnectorError`. Shared by the
21
+ * request path (`invokeFetch`) and the streaming read loop (`parseSSEStream`) so
22
+ * a mid-stream TCP drop / abort surfaces with the same taxonomy as a failure
23
+ * before the first byte — never a raw `TypeError`/`DOMException`.
24
+ *
25
+ * A manual `AbortController.abort()` raises an `AbortError` (consumer cancelled
26
+ * → `invalid_request`); `AbortSignal.timeout()` raises a distinct `TimeoutError`
27
+ * (the provider failed to respond in time → `provider_unavailable`). Any other
28
+ * rejection (network reset, DNS) is `provider_unavailable`.
29
+ */
30
+ export declare function transportErrorFrom(err: unknown): ConnectorError;
31
+ /**
32
+ * Append `_passthrough.query` params to a request URL. EVERY request path must
33
+ * honor this — not just OpenAI-compat chat — because some providers require a
34
+ * query param the facade doesn't model (e.g. Azure OpenAI's `api-version`).
35
+ * Preserves any query string the URL already carries.
36
+ */
37
+ export declare function appendPassthroughQuery(url: string, query?: Record<string, string>): string;
38
+ /**
39
+ * Parse a Server-Sent-Events response body into a sequence of JSON-decoded
40
+ * `data:` payloads. Skips comments/blank lines, stops at the `[DONE]` sentinel,
41
+ * and ignores any `data:` line that isn't valid JSON. Transport-agnostic over
42
+ * `\n\n` and `\r\n\r\n` event boundaries.
43
+ */
44
+ export declare function parseSSEStream(response: Response): AsyncGenerator<unknown>;
@@ -0,0 +1,209 @@
1
+ import { ConnectorError } from '../types/index.js';
2
+ /**
3
+ * Shared HTTP plumbing for every connector. Holds the BYO `fetch` and maps
4
+ * transport-level failures (abort, network) to `ConnectorError`. Applies no
5
+ * automatic key transformation and holds no state (umbrella invariants).
6
+ */
7
+ export class BaseConnector {
8
+ constructor(fetchImpl) {
9
+ const baseFetch = fetchImpl ?? globalThis.fetch;
10
+ // Force `redirect: 'error'` on every request at the transport seam so a 3xx
11
+ // can never silently re-send credentials to the redirect target. `fetch`
12
+ // strips only `Authorization`/`Cookie` cross-origin, so provider key headers
13
+ // (x-api-key, x-goog-api-key, api-key, …) would otherwise leak. No LLM
14
+ // endpoint legitimately redirects a POST.
15
+ this.fetchImpl = ((input, init) => baseFetch(input, { ...init, redirect: 'error' }));
16
+ }
17
+ async sendPostJson(url, body, headers, signal) {
18
+ return this.invokeFetch(url, {
19
+ method: 'POST',
20
+ headers: { 'Content-Type': 'application/json', ...headers },
21
+ body: JSON.stringify(body),
22
+ signal,
23
+ });
24
+ }
25
+ async invokeFetch(url, init) {
26
+ try {
27
+ return await this.fetchImpl(url, init);
28
+ }
29
+ catch (err) {
30
+ throw transportErrorFrom(err);
31
+ }
32
+ }
33
+ /**
34
+ * Guard a decoded 2xx body: a `null` here means the body was empty or not
35
+ * JSON (a proxy/captive-portal HTML page, a truncated read). Parsing that into
36
+ * an empty "successful" result is silent data loss, so raise instead.
37
+ */
38
+ requireDecodedBody(json, status) {
39
+ if (json === null) {
40
+ const message = 'Provider returned a successful status with an empty or non-JSON body';
41
+ throw new ConnectorError({
42
+ message,
43
+ statusCode: status,
44
+ providerCode: 'provider_unavailable',
45
+ providerMessage: message,
46
+ cause: null,
47
+ });
48
+ }
49
+ return json;
50
+ }
51
+ }
52
+ /**
53
+ * Map a caught transport-layer failure to a `ConnectorError`. Shared by the
54
+ * request path (`invokeFetch`) and the streaming read loop (`parseSSEStream`) so
55
+ * a mid-stream TCP drop / abort surfaces with the same taxonomy as a failure
56
+ * before the first byte — never a raw `TypeError`/`DOMException`.
57
+ *
58
+ * A manual `AbortController.abort()` raises an `AbortError` (consumer cancelled
59
+ * → `invalid_request`); `AbortSignal.timeout()` raises a distinct `TimeoutError`
60
+ * (the provider failed to respond in time → `provider_unavailable`). Any other
61
+ * rejection (network reset, DNS) is `provider_unavailable`.
62
+ */
63
+ export function transportErrorFrom(err) {
64
+ if (err instanceof ConnectorError)
65
+ return err;
66
+ const name = err?.name;
67
+ if (name === 'AbortError') {
68
+ return new ConnectorError({
69
+ message: err.message || 'Request cancelled',
70
+ statusCode: null,
71
+ providerCode: 'invalid_request',
72
+ cause: { raw: err },
73
+ });
74
+ }
75
+ return new ConnectorError({
76
+ message: err?.message || 'Network error',
77
+ statusCode: null,
78
+ providerCode: 'provider_unavailable',
79
+ cause: { raw: err },
80
+ });
81
+ }
82
+ /**
83
+ * Append `_passthrough.query` params to a request URL. EVERY request path must
84
+ * honor this — not just OpenAI-compat chat — because some providers require a
85
+ * query param the facade doesn't model (e.g. Azure OpenAI's `api-version`).
86
+ * Preserves any query string the URL already carries.
87
+ */
88
+ export function appendPassthroughQuery(url, query) {
89
+ if (!query || Object.keys(query).length === 0)
90
+ return url;
91
+ const sep = url.includes('?') ? '&' : '?';
92
+ return `${url}${sep}${new URLSearchParams(query).toString()}`;
93
+ }
94
+ /**
95
+ * Upper bound on the residual (not-yet-terminated) SSE buffer. A stream that
96
+ * never emits an event boundary would otherwise grow `buffer` without limit
97
+ * (memory-exhaustion DoS from a malicious/broken upstream). Generous enough for
98
+ * any single legitimate LLM event; complete events are drained before this is
99
+ * checked, so many-small-event streams never approach it.
100
+ */
101
+ const MAX_SSE_EVENT_CHARS = 16 * 1024 * 1024;
102
+ /**
103
+ * Parse a Server-Sent-Events response body into a sequence of JSON-decoded
104
+ * `data:` payloads. Skips comments/blank lines, stops at the `[DONE]` sentinel,
105
+ * and ignores any `data:` line that isn't valid JSON. Transport-agnostic over
106
+ * `\n\n` and `\r\n\r\n` event boundaries.
107
+ */
108
+ export async function* parseSSEStream(response) {
109
+ const body = response.body;
110
+ if (!body)
111
+ return;
112
+ const reader = body.getReader();
113
+ const decoder = new TextDecoder();
114
+ let buffer = '';
115
+ let drained = false;
116
+ try {
117
+ for (;;) {
118
+ let done;
119
+ let value;
120
+ try {
121
+ ({ done, value } = await reader.read());
122
+ }
123
+ catch (err) {
124
+ // A mid-stream read rejection (TCP reset, abort, undici error) must
125
+ // surface with the ConnectorError taxonomy, not as a raw DOMException —
126
+ // streams are the failures consumers are least able to catch generically.
127
+ throw transportErrorFrom(err);
128
+ }
129
+ if (done)
130
+ break;
131
+ buffer += decoder.decode(value, { stream: true });
132
+ let boundary;
133
+ while ((boundary = indexOfEventBoundary(buffer)) !== -1) {
134
+ const rawEvent = buffer.slice(0, boundary);
135
+ buffer = buffer.slice(boundary).replace(/^(?:\r\n|\r|\n)+/, '');
136
+ const payload = extractData(rawEvent);
137
+ if (payload === null)
138
+ continue;
139
+ if (payload === '[DONE]') {
140
+ // Leave `drained = false` so the `finally` cancels the reader. The
141
+ // sentinel does NOT mean the body is fully consumed — every
142
+ // OpenAI-compat stream ends here, and without a cancel undici can't
143
+ // release the socket (leak on every successful stream).
144
+ return;
145
+ }
146
+ const parsed = tryParseJson(payload);
147
+ if (parsed !== undefined)
148
+ yield parsed;
149
+ }
150
+ if (buffer.length > MAX_SSE_EVENT_CHARS) {
151
+ throw new ConnectorError({
152
+ message: `SSE event exceeded ${MAX_SSE_EVENT_CHARS} characters without a boundary`,
153
+ statusCode: null,
154
+ providerCode: 'provider_unavailable',
155
+ });
156
+ }
157
+ }
158
+ const tail = buffer.trim();
159
+ if (tail) {
160
+ const payload = extractData(tail);
161
+ if (payload !== null && payload !== '[DONE]') {
162
+ const parsed = tryParseJson(payload);
163
+ if (parsed !== undefined)
164
+ yield parsed;
165
+ }
166
+ }
167
+ drained = true;
168
+ }
169
+ finally {
170
+ // If the consumer broke out early (or we threw), the body is not fully read —
171
+ // cancel it so the underlying connection is released instead of leaking.
172
+ if (!drained) {
173
+ try {
174
+ await reader.cancel();
175
+ }
176
+ catch {
177
+ /* best-effort */
178
+ }
179
+ }
180
+ reader.releaseLock();
181
+ }
182
+ }
183
+ // An SSE event boundary is a blank line: two consecutive line terminators. The
184
+ // spec permits CRLF, CR, or LF as a terminator, so accept any pairing
185
+ // (`\n\n`, `\r\n\r\n`, `\r\r`, and mixed `\n\r\n` / `\r\n\n`) rather than only
186
+ // the two common forms — a CR-only or mixed upstream would otherwise never
187
+ // split and buffer until MAX_SSE_EVENT_CHARS.
188
+ const EVENT_BOUNDARY_RE = /(?:\r\n|\r|\n)(?:\r\n|\r|\n)/;
189
+ function indexOfEventBoundary(buffer) {
190
+ const m = EVENT_BOUNDARY_RE.exec(buffer);
191
+ return m ? m.index : -1;
192
+ }
193
+ function extractData(rawEvent) {
194
+ const dataParts = [];
195
+ for (const line of rawEvent.split(/\r\n|\r|\n/)) {
196
+ if (line.startsWith('data:')) {
197
+ dataParts.push(line.slice(5).replace(/^ /, ''));
198
+ }
199
+ }
200
+ return dataParts.length === 0 ? null : dataParts.join('\n');
201
+ }
202
+ function tryParseJson(text) {
203
+ try {
204
+ return JSON.parse(text);
205
+ }
206
+ catch {
207
+ return undefined;
208
+ }
209
+ }
@@ -0,0 +1,17 @@
1
+ import type { LlmProviderId, ChatInput, ChatResult, ChatStreamDelta, IChatConnector, ProviderConfigMap } from '../types/index.js';
2
+ export type ChatConfig<P extends LlmProviderId> = ProviderConfigMap[P];
3
+ /**
4
+ * The chat-completion facade. Construct by provider id + config — the facade
5
+ * dispatches to the shared OpenAI-compatible connector (native adapters slot in
6
+ * here as they land) — or pass a connector instance directly for advanced use.
7
+ * Switching vendor is a change of provider id + `model` only; `ChatInput` /
8
+ * `ChatResult` are identical across providers.
9
+ */
10
+ export declare class Chat<P extends LlmProviderId = LlmProviderId> {
11
+ readonly id: string;
12
+ private readonly connector;
13
+ constructor(providerId: P, config: ChatConfig<P>);
14
+ constructor(connector: IChatConnector);
15
+ complete(input: ChatInput): Promise<ChatResult>;
16
+ stream(input: ChatInput): AsyncIterable<ChatStreamDelta>;
17
+ }
@@ -0,0 +1,59 @@
1
+ import { OpenAICompatConnector } from '../providers/_shared/openai-compat.connector.js';
2
+ import { SPECS } from '../providers/_shared/spec.js';
3
+ import { AnthropicConnector } from '../providers/anthropic/index.js';
4
+ import { BedrockConnector } from '../providers/bedrock/index.js';
5
+ import { GeminiConnector } from '../providers/gemini/index.js';
6
+ import { ConnectorError } from '../types/index.js';
7
+ /**
8
+ * The chat-completion facade. Construct by provider id + config — the facade
9
+ * dispatches to the shared OpenAI-compatible connector (native adapters slot in
10
+ * here as they land) — or pass a connector instance directly for advanced use.
11
+ * Switching vendor is a change of provider id + `model` only; `ChatInput` /
12
+ * `ChatResult` are identical across providers.
13
+ */
14
+ export class Chat {
15
+ constructor(arg, config) {
16
+ if (typeof arg === 'object' && arg !== null) {
17
+ this.connector = arg;
18
+ this.id = arg.id;
19
+ return;
20
+ }
21
+ const providerId = arg;
22
+ this.id = providerId;
23
+ if (!config) {
24
+ throw new ConnectorError({
25
+ message: 'Chat facade requires `config` when constructed with a provider id',
26
+ statusCode: null,
27
+ providerCode: 'invalid_request',
28
+ });
29
+ }
30
+ // Native adapters — structurally different wire, same normalized surface.
31
+ if (providerId === 'anthropic') {
32
+ this.connector = new AnthropicConnector(config);
33
+ return;
34
+ }
35
+ if (providerId === 'bedrock') {
36
+ this.connector = new BedrockConnector(config);
37
+ return;
38
+ }
39
+ if (providerId === 'gemini') {
40
+ this.connector = new GeminiConnector(config);
41
+ return;
42
+ }
43
+ const spec = SPECS[providerId];
44
+ if (!spec) {
45
+ throw new ConnectorError({
46
+ message: `Unsupported LLM provider: ${String(providerId)}`,
47
+ statusCode: null,
48
+ providerCode: 'invalid_request',
49
+ });
50
+ }
51
+ this.connector = new OpenAICompatConnector(spec, config);
52
+ }
53
+ complete(input) {
54
+ return this.connector.complete(input);
55
+ }
56
+ stream(input) {
57
+ return this.connector.stream(input);
58
+ }
59
+ }
@@ -0,0 +1,23 @@
1
+ import type { EmbeddingsInput, EmbeddingsResult, IEmbeddingsConnector, OpenAICompatConfig } from '../types/index.js';
2
+ /**
3
+ * First-class providers that expose an OpenAI-float-shaped `/embeddings`
4
+ * surface (FR-AI-E1). Deliberately excludes: providers with no native
5
+ * embeddings (deepseek, xai), quantized/non-float shapes (perplexity int8),
6
+ * unstable coverage (groq), and the native-adapter providers (Anthropic →
7
+ * Voyage; Bedrock/Gemini native embeddings have a different shape — a future
8
+ * per-connector embeddings surface).
9
+ */
10
+ export declare const EMBEDDINGS_PROVIDER_IDS: readonly ["openai", "azure-openai", "openrouter", "together", "fireworks", "mistral", "deepinfra", "cloudflare", "vllm", "ollama", "lmstudio"];
11
+ export type EmbeddingsProviderId = (typeof EMBEDDINGS_PROVIDER_IDS)[number];
12
+ /**
13
+ * The embeddings facade — a separate operation from `Chat`. Construct by
14
+ * provider id + config, or with a connector instance. Only the OpenAI-float
15
+ * subset is accepted (type-restricted); other ids throw `ConnectorError`.
16
+ */
17
+ export declare class Embeddings<P extends EmbeddingsProviderId = EmbeddingsProviderId> {
18
+ readonly id: string;
19
+ private readonly connector;
20
+ constructor(providerId: P, config: OpenAICompatConfig);
21
+ constructor(connector: IEmbeddingsConnector);
22
+ create(input: EmbeddingsInput): Promise<EmbeddingsResult>;
23
+ }
@@ -0,0 +1,59 @@
1
+ import { OpenAICompatEmbeddingsConnector } from '../providers/_shared/openai-compat.embeddings.connector.js';
2
+ import { SPECS } from '../providers/_shared/spec.js';
3
+ import { ConnectorError } from '../types/index.js';
4
+ /**
5
+ * First-class providers that expose an OpenAI-float-shaped `/embeddings`
6
+ * surface (FR-AI-E1). Deliberately excludes: providers with no native
7
+ * embeddings (deepseek, xai), quantized/non-float shapes (perplexity int8),
8
+ * unstable coverage (groq), and the native-adapter providers (Anthropic →
9
+ * Voyage; Bedrock/Gemini native embeddings have a different shape — a future
10
+ * per-connector embeddings surface).
11
+ */
12
+ export const EMBEDDINGS_PROVIDER_IDS = [
13
+ 'openai',
14
+ 'azure-openai',
15
+ 'openrouter',
16
+ 'together',
17
+ 'fireworks',
18
+ 'mistral',
19
+ 'deepinfra',
20
+ 'cloudflare',
21
+ 'vllm',
22
+ 'ollama',
23
+ 'lmstudio',
24
+ ];
25
+ const SUPPORTED = new Set(EMBEDDINGS_PROVIDER_IDS);
26
+ /**
27
+ * The embeddings facade — a separate operation from `Chat`. Construct by
28
+ * provider id + config, or with a connector instance. Only the OpenAI-float
29
+ * subset is accepted (type-restricted); other ids throw `ConnectorError`.
30
+ */
31
+ export class Embeddings {
32
+ constructor(arg, config) {
33
+ if (typeof arg === 'object' && arg !== null) {
34
+ this.connector = arg;
35
+ this.id = arg.id;
36
+ return;
37
+ }
38
+ const providerId = arg;
39
+ this.id = providerId;
40
+ if (!SUPPORTED.has(providerId)) {
41
+ throw new ConnectorError({
42
+ message: `Provider '${String(providerId)}' has no OpenAI-compatible embeddings surface`,
43
+ statusCode: null,
44
+ providerCode: 'invalid_request',
45
+ });
46
+ }
47
+ if (!config) {
48
+ throw new ConnectorError({
49
+ message: 'Embeddings facade requires `config` when constructed with a provider id',
50
+ statusCode: null,
51
+ providerCode: 'invalid_request',
52
+ });
53
+ }
54
+ this.connector = new OpenAICompatEmbeddingsConnector(SPECS[providerId], config);
55
+ }
56
+ create(input) {
57
+ return this.connector.create(input);
58
+ }
59
+ }
@@ -0,0 +1,15 @@
1
+ export { Chat } from './facades/chat.facade.js';
2
+ export type { ChatConfig } from './facades/chat.facade.js';
3
+ export { Embeddings, EMBEDDINGS_PROVIDER_IDS } from './facades/embeddings.facade.js';
4
+ export type { EmbeddingsProviderId } from './facades/embeddings.facade.js';
5
+ export { OpenAICompatConnector } from './providers/_shared/openai-compat.connector.js';
6
+ export { OpenAICompatEmbeddingsConnector } from './providers/_shared/openai-compat.embeddings.connector.js';
7
+ export { SPECS } from './providers/_shared/spec.js';
8
+ export type { OpenAICompatSpec, SpecId } from './providers/_shared/spec.js';
9
+ export { AnthropicConnector } from './providers/anthropic/index.js';
10
+ export type { AnthropicConfig } from './providers/anthropic/index.js';
11
+ export { BedrockConnector } from './providers/bedrock/index.js';
12
+ export type { BedrockConfig } from './providers/bedrock/index.js';
13
+ export { GeminiConnector } from './providers/gemini/index.js';
14
+ export type { GeminiConfig } from './providers/gemini/index.js';
15
+ export * from './types/index.js';
@@ -0,0 +1,9 @@
1
+ export { Chat } from './facades/chat.facade.js';
2
+ export { Embeddings, EMBEDDINGS_PROVIDER_IDS } from './facades/embeddings.facade.js';
3
+ export { OpenAICompatConnector } from './providers/_shared/openai-compat.connector.js';
4
+ export { OpenAICompatEmbeddingsConnector } from './providers/_shared/openai-compat.embeddings.connector.js';
5
+ export { SPECS } from './providers/_shared/spec.js';
6
+ export { AnthropicConnector } from './providers/anthropic/index.js';
7
+ export { BedrockConnector } from './providers/bedrock/index.js';
8
+ export { GeminiConnector } from './providers/gemini/index.js';
9
+ export * from './types/index.js';
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,23 @@
1
+ import { BaseConnector } from '../../base/base.connector.js';
2
+ import type { ChatInput, ChatResult, ChatStreamDelta, IChatConnector, OpenAICompatConfig } from '../../types/index.js';
3
+ import type { OpenAICompatSpec } from './spec.js';
4
+ /**
5
+ * The single connector shared by every first-class OpenAI-compatible provider.
6
+ * It builds the `/chat/completions` request from the normalized `ChatInput`,
7
+ * parses the response (and SSE stream) into the normalized shapes, and maps
8
+ * vendor errors to `ConnectorError`. Provider differences are expressed by the
9
+ * `OpenAICompatSpec`, not by subclassing.
10
+ */
11
+ export declare class OpenAICompatConnector extends BaseConnector implements IChatConnector {
12
+ readonly id: string;
13
+ private readonly baseUrl;
14
+ private readonly spec;
15
+ private readonly config;
16
+ constructor(spec: OpenAICompatSpec, config: OpenAICompatConfig);
17
+ complete(input: ChatInput): Promise<ChatResult>;
18
+ stream(input: ChatInput): AsyncGenerator<ChatStreamDelta>;
19
+ private buildRequest;
20
+ private parseResult;
21
+ private mapStreamChunk;
22
+ private mapVendorError;
23
+ }