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.
Files changed (105) hide show
  1. moiryx-0.1.0a1/CHANGELOG.md +30 -0
  2. moiryx-0.1.0a1/LICENSE +21 -0
  3. moiryx-0.1.0a1/MANIFEST.in +3 -0
  4. moiryx-0.1.0a1/PKG-INFO +245 -0
  5. moiryx-0.1.0a1/README.md +208 -0
  6. moiryx-0.1.0a1/docs/01-vision-and-contract.md +41 -0
  7. moiryx-0.1.0a1/docs/02-architecture.md +51 -0
  8. moiryx-0.1.0a1/docs/03-configuration-and-agents.md +70 -0
  9. moiryx-0.1.0a1/docs/04-tools-runtime-and-output.md +86 -0
  10. moiryx-0.1.0a1/docs/05-providers.md +47 -0
  11. moiryx-0.1.0a1/docs/06-quality-security-release.md +66 -0
  12. moiryx-0.1.0a1/docs/README.md +37 -0
  13. moiryx-0.1.0a1/docs/decisions.md +47 -0
  14. moiryx-0.1.0a1/docs/release-notes-alpha.md +21 -0
  15. moiryx-0.1.0a1/docs/risks.md +25 -0
  16. moiryx-0.1.0a1/docs/roadmap.md +30 -0
  17. moiryx-0.1.0a1/docs/specification.md +131 -0
  18. moiryx-0.1.0a1/docs/tickets/README.md +129 -0
  19. moiryx-0.1.0a1/docs/tickets/TEMPLATE.md +42 -0
  20. moiryx-0.1.0a1/docs/traceability.md +26 -0
  21. moiryx-0.1.0a1/examples/__init__.py +1 -0
  22. moiryx-0.1.0a1/examples/agents/chat.md +4 -0
  23. moiryx-0.1.0a1/examples/agents/nested_reviewer.md +6 -0
  24. moiryx-0.1.0a1/examples/agents/reviewer.md +5 -0
  25. moiryx-0.1.0a1/examples/agents/word_counter.md +5 -0
  26. moiryx-0.1.0a1/examples/agents/workspace_editor.md +6 -0
  27. moiryx-0.1.0a1/examples/agents/workspace_reader.md +6 -0
  28. moiryx-0.1.0a1/examples/custom_tools.py +9 -0
  29. moiryx-0.1.0a1/examples/models.py +26 -0
  30. moiryx-0.1.0a1/examples/moiryx.yaml +23 -0
  31. moiryx-0.1.0a1/examples/run_text.py +16 -0
  32. moiryx-0.1.0a1/pyproject.toml +81 -0
  33. moiryx-0.1.0a1/setup.cfg +4 -0
  34. moiryx-0.1.0a1/src/moiryx/__init__.py +6 -0
  35. moiryx-0.1.0a1/src/moiryx/agent.py +200 -0
  36. moiryx-0.1.0a1/src/moiryx/agent_spec.py +154 -0
  37. moiryx-0.1.0a1/src/moiryx/config.py +354 -0
  38. moiryx-0.1.0a1/src/moiryx/errors.py +210 -0
  39. moiryx-0.1.0a1/src/moiryx/generation.py +105 -0
  40. moiryx-0.1.0a1/src/moiryx/messages.py +75 -0
  41. moiryx-0.1.0a1/src/moiryx/model_registry.py +62 -0
  42. moiryx-0.1.0a1/src/moiryx/models.py +108 -0
  43. moiryx-0.1.0a1/src/moiryx/observability.py +110 -0
  44. moiryx-0.1.0a1/src/moiryx/output/__init__.py +16 -0
  45. moiryx-0.1.0a1/src/moiryx/output/final_tool.py +35 -0
  46. moiryx-0.1.0a1/src/moiryx/output/loader.py +37 -0
  47. moiryx-0.1.0a1/src/moiryx/output/repair.py +77 -0
  48. moiryx-0.1.0a1/src/moiryx/providers/__init__.py +54 -0
  49. moiryx-0.1.0a1/src/moiryx/providers/azure_foundry.py +62 -0
  50. moiryx-0.1.0a1/src/moiryx/providers/azure_openai.py +74 -0
  51. moiryx-0.1.0a1/src/moiryx/providers/fake.py +80 -0
  52. moiryx-0.1.0a1/src/moiryx/providers/openai_compatible.py +391 -0
  53. moiryx-0.1.0a1/src/moiryx/providers/openrouter.py +52 -0
  54. moiryx-0.1.0a1/src/moiryx/providers/registry.py +114 -0
  55. moiryx-0.1.0a1/src/moiryx/providers/vertex_ai.py +378 -0
  56. moiryx-0.1.0a1/src/moiryx/py.typed +1 -0
  57. moiryx-0.1.0a1/src/moiryx/redaction.py +108 -0
  58. moiryx-0.1.0a1/src/moiryx/retry.py +181 -0
  59. moiryx-0.1.0a1/src/moiryx/runtime.py +724 -0
  60. moiryx-0.1.0a1/src/moiryx/tools/__init__.py +51 -0
  61. moiryx-0.1.0a1/src/moiryx/tools/builtin/__init__.py +38 -0
  62. moiryx-0.1.0a1/src/moiryx/tools/builtin/filesystem.py +432 -0
  63. moiryx-0.1.0a1/src/moiryx/tools/builtin/shell.py +153 -0
  64. moiryx-0.1.0a1/src/moiryx/tools/builtin/workspace.py +63 -0
  65. moiryx-0.1.0a1/src/moiryx/tools/decorator.py +21 -0
  66. moiryx-0.1.0a1/src/moiryx/tools/definition.py +89 -0
  67. moiryx-0.1.0a1/src/moiryx/tools/executor.py +108 -0
  68. moiryx-0.1.0a1/src/moiryx/tools/preflight.py +465 -0
  69. moiryx-0.1.0a1/src/moiryx/tools/registry.py +85 -0
  70. moiryx-0.1.0a1/src/moiryx/tools/serialization.py +88 -0
  71. moiryx-0.1.0a1/src/moiryx.egg-info/PKG-INFO +245 -0
  72. moiryx-0.1.0a1/src/moiryx.egg-info/SOURCES.txt +103 -0
  73. moiryx-0.1.0a1/src/moiryx.egg-info/dependency_links.txt +1 -0
  74. moiryx-0.1.0a1/src/moiryx.egg-info/requires.txt +19 -0
  75. moiryx-0.1.0a1/src/moiryx.egg-info/top_level.txt +1 -0
  76. moiryx-0.1.0a1/tests/test_agent.py +454 -0
  77. moiryx-0.1.0a1/tests/test_agent_spec.py +137 -0
  78. moiryx-0.1.0a1/tests/test_async_support.py +10 -0
  79. moiryx-0.1.0a1/tests/test_azure_foundry.py +116 -0
  80. moiryx-0.1.0a1/tests/test_azure_openai.py +218 -0
  81. moiryx-0.1.0a1/tests/test_azure_packaging.py +67 -0
  82. moiryx-0.1.0a1/tests/test_builtin_tools.py +439 -0
  83. moiryx-0.1.0a1/tests/test_concurrency.py +238 -0
  84. moiryx-0.1.0a1/tests/test_config.py +235 -0
  85. moiryx-0.1.0a1/tests/test_documentation_and_ci.py +129 -0
  86. moiryx-0.1.0a1/tests/test_dogfood.py +246 -0
  87. moiryx-0.1.0a1/tests/test_errors.py +61 -0
  88. moiryx-0.1.0a1/tests/test_generation.py +61 -0
  89. moiryx-0.1.0a1/tests/test_model_registry.py +66 -0
  90. moiryx-0.1.0a1/tests/test_models.py +133 -0
  91. moiryx-0.1.0a1/tests/test_observability.py +411 -0
  92. moiryx-0.1.0a1/tests/test_openai_compatible.py +503 -0
  93. moiryx-0.1.0a1/tests/test_openrouter.py +184 -0
  94. moiryx-0.1.0a1/tests/test_provider_protocol.py +164 -0
  95. moiryx-0.1.0a1/tests/test_provider_registry.py +112 -0
  96. moiryx-0.1.0a1/tests/test_public_api.py +27 -0
  97. moiryx-0.1.0a1/tests/test_release_artifacts.py +76 -0
  98. moiryx-0.1.0a1/tests/test_retry.py +188 -0
  99. moiryx-0.1.0a1/tests/test_runtime.py +325 -0
  100. moiryx-0.1.0a1/tests/test_structured_output.py +405 -0
  101. moiryx-0.1.0a1/tests/test_tool_executor.py +105 -0
  102. moiryx-0.1.0a1/tests/test_tool_preflight.py +331 -0
  103. moiryx-0.1.0a1/tests/test_tool_serialization.py +83 -0
  104. moiryx-0.1.0a1/tests/test_tools.py +147 -0
  105. 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.
@@ -0,0 +1,3 @@
1
+ include CHANGELOG.md
2
+ recursive-include docs *.md
3
+ recursive-include examples *.md *.py *.yaml
@@ -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`.
@@ -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.