@warlock.js/ai-groq 5.30.0 → 5.32.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/CHANGELOG.md CHANGED
@@ -4,6 +4,30 @@ All notable changes to `@warlock.js/ai-groq` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.32.0 - 2026-10-18
8
+
9
+ ### Changed
10
+
11
+ - Pin the package development compiler to TypeScript 7.0.2 and use explicit Node types and source roots with supported module resolution.
12
+
13
+ ## 5.31.0 - 2026-10-18
14
+
15
+ ### Added
16
+
17
+ - An adapter contract test verifies that aborting `signal` rejects `complete()` and `stream()` on the model and cancels the HTTP request.
18
+
19
+ ### Changed
20
+
21
+ - Chat calls inherit `providerOptions.openai.body` from the OpenAI adapter, including its protected fields and one-level plain-object merge. Use only this provider's accepted fields when sharing fallback options. Native `countTokens` remains absent; `sdk.count(text)` is an offline estimate.
22
+
23
+ ### Fixed
24
+
25
+ - `GROQ_DEFAULT_PRICING` (now exported) matches Groq's published prices (2026-10-17): `openai/gpt-oss-120b` output is 0.60, the gpt-oss models get their 50% cache-read rate, and `qwen/qwen3.8-27b` is added.
26
+
27
+ ### Upgrading
28
+
29
+ - **BREAKING** `llama-3.3-70b-versatile`, `llama-3.1-8b-instant` and `deepseek-r1-distill-llama-70b` no longer have built-in prices (Groq publishes none), so a USD budget on them rejects the run as unpriced. Pass `pricing` for them.
30
+
7
31
  ## 5.30.0 - 2026-10-16
8
32
 
9
33
  ### Changed
@@ -81,6 +105,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
81
105
  ### Changed
82
106
 
83
107
  - Lockstep release maintenance and dependency refresh.
108
+
84
109
  ## 5.20.0 - 2026-09-24
85
110
 
86
111
  ### Changed
package/README.md CHANGED
@@ -51,12 +51,12 @@ GroqModelConfig {
51
51
 
52
52
  A curated (not exhaustive, not an allow-list) set of current production ids is exported as `GROQ_KNOWN_MODELS`:
53
53
 
54
- | Model id | Notes |
55
- | --- | --- |
56
- | `llama-3.3-70b-versatile` | Flagship general-purpose text |
57
- | `llama-3.1-8b-instant` | Fastest / cheapest small text |
54
+ | Model id | Notes |
55
+ | -------------------------------------------- | ------------------------------------------------------ |
56
+ | `llama-3.3-70b-versatile` | Flagship general-purpose text |
57
+ | `llama-3.1-8b-instant` | Fastest / cheapest small text |
58
58
  | `openai/gpt-oss-120b` / `openai/gpt-oss-20b` | OpenAI open-weight family — vision + reasoning capable |
59
- | `deepseek-r1-distill-llama-70b` | DeepSeek-R1-style reasoning |
59
+ | `deepseek-r1-distill-llama-70b` | DeepSeek-R1-style reasoning |
60
60
 
61
61
  `name` is the upstream open-weight id (e.g. `openai/gpt-oss-120b`), NOT an OpenAI id. `groq.model()` accepts any string, so a newly launched Groq id works the moment Groq ships it.
62
62
 
@@ -64,23 +64,23 @@ A curated (not exhaustive, not an allow-list) set of current production ids is e
64
64
 
65
65
  The whole reason this wrapper exists: Groq's ids aren't OpenAI's, so the OpenAI adapter's prefix inference would never fire. `GroqSDK` carries its **own** name lists and injects the result as explicit capability config, which wins over the inner adapter's inference. An explicit caller value always wins.
66
66
 
67
- | Capability | Default |
68
- | --- | --- |
69
- | `vision` | Inferred from the Groq id — `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`). |
70
- | `reasoning` | Inferred from the Groq id — `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning.effort` maps to `reasoning_effort` on the wire. |
71
- | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
67
+ | Capability | Default |
68
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
69
+ | `vision` | Inferred from the Groq id — `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`). |
70
+ | `reasoning` | Inferred from the Groq id — `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning.effort` maps to `reasoning_effort` on the wire. |
71
+ | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
72
72
 
73
73
  ```ts
74
- groq.model({ name: "openai/gpt-oss-120b" }); // vision + reasoning auto-true
75
- groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
76
- groq.model({ name: "some-custom-llama", vision: true }); // explicit override
74
+ groq.model({ name: "openai/gpt-oss-120b" }); // vision + reasoning auto-true
75
+ groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
76
+ groq.model({ name: "some-custom-llama", vision: true }); // explicit override
77
77
  ```
78
78
 
79
79
  Reasoning models accept a discrete effort knob forwarded by the inner adapter as OpenAI's `reasoning_effort`:
80
80
 
81
81
  ```ts
82
82
  const model = groq.model({ name: "deepseek-r1-distill-llama-70b" });
83
- await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
83
+ await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
84
84
  ```
85
85
 
86
86
  ## Embeddings — not available on Groq
package/cjs/index.cjs CHANGED
@@ -232,47 +232,50 @@ var GroqSDK = class {
232
232
  }
233
233
  };
234
234
  /**
235
- * Built-in default USD-per-1,000,000-token rates for the current Groq
236
- * production models (mid-2026), used as the last fallback in `model()`'s
237
- * pricing resolution. A per-model or SDK-level `pricing` entry always
238
- * wins over these. Returns `undefined` for any unlisted id so cost stays
239
- * an honest absence rather than a false zero. Update when Groq revises
240
- * its public price list.
235
+ * Built-in default USD-per-1,000,000-token rates for the Groq models with
236
+ * a published price, used as the last fallback in `model()`'s pricing
237
+ * resolution. A per-model or SDK-level `pricing` entry always wins over
238
+ * these. Returns `undefined` for any unlisted id so cost stays an honest
239
+ * absence rather than a false zero.
241
240
  */
242
241
  function defaultPricingFor(name) {
243
242
  return GROQ_DEFAULT_PRICING[name];
244
243
  }
245
244
  /**
246
- * Default per-million-token USD rates for known Groq models. Kept inline
247
- * (not exported) so the public surface stays the inference helpers; a
248
- * caller who wants different numbers supplies `pricing` on the SDK or
249
- * per model.
245
+ * Default per-million-token USD rates for Groq models.
246
+ *
247
+ * Source: https://console.groq.com/docs/models (groq.com/pricing now
248
+ * redirects to the home page), fetched 2026-10-17. Cached input is 50%
249
+ * of the input rate for the gpt-oss models, the only ones Groq caches
250
+ * (https://console.groq.com/docs/prompt-caching; no write charge). The
251
+ * Llama models are listed as "Enterprise – Contact Sales" with no public
252
+ * price, so they stay unpriced — pass `pricing` for them.
250
253
  */
