llm-workflow-router 0.3.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.
- llm_workflow_router-0.3.0/LICENSE +21 -0
- llm_workflow_router-0.3.0/PKG-INFO +409 -0
- llm_workflow_router-0.3.0/README.md +376 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/PKG-INFO +409 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/SOURCES.txt +38 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/dependency_links.txt +1 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/entry_points.txt +2 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/requires.txt +16 -0
- llm_workflow_router-0.3.0/llm_workflow_router.egg-info/top_level.txt +1 -0
- llm_workflow_router-0.3.0/pyproject.toml +73 -0
- llm_workflow_router-0.3.0/router/__init__.py +5 -0
- llm_workflow_router-0.3.0/router/_version.py +7 -0
- llm_workflow_router-0.3.0/router/cli.py +225 -0
- llm_workflow_router-0.3.0/router/engine.py +80 -0
- llm_workflow_router-0.3.0/router/graphing.py +123 -0
- llm_workflow_router-0.3.0/router/integrations/__init__.py +0 -0
- llm_workflow_router-0.3.0/router/integrations/openai_agents.py +172 -0
- llm_workflow_router-0.3.0/router/logging/schema.py +82 -0
- llm_workflow_router-0.3.0/router/models/config.py +25 -0
- llm_workflow_router-0.3.0/router/models/enums.py +16 -0
- llm_workflow_router-0.3.0/router/models/metadata.py +16 -0
- llm_workflow_router-0.3.0/router/models/result.py +14 -0
- llm_workflow_router-0.3.0/router/observability/__init__.py +0 -0
- llm_workflow_router-0.3.0/router/observability/otel.py +247 -0
- llm_workflow_router-0.3.0/router/py.typed +0 -0
- llm_workflow_router-0.3.0/router/session.py +170 -0
- llm_workflow_router-0.3.0/router/validation/config_validator.py +48 -0
- llm_workflow_router-0.3.0/router/validation/transition_validator.py +333 -0
- llm_workflow_router-0.3.0/setup.cfg +4 -0
- llm_workflow_router-0.3.0/tests/test_cli.py +186 -0
- llm_workflow_router-0.3.0/tests/test_config_validation.py +43 -0
- llm_workflow_router-0.3.0/tests/test_engine.py +21 -0
- llm_workflow_router-0.3.0/tests/test_entrypoints.py +100 -0
- llm_workflow_router-0.3.0/tests/test_graphing.py +68 -0
- llm_workflow_router-0.3.0/tests/test_logging_schema.py +109 -0
- llm_workflow_router-0.3.0/tests/test_observability_and_analyze.py +75 -0
- llm_workflow_router-0.3.0/tests/test_openai_agents_adapter.py +117 -0
- llm_workflow_router-0.3.0/tests/test_otel_spans.py +167 -0
- llm_workflow_router-0.3.0/tests/test_session.py +122 -0
- llm_workflow_router-0.3.0/tests/test_transitions.py +136 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Doby Baxter
|
|
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,409 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: llm-workflow-router
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Deterministic workflow topology enforcement for LLM-powered systems.
|
|
5
|
+
Author: Doby Baxter
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: llm,agents,workflow,topology,validation,determinism
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: PyYAML>=6.0.1
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
23
|
+
Requires-Dist: coverage>=7.0.0; extra == "dev"
|
|
24
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
25
|
+
Requires-Dist: mypy>=1.8.0; extra == "dev"
|
|
26
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == "dev"
|
|
27
|
+
Requires-Dist: opentelemetry-sdk>=1.20; extra == "dev"
|
|
28
|
+
Provides-Extra: otel
|
|
29
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == "otel"
|
|
30
|
+
Provides-Extra: openai-agents
|
|
31
|
+
Requires-Dist: openai-agents>=0.18; extra == "openai-agents"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# LLM Workflow Router
|
|
35
|
+
|
|
36
|
+
Deterministic workflow topology enforcement for LLM-powered systems.
|
|
37
|
+
|
|
38
|
+
LLM Workflow Router is a stateless middleware engine designed to enforce explicit execution topology in AI systems that rely on large language models. It evaluates structured interaction metadata against strictly declared workflow rules and returns a terminal state.
|
|
39
|
+
|
|
40
|
+
It controls structure — not content.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Overview
|
|
45
|
+
|
|
46
|
+
Modern LLM-driven systems frequently suffer from:
|
|
47
|
+
|
|
48
|
+
- Recursive tool invocation loops
|
|
49
|
+
- Circular container transitions
|
|
50
|
+
- Cross-container contamination
|
|
51
|
+
- Implicit fallback behavior
|
|
52
|
+
- Unbounded workflow escalation
|
|
53
|
+
- Inconsistent refusal logic
|
|
54
|
+
|
|
55
|
+
Most mitigation strategies limit volume (timeouts, max tool calls, retries).
|
|
56
|
+
LLM Workflow Router enforces topology explicitly.
|
|
57
|
+
|
|
58
|
+
The engine evaluates structured interaction metadata and returns one of three terminal states:
|
|
59
|
+
|
|
60
|
+
- PROCEED
|
|
61
|
+
- REFUSE
|
|
62
|
+
- PAUSE
|
|
63
|
+
|
|
64
|
+
No content inspection.
|
|
65
|
+
No moderation.
|
|
66
|
+
No orchestration.
|
|
67
|
+
No mutation of input.
|
|
68
|
+
|
|
69
|
+
Only structural enforcement.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Core Design Principles
|
|
74
|
+
|
|
75
|
+
- Deterministic evaluation
|
|
76
|
+
- Stateless per evaluation
|
|
77
|
+
- Metadata-only inspection
|
|
78
|
+
- Explicit transitions only
|
|
79
|
+
- Static configuration (v1)
|
|
80
|
+
- Strict validation at load time
|
|
81
|
+
- No silent fallback
|
|
82
|
+
- Host application retains execution control
|
|
83
|
+
|
|
84
|
+
Same input → same output.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Architecture
|
|
89
|
+
|
|
90
|
+
Application
|
|
91
|
+
↓
|
|
92
|
+
WorkflowEngine.evaluate(metadata)
|
|
93
|
+
↓
|
|
94
|
+
[ PROCEED | REFUSE | PAUSE ]
|
|
95
|
+
↓
|
|
96
|
+
Application decides next action
|
|
97
|
+
|
|
98
|
+
The router does not:
|
|
99
|
+
|
|
100
|
+
- Execute tools
|
|
101
|
+
- Retry calls
|
|
102
|
+
- Modify prompts
|
|
103
|
+
- Orchestrate sessions
|
|
104
|
+
|
|
105
|
+
It enforces topology and returns a decision.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
Workflow rules are defined using static YAML configuration.
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
entrypoints:
|
|
115
|
+
- entry
|
|
116
|
+
|
|
117
|
+
containers:
|
|
118
|
+
entry:
|
|
119
|
+
allow_transitions:
|
|
120
|
+
- support
|
|
121
|
+
- REFUSE
|
|
122
|
+
allow_reentry: false
|
|
123
|
+
max_invocations: 2
|
|
124
|
+
|
|
125
|
+
support:
|
|
126
|
+
allow_transitions:
|
|
127
|
+
- faq
|
|
128
|
+
- REFUSE
|
|
129
|
+
allow_reentry: false
|
|
130
|
+
max_invocations: 3
|
|
131
|
+
|
|
132
|
+
faq:
|
|
133
|
+
allow_transitions:
|
|
134
|
+
- REFUSE
|
|
135
|
+
allow_reentry: true
|
|
136
|
+
max_invocations: 5
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Each container defines:
|
|
140
|
+
|
|
141
|
+
- Explicit allowed transitions
|
|
142
|
+
- Whether re-entry is permitted
|
|
143
|
+
- Maximum invocation depth
|
|
144
|
+
|
|
145
|
+
Implicit transitions are not allowed.
|
|
146
|
+
|
|
147
|
+
### Entrypoints
|
|
148
|
+
|
|
149
|
+
The optional top-level `entrypoints` list declares the authoritative root
|
|
150
|
+
containers. A workflow may have several roots — a support flow, a sales flow,
|
|
151
|
+
and an FAQ flow can each start in their own container — so `entrypoints` is a
|
|
152
|
+
list. When declared, any indegree-0 container that is *not* listed is treated
|
|
153
|
+
as an orphan and rejected. If `entrypoints` is omitted, roots are inferred from
|
|
154
|
+
graph shape (indegree 0) for backward compatibility, and an ambiguity warning
|
|
155
|
+
is raised when more than one root is inferred. See
|
|
156
|
+
`examples/multi_entry_config.yaml`.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Topology Validation
|
|
161
|
+
|
|
162
|
+
At configuration load time, the router performs strict validation:
|
|
163
|
+
|
|
164
|
+
- Invalid transition targets
|
|
165
|
+
- Unknown containers
|
|
166
|
+
- Unknown declared entrypoints
|
|
167
|
+
- Dead-end containers
|
|
168
|
+
- Missing entry points
|
|
169
|
+
- Orphan containers (indegree 0, not a declared entrypoint)
|
|
170
|
+
- Unreachable containers
|
|
171
|
+
- Self-transition contradictions
|
|
172
|
+
- Cycle detection
|
|
173
|
+
- Re-entry safety enforcement
|
|
174
|
+
|
|
175
|
+
Configuration errors raise exceptions immediately.
|
|
176
|
+
|
|
177
|
+
Fail loudly at load time.
|
|
178
|
+
Never fail silently at runtime.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Runtime Evaluation
|
|
183
|
+
|
|
184
|
+
The engine evaluates an immutable metadata structure:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
InteractionMetadata:
|
|
188
|
+
container: str
|
|
189
|
+
previous_state: InteractionState
|
|
190
|
+
transition_history: List[str]
|
|
191
|
+
invocation_depth: Dict[str, int]
|
|
192
|
+
requested_action: str
|
|
193
|
+
trace_id: Optional[str]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
EvaluationResult:
|
|
200
|
+
state: InteractionState
|
|
201
|
+
container: str
|
|
202
|
+
reason: Optional[ReasonCode]
|
|
203
|
+
trace_id: Optional[str]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
No exceptions during normal evaluation.
|
|
207
|
+
Only terminal states are returned.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## CLI Usage
|
|
212
|
+
|
|
213
|
+
Install:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pip install llm-workflow-router
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Validate configuration:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
llm-router validate config.yaml
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Analyze topology:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
llm-router analyze config.yaml
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Evaluate metadata:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
llm-router run --config config.yaml --metadata metadata.json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
Render the topology as a diagram:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
llm-router graph config.yaml # Mermaid (paste into any Mermaid renderer)
|
|
243
|
+
llm-router graph config.yaml --format dot # Graphviz DOT
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Like `analyze`, `graph` renders broken topologies too — unknown transition
|
|
247
|
+
targets are drawn and flagged, which is exactly what you want while debugging
|
|
248
|
+
a config.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Session Layer (optional)
|
|
253
|
+
|
|
254
|
+
The engine is stateless by design: every `evaluate()` receives a complete
|
|
255
|
+
metadata snapshot. If you'd rather not do that bookkeeping yourself,
|
|
256
|
+
`WorkflowSession` does it for you — and only advances on `PROCEED`:
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from router import WorkflowEngine, WorkflowSession
|
|
260
|
+
|
|
261
|
+
engine = WorkflowEngine(cfg)
|
|
262
|
+
session = WorkflowSession(engine, entry="entry", trace_id="req-42")
|
|
263
|
+
|
|
264
|
+
result = session.request("support") # entry -> support
|
|
265
|
+
result = session.request("faq") # support -> faq
|
|
266
|
+
result = session.request("REFUSE") # terminal; session closes
|
|
267
|
+
|
|
268
|
+
session.history # ("entry", "support")
|
|
269
|
+
session.invocation_depth # {"entry": 1, "support": 1, "faq": 1}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A `REFUSE` closes the session. A `PAUSE` suspends it until `resume()`.
|
|
273
|
+
`session.snapshot(target)` exposes the exact metadata the next request would
|
|
274
|
+
evaluate, so the session is fully auditable and you can drop down to raw
|
|
275
|
+
`engine.evaluate()` at any time. The engine itself remains pure and stateless.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## OpenAI Agents SDK Integration
|
|
280
|
+
|
|
281
|
+
Agents are containers. Handoffs are transitions. Attach a `TopologyGuard` to
|
|
282
|
+
a run and every handoff is structurally evaluated **before** the next agent
|
|
283
|
+
executes — a refused handoff raises `TopologyViolation` and aborts the run
|
|
284
|
+
loudly instead of letting the agent graph wander:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
pip install "llm-workflow-router[openai-agents]"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from agents import Agent, Runner
|
|
292
|
+
from router import WorkflowEngine
|
|
293
|
+
from router.integrations.openai_agents import TopologyGuard, TopologyViolation
|
|
294
|
+
|
|
295
|
+
guard = TopologyGuard(engine, entry="triage", trace_id="run-001")
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
result = await Runner.run(triage_agent, "I was double-charged.", hooks=guard)
|
|
299
|
+
except TopologyViolation as violation:
|
|
300
|
+
print("Refused:", violation.result.reason)
|
|
301
|
+
|
|
302
|
+
# Full structural audit trail of the run:
|
|
303
|
+
print(guard.session.history, guard.session.invocation_depth)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
By default an agent's `name` is its container name; pass `container_for=` to
|
|
307
|
+
map differently. The guard is content-blind — it never reads prompts,
|
|
308
|
+
messages, or tool arguments. See
|
|
309
|
+
[`examples/openai_agents_example.py`](examples/openai_agents_example.py) for a
|
|
310
|
+
complete runnable triage → billing → refunds system.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Why not just LangGraph (or my orchestrator's built-in graph)?
|
|
315
|
+
|
|
316
|
+
Orchestrators *describe* structure. This engine *enforces* it — as a separate,
|
|
317
|
+
framework-agnostic layer with properties orchestrators don't give you:
|
|
318
|
+
|
|
319
|
+
- **Independent enforcement.** The topology lives outside your agent
|
|
320
|
+
framework, so a prompt-induced detour, a buggy handoff, or a framework
|
|
321
|
+
upgrade can't silently widen what's reachable. The declared graph is a
|
|
322
|
+
contract, and violations fail loudly with structured reason codes.
|
|
323
|
+
- **Framework-agnostic.** The same YAML config governs an OpenAI Agents SDK
|
|
324
|
+
app today and whatever you migrate to next year. Adapters are thin; the
|
|
325
|
+
contract is portable.
|
|
326
|
+
- **Auditable determinism.** Same metadata + same config → same decision,
|
|
327
|
+
every time. Combined with the `wfrouter.*` OpenTelemetry conventions, you
|
|
328
|
+
get compliance-grade answers to "why was this transition refused?" — a
|
|
329
|
+
reason code, not a vibe.
|
|
330
|
+
- **Load-time topology analysis.** Cycles, orphans, dead ends, and
|
|
331
|
+
unreachable states are caught before anything runs — the kind of static
|
|
332
|
+
validation industrial control systems have had for decades and agent
|
|
333
|
+
frameworks mostly don't.
|
|
334
|
+
|
|
335
|
+
If you're happy inside one orchestrator and don't need independent structural
|
|
336
|
+
guarantees, its built-in graph may be enough. This tool exists for when
|
|
337
|
+
"probably follows the graph" isn't good enough.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Intended Audience
|
|
342
|
+
|
|
343
|
+
- AI SaaS developers
|
|
344
|
+
- Internal LLM tooling teams
|
|
345
|
+
- Agent orchestration builders
|
|
346
|
+
- Platform engineering teams
|
|
347
|
+
- Infrastructure-focused AI developers
|
|
348
|
+
|
|
349
|
+
Not intended for content moderation or prompt filtering.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Observability
|
|
354
|
+
|
|
355
|
+
Optional OpenTelemetry instrumentation is provided under a dedicated,
|
|
356
|
+
versioned namespace (`wfrouter.*`) that this project owns — it is deliberately
|
|
357
|
+
independent of the upstream `gen_ai.*` conventions, which assume a model at the
|
|
358
|
+
center of every span and do not fit a content-blind topology engine.
|
|
359
|
+
|
|
360
|
+
Install the extra:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
pip install "llm-workflow-router[otel]"
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Wrap evaluation:
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
from router.observability.otel import traced_evaluate
|
|
370
|
+
result = traced_evaluate(engine, metadata)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
If `opentelemetry-api` is not installed, instrumentation degrades to a no-op
|
|
374
|
+
and the engine behaves identically. A `PROCEED`, `REFUSE`, or `PAUSE` outcome
|
|
375
|
+
is a successful decision (span status OK); only genuine failures are errors.
|
|
376
|
+
See `OBSERVABILITY.md` for the full attribute and span conventions.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
# License
|
|
381
|
+
|
|
382
|
+
MIT. Use it, ship it, build on it. See [LICENSE](LICENSE).
|
|
383
|
+
|
|
384
|
+
If you deploy this in production, a note about your use case is always
|
|
385
|
+
appreciated (and helps prioritize the roadmap) — but never required.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## Version
|
|
390
|
+
|
|
391
|
+
Current version: 0.3.0
|
|
392
|
+
|
|
393
|
+
- Static configuration model
|
|
394
|
+
- Explicit multi-entrypoint declaration (with inferred fallback)
|
|
395
|
+
- Optional stateful `WorkflowSession` convenience layer
|
|
396
|
+
- OpenAI Agents SDK adapter (`TopologyGuard`)
|
|
397
|
+
- Mermaid / DOT topology export (`llm-router graph`)
|
|
398
|
+
- MIT licensed
|
|
399
|
+
|
|
400
|
+
No inheritance. No dynamic rule composition. See [CHANGELOG.md](CHANGELOG.md).
|
|
401
|
+
|
|
402
|
+
Future versions may extend topology modeling capabilities.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Author
|
|
407
|
+
|
|
408
|
+
Doby Baxter
|
|
409
|
+
Software systems developer focused on deterministic infrastructure and human-centered tooling.
|