urml-llm-bridge 0.1.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.
Files changed (29) hide show
  1. urml_llm_bridge-0.1.0/.gitignore +104 -0
  2. urml_llm_bridge-0.1.0/PKG-INFO +223 -0
  3. urml_llm_bridge-0.1.0/README.md +182 -0
  4. urml_llm_bridge-0.1.0/pyproject.toml +83 -0
  5. urml_llm_bridge-0.1.0/src/urml_llm_bridge/__init__.py +56 -0
  6. urml_llm_bridge-0.1.0/src/urml_llm_bridge/_version.py +5 -0
  7. urml_llm_bridge-0.1.0/src/urml_llm_bridge/bridge.py +242 -0
  8. urml_llm_bridge-0.1.0/src/urml_llm_bridge/errors.py +48 -0
  9. urml_llm_bridge-0.1.0/src/urml_llm_bridge/few_shot.py +400 -0
  10. urml_llm_bridge-0.1.0/src/urml_llm_bridge/grammar.py +290 -0
  11. urml_llm_bridge-0.1.0/src/urml_llm_bridge/prompt.py +253 -0
  12. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/__init__.py +10 -0
  13. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/anthropic.py +124 -0
  14. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/base.py +56 -0
  15. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/echo.py +82 -0
  16. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/llama_cpp.py +172 -0
  17. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/ollama.py +157 -0
  18. urml_llm_bridge-0.1.0/src/urml_llm_bridge/providers/openai.py +104 -0
  19. urml_llm_bridge-0.1.0/src/urml_llm_bridge/py.typed +0 -0
  20. urml_llm_bridge-0.1.0/tests/__init__.py +0 -0
  21. urml_llm_bridge-0.1.0/tests/test_bridge.py +511 -0
  22. urml_llm_bridge-0.1.0/tests/test_emit_prompt_cli.py +184 -0
  23. urml_llm_bridge-0.1.0/tests/test_few_shot_library.py +278 -0
  24. urml_llm_bridge-0.1.0/tests/test_grammar.py +194 -0
  25. urml_llm_bridge-0.1.0/tests/test_providers_anthropic.py +114 -0
  26. urml_llm_bridge-0.1.0/tests/test_providers_llama_cpp.py +135 -0
  27. urml_llm_bridge-0.1.0/tests/test_providers_ollama.py +114 -0
  28. urml_llm_bridge-0.1.0/tests/test_providers_openai.py +77 -0
  29. urml_llm_bridge-0.1.0/tests/test_translate_cli.py +289 -0