251
254
  const GROQ_DEFAULT_PRICING = {
252
- "llama-3.3-70b-versatile": {
253
- input: .59,
254
- output: .79
255
- },
256
- "llama-3.1-8b-instant": {
257
- input: .05,
258
- output: .08
259
- },
260
255
  "openai/gpt-oss-120b": {
261
256
  input: .15,
262
- output: .75
257
+ output: .6,
258
+ cachedInput: .075
263
259
  },
264
260
  "openai/gpt-oss-20b": {
265
- input: .1,
266
- output: .5
261
+ input: .075,
262
+ output: .3,
263
+ cachedInput: .0375
264
+ },
265
+ "openai/gpt-oss-safeguard-20b": {
266
+ input: .075,
267
+ output: .3,
268
+ cachedInput: .0375
267
269
  },
268
- "deepseek-r1-distill-llama-70b": {
269
- input: .75,
270
- output: .99
270
+ "qwen/qwen3.8-27b": {
271
+ input: .8,
272
+ output: 4
271
273
  }
272
274
  };
273
275
 
274
276
  //#endregion
275
277
  exports.GROQ_BASE_URL = GROQ_BASE_URL;
278
+ exports.GROQ_DEFAULT_PRICING = GROQ_DEFAULT_PRICING;
276
279
  exports.GROQ_KNOWN_MODELS = GROQ_KNOWN_MODELS;
277
280
  exports.GROQ_PROVIDER = GROQ_PROVIDER;
278
281
  exports.GroqSDK = GroqSDK;
package/cjs/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["OpenAISDK"],"sources":["../../../../../../../packages/ai-groq/src/known-models.ts","../../../../../../../packages/ai-groq/src/sdk.ts"],"sourcesContent":["/**\n * Default base URL for Groq's OpenAI-compatible Chat Completions\n * endpoint. Groq exposes the same wire protocol as OpenAI under this\n * prefix, which is exactly why `GroqSDK` can delegate to the\n * battle-tested `OpenAISDK` instead of reimplementing the transport.\n *\n * Verified against Groq's \"OpenAI Compatibility\" docs (mid-2026).\n * Override via `new GroqSDK({ baseURL })` if Groq ever relocates it.\n */\nexport const GROQ_BASE_URL = \"https://api.groq.com/openai/v1\";\n\n/**\n * The provider label every model produced by `GroqSDK` self-identifies\n * with. Flows through to `ModelContract.provider`, `AgentReport.model`,\n * logs, and any provider-aware middleware. Kept as a constant so the\n * wrapper and the inner `OpenAISDK` agree on one spelling.\n */\nexport const GROQ_PROVIDER = \"groq\";\n\n/**\n * Substrings identifying Groq-hosted model ids whose family accepts\n * image input (vision).\n *\n * Groq hosts *open* models on its LPU hardware, so the ids are the\n * upstream open-weight names rather than OpenAI's — which is why the\n * OpenAI adapter's `gpt-4o*` prefix inference would never fire here and\n * this provider must carry its OWN list. Verified against Groq's\n * supported-models catalog (mid-2026):\n *\n * - `openai/gpt-oss-*` — natively multimodal OpenAI open-weight family\n * (the current production vision + reasoning models on Groq).\n * - `llama-4` / `llama-3.2-*-vision` — Meta's multimodal Llama families.\n * Kept as substrings so any still-hosted or re-introduced variant is\n * covered even though some `llama-4` ids were deprecated in 2026.\n *\n * Matched as a substring (not a prefix) because Groq prefixes several\n * ids with an org segment (`openai/`, `meta-llama/`) and appends size /\n * date suffixes. Override per-model via\n * `groq.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"gpt-oss\",\n \"llama-4\",\n \"llama-3.2-11b-vision\",\n \"llama-3.2-90b-vision\",\n \"vision\",\n];\n\n/**\n * Substrings identifying Groq-hosted model ids that expose an internal\n * reasoning / thinking channel and accept the `reasoning_effort`\n * request parameter on the OpenAI-compatible Chat Completions endpoint.\n *\n * Verified against Groq's catalog (mid-2026):\n * - `gpt-oss` — the OpenAI open-weight family reasons by default.\n * - `deepseek-r1-distill` — DeepSeek-R1-style reasoning at Groq speed.\n * - `qwq` / `qwen3` — Qwen reasoning families (when hosted).\n *\n * Matched as a substring for the same org-prefix / suffix reason as the\n * vision list. Override per-model via\n * `groq.model({ name, reasoning: true | false })`.\n */\nconst REASONING_CAPABLE_SUBSTRINGS = [\n \"gpt-oss\",\n \"deepseek-r1\",\n \"deepseek-r1-distill\",\n \"qwq\",\n \"qwen3\",\n];\n\n/**\n * Infer whether a Groq-hosted model id supports vision based on the\n * known multimodal-family substrings. Unknown ids default to `false`\n * so passing an image attachment to a text-only model (e.g.\n * `llama-3.3-70b-versatile`) surfaces a clear, agent-side capability\n * error instead of an opaque upstream 400.\n *\n * @example\n * inferVisionCapability(\"openai/gpt-oss-120b\"); // → true\n * inferVisionCapability(\"meta-llama/llama-4-scout\"); // → true\n * inferVisionCapability(\"llama-3.3-70b-versatile\"); // → false\n * inferVisionCapability(\"llama-3.1-8b-instant\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Infer whether a Groq-hosted model id is a reasoning model based on\n * the known reasoning-family substrings. Unknown ids default to `false`\n * so the adapter never forwards an unsupported `reasoning_effort` param\n * to a non-reasoning model (which would 400).\n *\n * @example\n * inferReasoningCapability(\"openai/gpt-oss-20b\"); // → true\n * inferReasoningCapability(\"deepseek-r1-distill-llama-70b\"); // → true\n * inferReasoningCapability(\"llama-3.3-70b-versatile\"); // → false\n */\nexport function inferReasoningCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return REASONING_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Curated list of current Groq production chat model ids (mid-2026),\n * for autocomplete, docs, and default selection. NOT exhaustive and NOT\n * a runtime allow-list — `groq.model({ name })` accepts any string, so\n * a newly launched id works the moment Groq ships it without a package\n * bump. Deprecated ids (e.g. several `llama-4` variants retired in 2026)\n * are intentionally excluded.\n *\n * - `llama-3.3-70b-versatile` — flagship general-purpose text model.\n * - `llama-3.1-8b-instant` — fastest/cheapest small text model.\n * - `openai/gpt-oss-120b` / `openai/gpt-oss-20b` — OpenAI open-weight\n * family; vision + reasoning capable.\n * - `deepseek-r1-distill-llama-70b` — DeepSeek-R1-style reasoning.\n */\nexport const GROQ_KNOWN_MODELS = [\n \"llama-3.3-70b-versatile\",\n \"llama-3.1-8b-instant\",\n \"openai/gpt-oss-120b\",\n \"openai/gpt-oss-20b\",\n \"deepseek-r1-distill-llama-70b\",\n] as const;\n","import type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { OpenAISDK } from \"@warlock.js/ai-openai\";\nimport type {\n GroqEmbedderConfig,\n GroqModelConfig,\n GroqSDKConfig,\n} from \"./config.type\";\nimport {\n GROQ_BASE_URL,\n GROQ_PROVIDER,\n inferReasoningCapability,\n inferVisionCapability,\n} from \"./known-models\";\n\n/**\n * Groq-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Groq-hosted open models\n * (`llama-3.3-70b-versatile`, `llama-3.1-8b-instant`,\n * `openai/gpt-oss-*`, `deepseek-r1-distill-*`, …) served on Groq's fast\n * LPU hardware. Because Groq exposes the OpenAI Chat Completions wire\n * protocol verbatim, `GroqSDK` is a **thin wrapper over the already\n * battle-tested {@link OpenAISDK}** rather than a fresh transport\n * implementation — it owns one internal `OpenAISDK` pointed at Groq's\n * `baseURL` with the `\"groq\"` provider label, and delegates `model()` /\n * `embedder()` / `count()` to it.\n *\n * **Why a wrapper and not `OpenAISDK` directly?** Groq hosts *open*\n * models whose ids are the upstream open-weight names (`llama-…`,\n * `openai/gpt-oss-…`), not OpenAI's (`gpt-4o`, `o3`). The OpenAI\n * adapter's capability inference keys on OpenAI prefixes and would never\n * fire here, so every Groq model would come back with `vision`/\n * `reasoning` silently `false`. `GroqSDK` fixes that by computing\n * **this provider's own** vision/reasoning inference (see\n * `known-models.ts`) and injecting the result as explicit capability\n * config — which wins over the inner adapter's inference — plus a set of\n * default per-model pricing rates. Everything else (transport, retries,\n * streaming, structured output, error wrapping, usage accounting) is\n * inherited unchanged from `OpenAISDK`.\n *\n * **Responsibility.**\n * - Owns: one long-lived internal `OpenAISDK` (auth + Groq base URL) and\n * this provider's capability + default-pricing inference.\n * - Does NOT own: the wire protocol, streaming loop, structured-output\n * mapping, or error wrapping — all inherited from `OpenAISDK`.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });\n * const model = groq.model({ name: \"llama-3.3-70b-versatile\", temperature: 0.7 });\n * const reasoner = groq.model({ name: \"openai/gpt-oss-120b\" }); // vision + reasoning auto-true\n *\n * @example\n * // Compose into an `ai.groq` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, groq: new GroqSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.groq.model({ name: \"llama-3.1-8b-instant\" }) });\n */\nexport class GroqSDK implements SDKAdapterContract {\n /**\n * The wrapped OpenAI-compatible adapter, pre-pointed at Groq's\n * `baseURL` and labeled with the `\"groq\"` provider. All real wire work\n * happens here; `GroqSDK` only enriches the per-model config before\n * delegating.\n */\n private readonly openai: OpenAISDK;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GroqSDKConfig) {\n const { baseURL, provider, pricing, ...clientOptions } = config;\n\n this.provider = provider ?? GROQ_PROVIDER;\n this.pricing = pricing;\n\n // Build the inner OpenAI-compatible client pointed at Groq. Forward\n // every other upstream ClientOptions (timeout, maxRetries,\n // defaultHeaders, fetch, …) verbatim — they type-check, so dropping\n // them would be a silent footgun. We pass `pricing` through too so\n // the inner registry stays a fallback, while THIS class's `model()`\n // layers Groq default rates on top.\n this.openai = new OpenAISDK({\n ...clientOptions,\n baseURL: baseURL ?? GROQ_BASE_URL,\n provider: this.provider,\n pricing,\n });\n }\n\n /**\n * Build a `ModelContract` bound to the internal Groq-pointed client.\n *\n * The wrapper's whole job lives here: it computes **Groq's** vision /\n * reasoning inference from the model id and forwards the result as\n * *explicit* `vision` / `reasoning` config to the inner\n * `OpenAISDK.model()`. Because explicit capability config wins over the\n * inner adapter's OpenAI-prefix inference, the returned model reports\n * the right capabilities even though the id isn't an OpenAI name. A\n * caller-supplied `vision` / `reasoning` still wins over this\n * inference (we only fill the gap when the field is omitted).\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise this\n * SDK's `pricing` registry keyed by `config.name`; otherwise the\n * adapter's built-in default rate for the model; otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GroqModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? defaultPricingFor(config.name);\n\n return this.openai.model({\n ...config,\n // Fill capability inference only when the caller didn't pin it —\n // explicit caller config always wins, then Groq inference, then\n // (inside OpenAISDK) the harmless OpenAI inference that won't match.\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n structuredOutput: config.structuredOutput ?? true,\n pricing: resolvedPricing,\n });\n }\n\n /**\n * Rough offline token-count estimate. Delegated straight to the inner\n * `OpenAISDK`, which uses the core character-heuristic\n * (`approximateTokenCount`). Good for budgeting / quota guards, not for\n * billing.\n */\n public async count(text: string, model?: string): Promise<number> {\n return this.openai.count(text, model);\n }\n\n /**\n * Build an embedder bound to the internal client. Delegated to the\n * inner `OpenAISDK`.\n *\n * IMPORTANT: as of mid-2026 Groq does **not** expose an\n * OpenAI-compatible embeddings endpoint, so a live `.embed()` /\n * `.embedMany()` call will fail at the provider. The method is kept for\n * adapter symmetry; use `@warlock.js/ai-openai` or\n * `@warlock.js/ai-google` for retrieval embeddings.\n */\n public embedder(config: GroqEmbedderConfig): EmbedderContract {\n return this.openai.embedder(config);\n }\n\n // NOTE: `image()` is intentionally NOT implemented. Groq hosts no\n // image-generation API, and `SDKAdapterContract.image` is optional —\n // its structural absence IS the capability guard, so\n // `ai.groq.image(...)` is a compile-time error rather than a runtime\n // surprise (mirrors Anthropic / Bedrock / Ollama).\n}\n\n/**\n * Built-in default USD-per-1,000,000-token rates for the current Groq\n * production models (mid-2026), used as the last fallback in `model()`'s\n * pricing resolution. A per-model or SDK-level `pricing` entry always\n * wins over these. Returns `undefined` for any unlisted id so cost stays\n * an honest absence rather than a false zero. Update when Groq revises\n * its public price list.\n */\nfunction defaultPricingFor(name: string): ModelPricing | undefined {\n return GROQ_DEFAULT_PRICING[name];\n}\n\n/**\n * Default per-million-token USD rates for known Groq models. Kept inline\n * (not exported) so the public surface stays the inference helpers; a\n * caller who wants different numbers supplies `pricing` on the SDK or\n * per model.\n */\nconst GROQ_DEFAULT_PRICING: Record<string, ModelPricing> = {\n \"llama-3.3-70b-versatile\": { input: 0.59, output: 0.79 },\n \"llama-3.1-8b-instant\": { input: 0.05, output: 0.08 },\n \"openai/gpt-oss-120b\": { input: 0.15, output: 0.75 },\n \"openai/gpt-oss-20b\": { input: 0.1, output: 0.5 },\n \"deepseek-r1-distill-llama-70b\": { input: 0.75, output: 0.99 },\n};\n"],"mappings":";;;;;;;;;;;;;AASA,MAAa,gBAAgB;;;;;;;AAQ7B,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;AAuB7B,MAAM,4BAA4B;CAChC;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;AAgBA,MAAM,+BAA+B;CACnC;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;AAeA,SAAgB,sBAAsB,SAA0B;CAC9D,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,0BAA0B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACnF;;;;;;;;;;;;AAaA,SAAgB,yBAAyB,SAA0B;CACjE,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,6BAA6B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACtF;;;;;;;;;;;;;;;AAgBA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9DA,IAAa,UAAb,MAAmD;CAWjD,AAAO,YAAY,QAAuB;EACxC,MAAM,EAAE,SAAS,UAAU,SAAS,GAAG,kBAAkB;EAEzD,KAAK,WAAW;EAChB,KAAK,UAAU;EAQf,KAAK,SAAS,IAAIA,gCAAU;GAC1B,GAAG;GACH,SAAS;GACT,UAAU,KAAK;GACf;EACF,CAAC;CACH;;;;;;;;;;;;;;;;;;CAmBA,AAAO,MAAM,QAAwC;EACnD,MAAM,kBACJ,OAAO,WAAW,KAAK,UAAU,OAAO,SAAS,kBAAkB,OAAO,IAAI;EAEhF,OAAO,KAAK,OAAO,MAAM;GACvB,GAAG;GAIH,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAC1D,WAAW,OAAO,aAAa,yBAAyB,OAAO,IAAI;GACnE,kBAAkB,OAAO,oBAAoB;GAC7C,SAAS;EACX,CAAC;CACH;;;;;;;CAQA,MAAa,MAAM,MAAc,OAAiC;EAChE,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK;CACtC;;;;;;;;;;;CAYA,AAAO,SAAS,QAA8C;EAC5D,OAAO,KAAK,OAAO,SAAS,MAAM;CACpC;AAOF;;;;;;;;;AAUA,SAAS,kBAAkB,MAAwC;CACjE,OAAO,qBAAqB;AAC9B;;;;;;;AAQA,MAAM,uBAAqD;CACzD,2BAA2B;EAAE,OAAO;EAAM,QAAQ;CAAK;CACvD,wBAAwB;EAAE,OAAO;EAAM,QAAQ;CAAK;CACpD,uBAAuB;EAAE,OAAO;EAAM,QAAQ;CAAK;CACnD,sBAAsB;EAAE,OAAO;EAAK,QAAQ;CAAI;CAChD,iCAAiC;EAAE,OAAO;EAAM,QAAQ;CAAK;AAC/D"}
1
+ {"version":3,"file":"index.cjs","names":["OpenAISDK"],"sources":["../../../../../../../packages/ai-groq/src/known-models.ts","../../../../../../../packages/ai-groq/src/sdk.ts"],"sourcesContent":["/**\n * Default base URL for Groq's OpenAI-compatible Chat Completions\n * endpoint. Groq exposes the same wire protocol as OpenAI under this\n * prefix, which is exactly why `GroqSDK` can delegate to the\n * battle-tested `OpenAISDK` instead of reimplementing the transport.\n *\n * Verified against Groq's \"OpenAI Compatibility\" docs (mid-2026).\n * Override via `new GroqSDK({ baseURL })` if Groq ever relocates it.\n */\nexport const GROQ_BASE_URL = \"https://api.groq.com/openai/v1\";\n\n/**\n * The provider label every model produced by `GroqSDK` self-identifies\n * with. Flows through to `ModelContract.provider`, `AgentReport.model`,\n * logs, and any provider-aware middleware. Kept as a constant so the\n * wrapper and the inner `OpenAISDK` agree on one spelling.\n */\nexport const GROQ_PROVIDER = \"groq\";\n\n/**\n * Substrings identifying Groq-hosted model ids whose family accepts\n * image input (vision).\n *\n * Groq hosts *open* models on its LPU hardware, so the ids are the\n * upstream open-weight names rather than OpenAI's — which is why the\n * OpenAI adapter's `gpt-4o*` prefix inference would never fire here and\n * this provider must carry its OWN list. Verified against Groq's\n * supported-models catalog (mid-2026):\n *\n * - `openai/gpt-oss-*` — natively multimodal OpenAI open-weight family\n * (the current production vision + reasoning models on Groq).\n * - `llama-4` / `llama-3.2-*-vision` — Meta's multimodal Llama families.\n * Kept as substrings so any still-hosted or re-introduced variant is\n * covered even though some `llama-4` ids were deprecated in 2026.\n *\n * Matched as a substring (not a prefix) because Groq prefixes several\n * ids with an org segment (`openai/`, `meta-llama/`) and appends size /\n * date suffixes. Override per-model via\n * `groq.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"gpt-oss\",\n \"llama-4\",\n \"llama-3.2-11b-vision\",\n \"llama-3.2-90b-vision\",\n \"vision\",\n];\n\n/**\n * Substrings identifying Groq-hosted model ids that expose an internal\n * reasoning / thinking channel and accept the `reasoning_effort`\n * request parameter on the OpenAI-compatible Chat Completions endpoint.\n *\n * Verified against Groq's catalog (mid-2026):\n * - `gpt-oss` — the OpenAI open-weight family reasons by default.\n * - `deepseek-r1-distill` — DeepSeek-R1-style reasoning at Groq speed.\n * - `qwq` / `qwen3` — Qwen reasoning families (when hosted).\n *\n * Matched as a substring for the same org-prefix / suffix reason as the\n * vision list. Override per-model via\n * `groq.model({ name, reasoning: true | false })`.\n */\nconst REASONING_CAPABLE_SUBSTRINGS = [\n \"gpt-oss\",\n \"deepseek-r1\",\n \"deepseek-r1-distill\",\n \"qwq\",\n \"qwen3\",\n];\n\n/**\n * Infer whether a Groq-hosted model id supports vision based on the\n * known multimodal-family substrings. Unknown ids default to `false`\n * so passing an image attachment to a text-only model (e.g.\n * `llama-3.3-70b-versatile`) surfaces a clear, agent-side capability\n * error instead of an opaque upstream 400.\n *\n * @example\n * inferVisionCapability(\"openai/gpt-oss-120b\"); // → true\n * inferVisionCapability(\"meta-llama/llama-4-scout\"); // → true\n * inferVisionCapability(\"llama-3.3-70b-versatile\"); // → false\n * inferVisionCapability(\"llama-3.1-8b-instant\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Infer whether a Groq-hosted model id is a reasoning model based on\n * the known reasoning-family substrings. Unknown ids default to `false`\n * so the adapter never forwards an unsupported `reasoning_effort` param\n * to a non-reasoning model (which would 400).\n *\n * @example\n * inferReasoningCapability(\"openai/gpt-oss-20b\"); // → true\n * inferReasoningCapability(\"deepseek-r1-distill-llama-70b\"); // → true\n * inferReasoningCapability(\"llama-3.3-70b-versatile\"); // → false\n */\nexport function inferReasoningCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return REASONING_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Curated list of current Groq production chat model ids (mid-2026),\n * for autocomplete, docs, and default selection. NOT exhaustive and NOT\n * a runtime allow-list — `groq.model({ name })` accepts any string, so\n * a newly launched id works the moment Groq ships it without a package\n * bump. Deprecated ids (e.g. several `llama-4` variants retired in 2026)\n * are intentionally excluded.\n *\n * - `llama-3.3-70b-versatile` — flagship general-purpose text model.\n * - `llama-3.1-8b-instant` — fastest/cheapest small text model.\n * - `openai/gpt-oss-120b` / `openai/gpt-oss-20b` — OpenAI open-weight\n * family; vision + reasoning capable.\n * - `deepseek-r1-distill-llama-70b` — DeepSeek-R1-style reasoning.\n */\nexport const GROQ_KNOWN_MODELS = [\n \"llama-3.3-70b-versatile\",\n \"llama-3.1-8b-instant\",\n \"openai/gpt-oss-120b\",\n \"openai/gpt-oss-20b\",\n \"deepseek-r1-distill-llama-70b\",\n] as const;\n","import type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { OpenAISDK } from \"@warlock.js/ai-openai\";\nimport type { GroqEmbedderConfig, GroqModelConfig, GroqSDKConfig } from \"./config.type\";\nimport {\n GROQ_BASE_URL,\n GROQ_PROVIDER,\n inferReasoningCapability,\n inferVisionCapability,\n} from \"./known-models\";\n\n/**\n * Groq-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Groq-hosted open models\n * (`llama-3.3-70b-versatile`, `llama-3.1-8b-instant`,\n * `openai/gpt-oss-*`, `deepseek-r1-distill-*`, …) served on Groq's fast\n * LPU hardware. Because Groq exposes the OpenAI Chat Completions wire\n * protocol verbatim, `GroqSDK` is a **thin wrapper over the already\n * battle-tested {@link OpenAISDK}** rather than a fresh transport\n * implementation — it owns one internal `OpenAISDK` pointed at Groq's\n * `baseURL` with the `\"groq\"` provider label, and delegates `model()` /\n * `embedder()` / `count()` to it.\n *\n * **Why a wrapper and not `OpenAISDK` directly?** Groq hosts *open*\n * models whose ids are the upstream open-weight names (`llama-…`,\n * `openai/gpt-oss-…`), not OpenAI's (`gpt-4o`, `o3`). The OpenAI\n * adapter's capability inference keys on OpenAI prefixes and would never\n * fire here, so every Groq model would come back with `vision`/\n * `reasoning` silently `false`. `GroqSDK` fixes that by computing\n * **this provider's own** vision/reasoning inference (see\n * `known-models.ts`) and injecting the result as explicit capability\n * config — which wins over the inner adapter's inference — plus a set of\n * default per-model pricing rates. Everything else (transport, retries,\n * streaming, structured output, error wrapping, usage accounting) is\n * inherited unchanged from `OpenAISDK`.\n *\n * **Responsibility.**\n * - Owns: one long-lived internal `OpenAISDK` (auth + Groq base URL) and\n * this provider's capability + default-pricing inference.\n * - Does NOT own: the wire protocol, streaming loop, structured-output\n * mapping, or error wrapping — all inherited from `OpenAISDK`.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });\n * const model = groq.model({ name: \"llama-3.3-70b-versatile\", temperature: 0.7 });\n * const reasoner = groq.model({ name: \"openai/gpt-oss-120b\" }); // vision + reasoning auto-true\n *\n * @example\n * // Compose into an `ai.groq` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, groq: new GroqSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.groq.model({ name: \"llama-3.1-8b-instant\" }) });\n */\nexport class GroqSDK implements SDKAdapterContract {\n /**\n * The wrapped OpenAI-compatible adapter, pre-pointed at Groq's\n * `baseURL` and labeled with the `\"groq\"` provider. All real wire work\n * happens here; `GroqSDK` only enriches the per-model config before\n * delegating.\n */\n private readonly openai: OpenAISDK;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GroqSDKConfig) {\n const { baseURL, provider, pricing, ...clientOptions } = config;\n\n this.provider = provider ?? GROQ_PROVIDER;\n this.pricing = pricing;\n\n // Build the inner OpenAI-compatible client pointed at Groq. Forward\n // every other upstream ClientOptions (timeout, maxRetries,\n // defaultHeaders, fetch, …) verbatim — they type-check, so dropping\n // them would be a silent footgun. We pass `pricing` through too so\n // the inner registry stays a fallback, while THIS class's `model()`\n // layers Groq default rates on top.\n this.openai = new OpenAISDK({\n ...clientOptions,\n baseURL: baseURL ?? GROQ_BASE_URL,\n provider: this.provider,\n pricing,\n });\n }\n\n /**\n * Build a `ModelContract` bound to the internal Groq-pointed client.\n *\n * The wrapper's whole job lives here: it computes **Groq's** vision /\n * reasoning inference from the model id and forwards the result as\n * *explicit* `vision` / `reasoning` config to the inner\n * `OpenAISDK.model()`. Because explicit capability config wins over the\n * inner adapter's OpenAI-prefix inference, the returned model reports\n * the right capabilities even though the id isn't an OpenAI name. A\n * caller-supplied `vision` / `reasoning` still wins over this\n * inference (we only fill the gap when the field is omitted).\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise this\n * SDK's `pricing` registry keyed by `config.name`; otherwise the\n * adapter's built-in default rate for the model; otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GroqModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? defaultPricingFor(config.name);\n\n return this.openai.model({\n ...config,\n // Fill capability inference only when the caller didn't pin it —\n // explicit caller config always wins, then Groq inference, then\n // (inside OpenAISDK) the harmless OpenAI inference that won't match.\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n structuredOutput: config.structuredOutput ?? true,\n pricing: resolvedPricing,\n });\n }\n\n /**\n * Rough offline token-count estimate. Delegated straight to the inner\n * `OpenAISDK`, which uses the core character-heuristic\n * (`approximateTokenCount`). Good for budgeting / quota guards, not for\n * billing.\n */\n public async count(text: string, model?: string): Promise<number> {\n return this.openai.count(text, model);\n }\n\n /**\n * Build an embedder bound to the internal client. Delegated to the\n * inner `OpenAISDK`.\n *\n * IMPORTANT: as of mid-2026 Groq does **not** expose an\n * OpenAI-compatible embeddings endpoint, so a live `.embed()` /\n * `.embedMany()` call will fail at the provider. The method is kept for\n * adapter symmetry; use `@warlock.js/ai-openai` or\n * `@warlock.js/ai-google` for retrieval embeddings.\n */\n public embedder(config: GroqEmbedderConfig): EmbedderContract {\n return this.openai.embedder(config);\n }\n\n // NOTE: `image()` is intentionally NOT implemented. Groq hosts no\n // image-generation API, and `SDKAdapterContract.image` is optional —\n // its structural absence IS the capability guard, so\n // `ai.groq.image(...)` is a compile-time error rather than a runtime\n // surprise (mirrors Anthropic / Bedrock / Ollama).\n}\n\n/**\n * Built-in default USD-per-1,000,000-token rates for the Groq models with\n * a published price, used as the last fallback in `model()`'s pricing\n * resolution. A per-model or SDK-level `pricing` entry always wins over\n * these. Returns `undefined` for any unlisted id so cost stays an honest\n * absence rather than a false zero.\n */\nfunction defaultPricingFor(name: string): ModelPricing | undefined {\n return GROQ_DEFAULT_PRICING[name];\n}\n\n/**\n * Default per-million-token USD rates for Groq models.\n *\n * Source: https://console.groq.com/docs/models (groq.com/pricing now\n * redirects to the home page), fetched 2026-10-17. Cached input is 50%\n * of the input rate for the gpt-oss models, the only ones Groq caches\n * (https://console.groq.com/docs/prompt-caching; no write charge). The\n * Llama models are listed as \"Enterprise – Contact Sales\" with no public\n * price, so they stay unpriced — pass `pricing` for them.\n */\nexport const GROQ_DEFAULT_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"openai/gpt-oss-120b\": { input: 0.15, output: 0.6, cachedInput: 0.075 },\n \"openai/gpt-oss-20b\": { input: 0.075, output: 0.3, cachedInput: 0.0375 },\n \"openai/gpt-oss-safeguard-20b\": { input: 0.075, output: 0.3, cachedInput: 0.0375 },\n \"qwen/qwen3.8-27b\": { input: 0.8, output: 4 },\n};\n"],"mappings":";;;;;;;;;;;;;AASA,MAAa,gBAAgB;;;;;;;AAQ7B,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;AAuB7B,MAAM,4BAA4B;CAChC;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;AAgBA,MAAM,+BAA+B;CACnC;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;AAeA,SAAgB,sBAAsB,SAA0B;CAC9D,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,0BAA0B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACnF;;;;;;;;;;;;AAaA,SAAgB,yBAAyB,SAA0B;CACjE,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,6BAA6B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACtF;;;;;;;;;;;;;;;AAgBA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClEA,IAAa,UAAb,MAAmD;CAWjD,AAAO,YAAY,QAAuB;EACxC,MAAM,EAAE,SAAS,UAAU,SAAS,GAAG,kBAAkB;EAEzD,KAAK,WAAW;EAChB,KAAK,UAAU;EAQf,KAAK,SAAS,IAAIA,gCAAU;GAC1B,GAAG;GACH,SAAS;GACT,UAAU,KAAK;GACf;EACF,CAAC;CACH;;;;;;;;;;;;;;;;;;CAmBA,AAAO,MAAM,QAAwC;EACnD,MAAM,kBACJ,OAAO,WAAW,KAAK,UAAU,OAAO,SAAS,kBAAkB,OAAO,IAAI;EAEhF,OAAO,KAAK,OAAO,MAAM;GACvB,GAAG;GAIH,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAC1D,WAAW,OAAO,aAAa,yBAAyB,OAAO,IAAI;GACnE,kBAAkB,OAAO,oBAAoB;GAC7C,SAAS;EACX,CAAC;CACH;;;;;;;CAQA,MAAa,MAAM,MAAc,OAAiC;EAChE,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK;CACtC;;;;;;;;;;;CAYA,AAAO,SAAS,QAA8C;EAC5D,OAAO,KAAK,OAAO,SAAS,MAAM;CACpC;AAOF;;;;;;;;AASA,SAAS,kBAAkB,MAAwC;CACjE,OAAO,qBAAqB;AAC9B;;;;;;;;;;;AAYA,MAAa,uBAA+D;CAC1E,uBAAuB;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CACtE,sBAAsB;EAAE,OAAO;EAAO,QAAQ;EAAK,aAAa;CAAO;CACvE,gCAAgC;EAAE,OAAO;EAAO,QAAQ;EAAK,aAAa;CAAO;CACjF,oBAAoB;EAAE,OAAO;EAAK,QAAQ;CAAE;AAC9C"}
@@ -1,6 +1,5 @@
1
1
  import { OpenAISDKConfig } from "@warlock.js/ai-openai";
