All workWork /Fifty Agent SDK

Python agent runtime

Fifty Agent SDK

Your application. Not another agent loop.

build the tools and policy your application needs. reuse the runtime that connects model calls, tool execution and conversation history.

exploded metal assembly on a bone ground, a concept for separable runtime boundaries.
Concept visual · AI / GPT Image 2

concept artwork, not hardware or literal SDK architecture.

for Python builders making tool-using assistants, application-controlled tool calls or conversations that need more than one path.

Build the application. Reuse the runtime.

model requests, response parsing, tool dispatch and history recur across agent applications. Fifty Agent SDK factors that work into a composable runtime, so the application can concentrate on what its tools do and when they should run. the loop handles model and tool interaction; the runner connects that loop to conversation history. bring the client, tools and integration decisions that fit your application.

Bring your tools.

turn annotated async Python functions into typed local tools. connect remote tools through MCP, the Model Context Protocol, and invoke them through the same name-based surface. keep application functions close to the code that owns them while giving the loop a common way to call them. the selection below shows the local function boundary; use the full quickstart for imports and registry setup.

Typed tool selection

@tool()
async def get_weather(city: str) -> dict[str, Any]:
    """Return the current weather for a city."""
    return {"city": city, "temp_c": 21}

the weather value is a fixed fixture, not a network weather result.

exact contiguous README selection; imports and registry setup omitted. requires Any and tool. not executed here.

the weather example returns a fixed fixture, not a fetched weather result. remote transport remains a separate integration concern.

Choose the calling contract.

choose how the model asks for a tool: JSON for structured text, PROSE for labelled text, or NATIVE for provider tool calls. each mode coordinates parsing, prompts, declarations and result roles rather than asking the application to pair those pieces by hand. the unchanged selection below chooses JSON. select the mode alongside your client and request settings; endpoint compatibility is part of that integration.

plain-text terminal token replay requires PROSE. NATIVE rejects stream=True; replay is not live intermediate reasoning.

JSON loop selection

    loop = AgentLoop(
        llm=llm,
        registry=registry,
        prompts=PromptSections(persona="You are helpful."),
        safety=SafetyConfig(),
        model="gpt-4o",
        tool_mode=ToolMode.JSON,
    )

ToolMode.JSON supplies the coordinated text calling protocol.

exact contiguous README selection; source indentation retained. configured llm, registry, imports and surrounding async setup omitted. gpt-4o is the unchanged example token, not a new provider claim. not executed here.

Keep policy at dispatch.

an intervention is an application decision around a tool call. before dispatch, inspect the proposed call, deny it or replace its arguments. after a ToolResult, add a model-facing note to help the next step interpret the result. put these decisions at the invocation boundary instead of relying on task instructions to carry application policy.

these seams do not rename tools or replace output. they are not authorization infrastructure or a security sandbox.

  1. Before tool
  2. Tool invocation
  3. After tool

Load the instructions the task needs.

offer a catalog of skill names and descriptions instead of placing every instruction body in the opening prompt. load_skill brings a selected skill into the loop when needed. a skill is a set of trusted task instructions, useful for keeping procedures separate from the application functions that perform the work. its private loader registry sits beside the caller registry, without adding tools to it.

skills are not executable plugins or permissions. the selection keeps its literal ellipsis and omits existing loop setup; it is not a standalone program.

On-demand skills selection

from fifty_agent_sdk import AgentLoop, SkillSet, ToolMode, discover_skills

skills = SkillSet(
    discover_skills("skills")
)  # every skills/<name>/SKILL.md; hidden (dot) directories are skipped

loop = AgentLoop(
    ...,
    tool_mode=ToolMode.NATIVE,
    skills=skills,
)

skills= belongs to AgentLoop; its private loader does not mutate the caller registry.

exact contiguous README integration sketch. the literal ellipsis omits existing loop setup; this is not a standalone program or an execution recorded here.

Give the conversation another path.

keep conversation history behind one store contract, with branching operations available through Memory, SQL or Redis. the runner drives the loop and reads and writes the supplied store; the application coordinates concurrent turns and branch changes. read those relationships in the visible text architecture beside the historical core figure. the current companion explains calling modes, the private skill loader and intervention seams beyond that earlier slice.

Memory is process-local and non-durable. SQL needs optional dependencies, a driver and provisioned schema; Redis needs a service and retention policy. conversation history is not a resumable execution checkpoint.

Runtime boundaries

consumer app calls AgentRunner, which drives AgentLoop and reads/writes StateStore. AgentLoop calls model/client, Parser and a local/MCP tool registry.

// FIG.01 · FIFTY AGENT SDK · HISTORICAL CORE SLICE

simplified core boundaries from the earlier 1.5.0 source review, not exhaustive 1.12.0 architecture or a timing trace.

generated architecture illustration based on historical pinned source, not a runtime capture.

for small-screen reading, open the full-resolution figure or use the text equivalent.

Read the architecture as text

  1. consumer app → AgentRunner: invokes run.
  2. AgentRunner → AgentLoop: drives execution.
  3. AgentLoop → model/client: requests completion.
  4. AgentLoop → Parser: interprets response.
  5. AgentLoop → tool registry: invokes named local and MCP tools.
  6. AgentRunner ↔ StateStore: loads and persists conversation history.

What the core slice leaves out.

calling mode coordinates parsing, prompt format, declarations and result roles. JSON and PROSE use text protocols; NATIVE uses structured provider calls.

a skill catalog enters the loop prompt. load_skill dispatches through a separate private registry beside the caller registry; skills= never adds it to the caller registry.

before-tool intervention can deny or replace arguments around dispatch; after-tool intervention can add a model-facing note when a ToolResult exists. these seams surround invocation, not the state store.

