Skip to main content

Myli

Myli is a provider-neutral, pre-alpha Python harness for agents that propose RFC 6902 changes to application-owned JSON design documents. It never persists or applies the returned candidate to application state.

The core is independent of Pydantic, canvas formats, rendering stacks, model providers, ORMs, progress transports, and persistence systems.

Run contract

result = await myli.run(
    request=user_request,
    design=current_design,
    can_edit=True,
    capabilities={"media.transform"},
    history=history,
    on_event=handle_event,
    on_step=persist_step,
)

RunResult contains the user-facing message, optional validated candidate, changed flag, proposed patch, approved run-scoped assets, all tool outcomes, step traces, run ID, and final provider metadata. Successful and unsuccessful tool outcomes are also available as filtered properties.

Generic documents and injected models

from typing import Any

from myli import DesignSpec, ModelRequest, ModelResponse, Myli


def load_untrusted_document(value: Any) -> dict[str, Any]:
    if not isinstance(value, dict):
        raise ValueError("document must be an object")
    return value


design_spec = DesignSpec(
    name="design",
    schema={"type": "object"},
    validator=load_untrusted_document,
    serializer=lambda document: document,
    normalizer=normalize_trusted_stored_document,
    input_migrator=migrate_stored_document,
)


class ApplicationMainModel:
    async def complete(self, request: ModelRequest) -> ModelResponse:
        return await application_transport.complete(request)


myli = Myli(
    design_spec=design_spec,
    main_model=ApplicationMainModel(),
    vision_model=application_vision_model,
    renderer=application_renderer,
)

DesignSpec has deliberately separate methods for trusted stored input and untrusted model candidates. Candidate JSON is validated at runtime against the schema and must serialize back without coercion, default insertion, field dropping, or any other silent rewrite. Stored input may opt into migration and normalization before each run.

MainModel and VisionModel are public protocols. Applications may inject different transports, endpoints, credentials, or fakes. LiteLLMMainModel and LiteLLMVisionModel are optional implementations:

python -m pip install "myli[litellm]"

The LiteLLM main implementation supports structured, JSON, and text output modes, rejects options that override client-owned request fields, and translates provider failures into Myli's stable exception hierarchy.

Pydantic remains optional:

from myli.integrations.pydantic import PydanticDesignSpec

Tools, policies, and middleware

AgentTool is domain-neutral. A tool declares a JSON input schema, capabilities, per-run call budget, timeout, and result-size limit, then receives a ToolContext with the current run ID, isolated evidence, zero-based model step, run-unique step and tool-batch IDs, and its zero-based position and size within that batch:

async def execute(arguments, context):
    ...

Myli validates arguments and results, enforces capabilities and limits, and records a ToolOutcome with one of succeeded, failed, rejected, timed_out, or deferred. Calls are sequential by default. Parallel execution requires both the harness option and an explicit parallel_safe declaration on every call in the batch. Each tool can select FailureMode.RETURN_ERROR or FailureMode.RAISE.

ToolMiddleware can allow, reject, or defer work before execution and observe the outcome afterward. Applications can use it for approval, audit, tenancy, ordering, transactions, and rate limiting. The step and batch metadata lets middleware apply a rule exactly once per model work phase, including when a batch executes in parallel.

CandidatePolicy runs before rendering a proposal and before returning the final candidate. CandidateContext includes capabilities, approved assets, and all run-scoped outcomes; successful_tool_outcomes is the authorization-safe subset.

Assets and visual review

Named asset providers return generic Asset values with application-defined kinds and metadata. Myli assigns run-scoped references, detects conflicting provider identities, applies search budgets and result limits, and tracks provenance.

An asset can contain a RenderedArtifact preview or an async preview_loader. Previews load only after discovery and explicit inspection, are cached per run, and are bounded by timeout, byte-size, and media-type checks. Loading failures are returned to the model and can be retried. Binary artifacts never appear in step trace dictionaries.

When a renderer and vision model are configured, render_design validates the candidate and application policies before rendering, validates the artifact, and returns visual feedback as a tool result. A changed candidate cannot be rendered while editing is disabled, but the unchanged current design can be rendered diagnostically.

Events, traces, and limits

Events cover the run, model, tool, and validation lifecycle. They contain run and step IDs, status, safe messages, elapsed time, and an optional tool name, but no arguments or results. Applications can map them to logs, metrics, SSE, WebSockets, or ignore them.

StepTrace retains normalized model output, provider-exposed reasoning, tool calls and outcomes, validation failures, response ID, model, finish reason, usage, and latency. It provides to_dict() and redacted(). Async on_step and a configurable trace redactor make no persistence assumptions.

HarnessLimits bounds model steps, retries, renders, searches, search results, asset inspections, patches, documents, history, artifacts, operation timeouts, and optionally the entire run. Every custom tool retains its own limits.

Patch and concurrency safety

apply_json_patch implements add, remove, replace, move, copy, and test, including root operations and escaped pointers, against a deep copy. JsonPatchLimits guard operation count, patch bytes, pointer depth, value bytes, document bytes, invalid indexes, non-JSON values, non-finite numbers, child moves, and candidate expansion.

CancelledError is never wrapped. Cancellation propagates into model, renderer, vision, search, preview, and custom-tool work. All counters, assets, outcomes, and caches live in local run state, so one Myli instance can safely serve concurrent requests.

Development

uv sync
uv run ruff format --check .
uv run ruff check .
python -m unittest discover -s tests -v

Myli is distributed under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

myli-0.1.2.tar.gz (223.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

myli-0.1.2-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file myli-0.1.2.tar.gz.

File metadata

  • Download URL: myli-0.1.2.tar.gz
  • Upload date:
  • Size: 223.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for myli-0.1.2.tar.gz
Algorithm Hash digest
SHA256 cc66e933ba2eab8eff2dbed19a509509e0282a0166c50e62e03a5cad49fc4b5b
MD5 05dc0ea9bdcd7c8412a34d2b059cc278
BLAKE2b-256 230a111e044f5574a9512426820c6aeaf59dcad5645121d7180c20163f32d5f4

See more details on using hashes here.

File details

Details for the file myli-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: myli-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for myli-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 43891de09ded7e70db16b2e0fc67a90e44fad3699b8f6441a08a396ea8e651ce
MD5 1ab4f1dd7a17a93d8753453033061e90
BLAKE2b-256 4ad73a0f4e35277c136f61d1c32c27e5d1c28e596da4e2dd03871adf48618bea

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page