2
2
  import { EmbedderConfig, ModelConfig, ModelPricing } from "@warlock.js/ai";
3
-
4
3
  //#region ../../packages/ai-groq/src/config.type.d.ts
5
4
  /**
6
5
  * Configuration for the Groq SDK adapter.
@@ -37,7 +36,7 @@ import { EmbedderConfig, ModelConfig, ModelPricing } from "@warlock.js/ai";
37
36
  * pricing: { "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 } },
38
37
  * });
39
38
  */
40
- type GroqSDKConfig = Omit<OpenAISDKConfig, "baseURL" | "provider"> & {
39
+ export type GroqSDKConfig = Omit<OpenAISDKConfig, "baseURL" | "provider"> & {
41
40
  /**
42
41
  * Override Groq's OpenAI-compatible endpoint. Defaults to
43
42
  * `https://api.groq.com/openai/v1` when omitted — set this only to
@@ -75,7 +74,7 @@ type GroqSDKConfig = Omit<OpenAISDKConfig, "baseURL" | "provider"> & {
75
74
  * groq.model({ name: "openai/gpt-oss-120b" }); // vision + reasoning auto-true
76
75
  * groq.model({ name: "custom-llama", vision: true }); // explicit override
77
76
  */
78
- type GroqModelConfig = ModelConfig & {
77
+ export type GroqModelConfig = ModelConfig & {
79
78
  /**
80
79
  * Override the auto-inferred vision capability. When omitted, the
81
80
  * adapter checks the model id against Groq's known multimodal families
@@ -116,7 +115,6 @@ type GroqModelConfig = ModelConfig & {
116
115
  * // Will construct, but Groq currently has no embeddings model to call.
117
116
  * groq.embedder({ name: "some-future-embeddings-model" });
118
117
  */
119
- type GroqEmbedderConfig = EmbedderConfig;
118
+ export type GroqEmbedderConfig = EmbedderConfig;
120
119
  //#endregion
121
- export { GroqEmbedderConfig, GroqModelConfig, GroqSDKConfig };
122
120
  //# sourceMappingURL=config.type.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/config.type.ts"],"mappings":";;;;;;AAsCA;;;;;;;;;;;;;;;;;AAmBuC;AAoBvC;;;;;;;;;AAyBkB;AAiBlB;;;;AAA+C;KAjFnC,aAAA,GAAgB,IAAA,CAAK,eAAA;;;;;;EAM/B,OAAA;;;;;;EAMA,QAAA;;;;;;;EAOA,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBf,eAAA,GAAkB,WAAW;;;;;;;;EAQvC,MAAA;;;;;;;;;EASA,SAAA;;;;;;;;EAQA,gBAAA;AAAA;;;;;;;;;;;;;;;KAiBU,kBAAA,GAAqB,cAAc"}
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/config.type.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAsCY,gBAAgB,KAAK;;;;;;EAM/B;;;;;;EAMA;;;;;;;EAOA,UAAU,eAAe;;;;;;;;;;;;;;;;;;;YAoBf,kBAAkB;;;;;;;;EAQ5B;;;;;;;;;EASA;;;;;;;;EAQA;;;;;;;;;;;;;;;;YAiBU,qBAAqB"}
package/esm/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { GroqEmbedderConfig, GroqModelConfig, GroqSDKConfig } from "./config.type.mjs";
2
- import { GroqSDK } from "./sdk.mjs";
2
+ import { GROQ_DEFAULT_PRICING, GroqSDK } from "./sdk.mjs";
3
3
  import { GROQ_BASE_URL, GROQ_KNOWN_MODELS, GROQ_PROVIDER, inferReasoningCapability, inferVisionCapability } from "./known-models.mjs";
4
- export { GROQ_BASE_URL, GROQ_KNOWN_MODELS, GROQ_PROVIDER, type GroqEmbedderConfig, type GroqModelConfig, GroqSDK, type GroqSDKConfig, inferReasoningCapability, inferVisionCapability };
4
+ export { GROQ_BASE_URL, GROQ_DEFAULT_PRICING, GROQ_KNOWN_MODELS, GROQ_PROVIDER, type GroqEmbedderConfig, type GroqModelConfig, GroqSDK, type GroqSDKConfig, inferReasoningCapability, inferVisionCapability };
package/esm/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import { GROQ_BASE_URL, GROQ_KNOWN_MODELS, GROQ_PROVIDER, inferReasoningCapability, inferVisionCapability } from "./known-models.mjs";
2
- import { GroqSDK } from "./sdk.mjs";
2
+ import { GROQ_DEFAULT_PRICING, GroqSDK } from "./sdk.mjs";
3
3
 
4
- export { GROQ_BASE_URL, GROQ_KNOWN_MODELS, GROQ_PROVIDER, GroqSDK, inferReasoningCapability, inferVisionCapability };
4
+ export { GROQ_BASE_URL, GROQ_DEFAULT_PRICING, GROQ_KNOWN_MODELS, GROQ_PROVIDER, GroqSDK, inferReasoningCapability, inferVisionCapability };
@@ -8,14 +8,14 @@
8
8
  * Verified against Groq's "OpenAI Compatibility" docs (mid-2026).
9
9
  * Override via `new GroqSDK({ baseURL })` if Groq ever relocates it.
10
10
  */
11
- declare const GROQ_BASE_URL = "https://api.groq.com/openai/v1";
11
+ export declare const GROQ_BASE_URL = "https://api.groq.com/openai/v1";
12
12
  /**
13
13
  * The provider label every model produced by `GroqSDK` self-identifies
14
14
  * with. Flows through to `ModelContract.provider`, `AgentReport.model`,
15
15
  * logs, and any provider-aware middleware. Kept as a constant so the
16
16
  * wrapper and the inner `OpenAISDK` agree on one spelling.
17
17
  */
18
- declare const GROQ_PROVIDER = "groq";
18
+ export declare const GROQ_PROVIDER = "groq";
19
19
  /**
20
20
  * Infer whether a Groq-hosted model id supports vision based on the
21
21
  * known multimodal-family substrings. Unknown ids default to `false`
@@ -29,7 +29,7 @@ declare const GROQ_PROVIDER = "groq";
29
29
  * inferVisionCapability("llama-3.3-70b-versatile"); // → false
30
30
  * inferVisionCapability("llama-3.1-8b-instant"); // → false
31
31
  */
32
- declare function inferVisionCapability(modelId: string): boolean;
32
+ export declare function inferVisionCapability(modelId: string): boolean;
33
33
  /**
34
34
  * Infer whether a Groq-hosted model id is a reasoning model based on
35
35
  * the known reasoning-family substrings. Unknown ids default to `false`
@@ -41,7 +41,7 @@ declare function inferVisionCapability(modelId: string): boolean;
41
41
  * inferReasoningCapability("deepseek-r1-distill-llama-70b"); // → true
42
42
  * inferReasoningCapability("llama-3.3-70b-versatile"); // → false
43
43
  */
44
- declare function inferReasoningCapability(modelId: string): boolean;
44
+ export declare function inferReasoningCapability(modelId: string): boolean;
45
45
  /**
46
46
  * Curated list of current Groq production chat model ids (mid-2026),
47
47
  * for autocomplete, docs, and default selection. NOT exhaustive and NOT
@@ -56,7 +56,6 @@ declare function inferReasoningCapability(modelId: string): boolean;
56
56
  * family; vision + reasoning capable.
57
57
  * - `deepseek-r1-distill-llama-70b` — DeepSeek-R1-style reasoning.
58
58
  */
59
- declare const GROQ_KNOWN_MODELS: readonly ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "openai/gpt-oss-120b", "openai/gpt-oss-20b", "deepseek-r1-distill-llama-70b"];
59
+ export declare const GROQ_KNOWN_MODELS: readonly ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "openai/gpt-oss-120b", "openai/gpt-oss-20b", "deepseek-r1-distill-llama-70b"];
60
60
  //#endregion
61
- export { GROQ_BASE_URL, GROQ_KNOWN_MODELS, GROQ_PROVIDER, inferReasoningCapability, inferVisionCapability };
62
61
  //# sourceMappingURL=known-models.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"known-models.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/known-models.ts"],"mappings":";;AASA;;;;AAA0B;AAQ1B;;;cARa,aAAA;AAQa;AAkE1B;;;;AAAqD;AAlE3B,cAAb,aAAA;;;;AAmF2C;AAoBxD;;;;AAMU;;;;;iBA3CM,qBAAA,CAAsB,OAAe;;;;;;;;;;;;iBAiBrC,wBAAA,CAAyB,OAAe;;;;;;;;;;;;;;;cAoB3C,iBAAA"}
1
+ {"version":3,"file":"known-models.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/known-models.ts"],"mappings":";;;;;;;;;;qBASa;;;;;;;qBAQA;;;;;;;;;;;;;;wBAkEG,sBAAsB;;;;;;;;;;;;wBAiBtB,yBAAyB;;;;;;;;;;;;;;;qBAoB5B"}
package/esm/sdk.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { GroqEmbedderConfig, GroqModelConfig, GroqSDKConfig } from "./config.type.mjs";
2
- import { EmbedderContract, ModelContract, SDKAdapterContract } from "@warlock.js/ai";
3
-
2
+ import { EmbedderContract, ModelContract, ModelPricing, SDKAdapterContract } from "@warlock.js/ai";
4
3
  //#region ../../packages/ai-groq/src/sdk.d.ts
5
4
  /**
6
5
  * Groq-backed implementation of `SDKAdapterContract`.
@@ -47,7 +46,7 @@ import { EmbedderContract, ModelContract, SDKAdapterContract } from "@warlock.js
47
46
  * const ai = { agent, tool, systemPrompt, groq: new GroqSDK({ apiKey }) };
48
47
  * const myAgent = ai.agent({ model: ai.groq.model({ name: "llama-3.1-8b-instant" }) });
49
48
  */
50
- declare class GroqSDK implements SDKAdapterContract {
49
+ export declare class GroqSDK implements SDKAdapterContract {
51
50
  /**
52
51
  * The wrapped OpenAI-compatible adapter, pre-pointed at Groq's
53
52
  * `baseURL` and labeled with the `"groq"` provider. All real wire work
@@ -95,6 +94,16 @@ declare class GroqSDK implements SDKAdapterContract {
95
94
  */
96
95
  embedder(config: GroqEmbedderConfig): EmbedderContract;
97
96
  }
97
+ /**
98
+ * Default per-million-token USD rates for Groq models.
99
+ *
100
+ * Source: https://console.groq.com/docs/models (groq.com/pricing now
101
+ * redirects to the home page), fetched 2026-10-17. Cached input is 50%
102
+ * of the input rate for the gpt-oss models, the only ones Groq caches
103
+ * (https://console.groq.com/docs/prompt-caching; no write charge). The
104
+ * Llama models are listed as "Enterprise – Contact Sales" with no public
105
+ * price, so they stay unpriced — pass `pricing` for them.
106
+ */
107
+ export declare const GROQ_DEFAULT_PRICING: Readonly<Record<string, ModelPricing>>;
98
108
  //#endregion
99
- export { GroqSDK };
100
109
  //# sourceMappingURL=sdk.d.mts.map
package/esm/sdk.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/sdk.ts"],"mappings":";;;;;;AAgEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoF+D;;;;;;;;;;;cApFlD,OAAA,YAAmB,kBAAA;;;;;;;mBAOb,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,OAAA;cAEE,MAAA,EAAQ,aAAA;;;;;;;;;;;;;;;;;;EAqCpB,KAAA,CAAM,MAAA,EAAQ,eAAA,GAAkB,aAAA;;;;;;;EAsB1B,KAAA,CAAM,IAAA,UAAc,KAAA,YAAiB,OAAA;;;;;;;;;;;EAc3C,QAAA,CAAS,MAAA,EAAQ,kBAAA,GAAqB,gBAAA;AAAA"}
1
+ {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../../packages/ai-groq/src/sdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4Da,mBAAmB;;;;;;;mBAOb;mBACA;mBACA;EAEjB,YAAmB,QAAQ;;;;;;;;;;;;;;;;;;EAqCpB,MAAM,QAAQ,kBAAkB;;;;;;;EAsB1B,MAAM,cAAc,iBAAiB;;;;;;;;;;;EAc3C,SAAS,QAAQ,qBAAqB;;;;;;;;;;;;qBAgClC,sBAAsB,SAAS,eAAe"}
package/esm/sdk.mjs CHANGED
@@ -110,45 +110,47 @@ var GroqSDK = class {
110
110
  }
111
111
  };
112
112
  /**
113
- * Built-in default USD-per-1,000,000-token rates for the current Groq
114
- * production models (mid-2026), used as the last fallback in `model()`'s
115
- * pricing resolution. A per-model or SDK-level `pricing` entry always
116
- * wins over these. Returns `undefined` for any unlisted id so cost stays
117
- * an honest absence rather than a false zero. Update when Groq revises
118
- * its public price list.
113
+ * Built-in default USD-per-1,000,000-token rates for the Groq models with
114
+ * a published price, used as the last fallback in `model()`'s pricing
115
+ * resolution. A per-model or SDK-level `pricing` entry always wins over
116
+ * these. Returns `undefined` for any unlisted id so cost stays an honest
117
+ * absence rather than a false zero.
119
118
  */
120
119
  function defaultPricingFor(name) {
121
120
  return GROQ_DEFAULT_PRICING[name];
122
121
  }
123
122
  /**
124
- * Default per-million-token USD rates for known Groq models. Kept inline
125
- * (not exported) so the public surface stays the inference helpers; a
126
- * caller who wants different numbers supplies `pricing` on the SDK or
127
- * per model.
123
+ * Default per-million-token USD rates for Groq models.
124
+ *
125
+ * Source: https://console.groq.com/docs/models (groq.com/pricing now
126
+ * redirects to the home page), fetched 2026-10-17. Cached input is 50%
127
+ * of the input rate for the gpt-oss models, the only ones Groq caches
128
+ * (https://console.groq.com/docs/prompt-caching; no write charge). The
129
+ * Llama models are listed as "Enterprise – Contact Sales" with no public
130
+ * price, so they stay unpriced — pass `pricing` for them.
128
131
  */
129
132
  const GROQ_DEFAULT_PRICING = {
130
- "llama-3.3-70b-versatile": {
131
- input: .59,
132
- output: .79
133
- },
134
- "llama-3.1-8b-instant": {
135
- input: .05,
136
- output: .08
137
- },
138
133
  "openai/gpt-oss-120b": {
139
134
  input: .15,
140
- output: .75
135
+ output: .6,
136
+ cachedInput: .075
141
137
  },
142
138
  "openai/gpt-oss-20b": {
143
- input: .1,
144
- output: .5
139
+ input: .075,
140
+ output: .3,
141
+ cachedInput: .0375
142
+ },
143
+ "openai/gpt-oss-safeguard-20b": {
144
+ input: .075,
145
+ output: .3,
146
+ cachedInput: .0375
145
147
  },
146
- "deepseek-r1-distill-llama-70b": {
147
- input: .75,
148
- output: .99
148
+ "qwen/qwen3.8-27b": {
149
+ input: .8,
150
+ output: 4
149
151
  }
150
152
  };
151
153
 
152
154
  //#endregion
153
- export { GroqSDK };
155
+ export { GROQ_DEFAULT_PRICING, GroqSDK };
154
156
  //# sourceMappingURL=sdk.mjs.map
package/esm/sdk.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.mjs","names":[],"sources":["../../../../../../../packages/ai-groq/src/sdk.ts"],"sourcesContent":["import type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { OpenAISDK } from \"@warlock.js/ai-openai\";\nimport type {\n GroqEmbedderConfig,\n GroqModelConfig,\n GroqSDKConfig,\n} from \"./config.type\";\nimport {\n GROQ_BASE_URL,\n GROQ_PROVIDER,\n inferReasoningCapability,\n inferVisionCapability,\n} from \"./known-models\";\n\n/**\n * Groq-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Groq-hosted open models\n * (`llama-3.3-70b-versatile`, `llama-3.1-8b-instant`,\n * `openai/gpt-oss-*`, `deepseek-r1-distill-*`, …) served on Groq's fast\n * LPU hardware. Because Groq exposes the OpenAI Chat Completions wire\n * protocol verbatim, `GroqSDK` is a **thin wrapper over the already\n * battle-tested {@link OpenAISDK}** rather than a fresh transport\n * implementation — it owns one internal `OpenAISDK` pointed at Groq's\n * `baseURL` with the `\"groq\"` provider label, and delegates `model()` /\n * `embedder()` / `count()` to it.\n *\n * **Why a wrapper and not `OpenAISDK` directly?** Groq hosts *open*\n * models whose ids are the upstream open-weight names (`llama-…`,\n * `openai/gpt-oss-…`), not OpenAI's (`gpt-4o`, `o3`). The OpenAI\n * adapter's capability inference keys on OpenAI prefixes and would never\n * fire here, so every Groq model would come back with `vision`/\n * `reasoning` silently `false`. `GroqSDK` fixes that by computing\n * **this provider's own** vision/reasoning inference (see\n * `known-models.ts`) and injecting the result as explicit capability\n * config — which wins over the inner adapter's inference — plus a set of\n * default per-model pricing rates. Everything else (transport, retries,\n * streaming, structured output, error wrapping, usage accounting) is\n * inherited unchanged from `OpenAISDK`.\n *\n * **Responsibility.**\n * - Owns: one long-lived internal `OpenAISDK` (auth + Groq base URL) and\n * this provider's capability + default-pricing inference.\n * - Does NOT own: the wire protocol, streaming loop, structured-output\n * mapping, or error wrapping — all inherited from `OpenAISDK`.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });\n * const model = groq.model({ name: \"llama-3.3-70b-versatile\", temperature: 0.7 });\n * const reasoner = groq.model({ name: \"openai/gpt-oss-120b\" }); // vision + reasoning auto-true\n *\n * @example\n * // Compose into an `ai.groq` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, groq: new GroqSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.groq.model({ name: \"llama-3.1-8b-instant\" }) });\n */\nexport class GroqSDK implements SDKAdapterContract {\n /**\n * The wrapped OpenAI-compatible adapter, pre-pointed at Groq's\n * `baseURL` and labeled with the `\"groq\"` provider. All real wire work\n * happens here; `GroqSDK` only enriches the per-model config before\n * delegating.\n */\n private readonly openai: OpenAISDK;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GroqSDKConfig) {\n const { baseURL, provider, pricing, ...clientOptions } = config;\n\n this.provider = provider ?? GROQ_PROVIDER;\n this.pricing = pricing;\n\n // Build the inner OpenAI-compatible client pointed at Groq. Forward\n // every other upstream ClientOptions (timeout, maxRetries,\n // defaultHeaders, fetch, …) verbatim — they type-check, so dropping\n // them would be a silent footgun. We pass `pricing` through too so\n // the inner registry stays a fallback, while THIS class's `model()`\n // layers Groq default rates on top.\n this.openai = new OpenAISDK({\n ...clientOptions,\n baseURL: baseURL ?? GROQ_BASE_URL,\n provider: this.provider,\n pricing,\n });\n }\n\n /**\n * Build a `ModelContract` bound to the internal Groq-pointed client.\n *\n * The wrapper's whole job lives here: it computes **Groq's** vision /\n * reasoning inference from the model id and forwards the result as\n * *explicit* `vision` / `reasoning` config to the inner\n * `OpenAISDK.model()`. Because explicit capability config wins over the\n * inner adapter's OpenAI-prefix inference, the returned model reports\n * the right capabilities even though the id isn't an OpenAI name. A\n * caller-supplied `vision` / `reasoning` still wins over this\n * inference (we only fill the gap when the field is omitted).\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise this\n * SDK's `pricing` registry keyed by `config.name`; otherwise the\n * adapter's built-in default rate for the model; otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GroqModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? defaultPricingFor(config.name);\n\n return this.openai.model({\n ...config,\n // Fill capability inference only when the caller didn't pin it —\n // explicit caller config always wins, then Groq inference, then\n // (inside OpenAISDK) the harmless OpenAI inference that won't match.\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n structuredOutput: config.structuredOutput ?? true,\n pricing: resolvedPricing,\n });\n }\n\n /**\n * Rough offline token-count estimate. Delegated straight to the inner\n * `OpenAISDK`, which uses the core character-heuristic\n * (`approximateTokenCount`). Good for budgeting / quota guards, not for\n * billing.\n */\n public async count(text: string, model?: string): Promise<number> {\n return this.openai.count(text, model);\n }\n\n /**\n * Build an embedder bound to the internal client. Delegated to the\n * inner `OpenAISDK`.\n *\n * IMPORTANT: as of mid-2026 Groq does **not** expose an\n * OpenAI-compatible embeddings endpoint, so a live `.embed()` /\n * `.embedMany()` call will fail at the provider. The method is kept for\n * adapter symmetry; use `@warlock.js/ai-openai` or\n * `@warlock.js/ai-google` for retrieval embeddings.\n */\n public embedder(config: GroqEmbedderConfig): EmbedderContract {\n return this.openai.embedder(config);\n }\n\n // NOTE: `image()` is intentionally NOT implemented. Groq hosts no\n // image-generation API, and `SDKAdapterContract.image` is optional —\n // its structural absence IS the capability guard, so\n // `ai.groq.image(...)` is a compile-time error rather than a runtime\n // surprise (mirrors Anthropic / Bedrock / Ollama).\n}\n\n/**\n * Built-in default USD-per-1,000,000-token rates for the current Groq\n * production models (mid-2026), used as the last fallback in `model()`'s\n * pricing resolution. A per-model or SDK-level `pricing` entry always\n * wins over these. Returns `undefined` for any unlisted id so cost stays\n * an honest absence rather than a false zero. Update when Groq revises\n * its public price list.\n */\nfunction defaultPricingFor(name: string): ModelPricing | undefined {\n return GROQ_DEFAULT_PRICING[name];\n}\n\n/**\n * Default per-million-token USD rates for known Groq models. Kept inline\n * (not exported) so the public surface stays the inference helpers; a\n * caller who wants different numbers supplies `pricing` on the SDK or\n * per model.\n */\nconst GROQ_DEFAULT_PRICING: Record<string, ModelPricing> = {\n \"llama-3.3-70b-versatile\": { input: 0.59, output: 0.79 },\n \"llama-3.1-8b-instant\": { input: 0.05, output: 0.08 },\n \"openai/gpt-oss-120b\": { input: 0.15, output: 0.75 },\n \"openai/gpt-oss-20b\": { input: 0.1, output: 0.5 },\n \"deepseek-r1-distill-llama-70b\": { input: 0.75, output: 0.99 },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAa,UAAb,MAAmD;CAWjD,AAAO,YAAY,QAAuB;EACxC,MAAM,EAAE,SAAS,UAAU,SAAS,GAAG,kBAAkB;EAEzD,KAAK,WAAW;EAChB,KAAK,UAAU;EAQf,KAAK,SAAS,IAAI,UAAU;GAC1B,GAAG;GACH,SAAS;GACT,UAAU,KAAK;GACf;EACF,CAAC;CACH;;;;;;;;;;;;;;;;;;CAmBA,AAAO,MAAM,QAAwC;EACnD,MAAM,kBACJ,OAAO,WAAW,KAAK,UAAU,OAAO,SAAS,kBAAkB,OAAO,IAAI;EAEhF,OAAO,KAAK,OAAO,MAAM;GACvB,GAAG;GAIH,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAC1D,WAAW,OAAO,aAAa,yBAAyB,OAAO,IAAI;GACnE,kBAAkB,OAAO,oBAAoB;GAC7C,SAAS;EACX,CAAC;CACH;;;;;;;CAQA,MAAa,MAAM,MAAc,OAAiC;EAChE,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK;CACtC;;;;;;;;;;;CAYA,AAAO,SAAS,QAA8C;EAC5D,OAAO,KAAK,OAAO,SAAS,MAAM;CACpC;AAOF;;;;;;;;;AAUA,SAAS,kBAAkB,MAAwC;CACjE,OAAO,qBAAqB;AAC9B;;;;;;;AAQA,MAAM,uBAAqD;CACzD,2BAA2B;EAAE,OAAO;EAAM,QAAQ;CAAK;CACvD,wBAAwB;EAAE,OAAO;EAAM,QAAQ;CAAK;CACpD,uBAAuB;EAAE,OAAO;EAAM,QAAQ;CAAK;CACnD,sBAAsB;EAAE,OAAO;EAAK,QAAQ;CAAI;CAChD,iCAAiC;EAAE,OAAO;EAAM,QAAQ;CAAK;AAC/D"}
1
+ {"version":3,"file":"sdk.mjs","names":[],"sources":["../../../../../../../packages/ai-groq/src/sdk.ts"],"sourcesContent":["import type {\n EmbedderContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { OpenAISDK } from \"@warlock.js/ai-openai\";\nimport type { GroqEmbedderConfig, GroqModelConfig, GroqSDKConfig } from \"./config.type\";\nimport {\n GROQ_BASE_URL,\n GROQ_PROVIDER,\n inferReasoningCapability,\n inferVisionCapability,\n} from \"./known-models\";\n\n/**\n * Groq-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Groq-hosted open models\n * (`llama-3.3-70b-versatile`, `llama-3.1-8b-instant`,\n * `openai/gpt-oss-*`, `deepseek-r1-distill-*`, …) served on Groq's fast\n * LPU hardware. Because Groq exposes the OpenAI Chat Completions wire\n * protocol verbatim, `GroqSDK` is a **thin wrapper over the already\n * battle-tested {@link OpenAISDK}** rather than a fresh transport\n * implementation — it owns one internal `OpenAISDK` pointed at Groq's\n * `baseURL` with the `\"groq\"` provider label, and delegates `model()` /\n * `embedder()` / `count()` to it.\n *\n * **Why a wrapper and not `OpenAISDK` directly?** Groq hosts *open*\n * models whose ids are the upstream open-weight names (`llama-…`,\n * `openai/gpt-oss-…`), not OpenAI's (`gpt-4o`, `o3`). The OpenAI\n * adapter's capability inference keys on OpenAI prefixes and would never\n * fire here, so every Groq model would come back with `vision`/\n * `reasoning` silently `false`. `GroqSDK` fixes that by computing\n * **this provider's own** vision/reasoning inference (see\n * `known-models.ts`) and injecting the result as explicit capability\n * config — which wins over the inner adapter's inference — plus a set of\n * default per-model pricing rates. Everything else (transport, retries,\n * streaming, structured output, error wrapping, usage accounting) is\n * inherited unchanged from `OpenAISDK`.\n *\n * **Responsibility.**\n * - Owns: one long-lived internal `OpenAISDK` (auth + Groq base URL) and\n * this provider's capability + default-pricing inference.\n * - Does NOT own: the wire protocol, streaming loop, structured-output\n * mapping, or error wrapping — all inherited from `OpenAISDK`.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });\n * const model = groq.model({ name: \"llama-3.3-70b-versatile\", temperature: 0.7 });\n * const reasoner = groq.model({ name: \"openai/gpt-oss-120b\" }); // vision + reasoning auto-true\n *\n * @example\n * // Compose into an `ai.groq` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, groq: new GroqSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.groq.model({ name: \"llama-3.1-8b-instant\" }) });\n */\nexport class GroqSDK implements SDKAdapterContract {\n /**\n * The wrapped OpenAI-compatible adapter, pre-pointed at Groq's\n * `baseURL` and labeled with the `\"groq\"` provider. All real wire work\n * happens here; `GroqSDK` only enriches the per-model config before\n * delegating.\n */\n private readonly openai: OpenAISDK;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GroqSDKConfig) {\n const { baseURL, provider, pricing, ...clientOptions } = config;\n\n this.provider = provider ?? GROQ_PROVIDER;\n this.pricing = pricing;\n\n // Build the inner OpenAI-compatible client pointed at Groq. Forward\n // every other upstream ClientOptions (timeout, maxRetries,\n // defaultHeaders, fetch, …) verbatim — they type-check, so dropping\n // them would be a silent footgun. We pass `pricing` through too so\n // the inner registry stays a fallback, while THIS class's `model()`\n // layers Groq default rates on top.\n this.openai = new OpenAISDK({\n ...clientOptions,\n baseURL: baseURL ?? GROQ_BASE_URL,\n provider: this.provider,\n pricing,\n });\n }\n\n /**\n * Build a `ModelContract` bound to the internal Groq-pointed client.\n *\n * The wrapper's whole job lives here: it computes **Groq's** vision /\n * reasoning inference from the model id and forwards the result as\n * *explicit* `vision` / `reasoning` config to the inner\n * `OpenAISDK.model()`. Because explicit capability config wins over the\n * inner adapter's OpenAI-prefix inference, the returned model reports\n * the right capabilities even though the id isn't an OpenAI name. A\n * caller-supplied `vision` / `reasoning` still wins over this\n * inference (we only fill the gap when the field is omitted).\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise this\n * SDK's `pricing` registry keyed by `config.name`; otherwise the\n * adapter's built-in default rate for the model; otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GroqModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? defaultPricingFor(config.name);\n\n return this.openai.model({\n ...config,\n // Fill capability inference only when the caller didn't pin it —\n // explicit caller config always wins, then Groq inference, then\n // (inside OpenAISDK) the harmless OpenAI inference that won't match.\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n structuredOutput: config.structuredOutput ?? true,\n pricing: resolvedPricing,\n });\n }\n\n /**\n * Rough offline token-count estimate. Delegated straight to the inner\n * `OpenAISDK`, which uses the core character-heuristic\n * (`approximateTokenCount`). Good for budgeting / quota guards, not for\n * billing.\n */\n public async count(text: string, model?: string): Promise<number> {\n return this.openai.count(text, model);\n }\n\n /**\n * Build an embedder bound to the internal client. Delegated to the\n * inner `OpenAISDK`.\n *\n * IMPORTANT: as of mid-2026 Groq does **not** expose an\n * OpenAI-compatible embeddings endpoint, so a live `.embed()` /\n * `.embedMany()` call will fail at the provider. The method is kept for\n * adapter symmetry; use `@warlock.js/ai-openai` or\n * `@warlock.js/ai-google` for retrieval embeddings.\n */\n public embedder(config: GroqEmbedderConfig): EmbedderContract {\n return this.openai.embedder(config);\n }\n\n // NOTE: `image()` is intentionally NOT implemented. Groq hosts no\n // image-generation API, and `SDKAdapterContract.image` is optional —\n // its structural absence IS the capability guard, so\n // `ai.groq.image(...)` is a compile-time error rather than a runtime\n // surprise (mirrors Anthropic / Bedrock / Ollama).\n}\n\n/**\n * Built-in default USD-per-1,000,000-token rates for the Groq models with\n * a published price, used as the last fallback in `model()`'s pricing\n * resolution. A per-model or SDK-level `pricing` entry always wins over\n * these. Returns `undefined` for any unlisted id so cost stays an honest\n * absence rather than a false zero.\n */\nfunction defaultPricingFor(name: string): ModelPricing | undefined {\n return GROQ_DEFAULT_PRICING[name];\n}\n\n/**\n * Default per-million-token USD rates for Groq models.\n *\n * Source: https://console.groq.com/docs/models (groq.com/pricing now\n * redirects to the home page), fetched 2026-10-17. Cached input is 50%\n * of the input rate for the gpt-oss models, the only ones Groq caches\n * (https://console.groq.com/docs/prompt-caching; no write charge). The\n * Llama models are listed as \"Enterprise – Contact Sales\" with no public\n * price, so they stay unpriced — pass `pricing` for them.\n */\nexport const GROQ_DEFAULT_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"openai/gpt-oss-120b\": { input: 0.15, output: 0.6, cachedInput: 0.075 },\n \"openai/gpt-oss-20b\": { input: 0.075, output: 0.3, cachedInput: 0.0375 },\n \"openai/gpt-oss-safeguard-20b\": { input: 0.075, output: 0.3, cachedInput: 0.0375 },\n \"qwen/qwen3.8-27b\": { input: 0.8, output: 4 },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,IAAa,UAAb,MAAmD;CAWjD,AAAO,YAAY,QAAuB;EACxC,MAAM,EAAE,SAAS,UAAU,SAAS,GAAG,kBAAkB;EAEzD,KAAK,WAAW;EAChB,KAAK,UAAU;EAQf,KAAK,SAAS,IAAI,UAAU;GAC1B,GAAG;GACH,SAAS;GACT,UAAU,KAAK;GACf;EACF,CAAC;CACH;;;;;;;;;;;;;;;;;;CAmBA,AAAO,MAAM,QAAwC;EACnD,MAAM,kBACJ,OAAO,WAAW,KAAK,UAAU,OAAO,SAAS,kBAAkB,OAAO,IAAI;EAEhF,OAAO,KAAK,OAAO,MAAM;GACvB,GAAG;GAIH,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAC1D,WAAW,OAAO,aAAa,yBAAyB,OAAO,IAAI;GACnE,kBAAkB,OAAO,oBAAoB;GAC7C,SAAS;EACX,CAAC;CACH;;;;;;;CAQA,MAAa,MAAM,MAAc,OAAiC;EAChE,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK;CACtC;;;;;;;;;;;CAYA,AAAO,SAAS,QAA8C;EAC5D,OAAO,KAAK,OAAO,SAAS,MAAM;CACpC;AAOF;;;;;;;;AASA,SAAS,kBAAkB,MAAwC;CACjE,OAAO,qBAAqB;AAC9B;;;;;;;;;;;AAYA,MAAa,uBAA+D;CAC1E,uBAAuB;EAAE,OAAO;EAAM,QAAQ;EAAK,aAAa;CAAM;CACtE,sBAAsB;EAAE,OAAO;EAAO,QAAQ;EAAK,aAAa;CAAO;CACvE,gCAAgC;EAAE,OAAO;EAAO,QAAQ;EAAK,aAAa;CAAO;CACjF,oBAAoB;EAAE,OAAO;EAAK,QAAQ;CAAE;AAC9C"}
package/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: setup-groq
11
- description: 'Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.'
11
+ description: 'Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `GROQ_DEFAULT_PRICING`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.'
12
12
  ---
13
13
 
14
14
  # `@warlock.js/ai-groq`
@@ -31,10 +31,10 @@ const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });
31
31
  ## Producing a model
32
32
 
33
33
  ```ts
34
- groq.model({ name: "llama-3.3-70b-versatile" }) // flagship general-purpose text
35
- groq.model({ name: "llama-3.1-8b-instant" }) // fastest / cheapest small text
36
- groq.model({ name: "openai/gpt-oss-120b" }) // vision + reasoning auto-true
37
- groq.model({ name: "deepseek-r1-distill-llama-70b" }) // reasoning auto-true
34
+ groq.model({ name: "llama-3.3-70b-versatile" }); // flagship general-purpose text
35
+ groq.model({ name: "llama-3.1-8b-instant" }); // fastest / cheapest small text
36
+ groq.model({ name: "openai/gpt-oss-120b" }); // vision + reasoning auto-true
37
+ groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
38
38
  ```
39
39
 
40
40
  Returns a `ModelContract` you pass straight into `ai.agent({ model })`. `name` is the Groq-hosted **open-weight** id (the upstream name, e.g. `openai/gpt-oss-120b`), NOT an OpenAI id. Any current Groq model id works — the list above is curated, not an allow-list.
@@ -43,18 +43,18 @@ Returns a `ModelContract` you pass straight into `ai.agent({ model })`. `name` i
43
43
 
44
44
  The whole reason this wrapper exists: Groq's ids aren't OpenAI's, so the OpenAI adapter's prefix inference would never fire. `GroqSDK` carries its **own** name lists.
45
45
 
46
- | Flag | Default |
47
- | --- | --- |
48
- | `vision` | Inferred from the Groq model id. `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`). |
49
- | `reasoning` | Inferred from the Groq model id. `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning_effort` is forwarded. |
50
- | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
51
- | `promptCaching` | Inherited from the inner OpenAI adapter (`true`; read-side `cachedTokens` accounting). |
46
+ | Flag | Default |
47
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
48
+ | `vision` | Inferred from the Groq model id. `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`). |
49
+ | `reasoning` | Inferred from the Groq model id. `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning_effort` is forwarded. |
50
+ | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
51
+ | `promptCaching` | Inherited from the inner OpenAI adapter (`true`; read-side `cachedTokens` accounting). |
52
52
 
