# Model composition

The same maintained guide available through berry guide models.

Source: cd46e0e78d73497aec99e3e8d33c1fef632f36f9
URL: /reference/models

Design Berry programs by composing model routines. A generator performs the work
of a step: it transforms information, reasons, produces an artifact or uses its
declared Tools. A classifier produces the condition or decision that selects
what happens next. Native connections make those decisions into executable flow.

Start with this vocabulary when designing an agent. Generators supply the work
bodies, classifiers supply branch and loop decisions, and connections compose
them into a program. JavaScript is an optional supporting capability for a
specific computation; it is not required to organize these model compositions.

## Classical programming expressed through models [#classical-programming-expressed-through-models]

Shared invocation data can be selected deterministically instead of being copied
by the model into each child argument. In draft/review bodies, put
`$.parent.input.goal` inside double braces to select their caller's exact goal.
The children still declare their own input contracts for the changing draft
or editing request.
Caller data is frozen at child admission, not a live view into its future work.
For a plan produced during work, pass it through next.input and select it in the
following position or publish it with runtime.session.state.write and select
`$.session.state.plan.value` inside double braces. Readers are granted dependencies;
their results are recorded before inference. No automatic ancestor-context
copying takes place.

For reusable design patterns, invoke choose_pattern when offered, with the problem
or method name. Otherwise use load on the catalog: Read composition patterns with: berry guide patterns.
It covers planning, tool loops,
candidate search, verification, retrieval, delegation and memory with checked
native examples. Choose the smallest composition that serves the actual goal.

| Classical construct | Berry composition |
| --- | --- |
| Statement or work body | Generator instructions describing the transformation or action |
| Function | Named model routine with input/output and declared dependencies |
| Function parameter | Input value; descriptions explain its meaning to the model |
| Return value | Local output, or the final result of an invoked next chain |
| Local program data | An explicit payload carried between positions |
| Predicate/condition | Classifier output with described alternatives |
| if/else | Classifier with two alternatives connected to two branches |
| switch | Classifier with several alternatives and explicit cases |
| Compound condition | One classifier assessing a combined condition, or a chain of guards |
| Sequence | Generator A -> generator B through next |
| while | Classifier -> work generator -> the same classifier |
| repeat-until/do-while | Work generator -> classifier -> repeat or finish |
| for/foreach | Initial iteration payload -> classifier -> one-item generator -> classifier |
| break | A classifier alternative routed to a finish routine |
| continue | A branch returning to the loop check or increment position |
| Recursive function | A generator invokes its own declared routine, or routines invoke each other |
| Subroutine call and return | A routine listed in tools; its complete result returns to the caller |
| Parallel branches and join | A generator requests independent routines together, then combines their results |
| Background subtask | An async connection, followed by its correlated completion |
| Strategy selection | A classifier routes to one of several specialized generators |
| Evaluator/optimizer | Work generator -> independent classifier -> accepted output or another attempt |
| State runtime | Explicit state payload, classifier decisions and named transitions |
| Module/library | Public routine exports and pinned imports |
| Reusable instructions | Addressed content read through load or small build-time inclusions |

These are mappings onto the existing language. There are no separate frontmatter
keywords named if, while, for, parallel or recursive. The program is the graph of
routines, their contracts, model instructions and connections.

## Internal request configuration [#internal-request-configuration]

A routine's public arguments describe its task. Internal request preparation can
choose model settings or the offered tool subset before each generation decision.
It does not rewrite the callable contract. A missing `configure` uses declared
defaults without an extra call. Enabling effort alone never changes declared tools.

```yaml
name: answer
description: Answer the supplied question.
model:
  select:
    operation: generation
    supported_parameters:
      - effort
  effort: low
configure:
  routine: choose_request
  fields:
    - effort
```

Its body contains the answering instructions. The configuration routine is a
separate ordinary classifier, supplied in the same composition:

```yaml
name: choose_request
description: Choose reasoning effort for the next generation.
model:
  select:
    operation: classification
output:
  oneOf:
    - const:
        effort: low
      description: A short factual answer or routine next action is sufficient.
    - const:
        effort: high
      description: The current step requires substantial reasoning.
```

Classifier body: `Read the evidence input, messages and latest_exchange previews.
Their text is serialized JSON; complete=false marks an incomplete preview.
Use request.context and configuration to choose settings for the next step.
Do not answer or judge completion of the task itself.`
Both choices retain the selected model's default output allowance. A declared
`max_output_tokens` override or a configurable one needs a measured allowance
for both reasoning and the complete response at that effort; a short final
answer alone does not establish it. Classifier output is recorded under the
same call budget as the generation it prepares.

