Ling-3.0-tiny-RKNN / tests /portable_api_test.py
Sariel00's picture
Publish Ling-3.0-tiny RKNN engine and model
3fd1a35 verified
Raw History Blame Contribute Delete
4.66 kB
#!/usr/bin/env python3
"""Run against an initialized portable service; Python is client-side only."""
import argparse
import concurrent.futures
import http.client
import json
import time
import urllib.error
import urllib.request
def main():
p = argparse.ArgumentParser()
p.add_argument("--url", default="http://127.0.0.1:19093")
args = p.parse_args()
base = args.url.rstrip("/")
def call(path, body=None):
request = urllib.request.Request(base + path, None if body is None else json.dumps(body).encode(),
{"Content-Type": "application/json"})
with urllib.request.urlopen(request, timeout=120) as r:
return json.load(r)
def request(text, **kwargs):
return {"model": "mindnano-ling3-tiny", "messages": [{"role": "user", "content": text}],
"max_tokens": 24, "temperature": 0, "cache_prompt": False, **kwargs}
health = call("/health")
assert health["status"] == "ok"
assert call("/v1/models")["data"][0]["context_length"] == health["context_length"]
for patch, status in [({"model": "bad"}, 404), ({"max_tokens": -1}, 400),
({"tools": []}, 400), ({"messages": []}, 400),
({"messages": [{"role": "user", "content": "1" * (health["context_length"] + 1)}]}, 400)]:
try:
call("/v1/chat/completions", request("你好", **patch))
raise AssertionError("invalid request accepted")
except urllib.error.HTTPError as e:
assert e.code == status, (e.code, e.read())
assert "error" in json.load(e)
a = call("/v1/chat/completions", request("请用一句话介绍你自己。"))
b = call("/v1/chat/completions", request("只回答:一加一等于几?"))
again = call("/v1/chat/completions", request("请用一句话介绍你自己。"))
assert a["choices"][0]["message"]["content"] == again["choices"][0]["message"]["content"], "request isolation/reset failed"
for r in (a, b, again):
assert r["object"] == "chat.completion"
assert r["usage"]["total_tokens"] == r["usage"]["prompt_tokens"] + r["usage"]["completion_tokens"]
assert r["mindnano_metrics"]["cached_tokens"] == 0
r = urllib.request.Request(base + "/v1/chat/completions", json.dumps(
request("请用一句话介绍你自己。", stream=True, stream_options={"include_usage": True})).encode(),
{"Content-Type": "application/json"})
content = ""; done = False; metrics = None; usage = None
with urllib.request.urlopen(r, timeout=120) as response:
for raw in response:
line = raw.decode("utf-8").strip()
if not line.startswith("data: "):
continue
if line == "data: [DONE]":
done = True; break
event = json.loads(line[6:])
assert "error" not in event, event
for c in event["choices"]:
content += c["delta"].get("content", "")
metrics = event.get("mindnano_metrics", metrics)
usage = event.get("usage") or usage
assert done and usage and metrics
assert content == a["choices"][0]["message"]["content"], "SSE text mismatch"
# Cancel non-stream generation; health/cancel must remain available while NPU is busy.
with concurrent.futures.ThreadPoolExecutor() as pool:
future = pool.submit(call, "/v1/chat/completions", request("详细写一篇长篇故事。", max_tokens=1024))
for _ in range(100):
if call("/health")["active"]:
break
time.sleep(.02)
else:
raise AssertionError("generation did not start")
try:
call("/v1/chat/completions", request("并发请求"))
raise AssertionError("concurrent request was not rejected")
except urllib.error.HTTPError as e:
assert e.code == 429
assert call("/v1/cancel", {})["cancel_requested"]
try:
future.result(timeout=10)
raise AssertionError("cancel returned successful completion")
except urllib.error.HTTPError as e:
assert e.code == 409
final = call("/v1/chat/completions", request("请用一句话介绍你自己。"))
assert final["choices"][0]["message"]["content"] == content, "canceled state leaked"
print(json.dumps({"passed": True, "context": health["context_length"],
"sample": content, "metrics": [a["mindnano_metrics"], b["mindnano_metrics"], metrics]},
ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()