53
53
  **Override `vision`, `reasoning`, or `structuredOutput` explicitly** via `.model({ name, vision?, reasoning?, structuredOutput? })` — an explicit value always wins over inference.
54
54
 
55
55
  ```ts
56
- groq.model({ name: "some-custom-llama", vision: true }) // force on for an unlisted multimodal id
57
- groq.model({ name: "openai/gpt-oss-120b", reasoning: false }) // pin off
56
+ groq.model({ name: "some-custom-llama", vision: true }); // force on for an unlisted multimodal id
57
+ groq.model({ name: "openai/gpt-oss-120b", reasoning: false }); // pin off
58
58
  ```
59
59
 
60
60
  ## Reasoning (gpt-oss / deepseek-r1 distill)
@@ -63,7 +63,7 @@ Reasoning models accept a discrete effort knob, forwarded by the inner adapter a
63
63
 
64
64
  ```ts
65
65
  const model = groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
66
- await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
66
+ await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
67
67
  ```
68
68
 
69
69
  - `reasoning.effort` (`"low" | "medium" | "high"`) maps verbatim.
@@ -82,30 +82,44 @@ There is also no image-generation API on Groq, so `GroqSDK` intentionally does *
82
82
 
83
83
  ## Pricing — per-model registry + built-in defaults
