Skip to main content

axis-core

A modular, observable AI agent framework for building production-ready agents in Python.

Python 3.10+ License: Apache 2.0 Status: Beta

Highlights

  • Lifecycle execution engine: Initialize -> Observe -> Plan -> Act -> Evaluate -> Finalize
  • Pluggable adapters for models, memory, planners, and telemetry
  • Built-in model fallback that branches on normalized provider failure reasons
  • Transcript integrity normalization for tool-call/tool-result pairing before model calls
  • Optional context-window guard with tool-result-first pruning before model calls
  • Adapter compatibility transforms for provider-specific tool schema and tool-call ID constraints
  • Tool system with @tool, schema generation, destructive-action confirmation hooks, and idempotency helpers for retry-safe side effects
  • Optional per-agent tool allow/deny policy with glob patterns and deny-overrides-allow semantics
  • Runtime policy enforcement (timeouts, retries, rate limits, cache)
  • Checkpoint/resume support for phase-boundary recovery with per-step retry-policy fidelity
  • Budget controls for cost, token, and cycle limits
  • Provider-agnostic usage normalization for budget and telemetry accounting
  • Type hints with strict mypy coverage

Installation

# Core package
pip install axis-core

# Provider and adapter extras
pip install axis-core[anthropic]
pip install axis-core[openai]
pip install axis-core[openrouter]
pip install axis-core[redis]
pip install axis-core[sqlite]
pip install axis-core[synaptic]
# installs synaptic-core>=0.3.0,<0.4.0

# Everything
pip install axis-core[full]

Requires Python 3.10+.

Development

For local test runs, use the repo wrapper so pytest runs from the project's .venv, disables third-party plugin autoload, and explicitly loads the async and coverage plugins this repo uses:

./scripts/test.sh
./scripts/test.sh tests/test_lockfile.py
./scripts/test.sh -m "not slow"

Project Init Wizard

Use the setup wizard to install optional adapter packages and write defaults to .env.

axis-core init

Useful non-interactive flags:

  • --yes: run without prompts
  • --install <bundle>: install optional adapter bundle (anthropic, openai, openrouter, redis, sqlite, synaptic)
  • --memory <name> / --planner <name>: set default adapters
  • Default scaffold memory is synaptic (session-first setup) when no --memory is provided
  • --synaptic-db-path <path>: set AXIS_SYNAPTIC_PATH when using synaptic memory
  • --bootstrap-synaptic-db: initialize Synaptic SQLite schema during setup

Environment Variable Contract

Axis tracks env vars by ownership so behavior is predictable:

  • Config-owned defaults (read from the current process environment by axis_core.config.Config): AXIS_DEFAULT_MODEL, AXIS_DEFAULT_MEMORY, AXIS_DEFAULT_PLANNER, ANTHROPIC_API_KEY, OPENAI_API_KEY, AXIS_TELEMETRY, AXIS_VERBOSE, AXIS_DEBUG
  • Run-start runtime config (resolved by axis_core.config.resolve_runtime_settings() and axis_core.config.resolve_telemetry_settings() into typed runtime settings that Agent consumes before sink instantiation): AXIS_TRANSCRIPT_STRICT, AXIS_MAX_TOOL_RESULT_CHARS, AXIS_CONTEXT_STRATEGY, AXIS_MAX_CYCLE_CONTEXT, AXIS_CONTEXT_GUARD_ENABLED, AXIS_CONTEXT_WINDOW_TOKENS, AXIS_CONTEXT_GUARD_WARN_TOKENS, AXIS_CONTEXT_GUARD_BLOCK_TOKENS, AXIS_CONTEXT_PRUNE_ENABLED, AXIS_TELEMETRY_SINK, AXIS_TELEMETRY_REDACT, AXIS_TELEMETRY_COMPACT, AXIS_TELEMETRY_FILE, AXIS_TELEMETRY_CALLBACK, AXIS_TELEMETRY_BUFFER_MODE, AXIS_TELEMETRY_BATCH_SIZE
  • Constructor-time helper env reads: AXIS_PERSIST_SENSITIVE_TOOL_DATA and AXIS_SYNAPTIC_PATH
  • Provider-SDK-owned passthrough: OPENAI_BASE_URL

See docs/reference/env-vars.md and .env.example for the full current matrix.

If you keep defaults in a .env file, call bootstrap_environment() once during application startup before reading config values or creating agents:

from axis_core import Agent, bootstrap_environment

bootstrap_environment()
agent = Agent(...)

