Skip to content

Typed APIs with Bounded Agent-Owned Decisions

CONTRACT-FIRST PYTHON APIS

Define the endpoint. Summonpot owns the orchestration.

Declare the request, fixed goal, exact application-owned operations, and response. Summonpot orchestrates those operations; the agent chooses only where the contract leaves a bounded choice.

Summonpot — a simple contract for deterministic operations and bounded agent-owned decisions

GET STARTED

Install Summonpot

pip install "summonpot[serve,cli]"Quick start guide

EXPLORE THE MECHANISM

One contract. Explicit authority.

Explore the shipped bounded customer lookup: request-owned data, an allowed choice, and one application-owned operation.

Open the playground

Browser-local preview. Your browser runs a JavaScript preview; no Python server, model, or API call runs here.

Summonpot modernizes APIs for AI without replacing the endpoint with a separate agent layer. Declare one typed endpoint contract: what callers may send, the fixed goal, the exact application-owned operations Summonpot may orchestrate, and what the endpoint may return.

The agent’s authority is bounded by the endpoint declaration. Exact application behavior stays application-owned; semantic choices are agent-owned only where the contract explicitly allows them.

One endpoint contract

Typed requests, a declared goal, an explicit capability set, validation, HTTP routes, and OpenAPI stay together.

Bounded authority

The agent can make only declared choices. Required operations and locally validated output constrain execution.

Start without a provider key

Install the serving extras and use the keyless test model to wire a harmless endpoint.

The browser-local contract playground lets you inspect request-owned customer IDs, a bounded summary/detailed format you select, and one application-owned lookup. It does not call a model, execute Python, or contact a server. Compare the preview with the shipped bound-operation example for the actual Python declaration and API.

The existing overview diagram shows the current single-operation direct slice beside the agent-backed path. It is conceptual; eligibility remains limited to the complete contract described below.

One Summonpot application branches into either the exact shipped single-operation direct slice or an agent-backed endpoint through the same typed HTTP and OpenAPI framework.
One application contract supports the currently shipped direct slice and the agent-backed path. Open full size.

The existing overview remains the framework map. This diagram follows the runnable producer-choice inventory example: the model sees bounded candidate evidence and opaque tokens—not canonical objects. Summonpot checks request-local token membership and binds the exact validated candidate. The real application operation then checks current stock and revision and commits the SQLite receipt. The agent-mediated multi-candidate path remains in the same run; the 0.14.0 singleton optimization applies only to one choice in an otherwise eligible chain. The application owns transactions and idempotency.

Four-step validated producer choice: the application returns bounded in-stock candidate records; Summonpot shows inert descriptions and opaque tokens to the model; the agent selects a token that Summonpot verifies and binds to the exact canonical candidate; the application operation checks stock and revision in SQLite, commits an order and returns its locally validated receipt. The model does not receive canonical objects. The chain remains agent-mediated; application code owns transactions and idempotency.
Summonpot owns bounded evidence and token validation; the application owns the live-state check, transaction, and receipt. Open full size.

Accessible equivalent: the producer yields validated inventory snapshots; the model selects only from projected descriptions using opaque tokens. The runtime verifies token membership and binds the exact canonical candidate. The application operation checks stock and revision before its transaction and idempotency logic writes the order; local validation returns its canonical receipt.

This endpoint takes a validated topic, lets the agent choose the operation’s response style, and requires one application-owned operation. The operation computes a candidate summary; the model cannot supply its topic or replace its implementation. The final model response is schema-validated, not automatically bound to that candidate.

from typing import Literal
from pydantic import BaseModel, Field
from summonpot import AgentChoice, Exactly, FromRequest, Operation, Required, Summon
class SummaryRequest(BaseModel):
topic: str = Field(min_length=1, max_length=200)
class SummaryResponse(BaseModel):
summary: str
style: Literal["brief", "detailed"]
def build_summary(topic: str, style: Literal["brief", "detailed"]) -> SummaryResponse:
"""Build a summary for the requested topic in the selected style."""
length = 80 if style == "brief" else 240
return SummaryResponse(summary=f"{topic[:length]}", style=style)
summary_operation = Operation(
build_summary,
bind={"topic": FromRequest("topic"), "style": AgentChoice()},
output=SummaryResponse,
)
summon = Summon("summary-api")
@summon("/summarize")
def summarize(
request: SummaryRequest,
result=Required(summary_operation, calls=Exactly(1)),
) -> SummaryResponse:
"""Summarize the requested topic and choose a brief or detailed style."""
...

Save as app.py, install the serving extras, then start the local API:

Terminal window
python -m pip install 'summonpot[serve,cli]'
export SUMMONPOT_MODEL=test
summonpot serve app.py --host 127.0.0.1 --port 8000

The test model is keyless, not a side-effect-free sandbox. This example’s operation is harmless; use only harmless capabilities when wiring your own endpoint. Try it through the same HTTP route clients use:

Terminal window
curl -X POST http://127.0.0.1:8000/summarize \
-H 'Content-Type: application/json' \
-d '{"topic":"Contract-first APIs keep application behavior explicit."}'

