@opencow-ai/opencow-agent-sdk 0.4.2 → 0.4.4

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.
@@ -1,3 +1,39 @@
1
+ /**
2
+ * 把一个属性 schema 标记为「可空」。用于 OpenAI/Codex strict 模式下被强制列入
3
+ * `required` 的「原本可选」字段:strict 模式要求每个属性都出现在 `required`,
4
+ * 否则 400。这会逼模型给本应省略的可选字段编造一个占位值(典型即空串 `""`),
5
+ * 下游再因这个空值报错(issue #79:Read 的 `pages: ""` → Invalid pages parameter;
6
+ * issue #77:gpt 用 read 工具频繁出错)。让字段可空后,模型可以用 `null` 明确表达
7
+ * 「未提供」,再由入参解析层把 `null` 还原为缺省。
8
+ *
9
+ * 可空的两种写法没有「最大公约数」,必须按目标 provider 选择:
10
+ * - `'union'`(默认):`type: [T, "null"]`。OpenAI / Codex 的 **strict 结构化输出
11
+ * 只接受这种**(`nullable` 是 OpenAPI 扩展,不在其 JSON-Schema 子集里,会被
12
+ * strict 校验拒绝 400);DeepSeek / Kimi / Qwen 同样接受。
13
+ * - `'nullable'`:`nullable: true`。Gemini / Vertex 只接受这种,拒绝 type 数组。
14
+ *
15
+ * 注意 OpenAI chat(非 strict)会忽略 `nullable`,从而丢掉「可传 null」的提示,
16
+ * 退回 issue #77 的形态——所以对 OpenAI 路径必须用 `'union'`。Gemini 路径由调用方
17
+ * (normalizeSchemaForOpenAI)传 `'nullable'`,或经 splitTypeArrayToAnyOf 转换。
18
+ *
19
+ * 带 `enum`/`const` 的字段保持不动(避免自相矛盾);
20
+ * 无明确 `type` 的字段也保持不动(零回归)。
21
+ */
22
+ export declare function makeSchemaNullable(schema: Record<string, unknown>, style?: 'union' | 'nullable'): Record<string, unknown>;
23
+ /**
24
+ * 把 type 数组改写为 **Gemini/Vertex** 能接受的形式——仅在目标是 Gemini 时调用
25
+ * (见 normalizeSchemaForOpenAI 的 geminiTarget 门控)。OpenAI/DeepSeek/Kimi 等接受
26
+ * type 数组、且对 OpenAI 而言 `nullable` 会被忽略,故对它们不应调用本函数。
27
+ *
28
+ * Gemini 侧约束:拒绝 type 数组,拒绝 anyOf + null,只接受 nullable: true。
29
+ *
30
+ * 转换策略:
31
+ * - `type: ["T", "null"]` → `{ type: "T", nullable: true, ...结构性关键字 }`
32
+ * - `type: ["T1", "T2", ...]`(>1 种非 null 类型)→ 拆为 anyOf,null 以 nullable 表达
33
+ *
34
+ * 不递归——由调用方(normalizeSchemaForOpenAI)的递归遍历负责嵌套层级。
35
+ */
36
+ export declare function splitTypeArrayToAnyOf(schema: Record<string, unknown>): Record<string, unknown>;
1
37
  /**
2
38
  * Sanitize JSON Schema into a shape OpenAI-compatible providers and Codex
3
39
  * strict-mode tooling are more likely to accept. This strips provider-rejected
@@ -0,0 +1,20 @@
1
+ import type { z } from 'zod/v4';
2
+ /**
3
+ * 解析工具入参,并对 strict provider 把可选字段强制为 nullable 后模型回传的 `null`
4
+ * 做兜底还原。
5
+ *
6
+ * 背景:OpenAI/Codex strict 模式要求每个属性都出现在 `required`,可选字段会被
7
+ * `makeSchemaNullable` 标成可空(见 lib/schemaSanitizer.ts),模型遂用 `null` 表达
8
+ * 「未提供」。但工具的 Zod inputSchema 多用 `.optional()`(不接受 `null`),若直接
9
+ * `safeParse` 会得到 InputValidationError —— 这正是「① 让 wire schema 可空」必须搭配的
10
+ * 入参侧兜底(issue #79/#77)。
11
+ *
12
+ * 策略:先按原始 schema 解析;仅当「解析失败 且 顶层含 null 值」时,去掉这些 null 键
13
+ * 再解析一次,且只有重试成功才采用重试结果。由此:
14
+ * - 真正接受 null 的 `.nullable()` 字段:原始即可解析 → 不触发重试 → `null` 保留;
15
+ * - 与 null 无关的真实校验错误:去 null 后仍失败 → 返回原始错误,不掩盖;
16
+ * - 可选非空字段被强制塞入的 `null`:去掉后变缺省,`.optional()` 通过。
17
+ *
18
+ * 仅处理顶层 null,与 `makeSchemaNullable` 仅顶层可空的范围对齐。
19
+ */
20
+ export declare function safeParseToolInputWithNullCoercion<T extends z.ZodType>(schema: T, input: unknown): ReturnType<T['safeParse']>;
@@ -1,4 +1,12 @@
1
1
  import type { ModelOutputTokenCap, ProviderFeature } from '../types.js';