bootstrap_environment() loads .env via python-dotenv, refreshes config-owned defaults from the current process environment, and safely returns False if python-dotenv is unavailable.

CLI Runtime

Use ask for one-shot prompts and chat for interactive sessions.

# Single prompt
axis-core ask "Summarize the latest deployment status"

# Interactive chat (resume by id)
axis-core chat --session-id ops-room

# Show which agent source/config the CLI resolves
axis-core doctor

Runtime flags are shared across ask and chat:

  • --model <name>
  • --planner <name>
  • --memory <name>
  • --system "<prompt>"
  • --from <module>:<callable_or_object>: load a project agent/factory explicitly
  • --standalone: ignore project-bound agent config and use CLI defaults/env
  • --timeout <seconds>

The session command namespace is reserved for future session-admin operations.

Project-Bound CLI (Optional)

To make axis-core ask/chat/doctor use your project's canonical agent, configure:

[tool.axis_core]
agent = "main:create_agent"

When present, the CLI loads that object/factory first. If not present, it falls back to the standalone CLI behavior (defaults from env/flags).

Quick Start

import asyncio
from axis_core import Agent, Budget, tool


@tool
def get_weather(city: str) -> str:
    return f"Weather in {city}: Sunny, 72F"


def create_agent() -> Agent:
    return Agent(
        tools=[get_weather],
        model="claude-sonnet-4-20250514",
        fallback=["gpt-4o"],
        planner="sequential",
        budget=Budget(max_cost_usd=0.50),
        system="You are a concise assistant.",
    )


async def main() -> None:
    agent = create_agent()

    result = await agent.run_async("What is the weather in Tokyo?")
    print(result.output)
    print(result.stats)


asyncio.run(main())

Streaming

for event in agent.stream("Solve 42 * 137"):
    if event.is_token:
        print(event.token, end="", flush=True)
    elif event.is_final:
        print("\nDone")

API Notes

  • output_schema on Agent.run*, Agent.stream*, and Session.run* is enforced at runtime for final output coercion/validation.
  • stream_async(..., output_schema=...) also emits structured events: structured_partial (best effort while tokens arrive) and structured_final (validated output).
  • model, planner, and memory config values accept either a registered adapter name or a protocol-conforming adapter instance; invalid objects now fail fast during resolver setup with a clear error.
  • @tool input schemas preserve list[T], dict[str, T], TypedDict, Literal[...], and nested Pydantic models inside typed containers; non-optional unions are still rejected to keep schemas deterministic and provider-safe.
  • @tool output metadata now reflects supported return annotations for manifest introspection. T | None becomes anyOf[T, null], and missing or unsupported return annotations widen to {} instead of claiming a specific type. Tool return values are not runtime-validated yet.

Transcript Guards

Axis normalizes transcript tool-call/tool-result pairing before model calls to reduce provider request failures in long-running or resumed sessions.

Optional controls:

  • AXIS_TRANSCRIPT_STRICT=true: reject unresolved tool-call/tool-result pairing instead of silently repairing/dropping invalid entries.
  • AXIS_MAX_TOOL_RESULT_CHARS=<positive-int>: cap persisted tool-result content passed to model calls.
  • AXIS_CONTEXT_GUARD_ENABLED=true: enable token-threshold checks before model calls.
  • AXIS_CONTEXT_WINDOW_TOKENS=<positive-int>: set the context-window token budget used by guard and pruning checks.
  • AXIS_CONTEXT_GUARD_WARN_TOKENS=<positive-int>: emit warning telemetry when remaining tokens drop below threshold.
  • AXIS_CONTEXT_GUARD_BLOCK_TOKENS=<positive-int>: block model calls when remaining tokens drop below threshold.
  • AXIS_CONTEXT_PRUNE_ENABLED=true: opt in to tool-result-first transcript pruning before block decisions.

Runtime precedence for model-step transcript/context settings is: step payload override -> run-start resolved runtime config -> built-in default.

Tool Idempotency

Tool execution now propagates a stable per-step idempotency key through retry attempts:

  • Available in ToolContext.idempotency_key for tools that accept ctx: ToolContext.
  • Injected into the idempotency_key argument automatically when a tool function declares it.

For in-tool dedupe, use run_idempotent(...) to memoize side-effect results by key:

from axis_core import ToolContext, tool
from axis_core.tool import run_idempotent


@tool
async def send_invoice(ctx: ToolContext, customer_id: str) -> str:
    async def send_once() -> str:
        # external side effect
        return f"invoice:{customer_id}"

    return await run_idempotent(ctx, send_once)