This contract requires one Exactly(1) operation and declares an AgentChoice, so the operation’s call count and the final response schema are enforced. The topic argument is application-bound; the agent chooses the operation’s style argument. The keyless test model returns placeholder response strings, not a useful semantic summary. Response validation does not prove that final text reproduces the operation’s result.

A fully application-bound operation in the supported single-operation shape can instead execute directly without resolving a model. A uniquely ordered sequential result chain is included from version 0.11.0 and remains agent-mediated at that boundary. From version 0.12.0, fully resolved chains execute model-free only for a Pydantic request, at least one FromRequest binding, a unique required Exactly(1) sequence, supported request/result bindings or immutable defaults, and an exact full terminal response annotation. An AgentChoice normally remains agent-mediated; the result-dependent source exception included from version 0.14.0 is specified in the operation reference. Broader graph execution remains planned. Historical v0.11.0 chains remain agent-mediated; see the roadmap.

The v0.14.0 release supports exactly one producer-backed choice in an otherwise eligible uniquely ordered required Exactly(1) chain. Version 0.15.0 supports one or more producer-backed choices with framework-owned progression. In an otherwise eligible uniquely ordered required Exactly(1) chain, a Pydantic request, at least one FromRequest, supported request/result/authenticated-context bindings or immutable defaults, and exact full terminal response annotation identity remain required. All current consumer domains are checked before selection or start. Singleton bindings resolve locally; a decision-only selector returns ambiguous tokens with no business-operation tools. The shared kernel validates and invokes the operation, then continues locally until another genuine decision is needed. The canonical terminal result returns without a final model acknowledgment. Request/principal, reservations, token domains, usage budgets and one deadline remain request-local across frontiers and corrections. Explicit tool_calls_limit retains the previous one-way agent suffix; allow_direct=False and other noneligible declarations retain their existing route. Limits remain 128 candidates and 65,536 evidence characters per binding, not an aggregate prompt/context budget. Prefix effects may precede provider-configuration failure; no rollback, effect replay or durable resume is added. Historical v0.13.0 behavior remains agent-mediated for producer-backed choices. Historical v0.14.0 remains exactly-one-choice. See the operation reference for details.

One required typed operation may run a finite positive number of times (1 <= minimum <= maximum, maximum > 1) with a Pydantic request, at least one FromRequest, direct closed Literal[str] AgentChoice inputs, supported request/context bindings or immutable defaults, and exact full operation-output/endpoint-response annotation identity. Summonpot owns sequential decisions; business functions are not selector tools. It locally validates selections, shares one usage budget and deadline, resolves the model lazily, auto-finishes at maximum, and returns only the final successful canonical result; any failure aborts. Previous-result evidence is limited to 65,536 characters per decision, not an aggregate prompt budget. Explicit tool_calls_limit fails before provider/effects. No producer choices, FromResult, after, zero-minimum/unbounded bounds, implicit batch, or general graph. Existing Exactly(1) and producer-choice routes remain unchanged. Published v0.14.0 is untouched; milestone 3 remains partial and a narrow repeated-stage consumer is now documented below. Broader repeated-result addressing remains planned.

Version 0.15.0 repeated-stage final-result consumer

Section titled “Version 0.15.0 repeated-stage final-result consumer”

The original two-operation form lets one bounded repeated producer feed one required, choice-free terminal operation after the producer stage closes. The terminal uses FromResult(producer, field, invocation="final") to bind the final successful canonical result from one immutable closure record; meeting the minimum alone does not make it ready, and a failed or uncertain invocation aborts without earlier-result fallback. This two-operation shape is version 0.15.0, not published v0.14.0 behavior. It does not add aggregation, repeated consumers, inspect/correct/verify workflows, or general graphs. See the repeated-stage contract.

Version 0.15.0 single-stage sequential chains

Section titled “Version 0.15.0 single-stage sequential chains”

Version 0.15.0 supports a uniquely ordered chain with one nonterminal bounded repeated operation, an optional resolved prefix and a nonempty resolved suffix. Every other operation is required Exactly(1) with no agent choices. A Pydantic request needs at least one FromRequest across the whole chain, not necessarily on the repeated operation; supported request/result/authenticated-context bindings and immutable defaults remain required. The repeated operation has positive finite bounds (minimum >= 1, maximum > 1), direct closed plain-string Literal choices and a Pydantic intermediate distinct from the response. Ordinary FromResult reads single-invocation results; repeated results require invocation="final", with at least one suffix consumer. Exactly one terminal operation owns the endpoint’s exact full response annotation. Prefix operations run before one lazy model resolution; suffix operations run after immutable stage closure without further model decisions or acknowledgment. One principal, ledger, absolute deadline and usage budget span the request. Prefix results/context/ledger are not implicitly projected into model prompts; receiving constraints are checked before each start. Explicit function-tool quotas reject before all work. No feedback cycles, external choices, multiple repeated stages, aggregation, parallelism or durable recovery are added. Published behavior before v0.15.0 remains unchanged. See the single-stage chain contract.

