myli 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- myli-0.1.2/.github/workflows/release.yml +104 -0
- myli-0.1.2/.gitignore +11 -0
- myli-0.1.2/.readthedocs.yaml +17 -0
- myli-0.1.2/LICENSE +21 -0
- myli-0.1.2/PKG-INFO +198 -0
- myli-0.1.2/README.md +175 -0
- myli-0.1.2/docs/requirements.in +6 -0
- myli-0.1.2/docs/requirements.txt +86 -0
- myli-0.1.2/docs/source/api.md +134 -0
- myli-0.1.2/docs/source/architecture.md +62 -0
- myli-0.1.2/docs/source/changelog.md +54 -0
- myli-0.1.2/docs/source/conf.py +36 -0
- myli-0.1.2/docs/source/contributing.md +45 -0
- myli-0.1.2/docs/source/getting-started.md +114 -0
- myli-0.1.2/docs/source/index.md +43 -0
- myli-0.1.2/docs/source/integrations.md +228 -0
- myli-0.1.2/docs/source/safety.md +92 -0
- myli-0.1.2/docs/source/troubleshooting.md +81 -0
- myli-0.1.2/pyproject.toml +81 -0
- myli-0.1.2/src/myli/__init__.py +116 -0
- myli-0.1.2/src/myli/_json.py +121 -0
- myli-0.1.2/src/myli/_models.py +451 -0
- myli-0.1.2/src/myli/adapters.py +35 -0
- myli-0.1.2/src/myli/contracts.py +647 -0
- myli-0.1.2/src/myli/errors.py +49 -0
- myli-0.1.2/src/myli/harness.py +2099 -0
- myli-0.1.2/src/myli/integrations/__init__.py +1 -0
- myli-0.1.2/src/myli/integrations/litellm.py +6 -0
- myli-0.1.2/src/myli/integrations/pydantic.py +57 -0
- myli-0.1.2/src/myli/json_patch.py +282 -0
- myli-0.1.2/tests/test_harness.py +1065 -0
- myli-0.1.2/tests/test_json_patch.py +102 -0
- myli-0.1.2/tests/test_models.py +366 -0
- myli-0.1.2/tests/test_package.py +55 -0
- myli-0.1.2/tests/test_work_phase_metadata.py +194 -0
- myli-0.1.2/uv.lock +2220 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: release-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: false
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
name: Build and release
|
|
15
|
+
runs-on: ubuntu-24.04
|
|
16
|
+
timeout-minutes: 15
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Check out the release tag
|
|
23
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
29
|
+
with:
|
|
30
|
+
version: "0.11.28"
|
|
31
|
+
enable-cache: true
|
|
32
|
+
cache-dependency-glob: uv.lock
|
|
33
|
+
|
|
34
|
+
- name: Install Python
|
|
35
|
+
run: uv python install 3.14
|
|
36
|
+
|
|
37
|
+
- name: Install the project
|
|
38
|
+
run: uv sync --locked --python 3.14
|
|
39
|
+
|
|
40
|
+
- name: Verify tag matches package version
|
|
41
|
+
run: |
|
|
42
|
+
package_version="$(uv run python -c 'import myli; print(myli.__version__)')"
|
|
43
|
+
if [[ "${GITHUB_REF_NAME}" != "v${package_version}" ]]; then
|
|
44
|
+
echo "Release tag ${GITHUB_REF_NAME} does not match package version ${package_version}." >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
- name: Check formatting
|
|
49
|
+
run: uv run ruff format --check src tests
|
|
50
|
+
|
|
51
|
+
- name: Lint
|
|
52
|
+
run: uv run ruff check .
|
|
53
|
+
|
|
54
|
+
- name: Test
|
|
55
|
+
run: uv run python -m unittest discover -s tests -v
|
|
56
|
+
|
|
57
|
+
- name: Build distributions
|
|
58
|
+
run: uv build
|
|
59
|
+
|
|
60
|
+
- name: Upload distributions
|
|
61
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
62
|
+
with:
|
|
63
|
+
name: dist
|
|
64
|
+
path: dist/
|
|
65
|
+
|
|
66
|
+
- name: Create GitHub release
|
|
67
|
+
env:
|
|
68
|
+
GH_TOKEN: ${{ github.token }}
|
|
69
|
+
run: |
|
|
70
|
+
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
|
|
71
|
+
gh release upload "${GITHUB_REF_NAME}" dist/* --clobber
|
|
72
|
+
else
|
|
73
|
+
gh release create "${GITHUB_REF_NAME}" dist/* \
|
|
74
|
+
--generate-notes \
|
|
75
|
+
--title "Myli ${GITHUB_REF_NAME}" \
|
|
76
|
+
--verify-tag
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
pypi:
|
|
80
|
+
name: Publish to PyPI
|
|
81
|
+
needs: release
|
|
82
|
+
runs-on: ubuntu-24.04
|
|
83
|
+
|
|
84
|
+
environment:
|
|
85
|
+
name: pypi
|
|
86
|
+
|
|
87
|
+
permissions:
|
|
88
|
+
id-token: write
|
|
89
|
+
|
|
90
|
+
steps:
|
|
91
|
+
- name: Install uv
|
|
92
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
93
|
+
with:
|
|
94
|
+
version: "0.11.28"
|
|
95
|
+
enable-cache: false
|
|
96
|
+
|
|
97
|
+
- name: Download distributions
|
|
98
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
99
|
+
with:
|
|
100
|
+
name: dist
|
|
101
|
+
path: dist/
|
|
102
|
+
|
|
103
|
+
- name: Publish to PyPI
|
|
104
|
+
run: uv publish
|
myli-0.1.2/.gitignore
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
os: ubuntu-24.04
|
|
5
|
+
tools:
|
|
6
|
+
python: "3.12"
|
|
7
|
+
|
|
8
|
+
sphinx:
|
|
9
|
+
configuration: docs/source/conf.py
|
|
10
|
+
builder: dirhtml
|
|
11
|
+
fail_on_warning: true
|
|
12
|
+
|
|
13
|
+
python:
|
|
14
|
+
install:
|
|
15
|
+
- requirements: docs/requirements.txt
|
|
16
|
+
- method: pip
|
|
17
|
+
path: .
|
myli-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Myli contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
myli-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: myli
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A provider-neutral harness for structured visual-design agents
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: agents,asset-search,design,vision
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: jsonschema>=4.18.0
|
|
18
|
+
Provides-Extra: litellm
|
|
19
|
+
Requires-Dist: litellm>=1.40.0; extra == 'litellm'
|
|
20
|
+
Provides-Extra: pydantic
|
|
21
|
+
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Myli
|
|
25
|
+
|
|
26
|
+
Myli is a provider-neutral, pre-alpha Python harness for agents that propose
|
|
27
|
+
RFC 6902 changes to application-owned JSON design documents. It never persists
|
|
28
|
+
or applies the returned candidate to application state.
|
|
29
|
+
|
|
30
|
+
The core is independent of Pydantic, canvas formats, rendering stacks, model
|
|
31
|
+
providers, ORMs, progress transports, and persistence systems.
|
|
32
|
+
|
|
33
|
+
## Run contract
|
|
34
|
+
|
|
35
|
+
~~~python
|
|
36
|
+
result = await myli.run(
|
|
37
|
+
request=user_request,
|
|
38
|
+
design=current_design,
|
|
39
|
+
can_edit=True,
|
|
40
|
+
capabilities={"media.transform"},
|
|
41
|
+
history=history,
|
|
42
|
+
on_event=handle_event,
|
|
43
|
+
on_step=persist_step,
|
|
44
|
+
)
|
|
45
|
+
~~~
|
|
46
|
+
|
|
47
|
+
RunResult contains the user-facing message, optional validated candidate,
|
|
48
|
+
changed flag, proposed patch, approved run-scoped assets, all tool outcomes,
|
|
49
|
+
step traces, run ID, and final provider metadata. Successful and unsuccessful
|
|
50
|
+
tool outcomes are also available as filtered properties.
|
|
51
|
+
|
|
52
|
+
## Generic documents and injected models
|
|
53
|
+
|
|
54
|
+
~~~python
|
|
55
|
+
from typing import Any
|
|
56
|
+
|
|
57
|
+
from myli import DesignSpec, ModelRequest, ModelResponse, Myli
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_untrusted_document(value: Any) -> dict[str, Any]:
|
|
61
|
+
if not isinstance(value, dict):
|
|
62
|
+
raise ValueError("document must be an object")
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
design_spec = DesignSpec(
|
|
67
|
+
name="design",
|
|
68
|
+
schema={"type": "object"},
|
|
69
|
+
validator=load_untrusted_document,
|
|
70
|
+
serializer=lambda document: document,
|
|
71
|
+
normalizer=normalize_trusted_stored_document,
|
|
72
|
+
input_migrator=migrate_stored_document,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ApplicationMainModel:
|
|
77
|
+
async def complete(self, request: ModelRequest) -> ModelResponse:
|
|
78
|
+
return await application_transport.complete(request)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
myli = Myli(
|
|
82
|
+
design_spec=design_spec,
|
|
83
|
+
main_model=ApplicationMainModel(),
|
|
84
|
+
vision_model=application_vision_model,
|
|
85
|
+
renderer=application_renderer,
|
|
86
|
+
)
|
|
87
|
+
~~~
|
|
88
|
+
|
|
89
|
+
DesignSpec has deliberately separate methods for trusted stored input and
|
|
90
|
+
untrusted model candidates. Candidate JSON is validated at runtime against the
|
|
91
|
+
schema and must serialize back without coercion, default insertion, field
|
|
92
|
+
dropping, or any other silent rewrite. Stored input may opt into migration and
|
|
93
|
+
normalization before each run.
|
|
94
|
+
|
|
95
|
+
MainModel and VisionModel are public protocols. Applications may inject
|
|
96
|
+
different transports, endpoints, credentials, or fakes. LiteLLMMainModel and
|
|
97
|
+
LiteLLMVisionModel are optional implementations:
|
|
98
|
+
|
|
99
|
+
~~~bash
|
|
100
|
+
python -m pip install "myli[litellm]"
|
|
101
|
+
~~~
|
|
102
|
+
|
|
103
|
+
The LiteLLM main implementation supports structured, JSON, and text output
|
|
104
|
+
modes, rejects options that override client-owned request fields, and translates
|
|
105
|
+
provider failures into Myli's stable exception hierarchy.
|
|
106
|
+
|
|
107
|
+
Pydantic remains optional:
|
|
108
|
+
|
|
109
|
+
~~~python
|
|
110
|
+
from myli.integrations.pydantic import PydanticDesignSpec
|
|
111
|
+
~~~
|
|
112
|
+
|
|
113
|
+
## Tools, policies, and middleware
|
|
114
|
+
|
|
115
|
+
AgentTool is domain-neutral. A tool declares a JSON input schema, capabilities,
|
|
116
|
+
per-run call budget, timeout, and result-size limit, then receives a ToolContext
|
|
117
|
+
with the current run ID, isolated evidence, zero-based model step, run-unique
|
|
118
|
+
step and tool-batch IDs, and its zero-based position and size within that batch:
|
|
119
|
+
|
|
120
|
+
~~~python
|
|
121
|
+
async def execute(arguments, context):
|
|
122
|
+
...
|
|
123
|
+
~~~
|
|
124
|
+
|
|
125
|
+
Myli validates arguments and results, enforces capabilities and limits, and
|
|
126
|
+
records a ToolOutcome with one of succeeded, failed, rejected, timed_out, or
|
|
127
|
+
deferred. Calls are sequential by default. Parallel execution requires both the
|
|
128
|
+
harness option and an explicit parallel_safe declaration on every call in the
|
|
129
|
+
batch. Each tool can select FailureMode.RETURN_ERROR or FailureMode.RAISE.
|
|
130
|
+
|
|
131
|
+
ToolMiddleware can allow, reject, or defer work before execution and observe the
|
|
132
|
+
outcome afterward. Applications can use it for approval, audit, tenancy,
|
|
133
|
+
ordering, transactions, and rate limiting. The step and batch metadata lets
|
|
134
|
+
middleware apply a rule exactly once per model work phase, including when a
|
|
135
|
+
batch executes in parallel.
|
|
136
|
+
|
|
137
|
+
CandidatePolicy runs before rendering a proposal and before returning the final
|
|
138
|
+
candidate. CandidateContext includes capabilities, approved assets, and all
|
|
139
|
+
run-scoped outcomes; successful_tool_outcomes is the authorization-safe subset.
|
|
140
|
+
|
|
141
|
+
## Assets and visual review
|
|
142
|
+
|
|
143
|
+
Named asset providers return generic Asset values with application-defined
|
|
144
|
+
kinds and metadata. Myli assigns run-scoped references, detects conflicting
|
|
145
|
+
provider identities, applies search budgets and result limits, and tracks
|
|
146
|
+
provenance.
|
|
147
|
+
|
|
148
|
+
An asset can contain a RenderedArtifact preview or an async preview_loader.
|
|
149
|
+
Previews load only after discovery and explicit inspection, are cached per run,
|
|
150
|
+
and are bounded by timeout, byte-size, and media-type checks. Loading failures
|
|
151
|
+
are returned to the model and can be retried. Binary artifacts never appear in
|
|
152
|
+
step trace dictionaries.
|
|
153
|
+
|
|
154
|
+
When a renderer and vision model are configured, render_design validates the
|
|
155
|
+
candidate and application policies before rendering, validates the artifact,
|
|
156
|
+
and returns visual feedback as a tool result. A changed candidate cannot be
|
|
157
|
+
rendered while editing is disabled, but the unchanged current design can be
|
|
158
|
+
rendered diagnostically.
|
|
159
|
+
|
|
160
|
+
## Events, traces, and limits
|
|
161
|
+
|
|
162
|
+
Events cover the run, model, tool, and validation lifecycle. They contain run
|
|
163
|
+
and step IDs, status, safe messages, elapsed time, and an optional tool name,
|
|
164
|
+
but no arguments or results. Applications can map them to logs, metrics, SSE,
|
|
165
|
+
WebSockets, or ignore them.
|
|
166
|
+
|
|
167
|
+
StepTrace retains normalized model output, provider-exposed reasoning, tool
|
|
168
|
+
calls and outcomes, validation failures, response ID, model, finish reason,
|
|
169
|
+
usage, and latency. It provides to_dict() and redacted(). Async on_step and a
|
|
170
|
+
configurable trace redactor make no persistence assumptions.
|
|
171
|
+
|
|
172
|
+
HarnessLimits bounds model steps, retries, renders, searches, search results,
|
|
173
|
+
asset inspections, patches, documents, history, artifacts, operation timeouts,
|
|
174
|
+
and optionally the entire run. Every custom tool retains its own limits.
|
|
175
|
+
|
|
176
|
+
## Patch and concurrency safety
|
|
177
|
+
|
|
178
|
+
apply_json_patch implements add, remove, replace, move, copy, and test, including
|
|
179
|
+
root operations and escaped pointers, against a deep copy. JsonPatchLimits guard
|
|
180
|
+
operation count, patch bytes, pointer depth, value bytes, document bytes, invalid
|
|
181
|
+
indexes, non-JSON values, non-finite numbers, child moves, and candidate
|
|
182
|
+
expansion.
|
|
183
|
+
|
|
184
|
+
CancelledError is never wrapped. Cancellation propagates into model, renderer,
|
|
185
|
+
vision, search, preview, and custom-tool work. All counters, assets, outcomes,
|
|
186
|
+
and caches live in local run state, so one Myli instance can safely serve
|
|
187
|
+
concurrent requests.
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
~~~bash
|
|
192
|
+
uv sync
|
|
193
|
+
uv run ruff format --check .
|
|
194
|
+
uv run ruff check .
|
|
195
|
+
python -m unittest discover -s tests -v
|
|
196
|
+
~~~
|
|
197
|
+
|
|
198
|
+
Myli is distributed under the MIT License.
|
myli-0.1.2/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Myli
|
|
2
|
+
|
|
3
|
+
Myli is a provider-neutral, pre-alpha Python harness for agents that propose
|
|
4
|
+
RFC 6902 changes to application-owned JSON design documents. It never persists
|
|
5
|
+
or applies the returned candidate to application state.
|
|
6
|
+
|
|
7
|
+
The core is independent of Pydantic, canvas formats, rendering stacks, model
|
|
8
|
+
providers, ORMs, progress transports, and persistence systems.
|
|
9
|
+
|
|
10
|
+
## Run contract
|
|
11
|
+
|
|
12
|
+
~~~python
|
|
13
|
+
result = await myli.run(
|
|
14
|
+
request=user_request,
|
|
15
|
+
design=current_design,
|
|
16
|
+
can_edit=True,
|
|
17
|
+
capabilities={"media.transform"},
|
|
18
|
+
history=history,
|
|
19
|
+
on_event=handle_event,
|
|
20
|
+
on_step=persist_step,
|
|
21
|
+
)
|
|
22
|
+
~~~
|
|
23
|
+
|
|
24
|
+
RunResult contains the user-facing message, optional validated candidate,
|
|
25
|
+
changed flag, proposed patch, approved run-scoped assets, all tool outcomes,
|
|
26
|
+
step traces, run ID, and final provider metadata. Successful and unsuccessful
|
|
27
|
+
tool outcomes are also available as filtered properties.
|
|
28
|
+
|
|
29
|
+
## Generic documents and injected models
|
|
30
|
+
|
|
31
|
+
~~~python
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from myli import DesignSpec, ModelRequest, ModelResponse, Myli
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_untrusted_document(value: Any) -> dict[str, Any]:
|
|
38
|
+
if not isinstance(value, dict):
|
|
39
|
+
raise ValueError("document must be an object")
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
design_spec = DesignSpec(
|
|
44
|
+
name="design",
|
|
45
|
+
schema={"type": "object"},
|
|
46
|
+
validator=load_untrusted_document,
|
|
47
|
+
serializer=lambda document: document,
|
|
48
|
+
normalizer=normalize_trusted_stored_document,
|
|
49
|
+
input_migrator=migrate_stored_document,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ApplicationMainModel:
|
|
54
|
+
async def complete(self, request: ModelRequest) -> ModelResponse:
|
|
55
|
+
return await application_transport.complete(request)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
myli = Myli(
|
|
59
|
+
design_spec=design_spec,
|
|
60
|
+
main_model=ApplicationMainModel(),
|
|
61
|
+
vision_model=application_vision_model,
|
|
62
|
+
renderer=application_renderer,
|
|
63
|
+
)
|
|
64
|
+
~~~
|
|
65
|
+
|
|
66
|
+
DesignSpec has deliberately separate methods for trusted stored input and
|
|
67
|
+
untrusted model candidates. Candidate JSON is validated at runtime against the
|
|
68
|
+
schema and must serialize back without coercion, default insertion, field
|
|
69
|
+
dropping, or any other silent rewrite. Stored input may opt into migration and
|
|
70
|
+
normalization before each run.
|
|
71
|
+
|
|
72
|
+
MainModel and VisionModel are public protocols. Applications may inject
|
|
73
|
+
different transports, endpoints, credentials, or fakes. LiteLLMMainModel and
|
|
74
|
+
LiteLLMVisionModel are optional implementations:
|
|
75
|
+
|
|
76
|
+
~~~bash
|
|
77
|
+
python -m pip install "myli[litellm]"
|
|
78
|
+
~~~
|
|
79
|
+
|
|
80
|
+
The LiteLLM main implementation supports structured, JSON, and text output
|
|
81
|
+
modes, rejects options that override client-owned request fields, and translates
|
|
82
|
+
provider failures into Myli's stable exception hierarchy.
|
|
83
|
+
|
|
84
|
+
Pydantic remains optional:
|
|
85
|
+
|
|
86
|
+
~~~python
|
|
87
|
+
from myli.integrations.pydantic import PydanticDesignSpec
|
|
88
|
+
~~~
|
|
89
|
+
|
|
90
|
+
## Tools, policies, and middleware
|
|
91
|
+
|
|
92
|
+
AgentTool is domain-neutral. A tool declares a JSON input schema, capabilities,
|
|
93
|
+
per-run call budget, timeout, and result-size limit, then receives a ToolContext
|
|
94
|
+
with the current run ID, isolated evidence, zero-based model step, run-unique
|
|
95
|
+
step and tool-batch IDs, and its zero-based position and size within that batch:
|
|
96
|
+
|
|
97
|
+
~~~python
|
|
98
|
+
async def execute(arguments, context):
|
|
99
|
+
...
|
|
100
|
+
~~~
|
|
101
|
+
|
|
102
|
+
Myli validates arguments and results, enforces capabilities and limits, and
|
|
103
|
+
records a ToolOutcome with one of succeeded, failed, rejected, timed_out, or
|
|
104
|
+
deferred. Calls are sequential by default. Parallel execution requires both the
|
|
105
|
+
harness option and an explicit parallel_safe declaration on every call in the
|
|
106
|
+
batch. Each tool can select FailureMode.RETURN_ERROR or FailureMode.RAISE.
|
|
107
|
+
|
|
108
|
+
ToolMiddleware can allow, reject, or defer work before execution and observe the
|
|
109
|
+
outcome afterward. Applications can use it for approval, audit, tenancy,
|
|
110
|
+
ordering, transactions, and rate limiting. The step and batch metadata lets
|
|
111
|
+
middleware apply a rule exactly once per model work phase, including when a
|
|
112
|
+
batch executes in parallel.
|
|
113
|
+
|
|
114
|
+
CandidatePolicy runs before rendering a proposal and before returning the final
|
|
115
|
+
candidate. CandidateContext includes capabilities, approved assets, and all
|
|
116
|
+
run-scoped outcomes; successful_tool_outcomes is the authorization-safe subset.
|
|
117
|
+
|
|
118
|
+
## Assets and visual review
|
|
119
|
+
|
|
120
|
+
Named asset providers return generic Asset values with application-defined
|
|
121
|
+
kinds and metadata. Myli assigns run-scoped references, detects conflicting
|
|
122
|
+
provider identities, applies search budgets and result limits, and tracks
|
|
123
|
+
provenance.
|
|
124
|
+
|
|
125
|
+
An asset can contain a RenderedArtifact preview or an async preview_loader.
|
|
126
|
+
Previews load only after discovery and explicit inspection, are cached per run,
|
|
127
|
+
and are bounded by timeout, byte-size, and media-type checks. Loading failures
|
|
128
|
+
are returned to the model and can be retried. Binary artifacts never appear in
|
|
129
|
+
step trace dictionaries.
|
|
130
|
+
|
|
131
|
+
When a renderer and vision model are configured, render_design validates the
|
|
132
|
+
candidate and application policies before rendering, validates the artifact,
|
|
133
|
+
and returns visual feedback as a tool result. A changed candidate cannot be
|
|
134
|
+
rendered while editing is disabled, but the unchanged current design can be
|
|
135
|
+
rendered diagnostically.
|
|
136
|
+
|
|
137
|
+
## Events, traces, and limits
|
|
138
|
+
|
|
139
|
+
Events cover the run, model, tool, and validation lifecycle. They contain run
|
|
140
|
+
and step IDs, status, safe messages, elapsed time, and an optional tool name,
|
|
141
|
+
but no arguments or results. Applications can map them to logs, metrics, SSE,
|
|
142
|
+
WebSockets, or ignore them.
|
|
143
|
+
|
|
144
|
+
StepTrace retains normalized model output, provider-exposed reasoning, tool
|
|
145
|
+
calls and outcomes, validation failures, response ID, model, finish reason,
|
|
146
|
+
usage, and latency. It provides to_dict() and redacted(). Async on_step and a
|
|
147
|
+
configurable trace redactor make no persistence assumptions.
|
|
148
|
+
|
|
149
|
+
HarnessLimits bounds model steps, retries, renders, searches, search results,
|
|
150
|
+
asset inspections, patches, documents, history, artifacts, operation timeouts,
|
|
151
|
+
and optionally the entire run. Every custom tool retains its own limits.
|
|
152
|
+
|
|
153
|
+
## Patch and concurrency safety
|
|
154
|
+
|
|
155
|
+
apply_json_patch implements add, remove, replace, move, copy, and test, including
|
|
156
|
+
root operations and escaped pointers, against a deep copy. JsonPatchLimits guard
|
|
157
|
+
operation count, patch bytes, pointer depth, value bytes, document bytes, invalid
|
|
158
|
+
indexes, non-JSON values, non-finite numbers, child moves, and candidate
|
|
159
|
+
expansion.
|
|
160
|
+
|
|
161
|
+
CancelledError is never wrapped. Cancellation propagates into model, renderer,
|
|
162
|
+
vision, search, preview, and custom-tool work. All counters, assets, outcomes,
|
|
163
|
+
and caches live in local run state, so one Myli instance can safely serve
|
|
164
|
+
concurrent requests.
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
~~~bash
|
|
169
|
+
uv sync
|
|
170
|
+
uv run ruff format --check .
|
|
171
|
+
uv run ruff check .
|
|
172
|
+
python -m unittest discover -s tests -v
|
|
173
|
+
~~~
|
|
174
|
+
|
|
175
|
+
Myli is distributed under the MIT License.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# This file was autogenerated by uv via the following command:
|
|
2
|
+
# uv pip compile docs/requirements.in --python-version 3.12 --output-file docs/requirements.txt
|
|
3
|
+
accessible-pygments==0.0.5
|
|
4
|
+
# via furo
|
|
5
|
+
alabaster==1.0.0
|
|
6
|
+
# via sphinx
|
|
7
|
+
babel==2.18.0
|
|
8
|
+
# via sphinx
|
|
9
|
+
beautifulsoup4==4.15.0
|
|
10
|
+
# via furo
|
|
11
|
+
certifi==2026.7.22
|
|
12
|
+
# via requests
|
|
13
|
+
charset-normalizer==3.5.1
|
|
14
|
+
# via requests
|
|
15
|
+
docutils==0.21.2
|
|
16
|
+
# via
|
|
17
|
+
# myst-parser
|
|
18
|
+
# sphinx
|
|
19
|
+
furo==2025.12.19
|
|
20
|
+
# via -r docs/requirements.in
|
|
21
|
+
idna==3.19
|
|
22
|
+
# via requests
|
|
23
|
+
imagesize==2.0.0
|
|
24
|
+
# via sphinx
|
|
25
|
+
jinja2==3.1.6
|
|
26
|
+
# via
|
|
27
|
+
# myst-parser
|
|
28
|
+
# sphinx
|
|
29
|
+
markdown-it-py==4.2.0
|
|
30
|
+
# via
|
|
31
|
+
# mdit-py-plugins
|
|
32
|
+
# myst-parser
|
|
33
|
+
markupsafe==3.0.3
|
|
34
|
+
# via jinja2
|
|
35
|
+
mdit-py-plugins==0.6.1
|
|
36
|
+
# via myst-parser
|
|
37
|
+
mdurl==0.1.2
|
|
38
|
+
# via markdown-it-py
|
|
39
|
+
myst-parser==5.1.0
|
|
40
|
+
# via -r docs/requirements.in
|
|
41
|
+
packaging==26.3
|
|
42
|
+
# via sphinx
|
|
43
|
+
pygments==2.21.0
|
|
44
|
+
# via
|
|
45
|
+
# accessible-pygments
|
|
46
|
+
# furo
|
|
47
|
+
# sphinx
|
|
48
|
+
pyyaml==6.0.3
|
|
49
|
+
# via myst-parser
|
|
50
|
+
requests==2.34.2
|
|
51
|
+
# via sphinx
|
|
52
|
+
roman-numerals==4.1.0
|
|
53
|
+
# via roman-numerals-py
|
|
54
|
+
roman-numerals-py==4.1.0
|
|
55
|
+
# via sphinx
|
|
56
|
+
snowballstemmer==3.1.1
|
|
57
|
+
# via sphinx
|
|
58
|
+
soupsieve==2.9.2
|
|
59
|
+
# via beautifulsoup4
|
|
60
|
+
sphinx==8.2.3
|
|
61
|
+
# via
|
|
62
|
+
# -r docs/requirements.in
|
|
63
|
+
# furo
|
|
64
|
+
# myst-parser
|
|
65
|
+
# sphinx-basic-ng
|
|
66
|
+
# sphinx-copybutton
|
|
67
|
+
sphinx-basic-ng==1.0.0b2
|
|
68
|
+
# via furo
|
|
69
|
+
sphinx-copybutton==0.5.2
|
|
70
|
+
# via -r docs/requirements.in
|
|
71
|
+
sphinxcontrib-applehelp==2.0.0
|
|
72
|
+
# via sphinx
|
|
73
|
+
sphinxcontrib-devhelp==2.0.0
|
|
74
|
+
# via sphinx
|
|
75
|
+
sphinxcontrib-htmlhelp==2.1.0
|
|
76
|
+
# via sphinx
|
|
77
|
+
sphinxcontrib-jsmath==1.0.1
|
|
78
|
+
# via sphinx
|
|
79
|
+
sphinxcontrib-qthelp==2.0.0
|
|
80
|
+
# via sphinx
|
|
81
|
+
sphinxcontrib-serializinghtml==2.0.0
|
|
82
|
+
# via sphinx
|
|
83
|
+
typing-extensions==4.16.0
|
|
84
|
+
# via beautifulsoup4
|
|
85
|
+
urllib3==2.7.0
|
|
86
|
+
# via requests
|