File size: 8,142 Bytes
ce52794 69f9cd1 ce52794 69f9cd1 ce52794 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | """Checklist de aceite do endpoint Programa.
Cobre, nesta ordem: /v1/models, chat simples, stream, system, multi-turn,
tools + tool_choice=auto, tool result + continuacao, max_tokens, temperature,
stop, /v1/responses e /v1/messages (somente se o servidor expuser de verdade).
Uso:
BASE=https://erinaldordso-programa.hf.space/v1 python3 tests/test_agent.py
BASE=... MODEL=... APIKEY=... python3 tests/test_agent.py
Nao declara sucesso parcial: qualquer FAIL aborta com exit 1.
"""
import json
import os
import sys
import urllib.request
import tiktoken
BASE = os.environ.get("BASE", "https://erinaldordso-minecraft-friends-ai-zerogpu.hf.space/v1").rstrip("/")
MODEL = os.environ.get("MODEL", "granite-4.0-h-tiny")
APIKEY = os.environ.get("APIKEY", "")
PASS, FAIL = "PASS", "FAIL"
results = []
def req(method, path, body=None, stream=False):
url = BASE + path
data = json.dumps(body).encode() if body is not None else None
headers = {"Content-Type": "application/json"}
if APIKEY:
headers["Authorization"] = f"Bearer {APIKEY}"
r = urllib.request.Request(url, data=data, headers=headers, method=method)
if not stream:
with urllib.request.urlopen(r, timeout=300) as resp:
return resp.status, json.loads(resp.read().decode())
out = []
with urllib.request.urlopen(r, timeout=300) as resp:
for line in resp:
s = line.decode(errors="replace").strip()
if s.startswith("data:"):
s = s[5:].strip()
if s == "[DONE]":
break
try:
out.append(json.loads(s))
except json.JSONDecodeError:
pass
return 200, out
def check(name, cond, extra=""):
results.append((name, bool(cond)))
print(f"[{PASS if cond else FAIL}] {name} {extra}")
if not cond:
raise SystemExit(f"FAIL em {name}: {extra}")
TOOLS = [{"type": "function", "function": {
"name": "read_file",
"description": "Read a file from the project",
"parameters": {"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]}}},
{"type": "function", "function": {
"name": "list_directory",
"description": "List a directory",
"parameters": {"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]}}},
{"type": "function", "function": {
"name": "execute_command",
"description": "Run a shell command",
"parameters": {"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]}}}]
def main():
print(f"BASE={BASE} MODEL={MODEL}")
st, models = req("GET", "/models")
ids = [m.get("id", "") for m in models.get("data", [])]
check("models", st == 200 and any(MODEL in i or "granite" in i.lower() for i in ids), f"ids={ids[:4]}")
st, emb = req("POST", "/embeddings", {"model": "text-embedding-ada-002", "input": ["mine wood", "craft pickaxe"]})
vecs = emb.get("data", [])
check("embeddings Voyager", st == 200 and len(vecs) == 2 and all(isinstance(x.get("embedding"), list) and len(x.get("embedding")) > 0 for x in vecs), f"vectors={len(vecs)}")
enc = tiktoken.get_encoding("cl100k_base")
st, emb = req("POST", "/embeddings", {"model": "text-embedding-ada-002", "input": [enc.encode("mine wood"), enc.encode("craft pickaxe")]})
vecs = emb.get("data", [])
check("embeddings token input", st == 200 and len(vecs) == 2 and all(isinstance(x.get("embedding"), list) and len(x.get("embedding")) > 0 for x in vecs), f"vectors={len(vecs)}")
st, r = req("POST", "/chat/completions", {
"model": MODEL, "messages": [{"role": "user", "content": "diga ok"}],
"max_tokens": 16, "temperature": 0})
txt = r["choices"][0]["message"].get("content", "")
check("chat stream=false", st == 200 and isinstance(txt, str), f"len={len(txt)}")
st, chunks = req("POST", "/chat/completions", {
"model": MODEL, "messages": [{"role": "user", "content": "conte ate 3"}],
"max_tokens": 32, "temperature": 0, "stream": True}, stream=True)
joined = "".join((c.get("choices", [{}])[0].get("delta", {}) or {}).get("content") or "" for c in chunks)
check("chat stream=true", st == 200 and len(chunks) > 0, f"chunks={len(chunks)} chars={len(joined)}")
st, r = req("POST", "/chat/completions", {
"model": MODEL,
"messages": [{"role": "system", "content": "You are an autonomous software engineering agent."},
{"role": "user", "content": "diga ok"}],
"max_tokens": 16, "temperature": 0})
check("system message", st == 200, "")
st, r = req("POST", "/chat/completions", {
"model": MODEL,
"messages": [{"role": "user", "content": "meu projeto usa npm"},
{"role": "assistant", "content": "anotado"},
{"role": "user", "content": "qual gerenciador citei?"}],
"max_tokens": 32, "temperature": 0})
check("multi-turn", st == 200 and "npm" in json.dumps(r).lower(), "")
st, r = req("POST", "/chat/completions", {
"model": MODEL,
"messages": [
{"role": "system", "content": "You are an autonomous software engineering agent. Use tools instead of describing hypothetical actions. Inspect files before modifying them."},
{"role": "user", "content": "Leia package.json e diga qual comando executa os testes."}],
"tools": TOOLS[:1], "tool_choice": "auto",
"max_tokens": 256, "temperature": 0})
msg = r["choices"][0]["message"]
tcs = msg.get("tool_calls") or []
ok = (r["choices"][0].get("finish_reason") == "tool_calls" and tcs
and tcs[0]["function"]["name"] == "read_file"
and "package.json" in tcs[0]["function"].get("arguments", ""))
check("tools read_file", ok, f"finish={r['choices'][0].get('finish_reason')} calls={json.dumps(tcs)[:200]}")
tc = tcs[0]
st, r = req("POST", "/chat/completions", {
"model": MODEL,
"messages": [
{"role": "system", "content": "You are an autonomous software engineering agent."},
{"role": "user", "content": "Leia package.json e diga qual comando executa os testes."},
{"role": "assistant", "tool_calls": [{
"id": tc.get("id", "call_1"), "type": "function",
"function": {"name": "read_file", "arguments": tc["function"]["arguments"]}}]},
{"role": "tool", "tool_call_id": tc.get("id", "call_1"),
"content": "{\"name\": \"demo\", \"scripts\": {\"test\": \"npm test\"}}"}],
"max_tokens": 128, "temperature": 0})
txt2 = r["choices"][0]["message"].get("content", "")
check("tool result + continuacao", st == 200 and "npm test" in txt2, f"resp={txt2[:160]}")
st, r = req("POST", "/chat/completions", {
"model": MODEL, "messages": [{"role": "user", "content": "fale muito"}],
"max_tokens": 8, "temperature": 0})
check("max_tokens respeitado", st == 200, "")
st, r = req("POST", "/chat/completions", {
"model": MODEL, "messages": [{"role": "user", "content": "diga ok"}],
"max_tokens": 16, "temperature": 0.7, "stop": ["<|im_end|>"]})
check("temperature+stop", st == 200, "")
for path, label in [("/responses", "responses"), ("/messages", "messages")]:
try:
if label == "responses":
st, r = req("POST", path, {"model": MODEL, "input": "diga ok", "max_output_tokens": 16})
else:
st, r = req("POST", path, {"model": MODEL, "max_tokens": 16,
"messages": [{"role": "user", "content": "diga ok"}]})
check(label, st in (200, 201), f"status={st}")
except Exception as e: # noqa: BLE001
print(f"[SKIP] {label} ausente neste build ({str(e)[:120]})")
print(f"\nTodos os checks obrigatorios passaram ({len(results)}).")
if __name__ == "__main__":
main()
|