Version 0.15.0 prefix selection before repetition

Section titled “Version 0.15.0 prefix selection before repetition”

Version 0.15.0 also composes exactly one producer-backed choice argument on one required Exactly(1) prefix consumer with one nonterminal bounded repeated stage and a resolved suffix. The candidate producer is an earlier single-start operation returning list[ConcreteModel]; repeated choices remain direct closed string Literal values, and all other operations remain resolved required Exactly(1). Pydantic request/response, chain-wide FromRequest, unique ordering, final-invocation addressing and exact terminal response ownership remain required. Empty/unavailable/oversized candidate domains stop before selection or repeated work; a singleton binds locally, while ambiguity uses canonical request-local tokens. Singleton selection still leads to model-backed repeated decisions. One lazy model-configuration lookup, run, principal, ledger, deadline and usage budget span both phases. No business-function tools, reselection after same-run failure, effect replay or final model acknowledgment are added. A fresh request starts a fresh run. Published behavior before v0.15.0 remains unchanged. See the composed selection/stage contract. See the canonical contract.

Version 0.15.0 bounded checkpoint progression

Section titled “Version 0.15.0 bounded checkpoint progression”

Version 0.15.0 admits one explicit Required(check, calls=Between(1, M), until="accepted") with finite M >= 2. Its marker must name a stored, non-null strict Boolean field in the validated Pydantic output; until=None retains ordinary semantics. The uniquely ordered required region uses bounds 1..M; a nonempty uniquely ordered optional negative continuation uses 0..M-1. Exactly one Required(..., calls=Exactly(1)) terminal returns the exact full endpoint response and reads only final checkpoint fields. Unsupported marked shapes reject at registration. True closes; false continues, while the Mth false closes unresolved without another continuation. Empty choice domains abort with the existing redacted 409, not an unresolved result. Only one frame operation is in flight; continuation admission atomically retires that round’s authority, so closure cannot race it. Invocations use the existing shared kernel. Request, principal, ledger, usage budget and deadline are shared across rounds. No rollback, replay, durable resume or recovery is provided; effects may remain after later failure. Published behavior before v0.15.0 remains unchanged. See the source contract and examples 18–19.

  • Typed contract-first endpoints and generated HTTP/OpenAPI contracts.
  • Closed sets of application-owned callables; optional or required operations.
  • Bounded agent runtime with locally validated response models and limits.
  • A narrow, fully resolved single Exactly(1) operation path that executes without resolving or constructing a model.
  • Application-authenticated FromContext from version 0.13.0, injected from immutable server-created identity before effects, with no caller/model override. See the context contract.
  • Fixed, redacted failure responses and logs, with distinct input, operation/model output, provider, usage, application, and timeout categories.
  • One monotonic runtime deadline across preparation, execution, and finalization, checked before application effects.

The v0.10.0 deadline excludes HTTP body parsing, request validation, and response serialization. Synchronous work cannot be forcibly stopped, so an operation already running at timeout may still complete its effect. Direct execution does not fall back to a model after it starts. See failure responses and runtime deadline for status mappings and application-owned idempotency responsibilities.

Version 0.11.0 sequential chains of two or more required operations with exactly one ready operation at each step were agent-mediated. Fully resolved chains meeting the 0.12.0 boundary above run model-free. Within a chain, AgentChoice(from_result=producer) can select from a producer’s validated list[ConcreteModel] using inert projected descriptions and opaque request-local tokens (at most 128 candidates and 65,536 JSON characters per binding). The selected token maps locally to the exact canonical member; Python equality is not authority. Empty, unavailable, or oversized sets fail with a fixed redacted 409 before model decision or downstream work, without an extra model request. Primitive/untyped collections, direct unbounded choices within chains, broader bounds, and general graph execution remain unsupported. Authenticated context has a narrow implementation included from version 0.13.0, not included in version 0.12.0. Direct single-operation AgentChoice is unchanged. This behavior is included from 0.11.0; historical v0.10.0 does not include it. See the project roadmap and changelog for current boundaries.

The complete examples guide separates published behavior from version 0.15.0 lessons. Two focused checkout examples cover transport and contract details without adding a new runtime mode:

Both are current-checkout lessons, not a claim that version 0.15.0 output shorthand ships in v0.14.0. Earlier example identities and compatibility are preserved.

Summonpot is an open-source ModePot project. Join the ModePot Discord, explore the source on GitHub, or read the README.

The narrow model-free branch/join slice in version 0.15.0 accepts fully resolved required Exactly(1) acyclic graphs with a Pydantic request/response, at least one FromRequest, supported request/result/authenticated-context/default inputs, one unique sink reached by all operations, and one exact full-response producer. No AgentChoice is allowed. Operations run sequentially; ties among ready operations follow declaration order, and after expresses business ordering. This is not a public graph API, parallel scheduler, or feedback support. Existing execution paths are unchanged. Published v0.14.0 is unchanged; milestone 4 is partial, and milestone 3 remains partial. See example 20.