Muse Spark¶
Vision extraction with structured output, the decision cards and the listing drafts, the search plan, tool-calling steps for project proposals, and search grounding for prices. Runs at low reasoning effort throughout. No verdict is the model's: the rules decide fit.
Search grounding¶
Price guidance is one call with the web_search tool and the strict JSON schema of Findings
(comparables with their page, the estimate, the basis, caveats):
- Request.
tools: [{"type": "web_search", "search_context_size": PRICE_SEARCH_CONTEXT}], with an approximateuser_location(country from the request orPRICE_COUNTRY, city from the request) when one is given,max_tool_calls: PRICE_MAX_SEARCHES(4) with the same bound said in the instructions, since a guidance left to itself ran eleven or twelve searches and read 70,000 to 100,000 input tokens, andinclude: ["web_search_call.results"]so the pages the search returned come back with the answer. - Response. One
web_search_callitem per search, each with itsresults(title, URL, snippet), andurl_citationannotations on the text. The rules keep a comparable only when its page is among those results or citations: the model proposes, the pages decide. - Billing. The API reports no search count, so the client counts the
web_search_callitems and records them in the ledger at the published per-query rate, on top of the tokens. - Operation name
price, which is also the recorded fixture that answers in fixture mode: one search, four results, five proposed comparables of which one rests on no result.
The code path is app/value/service.py; the value guide shows what the
four figures mean.
Search the web for a part is the same shape: one call with the web_search tool
(search_context_size: PRICE_SEARCH_CONTEXT), max_tool_calls: PART_MAX_SEARCHES (4) said in
the instructions too, include: ["web_search_call.results"], and the strict JSON schema of
WebFinds (each page's URL, title, site, price and dimensions as stated, and a quote). The
brief is the search plan's, led by the device the part came from when it is recorded. The same
check keeps a page only when it is among the results or the citations
(app/model_api/client.py backed_pages). Operation name part-search; its fixture is a stub
written by hand (three results on example.org, four proposed pages of which one rests on no
result), not a recording. The code path is app/find/service.py
(search guide).
Tool calling¶
A project proposal is a loop of calls with function tools and the strict JSON schema of
ProposalSet. The tools are list_inventory (the items a build may count on, with their
fields), template_catalog (the projects that may be proposed, with what each requires) and
item_details (one item in full, by id). Each step is stateless: the request carries the
brief, every output item the model wrote so far and one function_call_output per
function_call, matched by call_id, since nothing is stored on the server. The loop ends
with the first answer that calls no tool, parsed against the schema, or fails after four
steps. The rules then keep only the allocations the inventory backs. Operation names
propose-1, propose-2 and so on, one per step, which are also the recorded fixtures: the
first step calls two tools, the second answers with three proposals of which one rests on an
item nobody has. The code path is app/projects/service.py and the loop itself is
respond_with_tools in the client.
Listing text¶
A pasted listing is read with one call and the strict JSON schema of ListingReading: the
title, the asking price and currency, the place and what is included as written, and the
fields the text supports with a confidence each. The field names offered are the label fields
plus the search profile's rule keys, so a plug diameter a seller wrote can be read; the
instructions say to leave out anything the seller did not state. Known fields pass through the
same normalisers as a label reading; a rule key the label set does not know is kept as read;
any other name is dropped. Operation name listing-text, which is also the recorded fixture.
The code path is app/find/service.py.
Search plan¶
A plan for a missing item is one call (effort low) with the strict JSON schema of
PlanWriting: three to six search phrasings with a line each on what they find, one match
pattern over listing text and the exclusion words. The brief carries the need's title, the
profile, the needed values, the values of the item that needs it, the notes and the top price;
the instructions ask for an RE2 pattern with short gaps and classes, no lookarounds,
backreferences, comments or inline flags, and never a part number the need does not give. The pattern goes through the same checks as one
typed by the owner and is dropped, with the reason, when it fails them. Operation name
search-plan, which is also the recorded fixture. The code path is app/find/service.py;
the checks are app/find/plan.py.
Decision cards¶
What should I do with it? is one call (effort low, an answer budget of 1,200 tokens) with
the strict JSON schema of ScenariosWriting: a keep and fix card (whether it is worth fixing,
the likely problems, the steps, the difficulty for this person and why, the parts, cost and
time), two or three reuse cards (each naming the kinds of item it uses from the rest of the
inventory) and a sell card (selling points and effort, never a price), with one recommendation
and its reason. The brief carries the item's accepted values (none that identifies it), its
condition, faults, tests, accessories and intent, the owner's rated skills, tools, effort,
whether they open devices up and budget, and the kinds of item they own besides with a count
each. Each text is cut to its bound and a reuse card without a title is dropped. Operation name
scenarios, which is also the recorded fixture. The code path is app/decide/service.py; see
Decide what it becomes.
Listing drafts¶
A Facebook Marketplace (US) draft is one call (effort low, an answer budget of 1,100 tokens)
with the strict JSON schema of DraftWriting: the title, the description, the category, the
condition, the tags, what it could be used for (a give-away), the photo order with a reason
each, and the facts each line rests on,
named by field. The input is the brief (accepted values without any that identifies the item,
the public listing details, the mode) and the item's first four photos at 768 px. The rules
then check the category and the condition against app/listing/marketplace_us.yaml, set the
price, cut the title and the tags, append the disclosures and drop any line that rests on
nothing confirmed. Operation names draft-sell and draft-give, which are also the recorded
fixtures. The code path is app/listing/service.py; see the seller flow.
Uses¶
What an item is good for is one call with the strict JSON schema of UsesWriting: up to three
uses, each with a title, what it would do, what else it needs, the effort and one thing to check
first. The brief carries the item's accepted values (none that identifies it or its owner), its
condition, faults, tests and accessories, the owner's preferences and the project templates the
rules already found it fills. The answer is shown under Ideas from the model: each text is trimmed
and cut to 200 characters, at most six needs and three uses are kept, and a use without a title
is dropped. Operation name uses, which is also the recorded fixture: three uses for a laptop.
The code path is app/decide/service.py; see Decide what it becomes.
Label extraction¶
One call per request, however many photos (up to MAX_PHOTOS, default 4):
- Input. The instructions describe the job (report only what is printed, confidence per
field, leave out unsupported fields). The user message carries the field list from
app/extract/fields.pyand oneinput_imagepart per photo. Photos are re-encoded first: EXIF applied and dropped, longest sideIMAGE_MAX_PX(default 1280), JPEG. JPEG, PNG, WebP and GIF up to 50 megapixels are accepted; HEIC is refused with a message to export as JPEG. - Output.
text.formatis the strict JSON schema ofLabelReading:item_type,label_text(a transcription) and a list of{name, value, unit, confidence}. The reply is validated back into the model; anything else is a502for the caller. - Effort and size.
reasoning.effortisEXTRACT_REASONING_EFFORT(defaultlow), the answer budget is 800 tokens. Reasoning tokens count againstmax_output_tokenson this API (a read atlowspent 798 of 800 on reasoning and stopped before the answer), so the client sends the budget plus a headroom by effort, 4,096 atlow, underMODEL_MAX_OUTPUT_TOKENSwhen set; tokens are billed as used, not as capped. The recorded one-photo call uses 1,480 input and 310 output tokens, about 0.003 USD at the standard rate. - Operation name
extract, which is also the name of the recorded fixture that answers every extraction in fixture mode.
The code path is app/extract/service.py; the normalisers in app/extract/normalize.py turn the
printed values into comparable ones, see the evidence model.
python -m app.extract photo.jpg # prints the extraction as JSON
./dev shell backend # then the same inside the container