84
84
 
85
- `pricing` is a registry keyed by model name, rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput` / `cachedOutput`). The adapter ships **built-in default rates** for the known Groq models as the final fallback.
85
+ `pricing` is a registry keyed by model name, rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput`). The adapter ships **built-in default rates** (`GROQ_DEFAULT_PRICING`) as the final fallback.
86
86
 
87
87
  ```ts
88
+ import { totalCost } from "@warlock.js/ai";
89
+ import { GROQ_DEFAULT_PRICING, GroqSDK } from "@warlock.js/ai-groq";
90
+
88
91
  const groq = new GroqSDK({
89
- apiKey,
92
+ apiKey: process.env.GROQ_API_KEY ?? "",
90
93
  pricing: {
91
- "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 }, // USD per 1M tokens
94
+ // No public price for Llama on Groq — use your contract rate.
95
+ "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
92
96
  },
93
97
  });
98
+
99
+ GROQ_DEFAULT_PRICING["openai/gpt-oss-120b"]; // { input: 0.15, output: 0.6, cachedInput: 0.075 }
100
+
101
+ const { usage } = await groq
102
+ .model({ name: "openai/gpt-oss-120b" })
103
+ .complete([{ role: "user", content: "hi" }]);
104
+ console.log(totalCost(usage.cost));
94
105
  ```
