@wrongstack/providers 0.257.2 → 0.264.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/dist/index.d.ts CHANGED
@@ -164,6 +164,26 @@ interface ConvertOptions {
164
164
  flattenContentToString?: boolean | undefined;
165
165
  stripCacheControl?: boolean | undefined;
166
166
  systemAsMessage?: boolean | undefined;
167
+ /**
168
+ * What to write as the assistant message's `content` field when the
169
+ * message has tool_calls but no prose. Two values:
170
+ *
171
+ * - `'empty_string'` (default): writes `content: ''`. This is the
172
+ * OpenAI 2024-2025 wire-format contract. Vanilla OpenAI, K2P7,
173
+ * strict Mistral / OpenRouter / DeepSeek proxies all reject
174
+ * requests where `content` is missing or `null` on a tool_call
175
+ * assistant message.
176
+ *
177
+ * - `'null'`: writes `content: null` explicitly. Some older or
178
+ * permissive proxies (e.g. certain vLLM builds, local llama.cpp
179
+ * servers) prefer this. Set this only if a specific provider
180
+ * rejects the empty-string form.
181
+ *
182
+ * The default is `'empty_string'` (NOT undefined) because omitting
183
+ * `content` entirely (the pre-2024 behaviour) breaks too many
184
+ * providers to be the safe default in 2025. Callers that need the
185
+ * old behaviour can opt in with `emptyToolCallContent: 'null'`.
186
+ */
167
187
  emptyToolCallContent?: 'null' | 'empty_string' | undefined;
168
188
  }
169
189
  declare function messagesToOpenAI(system: TextBlock[] | undefined, messages: Message[], opts?: ConvertOptions): OpenAIMessage[];
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { expectDefined, ProviderError, StreamHangError, safeParse, sanitizeJsonString, WrongStackError, ERROR_CODES } from '@wrongstack/core';
2
2
  import { Readable } from 'stream';
3
+ import { toErrorMessage } from '@wrongstack/core/utils';
3
4
  import { randomUUID } from 'crypto';
4
5
 
5
6
  var __defProp = Object.defineProperty;
@@ -657,9 +658,9 @@ var WireAdapter = class {
657
658
  httpRes = raw;
658
659
  } catch (err) {
659
660
  if (opts.signal.aborted) throw err;
660
- throw new ProviderError(err instanceof Error ? err.message : String(err), 0, true, this.id, {
661
+ throw new ProviderError(toErrorMessage(err), 0, true, this.id, {
661
662
  cause: err,
662
- body: { message: err instanceof Error ? err.message : String(err) }
663
+ body: { message: toErrorMessage(err) }
663
664
  });
664
665
  }
665
666
  if (!httpRes.ok) {
@@ -1209,6 +1210,7 @@ function toolsToOpenAI(tools) {
1209
1210
  }));
1210
1211
  }
1211
1212
  function messagesToOpenAI(system, messages, opts = {}) {
1213
+ const emptyContentMode = opts.emptyToolCallContent ?? "empty_string";
1212
1214
  const out = [];
1213
1215
  if (system && system.length > 0) {
1214
1216
  const sysText = system.map((b) => b.text).join("\n\n");
@@ -1255,7 +1257,6 @@ function messagesToOpenAI(system, messages, opts = {}) {
1255
1257
  if (text) {
1256
1258
  message.content = text;
1257
1259
  } else {
1258
- const emptyContentMode = opts.emptyToolCallContent ?? "empty_string";
1259
1260
  message.content = emptyContentMode === "null" ? null : "";
1260
1261
  }
1261
1262
  } else {
@@ -1627,7 +1628,7 @@ var mistralWireFormat = defineWireFormat({
1627
1628
  buildBody: (req) => {
1628
1629
  const body = {
1629
1630
  model: req.model,
1630
- messages: messagesToOpenAI(stripCacheControl(req.system), req.messages, {}),
1631
+ messages: messagesToOpenAI(stripCacheControl(req.system), req.messages),
1631
1632
  max_tokens: req.maxTokens,
1632
1633
  stream: true
1633
1634
  };
@@ -1908,7 +1909,7 @@ var openaiWireFormat = defineWireFormat({
1908
1909
  buildBody: (req) => {
1909
1910
  const body = {
1910
1911
  model: req.model,
1911
- messages: messagesToOpenAI(stripCacheControl2(req.system), req.messages, {}),
1912
+ messages: messagesToOpenAI(stripCacheControl2(req.system), req.messages),
1912
1913
  // Real OpenAI requires `max_completion_tokens`; newer model families
1913
1914
  // (gpt-4o, o1/o3/o4) 400 on the deprecated `max_tokens`. See issue #10.
1914
1915
  max_completion_tokens: req.maxTokens,