Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- **Runnable, searchable documentation** - added complete copyable programs across core guides, expanded CLI and public API coverage, normalized page descriptions for search snippets, removed duplicate social metadata, and added regression checks for page structure and version-switcher deployment.

## [0.8.0] - 2026-07-16

### Added
Expand Down
10 changes: 0 additions & 10 deletions overrides/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@
{% if page and page.meta and page.meta.keywords %}
<meta name="keywords" content="{{ page.meta.keywords }}">
{% endif %}
<!-- Open Graph + Twitter (text). The social plugin adds og:image / twitter:image in CI. -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{ config.site_name }}">
<meta property="og:locale" content="en_US">
<meta property="og:title" content="{{ page_title }}">
<meta property="og:description" content="{{ page_desc }}">
<meta property="og:url" content="{{ page.canonical_url }}">
<meta name="twitter:title" content="{{ page_title }}">
<meta name="twitter:description" content="{{ page_desc }}">

<script type="application/ld+json">
{
"@context": "https://schema.org",
Expand Down
35 changes: 35 additions & 0 deletions scripts/check_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
API_INVENTORY_HTML = SITE_DIR / "reference" / "api-inventory" / "index.html"
RUNTIME_CONTRACT_PATH = DOCS_DIR / "reference" / "runtime-contracts.md"
DOCS_SITE_PREFIX = "https://quantlix.github.io/anycode/latest/"
DESCRIPTION_MIN_LENGTH = 150
DESCRIPTION_MAX_LENGTH = 160


def _markdown_section(markdown: str, heading: str) -> str | None:
Expand Down Expand Up @@ -139,6 +141,28 @@ def check_runtime_contract(errors: list[str]) -> None:
errors.append(f"site_docs/reference/runtime-contracts.md has stale persisted-format rows: {missing_rows}")


def _count_level_one_headings(markdown: str) -> int:
count = 0
fence_character: str | None = None
fence_length = 0
for line in markdown.splitlines():
fence = re.match(r"^\s*(`{3,}|~{3,})", line)
if fence:
marker = fence.group(1)
if fence_character is None:
fence_character = marker[0]
fence_length = len(marker)
elif marker[0] == fence_character and len(marker) >= fence_length:
fence_character = None
fence_length = 0
continue
if fence_character is None:
if re.match(r"^#\s+\S", line):
count += 1
count += len(re.findall(r"<h1(?:\s|>)", line, re.IGNORECASE))
return count


def check_frontmatter(errors: list[str]) -> None:
for path in sorted(DOCS_DIR.rglob("*.md")):
text = path.read_text(encoding="utf-8")
Expand All @@ -164,6 +188,17 @@ def check_frontmatter(errors: list[str]) -> None:
if not isinstance(value, str) or not value.strip():
errors.append(f"{path.relative_to(REPO_ROOT)} frontmatter is missing '{field}'")

description = metadata.get("description")
if isinstance(description, str) and description.strip() and not DESCRIPTION_MIN_LENGTH <= len(description) <= DESCRIPTION_MAX_LENGTH:
errors.append(
f"{path.relative_to(REPO_ROOT)} description is {len(description)} characters; "
f"expected {DESCRIPTION_MIN_LENGTH}-{DESCRIPTION_MAX_LENGTH}"
)

heading_count = _count_level_one_headings(text[closing + len("\n---\n") :])
if heading_count != 1:
errors.append(f"{path.relative_to(REPO_ROOT)} has {heading_count} level-one headings; expected exactly 1")


def _docs_source_for_url(url: str) -> Path | None:
relative_url = url.removeprefix(DOCS_SITE_PREFIX).split("#", 1)[0].split("?", 1)[0].strip("/")
Expand Down
2 changes: 1 addition & 1 deletion site_docs/concepts/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AnyCode Concepts"
description: "Understanding-oriented explanations of how AnyCode works: the runtime architecture, and how agents, teams, and task graphs fit together."
description: "Understand how AnyCode orchestrates agents, teams, tools, shared memory, dependency-aware task graphs, scheduling, and provider-neutral model calls in Python."
keywords: AnyCode concepts, architecture, agents and teams, explanation, how anycode works
---

Expand Down
2 changes: 1 addition & 1 deletion site_docs/contributing/adr-template.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Architecture Decision Record Template"
description: "Copyable AnyCode ADR template covering context, decision, state and failure semantics, compatibility, security, evidence, rollout, and rollback."
description: "Use the AnyCode ADR template to record context, decisions, state and failure semantics, compatibility, security evidence, rollout, and rollback guidance."
keywords: ADR template, AnyCode architecture template, design decision checklist
---

Expand Down
2 changes: 1 addition & 1 deletion site_docs/contributing/contract-tests.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Contract Test Conventions"
description: "Conventions for AnyCode lifecycle, persistence, protocol, backend, and provider contract suites with golden fixtures and fault injection."
description: "Write AnyCode lifecycle, persistence, protocol, backend, and provider contract tests with golden fixtures, state-machine properties, and fault injection."
keywords: AnyCode contract tests, conformance suite, golden fixtures, fault injection
---

Expand Down
2 changes: 1 addition & 1 deletion site_docs/contributing/development.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AnyCode Development Workflow for Contributors"
description: "Set up AnyCode, create focused branches, implement code and docs together, run the local quality gates, and submit a review-ready pull request."
description: "Set up AnyCode, create focused branches, implement code and docs together, run every local quality gate, and submit a pull request for maintainer review."
keywords: AnyCode contributing, Python development workflow, AnyCode pull request, uv pytest ruff pyright, topic branch
---

Expand Down
2 changes: 1 addition & 1 deletion site_docs/contributing/maintainers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "AnyCode Maintainer Governance and Change Policy"
description: "Understand AnyCode maintainer roles, branch protection, change approval, compatibility review, deprecation windows, backports, and support policy."
description: "Understand AnyCode maintainer roles, branch protection, change approval, compatibility, deprecation windows, backports, release ownership, and support policy."
keywords: AnyCode maintainers, open source governance, branch policy, semantic versioning, deprecation policy, backport policy
---

Expand Down
2 changes: 1 addition & 1 deletion site_docs/contributing/releasing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Release AnyCode to PyPI with Trusted Publishing"
description: "Prepare, validate, publish, and verify an AnyCode release through TestPyPI, GitHub Releases, PyPI Trusted Publishing, and versioned docs."
description: "Prepare, validate, publish, and verify AnyCode releases through TestPyPI, GitHub Releases, PyPI Trusted Publishing, immutable tags, and versioned docs."
keywords: AnyCode release process, PyPI Trusted Publishing, TestPyPI, semantic versioning, GitHub Release, mike versioned docs
---

Expand Down
68 changes: 67 additions & 1 deletion site_docs/guides/context-engineering.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Engineer the AnyCode Context Window as History Grows"
description: "Control AnyCode's context with a ContextPolicy: trim, mask, offload, compact, and hand off history by pressure while preserving task state and verification failures."
description: "Control AnyCode context with policies that trim, mask, offload, compact, or hand off history under pressure while preserving task and verification state."
keywords: anycode context engineering, ContextPolicy, context window, context pressure, offload_text, model context profile, token budget, compaction, context manager
---

Expand Down Expand Up @@ -100,6 +100,72 @@ Set `ContextPolicy(redact_sensitive_data=False)` or pass `redact_sensitive_data=
!!! tip "tiktoken sharpens token counts"
By default AnyCode counts tokens heuristically. Install `anycode-py[tokens]` and the OpenAI-family profiles use `tiktoken` for exact counts, which makes the pressure ladder trigger at the right moments.

## The complete, runnable program

The fragments above are pieces of one file. Here is a complete `context_engineering.py` that registers a custom huge-context model profile, runs a policy in `"auto"` mode, assembles a long history through `ContextManager`, and prints the resulting pressure, the preserved task state and open failures, and a per-section usage table. It makes no LLM calls, so it runs offline with no API key.

```python title="context_engineering.py"
from anycode import ContextManager, LLMMessage, TextBlock
from anycode.context.reporting import render_usage_report_table
from anycode.types import ContextPolicy, ModelContextProfile


def make_history(n: int) -> list[LLMMessage]:
"""Build a long conversation so the context grows under pressure."""
return [
LLMMessage(
role="user" if i % 2 == 0 else "assistant",
content=[TextBlock(text=f"turn {i}: " + ("lorem ipsum " * 80))],
)
for i in range(n)
]


def main() -> None:
# Auto mode sizes the window from a model profile instead of a fixed cap.
# Register a custom profile for any model AnyCode does not already know.
giga = ModelContextProfile(
provider="myvendor",
model="giga-1m",
max_context_tokens=1_000_000,
max_output_tokens=64_000,
)
policy = ContextPolicy(
enabled=True,
mode="auto",
keep_recent_messages=6,
max_tool_output_tokens=4_000,
custom_profiles=(giga,),
preserved_task_state={"objective": "Migrate the billing module"},
preserved_verification_failures=("pytest: 2 failing in tests/test_billing.py",),
)

# ContextManager.assemble is synchronous — it reports what the policy would do.
manager = ContextManager(policy, provider="myvendor", model="giga-1m")
prepared, manifest = manager.assemble(make_history(40))

print(f"pressure: {manifest.pressure}")
print(f"prepared messages: {len(prepared)}")
print(f"preserved state: {manifest.preserved_task_state}")
print(f"open failures: {manifest.preserved_verification_failures}")
if manifest.usage_report is not None:
print()
print(render_usage_report_table(manifest.usage_report))


if __name__ == "__main__":
main()
```

Run it from the project root:

```bash
uv run python context_engineering.py
```

!!! tip "Tested copy"
See [`examples/26_context_engineering.py`](https://github.com/Quantlix/anycode/blob/main/examples/26_context_engineering.py) for section-aware budgets on a huge-context model, plus [`examples/19_adaptive_context.py`](https://github.com/Quantlix/anycode/blob/main/examples/19_adaptive_context.py) and [`examples/23_context_pressure.py`](https://github.com/Quantlix/anycode/blob/main/examples/23_context_pressure.py) for the offload, compaction, and handoff steps of the pressure ladder in action.

## Next steps

- [Give agents memory and RAG](memory-and-rag.md) — the retrieved context this policy budgets a section for.
Expand Down
63 changes: 62 additions & 1 deletion site_docs/guides/cost-tracking.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Track and Cap LLM Spend in AnyCode"
description: "Measure token cost per agent and model with CostTracker and CostReport, set a budget_usd ceiling, and stop or warn when an AnyCode run exceeds it."
description: "Measure token cost per agent and model with CostTracker and CostReport, set a budget_usd ceiling, and safely stop or warn when an AnyCode run exceeds it."
keywords: anycode cost tracking, CostConfig, CostTracker, CostReport, budget_usd, token cost, LLM spend, calculate_cost, DEFAULT_PRICING, cost budget
---

Expand Down Expand Up @@ -78,6 +78,67 @@ report = build_cost_report(tracker)
!!! warning "Two pricing tables exist"
The cost engine (`CostConfig` / `CostTracker`) and the guardrail `BudgetTracker` use *separate* price tables that can disagree, and `calculate_cost` silently returns `0.0` for a model it doesn't recognize. Treat cost numbers as close estimates, and add `custom_pricing` for any model you rely on.

## The complete, runnable program

The cost engine is pure arithmetic over token counts, so you can exercise the whole thing without spending a cent or setting an API key. This one file records a few calls against a budget, stops the moment the ceiling is crossed, renders a `CostReport`, and shows `calculate_cost` both estimating ahead of a run and returning `0.0` for an unknown model until you supply `custom_pricing`.

```python title="cost_math.py"
from anycode import CostConfig, CostTracker, build_cost_report, calculate_cost
from anycode.types import ModelPricing, TokenUsage


def main() -> None:
# A budget-aware tracker. record() returns each call's USD cost and
# accumulates spend per agent and per model.
tracker = CostTracker(config=CostConfig(budget_usd=0.10))

calls = [
("planner", "claude-haiku-4-5", TokenUsage(input_tokens=1_200, output_tokens=300)),
("builder", "claude-haiku-4-5", TokenUsage(input_tokens=8_000, output_tokens=2_500)),
("reviewer", "claude-sonnet-4-5", TokenUsage(input_tokens=40_000, output_tokens=9_000)),
]
for agent, model, usage in calls:
cost = tracker.record(agent, model, usage)
print(f"{agent:9s} {model:20s} ${cost:.6f}")
if tracker.is_budget_exhausted():
print(f" budget of ${tracker.config.budget_usd:.2f} exhausted — stop the run here")
break

report = build_cost_report(tracker)
print("\n=== Cost report ===")
print(f"total: ${report.total_cost_usd:.6f}")
print(f"tokens in/out: {report.total_input_tokens}/{report.total_output_tokens}")
for row in report.by_agent:
print(f" {row.agent} [{row.model}] ${row.total_cost_usd:.6f} over {row.calls} call(s)")

# calculate_cost is a pure function — handy for estimating before you run.
estimate = calculate_cost(TokenUsage(input_tokens=100_000, output_tokens=20_000), "claude-haiku-4-5")
print(f"\nestimate for 100k in / 20k out on claude-haiku-4-5: ${estimate:.4f}")

# An unknown model bills as $0.00 until you supply custom_pricing.
unknown = TokenUsage(input_tokens=1_000, output_tokens=1_000)
print(f"unknown model, default table: ${calculate_cost(unknown, 'my-model'):.4f}")
priced = calculate_cost(
unknown,
"my-model",
[ModelPricing(model="my-model", provider="myvendor", input_cost_per_1k=0.001, output_cost_per_1k=0.004)],
)
print(f"unknown model, custom pricing: ${priced:.4f}")


if __name__ == "__main__":
main()
```

Run it from the project root:

```bash
uv run python cost_math.py
```

!!! tip "Tested copy"
See [`examples/13_cost_tracking.py`](https://github.com/Quantlix/anycode/blob/main/examples/13_cost_tracking.py) for the CI-tested version, which attaches the same `CostConfig` to a live two-agent team and reads the `CostReport` off the `TeamRunResult`.

## Next steps

- [Route tasks by complexity](routing.md) — send cheap tasks to cheap models and measure the savings here.
Expand Down
93 changes: 92 additions & 1 deletion site_docs/guides/durability-backends.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Configure AnyCode Durability Backends"
description: Choose and configure AnyCode in-memory, SQLite, or Dapr durability backends for runs, events, leases, checkpoints, wakes, and signals.
description: Choose AnyCode in-memory, SQLite, or Dapr durability backends and configure guarantees for runs, events, leases, checkpoints, wakes, migrations, and signals.
keywords: AnyCode durability backend, SQLite agent state, Dapr agent persistence, AI agent leases, durable agent runs
---

Expand Down Expand Up @@ -135,6 +135,97 @@ The credential-free examples provide a smaller starting point:
- `examples/39_backend_failure_soak.py` exercises partitions and ambiguous post-commit failures.
- `tests/test_backend_conformance.py` is the reusable behavioral contract.

## The complete, runnable program

This one file runs the full backend workload end to end: admit a run, enqueue and claim its work under a lease, commit a state transition against the expected event sequence, save a checkpoint, register a timed wake, deliver an external signal, then export the portable snapshot. It uses `InMemoryDurabilityBackend`, so it needs no external services or extra dependencies. Swap the one backend line for `SQLiteDurabilityBackend(".anycode/backend.db")` (needs the `[persistence]` extra) or a configured `DaprDurabilityBackend` — the workload below does not change, which is the point of the contract.

```python title="durability_backend.py"
import asyncio
from datetime import UTC, datetime, timedelta

from anycode import (
Admission,
Checkpoint,
Event,
InMemoryDurabilityBackend,
Run,
WorkItem,
transition_run,
uuid7,
)
from anycode.backends import ExternalSignal, WakeRegistration

LEASE_SECONDS = 30.0


async def main() -> None:
backend = InMemoryDurabilityBackend()

caps = backend.capabilities()
print(f"backend={caps.backend} persistent={caps.persistent} external={caps.external}")
health = await backend.health()
print(f"health: {health.status}")

run_id = str(uuid7())
now = datetime.now(UTC)

# 1. Admit the run with its first event. The admission key is idempotent.
run = Run(id=run_id, correlation_id=run_id, created_at=now, updated_at=now)
initial = Event(id=str(uuid7()), run_id=run_id, sequence=1, type="run.accepted", correlation_id=run_id, emitted_at=now)
admitted = await backend.admit(Admission(admission_key=f"example:{run_id}", run=run, initial_event=initial))
if not admitted.admitted or admitted.run is None:
raise RuntimeError(admitted.error.message if admitted.error else "admission failed")

# 2. Enqueue ready work and claim it under a lease.
await backend.enqueue(WorkItem(id=str(uuid7()), run_id=run_id, task_id="export-task", available_at=now))
claimed = await backend.claim("worker-1", lease_seconds=LEASE_SECONDS)
if claimed.claim is None:
raise RuntimeError("ready work could not be claimed")

# 3. Commit a state transition against the expected event sequence.
queued = transition_run(admitted.run, "queued", now=now)
if queued.run is None or queued.event is None:
raise RuntimeError(queued.error.message if queued.error else "run transition failed")
committed = await backend.commit(claimed.claim, queued.event, expected_sequence=1, run=queued.run)
if not committed.accepted:
raise RuntimeError(committed.error.message if committed.error else "commit failed")

# 4. Save a checkpoint, register a timed wake, and deliver an external signal.
checkpoint = Checkpoint(
id=str(uuid7()),
run_id=run_id,
event_cursor=2,
generation=queued.run.generation,
attempt=queued.run.attempt,
correlation_id=run_id,
run=queued.run,
)
await backend.save_checkpoint(checkpoint)
await backend.register_wake(
WakeRegistration(id=str(uuid7()), run_id=run_id, wake_at=now + timedelta(minutes=5), reason="scheduled follow-up")
)
await backend.deliver_signal(ExternalSignal(id=str(uuid7()), run_id=run_id, name="operator-note", payload="continue"))

# 5. Export the portable snapshot — the same shape every backend produces.
snapshot = await backend.export_run(run_id)
assert snapshot is not None
print(f"exported run={snapshot.run.id} state={snapshot.run.state}")
print(f"events={len(snapshot.events)} wakes={len(snapshot.wakes)} signals={len(snapshot.signals)} checkpoint={snapshot.checkpoint is not None}")


if __name__ == "__main__":
asyncio.run(main())
```

Run it from the project root:

```bash
uv run python durability_backend.py
```

!!! tip "Tested copy"
See [`examples/38_pluggable_durability.py`](https://github.com/Quantlix/anycode/blob/main/examples/38_pluggable_durability.py) for the CI-tested version, which runs the same workload against SQLite or a Dapr state store, and [`examples/39_backend_failure_soak.py`](https://github.com/Quantlix/anycode/blob/main/examples/39_backend_failure_soak.py) for the injected-failure soak that proves an ambiguous commit never duplicates an event.

## Next steps

- [Propagate execution identity and policy](execution-identity.md)
Expand Down
Loading