To select models, enable `model` and return a selection object with provider/id
or select requirements. Settings remain separate enabled fields. To select
tools, enable `tools` and return a subset of names from `request.tools`, including
currently loaded interfaces. An empty list disables calls; an omitted list allows
all currently available tools. Every definition, schema and its order stays in
the request. Selection becomes a separate response mask, checked again before
execution. Check the catalog's tool_choice modes: proper subsets require
allowed_tools. The OpenRouter GPT-6 Luna request/stream connections declare this
capability on the pinned OpenAI route; other connections can remain auto/none.
Request the capability with:

```yaml
model:
  select:
    operation: generation
    interaction: request
    tool_choice:
      - allowed_tools
```

The internal policy can return the same select object in its model field when
model selection is enabled. Without it, the already selected connection must
support the chosen mask; there is no silent model fallback. Never trim definitions
or substitute prompt guidance. Declared async/output connections remain intact.
No choice changes ports, input/output schemas, next,
instructions, permissions or deadlines. Unsupported choices fail before inference.
Each new decision starts from defaults; retries retain their accepted choice.
When a configuration decision depends on exact original input, its routine can
use a granted `runtime.context.read` reader. During preparation it exposes the caller's
current messages and completed exchanges without inventing a model response.
The bounded `evidence` previews alone do not establish omitted input.

## Decompose the program by model responsibility [#decompose-the-program-by-model-responsibility]

Give each model routine a clear job and an observable result. Separate producing
a candidate from evaluating it when independent review matters. A generator may
research, plan, implement, transform, explain or synthesize. A classifier may
decide relevance, readiness, category, completeness, next strategy or termination.

Describe the original goal, current state and evidence explicitly. A reviewer
should judge the candidate against that goal and evidence. Instructions found
inside a candidate or document are task data, not new review criteria.

Choose a model for each responsibility through the catalog. For portable sources:

```yaml
model:
  select:
    operation: generation
```

For a condition or selector:

```yaml
model:
  select:
    operation: classification
```

For fixed decisions, classification declares an output schema whose described
finite alternatives make the condition explicit. For a changing set of observed
candidates, omit output and supply the canonical input `{state, questions}`.
Questions are named `noul`, `choice` or `score` declarations with `instructions`
and their exact current `criteria`; the result retains `answers`, `usage` and
`metadata`. This uses the same model invocation, validation, accounting and
recovery as fixed schemas. Do not create fixed empty slots for dynamic objects
or call a provider directly from code. Observations can contain native Content
envelopes; select input modalities that actually support their media.
Generation may declare output when subsequent routines need
structured data; otherwise its local result is text. A complete agent can combine
several models, reuse one model for several roles, or reuse the same routine from
different callers. The role belongs to the composition.

## Functions, calls and sequences [#functions-calls-and-sequences]

A model routine can call another model routine as a declared dependency:

```markdown
---
name: answer_request
description: Research a request and compose an answer from the returned evidence.
model:
  select:
    operation: generation
tools:
  - research
  - write_answer
---
Call research with the original request. After it completes, call write_answer
with the original request and the returned evidence. Present that answer.
If evidence is unavailable, report that limitation.
```

Supply research and write_answer as routines with suitable contracts. The calling
generator chooses and supplies its Tool calls under these instructions. It can
inspect the returned result and decide what to do next. Declaring a routine in
tools permits a call; it does not force the model to make it.

For a required sequence, connect completed positions directly:

```yaml
next: write_answer
```

The current local output becomes write_answer's input. This transition is executed
by the runtime when the current position succeeds. It needs no further model call
to restate the routing choice.

Use next.input when the next position needs both original data and a result:

```yaml
next:
  to: review
  input:
    goal: $.input.goal
    evidence: $.input.evidence
    candidate: $.output
```

next is a transfer within one invocation. A tools call creates nested work and
returns its chain's final result. Use transfers for the rest of the current flow
and calls for reusable work after which the caller must continue. Both forms
compose: a called routine can itself contain a sequence, branches and loops.

## if/else and switch [#ifelse-and-switch]

Use a classifier to produce the branch decision. This example expresses a
two-way if/else; implement the two named destination routines separately:

