Skip to content

Commit 9f2bb61

Browse files
committed
重构后端代理架构
1 parent 72e174f commit 9f2bb61

62 files changed

Lines changed: 8153 additions & 6948 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/codex_input_cursor.py

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
"""Cursor diff utilities for Codex provider input items.
2+
3+
The cursor is an auxiliary sequence of provider items that have already been
4+
absorbed into transcript. This module deliberately knows only how to compare
5+
provider item sequences.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import hashlib
11+
import json
12+
from collections.abc import Mapping, Sequence
13+
from dataclasses import dataclass
14+
from typing import Any
15+
16+
17+
DYNAMIC_FINGERPRINT_KEYS = frozenset({"id"})
18+
19+
20+
def canonical_provider_item_for_request(item: Any) -> Any:
21+
"""Return the Responses item shape Codex is expected to resend as input."""
22+
23+
if not isinstance(item, Mapping):
24+
return normalize_provider_item(item)
25+
26+
item_type = str(item.get("type") or "").strip()
27+
if item_type == "message":
28+
result = _pick(item, "type", "role", "content", "phase", "internal_chat_message_metadata_passthrough")
29+
if "content" in result:
30+
result["content"] = _canonical_message_content(result["content"])
31+
return result
32+
if item_type == "agent_message":
33+
result = _pick(item, "type", "author", "recipient", "content", "internal_chat_message_metadata_passthrough")
34+
if "content" in result:
35+
result["content"] = _canonical_agent_message_content(result["content"])
36+
return result
37+
if item_type == "reasoning":
38+
return _canonical_reasoning_item(item)
39+
if item_type == "function_call":
40+
return _pick(item, "type", "name", "namespace", "arguments", "call_id", "internal_chat_message_metadata_passthrough")
41+
if item_type == "function_call_output":
42+
return _pick(item, "type", "call_id", "output", "internal_chat_message_metadata_passthrough")
43+
if item_type == "custom_tool_call":
44+
return _pick(item, "type", "status", "call_id", "name", "input", "internal_chat_message_metadata_passthrough")
45+
if item_type == "custom_tool_call_output":
46+
return _pick(item, "type", "call_id", "name", "output", "internal_chat_message_metadata_passthrough")
47+
if item_type == "local_shell_call":
48+
return _pick(item, "type", "call_id", "status", "action", "internal_chat_message_metadata_passthrough")
49+
if item_type == "tool_search_call":
50+
return _pick(item, "type", "call_id", "status", "execution", "arguments", "internal_chat_message_metadata_passthrough")
51+
if item_type == "tool_search_output":
52+
return _pick(item, "type", "call_id", "status", "execution", "tools", "internal_chat_message_metadata_passthrough")
53+
if item_type == "web_search_call":
54+
return _pick(item, "type", "status", "action", "internal_chat_message_metadata_passthrough")
55+
if item_type == "image_generation_call":
56+
return _pick(item, "type", "status", "revised_prompt", "result", "internal_chat_message_metadata_passthrough")
57+
if item_type in {"compaction", "compaction_summary"}:
58+
result = _pick(item, "type", "encrypted_content", "internal_chat_message_metadata_passthrough")
59+
result["type"] = "compaction"
60+
return result
61+
if item_type == "context_compaction":
62+
return _pick(item, "type", "encrypted_content", "internal_chat_message_metadata_passthrough")
63+
if item_type == "additional_tools":
64+
return _pick(item, "type", "role", "tools")
65+
66+
return normalize_provider_item(item)
67+
68+
69+
def canonical_provider_items_for_request(items: Sequence[Any]) -> list[Any]:
70+
return [canonical_provider_item_for_request(item) for item in items]
71+
72+
73+
def _pick(item: Mapping[str, Any], *keys: str) -> dict[str, Any]:
74+
return {
75+
key: normalize_provider_item(item[key])
76+
for key in keys
77+
if key in item and key not in DYNAMIC_FINGERPRINT_KEYS
78+
}
79+
80+
81+
def _drop_none(item: dict[str, Any]) -> dict[str, Any]:
82+
return {key: value for key, value in item.items() if value is not None}
83+
84+
85+
def _canonical_reasoning_item(item: Mapping[str, Any]) -> dict[str, Any]:
86+
result = _drop_none(
87+
_pick(item, "type", "summary", "content", "encrypted_content", "internal_chat_message_metadata_passthrough")
88+
)
89+
content = result.get("content")
90+
if isinstance(content, list) and not _should_serialize_reasoning_content(content):
91+
del result["content"]
92+
return result
93+
94+
95+
def _should_serialize_reasoning_content(content: list[Any]) -> bool:
96+
return any(isinstance(part, Mapping) and part.get("type") == "reasoning_text" for part in content)
97+
98+
99+
def _canonical_message_content(content: Any) -> Any:
100+
if not isinstance(content, list):
101+
return normalize_provider_item(content)
102+
103+
canonical: list[Any] = []
104+
for part in content:
105+
if not isinstance(part, Mapping):
106+
canonical.append(normalize_provider_item(part))
107+
continue
108+
part_type = str(part.get("type") or "").strip()
109+
if part_type in {"input_text", "output_text"}:
110+
canonical.append(_pick(part, "type", "text"))
111+
elif part_type == "input_image":
112+
canonical.append(_pick(part, "type", "image_url", "detail"))
113+
else:
114+
canonical.append(normalize_provider_item(part))
115+
return canonical
116+
117+
118+
def _canonical_agent_message_content(content: Any) -> Any:
119+
if not isinstance(content, list):
120+
return normalize_provider_item(content)
121+
122+
canonical: list[Any] = []
123+
for part in content:
124+
if not isinstance(part, Mapping):
125+
canonical.append(normalize_provider_item(part))
126+
continue
127+
part_type = str(part.get("type") or "").strip()
128+
if part_type == "input_text":
129+
canonical.append(_pick(part, "type", "text"))
130+
elif part_type == "encrypted_content":
131+
canonical.append(_pick(part, "type", "encrypted_content"))
132+
else:
133+
canonical.append(normalize_provider_item(part))
134+
return canonical
135+
136+
137+
@dataclass(frozen=True)
138+
class CursorDiff:
139+
"""The suffix delta between the current cursor and a new input array."""
140+
141+
prefix_len: int
142+
pop: list[Any]
143+
append: list[Any]
144+
145+
def __iter__(self):
146+
yield self.prefix_len
147+
yield self.pop
148+
yield self.append
149+
150+
151+
def normalize_provider_item(value: Any) -> Any:
152+
"""Return a stable semantic projection used for provider item hashing.
153+
154+
Only exact ``id`` keys are removed. Semantic identifiers such as
155+
``call_id`` or ``file_id`` are preserved, as are opaque reasoning fields
156+
such as ``encrypted_content``.
157+
"""
158+
159+
if isinstance(value, Mapping):
160+
return {
161+
key: normalize_provider_item(child)
162+
for key, child in value.items()
163+
if key not in DYNAMIC_FINGERPRINT_KEYS
164+
}
165+
if isinstance(value, list):
166+
return [normalize_provider_item(child) for child in value]
167+
if isinstance(value, tuple):
168+
return [normalize_provider_item(child) for child in value]
169+
return value
170+
171+
172+
def fingerprint_provider_item(item: Any) -> str:
173+
"""Hash a provider item after semantic normalization."""
174+
175+
normalized = semantic_provider_item_for_fingerprint(canonical_provider_item_for_request(item))
176+
payload = json.dumps(
177+
normalized,
178+
ensure_ascii=False,
179+
sort_keys=True,
180+
separators=(",", ":"),
181+
).encode("utf-8")
182+
return hashlib.sha256(payload).hexdigest()
183+
184+
185+
def semantic_provider_item_for_fingerprint(value: Any) -> Any:
186+
if isinstance(value, Mapping):
187+
item_type = str(value.get("type") or "").strip()
188+
ignored_keys = {"id"}
189+
if item_type == "message":
190+
ignored_keys.add("phase")
191+
return {
192+
key: semantic_provider_item_for_fingerprint(child)
193+
for key, child in value.items()
194+
if key not in ignored_keys
195+
}
196+
if isinstance(value, list):
197+
return [semantic_provider_item_for_fingerprint(child) for child in value]
198+
if isinstance(value, tuple):
199+
return [semantic_provider_item_for_fingerprint(child) for child in value]
200+
return value
201+
202+
203+
def provider_items_equal(left: Any, right: Any) -> bool:
204+
"""Compare two provider items by normalized fingerprint."""
205+
206+
return fingerprint_provider_item(left) == fingerprint_provider_item(right)
207+
208+
209+
def longest_common_prefix_len(cursor: Sequence[Any], new_input: Sequence[Any]) -> int:
210+
"""Return the normalized-fingerprint common prefix length."""
211+
212+
prefix_len = 0
213+
max_len = min(len(cursor), len(new_input))
214+
while prefix_len < max_len:
215+
if not provider_items_equal(cursor[prefix_len], new_input[prefix_len]):
216+
break
217+
prefix_len += 1
218+
return prefix_len
219+
220+
221+
def longest_common_prefix(cursor: Sequence[Any], new_input: Sequence[Any]) -> int:
222+
"""Alias kept close to the design doc naming."""
223+
224+
return longest_common_prefix_len(cursor, new_input)
225+
226+
227+
def compute_diff(cursor: Sequence[Any], new_input: Sequence[Any]) -> CursorDiff:
228+
"""Compute the suffix pop/append delta from cursor to new input."""
229+
230+
prefix_len = longest_common_prefix_len(cursor, new_input)
231+
return CursorDiff(
232+
prefix_len=prefix_len,
233+
pop=list(cursor[prefix_len:]),
234+
append=list(new_input[prefix_len:]),
235+
)
236+
237+
238+
normalize_for_fingerprint = normalize_provider_item
239+
fingerprint_item = fingerprint_provider_item

0 commit comments

Comments
 (0)