Files

66 lines
3.5 KiB
Python

"""Small live API acceptance suite; writes evidence without API credentials."""
import argparse
import json
import time
import urllib.request
from pathlib import Path
p = argparse.ArgumentParser()
p.add_argument('--url', default='http://127.0.0.1:8000')
p.add_argument('--key-file', type=Path, default=Path(__file__).resolve().parents[1] / 'secrets/api-key')
p.add_argument('--output', type=Path, required=True)
args = p.parse_args()
key = args.key_file.read_text().strip()
def request(path, body=None):
payload = None if body is None else json.dumps(body, ensure_ascii=False).encode()
req = urllib.request.Request(args.url + path, data=payload,
headers={'Authorization': 'Bearer ' + key, 'Content-Type': 'application/json'})
return urllib.request.urlopen(req, timeout=600)
args.output.parent.mkdir(parents=True, exist_ok=True)
with args.output.open('x') as out:
def record(data):
line = json.dumps(data, ensure_ascii=False)
out.write(line + '\n'); out.flush(); print(line, flush=True)
record({'models': json.load(request('/v1/models'))})
for name, text, expected in [
('math', 'Compute 17 * 19. Reply with only the integer.', '323'),
('chinese', '请只回复:模型已就绪', '模型已就绪'),
]:
body = dict(model='qwen3.8-flash-next', messages=[dict(role='user', content=text)],
temperature=0, reasoning_effort='low', max_tokens=1024,
stream=True, stream_options={'include_usage': True})
start = time.monotonic(); first = None; content = ''; reasoning = ''; usage = {}; finish = None
with request('/v1/chat/completions', body) as response:
for raw in response:
if not raw.startswith(b'data: '): continue
data = raw[6:].strip()
if data == b'[DONE]': break
item = json.loads(data)
if item.get('usage'): usage = item['usage']
for choice in item.get('choices', []):
delta = choice.get('delta', {})
c = delta.get('content') or ''
r = delta.get('reasoning') or delta.get('reasoning_content') or ''
if (c or r) and first is None: first = time.monotonic()
content += c; reasoning += r
finish = choice.get('finish_reason') or finish
elapsed = time.monotonic() - start
passed = content.strip() == expected and finish == 'stop'
record(dict(test=name, passed=passed, content=content, reasoning_chars=len(reasoning),
ttft_s=None if first is None else first-start, elapsed_s=elapsed,
finish_reason=finish, usage=usage))
if not passed: raise SystemExit('Acceptance failure: ' + name)
body = dict(model='qwen3.8-flash-next', temperature=0, reasoning_effort='low', max_tokens=1024,
messages=[dict(role='user',content='Use get_weather to check the weather in Shanghai.')],
tools=[dict(type='function',function=dict(name='get_weather',description='Get current weather',
parameters=dict(type='object',properties={'city':{'type':'string'}},required=['city'])))],
tool_choice='auto')
result = json.load(request('/v1/chat/completions',body))
calls = result['choices'][0]['message'].get('tool_calls') or []
passed = bool(calls) and calls[0]['function']['name']=='get_weather'
record(dict(test='tool',passed=passed,response=result))
if not passed: raise SystemExit('Tool acceptance failure')