@@ -0,0 +1,104 @@
1
+ # --- Python ---
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ dist/
9
+ develop-eggs/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # Virtual envs
25
+ .venv/
26
+ venv/
27
+ env/
28
+ ENV/
29
+ .python-version
30
+
31
+ # Test / coverage / mypy / pyright
32
+ .coverage
33
+ .coverage.*
34
+ .cache
35
+ .pytest_cache/
36
+ .mypy_cache/
37
+ .dmypy.json
38
+ .pyre/
39
+ .pyright/
40
+ htmlcov/
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ .hypothesis/
45
+ .tox/
46
+
47
+ # --- Node / web tooling (kept out of core repo; here for safety) ---
48
+ node_modules/
49
+ .pnp.*
50
+ .yarn/cache/
51
+ .yarn/unplugged/
52
+ .yarn/build-state.yml
53
+ .yarn/install-state.gz
54
+ npm-debug.log*
55
+ yarn-debug.log*
56
+ yarn-error.log*
57
+
58
+ # --- C++ / Rust build artifacts (in case reference runtimes land here in early Phase 1) ---
59
+ target/
60
+ *.o
61
+ *.obj
62
+ *.a
63
+ *.lib
64
+ *.dll
65
+ *.dylib
66
+ *.exe
67
+ *.pdb
68
+ cmake-build-*/
69
+ CMakeCache.txt
70
+ CMakeFiles/
71
+ CMakeScripts/
72
+ Testing/
73
+ cmake_install.cmake
74
+ install_manifest.txt
75
+ compile_commands.json
76
+
77
+ # --- ROS 2 build dirs ---
78
+ build/
79
+ install/
80
+ log/
81
+
82
+ # --- Editor and OS junk ---
83
+ .vscode/
84
+ .idea/
85
+ *.swp
86
+ *.swo
87
+ *~
88
+ .DS_Store
89
+ Thumbs.db
90
+ desktop.ini
91
+
92
+ # --- Local-only artifacts ---
93
+ .env
94
+ .env.*
95
+ *.log
96
+ *.tmp
97
+ .local/
98
+ .scratch/
99
+
100
+ # --- Outreach campaign artifacts (operational, not part of the open standard) ---
101
+ # Target lists, message drafts, tracking data, and the launch-approval record
102
+ # stay local. They are private to the maintainer; committing them would
103
+ # telegraph the campaign before launch and could embarrass listed projects.
104
+ outreach/
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: urml-llm-bridge
3
+ Version: 0.1.0
4
+ Summary: Provider-agnostic glue between natural language and validated URML programs.
5
+ Project-URL: Homepage, https://github.com/URML-MARS/URML
6
+ Project-URL: Repository, https://github.com/URML-MARS/URML
7
+ Project-URL: Issues, https://github.com/URML-MARS/URML/issues
8
+ Author: URML Maintainers
9
+ License: Apache-2.0
10
+ Keywords: llm,robotics,specification,urml
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: pydantic<3,>=2.6
21
+ Requires-Dist: pyyaml<7,>=6.0
22
+ Requires-Dist: urml-validator>=0.1.0
23
+ Provides-Extra: anthropic
24
+ Requires-Dist: anthropic>=0.34; extra == 'anthropic'
25
+ Provides-Extra: dev
26
+ Requires-Dist: anthropic>=0.34; extra == 'dev'
27
+ Requires-Dist: httpx>=0.27; extra == 'dev'
28
+ Requires-Dist: mypy>=1.10; extra == 'dev'
29
+ Requires-Dist: openai>=1.40; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
31
+ Requires-Dist: pytest>=8; extra == 'dev'
32
+ Requires-Dist: ruff>=0.5; extra == 'dev'
33
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
34
+ Provides-Extra: llama-cpp
35
+ Requires-Dist: httpx>=0.27; extra == 'llama-cpp'
36
+ Provides-Extra: ollama
37
+ Requires-Dist: httpx>=0.27; extra == 'ollama'
38
+ Provides-Extra: openai
39
+ Requires-Dist: openai>=1.40; extra == 'openai'
40
+ Description-Content-Type: text/markdown
41
+
42
+ <p align="center">
43
+ <a href="https://urml.dev"><img src="https://urml.dev/favicon.svg" alt="URML" width="72" height="72"></a>
44
+ </p>
45
+
46
+ <p align="center">
47
+ A small, opinionated, human-readable language for describing robot intent.
48
+ </p>
49
+
50
+ <p align="center">
51
+ <a href="https://urml.dev"><b>urml.dev</b></a>
52
+ </p>
53
+
54
+ ---
55
+
56
+ # LLM Bridge
57
+
58
+ **Status:** `0.1.0`, aligned with the other four packages. Shipped: the provider-agnostic `Bridge` + bounded validator-feedback revision loop, real Anthropic / OpenAI adapters (lazy-imported) + the hermetic `EchoProvider`, the `urml translate` / `urml emit-prompt` CLI, and profile-scoped few-shot libraries (home / industrial / drone). The normative contract is [`spec/layer-4-nl-grammar/v0.1.0.md`](../../spec/layer-4-nl-grammar/); a hermetic end-to-end walkthrough is [`docs/demos/bridge-roundtrip.md`](../../docs/demos/bridge-roundtrip.md).
59
+
60
+ ## What this is
61
+
62
+ The **provider-agnostic glue** between natural-language input and a validated URML program. The LLM bridge:
63
+
64
+ 1. Takes a natural-language request from a user (or another system).
65
+ 2. Prompts a configured LLM with the [Layer-4 prompt contract](../../spec/layer-4-nl-grammar/), the connected robot's [Layer-1 capability manifest](../../spec/layer-1-hal/), and the active safety envelope.
66
+ 3. Receives the LLM's emission (a URML program).
67
+ 4. Calls the [validator](../validator/) to statically verify the emission.
68
+ 5. On rejection, surfaces the structured error back to the LLM and requests a revision. Repeats up to a configured bound.
69
+ 6. On acceptance, hands the validated program to the runtime for execution.
70
+ 7. When the request needs a capability the manifest does not declare, the model emits a `report(status: failure)` naming what is missing rather than fabricating capability. (An *interactive* clarifying-question protocol is **not** in v0.1 — see [`spec/layer-4-nl-grammar/v0.1.0.md`](../../spec/layer-4-nl-grammar/) §5.)
71
+
72
+ ## Provider neutrality is non-negotiable
73
+
74
+ URML's value as a standard depends on Layer 4 being **provider-neutral**. The LLM bridge must support, as first-class citizens:
75
+
76
+ - **Anthropic** (Claude family).
77
+ - **OpenAI** (GPT family).
78
+ - **Open-weights models** (Llama, Mistral, Qwen, and their successors), via local serving (vLLM, llama.cpp, Ollama) or hosted inference providers.
79
+ - **On-device models** for offline-capable deployments.
80
+
81
+ Adding a new provider must be a small adapter in `providers/`, not a structural change to the bridge. If a provider's particular feature would let URML produce better URML, the bridge surfaces the feature behind a profile-neutral abstraction — never by privileging that provider.
82
+
83
+ Vendor lock-in here is explicitly prohibited by [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do.
84
+
85
+ ## What the bridge does NOT do
86
+
87
+ - It does **not** include or embed any specific LLM provider's API client as a hard dependency. Provider clients are optional and pluggable.
88
+ - It does **not** require cloud connectivity. A deployment using a local open-weights model runs offline end to end.
89
+ - It does **not** persist user inputs, model outputs, or any other data without explicit, opt-in, documented purpose. Trust is the most valuable asset of this project; the bridge will not be the place it leaks.
90
+ - It does **not** execute URML. That is the runtime's job.
91
+ - It does **not** make safety decisions. The validator is the safety boundary; the bridge only relays.
92
+
93
+ ## Architecture (planned)
94
+
95
+ ```
96
+ ┌────────────────────────┐
97
+ user / NL │ LLM Bridge │ validated URML
98
+ ─────────────▶ • build prompt ├──────────────────▶ runtime
99
+ │ • call provider │
100
+ │ • validate emission │
101
+ │ • revise loop │
102
+ └──┬──────────────────▲──┘
103
+ │ │
104
+ ▼ │
105
+ providers/ validator
106
+ (anthropic.py, (separate
107
+ openai.py, process)
108
+ local_vllm.py,
109
+ on_device.py)
110
+ ```
111
+
112
+ The bridge is small. The intelligence lives in the LLM (which is configured, not built here) and in the validator (which is a separate process). The bridge orchestrates.
113
+
114
+ ## Language
115
+
116
+ - **Python**. `mypy --strict`. Public API fully type-annotated.
117
+
118
+ ## API (sketch)
119
+
120
+ ```python
121
+ from urml.llm_bridge import Bridge
122
+
123
+ bridge = Bridge(
124
+ provider="anthropic", # or "openai", "vllm", "ollama", ...
125
+ spec_versions={...},
126
+ manifest=manifest,
127
+ envelope=envelope,
128
+ profiles=("home",),
129
+ max_revisions=3,
130
+ )
131
+
132
+ result = bridge.translate("Bring me the red mug from the kitchen.")
133
+
134
+ if result.accepted:
135
+ runtime.execute(result.program)
136
+ else:
137
+ # After max_revisions, structured errors surface to the caller.
138
+ show_user(result.user_message, result.errors)
139
+ ```
140
+
141
+ ## Conformance contract
142
+
143
+ The bridge has its own conformance bar: for the published few-shot example library, the bridge produces accepted URML at or above a stated success rate (per-provider, declared in the bridge's release notes). The conformance suite includes these end-to-end fixtures.
144
+
145
+ ## Core Commitment
146
+
147
+ The LLM bridge — the *bridge logic and the prompt contract*, not any specific provider's API — is part of the [Core Commitment](../../CORE_COMMITMENT.md). It will always be Apache 2.0 and provider-agnostic.
148
+
149
+ ## Quickstart (current pre-alpha — hermetic, no provider needed)
150
+
151
+ ```bash
152
+ cd reference/llm-bridge
153
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
154
+ pip install -e ../validator # bridge depends on validator
155
+ pip install -e ".[dev]"
156
+ pytest
157
+ ```
158
+
159
+ Use it in code with the bundled `EchoProvider` (for tests / hermetic CI):
160
+
161
+ ```python
162
+ import json
163
+ from urml_llm_bridge import Bridge, EchoProvider
164
+
165
+ red_mug_program = { # the URML the LLM is expected to emit
166
+ "profile": "home",
167
+ "behavior": {"type": "sequence", "steps": [...]},
168
+ }
169
+
170
+ provider = EchoProvider(scripted=[json.dumps(red_mug_program)])
171
+ bridge = Bridge(provider=provider, manifest=manifest, envelope=envelope, profiles=("home",))
172
+ result = bridge.translate("Bring me the red mug from the kitchen.")
173
+
174
+ if result.accepted:
175
+ runtime.execute(result.program)
176
+ ```
177
+
178
+ The revision loop runs automatically when the validator rejects the LLM's emission: the bridge feeds the structured errors back to the LLM and asks for a corrected version, up to `max_revisions` times (default 3).
179
+
180
+ ### Using a real provider
181
+
182
+ Install the extra for the provider you want — the bridge package itself has no SDK dependencies:
183
+
184
+ ```bash
185
+ pip install urml-llm-bridge[anthropic] # adds the `anthropic` SDK
186
+ pip install urml-llm-bridge[openai] # adds the `openai` SDK
187
+ ```
188
+
189
+ Then:
190
+
191
+ ```python
192
+ from urml_llm_bridge import Bridge
193
+ from urml_llm_bridge.providers.anthropic import AnthropicProvider
194
+
195
+ provider = AnthropicProvider(model="claude-sonnet-4-6") # reads ANTHROPIC_API_KEY env var
196
+ bridge = Bridge(provider=provider, manifest=manifest, envelope=envelope, profiles=("home",))
197
+ result = bridge.translate("Bring me the red mug from the kitchen.")
198
+ ```
199
+
200
+ Or OpenAI:
201
+
202
+ ```python
203
+ from urml_llm_bridge.providers.openai import OpenAIProvider
204
+
205
+ provider = OpenAIProvider(model="gpt-4o") # reads OPENAI_API_KEY env var
206
+ ```
207
+
208
+ Both adapters surface their native structured-output mechanism — Anthropic via tool use (with the URML schema as the `emit_urml` tool's `input_schema`), OpenAI via `response_format={"type": "json_object"}` with the schema conveyed in the system prompt. Either way, conformance to the schema is validated downstream by `urml_validator.validate()` as part of the bridge's revision loop.
209
+
210
+ ## What's not in this pre-alpha (lands next)
211
+
212
+ - **CLI integration** — `urml translate` subcommand on top of `urml-validator`'s CLI.
213
+ - **Profile-specific few-shot libraries** (drone scenarios, industrial scenarios).
214
+ - **Multilingual few-shot variants** (Hebrew, Spanish, Japanese, Mandarin).
215
+ - **Conversation memory** for follow-up requests within a session.
216
+ - **OpenAI strict JSON-schema mode** — needs schema preprocessing to satisfy the strict-mode constraints (every property required, no `oneOf`, etc.). The current adapter uses `json_object` mode plus the schema in the system prompt for portability.
217
+
218
+ ## Related documents
219
+
220
+ - [`/spec/layer-4-nl-grammar/`](../../spec/layer-4-nl-grammar/) — the prompt contract this bridge implements.
221
+ - [`/reference/validator/`](../validator/) — the safety boundary this bridge feeds.
222
+ - [`/examples/`](../../examples/) — the paired natural-language / URML scenarios used as fixtures.
223
+ - [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do — the provider-neutrality requirement, in writing.
@@ -0,0 +1,182 @@
1
+ <p align="center">
2
+ <a href="https://urml.dev"><img src="https://urml.dev/favicon.svg" alt="URML" width="72" height="72"></a>
3
+ </p>
4
+
5
+ <p align="center">
6
+ A small, opinionated, human-readable language for describing robot intent.
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://urml.dev"><b>urml.dev</b></a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ # LLM Bridge
16
+
17
+ **Status:** `0.1.0`, aligned with the other four packages. Shipped: the provider-agnostic `Bridge` + bounded validator-feedback revision loop, real Anthropic / OpenAI adapters (lazy-imported) + the hermetic `EchoProvider`, the `urml translate` / `urml emit-prompt` CLI, and profile-scoped few-shot libraries (home / industrial / drone). The normative contract is [`spec/layer-4-nl-grammar/v0.1.0.md`](../../spec/layer-4-nl-grammar/); a hermetic end-to-end walkthrough is [`docs/demos/bridge-roundtrip.md`](../../docs/demos/bridge-roundtrip.md).
18
+
19
+ ## What this is
20
+
21
+ The **provider-agnostic glue** between natural-language input and a validated URML program. The LLM bridge:
22
+
23
+ 1. Takes a natural-language request from a user (or another system).
24
+ 2. Prompts a configured LLM with the [Layer-4 prompt contract](../../spec/layer-4-nl-grammar/), the connected robot's [Layer-1 capability manifest](../../spec/layer-1-hal/), and the active safety envelope.
25
+ 3. Receives the LLM's emission (a URML program).
26
+ 4. Calls the [validator](../validator/) to statically verify the emission.
27
+ 5. On rejection, surfaces the structured error back to the LLM and requests a revision. Repeats up to a configured bound.
28
+ 6. On acceptance, hands the validated program to the runtime for execution.
29
+ 7. When the request needs a capability the manifest does not declare, the model emits a `report(status: failure)` naming what is missing rather than fabricating capability. (An *interactive* clarifying-question protocol is **not** in v0.1 — see [`spec/layer-4-nl-grammar/v0.1.0.md`](../../spec/layer-4-nl-grammar/) §5.)
30
+
31
+ ## Provider neutrality is non-negotiable
32
+
33
+ URML's value as a standard depends on Layer 4 being **provider-neutral**. The LLM bridge must support, as first-class citizens:
34
+
35
+ - **Anthropic** (Claude family).
36
+ - **OpenAI** (GPT family).
37
+ - **Open-weights models** (Llama, Mistral, Qwen, and their successors), via local serving (vLLM, llama.cpp, Ollama) or hosted inference providers.
38
+ - **On-device models** for offline-capable deployments.
39
+
40
+ Adding a new provider must be a small adapter in `providers/`, not a structural change to the bridge. If a provider's particular feature would let URML produce better URML, the bridge surfaces the feature behind a profile-neutral abstraction — never by privileging that provider.
41
+
42
+ Vendor lock-in here is explicitly prohibited by [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do.
43
+
44
+ ## What the bridge does NOT do
45
+
46
+ - It does **not** include or embed any specific LLM provider's API client as a hard dependency. Provider clients are optional and pluggable.
47
+ - It does **not** require cloud connectivity. A deployment using a local open-weights model runs offline end to end.
48
+ - It does **not** persist user inputs, model outputs, or any other data without explicit, opt-in, documented purpose. Trust is the most valuable asset of this project; the bridge will not be the place it leaks.
49
+ - It does **not** execute URML. That is the runtime's job.
50
+ - It does **not** make safety decisions. The validator is the safety boundary; the bridge only relays.
51
+
52
+ ## Architecture (planned)
53
+
54
+ ```
55
+ ┌────────────────────────┐
56
+ user / NL │ LLM Bridge │ validated URML
57
+ ─────────────▶ • build prompt ├──────────────────▶ runtime
58
+ │ • call provider │
59
+ │ • validate emission │
60
+ │ • revise loop │
61
+ └──┬──────────────────▲──┘
62
+ │ │
63
+ ▼ │
64
+ providers/ validator
65
+ (anthropic.py, (separate
66
+ openai.py, process)
67
+ local_vllm.py,
68
+ on_device.py)
69
+ ```
70
+
71
+ The bridge is small. The intelligence lives in the LLM (which is configured, not built here) and in the validator (which is a separate process). The bridge orchestrates.
72
+
73
+ ## Language
74
+
75
+ - **Python**. `mypy --strict`. Public API fully type-annotated.
76
+
77
+ ## API (sketch)
78
+
79
+ ```python
80
+ from urml.llm_bridge import Bridge
81
+
82
+ bridge = Bridge(
83
+ provider="anthropic", # or "openai", "vllm", "ollama", ...
84
+ spec_versions={...},
85
+ manifest=manifest,
86
+ envelope=envelope,
87
+ profiles=("home",),
88
+ max_revisions=3,
89
+ )
90
+
91
+ result = bridge.translate("Bring me the red mug from the kitchen.")
92
+
93
+ if result.accepted:
94
+ runtime.execute(result.program)
95
+ else:
96
+ # After max_revisions, structured errors surface to the caller.
97
+ show_user(result.user_message, result.errors)
98
+ ```
99
+
100
+ ## Conformance contract
101
+
102
+ The bridge has its own conformance bar: for the published few-shot example library, the bridge produces accepted URML at or above a stated success rate (per-provider, declared in the bridge's release notes). The conformance suite includes these end-to-end fixtures.
103
+
104
+ ## Core Commitment
105
+
106
+ The LLM bridge — the *bridge logic and the prompt contract*, not any specific provider's API — is part of the [Core Commitment](../../CORE_COMMITMENT.md). It will always be Apache 2.0 and provider-agnostic.
107
+
108
+ ## Quickstart (current pre-alpha — hermetic, no provider needed)
109
+
110
+ ```bash
111
+ cd reference/llm-bridge
112
+ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
113
+ pip install -e ../validator # bridge depends on validator
114
+ pip install -e ".[dev]"
115
+ pytest
116
+ ```
117
+
118
+ Use it in code with the bundled `EchoProvider` (for tests / hermetic CI):
119
+
120
+ ```python
121
+ import json
122
+ from urml_llm_bridge import Bridge, EchoProvider
123
+
124
+ red_mug_program = { # the URML the LLM is expected to emit
125
+ "profile": "home",
126
+ "behavior": {"type": "sequence", "steps": [...]},
127
+ }
128
+
129
+ provider = EchoProvider(scripted=[json.dumps(red_mug_program)])
130
+ bridge = Bridge(provider=provider, manifest=manifest, envelope=envelope, profiles=("home",))
131
+ result = bridge.translate("Bring me the red mug from the kitchen.")
132
+
133
+ if result.accepted:
134
+ runtime.execute(result.program)
135
+ ```
136
+
137
+ The revision loop runs automatically when the validator rejects the LLM's emission: the bridge feeds the structured errors back to the LLM and asks for a corrected version, up to `max_revisions` times (default 3).
138
+
139
+ ### Using a real provider
140
+
141
+ Install the extra for the provider you want — the bridge package itself has no SDK dependencies:
142
+
143
+ ```bash
144
+ pip install urml-llm-bridge[anthropic] # adds the `anthropic` SDK
145
+ pip install urml-llm-bridge[openai] # adds the `openai` SDK
146
+ ```
147
+
148
+ Then:
149
+
150
+ ```python
151
+ from urml_llm_bridge import Bridge
152
+ from urml_llm_bridge.providers.anthropic import AnthropicProvider
153
+
154
+ provider = AnthropicProvider(model="claude-sonnet-4-6") # reads ANTHROPIC_API_KEY env var
155
+ bridge = Bridge(provider=provider, manifest=manifest, envelope=envelope, profiles=("home",))
156
+ result = bridge.translate("Bring me the red mug from the kitchen.")
157
+ ```
158
+
159
+ Or OpenAI:
160
+
161
+ ```python
162
+ from urml_llm_bridge.providers.openai import OpenAIProvider
163
+
164
+ provider = OpenAIProvider(model="gpt-4o") # reads OPENAI_API_KEY env var
165
+ ```
166
+
167
+ Both adapters surface their native structured-output mechanism — Anthropic via tool use (with the URML schema as the `emit_urml` tool's `input_schema`), OpenAI via `response_format={"type": "json_object"}` with the schema conveyed in the system prompt. Either way, conformance to the schema is validated downstream by `urml_validator.validate()` as part of the bridge's revision loop.
168
+
169
+ ## What's not in this pre-alpha (lands next)
170
+
171
+ - **CLI integration** — `urml translate` subcommand on top of `urml-validator`'s CLI.
172
+ - **Profile-specific few-shot libraries** (drone scenarios, industrial scenarios).
173
+ - **Multilingual few-shot variants** (Hebrew, Spanish, Japanese, Mandarin).
174
+ - **Conversation memory** for follow-up requests within a session.
175
+ - **OpenAI strict JSON-schema mode** — needs schema preprocessing to satisfy the strict-mode constraints (every property required, no `oneOf`, etc.). The current adapter uses `json_object` mode plus the schema in the system prompt for portability.
176
+
177
+ ## Related documents
178
+
179
+ - [`/spec/layer-4-nl-grammar/`](../../spec/layer-4-nl-grammar/) — the prompt contract this bridge implements.
180
+ - [`/reference/validator/`](../validator/) — the safety boundary this bridge feeds.
181
+ - [`/examples/`](../../examples/) — the paired natural-language / URML scenarios used as fixtures.
182
+ - [`CLAUDE.md`](../../CLAUDE.md) §What Claude Should Never Do — the provider-neutrality requirement, in writing.
@@ -0,0 +1,83 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "urml-llm-bridge"
7
+ version = "0.1.0"
8
+ description = "Provider-agnostic glue between natural language and validated URML programs."
9
+ readme = "README.md"
10
+ license = { text = "Apache-2.0" }
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "URML Maintainers" }]
13
+ keywords = ["robotics", "urml", "llm", "specification"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
23
+ ]
24
+ dependencies = [
25
+ "pydantic>=2.6,<3",
26
+ "PyYAML>=6.0,<7",
27
+ "urml-validator>=0.1.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ # Provider adapters are opt-in. Install the ones you need.
32
+ anthropic = ["anthropic>=0.34"]
33
+ openai = ["openai>=1.40"]
34
+ # On-device adapters (RFC-0021) talk HTTP to a local inference server.
35
+ # Both depend on httpx and require a separately-running server (llama-server
36
+ # or ollama serve); neither bundles model weights.
37
+ llama_cpp = ["httpx>=0.27"]
38
+ ollama = ["httpx>=0.27"]
39
+ dev = [
40
+ "pytest>=8",
41
+ "pytest-cov>=5",
42
+ "ruff>=0.5",
43
+ "mypy>=1.10",
44
+ "types-PyYAML>=6.0",
45
+ # Provider SDKs included in dev so mypy can type-check the adapters
46
+ # and the test suite can exercise them with mocked clients. End users
47
+ # install just the extras they need (e.g., pip install urml-llm-bridge[anthropic]).
48
+ "anthropic>=0.34",
49
+ "openai>=1.40",
50
+ "httpx>=0.27",
51
+ ]
52
+
53
+ [project.urls]
54
+ Homepage = "https://github.com/URML-MARS/URML"
55
+ Repository = "https://github.com/URML-MARS/URML"
56
+ Issues = "https://github.com/URML-MARS/URML/issues"
57
+
58
+ [tool.hatch.build.targets.wheel]
59
+ packages = ["src/urml_llm_bridge"]
60
+
61
+ [tool.ruff]
62
+ line-length = 120
63
+ target-version = "py311"
64
+ src = ["src", "tests"]
65
+
66
+ [tool.ruff.lint]
67
+ select = ["E", "F", "W", "I", "B", "UP", "N", "ANN", "RUF"]
68
+ ignore = ["ANN401"]
69
+
70
+ [tool.ruff.lint.per-file-ignores]
71
+ "tests/*" = ["ANN"]
72
+
73
+ [tool.mypy]
74
+ strict = true
75
+ python_version = "3.11"
76
+ plugins = ["pydantic.mypy"]
77
+ mypy_path = "src"
78
+ packages = ["urml_llm_bridge"]
79
+
80
+ [tool.pytest.ini_options]
81
+ minversion = "8.0"
82
+ testpaths = ["tests"]
83
+ addopts = ["-q", "--strict-markers"]
@@ -0,0 +1,56 @@
1
+ """urml_llm_bridge — provider-agnostic glue from natural language to validated URML.
2
+
3
+ Public API:
4
+
5
+ Bridge(provider, manifest, envelope=None, profiles=(), max_revisions=3)
6
+ .translate(user_request: str) -> TranslateResult
7
+
8
+ EchoProvider(responses: dict[str, str])
9
+ A hermetic provider for testing, with no network.
10
+
11
+ Bring your own provider by implementing LLMProvider.complete(). Real
12
+ adapters for Anthropic and OpenAI live under `urml_llm_bridge.providers`
13
+ and are imported lazily so the bridge has no hard dependency on either
14
+ SDK.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from urml_llm_bridge._version import __version__
20
+ from urml_llm_bridge.bridge import Bridge, TranslateResult
21
+ from urml_llm_bridge.errors import (
22
+ BridgeError,
23
+ BridgePolicyViolation,
24
+ BridgeRevisionExhausted,
25
+ ProviderError,
26
+ )
27
+ from urml_llm_bridge.few_shot import (
28
+ FewShot,
29
+ default_few_shots,
30
+ drone_few_shots,
31
+ few_shots_for,
32
+ home_few_shots,
33
+ industrial_few_shots,
34
+ )
35
+ from urml_llm_bridge.prompt import build_system_prompt
36
+ from urml_llm_bridge.providers.base import LLMProvider
37
+ from urml_llm_bridge.providers.echo import EchoProvider
38
+
39
+ __all__ = [
40
+ "Bridge",
41
+ "BridgeError",
42
+ "BridgePolicyViolation",
43
+ "BridgeRevisionExhausted",
44
+ "EchoProvider",
45
+ "FewShot",
46
+ "LLMProvider",
47
+ "ProviderError",
48
+ "TranslateResult",
49
+ "__version__",
50
+ "build_system_prompt",
51
+ "default_few_shots",
52
+ "drone_few_shots",
53
+ "few_shots_for",
54
+ "home_few_shots",
55
+ "industrial_few_shots",
56
+ ]
@@ -0,0 +1,5 @@
1
+ """Single source of truth for the bridge's `__version__`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__: str = "0.1.0"