osprey-optimizer 2026.11.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 (81) hide show
  1. osprey_optimizer-2026.11.0/.gitignore +226 -0
  2. osprey_optimizer-2026.11.0/LICENSE +28 -0
  3. osprey_optimizer-2026.11.0/PKG-INFO +52 -0
  4. osprey_optimizer-2026.11.0/README.md +25 -0
  5. osprey_optimizer-2026.11.0/pyproject.toml +57 -0
  6. osprey_optimizer-2026.11.0/src/osprey_optimizer/__init__.py +16 -0
  7. osprey_optimizer-2026.11.0/src/osprey_optimizer/_version.py +24 -0
  8. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/__init__.py +12 -0
  9. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/dependencies.py +58 -0
  10. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/environment_ops.py +294 -0
  11. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/job_control.py +416 -0
  12. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/job_status.py +71 -0
  13. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/job_tracker.py +524 -0
  14. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/main.py +289 -0
  15. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/manual_authorization.py +210 -0
  16. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/optimization_lifecycle.py +329 -0
  17. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/optimization_status.py +157 -0
  18. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/owner.py +96 -0
  19. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/__init__.py +0 -0
  20. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/environments.py +218 -0
  21. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/jobs.py +306 -0
  22. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/models.py +64 -0
  23. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/optimization.py +39 -0
  24. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/results.py +245 -0
  25. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/routers/run_config.py +142 -0
  26. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/runner.py +58 -0
  27. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/security.py +53 -0
  28. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/start_gates.py +61 -0
  29. osprey_optimizer-2026.11.0/src/osprey_optimizer/api/summary.py +286 -0
  30. osprey_optimizer-2026.11.0/src/osprey_optimizer/config/__init__.py +0 -0
  31. osprey_optimizer-2026.11.0/src/osprey_optimizer/config/logging.py +38 -0
  32. osprey_optimizer-2026.11.0/src/osprey_optimizer/environments/__init__.py +1 -0
  33. osprey_optimizer-2026.11.0/src/osprey_optimizer/environments/connector_env.py +413 -0
  34. osprey_optimizer-2026.11.0/src/osprey_optimizer/environments/interface.py +404 -0
  35. osprey_optimizer-2026.11.0/src/osprey_optimizer/environments/spec.py +231 -0
  36. osprey_optimizer-2026.11.0/src/osprey_optimizer/environments/write_authority.py +108 -0
  37. osprey_optimizer-2026.11.0/src/osprey_optimizer/events.py +177 -0
  38. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/__init__.py +5 -0
  39. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/api.py +261 -0
  40. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/config.py +254 -0
  41. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/enums.py +98 -0
  42. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/exceptions.py +149 -0
  43. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/process_scope.py +23 -0
  44. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/protocols.py +417 -0
  45. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/state.py +44 -0
  46. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/testing.py +28 -0
  47. osprey_optimizer-2026.11.0/src/osprey_optimizer/models/types.py +75 -0
  48. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/__init__.py +1 -0
  49. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/best_point.py +113 -0
  50. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/coercion.py +45 -0
  51. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/evaluation.py +173 -0
  52. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/generators/__init__.py +11 -0
  53. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/generators/baxus.py +631 -0
  54. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/runner.py +354 -0
  55. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/state.py +401 -0
  56. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/surrogate/__init__.py +5 -0
  57. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/surrogate/constructor.py +235 -0
  58. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/surrogate/wrapper.py +162 -0
  59. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/types.py +29 -0
  60. osprey_optimizer-2026.11.0/src/osprey_optimizer/optimization/xopt_backend.py +591 -0
  61. osprey_optimizer-2026.11.0/src/osprey_optimizer/results.py +601 -0
  62. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/__init__.py +0 -0
  63. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/bounds_resolver.py +167 -0
  64. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/draft_resolution.py +108 -0
  65. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/execution.py +567 -0
  66. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/orchestration.py +286 -0
  67. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/plugin_registry.py +221 -0
  68. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/result_pipeline.py +75 -0
  69. osprey_optimizer-2026.11.0/src/osprey_optimizer/services/write_authorization.py +500 -0
  70. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/__init__.py +0 -0
  71. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/cancellation.py +220 -0
  72. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/control.py +35 -0
  73. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/interval_thread.py +78 -0
  74. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/job_keys.py +57 -0
  75. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/json_safe.py +35 -0
  76. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/log_capture.py +64 -0
  77. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/numeric.py +14 -0
  78. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/process.py +88 -0
  79. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/redis_client.py +95 -0
  80. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/redis_state_store.py +149 -0
  81. osprey_optimizer-2026.11.0/src/osprey_optimizer/utils/serialization.py +24 -0
