One endpoint contract
Typed requests, a declared goal, an explicit capability set, validation, HTTP routes, and OpenAPI stay together.
CONTRACT-FIRST PYTHON APIS
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.

GET STARTED
pip install "summonpot[serve,cli]"Quick start guide EXPLORE THE MECHANISM
Explore the shipped bounded customer lookup: request-owned data, an allowed choice, and one application-owned operation.
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.

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.

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, Fieldfrom 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:
python -m pip install 'summonpot[serve,cli]'export SUMMONPOT_MODEL=testsummonpot serve app.py --host 127.0.0.1 --port 8000The 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:
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.
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 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 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 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.
Exactly(1) operation path that executes without resolving or constructing a model.FromContext from version 0.13.0, injected from immutable server-created identity before effects, with no caller/model override. See the context contract.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.