@codehz/ai 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.
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
@@ -1732,6 +1732,12 @@ const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
1732
1732
  "response.output_text.done",
1733
1733
  "response.reasoning.delta",
1734
1734
  "response.reasoning.done",
1735
+ "response.reasoning_summary_part.added",
1736
+ "response.reasoning_summary_part.done",
1737
+ "response.reasoning_summary_text.delta",
1738
+ "response.reasoning_summary_text.done",
1739
+ "response.reasoning_text.delta",
1740
+ "response.reasoning_text.done",
1735
1741
  "response.function_call_arguments.delta",
1736
1742
  "response.function_call_arguments.done",
1737
1743
  "response.content_part.added",
@@ -1740,11 +1746,46 @@ const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
1740
1746
  "response.refusal.done",
1741
1747
  "response.in_progress",
1742
1748
  "response.created",
1749
+ "response.queued",
1743
1750
  "response.completed",
1744
1751
  "response.failed",
1745
1752
  "response.incomplete",
1746
1753
  "error"
1747
1754
  ]);
1755
+ function createReasoningState(visibility = "summary") {
1756
+ return {
1757
+ visibility,
1758
+ hasDelta: false,
1759
+ doneTexts: [],
1760
+ completed: false
1761
+ };
1762
+ }
1763
+ function extractReasoningFromOutputItem(item) {
1764
+ const contentTexts = [];
1765
+ if (Array.isArray(item.content)) {
1766
+ for (const part of item.content) if (part && typeof part === "object" && part.type === "reasoning_text" && typeof part.text === "string") contentTexts.push(part.text);
1767
+ }
1768
+ const summaryTexts = [];
1769
+ if (Array.isArray(item.summary)) {
1770
+ for (const part of item.summary) if (part && typeof part === "object" && part.type === "summary_text" && typeof part.text === "string") summaryTexts.push(part.text);
1771
+ }
1772
+ if (contentTexts.length > 0) return {
1773
+ text: contentTexts.join("\n"),
1774
+ visibility: "full"
1775
+ };
1776
+ if (summaryTexts.length > 0) return {
1777
+ text: summaryTexts.join("\n"),
1778
+ visibility: "summary"
1779
+ };
1780
+ if (typeof item.encrypted_content === "string" && item.encrypted_content.length > 0) return {
1781
+ text: "",
1782
+ visibility: "opaque"
1783
+ };
1784
+ return {
1785
+ text: "",
1786
+ visibility: "summary"
1787
+ };
1788
+ }
1748
1789
  function isReplayCanonicalInput(item) {
1749
1790
  return item.type === "message" && item.role === "assistant" || item.type === "reasoning" || item.type === "function_call";
1750
1791
  }
@@ -1754,16 +1795,40 @@ function hasReplayCanonicalInput(input) {
1754
1795
  function extractFailureMessage(response) {
1755
1796
  return response.error?.message ?? response.failure?.message ?? "unknown";
1756
1797
  }
1757
- function canonicalToResponsesBlock(b) {
1758
- if (b.type === "text") return {
1759
- type: "text",
1760
- 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
1761
1817
  };
1762
- if (b.type === "json") return {
1763
- type: "text",
1764
- 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)
1765
1831
  };
1766
- throw new AIRequestError(`responses does not support content block type "${b.type}" in canonical mapping`, "UNSUPPORTED_CONTENT_BLOCK");
1767
1832
  }
