@codehz/ai 0.4.4 → 0.4.5

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.
@@ -0,0 +1,56 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write # required for npm trusted publishing (OIDC)
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+
18
+ - name: Verify tag matches package.json version
19
+ run: |
20
+ tag="${GITHUB_REF_NAME#v}"
21
+ version="$(node -p "require('./package.json').version")"
22
+ if [ "$tag" != "$version" ]; then
23
+ echo "Tag v${tag} does not match package.json version ${version}"
24
+ exit 1
25
+ fi
26
+
27
+ - uses: oven-sh/setup-bun@v2
28
+ with:
29
+ bun-version: latest
30
+
31
+ - name: Install dependencies
32
+ run: bun install --frozen-lockfile
33
+
34
+ - name: Typecheck
35
+ run: bun run typecheck
36
+
37
+ - name: Lint
38
+ run: bun run lint
39
+
40
+ - name: Test
41
+ run: bun test
42
+
43
+ # Trusted publishing requires npm CLI (OIDC); other package managers cannot publish this way.
44
+ - uses: actions/setup-node@v6
45
+ with:
46
+ node-version: "24"
47
+ registry-url: "https://registry.npmjs.org"
48
+ package-manager-cache: false # never cache package manager state for release builds
49
+
50
+ - name: Ensure npm CLI supports trusted publishing
51
+ run: npm install -g npm@latest
52
+
53
+ - name: Publish to npm
54
+ # No NPM_TOKEN: npm CLI exchanges the GitHub OIDC token automatically.
55
+ # Provenance is generated automatically for trusted publishing from public repos.
56
+ run: npm publish --access public
package/README.md CHANGED
@@ -45,10 +45,21 @@ type AIRequest = {
45
45
  toolChoice?: ToolChoice; // 工具选择策略
46
46
  temperature?: number; // 温度 (0–2)
47
47
  maxOutputTokens?: number; // 最大输出 token
48
+ reasoningLevel?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh"; // 可移植思考力度
48
49
  include?: { usage?; billing?; providerMetadata? };
49
50
  };