the runner manages conversation history. typed events return to the caller; optional audits and hooks are outside this simplified image and remain best effort.

Get started.

install the package, then follow the pinned README quickstart to connect a configured client, register tools and run a turn. start with the calling mode and history backend your application needs.

Install with pip

pip install fifty-agent-sdk

Python >=3.11

PyPI 1.12.0 · MIT

the command is the upstream installation instruction, not a recorded run. the full quickstart supplies the integration setup omitted from these selections.

Source and scope.

pinned sources, full implementation conditions and dated verification receipts. source selections are not recorded runs.

Release identity

PyPI records fifty-agent-sdk 1.12.0, published 2026-10-16. its source tag resolves to 6ceb9c6072aa7101eda959fb1948feb5b46d8506. GitHub’s latest formal Release in the captured review remains v1.5.0; the 1.12.0 tag is not a formal Release.

all 50 package payload files match by path and bytes across the downloaded wheel, sdist and pinned source: 49 Python files and py.typed. this does not establish whole-archive, metadata, dependency or build equivalence.

Implementation boundaries

calling modes coordinate the protocol. text tool descriptions snapshot at loop construction; native declarations refresh each iteration. for plain-text terminal token replay, use PROSE; stream=True is rejected with NATIVE. terminal deltas replay after parsing, not as live intermediate reasoning.

format cleanup is not the same as a model repair retry. the default text-format repair budget is one retry per iteration; explicit NATIVE can retry empty final text with its own reminder. malformed structured native calls do not use that text repair path. the require-tool reconsideration budget starts fresh per run; even a skill read can satisfy it, not prove external grounding.

observational hooks are not intervention return values. on_llm_call receives the response before native tool-call parsing, but this does not promise that every mutation to response content is reparsed as final text. use the documented before-tool and after-tool seams rather than treating observation hooks as a general rewrite API.

before_tool can deny a call or replace its arguments, not rename the tool. the default failure fallback denies; opt-in ALLOW runs the original call. after_tool adds a note only when a ToolResult exists, including error results, not for denial, unknown tools or timeouts. it cannot replace output or events; failures skip the note and cancellation propagates. this is not authorization infrastructure.

skills= adds a catalog to the loop prompt and a private skill-tool registry beside the caller registry. bodies and catalog are snapshots; reference files are read on demand. rebuild the SkillSet and loop to refresh. loaded bodies and tool roundtrips are not persisted across turns.

skills are trusted instructions, not executable plugins or a permission sandbox. frontmatter uses a strict YAML subset, not arbitrary YAML parity; allowed-tools is not enforced. file access is constrained, but POSIX no-follow behavior is not a measured Windows containment guarantee.

conversation history is not a resumable in-flight checkpoint. Memory is process-local and non-durable, with a lazy 3600-second inactivity expiry and 1000-session LRU cap by default; both can be disabled. active or queued operations can temporarily exceed the cap. SQL needs its optional dependencies, an async driver and application-provisioned schema; Redis needs a service and retention policy.

runner.state is the exact supplied store and a read-only accessor, not a wrapper. changing stores requires another runner. whole concurrent turns and branch changes remain application responsibilities. branch-tail truncation does not erase inherited history; iterative lineage traversal avoids recursive stack growth, not history materialization costs.

lifecycle events are not private thoughts. ToolProgressEvent remains reserved, not emitted. seeing a final event is not a persistence receipt; normal exhaustion, early close, errors and cancellation matter. optional audits and hooks are best effort, not complete forensic records. per-key argument metadata is not comprehensive redaction.

iteration caps and per-tool timeouts constrain specific work, not total wall-clock time. cap and parser/model-error fallback answers are separate. shipped parsers and client check argument nesting; custom implementations can differ. transport failures, cancellation and application concurrency still need handling. no claim that every failure produces a resilient completion.

MCP uses Streamable HTTP. refresh is manual or opt-in background work and does not unregister removed upstream tools. rebuild a text-mode loop when prompt-described tools change. per-result isError screening controls model-facing error text, not transport failures or security guarantees.

the model adapter targets an OpenAI-compatible chat-completions contract, not every provider endpoint or setting combination. application-selected clients, request settings and error handling remain part of integration.

Examples and verification

source selections, not recorded runs. imports and surrounding setup are omitted where noted. weather is fixed fixture data. the tag provides README examples and a test example, not a standalone examples directory; sketches with ellipses are incomplete programs.

release-source CI: 2,725 passed / 2 skipped per Python 3.11–3.14 job on 2026-10-16, run 38891183299 at 6ceb9c6072aa7101eda959fb1948feb5b46d8506. counts are per job, not summed unique tests. lint, format and strict mypy also succeeded.

historical GitHub Actions results, not fresh local SDK tests. the workflow tests editable source with extras, not the uploaded wheel. live PostgreSQL and Redis modules skipped without services; SQLite and fakeredis coverage do not establish live-service integration. matching package payloads do not turn source CI into a wheel test.

the earlier page’s 1.5.0 source review and CI run 30555126118 remain historical provenance: 745 passed / 2 skipped per job on 2026-07-30, not the current package receipt or proof of the original wheel’s metadata. the old native-batch single-slot correlation limitation was fixed and is not retained as a current limitation.

Unfinished/experimental work

this review inspects public source and distribution evidence. it does not establish production adoption, private-project integration, performance gains, a security audit or live backend acceptance. the generated hero is a concept; the retained architecture figure is a historical core slice, not an exhaustive current architecture.

Bring the application.

keep the tools and decisions that make it yours. build around a reusable runtime.

FIFTY.DEV●WORK · AGENT SDK●PYPI●v1.12.0●FIFTY.DEV●WORK · AGENT SDK●PYPI●v1.12.0●