1768
1833
  var ResponsesAdapter = class extends AdapterBase {
1769
1834
  kind = "responses";
@@ -1779,36 +1844,23 @@ var ResponsesAdapter = class extends AdapterBase {
1779
1844
  }
1780
1845
  buildRequest(request) {
1781
1846
  const input = [];
1847
+ let previousResponseId;
1848
+ let reasoningIndex = 0;
1782
1849
  for (const item of request.input) switch (item.type) {
1783
1850
  case "message":
1784
- if (item.role === "assistant") {
1785
- const blocks = mapper$3.ensureTextBlocks(item.content, `assistant message (${item.role}) content`).map(canonicalToResponsesBlock);
1786
- input.push({
1787
- type: "message",
1788
- role: item.role,
1789
- content: blocks
1790
- });
1791
- } else input.push({
1851
+ input.push({
1792
1852
  type: "message",
1793
1853
  role: item.role,
1794
- content: mapper$3.textFromBlocks(item.content, `input message (${item.role}) content`)
1854
+ content: messageContentAsString(item.content, `input message (${item.role}) content`)
1795
1855
  });
1796
1856
  break;
1797
- case "reasoning": {
1798
- const blocks = mapper$3.ensureReasoningBlocks(item.content, "reasoning content").map((b) => ({
1799
- type: "reasoning",
1800
- text: b.text
1801
- }));
1802
- input.push({
1803
- type: "reasoning",
1804
- content: blocks
1805
- });
1857
+ case "reasoning":
1858
+ input.push(mapReasoningInput(item, reasoningIndex++));
1806
1859
  break;
1807
- }
1808
1860
  case "tool_call":
1809
1861
  input.push({
1810
1862
  type: "function_call",
1811
- id: item.id,
1863
+ call_id: item.id,
1812
1864
  name: item.name,
1813
1865
  arguments: item.argumentsText
1814
1866
  });
@@ -1826,11 +1878,17 @@ var ResponsesAdapter = class extends AdapterBase {
1826
1878
  if (item.source !== "responses" || item.purpose !== "replay") break;
1827
1879
  assertOpaqueReplayEnvelope(item.payload);
1828
1880
  const payload = item.payload;
1829
- if ("id" in payload) {
1830
- 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");
1831
- 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({
1832
1890
  type: "item_reference",
1833
- id: payload.id
1891
+ id: itemReferenceId
1834
1892
  });
1835
1893
  }
1836
1894
  break;
@@ -1841,6 +1899,7 @@ var ResponsesAdapter = class extends AdapterBase {
1841
1899
  input,
1842
1900
  stream: true
1843
1901
  };
1902
+ if (previousResponseId) body.previous_response_id = previousResponseId;
1844
1903
  if (request.instructions) body.instructions = mapper$3.mapInstructions(request.instructions);
1845
1904
  body.tools = mapper$3.mapToolsIfPresent(request.tools, (t) => ({
1846
1905
  type: "function",
@@ -1879,7 +1938,28 @@ var ResponsesAdapter = class extends AdapterBase {
1879
1938
  let completedResponse;
1880
1939
  let unknownEventsWarned = false;
1881
1940
  const messageItemsWithDelta = /* @__PURE__ */ new Set();
1941
+ /** item_id → function name */
1882
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();
1945
+ const reasoningStates = /* @__PURE__ */ new Map();
1946
+ const resolveToolCallId = (itemId) => toolCallIds.get(itemId) ?? itemId;
1947
+ const ensureReasoningState = (itemId, visibility = "summary") => {
1948
+ let state = reasoningStates.get(itemId);
1949
+ if (!state) {
1950
+ state = createReasoningState(visibility);
1951
+ reasoningStates.set(itemId, state);
1952
+ }
1953
+ return state;
1954
+ };
1955
+ const completeReasoning = function* (itemId, text, visibility) {
1956
+ const state = ensureReasoningState(itemId, visibility);
1957
+ if (state.completed) return;
1958
+ state.completed = true;
1959
+ state.visibility = visibility;
1960
+ yield factory.reasoningCompleted(itemId);
1961
+ output.push(reasoningItem(text ? [textBlock(text)] : [], visibility, itemId));
1962
+ };
1883
1963
  for await (const batch of iterateProviderStreamBatches({
1884
1964
  reader,
1885
1965
  parser,
@@ -1901,18 +1981,33 @@ var ResponsesAdapter = class extends AdapterBase {
1901
1981
  case "message":
1902
1982
  yield factory.messageStarted(item.id);
1903
1983
  break;
1904
- case "reasoning":
1905
- yield factory.reasoningStarted(item.id, "full");
1984
+ case "reasoning": {
1985
+ const visibility = extractReasoningFromOutputItem(item).visibility;
1986
+ ensureReasoningState(item.id, visibility);
1987
+ yield factory.reasoningStarted(item.id, visibility);
1906
1988
  break;
1989
+ }
1907
1990
  case "function_call": {
1908
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;
1909
1993
  toolCallNames.set(item.id, name);
1910
- yield factory.toolCallStarted(item.id, name);
1994
+ toolCallIds.set(item.id, callId);
1995
+ yield factory.toolCallStarted(callId, name);
1911
1996
  break;
1912
1997
  }
1913
1998
  }
1914
1999
  continue;
1915
2000
  }
2001
+ if (sseEvent.type === "response.output_item.done") {
2002
+ const item = sseEvent.data.item;
2003
+ if (item.type === "reasoning") {
2004
+ const extracted = extractReasoningFromOutputItem(item);
2005
+ const state = ensureReasoningState(item.id, extracted.visibility);
2006
+ const text = extracted.text || (state.doneTexts.length > 0 ? state.doneTexts.join("\n") : "");
2007
+ yield* completeReasoning(item.id, text, extracted.visibility || state.visibility);
2008
+ }
2009
+ continue;
2010
+ }
1916
2011
  if (sseEvent.type === "response.output_text.delta") {
1917
2012
  const data = sseEvent.data;
1918
2013
  yield factory.messageDelta(data.item_id, textBlock(data.delta));
@@ -1926,26 +2021,75 @@ var ResponsesAdapter = class extends AdapterBase {
1926
2021
  output.push(messageItem([textBlock(data.text)], { id: data.item_id }));
1927
2022
  continue;
1928
2023
  }
2024
+ if (sseEvent.type === "response.reasoning_summary_text.delta") {
2025
+ const data = sseEvent.data;
2026
+ const state = ensureReasoningState(data.item_id, "summary");
2027
+ if (state.visibility === "opaque") state.visibility = "summary";
2028
+ if (data.delta) {
2029
+ state.hasDelta = true;
2030
+ yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
2031
+ }
2032
+ continue;
2033
+ }
2034
+ if (sseEvent.type === "response.reasoning_summary_text.done") {
2035
+ const data = sseEvent.data;
2036
+ const state = ensureReasoningState(data.item_id, "summary");
2037
+ if (state.visibility === "opaque") state.visibility = "summary";
2038
+ if (!state.hasDelta && data.text) {
2039
+ state.doneTexts.push(data.text);
2040
+ yield factory.reasoningDelta(data.item_id, textBlock(data.text));
2041
+ } else if (data.text) state.doneTexts.push(data.text);
2042
+ continue;
2043
+ }
2044
+ if (sseEvent.type === "response.reasoning_text.delta") {
2045
+ const data = sseEvent.data;
2046
+ const state = ensureReasoningState(data.item_id, "full");
2047
+ state.visibility = "full";
2048
+ if (data.delta) {
2049
+ state.hasDelta = true;
2050
+ yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
2051
+ }
2052
+ continue;
2053
+ }
2054
+ if (sseEvent.type === "response.reasoning_text.done") {
2055
+ const data = sseEvent.data;
2056
+ const state = ensureReasoningState(data.item_id, "full");
2057
+ state.visibility = "full";
2058
+ if (!state.hasDelta && data.text) {
2059
+ state.doneTexts.push(data.text);
2060
+ yield factory.reasoningDelta(data.item_id, textBlock(data.text));
2061
+ } else if (data.text) state.doneTexts.push(data.text);
2062
+ continue;
2063
+ }
2064
+ if (sseEvent.type === "response.reasoning_summary_part.added" || sseEvent.type === "response.reasoning_summary_part.done") continue;
1929
2065
  if (sseEvent.type === "response.reasoning.delta") {
1930
2066
  const data = sseEvent.data;
1931
- yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
2067
+ const state = ensureReasoningState(data.item_id, "full");
2068
+ state.visibility = "full";
2069
+ if (data.delta) {
2070
+ state.hasDelta = true;
2071
+ yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
2072
+ }
1932
2073
  continue;
1933
2074
  }
1934
2075
  if (sseEvent.type === "response.reasoning.done") {
1935
2076
  const data = sseEvent.data;
1936
- yield factory.reasoningCompleted(data.item_id);
1937
- output.push(reasoningItem([textBlock(data.text)], "full", data.item_id));
2077
+ const state = ensureReasoningState(data.item_id, "full");
2078
+ if (!state.hasDelta && data.text) yield factory.reasoningDelta(data.item_id, textBlock(data.text));
2079
+ yield* completeReasoning(data.item_id, data.text ?? state.doneTexts.join("\n"), "full");
1938
2080
  continue;
1939
2081
  }
1940
2082
  if (sseEvent.type === "response.function_call_arguments.delta") {
1941
2083
  const data = sseEvent.data;
1942
- 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 });
1943
2085
  continue;
1944
2086
  }
1945
2087
  if (sseEvent.type === "response.function_call_arguments.done") {
1946
2088
  const data = sseEvent.data;
1947
- const tcItem = toolCallItem(data.item_id, toolCallNames.get(data.item_id) ?? "unknown", data.arguments);
1948
- 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);
1949
2093
  output.push(tcItem);
1950
2094
  continue;
1951
2095
  }
@@ -1956,6 +2100,14 @@ var ResponsesAdapter = class extends AdapterBase {
1956
2100
  continue;
1957
2101
  }
1958
2102
  completedResponse = data.response;
2103
+ if (Array.isArray(data.response.output)) for (const item of data.response.output) {
2104
+ if (item?.type !== "reasoning" || !item.id) continue;
2105
+ const state = reasoningStates.get(item.id);
2106
+ if (state?.completed) continue;
2107
+ const extracted = extractReasoningFromOutputItem(item);
2108
+ const text = extracted.text || (state && state.doneTexts.length > 0 ? state.doneTexts.join("\n") : "");
2109
+ if (state || reasoningStates.has(item.id)) yield* completeReasoning(item.id, text, extracted.visibility);
2110
+ }
1959
2111
  if (sseEvent.type === "response.failed") yield factory.responseWarning(`Response failed: ${extractFailureMessage(data.response)}`, "PROVIDER_FAILURE");
1960
2112
  continue;
1961
2113
  }
@@ -1971,7 +2123,10 @@ var ResponsesAdapter = class extends AdapterBase {
1971
2123
  if (completedResponse.usage) auxiliary.recordUsage(usageFromOpenAIResponses(completedResponse.usage), "final", completedResponse.usage);
1972
2124
  }
1973
2125
  const replay = [...replayFromOutput(output)];
1974
- 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
+ }));
1975
2130
  const stopReason = completedResponse ? this.inferStopReason(completedResponse) : void 0;
1976
2131
  if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
1977
2132
  output,