50
51
  ```
51
52
 
53
+ `reasoningLevel` 是 portable 枚举,由各 adapter 映射到 provider 原生字段;未设置时不写相关 wire 字段。adapter 无法映射的 level(如 Ollama 的 `minimal` / `xhigh`)会抛 `AIRequestError`(`UNSUPPORTED_REASONING_LEVEL`)。需要 budget / summary 等特化参数时,仍可用构造期 `extraBody` 覆盖同名顶层键。
54
+
55
+ | Adapter | 映射 |
56
+ | --- | --- |
57
+ | `ResponsesAdapter` | `reasoning: { effort }` |
58
+ | `ChatCompletionsAdapter` | 顶层 `reasoning_effort` |
59
+ | `MessagesAdapter` | `thinking: { type: "disabled" }` 或 `{ type: "enabled", budget_tokens }`(由 `maxOutputTokens` 按比例推导,默认 4096) |
60
+ | `OllamaAdapter` | `think: false \| "low" \| "medium" \| "high"` |
61
+ | `MockAdapter` | 透传到 `MockHandlerContext.reasoningLevel` |
62
+
52
63
  `input` 是 item 数组,每个 item 可以是:
53
64
 
54
65
  | Item 类型 | 用途 |
@@ -133,16 +144,33 @@ import {
133
144
  } from "@codehz/ai";
134
145
 
135
146
  // OpenAI Responses API
136
- const responses = new ResponsesAdapter({ apiKey: "sk-..." });
147
+ const responses = new ResponsesAdapter({
148
+ apiKey: "sk-...",
149
+ // 可选:自定义请求头 / body 额外顶层字段(构造期静态,后写覆盖内置鉴权头与同名 body 键)
150
+ headers: { "OpenAI-Organization": "org-..." },
151
+ extraBody: { top_p: 0.9 },
152
+ });
137
153
 
138
154
  // Anthropic Messages API
139
- const messages = new MessagesAdapter({ apiKey: "sk-ant-..." });
155
+ const messages = new MessagesAdapter({
156
+ apiKey: "sk-ant-...",
157
+ headers: { "anthropic-beta": "..." },
158
+ extraBody: { top_p: 0.9 },
159
+ });
140
160
 
141
161
  // OpenAI Chat Completions
142
- const chat = new ChatCompletionsAdapter({ apiKey: "sk-..." });
162
+ const chat = new ChatCompletionsAdapter({
163
+ apiKey: "sk-...",
164
+ headers: { "OpenAI-Organization": "org-..." },
165
+ extraBody: { top_p: 0.9 },
166
+ });
143
167
 
144
168
  // Ollama
145
- const ollama = new OllamaAdapter({ baseUrl: "http://localhost:11434" });
169
+ const ollama = new OllamaAdapter({
170
+ baseUrl: "http://localhost:11434",
171
+ headers: { "X-Custom": "..." },
172
+ extraBody: { keep_alive: "10m" },
173
+ });
146
174
 
147
175
  // 面向测试的回调驱动 mock backend
148
176
  const mock = new MockAdapter({
package/dist/index.d.mts CHANGED
@@ -79,6 +79,8 @@ type IncludeSettings = {
79
79
  billing?: "off" | "best_effort";
80
80
  providerMetadata?: "off" | "best_effort";
81
81
  };
82
+ /** Portable reasoning / thinking effort. Mapped per-adapter to provider wire fields. */
83
+ type ReasoningLevel = "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
82
84
  type AIRequest = {
83
85
  instructions?: string | InstructionBlock[];
84
86
  input: InputItem[];
@@ -87,7 +89,13 @@ type AIRequest = {
87
89
  include?: IncludeSettings;
88
90
  metadata?: Record<string, string>;
89
91
  temperature?: number;
90
- maxOutputTokens?: number; /** AbortSignal 用于打断请求。abort 时 fetch 调用会被取消,流迭代器抛出 AbortError。 */
92
+ maxOutputTokens?: number;
93
+ /**
94
+ * Portable reasoning effort. Adapters map this to provider-native fields
95
+ * (e.g. Responses `reasoning.effort`, Chat Completions `reasoning_effort`,
96
+ * Messages `thinking`, Ollama `think`). Unsupported levels throw.
97
+ */
98
+ reasoningLevel?: ReasoningLevel; /** AbortSignal 用于打断请求。abort 时 fetch 调用会被取消,流迭代器抛出 AbortError。 */
91
99
  signal?: AbortSignal;
92
100
  };
93
101
  //#endregion
@@ -551,7 +559,9 @@ declare abstract class AdapterBase implements BackendAdapter {
551
559
  type ResponsesAdapterOptions = {
552
560
  apiKey: string;
553
561
  baseUrl?: string; /** 可注入自定义 fetch 实现(用于测试/代理) */
554
- fetch?: FetchFn;
562
+ fetch?: FetchFn; /** 额外请求头;后写覆盖内置 Authorization / Content-Type */
563
+ headers?: Record<string, string>; /** 额外 body 顶层字段;浅层合并,同名键可覆盖 */
564
+ extraBody?: Record<string, unknown>;
555
565
  };
556
566
  type ResponsesAPIRequest = {
557
567
  model: string;
@@ -564,7 +574,10 @@ type ResponsesAPIRequest = {
564
574
  };
565
575
  metadata?: Record<string, string>;
566
576
  temperature?: number;
567
- max_output_tokens?: number; /** 服务端多轮续写;opaque replay response id 映射到此字段,而非 item_reference */
577
+ max_output_tokens?: number; /** Portable reasoningLevel effort;summary 等特化字段不在此层 */
578
+ reasoning?: {
579
+ effort: string;
580
+ }; /** 服务端多轮续写;opaque replay 的 response id 映射到此字段,而非 item_reference */
568
581
  previous_response_id?: string;
569
582
  stream: true;
570
583
  };
@@ -637,6 +650,8 @@ declare class ResponsesAdapter extends AdapterBase {
637
650
  private apiKey;
638
651
  private baseUrl;
639
652
  private fetchFn;
653
+ private headers;
654
+ private extraBody;
640
655
  constructor(options: ResponsesAdapterOptions);
641
656
  protected buildRequest(request: NormalizedRequest): ResponsesAPIRequest;
642
657
  protected runStream(providerRequest: ResponsesAPIRequest, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
@@ -648,7 +663,9 @@ type MessagesAdapterOptions = {
648
663
  apiKey: string;
649
664
  apiVersion?: string;
650
665
  baseUrl?: string; /** 可注入自定义 fetch 实现(用于测试/代理) */
651
- fetch?: FetchFn;
666
+ fetch?: FetchFn; /** 额外请求头;后写覆盖内置 x-api-key / Content-Type / anthropic-version */
667
+ headers?: Record<string, string>; /** 额外 body 顶层字段;浅层合并,同名键可覆盖 */
668
+ extraBody?: Record<string, unknown>;
652
669
  };
653
670
  type MessagesAPIRequest = {
654
671
  model: string;
@@ -666,6 +683,8 @@ type MessagesAPIRequest = {
666
683
  thinking?: {
667
684
  type: "enabled";
668
685
  budget_tokens: number;
686
+ } | {
687
+ type: "disabled";
669
688
  };
670
689
  stream: true;
671
690
  };
@@ -706,6 +725,8 @@ declare class MessagesAdapter extends AdapterBase {
706
725
  private apiVersion;
707
726
  private baseUrl;
708
727
  private fetchFn;
728
+ private headers;
729
+ private extraBody;
709
730
  constructor(options: MessagesAdapterOptions);
710
731
  protected buildRequest(request: NormalizedRequest): MessagesAPIRequest;
711
732
  protected runStream(providerRequest: MessagesAPIRequest, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
@@ -715,7 +736,9 @@ declare class MessagesAdapter extends AdapterBase {
715
736
  type ChatCompletionsAdapterOptions = {
716
737
  apiKey: string;
717
738
  baseUrl?: string;
718
- fetch?: FetchFn;
739
+ fetch?: FetchFn; /** 额外请求头;后写覆盖内置 Authorization / Content-Type */
740
+ headers?: Record<string, string>; /** 额外 body 顶层字段;浅层合并,同名键可覆盖 */
741
+ extraBody?: Record<string, unknown>;
719
742
  };
720
743
  type ChatRequest = {
721
744
  model: string;
@@ -729,7 +752,8 @@ type ChatRequest = {
729
752
  };
730
753
  metadata?: Record<string, string>;
731
754
  temperature?: number;
732
- max_tokens?: number;
755
+ max_tokens?: number; /** Portable reasoningLevel → reasoning_effort */
756
+ reasoning_effort?: string;
733
757
  stream: true;
734
758
  n: 1;
735
759
  };
@@ -763,6 +787,8 @@ declare class ChatCompletionsAdapter extends AdapterBase {
763
787
  private apiKey;
764
788
  private baseUrl;
765
789
  private fetchFn;
790
+ private headers;
791
+ private extraBody;
766
792
  constructor(options: ChatCompletionsAdapterOptions);
767
793
  protected buildRequest(request: NormalizedRequest): ChatRequest;
768
794
  protected runStream(providerRequest: ChatRequest, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
@@ -772,13 +798,16 @@ declare class ChatCompletionsAdapter extends AdapterBase {
772
798
  type OllamaAdapterOptions = {
773
799
  /** Ollama 服务地址,默认 http://localhost:11434 */baseUrl?: string; /** 可选 API key(用于需要认证的代理场景) */
774
800
  apiKey?: string; /** 可注入自定义 fetch 实现 */
775
- fetch?: FetchFn;
801
+ fetch?: FetchFn; /** 额外请求头;后写覆盖内置 Content-Type / Authorization */
802
+ headers?: Record<string, string>; /** 额外 body 顶层字段;浅层合并,同名键可覆盖 */
803
+ extraBody?: Record<string, unknown>;
776
804
  };
777
805
  type OllamaChatRequest = {
778
806
  model: string;
779
807
  messages: OllamaMessage[];
780
808
  stream: true;
781
- tools?: OllamaTool[];
809
+ tools?: OllamaTool[]; /** Portable reasoningLevel → think;minimal/xhigh 不支持 */
810
+ think?: boolean | "low" | "medium" | "high";
782
811
  options?: {
783
812
  temperature?: number;
784
813
  num_predict?: number;
@@ -811,6 +840,8 @@ declare class OllamaAdapter extends AdapterBase {
811
840
  private baseUrl;
812
841
  private apiKey;
813
842
  private fetchFn;
843
+ private headers;
844
+ private extraBody;
814
845
  constructor(options?: OllamaAdapterOptions);
815
846
  protected buildRequest(request: NormalizedRequest): OllamaChatRequest;
816
847
  protected runStream(providerRequest: OllamaChatRequest, factory: EventFactory, request: NormalizedRequest): AsyncIterable<AIStreamEvent>;
@@ -857,7 +888,8 @@ type MockHandlerContext = {
857
888
  previousReplay: ReplayItem[];
858
889
  pendingToolCalls: readonly ToolCallItem[];
859
890
  history: readonly MockHistoryRecord[]; /** 请求的 AbortSignal,handler 可检查 signal.aborted 提前退出。 */
860
- signal?: AbortSignal;
891
+ signal?: AbortSignal; /** 当前请求的 portable reasoningLevel(若设置)。 */
892
+ reasoningLevel?: ReasoningLevel;
861
893
  };
862
894
  type MockWarningStep = {
863
895
  type: "warning";
@@ -1200,6 +1232,52 @@ declare function createCompletionGate(): {
1200
1232
  tryComplete(): boolean;
1201
1233
  };
1202
1234
  //#endregion
1235
+ //#region src/helpers/provider-request-options.d.ts
1236
+ /**
1237
+ * Provider 请求 headers / body 扩展合并
1238
+ *
1239
+ * 供真实后端 adapter 构造选项 `headers` / `extraBody` 使用:
1240
+ * - headers:内置鉴权头为基,自定义后写覆盖
1241
+ * - extraBody:已构建 body 为基,额外字段浅层 spread,同名顶层键可覆盖
1242
+ */
1243
+ /** 合并内置 headers 与自定义 headers;自定义后写覆盖同名键。 */
1244
+ declare function mergeProviderHeaders(base: Record<string, string>, custom?: Record<string, string>): Record<string, string>;
1245
+ /**
1246
+ * 将构造期 extraBody 浅层合并到已构建的 provider body。
1247
+ * 无 extraBody 时原样返回;有则允许覆盖同名顶层键。
1248
+ */
1249
+ declare function applyExtraBody<T extends object>(body: T, extraBody?: Record<string, unknown>): T;
1250
+ //#endregion
1251
+ //#region src/helpers/reasoning-level.d.ts
1252
+ declare const REASONING_LEVELS: readonly ["none", "minimal", "low", "medium", "high", "xhigh"];
1253
+ declare const REASONING_LEVEL_SET: ReadonlySet<string>;
1254
+ type OpenAIReasoningEffort = ReasoningLevel;
1255
+ type MessagesThinkingConfig = {
1256
+ type: "disabled";
1257
+ } | {
1258
+ type: "enabled";
1259
+ budget_tokens: number;
1260
+ };
1261
+ type OllamaThinkValue = false | "low" | "medium" | "high";
1262
+ /** 若 level 不在 supported 集合内则抛 AIRequestError。 */
1263
+ declare function assertSupportedReasoningLevel(level: ReasoningLevel, supported: ReadonlySet<ReasoningLevel>, adapterKind: string): void;
1264
+ /** Responses API:`reasoning: { effort }` */
1265
+ declare function mapResponsesReasoning(level: ReasoningLevel): {
1266
+ effort: OpenAIReasoningEffort;
1267
+ };
1268
+ /** Chat Completions:顶层 `reasoning_effort` */
1269
+ declare function mapChatCompletionsReasoningEffort(level: ReasoningLevel): OpenAIReasoningEffort;
1270
+ /**
1271
+ * Messages thinking budget。
1272
+ * 基于 maxTokens 按比例推导,clamp 到 [1024, max(1024, maxTokens - 1)],
1273
+ * 满足 Anthropic budget_tokens < max_tokens。
1274
+ */
1275
+ declare function mapMessagesThinkingBudget(level: Exclude<ReasoningLevel, "none">, maxTokens: number): number;
1276
+ /** Messages API:`thinking` 字段 */
1277
+ declare function mapMessagesThinking(level: ReasoningLevel, maxTokens: number): MessagesThinkingConfig;
1278
+ /** Ollama:`think` 字段;minimal/xhigh 不支持 */
1279
+ declare function mapOllamaThink(level: ReasoningLevel): OllamaThinkValue;
1280
+ //#endregion
1203
1281
  //#region src/helpers/request-mapper.d.ts
1204
1282
  declare class NormalizedRequestMapper {
1205
1283
  readonly kind: string;
@@ -1228,5 +1306,5 @@ declare class NormalizedRequestMapper {
1228
1306
  private ensureBlocks;
1229
1307
  }
1230
1308
  //#endregion
1231
- export { type AIClient, AIError, AIMappingError, AIProviderError, type AIRequest, AIRequestError, type AIResponse, AIStreamError, type AIStreamEvent, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, type AuxiliaryFinalizeOptions, type AuxiliaryFinalizeResult, type AuxiliaryInfo, type BackendAdapter, type BackendTrace, type BillingInfo, type BillingPostprocessHook, type BillingSource, ChatCompletionsAdapter, type ChatCompletionsAdapterOptions, type ContentBlock, type CreateAIClientOptions, type EventFactory, type EventFactoryBackend, type EventFactoryState, type FetchFn, type IncludeSettings, IncrementalStreamParser, type InputItem, type InstructionBlock, type JsonContentBlock, type LookupResult, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, type MessageCompletedEvent, type MessageDeltaEvent, type MessageItem, type MessageStartedEvent, MessagesAdapter, type MessagesAdapterOptions, MockAdapter, type MockAdapterOptions, type MockAuxiliaryStep, type MockCompleteStep, type MockErrorStep, type MockHandler, type MockHandlerContext, type MockHistoryRecord, type MockInputExpectation, type MockInterruptStep, type MockMessageStep, type MockOutputStep, type MockReasoningStep, type MockRequestExpectation, type MockStaticHandler, type MockStep, type MockTextStreamOptions, type MockThrowStep, type MockToolCallStep, type MockWarningStep, type NormalizeOptions, type NormalizedRequest, NormalizedRequestMapper, OllamaAdapter, type OllamaAdapterOptions, type OpaqueEnvelopeResult, type OpaqueItem, type OpenProviderJsonStreamOptions, type OpenedProviderStream, type OutputItem, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, type ProviderStreamBatch, type ProviderStreamBatchOptions, type ReasoningCompletedEvent, type ReasoningDeltaEvent, type ReasoningItem, type ReasoningStartedEvent, type ReplayItem, type ResponseAuxiliaryEvent, type ResponseCompletedEvent, type ResponseStartedEvent, type ResponseWarningEvent, ResponsesAdapter, type ResponsesAdapterOptions, type SseJsonEvent, type StopReason, type StreamEventBase, type StreamParseResult, type StreamResult, type StreamSplitResult, type SyntheticStreamOptions, type TextContentBlock, type ToolCallCompletedEvent, type ToolCallDeltaEvent, type ToolCallItem, type ToolCallStartedEvent, type ToolChoice, type ToolDefinition, type ToolResultItem, type Usage, type UsageSource, type ValidationIssue, WarningCode, aggregateEvents, assertMockRequest, assertOpaqueReplayEnvelope, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createChatCompletionsSseParser, createCompletionGate, createEventFactory, createNdjsonLineParser, createSseJsonParser, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, iterateProviderStreamBatches, jsonBlock, mapReasoningVisibility, mapStopReason, measureJsonDepth, messageItem, normalizeRequest, opaqueBlock, opaqueItem, openProviderJsonStream, parseChatCompletionsDataLine, parseSseJsonFrame, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
1309
+ export { type AIClient, AIError, AIMappingError, AIProviderError, type AIRequest, AIRequestError, type AIResponse, AIStreamError, type AIStreamEvent, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, type AuxiliaryFinalizeOptions, type AuxiliaryFinalizeResult, type AuxiliaryInfo, type BackendAdapter, type BackendTrace, type BillingInfo, type BillingPostprocessHook, type BillingSource, ChatCompletionsAdapter, type ChatCompletionsAdapterOptions, type ContentBlock, type CreateAIClientOptions, type EventFactory, type EventFactoryBackend, type EventFactoryState, type FetchFn, type IncludeSettings, IncrementalStreamParser, type InputItem, type InstructionBlock, type JsonContentBlock, type LookupResult, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, type MessageCompletedEvent, type MessageDeltaEvent, type MessageItem, type MessageStartedEvent, MessagesAdapter, type MessagesAdapterOptions, type MessagesThinkingConfig, MockAdapter, type MockAdapterOptions, type MockAuxiliaryStep, type MockCompleteStep, type MockErrorStep, type MockHandler, type MockHandlerContext, type MockHistoryRecord, type MockInputExpectation, type MockInterruptStep, type MockMessageStep, type MockOutputStep, type MockReasoningStep, type MockRequestExpectation, type MockStaticHandler, type MockStep, type MockTextStreamOptions, type MockThrowStep, type MockToolCallStep, type MockWarningStep, type NormalizeOptions, type NormalizedRequest, NormalizedRequestMapper, OllamaAdapter, type OllamaAdapterOptions, type OllamaThinkValue, type OpaqueEnvelopeResult, type OpaqueItem, type OpenAIReasoningEffort, type OpenProviderJsonStreamOptions, type OpenedProviderStream, type OutputItem, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, type ProviderStreamBatch, type ProviderStreamBatchOptions, REASONING_LEVELS, REASONING_LEVEL_SET, type ReasoningCompletedEvent, type ReasoningDeltaEvent, type ReasoningItem, type ReasoningLevel, type ReasoningStartedEvent, type ReplayItem, type ResponseAuxiliaryEvent, type ResponseCompletedEvent, type ResponseStartedEvent, type ResponseWarningEvent, ResponsesAdapter, type ResponsesAdapterOptions, type SseJsonEvent, type StopReason, type StreamEventBase, type StreamParseResult, type StreamResult, type StreamSplitResult, type SyntheticStreamOptions, type TextContentBlock, type ToolCallCompletedEvent, type ToolCallDeltaEvent, type ToolCallItem, type ToolCallStartedEvent, type ToolChoice, type ToolDefinition, type ToolResultItem, type Usage, type UsageSource, type ValidationIssue, WarningCode, aggregateEvents, applyExtraBody, assertMockRequest, assertOpaqueReplayEnvelope, assertSupportedReasoningLevel, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createChatCompletionsSseParser, createCompletionGate, createEventFactory, createNdjsonLineParser, createSseJsonParser, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, iterateProviderStreamBatches, jsonBlock, mapChatCompletionsReasoningEffort, mapMessagesThinking, mapMessagesThinkingBudget, mapOllamaThink, mapReasoningVisibility, mapResponsesReasoning, mapStopReason, measureJsonDepth, mergeProviderHeaders, messageItem, normalizeRequest, opaqueBlock, opaqueItem, openProviderJsonStream, parseChatCompletionsDataLine, parseSseJsonFrame, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
1232
1310
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -80,6 +80,14 @@ const TOOL_RESULT_OUTCOMES = /* @__PURE__ */ new Set([
80
80
  "rejected"
81
81
  ]);
82
82
  const INCLUDE_MODES = /* @__PURE__ */ new Set(["off", "best_effort"]);
83
+ const REASONING_LEVELS$1 = /* @__PURE__ */ new Set([
84
+ "none",
85
+ "minimal",
86
+ "low",
87
+ "medium",
88
+ "high",
89
+ "xhigh"
90
+ ]);
83
91
  function isRecord(value) {
84
92
  return typeof value === "object" && value !== null;
85
93
  }
@@ -248,6 +256,9 @@ function validateRequest(request) {
248
256
  message: "maxOutputTokens must be a positive integer"
249
257
  });
250
258
  }
259
+ if (request.reasoningLevel !== void 0) {
260
+ if (typeof request.reasoningLevel !== "string" || !REASONING_LEVELS$1.has(request.reasoningLevel)) pushIssue(issues, "reasoningLevel", "REASONING_LEVEL_INVALID", "reasoningLevel must be one of: none, minimal, low, medium, high, xhigh");
261
+ }
251
262
  if (request.include !== void 0) validateInclude(request.include, issues);
252
263
  if (request.metadata !== void 0) {
253
264
  if (!isRecord(request.metadata)) pushIssue(issues, "metadata", "METADATA_INVALID", "metadata must be an object");
@@ -1660,6 +1671,102 @@ function createCompletionGate() {
1660
1671
  };
1661
1672
  }
1662
1673
  //#endregion
1674
+ //#region src/helpers/provider-request-options.ts
1675
+ /**
1676
+ * Provider 请求 headers / body 扩展合并
1677
+ *
1678
+ * 供真实后端 adapter 构造选项 `headers` / `extraBody` 使用:
1679
+ * - headers:内置鉴权头为基,自定义后写覆盖
1680
+ * - extraBody:已构建 body 为基,额外字段浅层 spread,同名顶层键可覆盖
1681
+ */
1682
+ /** 合并内置 headers 与自定义 headers;自定义后写覆盖同名键。 */
1683
+ function mergeProviderHeaders(base, custom) {
1684
+ if (!custom) return base;
1685
+ return {
1686
+ ...base,
1687
+ ...custom
1688
+ };
1689
+ }
1690
+ /**
1691
+ * 将构造期 extraBody 浅层合并到已构建的 provider body。
1692
+ * 无 extraBody 时原样返回;有则允许覆盖同名顶层键。
1693
+ */
1694
+ function applyExtraBody(body, extraBody) {
1695
+ if (!extraBody) return body;
1696
+ return {
1697
+ ...body,
1698
+ ...extraBody
1699
+ };
1700
+ }
1701
+ //#endregion
1702
+ //#region src/helpers/reasoning-level.ts
1703
+ /**
1704
+ * Portable reasoningLevel → provider wire 字段映射
1705
+ *
1706
+ * 第一版只处理 level 枚举;budget/summary 等特化字段不在此层。
1707
+ * 无法映射的 level 抛 AIRequestError(UNSUPPORTED_REASONING_LEVEL)。
1708
+ */
1709
+ const REASONING_LEVELS = [
1710
+ "none",
1711
+ "minimal",
1712
+ "low",
1713
+ "medium",
1714
+ "high",
1715
+ "xhigh"
1716
+ ];
1717
+ const REASONING_LEVEL_SET = new Set(REASONING_LEVELS);
1718
+ const MESSAGES_BUDGET_RATIOS = {
1719
+ minimal: .02,
1720
+ low: .1,
1721
+ medium: .3,
1722
+ high: .6,
1723
+ xhigh: .9
1724
+ };
1725
+ const OLLAMA_SUPPORTED = /* @__PURE__ */ new Set([
1726
+ "none",
1727
+ "low",
1728
+ "medium",
1729
+ "high"
1730
+ ]);
1731
+ /** 若 level 不在 supported 集合内则抛 AIRequestError。 */
1732
+ function assertSupportedReasoningLevel(level, supported, adapterKind) {
1733
+ if (supported.has(level)) return;
1734
+ throw new AIRequestError(`reasoningLevel "${level}" is not supported by the ${adapterKind} adapter`, "UNSUPPORTED_REASONING_LEVEL");
1735
+ }
1736
+ /** Responses API:`reasoning: { effort }` */
1737
+ function mapResponsesReasoning(level) {
1738
+ return { effort: level };
1739
+ }
1740
+ /** Chat Completions:顶层 `reasoning_effort` */
1741
+ function mapChatCompletionsReasoningEffort(level) {
1742
+ return level;
1743
+ }
1744
+ /**
1745
+ * Messages thinking budget。
1746
+ * 基于 maxTokens 按比例推导,clamp 到 [1024, max(1024, maxTokens - 1)],
1747
+ * 满足 Anthropic budget_tokens < max_tokens。
1748
+ */
1749
+ function mapMessagesThinkingBudget(level, maxTokens) {
1750
+ const ratio = MESSAGES_BUDGET_RATIOS[level];
1751
+ const raw = Math.round(maxTokens * ratio);
1752
+ const upper = Math.max(1024, maxTokens - 1);
1753
+ return Math.min(Math.max(raw, 1024), upper);
1754
+ }
1755
+ /** Messages API:`thinking` 字段 */
1756
+ function mapMessagesThinking(level, maxTokens) {
1757
+ if (level === "none") return { type: "disabled" };
1758
+ return {
1759
+ type: "enabled",
1760
+ budget_tokens: mapMessagesThinkingBudget(level, maxTokens)
1761
+ };
1762
+ }
1763
+ /** Ollama:`think` 字段;minimal/xhigh 不支持 */
1764
+ function mapOllamaThink(level) {
1765
+ assertSupportedReasoningLevel(level, OLLAMA_SUPPORTED, "ollama");
1766
+ if (level === "none") return false;
1767
+ return level;
1768
+ }
1769
+ //#endregion
1663
1770
  //#region src/helpers/request-mapper.ts
1664
1771
  var NormalizedRequestMapper = class {
1665
1772
  kind;
@@ -1836,11 +1943,15 @@ var ResponsesAdapter = class extends AdapterBase {
1836
1943
  apiKey;
1837
1944
  baseUrl;
1838
1945
  fetchFn;
1946
+ headers;
1947
+ extraBody;
1839
1948
  constructor(options) {
1840
1949
  super();
1841
1950
  this.apiKey = options.apiKey;
1842
1951
  this.baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
1843
1952
  this.fetchFn = options.fetch ?? globalThis.fetch;
1953
+ this.headers = options.headers;
1954
+ this.extraBody = options.extraBody;
1844
1955
  }
1845
1956
  buildRequest(request) {
1846
1957
  const input = [];
@@ -1918,7 +2029,8 @@ var ResponsesAdapter = class extends AdapterBase {
1918
2029
  if (request.temperature !== void 0) body.temperature = request.temperature;
1919
2030
  if (request.maxOutputTokens !== void 0) body.max_output_tokens = request.maxOutputTokens;
1920
2031
  if (request.metadata) body.metadata = request.metadata;
1921
- return body;
2032
+ if (request.reasoningLevel !== void 0) body.reasoning = mapResponsesReasoning(request.reasoningLevel);
2033
+ return applyExtraBody(body, this.extraBody);
1922
2034
  }
1923
2035
  async *runStream(providerRequest, factory, request) {
1924
2036
  const auxiliary = this.createAuxiliaryState(request);
@@ -1926,10 +2038,10 @@ var ResponsesAdapter = class extends AdapterBase {
1926
2038
  const { reader } = await openProviderJsonStream({
1927
2039
  fetchFn: this.fetchFn,
1928
2040
  url: `${this.baseUrl}/responses`,
1929
- headers: {
2041
+ headers: mergeProviderHeaders({
1930
2042
  "Content-Type": "application/json",
1931
2043
  Authorization: `Bearer ${this.apiKey}`
1932
- },
2044
+ }, this.headers),
1933
2045
  body: providerRequest,
1934
2046
  signal: request.signal
1935
2047
  });
@@ -2240,12 +2352,16 @@ var MessagesAdapter = class extends AdapterBase {
2240
2352
  apiVersion;
2241
2353
  baseUrl;
2242
2354
  fetchFn;
2355
+ headers;
2356
+ extraBody;
2243
2357
  constructor(options) {
2244
2358
  super();
2245
2359
  this.apiKey = options.apiKey;
2246
2360
  this.apiVersion = options.apiVersion ?? "2023-06-01";
2247
2361
  this.baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
2248
2362
  this.fetchFn = options.fetch ?? globalThis.fetch;
2363
+ this.headers = options.headers;
2364
+ this.extraBody = options.extraBody;
2249
2365
  }
2250
2366
  buildRequest(request) {
2251
2367
  const messages = [];
@@ -2351,7 +2467,8 @@ var MessagesAdapter = class extends AdapterBase {
2351
2467
  })
2352
2468
  });
2353
2469
  if (request.temperature !== void 0) body.temperature = request.temperature;
2354
- return body;
2470
+ if (request.reasoningLevel !== void 0) body.thinking = mapMessagesThinking(request.reasoningLevel, body.max_tokens);
2471
+ return applyExtraBody(body, this.extraBody);
2355
2472
  }
2356
2473
  async *runStream(providerRequest, factory, request) {
2357
2474
  const auxiliary = this.createAuxiliaryState(request);
@@ -2360,11 +2477,11 @@ var MessagesAdapter = class extends AdapterBase {
2360
2477
  const { reader, headers } = await openProviderJsonStream({
2361
2478
  fetchFn: this.fetchFn,
2362
2479
  url: `${this.baseUrl}/messages`,
2363
- headers: {
2480
+ headers: mergeProviderHeaders({
2364
2481
  "Content-Type": "application/json",
2365
2482
  "x-api-key": this.apiKey,
2366
2483
  "anthropic-version": this.apiVersion
2367
- },
2484
+ }, this.headers),
2368
2485
  body: providerRequest,
2369
2486
  signal: request.signal
2370
2487
  });
@@ -2639,11 +2756,15 @@ var ChatCompletionsAdapter = class extends AdapterBase {
2639
2756
  apiKey;
2640
2757
  baseUrl;
2641
2758
  fetchFn;
2759
+ headers;
2760
+ extraBody;
2642
2761
  constructor(options) {
2643
2762
  super();
2644
2763
  this.apiKey = options.apiKey;
2645
2764
  this.baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
2646
2765
  this.fetchFn = options.fetch ?? globalThis.fetch;
2766
+ this.headers = options.headers;
2767
+ this.extraBody = options.extraBody;
2647
2768
  }
2648
2769
  buildRequest(request) {
2649
2770
  const messages = [];
@@ -2737,7 +2858,8 @@ var ChatCompletionsAdapter = class extends AdapterBase {
2737
2858
  if (request.temperature !== void 0) body.temperature = request.temperature;
2738
2859
  if (request.maxOutputTokens !== void 0) body.max_tokens = request.maxOutputTokens;
2739
2860
  if (request.metadata) body.metadata = request.metadata;
2740
- return body;
2861
+ if (request.reasoningLevel !== void 0) body.reasoning_effort = mapChatCompletionsReasoningEffort(request.reasoningLevel);
2862
+ return applyExtraBody(body, this.extraBody);
2741
2863
  }
2742
2864
  async *runStream(providerRequest, factory, request) {
2743
2865
  const auxiliary = this.createAuxiliaryState(request);
@@ -2745,10 +2867,10 @@ var ChatCompletionsAdapter = class extends AdapterBase {
2745
2867
  const { reader } = await openProviderJsonStream({
2746
2868
  fetchFn: this.fetchFn,
2747
2869
  url: `${this.baseUrl}/chat/completions`,
2748
- headers: {
2870
+ headers: mergeProviderHeaders({
2749
2871
  "Content-Type": "application/json",
2750
2872
  Authorization: `Bearer ${this.apiKey}`
2751
- },
2873
+ }, this.headers),
2752
2874
  body: providerRequest,
2753
2875
  signal: request.signal
2754
2876
  });
@@ -2974,11 +3096,15 @@ var OllamaAdapter = class extends AdapterBase {
2974
3096
  baseUrl;
2975
3097
  apiKey;
2976
3098
  fetchFn;
3099
+ headers;
3100
+ extraBody;
2977
3101
  constructor(options = {}) {
2978
3102
  super();
2979
3103
  this.baseUrl = options.baseUrl ?? "http://localhost:11434";
2980
3104
  this.apiKey = options.apiKey;
2981
3105
  this.fetchFn = options.fetch ?? globalThis.fetch;
3106
+ this.headers = options.headers;
3107
+ this.extraBody = options.extraBody;
2982
3108
  }
2983
3109
  buildRequest(request) {
2984
3110
  const messages = [];
@@ -3076,7 +3202,8 @@ var OllamaAdapter = class extends AdapterBase {
3076
3202
  if (request.temperature !== void 0) body.options.temperature = request.temperature;
3077
3203
  if (request.maxOutputTokens !== void 0) body.options.num_predict = request.maxOutputTokens;
3078
3204
  }
3079
- return body;
3205
+ if (request.reasoningLevel !== void 0) body.think = mapOllamaThink(request.reasoningLevel);
3206
+ return applyExtraBody(body, this.extraBody);
3080
3207
  }
3081
3208
  async *runStream(providerRequest, factory, request) {
3082
3209
  const auxiliary = this.createAuxiliaryState(request);
@@ -3088,7 +3215,7 @@ var OllamaAdapter = class extends AdapterBase {
3088
3215
  const { reader } = await openProviderJsonStream({
3089
3216
  fetchFn: this.fetchFn,
3090
3217
  url: `${this.baseUrl}/api/chat`,
3091
- headers,
3218
+ headers: mergeProviderHeaders(headers, this.headers),
3092
3219
  body: providerRequest,
3093
3220
  signal: request.signal
3094
3221
  });
@@ -3257,7 +3384,7 @@ var MockAdapter = class extends AdapterBase {
3257
3384
  }
3258
3385
  async buildRequest(request) {
3259
3386
  const turnIndex = this.cursor;
3260
- const context = this.buildHandlerContext(turnIndex, request.signal);
3387
+ const context = this.buildHandlerContext(turnIndex, request);
3261
3388
  const remainingPendingToolCalls = consumePendingToolCalls(this.pendingToolCalls, request.input);
3262
3389
  const handlerResult = this.handler(request, context);
3263
3390
  this.cursor += 1;
@@ -3396,7 +3523,7 @@ var MockAdapter = class extends AdapterBase {
3396
3523
  rawResponseId: completion.rawResponseId
3397
3524
  }, factory);
3398
3525
  }
3399
- buildHandlerContext(turnIndex, signal) {
3526
+ buildHandlerContext(turnIndex, request) {
3400
3527
  return {
3401
3528
  turnIndex,
3402
3529
  previousReplay: this.previousReplay.map(cloneItem),
@@ -3406,7 +3533,8 @@ var MockAdapter = class extends AdapterBase {
3406
3533
  replay: record.replay.map(cloneItem),
3407
3534
  toolCalls: record.toolCalls.map(cloneItem)
3408
3535
  })),
3409
- signal
3536
+ signal: request.signal,
3537
+ reasoningLevel: request.reasoningLevel
3410
3538
  };
3411
3539
  }
3412
3540
  };
@@ -3632,6 +3760,6 @@ function cloneItem(item) {
3632
3760
  return structuredClone(item);
3633
3761
  }
3634
3762
  //#endregion
3635
- export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, IncrementalStreamParser, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, MessagesAdapter, MockAdapter, NormalizedRequestMapper, OllamaAdapter, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, ResponsesAdapter, WarningCode, aggregateEvents, assertMockRequest, assertOpaqueReplayEnvelope, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createChatCompletionsSseParser, createCompletionGate, createEventFactory, createNdjsonLineParser, createSseJsonParser, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, iterateProviderStreamBatches, jsonBlock, mapReasoningVisibility, mapStopReason, measureJsonDepth, messageItem, normalizeRequest, opaqueBlock, opaqueItem, openProviderJsonStream, parseChatCompletionsDataLine, parseSseJsonFrame, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
3763
+ export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, IncrementalStreamParser, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, MessagesAdapter, MockAdapter, NormalizedRequestMapper, OllamaAdapter, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, REASONING_LEVELS, REASONING_LEVEL_SET, ResponsesAdapter, WarningCode, aggregateEvents, applyExtraBody, assertMockRequest, assertOpaqueReplayEnvelope, assertSupportedReasoningLevel, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createChatCompletionsSseParser, createCompletionGate, createEventFactory, createNdjsonLineParser, createSseJsonParser, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, iterateProviderStreamBatches, jsonBlock, mapChatCompletionsReasoningEffort, mapMessagesThinking, mapMessagesThinkingBudget, mapOllamaThink, mapReasoningVisibility, mapResponsesReasoning, mapStopReason, measureJsonDepth, mergeProviderHeaders, messageItem, normalizeRequest, opaqueBlock, opaqueItem, openProviderJsonStream, parseChatCompletionsDataLine, parseSseJsonFrame, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
3636
3764
 
3637
3765
  //# sourceMappingURL=index.mjs.map