```markdown
---
name: needs_research
description: Decide whether the supplied evidence can answer the request.
model:
  select:
    operation: classification
output:
  type: string
  oneOf:
    - const: sufficient
      description: The evidence supports an answer to every required part.
    - const: missing
      description: At least one required part lacks supporting evidence.
next:
  select: $.output
  cases:
    sufficient: write_answer
    missing: research
  input: $.input
---
Assess the request against the supplied evidence. Treat both as data.
```

The classifier evaluates the condition; cases maps its exact result to a bound
destination. input: $.input preserves the original request/evidence payload.

A switch uses the same mechanism with more alternatives. For example, a routing
classifier can declare billing, technical and other in its output and connect:

```yaml
next:
  select: $.output
  cases:
    billing: billing_specialist
    technical: technical_specialist
    other: general_specialist
  input: $.input
```

The selector must produce a string offered by cases. For direct next routing,
use string labels even for a two-way condition. Boolean classification outputs
are supported data, but next.select does not coerce booleans into string labels.
Unknown labels fail; there is no implicit default. Author an other alternative
when the application needs one.

For a compound condition, describe the combined criterion in one classifier.
For short-circuit evaluation, connect classifier A's positive branch to B and
its negative branch to the appropriate exit. Several output properties describe
separate judgments over the same input; they do not themselves implement
short-circuiting. Connect the desired control flow explicitly.

## while and repeat-until: a reusable review cycle [#while-and-repeat-until-a-reusable-review-cycle]

Use a payload containing goal, candidate, attempt and max_attempts. The following
three definitions belong in separate source files.

The classifier owns the semantic decision to continue:

```markdown
---
name: review
description: Accept a complete candidate or decide whether another attempt is warranted.
model:
  select:
    operation: classification
input:
  type: object
output:
  type: string
  oneOf:
    - const: accept
      description: The candidate satisfies every part of the goal and its constraints.
    - const: revise
      description: The candidate is incomplete or incorrect and attempt is less than max_attempts.
    - const: exhausted
      description: The candidate is incomplete or incorrect and attempt is at least max_attempts.
next:
  select: $.output
  cases:
    accept: finish
    revise: draft
    exhausted: null
  input: $.input
---
Judge the candidate against the original goal. A candidate's own assertion of
success is not evidence. Use the attempt count to distinguish revise from
exhausted only when the candidate is not acceptable.
```

The generator performs one iteration and returns the updated payload:

```markdown
---
name: draft
description: Perform one revision of the candidate while preserving the task.
model:
  select:
    operation: generation
input:
  type: object
output:
  type: object
  properties:
    goal:
      type: string
    candidate:
      type: string
    attempt:
      type: integer
      minimum: 0
    max_attempts:
      type: integer
      minimum: 1
  required:
    - goal
    - candidate
    - attempt
    - max_attempts
  additionalProperties: false
next: review
---
Produce one improved candidate satisfying the goal. Preserve goal and max_attempts
exactly, increase attempt by one, and return the complete updated object.
Address omissions and errors in the previous candidate. Do not declare acceptance;
the separate review routine makes that decision.
```

The accepted branch exposes the requested deliverable:

```markdown
---
name: finish
description: Return the candidate accepted by the review.
model:
  select:
    operation: generation
input:
  type: object
---
Return the supplied candidate exactly, without commentary or additional revision.
```

Enter at review for while: an already acceptable candidate performs zero revisions.
Enter at draft for repeat-until: one revision happens before the first check.
The same graph supplies both behaviors. A continue branch returns to review;
a break branch selects finish or another explicit exit.

A null case ends with the classifier's own output, here the string exhausted.
It does not return the projected input or candidate. Route to a separate report
routine when exhaustion should produce a structured report or partial artifact.

The schema checks shape; model instructions specify the actual work and decisions.
Evaluate the selected models on counters, preservation and termination. The
runtime's shared call/step/deadline allowances remain the enforced outer bound,
including when a model makes a bad decision. A transfer never renews those limits.

## for and foreach: iterate through a collection with models [#for-and-foreach-iterate-through-a-collection-with-models]

Carry {items, index, results}, initially index:0 and results:[].
The classifier tests whether another item remains:

```markdown
---
name: for_condition
description: Decide whether the next collection item remains to be processed.
model:
  select:
    operation: classification
input:
  type: object
output:
  type: string
  oneOf:
    - const: advance
      description: index is smaller than the number of items.
    - const: done
      description: index is at least the number of items.
next:
  select: $.output
  cases:
    advance: for_item
    done: collect_results
  input: $.input
---
Compare the current index with the number of supplied items.
```

The generator performs the loop body and advances its state:

