muscles 1.0.0__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.
- muscles-1.0.0/PKG-INFO +420 -0
- muscles-1.0.0/README.md +388 -0
- muscles-1.0.0/pyproject.toml +37 -0
- muscles-1.0.0/src/muscles/__init__.py +6 -0
- muscles-1.0.0/src/muscles/core/Makefile +26 -0
- muscles-1.0.0/src/muscles/core/__about__.py +6 -0
- muscles-1.0.0/src/muscles/core/__init__.py +304 -0
- muscles-1.0.0/src/muscles/core/core/__init__.py +136 -0
- muscles-1.0.0/src/muscles/core/core/actions.py +452 -0
- muscles-1.0.0/src/muscles/core/core/application_context.py +19 -0
- muscles-1.0.0/src/muscles/core/core/application_proxy.py +23 -0
- muscles-1.0.0/src/muscles/core/core/backend_pipeline.py +246 -0
- muscles-1.0.0/src/muscles/core/core/configure.py +513 -0
- muscles-1.0.0/src/muscles/core/core/container.py +82 -0
- muscles-1.0.0/src/muscles/core/core/context.py +241 -0
- muscles-1.0.0/src/muscles/core/core/dependency.py +298 -0
- muscles-1.0.0/src/muscles/core/core/generator.py +43 -0
- muscles-1.0.0/src/muscles/core/core/heandler.py +19 -0
- muscles-1.0.0/src/muscles/core/core/inspection.py +187 -0
- muscles-1.0.0/src/muscles/core/core/instance.py +558 -0
- muscles-1.0.0/src/muscles/core/core/module_loader.py +22 -0
- muscles-1.0.0/src/muscles/core/core/package_context.py +19 -0
- muscles-1.0.0/src/muscles/core/core/package_lifecycle.py +451 -0
- muscles-1.0.0/src/muscles/core/core/registry.py +122 -0
- muscles-1.0.0/src/muscles/core/core/runtime_mode.py +63 -0
- muscles-1.0.0/src/muscles/core/core/self.py +28 -0
- muscles-1.0.0/src/muscles/core/core/storage.py +284 -0
- muscles-1.0.0/src/muscles/core/cors.py +62 -0
- muscles-1.0.0/src/muscles/core/errors.py +83 -0
- muscles-1.0.0/src/muscles/core/exceptions.py +79 -0
- muscles-1.0.0/src/muscles/core/lifecycle.py +2 -0
- muscles-1.0.0/src/muscles/core/response/__init__.py +17 -0
- muscles-1.0.0/src/muscles/core/response/base.py +28 -0
- muscles-1.0.0/src/muscles/core/response/bytes.py +19 -0
- muscles-1.0.0/src/muscles/core/response/file.py +31 -0
- muscles-1.0.0/src/muscles/core/response/html.py +13 -0
- muscles-1.0.0/src/muscles/core/response/json.py +17 -0
- muscles-1.0.0/src/muscles/core/response/no_content.py +13 -0
- muscles-1.0.0/src/muscles/core/response/normalize.py +40 -0
- muscles-1.0.0/src/muscles/core/route_contract.py +79 -0
- muscles-1.0.0/src/muscles/core/schema/__init__.py +99 -0
- muscles-1.0.0/src/muscles/core/schema/collection.py +15 -0
- muscles-1.0.0/src/muscles/core/schema/column.py +192 -0
- muscles-1.0.0/src/muscles/core/schema/exception.py +8 -0
- muscles-1.0.0/src/muscles/core/schema/field.py +681 -0
- muscles-1.0.0/src/muscles/core/schema/group.py +43 -0
- muscles-1.0.0/src/muscles/core/schema/itinerary.py +980 -0
- muscles-1.0.0/src/muscles/core/schema/model.py +251 -0
- muscles-1.0.0/src/muscles/core/schema/openapi.py +126 -0
- muscles-1.0.0/src/muscles/core/schema/parameters.py +105 -0
- muscles-1.0.0/src/muscles/core/schema/pydantic_bridge.py +60 -0
- muscles-1.0.0/src/muscles/core/schema/request.py +167 -0
- muscles-1.0.0/src/muscles/core/schema/response.py +133 -0
- muscles-1.0.0/src/muscles/core/schema/schema.py +62 -0
- muscles-1.0.0/src/muscles/core/schema/security.py +152 -0
- muscles-1.0.0/src/muscles/core/schema/swagger.py +96 -0
- muscles-1.0.0/src/muscles/core/schema/user.py +58 -0
- muscles-1.0.0/src/muscles/core/schema/value_object.py +245 -0
- muscles-1.0.0/src/muscles/core/utils/__init__.py +8 -0
- muscles-1.0.0/src/muscles/core/utils/watchdog.py +69 -0
- muscles-1.0.0/src/muscles/lifecycle.py +1 -0
muscles-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: muscles
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Muscles framework core
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Denis B.
|
|
7
|
+
Author-email: denis@butko.info
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
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
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Requires-Dist: dnspython (==2.6.1)
|
|
18
|
+
Requires-Dist: jsonschema (>=4.18,<5)
|
|
19
|
+
Requires-Dist: pyyaml (>=6.0.0,<6.1.0)
|
|
20
|
+
Requires-Dist: watchdog (>=4.0.0,<5.0.0)
|
|
21
|
+
Project-URL: Documentation, https://github.com/butkoden/muscles#readme
|
|
22
|
+
Project-URL: Homepage, https://github.com/butkoden/muscles
|
|
23
|
+
Project-URL: PyPI, https://pypi.org/project/muscles/
|
|
24
|
+
Project-URL: Repository, https://github.com/butkoden/muscles
|
|
25
|
+
Project-URL: Releases, https://github.com/butkoden/muscles/releases
|
|
26
|
+
Project-URL: muscles-data, https://pypi.org/project/muscles-data/
|
|
27
|
+
Project-URL: muscles-ai, https://pypi.org/project/muscles-ai/
|
|
28
|
+
Project-URL: muscles-documents, https://pypi.org/project/muscles-documents/
|
|
29
|
+
Project-URL: muscles-benchmarks, https://pypi.org/project/muscles-benchmarks/
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Muscles Core (Release Candidate)
|
|
33
|
+
|
|
34
|
+
Muscles is the release-candidate framework core. The core package contains the
|
|
35
|
+
application lifecycle, configuration, shared schema objects, routing tree and
|
|
36
|
+
strategy interface. HTTP, ASGI and CLI packages reuse these primitives instead
|
|
37
|
+
of implementing their own framework model.
|
|
38
|
+
|
|
39
|
+
## Application Shape
|
|
40
|
+
|
|
41
|
+
`muscles-cli`, `muscles-asgi` and `muscles-wsgi` use the same core application
|
|
42
|
+
model. The selected runtime supplies a strategy; `Context` dispatches through
|
|
43
|
+
it:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from muscles import ApplicationMeta, Configurator, Context
|
|
47
|
+
from muscles.asgi import AsgiStrategy
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class App(metaclass=ApplicationMeta):
|
|
51
|
+
config = Configurator(obj={"main": {"HOST": "0.0.0.0", "PORT": "8080"}})
|
|
52
|
+
context = Context(AsgiStrategy, params={})
|
|
53
|
+
|
|
54
|
+
def run(self, *args):
|
|
55
|
+
return self.context.execute(*args, shutup=True)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For WSGI or CLI, use the corresponding strategy from `muscles.wsgi` or
|
|
59
|
+
`muscles.cli`. The runtime-specific package owns route declaration and server
|
|
60
|
+
startup; core does not require an extra `wsgi_runtime()` or `runtime.rest_api()`
|
|
61
|
+
factory. See [docs/golden-path.md](docs/golden-path.md).
|
|
62
|
+
|
|
63
|
+
## Packages
|
|
64
|
+
|
|
65
|
+
- `muscles` - core classes, schemas, routing and application metaclass.
|
|
66
|
+
- `muscles-wsgi` - WSGI runtime, pages, REST API and Swagger UI.
|
|
67
|
+
- `muscles-asgi` - ASGI runtime with the same routing and REST API model.
|
|
68
|
+
- `muscles-cli` - console command strategy built on the same route/group idea.
|
|
69
|
+
- `muscles-jsonrpc` - JSON-RPC projection for core actions.
|
|
70
|
+
- `muscles-sse` - Server-Sent Events projection for streaming actions.
|
|
71
|
+
- `muscles-mcp` - Model Context Protocol projection for AI tools.
|
|
72
|
+
- `muscles-data` - typed data ports, resource runtime and migration registry.
|
|
73
|
+
- `muscles-data-sql` - native SQL connections, repositories and SQL migrations.
|
|
74
|
+
- `muscles-data-sqlalchemy` - SQLAlchemy adapter for `muscles-data` resources.
|
|
75
|
+
- `muscles-otel` - optional lifecycle telemetry hooks and tracer provider.
|
|
76
|
+
- `muscles-documents` - document loading, parsing, chunking and sync actions.
|
|
77
|
+
- `muscles-ai` - read-only AI/RAG actions and runtime contracts.
|
|
78
|
+
- `muscles-benchmarks` - benchmark and regression suite for the ecosystem.
|
|
79
|
+
- `muscular-example` - layered learning examples for Muscles applications.
|
|
80
|
+
|
|
81
|
+
## How Packages Fit Together
|
|
82
|
+
|
|
83
|
+
`muscles` is the core contract. Runtime packages such as `muscles-asgi`,
|
|
84
|
+
`muscles-wsgi` and `muscles-cli` project the same app model into a concrete
|
|
85
|
+
transport. Extension packages such as `muscles-otel`, `muscles-data`,
|
|
86
|
+
`muscles-data-sql`, `muscles-documents` and `muscles-ai` add services, actions and diagnostics
|
|
87
|
+
through the package lifecycle:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from muscles import install_package, inspect_application, doctor_application
|
|
91
|
+
from muscles.otel import OtelPackage
|
|
92
|
+
|
|
93
|
+
app = App()
|
|
94
|
+
tracer = install_package(app, {"enabled": True}, OtelPackage())
|
|
95
|
+
|
|
96
|
+
contract = inspect_application(app)
|
|
97
|
+
doctor = doctor_application(app)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use the `DependencyContainer` for live objects that app code resolves at
|
|
101
|
+
runtime. Use `ApplicationRegistry` for metadata that tools can inspect: routes,
|
|
102
|
+
actions, package capabilities, doctor checks and generator providers. In plain
|
|
103
|
+
terms: the container is for running the app, the registry is for understanding
|
|
104
|
+
the app.
|
|
105
|
+
|
|
106
|
+
More detail: [docs/package-lifecycle.md](docs/package-lifecycle.md).
|
|
107
|
+
|
|
108
|
+
## Repository Map
|
|
109
|
+
|
|
110
|
+
Core and runtimes:
|
|
111
|
+
|
|
112
|
+
- [`muscles`](https://github.com/butkoden/muscles) - framework core and canonical documentation.
|
|
113
|
+
- [`muscles-asgi`](https://github.com/butkoden/muscles-asgi) - ASGI HTTP/API runtime.
|
|
114
|
+
- [`muscles-wsgi`](https://github.com/butkoden/muscles-wsgi) - WSGI HTTP/API runtime.
|
|
115
|
+
- [`muscles-cli`](https://github.com/butkoden/muscles-cli) - CLI runtime, project scaffolding and inspection commands.
|
|
116
|
+
|
|
117
|
+
Protocol projections:
|
|
118
|
+
|
|
119
|
+
- [`muscles-jsonrpc`](https://github.com/butkoden/muscles-jsonrpc) - JSON-RPC 2.0 projection over actions.
|
|
120
|
+
- [`muscles-sse`](https://github.com/butkoden/muscles-sse) - SSE delivery for `StreamResult` action output.
|
|
121
|
+
- [`muscles-mcp`](https://github.com/butkoden/muscles-mcp) - MCP tools/resources generated from Muscles contracts.
|
|
122
|
+
|
|
123
|
+
Framework extensions:
|
|
124
|
+
|
|
125
|
+
- [`muscles-data`](https://github.com/butkoden/muscles-data) - typed ports, resource runtime, policies and migration registry.
|
|
126
|
+
- [`muscles-data-sql`](https://github.com/butkoden/muscles-data-sql) - native SQL connections and migrations; the repository is `muscles-data-sql`.
|
|
127
|
+
- [`muscles-data-sqlalchemy`](https://github.com/butkoden/muscles-data-sqlalchemy) - SQLAlchemy-backed `muscles-data` resource adapter.
|
|
128
|
+
- [`muscles-migrations`](https://github.com/butkoden/muscles-migrations) - migration compatibility imports.
|
|
129
|
+
- [`muscles-permissions`](https://github.com/butkoden/muscles-permissions) - permission catalog and policy contracts.
|
|
130
|
+
- [`muscles-otel`](https://github.com/butkoden/muscles-otel) - optional lifecycle telemetry hooks for package, strategy and action execution.
|
|
131
|
+
- [`muscles-documents`](https://github.com/butkoden/muscles-documents) - document source ingestion, parsing, chunking and sync planning.
|
|
132
|
+
- [`muscles-ai`](https://github.com/butkoden/muscles-ai) - AI/RAG actions that can use document and data integrations.
|
|
133
|
+
|
|
134
|
+
Support repositories:
|
|
135
|
+
|
|
136
|
+
- [`muscles-benchmarks`](https://github.com/butkoden/muscles-benchmarks) - golden-path benchmarks and regression checks.
|
|
137
|
+
- [`muscular-example`](https://github.com/butkoden/muscular-example) - layered example application.
|
|
138
|
+
- [`muscles-landing`](https://github.com/butkoden/muscles-landing) - product site/application built on Muscles.
|
|
139
|
+
- [`muscular-asgi`](https://github.com/butkoden/muscular-asgi) - deprecated ASGI compatibility package.
|
|
140
|
+
|
|
141
|
+
## Installation
|
|
142
|
+
|
|
143
|
+
The release-candidate packages are distributed through PyPI. Install the core
|
|
144
|
+
framework with:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pip install "muscles>=1.0.0rc5,<2.0.0"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
For local development, source checkouts and the full ecosystem matrix, see
|
|
151
|
+
[docs/installation.md](docs/installation.md).
|
|
152
|
+
|
|
153
|
+
Installation matrix:
|
|
154
|
+
|
|
155
|
+
- core only: `pip install "muscles>=1.0.0rc5,<2.0.0"`
|
|
156
|
+
- ASGI runtime: `pip install muscles-asgi`
|
|
157
|
+
- WSGI runtime: `pip install muscles-wsgi`
|
|
158
|
+
- CLI tooling: `pip install muscles-cli`
|
|
159
|
+
- JSON-RPC projection: install `muscles-jsonrpc` from PyPI
|
|
160
|
+
- SSE projection: install `muscles-sse` from PyPI
|
|
161
|
+
- MCP projection: `pip install muscles-mcp`
|
|
162
|
+
- Data contracts: `pip install muscles-data`
|
|
163
|
+
- Native SQL: `pip install muscles-data-sql`
|
|
164
|
+
- SQLAlchemy resource adapter: `pip install muscles-data-sqlalchemy`
|
|
165
|
+
- OTel extension: `pip install muscles-otel`
|
|
166
|
+
- documents extension: `pip install muscles-documents`
|
|
167
|
+
- AI extension: `pip install muscles-ai`
|
|
168
|
+
- full/dev (source checkouts): clone all repositories and use `PYTHONPATH` with sibling `src` paths.
|
|
169
|
+
|
|
170
|
+
More detail: [docs/installation.md](docs/installation.md).
|
|
171
|
+
Repository ownership map: [docs/repositories.md](docs/repositories.md).
|
|
172
|
+
|
|
173
|
+
Core documentation:
|
|
174
|
+
|
|
175
|
+
- English: [docs/doc.md](docs/doc.md)
|
|
176
|
+
- Russian: [docs/doc.ru.md](docs/doc.ru.md)
|
|
177
|
+
- Framework package lifecycle: [docs/package-lifecycle.md](docs/package-lifecycle.md)
|
|
178
|
+
- Public API reference: [docs/public-api.md](docs/public-api.md)
|
|
179
|
+
- Справочник публичного API: [docs/public-api.ru.md](docs/public-api.ru.md)
|
|
180
|
+
|
|
181
|
+
## Naming
|
|
182
|
+
|
|
183
|
+
Use `Muscles` as the public framework name. Keep `muscles` for the Python
|
|
184
|
+
package/import name and `muscles-*` for official package identifiers.
|
|
185
|
+
|
|
186
|
+
More detail: [docs/naming.md](docs/naming.md).
|
|
187
|
+
|
|
188
|
+
## Positioning
|
|
189
|
+
|
|
190
|
+
Muscles is a framework for a **single application model** that can be projected
|
|
191
|
+
into multiple runtimes (WSGI, ASGI, CLI). It is not optimized for the shortest
|
|
192
|
+
"one endpoint in five minutes" path. It is optimized for consistency: the same
|
|
193
|
+
schemas, routing tree, lifecycle hooks and rules metadata stay reusable across
|
|
194
|
+
transports.
|
|
195
|
+
|
|
196
|
+
### When To Use Muscles
|
|
197
|
+
|
|
198
|
+
- you need Web/API/CLI to share one domain model and metadata;
|
|
199
|
+
- you want routing, schemas and rules to be declared once and reused;
|
|
200
|
+
- you want AI-assisted changes to follow a stable project structure.
|
|
201
|
+
|
|
202
|
+
### When Not To Use Muscles
|
|
203
|
+
|
|
204
|
+
- you only need a tiny HTTP service with no shared model beyond one runtime;
|
|
205
|
+
- your team prefers runtime-specific framework patterns per interface;
|
|
206
|
+
- you need mature batteries-included admin/ORM stack out of the box.
|
|
207
|
+
|
|
208
|
+
### FastAPI Relation
|
|
209
|
+
|
|
210
|
+
FastAPI is a strong choice for API-first delivery speed. Muscles targets a
|
|
211
|
+
different trade-off: one reusable model for API, pages and CLI workflows. These
|
|
212
|
+
tools can coexist in a portfolio; Muscles should not claim production maturity
|
|
213
|
+
or performance superiority without benchmark evidence.
|
|
214
|
+
|
|
215
|
+
More detail: [docs/positioning.md](docs/positioning.md).
|
|
216
|
+
The current project structure and conventions are documented in
|
|
217
|
+
[docs/golden-path.md](docs/golden-path.md).
|
|
218
|
+
|
|
219
|
+
## Golden Path Structure
|
|
220
|
+
|
|
221
|
+
Use the official Muscles project structure as the default style for people and
|
|
222
|
+
AI assistants:
|
|
223
|
+
|
|
224
|
+
```text
|
|
225
|
+
app/
|
|
226
|
+
application.py
|
|
227
|
+
config.py
|
|
228
|
+
domain/
|
|
229
|
+
schemas/
|
|
230
|
+
web/
|
|
231
|
+
api/
|
|
232
|
+
cli/
|
|
233
|
+
rules/
|
|
234
|
+
templates/
|
|
235
|
+
static/
|
|
236
|
+
tests/
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
More detail: [docs/golden-path.md](docs/golden-path.md).
|
|
240
|
+
Tutorial: [docs/golden-tutorial.md](docs/golden-tutorial.md).
|
|
241
|
+
|
|
242
|
+
## Application Shape
|
|
243
|
+
|
|
244
|
+
An application is usually a class with `ApplicationMeta`, a `Configurator` and a
|
|
245
|
+
`Context` bound to a strategy:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
from muscles import ApplicationMeta, Configurator, Context
|
|
249
|
+
from muscles.wsgi import WsgiStrategy
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class App(metaclass=ApplicationMeta):
|
|
253
|
+
config = Configurator(obj={"main": {"DEBUG": True}})
|
|
254
|
+
context = Context(WsgiStrategy, params={})
|
|
255
|
+
|
|
256
|
+
def run(self, *args):
|
|
257
|
+
return self.context.execute(*args, shutup=True)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`Context` owns lifecycle hooks and strategy execution. Keep it instance-local:
|
|
261
|
+
lists of hooks and params must not be shared between app instances.
|
|
262
|
+
|
|
263
|
+
## Routing
|
|
264
|
+
|
|
265
|
+
The core routing tree lives in `muscles.core.schema.itinerary`. It is used as a
|
|
266
|
+
generic structure for:
|
|
267
|
+
|
|
268
|
+
- page routes;
|
|
269
|
+
- REST API controllers/actions;
|
|
270
|
+
- CLI groups and commands;
|
|
271
|
+
- URL building and reverse lookup.
|
|
272
|
+
|
|
273
|
+
The current implementation indexes routes by `(path, method)` and keeps a match
|
|
274
|
+
cache. Static routes are checked before dynamic routes, so common paths avoid
|
|
275
|
+
walking the whole tree. Duplicate route registration is idempotent by route
|
|
276
|
+
name, which keeps repeated imports from making routing slower.
|
|
277
|
+
|
|
278
|
+
More detail: [docs/architecture.md](docs/architecture.md).
|
|
279
|
+
|
|
280
|
+
Backend primitives such as route groups, guards, middleware, exception mapping,
|
|
281
|
+
Bearer JWT auth, CORS and response helpers are documented in
|
|
282
|
+
[docs/backend-framework.md](docs/backend-framework.md).
|
|
283
|
+
|
|
284
|
+
`BackendPipeline` is the shared execution contract for runtime adapters. It
|
|
285
|
+
handles typed handler arguments, dependency resolution, guards, route-level
|
|
286
|
+
security and middleware, while ASGI/WSGI keep protocol parsing and response
|
|
287
|
+
serialization. `DependencyContainer` adds explicit `app`, `request` and
|
|
288
|
+
`transient` scopes for integrations that need more structure than legacy global
|
|
289
|
+
dependency registration.
|
|
290
|
+
|
|
291
|
+
## Schemas And OpenAPI
|
|
292
|
+
|
|
293
|
+
Schema classes (`Model`, `Collection`, `Column`, `String`, `Key`, request and
|
|
294
|
+
response bodies) describe data once and can be reused by runtime strategies. The
|
|
295
|
+
WSGI and ASGI packages read these structures to build OpenAPI automatically.
|
|
296
|
+
|
|
297
|
+
More detail: [docs/schema.md](docs/schema.md).
|
|
298
|
+
|
|
299
|
+
## Rules
|
|
300
|
+
|
|
301
|
+
Decorators such as `@rules` are intended to attach access control and metadata
|
|
302
|
+
to the same objects that routing uses. The important design rule is that a route,
|
|
303
|
+
API action or command should carry its permissions and properties together with
|
|
304
|
+
its schema, so every strategy can enforce or expose them consistently.
|
|
305
|
+
|
|
306
|
+
Practical examples: [docs/value-objects-rules.md](docs/value-objects-rules.md).
|
|
307
|
+
|
|
308
|
+
## Actions And Protocol Projections
|
|
309
|
+
|
|
310
|
+
Actions are first-class application contracts in core. Declare an action once
|
|
311
|
+
on a Muscles application instance, then let HTTP, CLI, MCP, JSON-RPC, SSE or
|
|
312
|
+
other protocol projections call it through the same dispatcher:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from muscles import ApplicationMeta, Context, ActionDispatcher
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class BookingApp(metaclass=ApplicationMeta):
|
|
319
|
+
context = Context(MyStrategy, transport="app", params={})
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
app = BookingApp()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@app.action(
|
|
326
|
+
name="bookings.create",
|
|
327
|
+
description="Create a booking request",
|
|
328
|
+
input_schema={
|
|
329
|
+
"type": "object",
|
|
330
|
+
"properties": {
|
|
331
|
+
"title": {"type": "string"},
|
|
332
|
+
"guest_count": {"type": "integer"},
|
|
333
|
+
},
|
|
334
|
+
"required": ["title"],
|
|
335
|
+
},
|
|
336
|
+
rules=["bookings.public_create"],
|
|
337
|
+
transports=["http", "cli", "mcp"],
|
|
338
|
+
)
|
|
339
|
+
def create_booking(payload, context):
|
|
340
|
+
return {"title": payload["title"], "transport": context.transport}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
result = ActionDispatcher(app).execute(
|
|
344
|
+
"bookings.create",
|
|
345
|
+
{"title": "Discovery call"},
|
|
346
|
+
transport="mcp",
|
|
347
|
+
)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`inspect_application(app)` is the source of truth for discovery. Protocol
|
|
351
|
+
packages should build tools, methods, routes or stream metadata from this
|
|
352
|
+
contract and send execution back to `ActionDispatcher`. They should not keep a
|
|
353
|
+
second validation, permissions or business model.
|
|
354
|
+
|
|
355
|
+
The action registry is application-scoped. New protocol projections should avoid
|
|
356
|
+
mutable module-level registries as their source of truth, because those leak
|
|
357
|
+
state between app instances and tests.
|
|
358
|
+
|
|
359
|
+
### Stream Results
|
|
360
|
+
|
|
361
|
+
English:
|
|
362
|
+
|
|
363
|
+
Streaming actions return `StreamResult(source=...)` with business
|
|
364
|
+
`StreamEvent(type="progress" | "log" | "result" | "error", data=...)` items.
|
|
365
|
+
The core contract is protocol-neutral: SSE heartbeat, MCP envelopes, JSON-RPC
|
|
366
|
+
notifications and CLI progress lines are transport projections over the same
|
|
367
|
+
events. Transports should preserve backpressure with bounded delivery and call
|
|
368
|
+
`close()` on disconnect when the source supports it. Long-blocking sources must
|
|
369
|
+
cooperate by unblocking the active `next()` call after `close()`.
|
|
370
|
+
|
|
371
|
+
Русский:
|
|
372
|
+
|
|
373
|
+
Streaming actions возвращают `StreamResult(source=...)` с business events
|
|
374
|
+
`StreamEvent(type="progress" | "log" | "result" | "error", data=...)`.
|
|
375
|
+
Core contract не зависит от протокола: SSE heartbeat, MCP envelopes,
|
|
376
|
+
JSON-RPC notifications и CLI progress lines являются transport-проекциями одних
|
|
377
|
+
и тех же событий. Transport должен сохранять backpressure через bounded delivery
|
|
378
|
+
и вызывать `close()` при disconnect, если source это поддерживает. Долгие
|
|
379
|
+
blocking sources должны быть cooperative: `close()` должен разблокировать
|
|
380
|
+
активный `next()`.
|
|
381
|
+
|
|
382
|
+
User docs:
|
|
383
|
+
|
|
384
|
+
- English: [docs/action-contract.en.md](docs/action-contract.en.md)
|
|
385
|
+
- Русский: [docs/action-contract.ru.md](docs/action-contract.ru.md)
|
|
386
|
+
|
|
387
|
+
## AI Workflow
|
|
388
|
+
|
|
389
|
+
Official AI-oriented instructions:
|
|
390
|
+
|
|
391
|
+
- [docs/ai/AGENTS.md](docs/ai/AGENTS.md)
|
|
392
|
+
- [docs/ai/cursor-rules.md](docs/ai/cursor-rules.md)
|
|
393
|
+
- [docs/ai/copilot-instructions.md](docs/ai/copilot-instructions.md)
|
|
394
|
+
|
|
395
|
+
## Production Deploy
|
|
396
|
+
|
|
397
|
+
Umbrella guide: [docs/production-deploy.md](docs/production-deploy.md).
|
|
398
|
+
|
|
399
|
+
## Benchmarks
|
|
400
|
+
|
|
401
|
+
Benchmark docs: [docs/benchmarks.md](docs/benchmarks.md).
|
|
402
|
+
|
|
403
|
+
## Contributing
|
|
404
|
+
|
|
405
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
406
|
+
|
|
407
|
+
## Development
|
|
408
|
+
|
|
409
|
+
Run tests from each repository with its local `src` on `PYTHONPATH`:
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
PYTHONPATH=src python -m pytest -q
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
When testing an integration app that uses all packages from sibling checkouts:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
PYTHONPATH=../muscles/src:../muscles-wsgi/src:../muscles-asgi/src:../muscles-cli/src:../muscles-jsonrpc/src:../muscles-sse/src:../muscles-mcp/src:../muscles-data/src:../muscles-data-sql/src:../muscles-data-sqlalchemy/src:../muscles-otel/src:../muscles-documents/src:../muscles-ai/src python -m pytest -q
|
|
419
|
+
```
|
|
420
|
+
|