Image-Text-to-Text
Safetensors
GGUF
English
llama.cpp
test-fixture
tool-calling
ocr
mtp
pruning
conversational
Instructions to use Serveurperso/small-test with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Serveurperso/small-test with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: llama cli -hf Serveurperso/small-test:F16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: llama cli -hf Serveurperso/small-test:F16
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: ./llama-cli -hf Serveurperso/small-test:F16
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf Serveurperso/small-test:F16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf Serveurperso/small-test:F16
Use Docker
docker model run hf.co/Serveurperso/small-test:F16
- LM Studio
- Jan
- vLLM
How to use Serveurperso/small-test with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Serveurperso/small-test" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Serveurperso/small-test", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/Serveurperso/small-test:F16
- Ollama
How to use Serveurperso/small-test with Ollama:
ollama run hf.co/Serveurperso/small-test:F16
- Unsloth Desktop
- Pi
How to use Serveurperso/small-test with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "Serveurperso/small-test:F16" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use Serveurperso/small-test with Docker Model Runner:
docker model run hf.co/Serveurperso/small-test:F16
- Lemonade
How to use Serveurperso/small-test with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Serveurperso/small-test:F16
Run and chat with the model
lemonade run user.small-test-F16
List all available models
lemonade list
- Hermes Agent
How to use Serveurperso/small-test with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default Serveurperso/small-test:F16
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use Serveurperso/small-test with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf Serveurperso/small-test:F16
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "Serveurperso/small-test:F16" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
File size: 35,149 Bytes
4a393d1 | 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 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | # synthetic multi step tool calling trajectories, rendered through the chat template
# every value the model must emit is either copied from the user prompt or from a previous tool result,
# tool descriptions carry "e.g." examples that never match the correct value
import json, random, sys, os
from multiprocessing import Pool
import render as R
CONS="bdfgklmnprstvz"; VOW="aeiou"
TLDS=["com","net","org","io","de","fr","it","es","nl","co","app","dev"]
NOUNS=["listing","invoice","ticket","parcel","article","venue","booking","shipment","course","recipe","device","repo","dataset","contract","vessel","station"]
METRICS=["price","score","rating","latency_ms","stock","temperature","occupancy","volume"]
SITES=["server","website","mirror","endpoint","host"]
CITIES_HINT=["city","town","region","area"]
CI_BLOCKLIST=["azzoo","kettle","bmi","exercise","trending","mobile_app","geolocation","halle-eins","galerie-deux","galleria-tre","stock_quote","advisor","commercial_prop","video_recommend","voltara","rivex","vltr","rvxn","halverton","fleet"]
def word(rng,n=None):
n=n or rng.randint(2,4)
return "".join(rng.choice(CONS)+rng.choice(VOW) for _ in range(n))
def name(rng): return word(rng).capitalize()
def domain(rng): return f"{word(rng)}-{word(rng)}.{rng.choice(TLDS)}" if rng.random()<0.5 else f"{word(rng)}{word(rng,1)}.{rng.choice(TLDS)}"
def ident(rng): return "".join(rng.choice("ABCDEFGHJKLMNPQRSTUVWXYZ") for _ in range(rng.randint(2,3)))+str(rng.randint(1000,999999))
def ticker(rng): return "".join(rng.choice("ABCDEFGHJKLMNPQRSTUVWXYZ") for _ in range(rng.randint(3,5)))
def money(rng): return round(rng.uniform(3,900),2)
def coord(rng): return round(rng.uniform(-60,70),4),round(rng.uniform(-150,150),4)
def tool(fname,desc,props,required):
return {"type":"function","function":{"name":fname,"description":desc,"parameters":{"type":"object","properties":props,"required":required}}}
def strp(desc,eg): return {"type":"string","description":f"{desc} (e.g. '{eg}')"}
def intp(desc,eg): return {"type":"integer","description":f"{desc} (e.g. {eg})"}
def nump(desc): return {"type":"number","description":desc}
def call(rng,fname,args,lead=None):
content=lead if (lead and rng.random()<0.25) else ""
return {"role":"assistant","content":content,"tool_calls":[{"type":"function","function":{"name":fname,"arguments":args}}]}
def calls(fnames_args):
return {"role":"assistant","content":"","tool_calls":[{"type":"function","function":{"name":f,"arguments":a}} for f,a in fnames_args]}
def result(obj): return {"role":"tool","content":json.dumps(obj)}
def list_items(rng,noun,n):
items=[{"id":ident(rng),"name":f"{name(rng)} {name(rng)}","rating":round(rng.uniform(2.5,5.0),1),"price":money(rng)} for _ in range(n)]
return items
# search -> detail on the best item (id copied from the result) -> one more lookup on the same id -> summary
def chain(rng):
noun=rng.choice(NOUNS); brand=name(rng)
q=f"{word(rng)} {word(rng)} {noun}"
search=f"{brand.lower()}_search_{noun}s"; get=f"{brand.lower()}_get_{noun}"; extra=rng.choice(["reviews","history","availability","specs"]); more=f"{brand.lower()}_get_{noun}_{extra}"
tools=[tool(search,f"Search {brand} {noun}s by keyword.",{"query":strp("Search keyword or phrase",f"{word(rng)} {word(rng)}"),"page":intp("Page number",1)},["query"]),
tool(get,f"Retrieve details about a {brand} {noun}.",{f"{noun}_id":strp(f"{brand} {noun} identifier",ident(rng))},[f"{noun}_id"]),
tool(more,f"Fetch {extra} for a {brand} {noun}.",{f"{noun}_id":strp(f"{brand} {noun} identifier",ident(rng)),"limit":intp("Maximum entries",5)},[f"{noun}_id"])]
rng.shuffle(tools)
crit=rng.choice(["top-rated","cheapest","most expensive"])
user=f"Please search {brand} for '{q}', then get full details on the {crit} result, and finally fetch its {extra}. Give me a short summary."
items=list_items(rng,noun,rng.randint(2,4))
best=max(items,key=lambda x:x["rating"]) if crit=="top-rated" else (min if crit=="cheapest" else max)(items,key=lambda x:x["price"])
detail={"id":best["id"],"name":best["name"],"price":best["price"],"rating":best["rating"],"in_stock":rng.random()<0.8,"seller":name(rng)}
extras={extra:[{"text":f"{name(rng)} says it is {rng.choice(['great','fine','slow','solid','noisy'])}.","stars":rng.randint(1,5)} for _ in range(rng.randint(1,3))]}
msgs=[{"role":"user","content":user},
call(rng,search,{"query":q},f"Searching {brand} for {q}."),result({"results":items,"page":1}),
call(rng,get,{f"{noun}_id":best["id"]},f"Looking up {best['id']}."),result(detail),
call(rng,more,{f"{noun}_id":best["id"]}),result(extras),
{"role":"assistant","content":f"The {crit} {noun} for '{q}' is {best['name']} ({best['id']}) at {best['price']} with a rating of {best['rating']}. It has {len(extras[extra])} {extra} entries, mostly {extras[extra][0]['text'].split(' is ')[-1].rstrip('.')}."}]
return msgs,tools
# one lookup per entity named in the prompt, sequential or parallel, then a summary filtered on a stated criterion
def fanout(rng):
kind=rng.choice(["host","ticker","id"])
n=rng.randint(2,4)
ents=[domain(rng) if kind=="host" else ticker(rng) if kind=="ticker" else ident(rng) for _ in range(n)]
fname={"host":"lookup_"+rng.choice(SITES)+"_location","ticker":"get_market_quote","id":"get_"+rng.choice(NOUNS)+"_status"}[kind]
pname={"host":"host","ticker":"symbol","id":"id"}[kind]
eg={"host":domain(rng),"ticker":ticker(rng),"id":ident(rng)}[kind]
tools=[tool(fname,{"host":"Look up the geographic location of a website's server.","ticker":"Retrieve the latest market quote for a ticker symbol.","id":"Check the current status of an item by identifier."}[kind],{pname:strp({"host":"Hostname or IP address","ticker":"Ticker symbol","id":"Item identifier"}[kind],eg)},[pname])]
if rng.random()<0.5: tools.append(tool("send_"+rng.choice(["report","alert","note"]),"Send a short text message to the user's inbox.",{"text":strp("Message body","hello")},["text"]))
res={}
for e in ents:
if kind=="host":
la,lo=coord(rng); res[e]={"host":e,"city":name(rng),"country":rng.choice(["DE","FR","IT","ES","NL","PL","AT"]),"lat":la,"lon":lo,"population":rng.choice([12000,45000,800000,2500000,3400000])}
elif kind=="ticker": res[e]={"symbol":e,"price":money(rng),"change_pct":round(rng.uniform(-6,6),2)}
else: res[e]={"id":e,"status":rng.choice(["open","closed","pending","shipped"]),"updated":f"2026-0{rng.randint(1,9)}-{rng.randint(10,28)}"}
lst=", ".join(ents[:-1])+f" and {ents[-1]}"
if kind=="host":
user=f"I got enquiries from {n} sites: {lst}. Look up where each one's server is located. Discard any that sit in a big city (over one million people) and report the coordinates of the others."
keep=[e for e in ents if res[e]["population"]<1000000]
final=("None of them is outside a big city." if not keep else "Outside big cities: "+"; ".join(f"{e} in {res[e]['city']} at {res[e]['lat']}, {res[e]['lon']}" for e in keep)+".")
elif kind=="ticker":
thr=rng.randint(20,400)
user=f"Get the latest quote for {lst}. Tell me which ones trade above ${thr}."
keep=[e for e in ents if res[e]["price"]>thr]
final=("None trades above the threshold." if not keep else "Above the threshold: "+", ".join(f"{e} at {res[e]['price']}" for e in keep)+".")
else:
user=f"Check the status of {lst} and tell me which ones are still open or pending."
keep=[e for e in ents if res[e]["status"] in("open","pending")]
final=("All of them are closed or shipped." if not keep else "Still active: "+", ".join(f"{e} ({res[e]['status']})" for e in keep)+".")
msgs=[{"role":"user","content":user}]
if rng.random()<0.5:
msgs.append(calls([(fname,{pname:e}) for e in ents]))
for e in ents: msgs.append(result(res[e]))
else:
for e in ents: msgs+=[call(rng,fname,{pname:e},f"Checking {e}."),result(res[e])]
msgs.append({"role":"assistant","content":final})
return msgs,tools
# numbered steps, a threshold decides which entity continues, later steps copy values from the prompt
def conditional(rng):
a,b=name(rng),name(rng); ta,tb=ticker(rng),ticker(rng); city=name(rng); topic=f"{word(rng)} {word(rng)}"
thr=rng.randint(20,300); pa,pb=money(rng),money(rng)
while (pa>thr)==(pb>thr): pa,pb=money(rng),money(rng)
win,wt=(a,ta) if pa>thr else (b,tb)
noun=rng.choice(NOUNS); kind=rng.choice(["office","garage","warehouse","studio"])
tools=[tool("get_market_quote","Retrieve the latest market quote for a ticker symbol.",{"symbol":strp("Ticker symbol",ticker(rng)),"interval":strp("Time interval","1day")},["symbol"]),
tool("search_incidents","Search public incident reports mentioning a company or product.",{"query":strp("Keyword or company name",name(rng)),"limit":intp("Maximum results",5)},["query"]),
tool("search_"+kind+"s",f"Search {kind}s available in a given city.",{"city":strp("City name",name(rng)),"max_price":nump("Maximum monthly price")},["city"]),
tool("get_"+noun+"_recommendations",f"Fetch recommended {noun}s about a topic.",{"query":strp("Topic",f"{word(rng)} {word(rng)}"),"count":intp("Number of results",3)},["query"])]
user=(f"I need a multi-step analysis:\n1. Get the latest quote for {a} ({ta}) and {b} ({tb}). If either is above ${thr}, continue with that company.\n"
f"2. Search incident reports about that company.\n3. Find {kind}s in {city}.\n4. Recommend {noun}s about {topic}.\nWork through all four steps and give me a concise summary.")
inc=[{"title":f"{win} {rng.choice(['outage','recall','breach','delay'])}","severity":rng.choice(["low","medium","high"])} for _ in range(rng.randint(1,3))]
props=[{"id":ident(rng),"address":f"{rng.randint(1,200)} {name(rng)} Street, {city}","price":rng.randint(800,9000)} for _ in range(rng.randint(1,3))]
recs=[{"title":f"{name(rng)} on {topic}","duration_min":rng.randint(5,60)} for _ in range(rng.randint(1,3))]
msgs=[{"role":"user","content":user}]
if rng.random()<0.5:
msgs.append(calls([("get_market_quote",{"symbol":ta}),("get_market_quote",{"symbol":tb})]))
msgs+=[result({"symbol":ta,"price":pa}),result({"symbol":tb,"price":pb})]
else:
msgs+=[call(rng,"get_market_quote",{"symbol":ta}),result({"symbol":ta,"price":pa}),call(rng,"get_market_quote",{"symbol":tb}),result({"symbol":tb,"price":pb})]
msgs+=[call(rng,"search_incidents",{"query":win},f"{win} is above ${thr}, checking incidents."),result({"incidents":inc}),
call(rng,"search_"+kind+"s",{"city":city}),result({"results":props}),
call(rng,"get_"+noun+"_recommendations",{"query":topic}),result({"results":recs}),
{"role":"assistant","content":f"{win} ({wt}) trades at {max(pa,pb)}, above ${thr}. Incidents: {', '.join(i['title'] for i in inc)}. {len(props)} {kind}(s) found in {city}, cheapest at {min(p['price'] for p in props)} per month. Recommended: {recs[0]['title']}."}]
return msgs,tools
# numeric inputs with units named in the prompt, then a follow-up lookup that reuses the computed value
def measure(rng):
w=round(rng.uniform(45,130),1); h=round(rng.uniform(1.45,2.05),2); age=rng.randint(18,80)
metric=rng.choice(["body_mass_index","calorie_need","dose","fitness_score"])
v=round(w/(h*h),1) if metric=="body_mass_index" else round(w*rng.uniform(20,35))
tools=[tool("calculate_"+metric,f"Calculate {metric.replace('_',' ')} from body measurements.",{"weight_kg":nump("Body weight in kilograms"),"height_m":nump("Height in meters"),"age":intp("Age in years",40)},["weight_kg","height_m"]),
tool("get_plan","Suggest a plan for a given category and level.",{"category":strp("Plan category","cardio"),"level":strp("Difficulty level: beginner, intermediate, expert","beginner")},["category"])]
cat=rng.choice(["strength","cardio","mobility","endurance"]); lvl=rng.choice(["beginner","intermediate","expert"])
user=rng.choice([f"I weigh {w} kg, I'm {h} m tall and {age} years old. Compute my {metric.replace('_',' ')} and then suggest a {lvl} {cat} plan.",
f"Height {h} m, weight {w} kg, age {age}. What is my {metric.replace('_',' ')}? Then give me a {cat} plan for a {lvl}."])
plan=[{"name":f"{name(rng)} {rng.choice(['press','pull','squat','run','stretch'])}","sets":rng.randint(2,5)} for _ in range(rng.randint(2,4))]
msgs=[{"role":"user","content":user},
call(rng,"calculate_"+metric,{"weight_kg":w,"height_m":h,"age":age}),result({metric:v}),
call(rng,"get_plan",{"category":cat,"level":lvl}),result({"plan":plan}),
{"role":"assistant","content":f"Your {metric.replace('_',' ')} is {v}. Suggested {lvl} {cat} plan: "+", ".join(f"{p['name']} ({p['sets']} sets)" for p in plan)+"."}]
return msgs,tools
# two discovery tools called in order on values from the prompt, then a cross reference in the summary
def discover(rng):
topic=f"{word(rng)} {word(rng)}"; cat=f"{name(rng)} {rng.choice(NOUNS)}"
ta="get_popular_"+rng.choice(["questions","threads","topics"]); tb="search_"+rng.choice(["store","catalog","directory"])
tools=[tool(ta,"Fetch the most popular community entries about a topic.",{"query":strp("Topic",f"{word(rng)} {word(rng)}"),"limit":intp("Maximum entries",5)},["query"]),
tool(tb,"Search the catalog for entries matching a category or keyword.",{"query":strp("Search keyword",f"{name(rng)} {word(rng)}"),"count":intp("Number of results",3)},["query"])]
rng.shuffle(tools)
user=f"I'm planning a session on {topic}. First find what people ask most about {topic}, then search the catalog for '{cat}' and tell me what is missing."
qs=[{"title":f"How do I {word(rng)} my {word(rng)}?","votes":rng.randint(3,900)} for _ in range(rng.randint(2,4))]
apps=[{"name":name(rng)+name(rng),"rating":round(rng.uniform(2,5),1)} for _ in range(rng.randint(1,3))]
msgs=[{"role":"user","content":user},
call(rng,ta,{"query":topic}),result({"entries":qs}),
call(rng,tb,{"query":cat}),result({"results":apps}),
{"role":"assistant","content":f"Top question about {topic}: \"{qs[0]['title']}\" ({qs[0]['votes']} votes). The catalog has {len(apps)} entries for '{cat}', best rated {max(apps,key=lambda x:x['rating'])['name']}, so nothing covers that question yet."}]
return msgs,tools
# parameter kinds: how a value is generated and how the prompt names it
def pval(rng,kind):
if kind=="city": return name(rng)
if kind=="ticker": return ticker(rng)
if kind=="domain": return domain(rng)
if kind=="email": return f"{word(rng)}.{word(rng)}@{word(rng)}.{rng.choice(TLDS)}"
if kind=="phrase": return " ".join(word(rng) for _ in range(rng.randint(1,3)))
if kind=="ident": return ident(rng)
if kind=="date": return f"2026-{rng.randint(1,12):02d}-{rng.randint(1,28):02d}"
if kind=="int": return rng.randint(1,20)
if kind=="money": return rng.choice([50,100,250,500,1000,2500])
if kind=="person": return f"{name(rng)} {name(rng)}"
if kind=="code": return rng.choice(["DE","FR","IT","ES","NL","PL","AT","PT","SE","JP","BR"])
if kind=="enum": return rng.choice(["low","medium","high"])
if kind=="word": return word(rng)
raise ValueError(kind)
PARAMS=[("city","city","City name"),("symbol","ticker","Ticker symbol"),("host","domain","Hostname"),("email","email","Recipient email address"),
("query","phrase","Search keyword or phrase"),("topic","phrase","Topic to look for"),("keyword","phrase","Keyword"),("user_id","ident","User identifier"),
("order_id","ident","Order identifier"),("ticket_id","ident","Ticket identifier"),("date","date","Date in YYYY-MM-DD"),("limit","int","Maximum number of results"),
("count","int","Number of entries"),("max_price","money","Maximum price"),("recipient","person","Full name of the recipient"),("country","code","Two letter country code"),
("priority","enum","Priority: low, medium, high"),("category","word","Category name"),("tag","word","Tag to filter on"),("region","city","Region or area name")]
VERBS=["get","fetch","search","list","check","lookup","create","send","update","find","compute","schedule"]
def pipeline_tool(rng,used):
# one random tool with 1 to 3 parameters, the first one is required, names are unique in the tool set
while True:
fname=f"{rng.choice(VERBS)}_{rng.choice(NOUNS)}{rng.choice(['','s','_details','_status','_history','_summary'])}"
if fname not in used: used.add(fname); break
ps=rng.sample(PARAMS,rng.randint(1,3)); props={}; req=[]
for i,(pn,kind,desc) in enumerate(ps):
eg=pval(rng,kind)
props[pn]={"type":"integer" if kind in("int","money") else "string","description":f"{desc} (e.g. {eg if kind in('int','money') else repr(eg)})"} if rng.random()<0.7 else {"type":"integer" if kind in("int","money") else "string","description":desc}
if i==0 or rng.random()<0.3: req.append(pn)
desc=f"{fname.split('_')[0].capitalize()} {' '.join(fname.split('_')[1:])} "+rng.choice(["from the backend.","for the current account.","in the catalog.","using the public API.","from the records."])
return tool(fname,desc,props,req),ps
def step_phrase(rng,fname,args,src,off=0):
verb,rest=fname.split("_",1); rest=rest.replace("_"," ")
parts=[]
for pn,v in args.items():
s=src[pn]
if s=="prompt": parts.append(rng.choice([f"{pn} {v!r}" if isinstance(v,str) else f"{pn} {v}",f"{pn}={v}",f"{pn} set to {v}"]))
elif s=="winner": parts.append(rng.choice([f"{pn} set to that company",f"{pn} being the one selected in step 1",f"that entity as {pn}"]))
else:
n=s+off; parts.append(rng.choice([f"the {pn} from step {n}",f"the {pn} you got in step {n}",f"the {pn} returned by the previous call" if s==len(src) else f"the {pn} from step {n}"]))
return f"{verb} {rest} with "+" and ".join(parts) if parts else f"{verb} {rest}"
def pipeline(rng):
used=set(); K=rng.choices([2,3,4,5,6,7],[1,2,3,3,2,1])[0]
tools=[]; specs=[]
for _ in range(K+rng.randint(0,2)):
t,ps=pipeline_tool(rng,used); tools.append(t); specs.append(ps)
order=rng.sample(range(len(tools)),K)
steps=[]; results=[]; gate=None
if rng.random()<0.35:
# gate: the same metric tool on two entities named in the prompt, the one above the threshold continues
ent=rng.choice([("symbol","ticker"),("host","domain"),("user_id","ident")]); pn,kind=ent
a,b=name(rng),name(rng); va,vb=pval(rng,kind),pval(rng,kind); thr=rng.randint(10,500)
ma,mb=rng.randint(1,1000),rng.randint(1,1000)
while (ma>thr)==(mb>thr): ma,mb=rng.randint(1,1000),rng.randint(1,1000)
gname=f"get_{rng.choice(['metric','quote','score','level'])}"; used.add(gname)
metric=rng.choice(["price","score","load","risk"])
tools.insert(0,tool(gname,f"Retrieve the current {metric} for one entity.",{pn:strp({"symbol":"Ticker symbol","host":"Hostname","user_id":"User identifier"}[pn],pval(rng,kind))},[pn]))
specs.insert(0,[(pn,kind,"")]); order=[i+1 for i in order]
gate=dict(gname=gname,pn=pn,a=a,b=b,va=va,vb=vb,thr=thr,ma=ma,mb=mb,metric=metric,win=a if ma>thr else b)
for k,ti in enumerate(order):
fname=tools[ti]["function"]["name"]; ps=specs[ti]; req=tools[ti]["function"]["parameters"]["required"]
args={}; src={}
for pn,kind,desc in ps:
if pn not in req and rng.random()<0.5: continue
prev=[(j,r) for j,r in enumerate(results) if pn in r]
if prev and rng.random()<0.8:
j,r=rng.choice(prev); args[pn]=r[pn]; src[pn]=j+1
elif gate and kind=="phrase" and "winner" not in src.values() and rng.random()<0.7: args[pn]=gate["win"]; src[pn]="winner"
else: args[pn]=pval(rng,kind); src[pn]="prompt"
res={"status":rng.choice(["ok","done","found"])}
for pn,kind,desc in rng.sample(PARAMS,rng.randint(1,3)): res[pn]=pval(rng,kind)
res[rng.choice(["result","value","total","score"])]=rng.choice([rng.randint(1,999),round(rng.uniform(0,100),1),name(rng),f"{name(rng)} {name(rng)}"])
steps.append((fname,args,src)); results.append(res)
opt=None
cand=[(j,k) for j in range(len(results)-1) for k,v in results[j].items() if isinstance(v,(int,float)) and not isinstance(v,bool)]
if rng.random()<0.3 and cand:
# the last step is conditional on a numeric field of an earlier result, half the time the condition is false and the step is skipped
j,key=rng.choice(cand); val=results[j][key]
thr=val-rng.randint(1,50) if rng.random()<0.5 else val+rng.randint(1,50)
opt=dict(j=j+1+(1 if gate else 0),key=key,thr=thr,do=val>thr)
numbered=rng.random()<0.5 or gate is not None or opt is not None
phrases=[step_phrase(rng,f,a,s,1 if gate else 0) for f,a,s in steps]
if opt: phrases[-1]=f"only if the {opt['key']} from step {opt['j']} is above {opt['thr']}, {phrases[-1]}"
if gate:
g=gate; phrases.insert(0,f"get the {g['metric']} for {g['a']} ({g['va']}) and {g['b']} ({g['vb']}). If either is above {g['thr']}, continue with that one")
if numbered: user=rng.choice(["Please do the following:","I need these steps done in order:","Run this pipeline for me:"])+"\n"+"\n".join(f"{i+1}. {p[0].upper()+p[1:]}." for i,p in enumerate(phrases))+"\n"+rng.choice(["Then summarize.","Give me a short summary at the end.","Report the results briefly."])
else: user=rng.choice(["First ","Start by ","Could you "])+phrases[0]+"".join(f", then {p}" for p in phrases[1:])+rng.choice([". Summarize the results.",", and give me a short recap.",". Keep the summary brief."])
msgs=[{"role":"user","content":user}]
if gate:
g=gate; ra={g["pn"]:g["va"],g["metric"]:g["ma"]}; rb={g["pn"]:g["vb"],g["metric"]:g["mb"]}
if rng.random()<0.5: msgs+=[calls([(g["gname"],{g["pn"]:g["va"]}),(g["gname"],{g["pn"]:g["vb"]})]),result(ra),result(rb)]
else: msgs+=[call(rng,g["gname"],{g["pn"]:g["va"]}),result(ra),call(rng,g["gname"],{g["pn"]:g["vb"]}),result(rb)]
k=0; n_do=len(steps)-(0 if opt is None or opt["do"] else 1)
while k<n_do:
fname,args,src=steps[k]
# two consecutive steps whose arguments all come from the prompt can go out as parallel calls
if k+1<n_do and all(v=="prompt" for v in src.values()) and all(v=="prompt" for v in steps[k+1][2].values()) and rng.random()<0.3:
msgs+=[calls([(fname,args),(steps[k+1][0],steps[k+1][1])]),result(results[k]),result(results[k+1])]; k+=2; continue
msgs+=[call(rng,fname,args,rng.choice([f"Calling {fname}.",f"Next step: {fname.replace('_',' ')}.","Working on the next step."])),result(results[k])]; k+=1
summ=[f"{gate['win']} is above {gate['thr']} with {gate['metric']} {max(gate['ma'],gate['mb'])}"] if gate else []
for (fname,args,src),res in list(zip(steps,results))[:n_do]:
key=[k for k in res if k!="status"][-1]; summ.append(f"{fname.replace('_',' ')} returned {key} {res[key]}")
if opt and not opt["do"]: summ.append(f"{opt['key']} from step {opt['j']} is {results[opt['j']-1-(1 if gate else 0)][opt['key']]}, not above {opt['thr']}, so {steps[-1][0].replace('_',' ')} was skipped")
msgs.append({"role":"assistant","content":rng.choice(["Done. ","All steps completed. ","Here is the summary: "])+"; ".join(summ)+"."})
if rng.random()<0.25:
# a follow-up turn that reuses the tools with a value from an earlier result
ti=rng.choice(order); fname=tools[ti]["function"]["name"]; ps=specs[ti]; pn,kind,desc=ps[0]
prev=[r for r in results if pn in r]
v=rng.choice(prev)[pn] if prev else pval(rng,kind)
msgs+=[{"role":"user","content":rng.choice([f"Thanks. Now {fname.replace('_',' ')} again with {pn} {v!r}.",f"One more thing: run {fname} for {pn} {v}."])},
call(rng,fname,{pn:v}),result({"status":"ok",pn:v,"note":name(rng)}),
{"role":"assistant","content":f"{fname.replace('_',' ')} for {pn} {v} is done, status ok."}]
return msgs,tools
# a question about the tools themselves is answered in plain text without any call
def capabilities(rng):
used=set(); tools=[pipeline_tool(rng,used)[0] for _ in range(rng.randint(2,4))]
names=[t["function"]["name"] for t in tools]
user=rng.choice(["What can you do for me here?","Which tools do you have available?","Hi, what are you able to help with?","List your functions please."])
msgs=[{"role":"user","content":user},{"role":"assistant","content":"I can "+", ".join(n.replace("_"," ") for n in names[:-1])+f" and {names[-1].replace('_',' ')}. Tell me what you need and I will run the right one."}]
return msgs,tools
SYSTEMS=["You are a helpful assistant.","You are a coding assistant.","You are a tool-calling agent.","You are a chatbot that uses tools/functions. Dont overthink things.","You are a tools-calling assistant.","You are an assistant with access to functions. Use them when they help.","Answer concisely."]
CITIES=["Paris","Berlin","Madrid","Rome","Lisbon","Vienna","Prague","Warsaw","Oslo","Dublin","Tokyo","Seoul","Bangkok","Hanoi","Cairo","Nairobi","Lagos","Lima","Bogota","Santiago","Montreal","Toronto","Denver","Austin","Seattle","Boston","Sydney","Auckland","Mumbai","Jakarta"]
COUNTRIES={"Paris":"France","Berlin":"Germany","Madrid":"Spain","Rome":"Italy","Lisbon":"Portugal","Vienna":"Austria","Prague":"Czechia","Warsaw":"Poland","Oslo":"Norway","Dublin":"Ireland","Tokyo":"Japan","Seoul":"South Korea","Bangkok":"Thailand","Hanoi":"Vietnam","Cairo":"Egypt","Nairobi":"Kenya","Lagos":"Nigeria","Lima":"Peru","Bogota":"Colombia","Santiago":"Chile","Montreal":"Canada","Toronto":"Canada","Denver":"USA","Austin":"USA","Seattle":"USA","Boston":"USA","Sydney":"Australia","Auckland":"New Zealand","Mumbai":"India","Jakarta":"Indonesia"}
# a vague request with a single tool: the tool is called with plausible arguments instead of answering in prose
def vague(rng):
used=set(); t,ps=pipeline_tool(rng,used); fname=t["function"]["name"]
tools=[t]+([pipeline_tool(rng,used)[0]] if rng.random()<0.3 else [])
rng.shuffle(tools)
user=rng.choice(["Write an example.","Give me an example.","Show me how it works.","Run it.","Do it.","Try it out.","Give me something.","Go ahead.","Can you do that for me?","Please proceed.","Test it.","Use the tool."])
args={pn:pval(rng,kind) for pn,kind,desc in ps if pn in t["function"]["parameters"]["required"] or rng.random()<0.3}
res={"status":"ok",rng.choice(["result","value","id"]):rng.choice([rng.randint(1,999),ident(rng),name(rng)])}
key=[k for k in res if k!="status"][0]
msgs=[{"role":"user","content":user},call(rng,fname,args),result(res),
{"role":"assistant","content":rng.choice([f"Done, {fname.replace('_',' ')} returned {key} {res[key]}.",f"Here is an example: {fname.replace('_',' ')} with {', '.join(f'{k} {v}' for k,v in args.items())} gives {key} {res[key]}.",f"I ran {fname.replace('_',' ')}, {key} is {res[key]}."])}]
return msgs,tools
# a code execution tool: the argument is real Python that does what the user asked
def code(rng):
fname=rng.choice(["python","run_python","execute_code","code_interpreter","exec_python","ipython"])
tools=[tool(fname,rng.choice(["Runs code in a Python interpreter and returns the output.","Execute Python code.","Runs the given Python code and returns its stdout."]),{"code":{"type":"string","description":"The Python code to run."}},["code"])]
a,b=rng.randint(1,99),rng.randint(1,99); n=rng.randint(3,12); w=word(rng); msg=rng.choice(["hello world","Hello, World!","hi there",f"hello {w}"])
q=rng.choice(['"',"'"])
kind=rng.choice(["hello","hello","add","range","len","upper","square"])
if kind=="hello": user=rng.choice([f"say {msg} with python",f"print {msg} using python",f"write python that prints {msg}",f"Say {msg} in Python."]); c=f"print({q}{msg}{q})"; out=msg
elif kind=="add": user=rng.choice([f"compute {a} + {b} in python",f"what is {a} plus {b}? use python",f"add {a} and {b} with python"]); c=f"print({a} + {b})"; out=str(a+b)
elif kind=="range": user=rng.choice([f"print the numbers from 1 to {n} in python",f"use python to list 1 to {n}"]); c=f"for i in range(1, {n+1}):\n print(i)"; out="\n".join(str(i) for i in range(1,n+1))
elif kind=="len": user=rng.choice([f"how many characters in {q}{w}{q}? use python",f"python: length of {q}{w}{q}"]); c=f"print(len({q}{w}{q}))"; out=str(len(w))
elif kind=="upper": user=rng.choice([f"uppercase {q}{w}{q} with python",f"use python to make {q}{w}{q} uppercase"]); c=f"print({q}{w}{q}.upper())"; out=w.upper()
else: user=rng.choice([f"square {a} in python",f"what is {a} squared? run python"]); c=f"print({a} ** 2)"; out=str(a*a)
msgs=[{"role":"user","content":user},call(rng,fname,{"code":c}),result({"stdout":out}),{"role":"assistant","content":rng.choice([f"Output: {out}",f"The code printed {out}.",f"Result: {out}"])}]
return msgs,tools
# a location argument copied exactly as the user wrote it, city alone or city with its country
def weather(rng):
city=rng.choice(CITIES); loc=city if rng.random()<0.6 else f"{city}, {COUNTRIES[city]}"
fname=rng.choice(["get_current_weather","get_weather","weather_lookup","fetch_forecast"])
props={"location":strp("The city and country, or city alone",f"{rng.choice(CITIES)}")}
if rng.random()<0.5: props["unit"]={"type":"string","enum":["celsius","fahrenheit"],"description":"Temperature unit"}
tools=[tool(fname,"Get the current weather for a location.",props,["location"])]
if rng.random()<0.4: tools.append(pipeline_tool(rng,{fname})[0]); rng.shuffle(tools)
user=rng.choice([f"What is the weather in {loc}?",f"What's the weather like in {loc} right now?",f"Is it raining in {loc}?",f"Give me the forecast for {loc}.",f"Temperature in {loc} today?"])
args={"location":loc}
if "unit" in props and rng.random()<0.4: args["unit"]=rng.choice(["celsius","fahrenheit"])
temp=rng.randint(-5,38); cond=rng.choice(["sunny","cloudy","light rain","clear","overcast","windy"])
msgs=[{"role":"user","content":user},call(rng,fname,args),result({"location":loc,"temperature_c":temp,"condition":cond}),
{"role":"assistant","content":f"It is {cond} in {loc}, {temp} degrees Celsius."}]
return msgs,tools
# two named companies, both quoted, a threshold picks one, then heterogeneous tools whose arguments come from the prompt,
# only the report search takes the selected company, the other tools take their own values
def compare_then(rng):
a,b=name(rng)+" "+rng.choice(["Inc.","Labs","Corp","Group","AG"]),name(rng)+" "+rng.choice(["Inc.","Labs","Corp","Group","AG"])
ta,tb=ticker(rng),ticker(rng); thr=rng.randint(20,400); pa,pb=money(rng),money(rng)
while (pa>thr)==(pb>thr): pa,pb=money(rng),money(rng)
win=a if pa>thr else b
city=rng.choice(CITIES) if rng.random()<0.5 else name(rng); topic=" ".join(word(rng) for _ in range(2)); kind=rng.choice(["office","warehouse","studio","rental","garage"])
q=rng.choice(["get_stock_price","get_share_price","get_quote","lookup_price"])
rep=rng.choice(["search_reports","search_news","find_incidents","search_filings"])
prop="search_"+kind+"s"; rec=rng.choice(["get_talks","list_articles","find_podcasts","get_tutorials"])
tools=[tool(q,"Get the latest share price for a ticker symbol.",{"symbol":strp("Ticker symbol",ticker(rng))},["symbol"]),
tool(rep,"Search public reports mentioning a company.",{"query":strp("Company name or keyword",name(rng)),"limit":intp("Maximum results",5)},["query"]),
tool(prop,f"Search {kind}s for rent in a city.",{"city":strp("City name",rng.choice(CITIES)),"max_price":intp("Maximum monthly price",2000)},["city"]),
tool(rec,"Get recommended content about a topic.",{"query":strp("Topic",f"{word(rng)} {word(rng)}"),"count":intp("Number of results",3)},["query"])]
rng.shuffle(tools)
user=rng.choice([f"I need a multi-step analysis:\n1. Get the share price of {a} ({ta}) and {b} ({tb}). If either is above ${thr}, continue with that company.\n2. Search reports about that company.\n3. Find {kind}s in {city}.\n4. Get content about {topic}.\nWork through all four steps and summarize.",
f"Check the share price for {a} ({ta}) and {b} ({tb}). Whichever is above ${thr}, search reports about it, then find {kind}s in {city}, then get content about {topic}. Summarize at the end."])
msgs=[{"role":"user","content":user}]
if rng.random()<0.5: msgs+=[calls([(q,{"symbol":ta}),(q,{"symbol":tb})]),result({"symbol":ta,"price":pa}),result({"symbol":tb,"price":pb})]
else: msgs+=[call(rng,q,{"symbol":ta}),result({"symbol":ta,"price":pa}),call(rng,q,{"symbol":tb}),result({"symbol":tb,"price":pb})]
reps=[{"title":f"{win} {rng.choice(['outage','recall','audit','delay'])}","severity":rng.choice(["low","medium","high"])} for _ in range(rng.randint(1,3))]
props=[{"id":ident(rng),"address":f"{rng.randint(1,200)} {name(rng)} Street, {city}","price":rng.randint(500,5000)} for _ in range(rng.randint(1,3))]
recs=[{"title":f"{name(rng)} on {topic}","minutes":rng.randint(5,60)} for _ in range(rng.randint(1,3))]
msgs+=[call(rng,rep,{"query":win},f"{win} is above ${thr}."),result({"results":reps}),
call(rng,prop,{"city":city}),result({"results":props}),
call(rng,rec,{"query":topic}),result({"results":recs}),
{"role":"assistant","content":f"{win} trades at {max(pa,pb)}, above ${thr}. Reports: {', '.join(r['title'] for r in reps)}. {len(props)} {kind}(s) in {city}, cheapest {min(p['price'] for p in props)} per month. Content on {topic}: {recs[0]['title']}."}]
return msgs,tools
PATTERNS=[(compare_then,0.01),(pipeline,0.39),(chain,0.11),(fanout,0.12),(conditional,0.07),(measure,0.05),(discover,0.05),(capabilities,0.05),(vague,0.06),(code,0.05),(weather,0.04)]
def gen(seed):
# a random name that collides with a CI test name is redrawn from a shifted seed
while True:
rng=random.Random(seed)
fn=rng.choices([p for p,_ in PATTERNS],[w for _,w in PATTERNS])[0]
msgs,tools=fn(rng)
# a system prompt in a third of the documents, the server tests always send one
if rng.random()<0.33: msgs=[{"role":"system","content":rng.choice(SYSTEMS)}]+msgs
text,spans=R.render(msgs,tools)
if not any(b in text.lower() for b in CI_BLOCKLIST): return json.dumps({"text":text,"spans":spans},ensure_ascii=False)
seed+=10**7
if __name__=="__main__":
model_dir,n,out=sys.argv[1],int(sys.argv[2]),sys.argv[3]
R.init(model_dir)
n_ev=max(1,n//100)
with Pool(16) as p: lines=list(p.imap(gen,range(n),chunksize=64))
with open(out+".jsonl","w") as f: f.write("\n".join(lines[n_ev:])+"\n")
with open(out+".eval.jsonl","w") as f: f.write("\n".join(lines[:n_ev])+"\n")
print(f"{out}: {n-n_ev} train, {n_ev} eval")
|