95
106
 
96
- Resolution at `model()` time: per-model `pricing` (`groq.model({ name, pricing })`) > SDK `pricing[name]` > adapter built-in default for the id > `undefined` (no cost computed). Built-in defaults exist for `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `openai/gpt-oss-120b`, `openai/gpt-oss-20b`, and `deepseek-r1-distill-llama-70b`; supply your own for anything else.
107
+ Resolution at `model()` time: per-model `pricing` (`groq.model({ name, pricing })`) > SDK `pricing[name]` > `GROQ_DEFAULT_PRICING` > `undefined` (no cost computed).
108
+
109
+ - **Source.** https://console.groq.com/docs/models (groq.com/pricing now redirects to the home page), fetched 2026-10-17: `openai/gpt-oss-120b`, `openai/gpt-oss-20b`, `openai/gpt-oss-safeguard-20b`, `qwen/qwen3.8-27b`. Cached input is 50% of the input rate for the gpt-oss models, the only ones Groq caches — automatically, with no write charge and no request hint (https://console.groq.com/docs/prompt-caching).
110
+ - **Unpriced:** the Llama models (listed as "Enterprise – Contact Sales", no public price) and anything else not listed — supply your own rate.
97
111
 
98
112
  ## Token counting
99
113
 
100
114
  ```ts