```markdown
---
name: for_item
description: Summarize exactly one item and advance the collection state.
model:
  select:
    operation: generation
input:
  type: object
output:
  type: object
  properties:
    items:
      type: array
      items:
        type: string
    index:
      type: integer
      minimum: 0
    results:
      type: array
      items:
        type: string
  required:
    - items
    - index
    - results
  additionalProperties: false
next: for_condition
---
Summarize items[index] in one sentence and append that sentence to results.
Process exactly one item. Preserve items and earlier results exactly.
Increase index by one and return the complete updated object.
```

The exit routine returns the accumulated results:

```markdown
---
name: collect_results
description: Return the completed collection of summaries.
model:
  select:
    operation: generation
input:
  type: object
output:
  type: array
  items:
    type: string
---
Return the supplied results array unchanged.
```

This is initialization -> condition -> body/increment -> condition -> exit.
An empty list exits without invoking for_item. To filter, classify an item before
processing it; the rejected branch must still advance the index. To fold/reduce,
carry an accumulator updated by the work generator. To traverse a tree, carry a
frontier or use recursive calls. Preserve the original goal alongside collection
state when each iteration needs it.

These are semantic model operations. Check preservation and progress with real
inputs. Use an explicitly granted exact helper when a particular transformation
requires mechanical precision; keep the surrounding model composition visible.

## Recursion, divide-and-conquer and hierarchical work [#recursion-divide-and-conquer-and-hierarchical-work]

A model routine may declare itself in tools. Its child execution has its own
input and model context, and returns a result to the calling model:

```markdown
---
name: solve
description: Solve a task directly or recursively combine smaller solutions.
model:
  select:
    operation: generation
input:
  type: object
  properties:
    task:
      type: string
    remaining:
      type: integer
      minimum: 0
  required:
    - task
    - remaining
  additionalProperties: false
tools:
  - solve
---
If remaining is zero, answer from the available information or report what is
missing; do not invoke solve again. For a task you can answer directly, answer it.
Otherwise divide it into smaller concrete subproblems. Call solve for each with
remaining reduced by one. Keep the original goal while combining the returned
solutions. Report unresolved parts rather than claiming they were completed.
```

Use a meaningful base case and a decreasing measure. Mutual recursion works
through the same declared calls. Keep child questions bounded and supply the
evidence they actually need. The parent's entire model context is not implicitly
shared with its child. Ancestors and descendants share execution allowances.

Recursive calls return to the parent; a cycle of next transfers moves the same
invocation between positions. Choose according to whether earlier work must
resume and combine a returned result. Runtime call cycles are allowed, while
cycles in instruction inclusion or content references are rejected.

## Parallel experts, map/reduce and async delegation [#parallel-experts-mapreduce-and-async-delegation]

A generator can request independent routines in one Tool-call batch. The runtime
admits and schedules that batch under its shared concurrency limits. A reviewer
or synthesizer can use the joined results:

```markdown
---
name: assess
description: Combine independent factual and clarity reviews of a candidate.
model:
  select:
    operation: generation
tools:
  - check_facts
  - check_clarity
---
Submit check_facts and check_clarity together for the supplied candidate and its
evidence. They are independent assessments. After both return, combine their
findings into one review. Preserve disagreements and identify failed assessments.
```

Supply the two routines with the appropriate model operations and schemas.
Model instructions request a batch; verify the actual trace when parallel
dispatch is a requirement. Declaring tools alone does not force simultaneous
calls. Results are correlated with accepted call identities and request order.

For map/reduce, a coordinator generator creates independent item calls, then a
generator combines their results. For a committee, several model routines judge
the same evidence and another routine resolves or reports disagreement.
For a plan/execute/review flow, a planner returns a structured plan, a worker
composition performs it, and an independent classifier selects finish or revise.

Use an async connection when a calling model should continue after acceptance:

```yaml
tools:
  - worker:
      async: true
      output: true
```

A receipt identifies accepted work; the later completion supplies the result.
output:true forwards the child's output when both endpoints have output ports.
Declare bidirectional ports on a generation entry that accepts further input. It can process new user messages while the child works.

Async work remains attached to its invocation. A terminal generator must process
outstanding completions; a next transfer can hand explicitly async work to later
positions. Cancellation and deadlines apply to owned work. An independent service
or scheduled invocation needs its explicit host contract.

## State, interfaces and reusable behavior [#state-interfaces-and-reusable-behavior]

