moiryx 0.1.0a1__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.
- moiryx-0.1.0a1/CHANGELOG.md +30 -0
- moiryx-0.1.0a1/LICENSE +21 -0
- moiryx-0.1.0a1/MANIFEST.in +3 -0
- moiryx-0.1.0a1/PKG-INFO +245 -0
- moiryx-0.1.0a1/README.md +208 -0
- moiryx-0.1.0a1/docs/01-vision-and-contract.md +41 -0
- moiryx-0.1.0a1/docs/02-architecture.md +51 -0
- moiryx-0.1.0a1/docs/03-configuration-and-agents.md +70 -0
- moiryx-0.1.0a1/docs/04-tools-runtime-and-output.md +86 -0
- moiryx-0.1.0a1/docs/05-providers.md +47 -0
- moiryx-0.1.0a1/docs/06-quality-security-release.md +66 -0
- moiryx-0.1.0a1/docs/README.md +37 -0
- moiryx-0.1.0a1/docs/decisions.md +47 -0
- moiryx-0.1.0a1/docs/release-notes-alpha.md +21 -0
- moiryx-0.1.0a1/docs/risks.md +25 -0
- moiryx-0.1.0a1/docs/roadmap.md +30 -0
- moiryx-0.1.0a1/docs/specification.md +131 -0
- moiryx-0.1.0a1/docs/tickets/README.md +129 -0
- moiryx-0.1.0a1/docs/tickets/TEMPLATE.md +42 -0
- moiryx-0.1.0a1/docs/traceability.md +26 -0
- moiryx-0.1.0a1/examples/__init__.py +1 -0
- moiryx-0.1.0a1/examples/agents/chat.md +4 -0
- moiryx-0.1.0a1/examples/agents/nested_reviewer.md +6 -0
- moiryx-0.1.0a1/examples/agents/reviewer.md +5 -0
- moiryx-0.1.0a1/examples/agents/word_counter.md +5 -0
- moiryx-0.1.0a1/examples/agents/workspace_editor.md +6 -0
- moiryx-0.1.0a1/examples/agents/workspace_reader.md +6 -0
- moiryx-0.1.0a1/examples/custom_tools.py +9 -0
- moiryx-0.1.0a1/examples/models.py +26 -0
- moiryx-0.1.0a1/examples/moiryx.yaml +23 -0
- moiryx-0.1.0a1/examples/run_text.py +16 -0
- moiryx-0.1.0a1/pyproject.toml +81 -0
- moiryx-0.1.0a1/setup.cfg +4 -0
- moiryx-0.1.0a1/src/moiryx/__init__.py +6 -0
- moiryx-0.1.0a1/src/moiryx/agent.py +200 -0
- moiryx-0.1.0a1/src/moiryx/agent_spec.py +154 -0
- moiryx-0.1.0a1/src/moiryx/config.py +354 -0
- moiryx-0.1.0a1/src/moiryx/errors.py +210 -0
- moiryx-0.1.0a1/src/moiryx/generation.py +105 -0
- moiryx-0.1.0a1/src/moiryx/messages.py +75 -0
- moiryx-0.1.0a1/src/moiryx/model_registry.py +62 -0
- moiryx-0.1.0a1/src/moiryx/models.py +108 -0
- moiryx-0.1.0a1/src/moiryx/observability.py +110 -0
- moiryx-0.1.0a1/src/moiryx/output/__init__.py +16 -0
- moiryx-0.1.0a1/src/moiryx/output/final_tool.py +35 -0
- moiryx-0.1.0a1/src/moiryx/output/loader.py +37 -0
- moiryx-0.1.0a1/src/moiryx/output/repair.py +77 -0
- moiryx-0.1.0a1/src/moiryx/providers/__init__.py +54 -0
- moiryx-0.1.0a1/src/moiryx/providers/azure_foundry.py +62 -0
- moiryx-0.1.0a1/src/moiryx/providers/azure_openai.py +74 -0
- moiryx-0.1.0a1/src/moiryx/providers/fake.py +80 -0
- moiryx-0.1.0a1/src/moiryx/providers/openai_compatible.py +391 -0
- moiryx-0.1.0a1/src/moiryx/providers/openrouter.py +52 -0
- moiryx-0.1.0a1/src/moiryx/providers/registry.py +114 -0
- moiryx-0.1.0a1/src/moiryx/providers/vertex_ai.py +378 -0
- moiryx-0.1.0a1/src/moiryx/py.typed +1 -0
- moiryx-0.1.0a1/src/moiryx/redaction.py +108 -0
- moiryx-0.1.0a1/src/moiryx/retry.py +181 -0
- moiryx-0.1.0a1/src/moiryx/runtime.py +724 -0
- moiryx-0.1.0a1/src/moiryx/tools/__init__.py +51 -0
- moiryx-0.1.0a1/src/moiryx/tools/builtin/__init__.py +38 -0
- moiryx-0.1.0a1/src/moiryx/tools/builtin/filesystem.py +432 -0
- moiryx-0.1.0a1/src/moiryx/tools/builtin/shell.py +153 -0
- moiryx-0.1.0a1/src/moiryx/tools/builtin/workspace.py +63 -0
- moiryx-0.1.0a1/src/moiryx/tools/decorator.py +21 -0
- moiryx-0.1.0a1/src/moiryx/tools/definition.py +89 -0
- moiryx-0.1.0a1/src/moiryx/tools/executor.py +108 -0
- moiryx-0.1.0a1/src/moiryx/tools/preflight.py +465 -0
- moiryx-0.1.0a1/src/moiryx/tools/registry.py +85 -0
- moiryx-0.1.0a1/src/moiryx/tools/serialization.py +88 -0
- moiryx-0.1.0a1/src/moiryx.egg-info/PKG-INFO +245 -0
- moiryx-0.1.0a1/src/moiryx.egg-info/SOURCES.txt +103 -0
- moiryx-0.1.0a1/src/moiryx.egg-info/dependency_links.txt +1 -0
- moiryx-0.1.0a1/src/moiryx.egg-info/requires.txt +19 -0
- moiryx-0.1.0a1/src/moiryx.egg-info/top_level.txt +1 -0
- moiryx-0.1.0a1/tests/test_agent.py +454 -0
- moiryx-0.1.0a1/tests/test_agent_spec.py +137 -0
- moiryx-0.1.0a1/tests/test_async_support.py +10 -0
- moiryx-0.1.0a1/tests/test_azure_foundry.py +116 -0
- moiryx-0.1.0a1/tests/test_azure_openai.py +218 -0
- moiryx-0.1.0a1/tests/test_azure_packaging.py +67 -0
- moiryx-0.1.0a1/tests/test_builtin_tools.py +439 -0
- moiryx-0.1.0a1/tests/test_concurrency.py +238 -0
- moiryx-0.1.0a1/tests/test_config.py +235 -0
- moiryx-0.1.0a1/tests/test_documentation_and_ci.py +129 -0
- moiryx-0.1.0a1/tests/test_dogfood.py +246 -0
- moiryx-0.1.0a1/tests/test_errors.py +61 -0
- moiryx-0.1.0a1/tests/test_generation.py +61 -0
- moiryx-0.1.0a1/tests/test_model_registry.py +66 -0
- moiryx-0.1.0a1/tests/test_models.py +133 -0
- moiryx-0.1.0a1/tests/test_observability.py +411 -0
- moiryx-0.1.0a1/tests/test_openai_compatible.py +503 -0
- moiryx-0.1.0a1/tests/test_openrouter.py +184 -0
- moiryx-0.1.0a1/tests/test_provider_protocol.py +164 -0
- moiryx-0.1.0a1/tests/test_provider_registry.py +112 -0
- moiryx-0.1.0a1/tests/test_public_api.py +27 -0
- moiryx-0.1.0a1/tests/test_release_artifacts.py +76 -0
- moiryx-0.1.0a1/tests/test_retry.py +188 -0
- moiryx-0.1.0a1/tests/test_runtime.py +325 -0
- moiryx-0.1.0a1/tests/test_structured_output.py +405 -0
- moiryx-0.1.0a1/tests/test_tool_executor.py +105 -0
- moiryx-0.1.0a1/tests/test_tool_preflight.py +331 -0
- moiryx-0.1.0a1/tests/test_tool_serialization.py +83 -0
- moiryx-0.1.0a1/tests/test_tools.py +147 -0
- moiryx-0.1.0a1/tests/test_vertex_ai.py +304 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes are recorded here. Versions follow PEP 440; `0.1.0a1` is the
|
|
4
|
+
first alpha.
|
|
5
|
+
|
|
6
|
+
## 0.1.0a1
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Markdown agent definitions, YAML model aliases, and the `Agent` and `@tool`
|
|
11
|
+
public API.
|
|
12
|
+
- Text-agent runtime with tool validation, conservative repair of malformed
|
|
13
|
+
calls, and separate retry budgets.
|
|
14
|
+
- Pydantic structured results, including nested models, through a synthetic
|
|
15
|
+
final tool or native JSON schema where the provider supports it.
|
|
16
|
+
- OpenAI-compatible, OpenRouter, Azure OpenAI, Azure Foundry, and Vertex AI
|
|
17
|
+
adapters.
|
|
18
|
+
- Workspace-scoped file tools and an explicitly enabled `shell` tool.
|
|
19
|
+
- Run events, optional JSONL traces, and centralized secret redaction.
|
|
20
|
+
- Deterministic AC1–AC11 acceptance tests, examples, and a CI matrix.
|
|
21
|
+
|
|
22
|
+
### Alpha limitations
|
|
23
|
+
|
|
24
|
+
- No streaming, sessions, multimodal input, or automatic model fallback.
|
|
25
|
+
- `shell` is not a sandbox; use it only in trusted environments.
|
|
26
|
+
- Function calling and JSON-schema behavior of local models depend on the
|
|
27
|
+
model and its chat template.
|
|
28
|
+
|
|
29
|
+
See the [alpha release notes](docs/release-notes-alpha.md) for verification
|
|
30
|
+
details and limitations.
|
moiryx-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moiryx 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.
|
moiryx-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moiryx
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Thin, config-driven Python runtime for LLM agents.
|
|
5
|
+
Author: Moiryx contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/kamilsz713/moiryx
|
|
8
|
+
Project-URL: Issues, https://github.com/kamilsz713/moiryx/issues
|
|
9
|
+
Keywords: agents,llm,tools,structured-output
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: docstring-parser<1,>=0.17
|
|
22
|
+
Requires-Dist: httpx<1,>=0.27
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
24
|
+
Requires-Dist: PyYAML<7,>=6
|
|
25
|
+
Provides-Extra: azure
|
|
26
|
+
Provides-Extra: google
|
|
27
|
+
Requires-Dist: google-genai<2,>=1; extra == "google"
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: google-genai<2,>=1; extra == "all"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build<2,>=1.3; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy<2,>=1.11; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest<10,>=8.3; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-asyncio<2,>=1; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# Moiryx
|
|
39
|
+
|
|
40
|
+
Moiryx lets you define an agent in Markdown, select its model in YAML, and call
|
|
41
|
+
it like an async Python object. You can move the same agent from a local
|
|
42
|
+
`llama-server` to OpenRouter, Azure, or Vertex AI without changing Python code.
|
|
43
|
+
|
|
44
|
+
This project is an alpha. Its deliberately small user-facing API consists of
|
|
45
|
+
`Agent`, `@tool`, and `moiryx.yaml`.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Install the alpha from PyPI once it is available:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pip install moiryx==0.1.0a1
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For development from this checkout:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Vertex AI requires the optional Google Gen AI SDK. For a PyPI installation:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python -m pip install "moiryx[google]==0.1.0a1"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
From this checkout:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python -m pip install -e ".[google]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Your first agent with a local llama-server
|
|
74
|
+
|
|
75
|
+
Start an OpenAI-compatible endpoint and create `moiryx.yaml`:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
providers:
|
|
79
|
+
local:
|
|
80
|
+
type: openai_compatible
|
|
81
|
+
base_url: http://127.0.0.1:8080/v1
|
|
82
|
+
|
|
83
|
+
models:
|
|
84
|
+
local_chat:
|
|
85
|
+
provider: local
|
|
86
|
+
model: local-model
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Save the agent as `agents/chat.md`:
|
|
90
|
+
|
|
91
|
+
```markdown
|
|
92
|
+
---
|
|
93
|
+
model: local_chat
|
|
94
|
+
---
|
|
95
|
+
Answer directly and say when you are uncertain.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Call it from Python:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
|
|
103
|
+
from moiryx import Agent
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async def main() -> None:
|
|
107
|
+
agent = Agent("agents/chat.md")
|
|
108
|
+
answer = await agent("What is the difference between a process and a thread?")
|
|
109
|
+
print(answer)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
asyncio.run(main())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For a long-lived application, call `await agent.aclose()` when you are done, or
|
|
116
|
+
use `async with Agent(...)`. This releases the provider's HTTP connections. A
|
|
117
|
+
one-off script can simply exit.
|
|
118
|
+
|
|
119
|
+
## Built-in tools
|
|
120
|
+
|
|
121
|
+
Tools are opt-in. Add only the ones an agent needs to its frontmatter:
|
|
122
|
+
|
|
123
|
+
```markdown
|
|
124
|
+
---
|
|
125
|
+
model: local_chat
|
|
126
|
+
tools: [read_file, list_files, grep]
|
|
127
|
+
---
|
|
128
|
+
Inspect files in the workspace and cite the paths you used.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Available tools are `read_file`, `list_files`, `glob_files`, `grep`,
|
|
132
|
+
`write_file`, `edit_file`, and `shell`. File operations stay within
|
|
133
|
+
`runtime.workspace_root` by default.
|
|
134
|
+
|
|
135
|
+
> `shell` is not a security sandbox. Enable it only for agents and workspaces
|
|
136
|
+
> you trust.
|
|
137
|
+
|
|
138
|
+
## Custom tools
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from moiryx import tool
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@tool
|
|
145
|
+
def word_count(text: str) -> int:
|
|
146
|
+
"""Count words in text."""
|
|
147
|
+
return len(text.split())
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Import the module through the configuration, then select the tool in the
|
|
151
|
+
agent definition:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
tool_modules: [my_tools]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```markdown
|
|
158
|
+
---
|
|
159
|
+
model: local_chat
|
|
160
|
+
tools: [word_count]
|
|
161
|
+
---
|
|
162
|
+
Use the tool to count words accurately.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Structured output
|
|
166
|
+
|
|
167
|
+
Declare the result with an ordinary Pydantic model:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from pydantic import BaseModel, Field
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ReviewResult(BaseModel):
|
|
174
|
+
accepted: bool
|
|
175
|
+
score: float = Field(ge=0, le=1)
|
|
176
|
+
findings: list[str]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Reference it as `module:Class` in the agent:
|
|
180
|
+
|
|
181
|
+
```markdown
|
|
182
|
+
---
|
|
183
|
+
model: local_chat
|
|
184
|
+
output: review_models:ReviewResult
|
|
185
|
+
---
|
|
186
|
+
Review the change and return a result matching the schema.
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`await agent(...)` returns a `ReviewResult` instance. JSON embedded in plain
|
|
190
|
+
text does not count as a structured result.
|
|
191
|
+
|
|
192
|
+
## Switching providers in YAML
|
|
193
|
+
|
|
194
|
+
Keep the Python code and agent file; change the model alias configuration:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
providers:
|
|
198
|
+
router:
|
|
199
|
+
type: openrouter
|
|
200
|
+
api_key: ${OPENROUTER_API_KEY}
|
|
201
|
+
headers:
|
|
202
|
+
HTTP-Referer: https://example.invalid
|
|
203
|
+
X-OpenRouter-Title: Moiryx example
|
|
204
|
+
|
|
205
|
+
models:
|
|
206
|
+
local_chat:
|
|
207
|
+
provider: router
|
|
208
|
+
model: anthropic/claude-sonnet-4.5
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Supported provider types are `openai_compatible` (including llama-server,
|
|
212
|
+
vLLM, and SGLang), `openrouter`, `azure_openai`, `azure_foundry`, and
|
|
213
|
+
`vertex_ai` (using Application Default Credentials or a service account).
|
|
214
|
+
|
|
215
|
+
## Logging and traces
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
logging:
|
|
219
|
+
level: INFO
|
|
220
|
+
trace_dir: .moiryx/runs
|
|
221
|
+
include_raw_response: false
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Each run has its own ID and, when tracing is enabled, an
|
|
225
|
+
`.moiryx/runs/<run-id>/events.jsonl` file. Raw provider responses are off by
|
|
226
|
+
default. If enabled, configured secrets and sensitive fields are still
|
|
227
|
+
redacted.
|
|
228
|
+
|
|
229
|
+
## Examples and development
|
|
230
|
+
|
|
231
|
+
See [`examples/`](examples/) for runnable definitions and [`docs/`](docs/) for
|
|
232
|
+
architecture, limitations, release notes, and the issue backlog.
|
|
233
|
+
|
|
234
|
+
Run the local quality checks:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
python scripts/check.py
|
|
238
|
+
python -m pytest -m acceptance -q
|
|
239
|
+
python -m build
|
|
240
|
+
python scripts/audit_artifacts.py dist
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Acceptance tests use deterministic fake providers; they need no network or
|
|
244
|
+
credentials. The live local-endpoint test is opt-in through
|
|
245
|
+
`MOIRYX_OPENAI_COMPATIBLE_LIVE_URL` and `MOIRYX_OPENAI_COMPATIBLE_LIVE_MODEL`.
|
moiryx-0.1.0a1/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Moiryx
|
|
2
|
+
|
|
3
|
+
Moiryx lets you define an agent in Markdown, select its model in YAML, and call
|
|
4
|
+
it like an async Python object. You can move the same agent from a local
|
|
5
|
+
`llama-server` to OpenRouter, Azure, or Vertex AI without changing Python code.
|
|
6
|
+
|
|
7
|
+
This project is an alpha. Its deliberately small user-facing API consists of
|
|
8
|
+
`Agent`, `@tool`, and `moiryx.yaml`.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Install the alpha from PyPI once it is available:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m pip install moiryx==0.1.0a1
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For development from this checkout:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
python -m pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Vertex AI requires the optional Google Gen AI SDK. For a PyPI installation:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python -m pip install "moiryx[google]==0.1.0a1"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
From this checkout:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m pip install -e ".[google]"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Your first agent with a local llama-server
|
|
37
|
+
|
|
38
|
+
Start an OpenAI-compatible endpoint and create `moiryx.yaml`:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
providers:
|
|
42
|
+
local:
|
|
43
|
+
type: openai_compatible
|
|
44
|
+
base_url: http://127.0.0.1:8080/v1
|
|
45
|
+
|
|
46
|
+
models:
|
|
47
|
+
local_chat:
|
|
48
|
+
provider: local
|
|
49
|
+
model: local-model
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Save the agent as `agents/chat.md`:
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
---
|
|
56
|
+
model: local_chat
|
|
57
|
+
---
|
|
58
|
+
Answer directly and say when you are uncertain.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Call it from Python:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import asyncio
|
|
65
|
+
|
|
66
|
+
from moiryx import Agent
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def main() -> None:
|
|
70
|
+
agent = Agent("agents/chat.md")
|
|
71
|
+
answer = await agent("What is the difference between a process and a thread?")
|
|
72
|
+
print(answer)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
asyncio.run(main())
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For a long-lived application, call `await agent.aclose()` when you are done, or
|
|
79
|
+
use `async with Agent(...)`. This releases the provider's HTTP connections. A
|
|
80
|
+
one-off script can simply exit.
|
|
81
|
+
|
|
82
|
+
## Built-in tools
|
|
83
|
+
|
|
84
|
+
Tools are opt-in. Add only the ones an agent needs to its frontmatter:
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
---
|
|
88
|
+
model: local_chat
|
|
89
|
+
tools: [read_file, list_files, grep]
|
|
90
|
+
---
|
|
91
|
+
Inspect files in the workspace and cite the paths you used.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Available tools are `read_file`, `list_files`, `glob_files`, `grep`,
|
|
95
|
+
`write_file`, `edit_file`, and `shell`. File operations stay within
|
|
96
|
+
`runtime.workspace_root` by default.
|
|
97
|
+
|
|
98
|
+
> `shell` is not a security sandbox. Enable it only for agents and workspaces
|
|
99
|
+
> you trust.
|
|
100
|
+
|
|
101
|
+
## Custom tools
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from moiryx import tool
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@tool
|
|
108
|
+
def word_count(text: str) -> int:
|
|
109
|
+
"""Count words in text."""
|
|
110
|
+
return len(text.split())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Import the module through the configuration, then select the tool in the
|
|
114
|
+
agent definition:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
tool_modules: [my_tools]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```markdown
|
|
121
|
+
---
|
|
122
|
+
model: local_chat
|
|
123
|
+
tools: [word_count]
|
|
124
|
+
---
|
|
125
|
+
Use the tool to count words accurately.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Structured output
|
|
129
|
+
|
|
130
|
+
Declare the result with an ordinary Pydantic model:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from pydantic import BaseModel, Field
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ReviewResult(BaseModel):
|
|
137
|
+
accepted: bool
|
|
138
|
+
score: float = Field(ge=0, le=1)
|
|
139
|
+
findings: list[str]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Reference it as `module:Class` in the agent:
|
|
143
|
+
|
|
144
|
+
```markdown
|
|
145
|
+
---
|
|
146
|
+
model: local_chat
|
|
147
|
+
output: review_models:ReviewResult
|
|
148
|
+
---
|
|
149
|
+
Review the change and return a result matching the schema.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`await agent(...)` returns a `ReviewResult` instance. JSON embedded in plain
|
|
153
|
+
text does not count as a structured result.
|
|
154
|
+
|
|
155
|
+
## Switching providers in YAML
|
|
156
|
+
|
|
157
|
+
Keep the Python code and agent file; change the model alias configuration:
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
providers:
|
|
161
|
+
router:
|
|
162
|
+
type: openrouter
|
|
163
|
+
api_key: ${OPENROUTER_API_KEY}
|
|
164
|
+
headers:
|
|
165
|
+
HTTP-Referer: https://example.invalid
|
|
166
|
+
X-OpenRouter-Title: Moiryx example
|
|
167
|
+
|
|
168
|
+
models:
|
|
169
|
+
local_chat:
|
|
170
|
+
provider: router
|
|
171
|
+
model: anthropic/claude-sonnet-4.5
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Supported provider types are `openai_compatible` (including llama-server,
|
|
175
|
+
vLLM, and SGLang), `openrouter`, `azure_openai`, `azure_foundry`, and
|
|
176
|
+
`vertex_ai` (using Application Default Credentials or a service account).
|
|
177
|
+
|
|
178
|
+
## Logging and traces
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
logging:
|
|
182
|
+
level: INFO
|
|
183
|
+
trace_dir: .moiryx/runs
|
|
184
|
+
include_raw_response: false
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Each run has its own ID and, when tracing is enabled, an
|
|
188
|
+
`.moiryx/runs/<run-id>/events.jsonl` file. Raw provider responses are off by
|
|
189
|
+
default. If enabled, configured secrets and sensitive fields are still
|
|
190
|
+
redacted.
|
|
191
|
+
|
|
192
|
+
## Examples and development
|
|
193
|
+
|
|
194
|
+
See [`examples/`](examples/) for runnable definitions and [`docs/`](docs/) for
|
|
195
|
+
architecture, limitations, release notes, and the issue backlog.
|
|
196
|
+
|
|
197
|
+
Run the local quality checks:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
python scripts/check.py
|
|
201
|
+
python -m pytest -m acceptance -q
|
|
202
|
+
python -m build
|
|
203
|
+
python scripts/audit_artifacts.py dist
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Acceptance tests use deterministic fake providers; they need no network or
|
|
207
|
+
credentials. The live local-endpoint test is opt-in through
|
|
208
|
+
`MOIRYX_OPENAI_COMPATIBLE_LIVE_URL` and `MOIRYX_OPENAI_COMPATIBLE_LIVE_MODEL`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Vision and public contract
|
|
2
|
+
|
|
3
|
+
Moiryx is a small, configuration-driven Python runtime for LLM agents. It
|
|
4
|
+
handles provider requests, tool calls, validation, retries, and typed results;
|
|
5
|
+
application orchestration remains ordinary Python.
|
|
6
|
+
|
|
7
|
+
## Public API
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from moiryx import Agent, tool
|
|
11
|
+
|
|
12
|
+
reviewer = Agent("agents/reviewer.md")
|
|
13
|
+
review = await reviewer("Review src/cache.py")
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The v0.1 contract is intentionally narrow:
|
|
17
|
+
|
|
18
|
+
- `Agent` takes an agent-file path; `await agent(prompt)` starts an isolated run.
|
|
19
|
+
- An agent without `output` returns final text. An agent with `output` returns
|
|
20
|
+
an instance of the declared Pydantic model.
|
|
21
|
+
- `@tool` registers an ordinary Python function under its function name.
|
|
22
|
+
- Markdown contains instructions, YAML selects the provider and model, and
|
|
23
|
+
Python controls multi-agent orchestration.
|
|
24
|
+
- No default result wrapper, `run()` method, constructor model override, or
|
|
25
|
+
JSON-from-prose fallback is part of the public API.
|
|
26
|
+
|
|
27
|
+
## Scope
|
|
28
|
+
|
|
29
|
+
The alpha includes text and structured agent loops, custom and workspace
|
|
30
|
+
tools, OpenAI-compatible/OpenRouter/Azure/Vertex AI adapters, bounded retries,
|
|
31
|
+
run events, optional JSONL traces, and deterministic offline tests.
|
|
32
|
+
|
|
33
|
+
It does not include a workflow DSL, persistent memory, RAG framework, vector
|
|
34
|
+
store, HTTP server, GUI, distributed workers, streaming API, automatic context
|
|
35
|
+
compression, MCP, policy engine, or automatic model fallback. Those should be
|
|
36
|
+
added only when real use cases justify shared abstractions.
|
|
37
|
+
|
|
38
|
+
Success means a text agent, a tool-using agent, and a nested structured-result
|
|
39
|
+
agent work without special cases; the same agent can switch providers through
|
|
40
|
+
YAML; invalid tool calls cannot cause guessed side effects; concurrent runs do
|
|
41
|
+
not share history; and errors and traces explain what happened.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
Application code ── await agent(prompt) ──> Agent
|
|
5
|
+
├── Markdown -> AgentSpec
|
|
6
|
+
├── YAML -> model/provider registry
|
|
7
|
+
└── selected tools/output model
|
|
8
|
+
│
|
|
9
|
+
▼
|
|
10
|
+
Runtime + per-call RunContext
|
|
11
|
+
├── provider retry and limits
|
|
12
|
+
├── whole-batch tool preflight
|
|
13
|
+
├── tool execution and repair
|
|
14
|
+
├── structured result validation
|
|
15
|
+
└── events and trace
|
|
16
|
+
│
|
|
17
|
+
▼
|
|
18
|
+
ProviderAdapter -> API/SDK
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`Agent` resolves configuration, model, provider, tools, and output schema at
|
|
22
|
+
construction. It holds no per-run messages. Each call creates its own
|
|
23
|
+
`RunContext`, so one agent instance can serve concurrent calls without mixing
|
|
24
|
+
histories. Call `await agent.aclose()` or use `async with Agent(...)` to release
|
|
25
|
+
its provider client in a long-lived process.
|
|
26
|
+
|
|
27
|
+
The provider boundary takes a normalized `ModelRequest` and returns a
|
|
28
|
+
`ModelResponse`. Adapters retain malformed raw tool arguments for the shared
|
|
29
|
+
repair layer but do not expose SDK objects to the runtime. The core does not
|
|
30
|
+
branch on provider names, and tools do not import provider adapters.
|
|
31
|
+
|
|
32
|
+
## One run
|
|
33
|
+
|
|
34
|
+
1. Start an isolated context with system and user messages.
|
|
35
|
+
2. Request a model response; retries do not consume extra agent steps.
|
|
36
|
+
3. Preflight every tool call before executing any call in the batch.
|
|
37
|
+
4. If the batch is invalid, execute none of it and return bounded repair
|
|
38
|
+
feedback. Otherwise, execute calls sequentially.
|
|
39
|
+
5. Stop on final text or a validated structured result. Missing content,
|
|
40
|
+
exhausted repair budgets, and step limits raise explicit errors.
|
|
41
|
+
|
|
42
|
+
Events observe this flow but never control it. Logging and optional JSONL
|
|
43
|
+
traces redact configured secrets before output.
|
|
44
|
+
|
|
45
|
+
## Repository layout
|
|
46
|
+
|
|
47
|
+
- `src/moiryx/agent.py`, `agent_spec.py`, `config.py`: user entry point and
|
|
48
|
+
configuration loading.
|
|
49
|
+
- `runtime.py`, `messages.py`, `models.py`: provider-neutral execution.
|
|
50
|
+
- `tools/`, `output/`, `providers/`: isolated subsystems.
|
|
51
|
+
- `tests/`: deterministic unit, adapter, acceptance, and opt-in live tests.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Configuration and agent definitions
|
|
2
|
+
|
|
3
|
+
Moiryx reads the path in `MOIRYX_CONFIG` when set, otherwise `./moiryx.yaml`
|
|
4
|
+
from the current working directory. It does not merge files or search parent
|
|
5
|
+
directories. Call `reset_config_cache()` only when deliberately reloading a
|
|
6
|
+
configuration in the same process.
|
|
7
|
+
|
|
8
|
+
```yaml
|
|
9
|
+
providers:
|
|
10
|
+
local:
|
|
11
|
+
type: openai_compatible
|
|
12
|
+
base_url: http://127.0.0.1:8080/v1
|
|
13
|
+
|
|
14
|
+
models:
|
|
15
|
+
reviewer:
|
|
16
|
+
provider: local
|
|
17
|
+
model: local-model
|
|
18
|
+
|
|
19
|
+
runtime:
|
|
20
|
+
default_max_steps: 20
|
|
21
|
+
provider_retry_attempts: 3
|
|
22
|
+
structured_output_retries: 2
|
|
23
|
+
tool_call_repair_attempts: 2
|
|
24
|
+
tool_timeout_seconds: 60
|
|
25
|
+
workspace_root: .
|
|
26
|
+
allow_paths_outside_workspace: false
|
|
27
|
+
max_tool_output_chars: 50000
|
|
28
|
+
|
|
29
|
+
logging:
|
|
30
|
+
level: INFO
|
|
31
|
+
trace_dir: .moiryx/runs
|
|
32
|
+
include_raw_response: false
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`providers` keys name connections; `models` keys name logical model aliases.
|
|
36
|
+
Model IDs are opaque strings, including IDs with slashes. String values may
|
|
37
|
+
contain `${ENV_VAR}` references; missing variables fail configuration loading.
|
|
38
|
+
Keep secrets in environment variables rather than committed YAML.
|
|
39
|
+
|
|
40
|
+
Generation options merge in this order: runtime defaults, model settings,
|
|
41
|
+
then agent overrides. The common options are `temperature`, `max_tokens`,
|
|
42
|
+
`top_p`, and `seed`. Unknown options are rejected. `tool_modules` explicitly
|
|
43
|
+
lists modules to import for custom tools; there is no filesystem scan.
|
|
44
|
+
|
|
45
|
+
An agent file contains YAML frontmatter followed by Markdown system
|
|
46
|
+
instructions:
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
---
|
|
50
|
+
name: reviewer
|
|
51
|
+
model: reviewer
|
|
52
|
+
tools: [read_file, grep]
|
|
53
|
+
output: my_project.schemas.review:ReviewOutput
|
|
54
|
+
max_steps: 20
|
|
55
|
+
generation:
|
|
56
|
+
temperature: 0.2
|
|
57
|
+
---
|
|
58
|
+
Review the code and report specific findings.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Only `model` is required. `name` defaults to the file stem; `tools` defaults
|
|
62
|
+
to an empty list; `output` is an import path to a `BaseModel` subclass;
|
|
63
|
+
`max_steps` defaults to `runtime.default_max_steps`. The constructor catches
|
|
64
|
+
invalid files, aliases, tool names, output models, options, and unsupported
|
|
65
|
+
provider capabilities before the first request.
|
|
66
|
+
|
|
67
|
+
The `moiryx.run` logger emits run boundaries at INFO, model and successful
|
|
68
|
+
tool steps at DEBUG, retries and repairs at WARNING, and failed runs at ERROR.
|
|
69
|
+
Setting `logging.trace_dir` writes one `events.jsonl` per run. Raw responses
|
|
70
|
+
are disabled by default and remain subject to central redaction when enabled.
|