omagent 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.
omagent-0.1.0/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Masoud Masoumi
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
omagent-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: omagent
3
+ Version: 0.1.0
4
+ Summary: LLM-assisted OpenModelica modeling: a tested agentic generate-compile-simulate-verify loop with structured omc diagnostics, quantitative trajectory verification, and a benchmark task ladder.
5
+ Author: Masoud Masoumi
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/MasoudMiM/omeganet
8
+ Project-URL: Issues, https://github.com/MasoudMiM/omeganet/issues
9
+ Keywords: modelica,openmodelica,llm,simulation,agent,modeling
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: omc
23
+ Requires-Dist: OMPython>=3.6; extra == "omc"
24
+ Provides-Extra: results
25
+ Requires-Dist: scipy>=1.10; extra == "results"
26
+ Provides-Extra: llm
27
+ Requires-Dist: anthropic>=0.40; extra == "llm"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8; extra == "dev"
30
+ Provides-Extra: all
31
+ Requires-Dist: OMPython>=3.6; extra == "all"
32
+ Requires-Dist: scipy>=1.10; extra == "all"
33
+ Requires-Dist: anthropic>=0.40; extra == "all"
34
+ Requires-Dist: pytest>=8; extra == "all"
35
+ Dynamic: license-file
36
+
37
+ # omagent
38
+
39
+ **LLM-assisted modeling for OpenModelica** — an open-source, headless Python
40
+ agent that turns a natural-language task into a *verified* Modelica model:
41
+
42
+ ```
43
+ natural language ──> generate ──> compile (omc) ──> simulate ──> verify physics
44
+ ▲ │
45
+ └──── structured error / verifier feedback ──┘
46
+ ```
47
+
48
+ Unlike a plain code assistant, omagent closes the loop on **physics, not just
49
+ compilation**: quantitative verifiers check trajectories against expected
50
+ behavior (final values, settling windows, bounds, overshoot), and every
51
+ failure — compiler diagnostics or physics complaints — is parsed into
52
+ structured feedback for the next fix attempt.
53
+
54
+ Key capabilities:
55
+
56
+ - **Structured omc diagnostics** — parses `getErrorString()`, simulation
57
+ logs, and OMPython's exception format into records with severity, source
58
+ location, and failure kind (syntax / lookup / type / balance / connect /
59
+ initialization / runtime)
60
+ - **Environment-grounded fix hints** — on `Class X not found`, omagent asks
61
+ omc what the parent package *actually* contains (`getClassNames`) and puts
62
+ near-miss suggestions into the fix prompt. This resolves the dominant
63
+ observed failure mode: stale library knowledge (e.g. MSL 3.2 names such as
64
+ `Basic.EMF` vs. MSL 4.x `Basic.RotationalEMF`)
65
+ - **Quantitative verification** — reads CSV or Dymola-format `.mat` results;
66
+ verifier complaints ("final value of x is 1.93, expected 2.0") drive tuning
67
+ - **Benchmark task ladder** — 5 escalating, auto-gradable tasks with full
68
+ transcript capture, in the format the OpenModelica benchmark discussion
69
+ ([OpenModelica#15385](https://github.com/OpenModelica/OpenModelica/issues/15385))
70
+ calls for
71
+ - **LLM-backend-agnostic** — the loop depends on a one-method protocol; an
72
+ Anthropic adapter ships, any provider or local model plugs in
73
+ - **Tested** — 83 unit tests run without OpenModelica installed; 4
74
+ integration tests validate against a live omc
75
+
76
+ ## Installation
77
+
78
+ Requires Python >= 3.10. The core package has zero hard dependencies;
79
+ features are opt-in extras:
80
+
81
+ ```bash
82
+ pip install -e . # parsers + loop only (no omc needed)
83
+ pip install -e ".[omc]" # + OMPython (talk to a real omc)
84
+ pip install -e ".[results]" # + scipy (.mat result files; CSV needs nothing)
85
+ pip install -e ".[llm]" # + anthropic adapter
86
+ pip install -e ".[all]" # everything, including pytest
87
+ ```
88
+
89
+ To use it against a real compiler you need
90
+ [OpenModelica](https://openmodelica.org) (tested with 1.26–1.27) with the
91
+ Modelica Standard Library installed for `omc`:
92
+
93
+ ```bash
94
+ echo 'installPackage(Modelica); getErrorString();' > /tmp/i.mos && omc /tmp/i.mos
95
+ ```
96
+
97
+ > Note: OMEdit installs the MSL for itself automatically; headless `omc`
98
+ > sessions do not. If models using `Modelica.*` fail with "Class ... not
99
+ > found", this is why.
100
+
101
+ Verify your setup:
102
+
103
+ ```bash
104
+ pytest -m "not integration" # unit tests, no omc required
105
+ pytest -m integration # against your live omc (+MSL, scipy for .mat)
106
+ ```
107
+
108
+ ## Quick start
109
+
110
+ ```python
111
+ from omagent import AgentLoop, OMSession, all_of, expect_bounds, expect_final
112
+ from omagent.llm import ClaudeLLM # or any object with .propose(...)
113
+
114
+ # physics acceptance criteria — complaints feed back into the fix loop
115
+ verifier = all_of(
116
+ expect_bounds("x", lo=-0.105, hi=0.105),
117
+ expect_final("x", 0.0, atol=0.06, rtol=0.0),
118
+ )
119
+
120
+ loop = AgentLoop(
121
+ OMSession(), # real omc via OMPython
122
+ ClaudeLLM(), # needs ANTHROPIC_API_KEY
123
+ max_attempts=4,
124
+ simulate_options={"stopTime": 10.0, "outputFormat": "csv"},
125
+ verifier=verifier,
126
+ )
127
+ result = loop.run(
128
+ "A mass-spring-damper: m = 1 kg, c = 100 N/m, d = 1 N.s/m, released "
129
+ "from x = 0.1 m at rest. Name position x and velocity v.")
130
+
131
+ print(result.success, result.model_name)
132
+ print(result.final_code)
133
+ for a in result.attempts:
134
+ print(a.n, a.stage, a.complaint)
135
+ ```
136
+
137
+ Bring your own LLM by implementing one method:
138
+
139
+ ```python
140
+ class MyLLM:
141
+ def propose(self, task, previous_code, error_summary):
142
+ # previous_code/error_summary are None on the first (fresh) call;
143
+ # on retries they contain the failed model and structured feedback.
144
+ return "... complete Modelica model ..."
145
+ ```
146
+
147
+ ### Run the benchmark ladder
148
+
149
+ ```bash
150
+ export ANTHROPIC_API_KEY=...
151
+ python examples/run_ladder.py # all 5 tiers
152
+ python examples/run_ladder.py --max-tier 3 # subset by difficulty
153
+ python examples/run_ladder.py --tasks dc_motor --model claude-opus-4-8
154
+ ```
155
+
156
+ Tiers: (1) pure-equation dynamics, (2) MSL component composition, (3) hybrid
157
+ events, (4) verifier-driven design — the requirement is given, the parameter
158
+ is not, (5) multi-domain electro-mechanical. Per-task JSON transcripts
159
+ (attempt history, diagnostics, code, LLM rounds) land in `transcripts/`,
160
+ with `summary.json` aggregating results.
161
+
162
+ ### Use pieces standalone
163
+
164
+ ```python
165
+ from omagent import OMSession, parse_error_string, summarize_for_llm, load_result
166
+
167
+ s = OMSession()
168
+ r = s.load_string(my_modelica_code) # honest success verdict + diagnostics
169
+ print(summarize_for_llm(r.diagnostics)) # deduplicated digest for any prompt
170
+
171
+ sim = s.simulate("MyModel", stopTime=5.0, outputFormat="csv")
172
+ res = load_result(sim.value["resultFile"])
173
+ times, x = res.series("x")
174
+ ```
175
+
176
+ ## Project layout
177
+
178
+ ```
179
+ omagent/
180
+ errors.py # omc diagnostic parsing + classification
181
+ session.py # OMSession: testable wrapper over OMPython/omc
182
+ loop.py # AgentLoop + lookup-suggestion feedback
183
+ results.py # CSV/.mat readers + quantitative verifiers
184
+ llm.py # Anthropic adapter (protocol: bring your own)
185
+ tasks.py # benchmark task ladder definitions
186
+ runner.py # ladder execution + transcript persistence
187
+ examples/ # first_run.py, run_ladder.py
188
+ tests/ # 83 unit + 4 integration tests
189
+ ```
190
+
191
+ ## Design notes
192
+
193
+ - **Testable by construction.** `OMSession` talks to any object with
194
+ `sendExpression()`; tests replay recorded omc output, so the full agent
195
+ loop is unit-tested without a compiler or an API key.
196
+ - **Both OMPython contracts.** Older OMPython returns and lets you read
197
+ `getErrorString()`; newer OMPython raises `OMCSessionException` on
198
+ error-level messages. Both yield identical structured failures.
199
+ - **Environment failures are not model failures.** The ladder runner loads
200
+ the MSL when a task requires it and reports load problems as
201
+ `environment` outcomes with zero attempts charged to the LLM.
202
+
203
+ ## Roadmap
204
+
205
+ - Warning-level quality gates (e.g. treat "initial conditions over
206
+ specified" as a verifier complaint)
207
+ - Multi-run variance measurement and cross-model comparison in the runner
208
+ - Optional MCP tool surface, composing with OMEdit's built-in MCP server
209
+ - More ladder tiers targeting thermal/fluid domains and third-party libraries
210
+
211
+ ## License
212
+
213
+ BSD-3-Clause — see [LICENSE](LICENSE).
@@ -0,0 +1,177 @@
1
+ # omagent
2
+
3
+ **LLM-assisted modeling for OpenModelica** — an open-source, headless Python
4
+ agent that turns a natural-language task into a *verified* Modelica model:
5
+
6
+ ```
7
+ natural language ──> generate ──> compile (omc) ──> simulate ──> verify physics
8
+ ▲ │
9
+ └──── structured error / verifier feedback ──┘
10
+ ```
11
+
12
+ Unlike a plain code assistant, omagent closes the loop on **physics, not just
13
+ compilation**: quantitative verifiers check trajectories against expected
14
+ behavior (final values, settling windows, bounds, overshoot), and every
15
+ failure — compiler diagnostics or physics complaints — is parsed into
16
+ structured feedback for the next fix attempt.
17
+
18
+ Key capabilities:
19
+
20
+ - **Structured omc diagnostics** — parses `getErrorString()`, simulation
21
+ logs, and OMPython's exception format into records with severity, source
22
+ location, and failure kind (syntax / lookup / type / balance / connect /
23
+ initialization / runtime)
24
+ - **Environment-grounded fix hints** — on `Class X not found`, omagent asks
25
+ omc what the parent package *actually* contains (`getClassNames`) and puts
26
+ near-miss suggestions into the fix prompt. This resolves the dominant
27
+ observed failure mode: stale library knowledge (e.g. MSL 3.2 names such as
28
+ `Basic.EMF` vs. MSL 4.x `Basic.RotationalEMF`)
29
+ - **Quantitative verification** — reads CSV or Dymola-format `.mat` results;
30
+ verifier complaints ("final value of x is 1.93, expected 2.0") drive tuning
31
+ - **Benchmark task ladder** — 5 escalating, auto-gradable tasks with full
32
+ transcript capture, in the format the OpenModelica benchmark discussion
33
+ ([OpenModelica#15385](https://github.com/OpenModelica/OpenModelica/issues/15385))
34
+ calls for
35
+ - **LLM-backend-agnostic** — the loop depends on a one-method protocol; an
36
+ Anthropic adapter ships, any provider or local model plugs in
37
+ - **Tested** — 83 unit tests run without OpenModelica installed; 4
38
+ integration tests validate against a live omc
39
+
40
+ ## Installation
41
+
42
+ Requires Python >= 3.10. The core package has zero hard dependencies;
43
+ features are opt-in extras:
44
+
45
+ ```bash
46
+ pip install -e . # parsers + loop only (no omc needed)
47
+ pip install -e ".[omc]" # + OMPython (talk to a real omc)
48
+ pip install -e ".[results]" # + scipy (.mat result files; CSV needs nothing)
49
+ pip install -e ".[llm]" # + anthropic adapter
50
+ pip install -e ".[all]" # everything, including pytest
51
+ ```
52
+
53
+ To use it against a real compiler you need
54
+ [OpenModelica](https://openmodelica.org) (tested with 1.26–1.27) with the
55
+ Modelica Standard Library installed for `omc`:
56
+
57
+ ```bash
58
+ echo 'installPackage(Modelica); getErrorString();' > /tmp/i.mos && omc /tmp/i.mos
59
+ ```
60
+
61
+ > Note: OMEdit installs the MSL for itself automatically; headless `omc`
62
+ > sessions do not. If models using `Modelica.*` fail with "Class ... not
63
+ > found", this is why.
64
+
65
+ Verify your setup:
66
+
67
+ ```bash
68
+ pytest -m "not integration" # unit tests, no omc required
69
+ pytest -m integration # against your live omc (+MSL, scipy for .mat)
70
+ ```
71
+
72
+ ## Quick start
73
+
74
+ ```python
75
+ from omagent import AgentLoop, OMSession, all_of, expect_bounds, expect_final
76
+ from omagent.llm import ClaudeLLM # or any object with .propose(...)
77
+
78
+ # physics acceptance criteria — complaints feed back into the fix loop
79
+ verifier = all_of(
80
+ expect_bounds("x", lo=-0.105, hi=0.105),
81
+ expect_final("x", 0.0, atol=0.06, rtol=0.0),
82
+ )
83
+
84
+ loop = AgentLoop(
85
+ OMSession(), # real omc via OMPython
86
+ ClaudeLLM(), # needs ANTHROPIC_API_KEY
87
+ max_attempts=4,
88
+ simulate_options={"stopTime": 10.0, "outputFormat": "csv"},
89
+ verifier=verifier,
90
+ )
91
+ result = loop.run(
92
+ "A mass-spring-damper: m = 1 kg, c = 100 N/m, d = 1 N.s/m, released "
93
+ "from x = 0.1 m at rest. Name position x and velocity v.")
94
+
95
+ print(result.success, result.model_name)
96
+ print(result.final_code)
97
+ for a in result.attempts:
98
+ print(a.n, a.stage, a.complaint)
99
+ ```
100
+
101
+ Bring your own LLM by implementing one method:
102
+
103
+ ```python
104
+ class MyLLM:
105
+ def propose(self, task, previous_code, error_summary):
106
+ # previous_code/error_summary are None on the first (fresh) call;
107
+ # on retries they contain the failed model and structured feedback.
108
+ return "... complete Modelica model ..."
109
+ ```
110
+
111
+ ### Run the benchmark ladder
112
+
113
+ ```bash
114
+ export ANTHROPIC_API_KEY=...
115
+ python examples/run_ladder.py # all 5 tiers
116
+ python examples/run_ladder.py --max-tier 3 # subset by difficulty
117
+ python examples/run_ladder.py --tasks dc_motor --model claude-opus-4-8
118
+ ```
119
+
120
+ Tiers: (1) pure-equation dynamics, (2) MSL component composition, (3) hybrid
121
+ events, (4) verifier-driven design — the requirement is given, the parameter
122
+ is not, (5) multi-domain electro-mechanical. Per-task JSON transcripts
123
+ (attempt history, diagnostics, code, LLM rounds) land in `transcripts/`,
124
+ with `summary.json` aggregating results.
125
+
126
+ ### Use pieces standalone
127
+
128
+ ```python
129
+ from omagent import OMSession, parse_error_string, summarize_for_llm, load_result
130
+
131
+ s = OMSession()
132
+ r = s.load_string(my_modelica_code) # honest success verdict + diagnostics
133
+ print(summarize_for_llm(r.diagnostics)) # deduplicated digest for any prompt
134
+
135
+ sim = s.simulate("MyModel", stopTime=5.0, outputFormat="csv")
136
+ res = load_result(sim.value["resultFile"])
137
+ times, x = res.series("x")
138
+ ```
139
+
140
+ ## Project layout
141
+
142
+ ```
143
+ omagent/
144
+ errors.py # omc diagnostic parsing + classification
145
+ session.py # OMSession: testable wrapper over OMPython/omc
146
+ loop.py # AgentLoop + lookup-suggestion feedback
147
+ results.py # CSV/.mat readers + quantitative verifiers
148
+ llm.py # Anthropic adapter (protocol: bring your own)
149
+ tasks.py # benchmark task ladder definitions
150
+ runner.py # ladder execution + transcript persistence
151
+ examples/ # first_run.py, run_ladder.py
152
+ tests/ # 83 unit + 4 integration tests
153
+ ```
154
+
155
+ ## Design notes
156
+
157
+ - **Testable by construction.** `OMSession` talks to any object with
158
+ `sendExpression()`; tests replay recorded omc output, so the full agent
159
+ loop is unit-tested without a compiler or an API key.
160
+ - **Both OMPython contracts.** Older OMPython returns and lets you read
161
+ `getErrorString()`; newer OMPython raises `OMCSessionException` on
162
+ error-level messages. Both yield identical structured failures.
163
+ - **Environment failures are not model failures.** The ladder runner loads
164
+ the MSL when a task requires it and reports load problems as
165
+ `environment` outcomes with zero attempts charged to the LLM.
166
+
167
+ ## Roadmap
168
+
169
+ - Warning-level quality gates (e.g. treat "initial conditions over
170
+ specified" as a verifier complaint)
171
+ - Multi-run variance measurement and cross-model comparison in the runner
172
+ - Optional MCP tool surface, composing with OMEdit's built-in MCP server
173
+ - More ladder tiers targeting thermal/fluid domains and third-party libraries
174
+
175
+ ## License
176
+
177
+ BSD-3-Clause — see [LICENSE](LICENSE).
@@ -0,0 +1,21 @@
1
+ """omagent — testable tooling layer for LLM-assisted OpenModelica workflows."""
2
+
3
+ from .errors import (
4
+ Diagnostic, Kind, Severity,
5
+ classify, parse_error_string, parse_ompython_exception, parse_simulation_messages, summarize_for_llm,
6
+ )
7
+ from .session import Backend, OMSession, OpResult
8
+ from .loop import AgentLoop, Attempt, LLM, LoopResult, Verifier, extract_code, extract_model_name
9
+ from .results import (SimulationResult, all_of, expect_bounds, expect_final,
10
+ expect_value_at, load_result)
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = [
14
+ "Diagnostic", "Kind", "Severity", "classify", "parse_error_string",
15
+ "parse_ompython_exception", "parse_simulation_messages", "summarize_for_llm",
16
+ "Backend", "OMSession", "OpResult",
17
+ "AgentLoop", "Attempt", "LLM", "LoopResult", "Verifier",
18
+ "extract_code", "extract_model_name",
19
+ "SimulationResult", "load_result", "expect_final", "expect_value_at",
20
+ "expect_bounds", "all_of",
21
+ ]