Tool Policy

Use ToolPolicy to allow and deny tool names per agent. Deny patterns always override allow patterns.

from axis_core import Agent, ToolPolicy

agent = Agent(
    tools=[...],
    tool_policy=ToolPolicy(
        allow=("safe_*",),
        deny=("safe_delete_*",),
    ),
)

Runtime Hardening Guarantees

  • Model fallback decisions branch on normalized failure reasons (rate_limit, timeout, connection_error, service_unavailable, transient_provider_error) so non-recoverable causes fail fast instead of attempting fallback.
  • OpenAI Chat Completions and Responses backends now share identical error normalization (reason, status_code, provider_code) so fallback/retry behavior is backend-independent.
  • OpenAI and Anthropic adapters sanitize provider-problematic tool schema fields and normalize tool-call identifiers before provider submission.
  • Usage accounting is normalized across providers (prompt/completion and input/output variants) before budget tracking.

Documentation

Supported Providers

Provider Installation Notes
Anthropic pip install axis-core[anthropic] Claude models via Anthropic adapter
OpenAI pip install axis-core[openai] Chat Completions + Responses routing
OpenRouter pip install axis-core[openrouter] OpenAI-compatible base URL path

For OpenRouter:

OPENAI_API_KEY=<openrouter-api-key>
OPENAI_BASE_URL=https://openrouter.ai/api/v1

Synaptic Interop

  • Synaptic integration is Axis-owned via axis_core.adapters.memory.synaptic.SynapticMemory.
  • Supported synaptic-core range: >=0.3.0,<0.4.0.
  • Axis uses the canonical public Synaptic 0.3 client, synaptic_core.Synaptic, for memory and session helpers.
  • The maintained first-party interop surface is Synaptic, client.session("..."), KV methods (set/get/find/delete/clear), and public session persistence methods (store_session/retrieve_session/update_session).

Session-first canonical Synaptic usage:

from synaptic_core import Synaptic

client = Synaptic(db_path="synaptic.db")
session = client.session("agent-session")

await session.remember("User asked about deployment status.")
result = await session.recall("deployment status")

await client.set("axis:last_run", {"ok": True}, namespace="axis")

Source-tree validation against a sibling synaptic-core checkout:

# from axis-core/
PYTHONPATH="$(cd ../synaptic-core/src && pwd)" \
./scripts/test.sh tests/adapters/memory/test_synaptic.py tests/adapters/memory/test_synaptic_integration.py

# from synaptic-core/
.venv/bin/python -m pytest tests/synaptic_core/test_api_v03.py tests/synaptic_core/test_core_integration.py -q

The Axis command must import from the sibling synaptic-core/src tree rather than a wheel in site-packages.

Status

v0.13.0 (Beta)

  • Beta means APIs are stabilizing, but breaking changes are still possible before 1.0.0.
  • See CHANGELOG.md for release notes.

Development

pip install -e ".[dev,anthropic,openai]"
./scripts/test.sh
ruff check axis_core tests --fix
mypy axis_core --strict

For full contributor workflow and quality gates, see CONTRIBUTING.md.

License

Apache License 2.0. See LICENSE.

Metadata

Release files for axis-core 0.13.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for axis-core 0.13.0
File Size Uploaded Provenance Actions
axis_core-0.13.0.tar.gz 274.3 kB Without provenance Details for axis_core-0.13.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for axis-core 0.13.0
File Interpreter ABI Platform Actions
axis_core-0.13.0-py3-none-any.whl Python 3 none any Details for axis_core-0.13.0-py3-none-any.whl

Total release size: 444.8 kB

Release files / axis_core-0.13.0.tar.gz

Download URL axis_core-0.13.0.tar.gz
Size 274.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b16739b1a88745c39b26a0e56fa675e5e522ae0367b2fca9a8ba9a9297812746
BLAKE2b-256 checksum
How to use checksums
2a6af18e430a109cc1054eb5f5e32d99e7ba4de2117e7996a469734a124b1a80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release files / axis_core-0.13.0-py3-none-any.whl

Download URL axis_core-0.13.0-py3-none-any.whl
Size 170.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e8c2056dfbd8bb8efaab5b4f21c8816db8c9b31fb94e59d966c98b736cea5c4
BLAKE2b-256 checksum
How to use checksums
e208bf14c362ba1c317b03d7d1674454b9d45af9d2512dbfd2ac1790f06ba553
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Security for axis-core 0.13.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page