@opencode-ai/ai 0.0.0-dev-18234 → 0.0.0-dev-18235

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.
@@ -241,7 +241,7 @@ const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (mes
241
241
  };
242
242
  return { role: "user", content };
243
243
  });
244
- const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, options = {}) {
244
+ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(function* (message, configuredField, requireReasoning, options) {
245
245
  const content = [];
246
246
  const reasoning = [];
247
247
  const toolCalls = [];
@@ -267,22 +267,22 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
267
267
  const nativeReasoning = openAICompatibleReasoningContent(message.native?.openaiCompatible);
268
268
  const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails));
269
269
  const field = (() => {
270
- if (configuredField !== undefined)
270
+ if (configuredField !== undefined && (requireReasoning || reasoning.length > 0 || nativeReasoning !== undefined))
271
271
  return configuredField;
272
272
  if (reasoning.length === 0)
273
- return undefined;
273
+ return requireReasoning ? "reasoning_content" : undefined;
274
274
  if (observedField !== undefined)
275
275
  return observedField;
276
276
  if (nativeReasoning !== undefined)
277
277
  return "reasoning_content";
278
- if (!fullyStructured)
278
+ if (!fullyStructured || requireReasoning)
279
279
  return "reasoning_content";
280
280
  })();
281
281
  const reasoningText = (() => {
282
282
  if (configuredField !== undefined)
283
- return reasoning.length === 0 ? (nativeReasoning ?? "") : text;
283
+ return reasoning.length === 0 ? (nativeReasoning ?? (requireReasoning ? "" : undefined)) : text;
284
284
  if (reasoning.length === 0)
285
- return nativeReasoning;
285
+ return nativeReasoning ?? (requireReasoning ? "" : undefined);
286
286
  return text;
287
287
  })();
288
288
  const cached = message.content.findLast((part) => "cache" in part && part.cache !== undefined);
@@ -326,11 +326,11 @@ const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (m
326
326
  }
327
327
  return { messages, images };
328
328
  });
329
- const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, options = {}) {
329
+ const lowerMessage = Effect.fn("OpenAIChat.lowerMessage")(function* (message, reasoningField, requireReasoning, options) {
330
330
  if (message.role === "user")
331
331
  return [yield* lowerUserMessage(message, options)];
332
332
  if (message.role === "assistant")
333
- return [yield* lowerAssistantMessage(message, reasoningField, options)];
333
+ return [yield* lowerAssistantMessage(message, reasoningField, requireReasoning, options)];
334
334
  return (yield* lowerToolMessages(message, options)).messages;
335
335
  });
336
336
  const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request, options) {
@@ -350,6 +350,12 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
350
350
  : [{ role: "system", content: ProviderShared.joinText(request.system) }];
351
351
  const messages = [...system];
352
352
  const modelID = request.model.id.toLowerCase();
353
+ const requireReasoning = request.model.compatibility?.requireReasoning ??
354
+ (request.model.compatibility?.reasoningField !== undefined ||
355
+ request.model.provider === "deepseek" ||
356
+ request.model.route.endpoint.baseURL?.toLowerCase().includes("deepseek.com") ||
357
+ modelID.includes("deepseek"));
358
+ const reasoningField = request.model.compatibility?.reasoningField;
353
359
  const mistral = ["mistral", "devstral", "codestral", "pixtral", "mixtral"].some((family) => modelID.includes(family));
354
360
  const lowering = {
355
361
  ...options,
@@ -427,7 +433,7 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request,
427
433
  continue;
428
434
  }
429
435
  flushImages();
430
- messages.push(...(yield* lowerMessage(message, request.model.compatibility?.reasoningField, lowering)));
436
+ messages.push(...(yield* lowerMessage(message, reasoningField, requireReasoning, lowering)));
431
437
  }
432
438
  flushImages();
433
439
  return messages;
@@ -72,6 +72,8 @@ export type LanguageModelMaxTokensFieldCompatibility = Schema.Schema.Type<typeof
72
72
  declare const LanguageModelCompatibility_base: Schema.Class<LanguageModelCompatibility, Schema.Struct<{
73
73
  readonly toolSchema: Schema.optional<Schema.Literals<readonly ["gemini", "moonshot"]>>;
74
74
  readonly reasoningField: Schema.optional<Schema.String>;
75
+ /** Require every assistant message to include its reasoning field, even when empty. */
76
+ readonly requireReasoning: Schema.optional<Schema.Boolean>;
75
77
  readonly maxTokensField: Schema.optional<Schema.Literals<readonly ["max_completion_tokens", "max_tokens"]>>;
76
78
  readonly requireFinishReason: Schema.optional<Schema.Boolean>;
77
79
  readonly requireAssistantAfterTool: Schema.optional<Schema.Boolean>;
@@ -99,6 +99,8 @@ export const LanguageModelMaxTokensFieldCompatibility = Schema.Literals(["max_co
99
99
  export class LanguageModelCompatibility extends Schema.Class("LLM.LanguageModelCompatibility")({
100
100
  toolSchema: Schema.optional(LanguageModelToolSchemaCompatibility),
101
101
  reasoningField: Schema.optional(Schema.String),
102
+ /** Require every assistant message to include its reasoning field, even when empty. */
103
+ requireReasoning: Schema.optional(Schema.Boolean),
102
104
  maxTokensField: Schema.optional(LanguageModelMaxTokensFieldCompatibility),
103
105
  requireFinishReason: Schema.optional(Schema.Boolean),
104
106
  requireAssistantAfterTool: Schema.optional(Schema.Boolean),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-18234",
3
+ "version": "0.0.0-dev-18235",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-rc.111",
33
- "@opencode-ai/http-recorder": "0.0.0-dev-18234",
33
+ "@opencode-ai/http-recorder": "0.0.0-dev-18235",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.3.13",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@smithy/eventstream-codec": "4.2.14",
41
41
  "@smithy/util-utf8": "4.2.2",
42
- "@opencode-ai/schema": "0.0.0-dev-18234",
42
+ "@opencode-ai/schema": "0.0.0-dev-18235",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-rc.111",
45
45
  "google-auth-library": "10.5.0"