llm.chat(conversation, { service, model, address, key, system, tools, shape, effort, reply_tokens, timeout, own })
gives { text, calls, stop, usage, kept }
llm.decide(state, { service, model, address, key, questions, timeout })
gives { answers, model, usage }
llm.takes(blob, { service }) returns true when the service accepts the blob in files
chat holds a conversation with a model. decide puts questions with fixed answers to a System One model, which answers them all at once with probabilities and writes no text.
conversation is a list of parts, each one of these.
{ role: "user", content, files }what the model is told
{ role: "assistant", content, calls, kept }a reply of the model's
{ role: "tool", results: [{ id, name, result, error, files }] }the results of its tool calls
files maps a name to a blob: an image (png, jpeg, gif or webp, up to 5 MiB) or a PDF (up to 32 MiB). The plugin sends each to the model after a line that gives its name. Any other blob fails the call before anything is sent.
Options:
serviceanthropic, openai (the Responses API), openai-chat (the Chat Completions API) or claude-code (the claude program on the runner's computer); default anthropic
modelthe model's name; required
addressthe service's URL, when it is not https://api.anthropic.com or https://api.openai.com
keya handle to the secret that holds the service's key
systemthe system prompt
toolsthe tools the model may call: [{ name, does, args }], with args a JSON Schema with type: "object"
shapea reply that is data, as a JSON Schema with type: "object"; text is then JSON. A field with format: "blob" and no description is described as a blob's name
effortlow, medium or high; claude-code also takes xhigh and max
reply_tokensthe most tokens a reply may hold
timeoutseconds, or text such as "5m"; default 3m
ownfor claude-code: the program's own tools that it may use, by name
Returns:
textthe model's reply; also emitted as "text" while it arrives, for a client that watches the run
callsthe tool calls the model made: [{ id, name, args }]
stop"end", "tools" when it made tool calls, or "length" when the reply was cut at its limit
usage{ input, output }, in tokens
keptwhat the service needs back unchanged, or null; put it in the assistant part
For decide, state is the text or data the questions are about, and questions is written in the System One format, which TypeSafe's Jev set: each question under its name, with these fields.
typenoul, choice or score
instructionswhat is asked
criteriafor choice, { option: description }; for score, the levels from lowest to highest; noul has none
Options for decide:
servicesystemone; default systemone
modelthe model's name; required
addressthe service's URL, when it is not https://api.typesafe.ai: a model served on the runner's computer, or by a cloud
keya handle to the secret that holds the service's key; a model on the runner's computer has none
questionsthe questions, as above; required
timeoutas for chat
Returns, for decide:
answerseach answer under its question's name, as the service wrote it: { type, noul } for noul, the probability that the statement holds; { type, choice, probabilities, confidence } for choice; and { type, score, legend, probabilities, confidence } for score. confidence is how concentrated the probabilities are, not how likely the answer is right: a condition reads a probability
modelthe version of the model that answered; jev-latest names a different version after each release
usage{ input, output }, in tokens
Errors:
bad_inputthe service does not accept a blob in files, or does not make the call
refusedthe service could not be reached, returned an error, or stopped before the reply was complete
timeoutthe reply was not complete within the timeout
not_foundthe claude program could not be started
failedthe claude program ended without a reply
Needs net:host for the service and secret:NAME@net:host for its key. claude-code needs these in their place.
exec:claudeto start the program
env:HOMEwith no key: the program is signed in as the runner's user
env:USERPROFILEthe same, on Windows
secret:NAME@exec:claudewith a key: an API key, or the token that claude setup-token prints
networkfor WebSearch or WebFetch in own:
diskfor Read, Glob, Grep, Edit, Write or NotebookEdit in own:
allfor any other tool in own:, such as Bash or Skill
claude-code ignores address and reply. Agent lists the types, and says how Corbie starts the claude program.
This script asks for a reply as data.
allow: [net:api.anthropic.com, "secret:ANTHROPIC_KEY@net:api.anthropic.com"]
const reply = llm.chat([{ role: "user", content: "Say hello." }], {
model: "claude-sonnet-5-5", key: secrets.get("ANTHROPIC_KEY"),
shape: { type: "object", properties: { greeting: { type: "string" } } },
})
return JSON.parse(reply.text) // gives { greeting: "Hello." }This script sends an image with its question.
allow: [fs.read:/srv/reports/**, net:api.anthropic.com, "secret:ANTHROPIC_KEY@net:api.anthropic.com"]
const chart = blobs.file("/srv/reports/chart.png")
llm.takes(chart, { service: "anthropic" }) // gives true
const reply = llm.chat([{ role: "user", content: "What is this?", files: { chart } }], {
model: "claude-sonnet-5-5", key: secrets.get("ANTHROPIC_KEY"),
})
return reply.text // gives "A picture, named chart."This script calls the claude program, signed in as the runner's user.
allow: [exec:claude, env:HOME]
const reply = llm.chat([{ role: "user", content: "Say hello." }], { service: "claude-code", model: "sonnet" })
return reply.text // gives "Hello."This script puts two questions to a System One model.
allow: [net:api.typesafe.ai, "secret:TYPESAFE_KEY@net:api.typesafe.ai"]
const r = llm.decide("Checkout has been failing for every customer for the last hour.", {
model: "jev-latest", key: secrets.get("TYPESAFE_KEY"),
questions: {
team: { type: "choice", instructions: "Which team should handle this?",
criteria: { billing: "Payments, invoices and refunds", technical: "Outages, errors and configuration" } },
urgent: { type: "noul", instructions: "The customer cannot use the product." },
},
})
r.answers.team.choice // gives "technical"
r.answers.urgent.noul > 0.9 // gives true
return r.model // gives "jev-1.13.0"