@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,50 @@
1
+ # Groq — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `groq` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('groq', { apiKey: process.env.GROQ_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'llama-3.3-70b-versatile',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.groq.com/openai/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://console.groq.com/keys>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.groq.com/openai/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - **Baseline-exception (handled):** in streaming, token usage is on `x_groq.usage` of the final chunk, not top-level `usage`. The connector relocates it so `ChatStreamDelta.usage` is populated the same as everywhere else (spec `streamUsagePath: 'x_groq'`).
39
+ - Strict JSON-schema structured output is limited to `openai/gpt-oss-*` models and can't combine with streaming or tools; other models are best-effort.
40
+ - Model ids are provider-namespaced (`openai/…`, `meta-llama/…`, `qwen/…`).
41
+
42
+ ## Embeddings
43
+
44
+ Groq exposes no OpenAI-float embeddings surface, so there is no `Embeddings` support — `new Embeddings('groq', …)` is a type error and throws `ConnectorError` (`invalid_request`).
45
+
46
+ ## Error mapping & passthrough
47
+
48
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
49
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
50
+ - Rate-limit guidance: <https://console.groq.com/docs/rate-limits>.
@@ -0,0 +1,62 @@
1
+ # LM Studio (self-host) — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `lmstudio` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('lmstudio', {});
11
+ const res = await chat.complete({
12
+ model: '<loaded-model>',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | no | — | Optional `Authorization: Bearer <apiKey>` (API-token auth since v0.4.0; off by default). |
22
+ | `baseUrl` | no | `http://localhost:1234/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Local single-user server; auth is off by default (leave `apiKey` empty → no auth header). If you enabled API-token auth, pass the token as `apiKey`; it is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST http://localhost:1234/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - Self-host: local single-user server; override `baseUrl` if not on `:1234`.
39
+ - With a single model loaded, the server ignores the requested model id and serves whatever is loaded.
40
+ - Structured output is grammar-enforced (llama.cpp / MLX); small models can be unreliable.
41
+
42
+ ## Embeddings
43
+
44
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
45
+
46
+ ```ts
47
+ import { Embeddings } from '@thinwrap/llm';
48
+
49
+ const emb = new Embeddings('lmstudio', {});
50
+ const out = await emb.create({
51
+ model: '<embedding-model>',
52
+ input: ['hello', 'world'],
53
+ });
54
+ ```
55
+
56
+ `POST http://localhost:1234/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
57
+
58
+ ## Error mapping & passthrough
59
+
60
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
61
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
62
+ - Rate-limit guidance: <https://lmstudio.ai/docs/app/api>.
@@ -0,0 +1,61 @@
1
+ # Mistral — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `mistral` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('mistral', { apiKey: process.env.MISTRAL_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'mistral-large-latest',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.mistral.ai/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://console.mistral.ai/api-keys>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.mistral.ai/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - **Baseline-exception (handled):** Mistral 422s on unknown params, so the connector omits OpenAI-only extras like `stream_options`. Keep `_passthrough.body` to known Mistral fields.
39
+ - Model ids are date-stamped (e.g. `-YYMM`).
40
+
41
+ ## Embeddings
42
+
43
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
44
+
45
+ ```ts
46
+ import { Embeddings } from '@thinwrap/llm';
47
+
48
+ const emb = new Embeddings('mistral', { apiKey: process.env.MISTRAL_API_KEY! });
49
+ const out = await emb.create({
50
+ model: 'mistral-embed',
51
+ input: ['hello', 'world'],
52
+ });
53
+ ```
54
+
55
+ `POST https://api.mistral.ai/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
56
+
57
+ ## Error mapping & passthrough
58
+
59
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
60
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
61
+ - Rate-limit guidance: <https://docs.mistral.ai/admin/user-management-finops/tier>.
@@ -0,0 +1,62 @@
1
+ # Ollama (self-host) — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `ollama` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('ollama', {});
11
+ const res = await chat.complete({
12
+ model: 'llama3.1:8b',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | no | — | None by default (local server); if set, sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no* | `http://localhost:11434/v1` | *Required for a non-default host. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ No auth by default — leave `apiKey` empty and no auth header is sent. Override `baseUrl` for a remote host.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST http://localhost:11434/v1/chat/completions` (override `baseUrl` for a non-default host).
35
+
36
+ ### Notes / quirks
37
+
38
+ - Self-host: no auth by default; override `baseUrl` for a non-default host.
39
+ - Model ids are host-local `name:tag` (e.g. `llama3.1:8b`) — a 404 means the model isn't pulled (`ollama pull …`).
40
+ - The `/v1` compat layer is documented-partial (drops `tool_choice`/`logit_bias`/`n`; base64-only images).
41
+
42
+ ## Embeddings
43
+
44
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
45
+
46
+ ```ts
47
+ import { Embeddings } from '@thinwrap/llm';
48
+
49
+ const emb = new Embeddings('ollama', {});
50
+ const out = await emb.create({
51
+ model: 'nomic-embed-text',
52
+ input: ['hello'],
53
+ });
54
+ ```
55
+
56
+ `POST http://localhost:11434/v1/embeddings` with `encoding_format: 'float'`; `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
57
+
58
+ ## Error mapping & passthrough
59
+
60
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
61
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
62
+ - Rate-limit guidance: <https://docs.ollama.com/api>.
@@ -0,0 +1,62 @@
1
+ # OpenAI — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `openai` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('openai', { apiKey: process.env.OPENAI_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'gpt-4o',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.openai.com/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create an API key at <https://platform.openai.com/api-keys>. It is sent verbatim as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.openai.com/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - The reference provider — its Chat Completions shape is the canonical facade shape.
39
+ - Reasoning models (o-series / GPT-5-class) reject `temperature`/`top_p`/penalties with a hard **HTTP 400** and require `max_completion_tokens` — guard by model family; don't blanket-pass sampling knobs.
40
+ - OpenAI also ships the newer **Responses API**; this connector targets Chat Completions (D1).
41
+
42
+ ## Embeddings
43
+
44
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
45
+
46
+ ```ts
47
+ import { Embeddings } from '@thinwrap/llm';
48
+
49
+ const emb = new Embeddings('openai', { apiKey: process.env.OPENAI_API_KEY! });
50
+ const out = await emb.create({
51
+ model: 'text-embedding-3-small',
52
+ input: ['hello', 'world'],
53
+ });
54
+ ```
55
+
56
+ `POST https://api.openai.com/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` (Matryoshka) when set. Vectors are returned in input order on `out.embeddings`.
57
+
58
+ ## Error mapping & passthrough
59
+
60
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
61
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
62
+ - Rate-limit guidance: <https://platform.openai.com/docs/guides/rate-limits>.
@@ -0,0 +1,62 @@
1
+ # OpenRouter — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `openrouter` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('openrouter', { apiKey: process.env.OPENROUTER_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'openai/gpt-4o',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://openrouter.ai/api/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://openrouter.ai/keys>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://openrouter.ai/api/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - Aggregator: model ids are `{provider}/{model}` (e.g. `anthropic/claude-sonnet-4.5`), with optional `:free`/`:nitro`/`:floor` suffixes.
39
+ - Pass `HTTP-Referer` / `X-Title` via `headers` for app attribution.
40
+ - Drops params an underlying provider lacks (unless `provider.require_parameters` is set via `_passthrough`); `n` is unsupported.
41
+
42
+ ## Embeddings
43
+
44
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
45
+
46
+ ```ts
47
+ import { Embeddings } from '@thinwrap/llm';
48
+
49
+ const emb = new Embeddings('openrouter', { apiKey: process.env.OPENROUTER_API_KEY! });
50
+ const out = await emb.create({
51
+ model: 'openai/text-embedding-3-small',
52
+ input: ['hello', 'world'],
53
+ });
54
+ ```
55
+
56
+ `POST https://openrouter.ai/api/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
57
+
58
+ ## Error mapping & passthrough
59
+
60
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
61
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
62
+ - Rate-limit guidance: <https://openrouter.ai/docs/api-reference/limits>.
@@ -0,0 +1,50 @@
1
+ # Perplexity (Sonar) — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `perplexity` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('perplexity', { apiKey: process.env.PERPLEXITY_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'sonar',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.perplexity.ai` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://www.perplexity.ai/settings/api>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.perplexity.ai/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - **Baseline-exception (handled):** the Sonar chat surface has **no tool/function calling** (`tools` are not sent); tools live on Perplexity's separate Agent API.
39
+ - Sampling surface is stripped (no `top_k`/penalties/`seed`/`n` on Sonar).
40
+ - Model ids are `sonar` / `sonar-pro` / `sonar-reasoning-pro` / `sonar-deep-research`.
41
+
42
+ ## Embeddings
43
+
44
+ Perplexity exposes no OpenAI-float embeddings surface, so there is no `Embeddings` support — `new Embeddings('perplexity', …)` is a type error and throws `ConnectorError` (`invalid_request`).
45
+
46
+ ## Error mapping & passthrough
47
+
48
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
49
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
50
+ - Rate-limit guidance: <https://docs.perplexity.ai/guides/usage-tiers>.
@@ -0,0 +1,61 @@
1
+ # Together AI — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `together` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('together', { apiKey: process.env.TOGETHER_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.together.ai/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://api.together.ai/settings/api-keys>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.together.ai/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - Model ids are namespaced `org/model` slugs — **OpenAI model strings like `gpt-4o` return 404**; use Together's ids.
39
+ - `service_tier`, `store`, `metadata`, `prediction`, and the vision `detail` field are accepted but ignored; `seed` is best-effort.
40
+
41
+ ## Embeddings
42
+
43
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
44
+
45
+ ```ts
46
+ import { Embeddings } from '@thinwrap/llm';
47
+
48
+ const emb = new Embeddings('together', { apiKey: process.env.TOGETHER_API_KEY! });
49
+ const out = await emb.create({
50
+ model: 'BAAI/bge-large-en-v1.5',
51
+ input: ['hello', 'world'],
52
+ });
53
+ ```
54
+
55
+ `POST https://api.together.ai/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
56
+
57
+ ## Error mapping & passthrough
58
+
59
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
60
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
61
+ - Rate-limit guidance: <https://docs.together.ai/docs/rate-limits>.
@@ -0,0 +1,62 @@
1
+ # vLLM (self-host) — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `vllm` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('vllm', { apiKey: process.env.VLLM_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: '<served-model-name>',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | no | — | Optional `Authorization: Bearer <apiKey>` (open if the server has no `--api-key`). |
22
+ | `baseUrl` | no | `http://localhost:8000/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Open by default. If the server was launched with `--api-key`, pass that value as `apiKey`; it is sent as `Authorization: Bearer <apiKey>`. When `apiKey` is empty, no auth header is sent.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST http://localhost:8000/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - Self-host: override `baseUrl` for a remote server; the compat server is vLLM's first-class interface.
39
+ - Model ids are operator-defined (the HF repo id or `--served-model-name`).
40
+ - `tool_choice: 'auto'` requires the server launched with `--enable-auto-tool-choice` + a model-matched `--tool-call-parser`.
41
+
42
+ ## Embeddings
43
+
44
+ Supported (served by the shared `OpenAICompatEmbeddingsConnector`).
45
+
46
+ ```ts
47
+ import { Embeddings } from '@thinwrap/llm';
48
+
49
+ const emb = new Embeddings('vllm', { apiKey: process.env.VLLM_API_KEY! });
50
+ const out = await emb.create({
51
+ model: '<embedding-model>',
52
+ input: ['hello', 'world'],
53
+ });
54
+ ```
55
+
56
+ `POST http://localhost:8000/v1/embeddings` with `encoding_format: 'float'`. `dimensions` maps to `dimensions` when set. Vectors are returned in input order on `out.embeddings`.
57
+
58
+ ## Error mapping & passthrough
59
+
60
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
61
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
62
+ - Rate-limit guidance: <https://docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server/>.
@@ -0,0 +1,50 @@
1
+ # xAI (Grok) — `@thinwrap/llm`
2
+
3
+ First-class OpenAI-compatible provider — served by the shared `OpenAICompatConnector` via a row in `src/providers/_shared/spec.ts` (no per-provider connector class). It emits the identical normalized `ChatResult` / `ChatStreamDelta` / `ConnectorError` as every other connector, so switching to/from `xai` is just the provider id + `model`.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { Chat } from '@thinwrap/llm';
9
+
10
+ const chat = new Chat('xai', { apiKey: process.env.XAI_API_KEY! });
11
+ const res = await chat.complete({
12
+ model: 'grok-4',
13
+ messages: [{ role: 'user', content: 'Say hi in one word.' }],
14
+ });
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ | Field | Required | Default | Notes |
20
+ |---|---|---|---|
21
+ | `apiKey` | yes | — | Sent as `Authorization: Bearer <apiKey>`. |
22
+ | `baseUrl` | no | `https://api.x.ai/v1` | Override for proxies / gateways. |
23
+ | `fetch` | no | `globalThis.fetch` | Bring-your-own fetch. |
24
+ | `headers` | no | — | Extra headers merged onto every request. |
25
+
26
+ ## Auth setup
27
+
28
+ Create a key at <https://console.x.ai>. It is sent as `Authorization: Bearer <apiKey>`.
29
+
30
+ ## Chat
31
+
32
+ ### Endpoint
33
+
34
+ `POST https://api.x.ai/v1/chat/completions`
35
+
36
+ ### Notes / quirks
37
+
38
+ - Model ids are `grok-*`.
39
+ - xAI positions Chat Completions as a **legacy** endpoint (it steers new work to its own Responses API); this connector targets Chat Completions.
40
+ - In streaming, a tool/function call arrives **whole in a single chunk**, not as incremental argument deltas.
41
+
42
+ ## Embeddings
43
+
44
+ xAI exposes no OpenAI-float embeddings surface, so there is no `Embeddings` support — `new Embeddings('xai', …)` is a type error and throws `ConnectorError` (`invalid_request`).
45
+
46
+ ## Error mapping & passthrough
47
+
48
+ - Non-2xx responses throw `ConnectorError` with a 7-value `providerCode` (`auth_failed` / `rate_limited` / `provider_unavailable` / `invalid_request` / `context_length_exceeded` / `content_filtered` / `unknown`). The raw vendor body is on `cause.raw`; retry hints (when present) on `cause.retryAfter` / `cause.retryAfterSeconds`.
49
+ - Sub-baseline features — `topK`, `seed`, `frequency`/`presence_penalty`, `logitBias`, `n`, reasoning control beyond `reasoning.effort`, prompt caching — ride through `_passthrough` (request) / `raw` (result). They are never emulated.
50
+ - Rate-limit guidance: <https://docs.x.ai/docs/rate-limits>.