2
+ /**
3
+ * 判断最终目标是否为 Gemini/Vertex 系列。
4
+ *
5
+ * 这是 Gemini 专属适配(关闭并行工具调用、schema 用 nullable:true 而非 type 数组)的
6
+ * 统一判定入口。后端纯透传时 SDK 侧 family 恒为 openai,无法据此识别最终 provider,
7
+ * 因此依据 CLAUDE_CODE_USE_GEMINI 环境标志 + model 名两路信号判断。
8
+ */
9
+ export declare function isGeminiTarget(model: string): boolean;
2
10
  export declare function getOpenAICompatMaxOutputTokens(model: string): ModelOutputTokenCap | null;
3
11
  export declare function getOpenAICompatContextWindow(model: string): number | null;
4
12
  export declare function openAICompatSupports(feature: ProviderFeature, model: string): boolean;
@@ -1 +1 @@
1
- export { sanitizeSchemaForOpenAICompat } from '../../lib/schemaSanitizer.js';
1
+ export { sanitizeSchemaForOpenAICompat, makeSchemaNullable, } from '../../lib/schemaSanitizer.js';
@@ -23,7 +23,7 @@
23
23
  import { type AnthropicStreamEvent, type ShimCreateParams } from '../../providers/codex/shim.js';
24
24
  interface OpenAIMessage {
25
25
  role: 'system' | 'user' | 'assistant' | 'tool';
26
- content?: string | Array<{
26
+ content?: string | null | Array<{
27
27
  type: string;
28
28
  text?: string;
29
29
  image_url?: {
@@ -65,7 +65,7 @@ export declare function convertTools(tools: Array<{
65
65
  name: string;
66
66
  description?: string;
67
67
  input_schema?: Record<string, unknown>;
68
- }>): OpenAITool[];
68
+ }>, model?: string): OpenAITool[];
69
69
  export declare function makeMessageId(): string;
70
70
  /**
71
71
  * Async generator that transforms an OpenAI SSE stream into
@@ -146,6 +146,13 @@ export declare function convertOpenAIResponseToAnthropic(data: {
146
146
  };
147
147
  extra_content?: Record<string, unknown>;
148
148
  }>;
149
+ reasoning_content?: string | null;
150
+ reasoning?: string | null;
151
+ reasoning_details?: Array<{
152
+ type?: string;
153
+ content?: string;
154
+ summary?: string;
155
+ }> | null;
149
156
  };
150
157
  finish_reason?: string;
151
158
  }>;