Pass durable task facts explicitly: original goal, evidence, current artifact,
decisions, accumulated results and remaining work. next.input can select and
construct data without asking a model to copy it. Use singular JSONPath such as
$.input.goal or $.output.items[-1]. Bare strings in next.input are paths;
literal strings need $literal. Missing selections fail and null remains data.

A routine's model context, a payload passed to another position, and persistent
application State have different lifetimes. Use granted runtime.state.read/runtime.state.write or
session-scoped counterparts for application records across invocations.
Keep the State version returned by a read; a conflicting write requires a new
decision. A sequence of model/Tool calls is not a transaction over external effects.

Encapsulation means one owner for each decision, invariant and mutable fact.
Expose a small callable contract and keep implementation details private.
Alternative generators can implement the same contract; a classifier selects
the appropriate strategy. Test both structural compatibility and task quality.
Do not equate model-name substitution with equivalent behavior.

Factor recurring behavior into callable library exports. Factor recurring
guidance into instruction content. Consumers can load that guidance as needed
through ordinary load. An active `[[name]]` source reference supplies an exact
address; references shown in inline code or fenced examples remain literal.
A standalone `{{name}}` includes small unconditional instructions during build.
Neither reading nor inclusion executes a routine or expands grants.

A library uses plugin.json with standard name/version and explicit exports in
extensions["ai.strawberry.berry"], without an agents declaration. Imports pin exact public
definitions; private routines stay behind their interfaces. Changes produce
another immutable revision, while accepted consumers keep their exact bindings.

## Effects, interaction and failures [#effects-interaction-and-failures]

A generator performs external actions through granted Tools and evaluates the
returned evidence. A plan, generated command or success claim is not evidence
that the effect happened. UI, files, processes, messages and model calls retain
their own concrete contracts.

For a semantic failure such as an inadequate candidate, let a classifier choose
revise, another strategy, partial completion or exhaustion. Transport failures
and unknown external outcomes are execution facts; they do not automatically
follow a successful next branch. Inspect what happened before proposing another
effect. A repeated effect needs the owner's retry/idempotency contract.

Human input is another interaction source. Use runtime.user.request for choices
when granted, with its output connected; the answer returns through the session's
existing continuation. Use runtime.ui.publish and explicit bound actions for interfaces.
Generating an interface does not require creating another agent.

Long-lived processes and schedules use the corresponding host Tools. They need
explicit ownership and lifecycle. Model routines compose their behavior, while
the runtime enforces grants, budgets, recording and supported recovery.

The same public agent can run directly or through runtime.schedule.create; do
not invent a daemon-agent type or keep an inference loop alive merely to wait.
An interval retains activation intent; no interval means one occurrence. Each
occurrence has finite execution limits. Exact root/entry and optional agent
identify its accepted source. Fresh runs can share an application State checkpoint;
an explicit session receives sourced timer input under its existing continuity.
Busy sessions skip background input instead of interrupting a human turn.
runtime.schedule.next allows a calling scheduled session to inspect its own
version and observed now, then choose a future start without changing rights,
input or limits. It cannot undo Pause or revive failed intent. Declare and grant
only the schedule operations the composition needs.

Place a deterministic source-version check before optional inference. Persist
claims and decisions in bounded State records. An unresolved earlier effect must
remain visible for reconciliation; starting a fresh occurrence does not make a
notification, payment or external write safe to repeat. Return quietly when no
work is needed. Communicate only through an explicitly granted notification,
message or authored output. Silence has no magic token.

JavaScript routines remain available for focused exact algorithms, data adapters
or error-handling requirements. Introduce one for a concrete responsibility.
Sequences, conditions, model loops, recursion and model delegation already have
native representations described above.

## Verify the composition you designed [#verify-the-composition-you-designed]

Build and prepare the declared graph, then invoke representative inputs and
inspect the accepted trajectory. A successful build establishes source validity;
it does not establish a classifier's judgment or a generator's task quality.

For model control flow, check:
- if/switch: each offered branch and the original data delivered to its target.
- while: zero iterations, successful revision and exhaustion.
- repeat-until: at least one body execution before checking.
- for: empty, one-item and multi-item inputs, ordering and preserved state.
- recursion: direct/base cases, decomposition, return to the parent and limits.
- parallel work: actual call batching, correlation and failed-branch handling.
- async dialogue: another user message while work is pending, then one completion.
- retained work: exact accepted calls and inputs survive supported restoration.

Judge the result against the original user goal and observable evidence.
Keep model decisions, actual effects and runtime guarantees distinct. Improve
instructions, schemas, model selection or graph structure at the owner of the
observed problem.