101
- await groq.count("some text") // approximate heuristic (delegated), not a real tokenizer
115
+ await groq.count("some text"); // approximate heuristic (delegated), not a real tokenizer
102
116
  ```
103
117
 
104
118
  Good for budgeting; not for billing.
105
119
 
106
120
  ## Errors
107
121
 
108
- Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by the inner OpenAI adapter's error wrapper (dispatch keys on `status + code`). See the `handle-ai-errors` topic of the `warlock-js-ai` skill. Note Groq's documented OpenAI-compat gaps — `n` must be `1`, and `logprobs` / `logit_bias` / `top_logprobs` / `messages[].name` are unsupported.
122
+ Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by the inner OpenAI adapter's error wrapper (dispatch keys on `status + code`). See the `handle-ai-errors` topic of the `warlock-js-ai` skill. HTTP 503 maps to `ProviderOverloadedError` (`code: "PROVIDER_OVERLOADED"`, `retryable: true`); every wrapped error carries `status`, `retryable` and `billed` (`false` for 401 / 402 / 403, otherwise `"unknown"`). Note Groq's documented OpenAI-compat gaps — `n` must be `1`, and `logprobs` / `logit_bias` / `top_logprobs` / `messages[].name` are unsupported.
109
123
 
110
124
  ## When NOT to use this skill
111
125
 
@@ -119,4 +133,18 @@ Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by th
119
133
  - The `run-ai-agent` topic of the `warlock-js-ai` skill — passing the model into `ai.agent({...})`
120
134
  - The `pick-ai-provider` topic of the `warlock-js-ai` skill — adapter comparison
121
135
 
136
+ ## Provider body options and token estimates
137
+
138
+ This adapter delegates Chat Completions to the OpenAI adapter, so
139
+ `providerOptions.openai.body` adds native Chat request fields. Other provider
140
+ keys are ignored. Plain-object fields merge one level deep with neutral options;
141
+ caller-provided keys win, while arrays, scalars and `null` replace the field.
142
+ The OpenAI adapter's protected conversation, tool and transport fields remain
143
+ protected here too. Send only fields supported by this provider; do not reuse
144
+ OpenAI-only bodies across a mixed-provider fallback chain.
145
+
146
+ Models from this adapter have no native `countTokens` method. Check
147
+ `model.countTokens` before using an optional native counting capability;
148
+ `sdk.count(text)` is a separate offline estimate.
149
+
122
150
 
package/llms.txt CHANGED
@@ -6,4 +6,4 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [setup-groq](@warlock.js/ai-groq/setup-groq/SKILL.md): Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.
9
+ - [setup-groq](@warlock.js/ai-groq/setup-groq/SKILL.md): Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `GROQ_DEFAULT_PRICING`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.
package/package.json CHANGED
@@ -16,13 +16,13 @@
16
16
  "directory": "packages/ai-groq"
17
17
  },
18
18
  "peerDependencies": {
19
- "@warlock.js/ai": "5.30.0"
19
+ "@warlock.js/ai": "5.32.0"
20
20
  },
21
21
  "dependencies": {
22
- "@warlock.js/ai-openai": "5.30.0",
23
- "@warlock.js/logger": "5.30.0"
22
+ "@warlock.js/ai-openai": "5.32.0",
23
+ "@warlock.js/logger": "5.32.0"
24
24
  },
25
- "version": "5.30.0",
25
+ "version": "5.32.0",
26
26
  "main": "./cjs/index.cjs",
27
27
  "module": "./esm/index.mjs",
28
28
  "types": "./esm/index.d.mts",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-groq
3
- description: 'Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.'
3
+ description: 'Wire @warlock.js/ai-groq — new GroqSDK({apiKey, baseURL?, provider?, pricing?}) for Groq-hosted open models on LPU hardware (llama-3.3-70b-versatile, llama-3.1-8b-instant, openai/gpt-oss-120b/20b, deepseek-r1-distill-llama-70b). Thin wrapper over @warlock.js/ai-openai — uses the OpenAI-compatible endpoint https://api.groq.com/openai/v1, delegating transport/streaming/structured-output/errors to OpenAISDK while injecting Groqs OWN vision/reasoning inference + default pricing. .model({name, vision?, reasoning?, structuredOutput?}) for ModelContract. Triggers: `GroqSDK`, `GROQ_DEFAULT_PRICING`, `groq.model`, `groq.count`, `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `gpt-oss`, `deepseek-r1-distill`, `groq vision`, `groq reasoning`, `reasoning_effort`, `https://api.groq.com/openai/v1`, OpenAI-compatible Groq, LPU, fast inference; "wire groq into a warlock agent", "use llama 3.3 on groq", "run gpt-oss / deepseek reasoning on groq", "point warlock at the groq endpoint"; import `import { GroqSDK } from "@warlock.js/ai-groq"`. Skip: agent wiring — the `run-ai-agent` topic of the `warlock-js-ai` skill; adapter comparison — the `pick-ai-provider` topic of the `warlock-js-ai` skill; the wrapped adapter `@warlock.js/ai-openai` (`setup-openai`); embeddings (Groq has none — use openai/google); siblings `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `groq-sdk` / `openai` SDK.'
4
4
  ---
5
5
 
6
6
  # `@warlock.js/ai-groq`
@@ -23,10 +23,10 @@ const groq = new GroqSDK({ apiKey: process.env.GROQ_API_KEY! });
23
23
  ## Producing a model
24
24
 
25
25
  ```ts
