Tools for developing and optimizing side effect free background agents
Project description
Weak Incentives
Lean, typed building blocks for side-effect-free background agents. Compose deterministic prompts, run typed tools, and parse strict JSON replies without heavy dependencies. Optional adapters snap in when you need a model provider.
Highlights
- Namespaced prompt trees with deterministic Markdown renders, placeholder verification, and tool-aware versioning metadata.
- Stdlib-only dataclass serde (
parse,dump,clone,schema) keeps request and response types honest end-to-end. - Session state container and event bus collect prompt and tool telemetry for downstream automation.
- Built-in planning and virtual filesystem tool suites give agents durable plans and sandboxed edits backed by reducers and selectors.
- Optional OpenAI and LiteLLM adapters integrate structured output parsing, tool orchestration, and telemetry hooks.
Requirements
- Python 3.12+ (the repository pins 3.14 in
.python-versionfor development) uvCLI
Install
uv add weakincentives
# optional provider adapters
uv add "weakincentives[openai]"
uv add "weakincentives[litellm]"
# cloning the repo? use: uv sync --extra openai --extra litellm
Quickstart
from dataclasses import dataclass
from weakincentives import (
MarkdownSection,
Prompt,
Tool,
ToolResult,
parse_structured_output,
)
@dataclass
class ResearchGuidance:
topic: str
@dataclass
class SourceLookup:
source_id: str
@dataclass
class SourceDetails:
source_id: str
title: str
@dataclass
class ResearchSummary:
summary: str
citations: list[str]
def lookup_source(params: SourceLookup) -> ToolResult[SourceDetails]:
details = SourceDetails(source_id=params.source_id, title="Ada Lovelace Archive")
return ToolResult(message=f"Loaded {details.title}", value=details)
catalog_tool = Tool[SourceLookup, SourceDetails](
name="catalog_lookup",
description="Look up a primary source identifier and return details.",
handler=lookup_source,
)
task_section = MarkdownSection[ResearchGuidance](
title="Task",
template=(
"Research ${topic}. Use `catalog_lookup` for citations and reply with a "
"JSON summary."
),
key="research.task",
tools=[catalog_tool],
)
prompt = Prompt[ResearchSummary](
ns="examples/research",
key="research.run",
name="research_prompt",
sections=[task_section],
)
rendered = prompt.render(ResearchGuidance(topic="Ada Lovelace"))
print(rendered.text)
print([tool.name for tool in rendered.tools])
reply = """```json
{
"summary": "Ada Lovelace pioneered computing...",
"citations": ["catalog_lookup:ada-archive"]
}
```"""
result = parse_structured_output(reply, rendered)
print(result.summary)
print(result.citations)
The rendered prompt text stays deterministic, tool metadata travels with the prompt,
and parse_structured_output enforces your dataclass contract.
Sessions and Built-in Tools
Session state turns prompt output and tool calls into durable data. Built-in planning and virtual filesystem sections register reducers on the provided session.
from weakincentives.session import Session, select_latest
from weakincentives.tools import (
PlanningToolsSection,
Plan,
VfsToolsSection,
VirtualFileSystem,
)
session = Session()
planning_section = PlanningToolsSection(session=session)
vfs_section = VfsToolsSection(session=session)
prompt = Prompt[ResearchSummary](
ns="examples/research",
key="research.session",
sections=[task_section, planning_section, vfs_section],
)
active_plan = select_latest(session, Plan)
vfs_snapshot = select_latest(session, VirtualFileSystem)
Use session.select_all(...) or the helpers in weakincentives.session to drive UI
state, persistence, or audits after each adapter run.
Adapter Integrations
Adapters stay optional and only load their dependencies when you import them.
from weakincentives.adapters.openai import OpenAIAdapter
from weakincentives.events import InProcessEventBus
from weakincentives.session import Session
from weakincentives.tools import Plan
bus = InProcessEventBus()
session = Session(bus=bus)
adapter = OpenAIAdapter(
model="gpt-4o-mini",
client_kwargs={"api_key": "sk-..."},
)
response = adapter.evaluate(
prompt,
ResearchGuidance(topic="Ada Lovelace"),
bus=bus,
)
plan_history = session.select_all(Plan)
InProcessEventBus publishes ToolInvoked and PromptExecuted events for the
session (or any other subscriber) to consume.
Development Setup
-
Install Python 3.14 (for example with
pyenv install 3.14.0). -
Install
uv, then bootstrap the environment and hooks:uv sync ./install-hooks.sh -
Run checks with
uv runso everything shares the managed virtualenv:make format/make format-checkmake lint/make lint-fixmake typecheck(Ty + Pyright, warnings fail the build)make test(pytest viabuild/run_pytest.py, 100% coverage enforced)make check(aggregates the quiet checks above plus Bandit, Deptry, pip-audit, and markdown linting)
Documentation
AGENTS.md— operational handbook and contributor workflow.specs/— design docs for prompts, planning tools, and adapters.ROADMAP.md— upcoming feature sketches.docs/api/— API reference material.
License
Apache 2.0 • Status: Alpha (APIs may change between releases)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file weakincentives-0.3.0.tar.gz.
File metadata
- Download URL: weakincentives-0.3.0.tar.gz
- Upload date:
- Size: 1.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1219ffb6f4dce75b0c5f2d1308cf8f931bb5e095cbde2a78232a99131f7f3d13
|
|
| MD5 |
1a43068480bf22c5c1494d9fe2700f25
|
|
| BLAKE2b-256 |
0db64b2c53e09125e9590bca4d15f65895ebcce8e7d276aab46a20e2daf3b914
|
Provenance
The following attestation bundles were made for weakincentives-0.3.0.tar.gz:
Publisher:
release.yml on weakincentives/weakincentives
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weakincentives-0.3.0.tar.gz -
Subject digest:
1219ffb6f4dce75b0c5f2d1308cf8f931bb5e095cbde2a78232a99131f7f3d13 - Sigstore transparency entry: 660405372
- Sigstore integration time:
-
Permalink:
weakincentives/weakincentives@c14f6ae341e4b168368e67732f5c9f54a67589d6 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/weakincentives
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c14f6ae341e4b168368e67732f5c9f54a67589d6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file weakincentives-0.3.0-py3-none-any.whl.
File metadata
- Download URL: weakincentives-0.3.0-py3-none-any.whl
- Upload date:
- Size: 72.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c59387dd42e48a21acf3bbeaa4d27775b3ec622998095c28e80b92e36479424
|
|
| MD5 |
91a9860f5e7b297ad14180e3733349f8
|
|
| BLAKE2b-256 |
567ce40fe285d3801b67fa615b702ea97d54067595fe87bcf34864e47ecef9fd
|
Provenance
The following attestation bundles were made for weakincentives-0.3.0-py3-none-any.whl:
Publisher:
release.yml on weakincentives/weakincentives
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weakincentives-0.3.0-py3-none-any.whl -
Subject digest:
6c59387dd42e48a21acf3bbeaa4d27775b3ec622998095c28e80b92e36479424 - Sigstore transparency entry: 660405376
- Sigstore integration time:
-
Permalink:
weakincentives/weakincentives@c14f6ae341e4b168368e67732f5c9f54a67589d6 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/weakincentives
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c14f6ae341e4b168368e67732f5c9f54a67589d6 -
Trigger Event:
release
-
Statement type: