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.
@@ -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,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .mypy_cache/
7
+ .idea/
8
+ .venv/
9
+ build/
10
+ dist/
11
+ docs/_build/
@@ -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,6 @@
1
+ # Top-level documentation dependencies. Keep these versions explicit so that
2
+ # Read the Docs and local builds use the same documentation toolchain.
3
+ Sphinx==8.2.3
4
+ furo==2025.12.19
5
+ myst-parser==5.1.0
6
+ sphinx-copybutton==0.5.2
@@ -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