Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions proxy/dsml_shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def _coerce_param(pname, string_attr, raw, prop_schema):
if string_attr == "true":
return raw
typ = (prop_schema or {}).get("type")
if typ == "string":
return raw
try:
if typ == "integer":
return int(raw)
Expand Down
8 changes: 8 additions & 0 deletions test/test_dsml_shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ def test_string_true_stays_string_even_if_numeric(self):
blk = wrap_typed(P2, "calc", [("n", ' string="true"', "42")])
self.assertEqual(ds.parse_dsml_tool_calls(blk, NUM)[0]["input"]["n"], "42")

def test_schema_string_stays_string_without_string_attr(self):
# DeepSeek can omit string="true" on string parameters. Schema type string
# must win over the JSON fallback, or numeric-looking text is coerced and
# the whole tool call gets discarded by validation.
blk = wrap_typed(P2, "web_search", [("query", "", "42")])
self.assertEqual(ds.parse_dsml_tool_calls(blk, WS),
[{"name": "web_search", "input": {"query": "42"}}])

def test_missing_string_attr_falls_back_json_then_str(self):
blk = wrap_typed(P2, "calc", [("f", "", "3.5")])
# f 是 number → 3.5;无 schema 属性的 json 兜底另测
Expand Down
50 changes: 49 additions & 1 deletion test/test_proxy_dsml_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def serve():
c, _ = srv.accept()
except OSError:
return
req = c.recv(65536)
req = _read_http_request(c)
is_stream = b'"stream": true' in req or b'"stream":true' in req
if is_stream:
sse = _build_sse()
Expand All @@ -95,6 +95,54 @@ def serve():
return f"http://127.0.0.1:{port}/up", srv


def _read_http_request(sock):
data = b""
while b"\r\n\r\n" not in data:
chunk = sock.recv(65536)
if not chunk:
return data
data += chunk

head, sep, body = data.partition(b"\r\n\r\n")
content_length = 0
for line in head.split(b"\r\n"):
name, colon, value = line.partition(b":")
if colon and name.strip().lower() == b"content-length":
try:
content_length = int(value.strip())
except ValueError:
content_length = 0
break

while len(body) < content_length:
chunk = sock.recv(65536)
if not chunk:
break
body += chunk
return head + sep + body


class _ChunkedSocket:
def __init__(self, chunks):
self._chunks = list(chunks)

def recv(self, _size):
if not self._chunks:
return b""
return self._chunks.pop(0)


class HttpRequestReader(unittest.TestCase):
def test_reads_body_split_after_headers(self):
body = b'{"stream": true, "messages":["large enough to split"]}'
head = (b"POST /up HTTP/1.1\r\nHost: 127.0.0.1\r\n"
b"Content-Type: application/json\r\n"
+ f"Content-Length: {len(body)}\r\n\r\n".encode("ascii"))
sock = _ChunkedSocket([head[:17], head[17:], body[:9], body[9:]])
req = _read_http_request(sock)
self.assertEqual(req, head + body)


def raw_post(host, port, path, body):
s = socket.create_connection((host, port), timeout=5)
req = (f"POST {path} HTTP/1.1\r\nHost: {host}\r\n"
Expand Down