@codehz/ai 0.4.3 → 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.
package/dist/index.d.mts CHANGED
@@ -564,49 +564,72 @@ type ResponsesAPIRequest = {
564
564
  };
565
565
  metadata?: Record<string, string>;
566
566
  temperature?: number;
567
- max_output_tokens?: number;
567
+ max_output_tokens?: number; /** 服务端多轮续写;opaque replay 的 response id 映射到此字段,而非 item_reference */
568
+ previous_response_id?: string;
568
569
  stream: true;
569
570
  };
570
- type ResponsesInputItem = {
571
+ /** EasyInputMessage:content 可为 string,或 input_* content parts */
572
+ type ResponsesEasyMessage = {
571
573
  type: "message";
572
- role: "user" | "assistant";
573
- content: string;
574
+ role: "user" | "assistant" | "system" | "developer";
575
+ content: string | ResponsesInputContentPart[];
576
+ };
577
+ type ResponsesInputContentPart = {
578
+ type: "input_text";
579
+ text: string;
574
580
  } | {
575
- type: "message";
576
- role: "assistant";
577
- content: ResponsesContentBlock[];
581
+ type: "input_image";
582
+ image_url: string;
583
+ detail?: "auto" | "low" | "high";
578
584
  } | {
585
+ type: "input_file";
586
+ file_url?: string;
587
+ file_id?: string;
588
+ filename?: string;
589
+ };
590
+ /** function_call:call_id 必填;id 是可选的 item id */
591
+ type ResponsesFunctionCall = {
579
592
  type: "function_call";
580
- id: string;
593
+ call_id: string;
581
594
  name: string;
582
595
  arguments: string;
583
- call_id?: string;
584
- } | {
596
+ id?: string;
597
+ status?: "in_progress" | "completed" | "incomplete";
598
+ };
599
+ type ResponsesFunctionCallOutput = {
585
600
  type: "function_call_output";
586
601
  call_id: string;
587
602
  output: string;
588
- } | {
603
+ id?: string;
604
+ status?: "in_progress" | "completed" | "incomplete";
605
+ };
606
+ /** reasoning:id + summary/content/encrypted_content,不是任意 content blocks */
607
+ type ResponsesReasoningInput = {
589
608
  type: "reasoning";
590
- content: ResponsesContentBlock[];
591
- } | {
592
- type: "item_reference";
593
609
  id: string;
610
+ summary: Array<{
611
+ type: "summary_text";
612
+ text: string;
613
+ }>;
614
+ content?: Array<{
615
+ type: "reasoning_text";
616
+ text: string;
617
+ }>;
618
+ encrypted_content?: string | null;
619
+ status?: "in_progress" | "completed" | "incomplete";
594
620
  };
595
- type ResponsesContentBlock = {
596
- type: "text";
597
- text: string;
598
- } | {
599
- type: "reasoning";
600
- text: string;
601
- } | {
602
- type: "refusal";
603
- refusal: string;
621
+ /** 引用既有 item(不是 response id) */
622
+ type ResponsesItemReference = {
623
+ type: "item_reference";
624
+ id: string;
604
625
  };
626
+ type ResponsesInputItem = ResponsesEasyMessage | ResponsesFunctionCall | ResponsesFunctionCallOutput | ResponsesReasoningInput | ResponsesItemReference;
605
627
  type ResponsesTool = {
606
628
  type: "function";
607
629
  name: string;
608
630
  description?: string;
609
631
  parameters: Record<string, unknown>;
632
+ strict?: boolean | null;
610
633
  };
611
634
  declare class ResponsesAdapter extends AdapterBase {
612
635
  readonly kind: "responses";
package/dist/index.mjs CHANGED
@@ -1795,16 +1795,40 @@ function hasReplayCanonicalInput(input) {
1795
1795
  function extractFailureMessage(response) {
1796
1796
  return response.error?.message ?? response.failure?.message ?? "unknown";
1797
1797
  }
1798
- function canonicalToResponsesBlock(b) {
1799
- if (b.type === "text") return {
1800
- type: "text",
1801
- text: b.text
1798
+ function readNonEmptyString(value, maxLen = 256) {
1799
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLen) return void 0;
1800
+ return value;
1801
+ }
1802
+ /** 将 canonical text/json blocks 压成 EasyInputMessage 的 string content。 */
1803
+ function messageContentAsString(blocks, field) {
1804
+ return mapper$3.textFromBlocks(blocks, field);
1805
+ }
1806
+ function mapReasoningInput(item, index) {
1807
+ const text = mapper$3.textFromBlocks(mapper$3.ensureReasoningBlocks(item.content, "reasoning content"), "reasoning content");
1808
+ const id = item.id && item.id.length > 0 ? item.id : `reasoning_replay_${index}`;
1809
+ if (item.visibility === "full") return {
1810
+ type: "reasoning",
1811
+ id,
1812
+ summary: [],
1813
+ content: text ? [{
1814
+ type: "reasoning_text",
1815
+ text
1816
+ }] : void 0
1802
1817
  };
1803
- if (b.type === "json") return {
1804
- type: "text",
1805
- text: JSON.stringify(b.json)
1818
+ return {
1819
+ type: "reasoning",
1820
+ id,
1821
+ summary: text ? [{
1822
+ type: "summary_text",
1823
+ text
1824
+ }] : []
1825
+ };
1826
+ }
1827
+ function extractOpaqueContinuationId(payload) {
1828
+ return {
1829
+ previousResponseId: readNonEmptyString(payload.previous_response_id) ?? (typeof payload.item_id === "string" ? void 0 : readNonEmptyString(payload.id)),
1830
+ itemReferenceId: readNonEmptyString(payload.item_id)
1806
1831
  };
1807
- throw new AIRequestError(`responses does not support content block type "${b.type}" in canonical mapping`, "UNSUPPORTED_CONTENT_BLOCK");
1808
1832
  }
1809
1833
  var ResponsesAdapter = class extends AdapterBase {
1810
1834
  kind = "responses";
@@ -1820,36 +1844,23 @@ var ResponsesAdapter = class extends AdapterBase {
1820
1844
  }
1821
1845
  buildRequest(request) {
1822
1846
  const input = [];
1847
+ let previousResponseId;
1848
+ let reasoningIndex = 0;
1823
1849
  for (const item of request.input) switch (item.type) {
1824
1850
  case "message":
1825
- if (item.role === "assistant") {
1826
- const blocks = mapper$3.ensureTextBlocks(item.content, `assistant message (${item.role}) content`).map(canonicalToResponsesBlock);
1827
- input.push({
1828
- type: "message",
1829
- role: item.role,
1830
- content: blocks
1831
- });
1832
- } else input.push({
1851
+ input.push({
1833
1852
  type: "message",
1834
1853
  role: item.role,
1835
- content: mapper$3.textFromBlocks(item.content, `input message (${item.role}) content`)
1854
+ content: messageContentAsString(item.content, `input message (${item.role}) content`)
1836
1855
  });
1837
1856
  break;
1838
- case "reasoning": {
1839
- const blocks = mapper$3.ensureReasoningBlocks(item.content, "reasoning content").map((b) => ({
1840
- type: "reasoning",
1841
- text: b.text
1842
- }));
1843
- input.push({
1844
- type: "reasoning",
1845
- content: blocks
1846
- });
1857
+ case "reasoning":
1858
+ input.push(mapReasoningInput(item, reasoningIndex++));
1847
1859
  break;
1848
- }
1849
1860
  case "tool_call":
1850
1861
  input.push({
1851
1862
  type: "function_call",
1852
- id: item.id,
1863
+ call_id: item.id,
1853
1864
  name: item.name,
1854
1865
  arguments: item.argumentsText
1855
1866
  });
@@ -1867,11 +1878,17 @@ var ResponsesAdapter = class extends AdapterBase {
1867
1878
  if (item.source !== "responses" || item.purpose !== "replay") break;
1868
1879
  assertOpaqueReplayEnvelope(item.payload);
1869
1880
  const payload = item.payload;
1870
- if ("id" in payload) {
1871
- if (typeof payload.id !== "string" || payload.id.length === 0 || payload.id.length > 256) throw new AIRequestError("Invalid opaque replay payload: id must be a non-empty string (max 256)", "INVALID_OPAQUE_REPLAY");
1872
- if (!hasReplayCanonicalInput(input)) input.push({
1881
+ for (const key of [
1882
+ "id",
1883
+ "previous_response_id",
1884
+ "item_id"
1885
+ ]) if (key in payload && (typeof payload[key] !== "string" || payload[key].length === 0 || payload[key].length > 256)) throw new AIRequestError(`Invalid opaque replay payload: ${key} must be a non-empty string (max 256)`, "INVALID_OPAQUE_REPLAY");
1886
+ const { previousResponseId: prevId, itemReferenceId } = extractOpaqueContinuationId(payload);
1887
+ if (!hasReplayCanonicalInput(input)) {
1888
+ if (prevId && !previousResponseId) previousResponseId = prevId;
1889
+ else if (itemReferenceId) input.push({
1873
1890
  type: "item_reference",
1874
- id: payload.id
1891
+ id: itemReferenceId
1875
1892
  });
1876
1893
  }
1877
1894
  break;
@@ -1882,6 +1899,7 @@ var ResponsesAdapter = class extends AdapterBase {
1882
1899
  input,
1883
1900
  stream: true
1884
1901
  };
1902
+ if (previousResponseId) body.previous_response_id = previousResponseId;
1885
1903
  if (request.instructions) body.instructions = mapper$3.mapInstructions(request.instructions);
1886
1904
  body.tools = mapper$3.mapToolsIfPresent(request.tools, (t) => ({
1887
1905
  type: "function",
@@ -1920,8 +1938,12 @@ var ResponsesAdapter = class extends AdapterBase {
1920
1938
  let completedResponse;
1921
1939
  let unknownEventsWarned = false;
1922
1940
  const messageItemsWithDelta = /* @__PURE__ */ new Set();
1941
+ /** item_id → function name */
1923
1942
  const toolCallNames = /* @__PURE__ */ new Map();
1943
+ /** item_id → call_id(canonical ToolCallItem.id / function_call_output.call_id) */
1944
+ const toolCallIds = /* @__PURE__ */ new Map();
1924
1945
  const reasoningStates = /* @__PURE__ */ new Map();
1946
+ const resolveToolCallId = (itemId) => toolCallIds.get(itemId) ?? itemId;
1925
1947
  const ensureReasoningState = (itemId, visibility = "summary") => {
1926
1948
  let state = reasoningStates.get(itemId);
1927
1949
  if (!state) {
@@ -1967,8 +1989,10 @@ var ResponsesAdapter = class extends AdapterBase {
1967
1989
  }
1968
1990
  case "function_call": {
1969
1991
  const name = typeof item.name === "string" ? item.name : "unknown";
1992
+ const callId = typeof item.call_id === "string" && item.call_id.length > 0 ? item.call_id : item.id;
1970
1993
  toolCallNames.set(item.id, name);
1971
- yield factory.toolCallStarted(item.id, name);
1994
+ toolCallIds.set(item.id, callId);
1995
+ yield factory.toolCallStarted(callId, name);
1972
1996
  break;
1973
1997
  }
1974
1998
  }
@@ -2057,13 +2081,15 @@ var ResponsesAdapter = class extends AdapterBase {
2057
2081
  }
2058
2082
  if (sseEvent.type === "response.function_call_arguments.delta") {
2059
2083
  const data = sseEvent.data;
2060
- if (data.delta) yield factory.toolCallDelta(data.item_id, { argumentsText: data.delta });
2084
+ if (data.delta) yield factory.toolCallDelta(resolveToolCallId(data.item_id), { argumentsText: data.delta });
2061
2085
  continue;
2062
2086
  }
2063
2087
  if (sseEvent.type === "response.function_call_arguments.done") {
2064
2088
  const data = sseEvent.data;
2065
- const tcItem = toolCallItem(data.item_id, toolCallNames.get(data.item_id) ?? "unknown", data.arguments);
2066
- yield factory.toolCallCompleted(data.item_id);
2089
+ const callId = resolveToolCallId(data.item_id);
2090
+ if (!toolCallIds.has(data.item_id)) toolCallIds.set(data.item_id, callId);
2091
+ const tcItem = toolCallItem(callId, toolCallNames.get(data.item_id) ?? "unknown", data.arguments);
2092
+ yield factory.toolCallCompleted(callId);
2067
2093
  output.push(tcItem);
2068
2094
  continue;
2069
2095
  }
@@ -2097,7 +2123,10 @@ var ResponsesAdapter = class extends AdapterBase {
2097
2123
  if (completedResponse.usage) auxiliary.recordUsage(usageFromOpenAIResponses(completedResponse.usage), "final", completedResponse.usage);
2098
2124
  }
2099
2125
  const replay = [...replayFromOutput(output)];
2100
- if (completedResponse?.id) replay.push(opaqueItem("responses", "replay", { id: completedResponse.id }));
2126
+ if (completedResponse?.id) replay.push(opaqueItem("responses", "replay", {
2127
+ id: completedResponse.id,
2128
+ previous_response_id: completedResponse.id
2129
+ }));
2101
2130
  const stopReason = completedResponse ? this.inferStopReason(completedResponse) : void 0;
2102
2131
  if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
2103
2132
  output,