26
- groq.model({ name: "llama-3.3-70b-versatile" }) // flagship general-purpose text
27
- groq.model({ name: "llama-3.1-8b-instant" }) // fastest / cheapest small text
28
- groq.model({ name: "openai/gpt-oss-120b" }) // vision + reasoning auto-true
29
- groq.model({ name: "deepseek-r1-distill-llama-70b" }) // reasoning auto-true
26
+ groq.model({ name: "llama-3.3-70b-versatile" }); // flagship general-purpose text
27
+ groq.model({ name: "llama-3.1-8b-instant" }); // fastest / cheapest small text
28
+ groq.model({ name: "openai/gpt-oss-120b" }); // vision + reasoning auto-true
29
+ groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
30
30
  ```
31
31
 
32
32
  Returns a `ModelContract` you pass straight into `ai.agent({ model })`. `name` is the Groq-hosted **open-weight** id (the upstream name, e.g. `openai/gpt-oss-120b`), NOT an OpenAI id. Any current Groq model id works — the list above is curated, not an allow-list.
@@ -35,18 +35,18 @@ Returns a `ModelContract` you pass straight into `ai.agent({ model })`. `name` i
35
35
 
36
36
  The whole reason this wrapper exists: Groq's ids aren't OpenAI's, so the OpenAI adapter's prefix inference would never fire. `GroqSDK` carries its **own** name lists.
37
37
 
38
- | Flag | Default |
39
- | --- | --- |
40
- | `vision` | Inferred from the Groq model id. `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`). |
41
- | `reasoning` | Inferred from the Groq model id. `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning_effort` is forwarded. |
42
- | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
43
- | `promptCaching` | Inherited from the inner OpenAI adapter (`true`; read-side `cachedTokens` accounting). |
38
+ | Flag | Default |
39
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | `vision` | Inferred from the Groq model id. `true` for `gpt-oss`, `llama-4`, `llama-3.2-*-vision`; `false` otherwise (e.g. `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`). |
41
+ | `reasoning` | Inferred from the Groq model id. `true` for `gpt-oss`, `deepseek-r1` / `deepseek-r1-distill`, `qwq`, `qwen3`; `false` otherwise. Drives whether `reasoning_effort` is forwarded. |
42
+ | `structuredOutput` | `true` — Groq accepts OpenAI-style `response_format`. |
43
+ | `promptCaching` | Inherited from the inner OpenAI adapter (`true`; read-side `cachedTokens` accounting). |
44
44
 
45
45
  **Override `vision`, `reasoning`, or `structuredOutput` explicitly** via `.model({ name, vision?, reasoning?, structuredOutput? })` — an explicit value always wins over inference.
46
46
 
47
47
  ```ts
48
- groq.model({ name: "some-custom-llama", vision: true }) // force on for an unlisted multimodal id
49
- groq.model({ name: "openai/gpt-oss-120b", reasoning: false }) // pin off
48
+ groq.model({ name: "some-custom-llama", vision: true }); // force on for an unlisted multimodal id
49
+ groq.model({ name: "openai/gpt-oss-120b", reasoning: false }); // pin off
50
50
  ```
51
51
 
52
52
  ## Reasoning (gpt-oss / deepseek-r1 distill)
@@ -55,7 +55,7 @@ Reasoning models accept a discrete effort knob, forwarded by the inner adapter a
55
55
 
56
56
  ```ts
57
57
  const model = groq.model({ name: "deepseek-r1-distill-llama-70b" }); // reasoning auto-true
58
- await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
58
+ await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
59
59
  ```
60
60
 
61
61
  - `reasoning.effort` (`"low" | "medium" | "high"`) maps verbatim.
@@ -74,30 +74,44 @@ There is also no image-generation API on Groq, so `GroqSDK` intentionally does *
74
74
 
75
75
  ## Pricing — per-model registry + built-in defaults
76
76
 
77
- `pricing` is a registry keyed by model name, rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput` / `cachedOutput`). The adapter ships **built-in default rates** for the known Groq models as the final fallback.
77
+ `pricing` is a registry keyed by model name, rates in **USD per 1,000,000 tokens** (`ModelPricing`: `input`, `output`, optional `cachedInput`). The adapter ships **built-in default rates** (`GROQ_DEFAULT_PRICING`) as the final fallback.
78
78
 
79
79
  ```ts
80
+ import { totalCost } from "@warlock.js/ai";
81
+ import { GROQ_DEFAULT_PRICING, GroqSDK } from "@warlock.js/ai-groq";
82
+
80
83
  const groq = new GroqSDK({
81
- apiKey,
84
+ apiKey: process.env.GROQ_API_KEY ?? "",
82
85
  pricing: {
83
- "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 }, // USD per 1M tokens
86
+ // No public price for Llama on Groq — use your contract rate.
87
+ "llama-3.3-70b-versatile": { input: 0.59, output: 0.79 },
84
88
  },
85
89
  });
90
+
91
+ GROQ_DEFAULT_PRICING["openai/gpt-oss-120b"]; // { input: 0.15, output: 0.6, cachedInput: 0.075 }
92
+
93
+ const { usage } = await groq
94
+ .model({ name: "openai/gpt-oss-120b" })
95
+ .complete([{ role: "user", content: "hi" }]);
96
+ console.log(totalCost(usage.cost));
86
97
  ```
87
98
 
88
- Resolution at `model()` time: per-model `pricing` (`groq.model({ name, pricing })`) > SDK `pricing[name]` > adapter built-in default for the id > `undefined` (no cost computed). Built-in defaults exist for `llama-3.3-70b-versatile`, `llama-3.1-8b-instant`, `openai/gpt-oss-120b`, `openai/gpt-oss-20b`, and `deepseek-r1-distill-llama-70b`; supply your own for anything else.
99
+ Resolution at `model()` time: per-model `pricing` (`groq.model({ name, pricing })`) > SDK `pricing[name]` > `GROQ_DEFAULT_PRICING` > `undefined` (no cost computed).
100
+
101
+ - **Source.** https://console.groq.com/docs/models (groq.com/pricing now redirects to the home page), fetched 2026-10-17: `openai/gpt-oss-120b`, `openai/gpt-oss-20b`, `openai/gpt-oss-safeguard-20b`, `qwen/qwen3.8-27b`. Cached input is 50% of the input rate for the gpt-oss models, the only ones Groq caches — automatically, with no write charge and no request hint (https://console.groq.com/docs/prompt-caching).
102
+ - **Unpriced:** the Llama models (listed as "Enterprise – Contact Sales", no public price) and anything else not listed — supply your own rate.
89
103
 
90
104
  ## Token counting
91
105
 
92
106
  ```ts
93
- await groq.count("some text") // approximate heuristic (delegated), not a real tokenizer
107
+ await groq.count("some text"); // approximate heuristic (delegated), not a real tokenizer
94
108
  ```
95
109
 
96
110
  Good for budgeting; not for billing.
97
111
 
98
112
  ## Errors
99
113
 
100
- Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by the inner OpenAI adapter's error wrapper (dispatch keys on `status + code`). See the `handle-ai-errors` topic of the `warlock-js-ai` skill. Note Groq's documented OpenAI-compat gaps — `n` must be `1`, and `logprobs` / `logit_bias` / `top_logprobs` / `messages[].name` are unsupported.
114
+ Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by the inner OpenAI adapter's error wrapper (dispatch keys on `status + code`). See the `handle-ai-errors` topic of the `warlock-js-ai` skill. HTTP 503 maps to `ProviderOverloadedError` (`code: "PROVIDER_OVERLOADED"`, `retryable: true`); every wrapped error carries `status`, `retryable` and `billed` (`false` for 401 / 402 / 403, otherwise `"unknown"`). Note Groq's documented OpenAI-compat gaps — `n` must be `1`, and `logprobs` / `logit_bias` / `top_logprobs` / `messages[].name` are unsupported.
101
115
 
102
116
  ## When NOT to use this skill
103
117
 
@@ -110,3 +124,17 @@ Raw errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy by th
110
124
  - The `setup-openai` topic of the `warlock-js-ai-openai` skill — the wrapped adapter; full wire / streaming / structured-output detail
111
125
  - The `run-ai-agent` topic of the `warlock-js-ai` skill — passing the model into `ai.agent({...})`
112
126
  - The `pick-ai-provider` topic of the `warlock-js-ai` skill — adapter comparison
127
+
128
+ ## Provider body options and token estimates
129
+
130
+ This adapter delegates Chat Completions to the OpenAI adapter, so
131
+ `providerOptions.openai.body` adds native Chat request fields. Other provider
132
+ keys are ignored. Plain-object fields merge one level deep with neutral options;
133
+ caller-provided keys win, while arrays, scalars and `null` replace the field.
134
+ The OpenAI adapter's protected conversation, tool and transport fields remain
135
+ protected here too. Send only fields supported by this provider; do not reuse
136
+ OpenAI-only bodies across a mixed-provider fallback chain.
137
+
138
+ Models from this adapter have no native `countTokens` method. Check
139
+ `model.countTokens` before using an optional native counting capability;
140
+ `sdk.count(text)` is a separate offline estimate.