@@ -0,0 +1,226 @@
1
+ # Scratch notes (local idea capture)
2
+ scratch/
3
+
4
+ # NFS temporary files
5
+ .nfs*
6
+
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ /build/
18
+ develop-eggs/
19
+ /dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+
33
+ # PyInstaller
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .coverage
45
+ .coverage*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ coverage.json
50
+ *.cover
51
+ .hypothesis/
52
+
53
+ # CI diagnostics drop (per-worker event logs, stack dumps, container logs).
54
+ # Written by _harness/ci_diagnostics.py and .github/actions/capture-ci-diagnostics
55
+ # whenever OSPREY_CI_DIAG_DIR is set; uploaded as an artifact, never committed.
56
+ /ci-diag/
57
+ .pytest_cache/
58
+ .mypy_cache/
59
+ # The import graph lint-imports (import-linter) caches between runs.
60
+ .import_linter_cache/
61
+
62
+ # Jupyter Notebook
63
+ .ipynb_checkpoints
64
+
65
+ # Environment directories
66
+ .env
67
+ .venv
68
+ env/
69
+ venv/
70
+ ENV/
71
+ env.bak/
72
+ venv.bak/
73
+
74
+ # IDE specific files
75
+ .idea/
76
+ .vscode/
77
+ *.swp
78
+ *.swo
79
+
80
+ # OS specific files
81
+ .DS_Store
82
+ .DS_Store?
83
+ ._*
84
+ .Spotlight-V100
85
+ .Trashes
86
+ ehthumbs.db
87
+ Thumbs.db
88
+
89
+ # Data, models, and results
90
+ !data/.gitkeep
91
+ # models/ - Removed this to allow tracking model_config.py
92
+ !models/.gitkeep
93
+ results/
94
+ !results/.gitkeep
95
+ # Any source dir named results/ is SOURCE, not ML output — the broad unanchored
96
+ # results/ ignore above must not swallow it (this negation is deliberately
97
+ # path-independent: pinning it to one package broke when the bluesky-web
98
+ # sidecar moved services/ -> interfaces/, silently dropping the panel from the move).
99
+ !src/**/results/
100
+ # Benchmark model-matrix run exhaust (#259) — regenerable from scripts/benchmark/.
101
+ # All generated artifacts (dashboard.html, results_box/, live_updater.log) live
102
+ # under one output dir co-located with the tooling, keeping the repo root clean.
103
+ scripts/benchmark/_out/
104
+ *.h5
105
+ *.pkl
106
+ *.pt
107
+ *.pth
108
+ *.onnx
109
+ *.mlmodel
110
+
111
+ # Logs
112
+ logs/
113
+ *.log
114
+ !tests/deployment/fixtures/*.log
115
+ tensorboard/
116
+
117
+ # Temporary files
118
+ tmp/
119
+ temp/
120
+
121
+ *.jpg
122
+ *.gif
123
+ *.bmp
124
+ *.tiff
125
+ *.ico
126
+ # The demo template's CANOPY screens ship their images.
127
+ !src/osprey/templates/apps/demo/data/canopy/library/screens/**/images/*.jpg
128
+
129
+ # Landing-page demo video: recorded takes are never committed; the docs build
130
+ # downloads the MP4s from the release manifest.json names. The posters and
131
+ # manifest.json are committed, so the posters must override the `*.jpg` rule.
132
+ docs/demo-video/
133
+ docs/source/_static/demo/*.mp4
134
+ !docs/source/_static/demo/*-poster.jpg
135
+
136
+ # Credentials
137
+ .env
138
+
139
+ delete_me*
140
+ valves.json
141
+ *.code-workspace
142
+
143
+ # Agent data directories
144
+ _agent_data/*
145
+ # Track only the Codex marketplace manifest; never Codex plugin state.
146
+ .agents/*
147
+ !.agents/plugins/
148
+ .agents/plugins/*
149
+ !.agents/plugins/marketplace.json
150
+
151
+ # Local coding-agent guidance (kept out of git; see /CLAUDE.md below)
152
+ AGENTS.md
153
+
154
+ # Sample logbook data (large JSONL files)
155
+ logbook_*.jsonl
156
+
157
+ # Auto-generated documentation
158
+ _autosummary/
159
+ docs/build/
160
+
161
+ # PID files
162
+ *.pid
163
+
164
+ # Development services and applications (keep locally but not in git)
165
+ /.claude
166
+ _ISSUES
167
+ /CLAUDE.md
168
+ .mcp.json
169
+
170
+ # Git worktrees
171
+ .worktrees/
172
+
173
+ # Generated benchmark databases
174
+ data/benchmarks/cross_paradigm/tier*/
175
+
176
+ # OSPREY workspace (runtime data, not tracked — now covered by _agent_data/* above)
177
+
178
+ # Vendor assets (fetched at build time via `osprey vendor fetch`)
179
+ src/osprey/interfaces/artifacts/static/js/vendor/
180
+ src/osprey/interfaces/artifacts/static/css/vendor/
181
+ src/osprey/interfaces/web_terminal/static/vendor/*
182
+ # The dock engine is tracked: vendored from CANOPY's screen runtime at a pinned commit.
183
+ !src/osprey/interfaces/web_terminal/static/vendor/dock-engine.mjs
184
+ !src/osprey/interfaces/web_terminal/static/vendor/dock-engine.d.mts
185
+ src/osprey/interfaces/bluesky_web/panels/bluesky/vendor/
186
+ src/osprey/interfaces/optimizer_web/panels/optimizer/vendor/
187
+ # The okf panel's own vendored marked.min.js is tracked; only DOMPurify is fetched.
188
+ src/osprey/interfaces/okf_panel/static/vendor/purify.min.js
189
+ tests/e2e/claude_code/benchmark_results/
190
+
191
+ # Model files staged by hand for hosts that cannot reach the model host. They
192
+ # reach the qmd sidecar over the read-only mount `services.qmd.models_dir`
193
+ # names, never through a build context — but the staging directory itself can
194
+ # sit anywhere, this tree included. Gigabytes of GGUF, never source — keep them
195
+ # out of git and out of the build (see also the hatch exclude in pyproject.toml)
196
+ **/prefetched-models/
197
+
198
+ # Playwright MCP browser-session cache (page snapshots, console logs)
199
+ .playwright-mcp/
200
+
201
+ # Node.js (dev-only JS toolchain; not part of the Python wheel)
202
+ node_modules/
203
+
204
+ # Working directory gitlab-ci-local writes when the deploy goldens are checked
205
+ # against a real pipeline parser. Created wherever the check is run from.
206
+ .gitlab-ci-local/
207
+
208
+ # Version stamp written by the hatch-vcs build hook. Committing it would recreate
209
+ # the stale-literal problem the git-derived version exists to remove. It is still
210
+ # packaged into the wheel and sdist — the hook registers it as a build artifact.
211
+ src/osprey/_version.py
212
+ packages/osprey-connectors/src/osprey_connectors/_version.py
213
+ packages/osprey-optimizer/src/osprey_optimizer/_version.py
214
+ packages/pyaml-cs-osprey/src/pyaml_cs_osprey/_version.py
215
+
216
+ # feature-scaffolding logs (not committed)
217
+ .claude/.logs/
218
+
219
+ # Process scaffolding — never committed (specs/plans live here uncommitted)
220
+ docs/superpowers/
221
+
222
+ # Locally scaffolded deployment project. Carries a real .env, build output and
223
+ # run data; belongs to the working copy, never to the repo. Anchored to the root
224
+ # so the pattern cannot swallow a same-named path elsewhere in the tree.
225
+ /my-control-assistant/
226
+ /osprey-demo/
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.5
2
+ Name: osprey-optimizer
3
+ Version: 2026.11.0
4
+ Summary: Xopt-driven machine optimization service for OSPREY
5
+ License: BSD-3-Clause
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: osprey-connectors>=2026.11.11
9
+ Requires-Dist: pydantic>=2.12.5
10
+ Requires-Dist: pyyaml>=6.0.2
11
+ Provides-Extra: service
12
+ Requires-Dist: botorch==0.17.2; extra == 'service'
13
+ Requires-Dist: fastapi>=0.141.1; extra == 'service'
14
+ Requires-Dist: gpytorch==1.15.2; extra == 'service'
15
+ Requires-Dist: joblib>=1.5.2; extra == 'service'
16
+ Requires-Dist: numpy>=2.2.6; extra == 'service'
17
+ Requires-Dist: pandas>=2.2.3; extra == 'service'
18
+ Requires-Dist: polars>=2.0.0; extra == 'service'
19
+ Requires-Dist: python-multipart>=0.0.32; extra == 'service'
20
+ Requires-Dist: redis>=5.0.4; extra == 'service'
21
+ Requires-Dist: scikit-learn>=1.8.0; extra == 'service'
22
+ Requires-Dist: starlette; extra == 'service'
23
+ Requires-Dist: torch==2.13.0; extra == 'service'
24
+ Requires-Dist: uvicorn[standard]>=0.52.1; extra == 'service'
25
+ Requires-Dist: xopt==3.1.2; extra == 'service'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # osprey-optimizer
29
+
30
+ The OSPREY optimizer service: Xopt-driven tuning runs against a control
31
+ system, with every write going through an OSPREY connector, so the write
32
+ switch, channel limits and `max_step` apply to each one. An optimization
33
+ environment is a YAML file of setpoint/readback channels and observables;
34
+ Python environments register on the `osprey.optimizer.environments`
35
+ entry-point group and are opt-in: the service loads only those named in
36
+ `OPTIMIZER_PLUGINS` (comma-separated). A plugin must reach the machine through
37
+ `ConnectorInterface`; that is not yet enforced for plugin code.
38
+
39
+ ## Install
40
+
41
+ ```
42
+ pip install osprey-optimizer # models and environment files only
43
+ pip install "osprey-optimizer[service]" # the engine and its HTTP service
44
+ ```
45
+
46
+ The base install is what the OSPREY framework imports: the request, config
47
+ and status models, and the environment-file format. It does not load the
48
+ search stack. The `service` extra adds Xopt, PyTorch, BoTorch, Redis and
49
+ FastAPI, and is what the optimizer's container image installs. For a
50
+ CPU-only PyTorch, add `--extra-index-url https://download.pytorch.org/whl/cpu`.
51
+
52
+ The service app is `osprey_optimizer.api.main:app`.
@@ -0,0 +1,25 @@
1
+ # osprey-optimizer
2
+
3
+ The OSPREY optimizer service: Xopt-driven tuning runs against a control
4
+ system, with every write going through an OSPREY connector, so the write
5
+ switch, channel limits and `max_step` apply to each one. An optimization
6
+ environment is a YAML file of setpoint/readback channels and observables;
7
+ Python environments register on the `osprey.optimizer.environments`
8
+ entry-point group and are opt-in: the service loads only those named in
9
+ `OPTIMIZER_PLUGINS` (comma-separated). A plugin must reach the machine through
10
+ `ConnectorInterface`; that is not yet enforced for plugin code.
11
+
12
+ ## Install
13
+
14
+ ```
15
+ pip install osprey-optimizer # models and environment files only
16
+ pip install "osprey-optimizer[service]" # the engine and its HTTP service
17
+ ```
18
+
19
+ The base install is what the OSPREY framework imports: the request, config
20
+ and status models, and the environment-file format. It does not load the
21
+ search stack. The `service` extra adds Xopt, PyTorch, BoTorch, Redis and
22
+ FastAPI, and is what the optimizer's container image installs. For a
23
+ CPU-only PyTorch, add `--extra-index-url https://download.pytorch.org/whl/cpu`.
24
+
25
+ The service app is `osprey_optimizer.api.main:app`.
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["hatchling", "hatch-vcs"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "osprey-optimizer"
7
+ dynamic = ["version"]
8
+ description = "Xopt-driven machine optimization service for OSPREY"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = {text = "BSD-3-Clause"}
12
+ # The base is the optimizer's contracts: the request, config and status models
13
+ # and the environment-file format. The framework imports these (MCP tool
14
+ # schemas, approval previews, the build-time environment check), so they must
15
+ # load without the search stack below.
16
+ dependencies = [
17
+ "osprey-connectors>=2026.11.11",
18
+ "pydantic>=2.12.5",
19
+ "PyYAML>=6.0.2",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ # The engine and its HTTP service: installed only in the optimizer's own image.
24
+ # Torch and the BoTorch stack are gigabytes. The workspace resolves torch from
25
+ # the CPU wheel index (root pyproject.toml, tool.uv.sources); a pip install from
26
+ # PyPI has to pass that index itself.
27
+ service = [
28
+ "xopt==3.1.2",
29
+ "torch==2.13.0",
30
+ "botorch==0.17.2",
31
+ "gpytorch==1.15.2",
32
+ "polars>=2.0.0",
33
+ "pandas>=2.2.3",
34
+ "numpy>=2.2.6",
35
+ "scikit-learn>=1.8.0",
36
+ "joblib>=1.5.2",
37
+ "redis>=5.0.4",
38
+ "fastapi>=0.141.1",
39
+ "starlette", # imported directly by the app (middleware, responses)
40
+ "uvicorn[standard]>=0.52.1",
41
+ # FastAPI parses the model upload's form body with it and refuses to build
42
+ # that route without it, so the app does not import.
43
+ "python-multipart>=0.0.32",
44
+ ]
45
+
46
+ [tool.hatch.version]
47
+ source = "vcs"
48
+ # Versions with the framework's calendar stream from the same v-tags (unlike
49
+ # osprey-connectors, which has its own osprey-connectors-v* stream): one tag
50
+ # builds the framework and optimizer wheels, and they carry the same number.
51
+ raw-options = { root = "../..", version_scheme = "post-release", git_describe_command = "git describe --dirty --tags --long --abbrev=40 --match v[0-9]*" }
52
+
53
+ [tool.hatch.build.hooks.vcs]
54
+ version-file = "src/osprey_optimizer/_version.py"
55
+
56
+ [tool.hatch.build.targets.wheel]
57
+ packages = ["src/osprey_optimizer"]
@@ -0,0 +1,16 @@
1
+ """Optimizer service: Xopt-driven tuning jobs behind a FastAPI job API.
2
+
3
+ Ported from tuning_scripts at commit 1bac27a1.
4
+
5
+ The base install carries the contracts: ``models`` and ``environments`` load
6
+ with pydantic, PyYAML and osprey-connectors only, and the OSPREY framework
7
+ imports them. The search stack (xopt, torch, botorch, polars, redis, fastapi)
8
+ is the ``service`` extra, installed only in the optimizer's image.
9
+
10
+ Sub-packages:
11
+ - models: request/response models, environment protocols, enums
12
+ - environments: environment files and the connector-driven environment
13
+ - api: FastAPI app, routers, job tracking and control (needs ``service``)
14
+ - optimization: phase runner, evaluator, Xopt backend, custom generators
15
+ - services: execution and orchestration inside the job worker
16
+ """
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '2026.11.0'
22
+ __version_tuple__ = version_tuple = (2026, 11, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -0,0 +1,12 @@
1
+ """The optimizer's HTTP service. Needs the ``service`` extra."""
2
+
3
+ import importlib.util
4
+
5
+ _SERVICE_ROOTS = ("fastapi", "redis", "polars", "xopt", "torch")
6
+
7
+ _missing = [name for name in _SERVICE_ROOTS if importlib.util.find_spec(name) is None]
8
+ if _missing:
9
+ raise ImportError(
10
+ f"osprey_optimizer.api needs the optimizer's service extra (missing: {', '.join(_missing)}). "
11
+ 'Install it with: pip install "osprey-optimizer[service]"'
12
+ )
@@ -0,0 +1,58 @@
1
+ """FastAPI dependency callables for shared singletons.
2
+
3
+ Each function reads from ``request.app.state``, which is populated by the
4
+ lifespan handler in ``main.py``. Use them with ``Depends()`` in endpoint
5
+ signatures so that routers stay decoupled from initialization logic.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from typing import Any
10
+
11
+ from fastapi import Depends, Query, Request
12
+
13
+ from osprey_optimizer.api.job_status import load_job_meta
14
+ from osprey_optimizer.api.job_tracker import JobMeta, JobTracker
15
+ from osprey_optimizer.models.config import ControlTarget, ExecutionMode, machine_config
16
+ from osprey_optimizer.services.plugin_registry import EnvironmentRegistry
17
+
18
+
19
+ def get_job_tracker(request: Request) -> JobTracker:
20
+ return request.app.state.job_tracker # type: ignore[no-any-return]
21
+
22
+
23
+ def require_job_meta(job_id: str, tracker: JobTracker = Depends(get_job_tracker)) -> JobMeta:
24
+ """Resolve the path job's metadata once (404 if unknown, 500 if corrupt).
25
+
26
+ Endpoints under ``/jobs/{job_id}`` inject this instead of re-checking
27
+ existence and re-reading the record: FastAPI caches it for the request, so
28
+ the type/creation-time checks they used to make one call each now share a
29
+ single Redis read.
30
+ """
31
+ return load_job_meta(tracker, job_id)
32
+
33
+
34
+ def get_environment_registry(request: Request) -> EnvironmentRegistry:
35
+ return request.app.state.environment_registry # type: ignore[no-any-return]
36
+
37
+
38
+ @dataclass(frozen=True)
39
+ class Machine:
40
+ """The machine a request reaches, as the request named it."""
41
+
42
+ target: ControlTarget
43
+ execution_mode: ExecutionMode | None = None
44
+
45
+ def env_config(self) -> dict[str, Any]:
46
+ return machine_config(self.target, self.execution_mode)
47
+
48
+
49
+ def require_machine(
50
+ target: ControlTarget = Query(
51
+ description="The machine this call reaches: va, standin or live. No default."
52
+ ),
53
+ execution_mode: ExecutionMode | None = Query(
54
+ default=None, description="readonly: reads only; every write is refused."
55
+ ),
56
+ ) -> Machine:
57
+ """Every endpoint that reaches a machine names it; there is no ambient target."""
58
+ return Machine(target=target, execution_mode=execution_mode)