Tools
A request with no tools field is served Drael's own catalogue, and the loop runs on our side: the model calls a tool, the result goes back to it, and you receive the answer.
A request that carries its own tools gets none of ours and executes them itself, which is how every OpenAI client already works. The two modes do not mix: the catalogue is served or it is not.
The catalogue
| Tool | Arguments | What it does |
run_code | code, language (python or bash), network | Runs a program in a sandbox. Files persist across calls in the same session. Network is off unless the call asks for it. |
generate_image | prompt, negative, aspect_ratio | Generates an image and returns its URL. See images. |
web_search | query, limit, onion | Searches the web and returns ranked results with titles, URLs and snippets. |
web_fetch | url | Fetches a page as text, including sites behind bot protection and .onion addresses. |
breach_lookup | kind, value | Looks an identifier up in breach corpora. Paid plans only. |
contact_lookup | name, company | Resolves professional contact details for a person or a company domain. Paid plans only. |
Six, under a hard ceiling of eight. Prompt and schema volume degrade the model: it follows instructions worse as the served context grows, and the previous product reached 13.218 tokens of tool schema and paid for it in invented tool names and shallower answers. Adding a tool here is an architecture decision, not a commit. The whole catalogue is visible to the model at once: there is no menu, no fold and no indirection, because those exist only to hide a catalogue that is too large.
A tool with no provider wired behind it is not served at all, rather than served and unable to answer.
The loop
One request is bounded at six model turns. A model that keeps calling tools without answering is a loop, and a loop on a GPU this product owns is an outage rather than a bill. One turn before the ceiling the catalogue is withdrawn and the model is told, in the conversation, to answer with what it has and to say plainly what it could not check.
You never see the intermediate turns: /v1 is a distribution channel, and a client pointed at it has to see an ordinary OpenAI stream. What the model says before calling a tool does pass through.
What a tool answers with
Every result is a JSON document the model reads, and its shape is the part that matters.
- An empty result says it is empty:
found: falsewith a note saying the corpus was queried and holds nothing. "Nothing found" and "the provider could not answer" must never be the same shape, because a model that cannot tell them apart narrates over the difference. - A failed call says
ok: falseand says why. It is never silence. - Each field is cut at 8 KiB, with the cut stated in the text rather than implied. A result is prompt volume by another name: it is replayed to the model on every later turn.
Running code
run_code answers with ok, exit_code, stdout, stderr, truncated, timed_out and duration_s.
- Python or bash, in one container per run, with a read-only root and only that session's directory mounted. The tenant boundary and the network flag are the two things here that are real security, and they are tested as such.
- Network is off by default and is a per-call flag. A run that did not ask for it has none.
- A program is at most 256 KiB of source.
- A run is given 30 seconds unless it asks for longer, and cannot be given more than 5 minutes.
- The sandbox returns at most 64 KiB of each stream, and the tool result carries at most 8 KiB of each. Both cuts are reported in
truncated. - Memory, CPU and process count are bounded per run, so a runaway program takes the run down and nothing else.
Files live under the session named by X-Drael-Session (see chat). A request with no session header gets a workspace for that request alone, so nothing it writes is readable later.
Search and fetch
Search runs on our own SearXNG instance. It aggregates the engines rather than being one, so there is no third party holding a log of what this product's users searched for; that is the whole reason it is ours. limit is honoured up to 20 and defaults to 10.
onion: true asks the hidden services instead of the clear web. Discovery there is deliberately narrow: web_fetch reaches any .onion address through Tor, so the model can read a hidden service it already knows, and it cannot presently discover one it does not.
web_fetch renders the page with a real browser, which is what reaching a site behind a bot challenge takes. Onion addresses do not go through it: they are fetched over a SOCKS5 dialer that resolves the hostname at the proxy, so the address never touches this host's resolver.
What a call costs
run_code,web_searchandweb_fetchcost nothing. They run on a GPU and a search stack we already own, so they are not counted against anything.generate_imageis cash at a provider per attempt, and counts against the plan's images for the week.breach_lookupandcontact_lookupare on the paid plans and count against the plan's lookups for the week. A contact lookup is metered per contact returned, and a premium contact counts double.
Credit is held before the call and settled after it, so a tool that did not run is not charged. The weekly figures are published by GET /api/billing/plans and on the pricing page; they are not restated here, because a number in two places is a number that drifts.
When a plan's allowance for the week is used, the call answers weekly_allowance_reached, and extra usage carries on past it if you have turned it on and set a daily ceiling. A tool the plan does not include at all answers plan_required, and the model is told plainly that it did not run.