labpilot-ai 0.1.2__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.
- labpilot_ai-0.1.2/PKG-INFO +150 -0
- labpilot_ai-0.1.2/README.md +121 -0
- labpilot_ai-0.1.2/labpilot_ai/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/__main__.py +4 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/co_sequence_prompt.py +86 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/command_schema.py +30 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/error_advisor.py +39 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/json_parser.py +14 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/llm_client.py +127 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/prompt_builder.py +95 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/protocol_designer.py +23 -0
- labpilot_ai-0.1.2/labpilot_ai/ai/protocol_importer.py +57 -0
- labpilot_ai-0.1.2/labpilot_ai/analysis/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/analysis/fit_models.py +186 -0
- labpilot_ai-0.1.2/labpilot_ai/analysis/plotting.py +89 -0
- labpilot_ai-0.1.2/labpilot_ai/analysis/report_generator.py +61 -0
- labpilot_ai-0.1.2/labpilot_ai/app/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/app/error_center.py +167 -0
- labpilot_ai-0.1.2/labpilot_ai/app/main_window.py +3885 -0
- labpilot_ai-0.1.2/labpilot_ai/app/theme.py +105 -0
- labpilot_ai-0.1.2/labpilot_ai/blacs_ctrl/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/blacs_ctrl/manual_bridge_server.py +47 -0
- labpilot_ai-0.1.2/labpilot_ai/blacs_ctrl/manual_client.py +31 -0
- labpilot_ai-0.1.2/labpilot_ai/bootstrap_labscript.py +21 -0
- labpilot_ai-0.1.2/labpilot_ai/co_sequence/__init__.py +17 -0
- labpilot_ai-0.1.2/labpilot_ai/co_sequence/log_store.py +90 -0
- labpilot_ai-0.1.2/labpilot_ai/co_sequence/patch_validator.py +340 -0
- labpilot_ai-0.1.2/labpilot_ai/config/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/config/registry_editor.py +315 -0
- labpilot_ai-0.1.2/labpilot_ai/config/settings_manager.py +100 -0
- labpilot_ai-0.1.2/labpilot_ai/directory/__init__.py +5 -0
- labpilot_ai-0.1.2/labpilot_ai/directory/path_registry.py +186 -0
- labpilot_ai-0.1.2/labpilot_ai/experiment_log/__init__.py +5 -0
- labpilot_ai-0.1.2/labpilot_ai/experiment_log/generator.py +145 -0
- labpilot_ai-0.1.2/labpilot_ai/knowledge/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/knowledge/context_builder.py +43 -0
- labpilot_ai-0.1.2/labpilot_ai/knowledge/indexer.py +290 -0
- labpilot_ai-0.1.2/labpilot_ai/knowledge/search.py +99 -0
- labpilot_ai-0.1.2/labpilot_ai/label.png +0 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/h5_loader.py +50 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/module_manager.py +55 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/multi_runner.py +19 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/result_store.py +55 -0
- labpilot_ai-0.1.2/labpilot_ai/lyse_ctrl/single_runner.py +22 -0
- labpilot_ai-0.1.2/labpilot_ai/main.py +26 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/auto_loop.py +259 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/base.py +30 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/bayesian.py +46 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/experiment_loop.py +55 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/grid_search.py +20 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/history.py +27 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/lyse_feedback.py +91 -0
- labpilot_ai-0.1.2/labpilot_ai/optimizer/objective.py +116 -0
- labpilot_ai-0.1.2/labpilot_ai/protocol/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/protocol/protocol_store.py +8 -0
- labpilot_ai-0.1.2/labpilot_ai/runmanager_ctrl/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/runmanager_ctrl/backend.py +86 -0
- labpilot_ai-0.1.2/labpilot_ai/runmanager_ctrl/scan_builder.py +9 -0
- labpilot_ai-0.1.2/labpilot_ai/safety/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/safety/audit_log.py +14 -0
- labpilot_ai-0.1.2/labpilot_ai/safety/validator.py +375 -0
- labpilot_ai-0.1.2/labpilot_ai/storage/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/storage/database.py +273 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/blacs_manual_registry.yaml +12 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/global_registry.yaml +17 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/lyse_registry.yaml +16 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/project_settings.yaml +56 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/safety_rules.yaml +2 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/configs/voice_lexicon.yaml +8 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/00_overview/architecture.md +44 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/00_overview/project_structure.md +54 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/00_overview/quick_start.md +60 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/01_voice/cpu_gpu_stt.md +78 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/01_voice/cuda_troubleshooting.md +96 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/01_voice/lexicon_terms.md +44 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/01_voice/manual_recording.md +45 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/01_voice/wake_standby.md +34 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/02_ui/co_sequence.md +39 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/02_ui/command_diagnostics.md +44 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/02_ui/industrial_ui.md +42 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/03_safety_ai/action_schema.md +101 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/03_safety_ai/llm_client.md +51 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/03_safety_ai/safety_validator.md +48 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/04_labscript_interfaces/blacs.md +50 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/04_labscript_interfaces/lyse_data.md +68 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/04_labscript_interfaces/runmanager.md +51 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/05_analysis_optimizer/optimizer.md +109 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/05_analysis_optimizer/plotting_fitting.md +73 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/05_analysis_optimizer/protocol_designer.md +31 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/05_analysis_optimizer/protocol_report.md +40 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/06_configuration/directory.md +28 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/06_configuration/knowledge_sources.md +34 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/06_configuration/registry_editor.md +32 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/06_configuration/settings_and_registries.md +87 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/06_configuration/voice_settings.md +73 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/07_development/error_center.md +24 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/07_development/experiment_log.md +43 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/07_development/package_templates.md +38 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/07_development/packaging.md +69 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/07_development/tests.md +85 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/manual/README.md +41 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/plugins/multi_modules/example_multi.py +2 -0
- labpilot_ai-0.1.2/labpilot_ai/templates/plugins/single_modules/example_single.py +2 -0
- labpilot_ai-0.1.2/labpilot_ai/utils/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/utils/json_utils.py +18 -0
- labpilot_ai-0.1.2/labpilot_ai/utils/paths.py +20 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/__init__.py +1 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/cuda_paths.py +128 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/diagnostics.py +138 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/lexicon.py +138 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/recorder.py +107 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/stt_backend.py +172 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/stt_worker.py +75 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/vad.py +27 -0
- labpilot_ai-0.1.2/labpilot_ai/voice/wake_agent.py +32 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/PKG-INFO +150 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/SOURCES.txt +139 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/dependency_links.txt +1 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/entry_points.txt +2 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/requires.txt +26 -0
- labpilot_ai-0.1.2/labpilot_ai.egg-info/top_level.txt +1 -0
- labpilot_ai-0.1.2/pyproject.toml +47 -0
- labpilot_ai-0.1.2/setup.cfg +4 -0
- labpilot_ai-0.1.2/tests/test_analysis_results.py +45 -0
- labpilot_ai-0.1.2/tests/test_auto_loop.py +135 -0
- labpilot_ai-0.1.2/tests/test_co_sequence_directory_experiment_log.py +93 -0
- labpilot_ai-0.1.2/tests/test_error_center.py +44 -0
- labpilot_ai-0.1.2/tests/test_knowledge.py +87 -0
- labpilot_ai-0.1.2/tests/test_lyse_modules.py +18 -0
- labpilot_ai-0.1.2/tests/test_objective.py +18 -0
- labpilot_ai-0.1.2/tests/test_optimizer.py +71 -0
- labpilot_ai-0.1.2/tests/test_paths.py +7 -0
- labpilot_ai-0.1.2/tests/test_protocol_importer.py +50 -0
- labpilot_ai-0.1.2/tests/test_registry_editor.py +149 -0
- labpilot_ai-0.1.2/tests/test_release_hardening.py +47 -0
- labpilot_ai-0.1.2/tests/test_safety.py +70 -0
- labpilot_ai-0.1.2/tests/test_ui_imports.py +23 -0
- labpilot_ai-0.1.2/tests/test_voice.py +145 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: labpilot-ai
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Industrial AI copilot for labscript-suite experiments: voice commands, runmanager/BLACS/lyse control, optimization, Knowledge, Co-Sequence, and supervised logs.
|
|
5
|
+
Author: LabPilot AI Developers
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: PyQt5>=5.15
|
|
9
|
+
Requires-Dist: openai>=1.0
|
|
10
|
+
Requires-Dist: PyYAML>=6.0
|
|
11
|
+
Requires-Dist: numpy>=1.23
|
|
12
|
+
Requires-Dist: pandas>=1.5
|
|
13
|
+
Requires-Dist: h5py>=3.8
|
|
14
|
+
Requires-Dist: matplotlib>=3.6
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == "dev"
|
|
17
|
+
Requires-Dist: black; extra == "dev"
|
|
18
|
+
Requires-Dist: ruff; extra == "dev"
|
|
19
|
+
Provides-Extra: fit
|
|
20
|
+
Requires-Dist: scipy>=1.9; extra == "fit"
|
|
21
|
+
Provides-Extra: voice
|
|
22
|
+
Requires-Dist: sounddevice; extra == "voice"
|
|
23
|
+
Requires-Dist: faster-whisper; extra == "voice"
|
|
24
|
+
Provides-Extra: opt
|
|
25
|
+
Requires-Dist: scikit-optimize; extra == "opt"
|
|
26
|
+
Requires-Dist: optuna; extra == "opt"
|
|
27
|
+
Provides-Extra: docs
|
|
28
|
+
Requires-Dist: pypdf; extra == "docs"
|
|
29
|
+
|
|
30
|
+
# LabPilot AI
|
|
31
|
+
|
|
32
|
+
LabPilot AI is an industrial-style local AI copilot for labscript-suite experiments. It does not modify labscript, runmanager, BLACS, or lyse internals. Instead, it wraps stable boundaries: registry whitelists, local safety validation, runmanager/BLACS/lyse adapters, HDF5-compatible data, SQLite/JSONL logs, and a PyQt5 desktop UI.
|
|
33
|
+
|
|
34
|
+
The first release is designed to be safe by default: Mock LLM, Mock runmanager/BLACS, and Dry-run workflows can demonstrate the full loop before any real hardware action is enabled.
|
|
35
|
+
|
|
36
|
+
## Main Features
|
|
37
|
+
|
|
38
|
+
- Industrial PyQt5 UI: Command, Runmanager, BLACS Manual, Lyse, Optimizer, Co-Sequence, Experiment Log, Protocol, Knowledge, Directory, Diagnostics, Error Center, and Settings.
|
|
39
|
+
- Voice input: manual recording, standby wake mode, Chinese/English transcription, CPU/GPU faster-whisper backends, scientific-term correction, and microphone signal display.
|
|
40
|
+
- AI command schema: strict JSON actions using `type`, including `set_global`, `set_blacs_manual`, `engage`, `load_h5`, `run_single_lyse`, `run_multi_lyse`, `plot`, `fit`, `start_optimization`, and `generate_report`.
|
|
41
|
+
- Safety layer: whitelist validation, type/range/array checks, high-risk confirmations, dry-run previews, diffs, rollback hooks, SQLite audit records, and Error Center reporting.
|
|
42
|
+
- runmanager control: globals read/write, float/int/bool/array scan handling, shot preview, and engage through adapters.
|
|
43
|
+
- BLACS manual control: mock/localhost bridge client, limited to registered manual channels.
|
|
44
|
+
- lyse/HDF5 workflow: H5 folder loading, single/multi module selection, result table merging, JSONL/SQLite cache, plots, fits, and reports.
|
|
45
|
+
- Optimizer: grid and Bayesian ask/tell, plus supervised auto loop: set parameters, engage, wait for new H5, run checked lyse modules, evaluate objective, and continue safely.
|
|
46
|
+
- Knowledge base: local SQLite FTS index over sequence code, connection table, lyse modules, labscript source, manuals, papers, and registries. Remote LLMs only receive short retrieved snippets.
|
|
47
|
+
- Protocol Designer: import text/Markdown/PDF text and image paths to generate experiment-design suggestions without executing them.
|
|
48
|
+
- Co-Sequence: AI-assisted restricted diffs for only the active sequence file and active connection table, with validation, backup, review, color tags, and change logs.
|
|
49
|
+
- Experiment Log: daily Markdown/LaTeX/DokuWiki logs from natural-language commands, run records, optimizer/analysis records, Co-Sequence changes, errors, and Knowledge snippets.
|
|
50
|
+
- Directory: the primary path console for active sequence, connection table, globals, BLACS context, lyse folders, H5 output, Knowledge sources, manuals, papers, and log folders.
|
|
51
|
+
- Package templates: PyPI installs include default configs, example plugins, `label.png`, and the manual templates.
|
|
52
|
+
|
|
53
|
+
## First-Release Limits
|
|
54
|
+
|
|
55
|
+
- LabPilot AI does not modify labscript-suite internals.
|
|
56
|
+
- Co-Sequence never edits arbitrary files; it is restricted to the active sequence `.py` and active connection table `.py`.
|
|
57
|
+
- Co-Sequence does not automatically run experiments after code edits.
|
|
58
|
+
- lyse fitting and plotting parameters are stored in LabPilot analysis records, JSONL, SQLite, and reports. The first release does not write fitting results back into original H5 files.
|
|
59
|
+
- Knowledge snippets are short context references only; LabPilot AI does not execute arbitrary source code from the Knowledge database.
|
|
60
|
+
- Real runmanager/BLACS/lyse hardware workflows must be validated in the lab in stages: low-risk globals, one shot, small grid scan, then BLACS single-channel tests.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
Use the labscript conda environment or another environment that can import the labscript suite:
|
|
65
|
+
|
|
66
|
+
```powershell
|
|
67
|
+
conda activate labscript
|
|
68
|
+
cd E:\Labpilot\labpilot_ai
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Optional extras:
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
pip install -e ".[voice]" # sounddevice + faster-whisper
|
|
76
|
+
pip install -e ".[fit,opt]" # scipy + scikit-optimize/optuna
|
|
77
|
+
pip install -e ".[docs]" # pypdf
|
|
78
|
+
pip install -e ".[dev]" # pytest/black/ruff
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Launch:
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
labpilot-ai
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
or:
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
python -m labpilot_ai
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Configuration
|
|
94
|
+
|
|
95
|
+
DeepSeek/OpenAI-compatible settings can be entered in the UI or provided as environment variables:
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
$env:DEEPSEEK_API_KEY="your key"
|
|
99
|
+
$env:DEEPSEEK_BASE_URL="https://api.deepseek.com"
|
|
100
|
+
$env:DEEPSEEK_MODEL="deepseek-v4-pro"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If no key is available, keep `Mock LLM` enabled to test parsing and safety flows offline.
|
|
104
|
+
|
|
105
|
+
## Project Templates
|
|
106
|
+
|
|
107
|
+
On first run, click `Init/repair project templates` in Settings to copy clean local templates:
|
|
108
|
+
|
|
109
|
+
- `configs/global_registry.yaml`
|
|
110
|
+
- `configs/blacs_manual_registry.yaml`
|
|
111
|
+
- `configs/lyse_registry.yaml`
|
|
112
|
+
- `configs/project_settings.yaml`
|
|
113
|
+
- `plugins/single_modules/`
|
|
114
|
+
- `plugins/multi_modules/`
|
|
115
|
+
- `manual/`
|
|
116
|
+
|
|
117
|
+
Existing local files are not overwritten. Registry saves create `.bak` backups.
|
|
118
|
+
|
|
119
|
+
## Voice And CUDA Notes
|
|
120
|
+
|
|
121
|
+
CPU mode is the most stable default:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
voice:
|
|
125
|
+
device: "cpu"
|
|
126
|
+
compute_type: "int8"
|
|
127
|
+
model_size: "small"
|
|
128
|
+
isolated_stt: true
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
GPU STT on RTX cards can use:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
voice:
|
|
135
|
+
device: "cuda"
|
|
136
|
+
gpu_compute_type: "float16"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If Windows reports `Library cublas64_12.dll is not found or cannot be loaded`, run Diagnostics. LabPilot AI includes CUDA DLL path discovery for CUDA Toolkit, `nvidia-cublas-cu12`, `nvidia-cudnn-cu12`, and CTranslate2 directories. See `manual/01_voice/cuda_troubleshooting.md`.
|
|
140
|
+
|
|
141
|
+
## Release Checks
|
|
142
|
+
|
|
143
|
+
```powershell
|
|
144
|
+
cd E:\Labpilot\labpilot_ai
|
|
145
|
+
python -m compileall -q labpilot_ai
|
|
146
|
+
python -m pytest -q
|
|
147
|
+
git diff --check
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Before publishing, confirm the wheel/sdist does not contain API keys, experiment H5 data, `labpilot_outputs/`, pycache, build folders, or egg-info.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# LabPilot AI
|
|
2
|
+
|
|
3
|
+
LabPilot AI is an industrial-style local AI copilot for labscript-suite experiments. It does not modify labscript, runmanager, BLACS, or lyse internals. Instead, it wraps stable boundaries: registry whitelists, local safety validation, runmanager/BLACS/lyse adapters, HDF5-compatible data, SQLite/JSONL logs, and a PyQt5 desktop UI.
|
|
4
|
+
|
|
5
|
+
The first release is designed to be safe by default: Mock LLM, Mock runmanager/BLACS, and Dry-run workflows can demonstrate the full loop before any real hardware action is enabled.
|
|
6
|
+
|
|
7
|
+
## Main Features
|
|
8
|
+
|
|
9
|
+
- Industrial PyQt5 UI: Command, Runmanager, BLACS Manual, Lyse, Optimizer, Co-Sequence, Experiment Log, Protocol, Knowledge, Directory, Diagnostics, Error Center, and Settings.
|
|
10
|
+
- Voice input: manual recording, standby wake mode, Chinese/English transcription, CPU/GPU faster-whisper backends, scientific-term correction, and microphone signal display.
|
|
11
|
+
- AI command schema: strict JSON actions using `type`, including `set_global`, `set_blacs_manual`, `engage`, `load_h5`, `run_single_lyse`, `run_multi_lyse`, `plot`, `fit`, `start_optimization`, and `generate_report`.
|
|
12
|
+
- Safety layer: whitelist validation, type/range/array checks, high-risk confirmations, dry-run previews, diffs, rollback hooks, SQLite audit records, and Error Center reporting.
|
|
13
|
+
- runmanager control: globals read/write, float/int/bool/array scan handling, shot preview, and engage through adapters.
|
|
14
|
+
- BLACS manual control: mock/localhost bridge client, limited to registered manual channels.
|
|
15
|
+
- lyse/HDF5 workflow: H5 folder loading, single/multi module selection, result table merging, JSONL/SQLite cache, plots, fits, and reports.
|
|
16
|
+
- Optimizer: grid and Bayesian ask/tell, plus supervised auto loop: set parameters, engage, wait for new H5, run checked lyse modules, evaluate objective, and continue safely.
|
|
17
|
+
- Knowledge base: local SQLite FTS index over sequence code, connection table, lyse modules, labscript source, manuals, papers, and registries. Remote LLMs only receive short retrieved snippets.
|
|
18
|
+
- Protocol Designer: import text/Markdown/PDF text and image paths to generate experiment-design suggestions without executing them.
|
|
19
|
+
- Co-Sequence: AI-assisted restricted diffs for only the active sequence file and active connection table, with validation, backup, review, color tags, and change logs.
|
|
20
|
+
- Experiment Log: daily Markdown/LaTeX/DokuWiki logs from natural-language commands, run records, optimizer/analysis records, Co-Sequence changes, errors, and Knowledge snippets.
|
|
21
|
+
- Directory: the primary path console for active sequence, connection table, globals, BLACS context, lyse folders, H5 output, Knowledge sources, manuals, papers, and log folders.
|
|
22
|
+
- Package templates: PyPI installs include default configs, example plugins, `label.png`, and the manual templates.
|
|
23
|
+
|
|
24
|
+
## First-Release Limits
|
|
25
|
+
|
|
26
|
+
- LabPilot AI does not modify labscript-suite internals.
|
|
27
|
+
- Co-Sequence never edits arbitrary files; it is restricted to the active sequence `.py` and active connection table `.py`.
|
|
28
|
+
- Co-Sequence does not automatically run experiments after code edits.
|
|
29
|
+
- lyse fitting and plotting parameters are stored in LabPilot analysis records, JSONL, SQLite, and reports. The first release does not write fitting results back into original H5 files.
|
|
30
|
+
- Knowledge snippets are short context references only; LabPilot AI does not execute arbitrary source code from the Knowledge database.
|
|
31
|
+
- Real runmanager/BLACS/lyse hardware workflows must be validated in the lab in stages: low-risk globals, one shot, small grid scan, then BLACS single-channel tests.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
Use the labscript conda environment or another environment that can import the labscript suite:
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
conda activate labscript
|
|
39
|
+
cd E:\Labpilot\labpilot_ai
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Optional extras:
|
|
44
|
+
|
|
45
|
+
```powershell
|
|
46
|
+
pip install -e ".[voice]" # sounddevice + faster-whisper
|
|
47
|
+
pip install -e ".[fit,opt]" # scipy + scikit-optimize/optuna
|
|
48
|
+
pip install -e ".[docs]" # pypdf
|
|
49
|
+
pip install -e ".[dev]" # pytest/black/ruff
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Launch:
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
labpilot-ai
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
or:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
python -m labpilot_ai
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Configuration
|
|
65
|
+
|
|
66
|
+
DeepSeek/OpenAI-compatible settings can be entered in the UI or provided as environment variables:
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
$env:DEEPSEEK_API_KEY="your key"
|
|
70
|
+
$env:DEEPSEEK_BASE_URL="https://api.deepseek.com"
|
|
71
|
+
$env:DEEPSEEK_MODEL="deepseek-v4-pro"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If no key is available, keep `Mock LLM` enabled to test parsing and safety flows offline.
|
|
75
|
+
|
|
76
|
+
## Project Templates
|
|
77
|
+
|
|
78
|
+
On first run, click `Init/repair project templates` in Settings to copy clean local templates:
|
|
79
|
+
|
|
80
|
+
- `configs/global_registry.yaml`
|
|
81
|
+
- `configs/blacs_manual_registry.yaml`
|
|
82
|
+
- `configs/lyse_registry.yaml`
|
|
83
|
+
- `configs/project_settings.yaml`
|
|
84
|
+
- `plugins/single_modules/`
|
|
85
|
+
- `plugins/multi_modules/`
|
|
86
|
+
- `manual/`
|
|
87
|
+
|
|
88
|
+
Existing local files are not overwritten. Registry saves create `.bak` backups.
|
|
89
|
+
|
|
90
|
+
## Voice And CUDA Notes
|
|
91
|
+
|
|
92
|
+
CPU mode is the most stable default:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
voice:
|
|
96
|
+
device: "cpu"
|
|
97
|
+
compute_type: "int8"
|
|
98
|
+
model_size: "small"
|
|
99
|
+
isolated_stt: true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
GPU STT on RTX cards can use:
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
voice:
|
|
106
|
+
device: "cuda"
|
|
107
|
+
gpu_compute_type: "float16"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If Windows reports `Library cublas64_12.dll is not found or cannot be loaded`, run Diagnostics. LabPilot AI includes CUDA DLL path discovery for CUDA Toolkit, `nvidia-cublas-cu12`, `nvidia-cudnn-cu12`, and CTranslate2 directories. See `manual/01_voice/cuda_troubleshooting.md`.
|
|
111
|
+
|
|
112
|
+
## Release Checks
|
|
113
|
+
|
|
114
|
+
```powershell
|
|
115
|
+
cd E:\Labpilot\labpilot_ai
|
|
116
|
+
python -m compileall -q labpilot_ai
|
|
117
|
+
python -m pytest -q
|
|
118
|
+
git diff --check
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Before publishing, confirm the wheel/sdist does not contain API keys, experiment H5 data, `labpilot_outputs/`, pycache, build folders, or egg-info.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _read_limited(path, max_chars):
|
|
6
|
+
path = Path(path)
|
|
7
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
8
|
+
truncated = len(text) > max_chars
|
|
9
|
+
if truncated:
|
|
10
|
+
text = text[:max_chars] + "\n\n# [LabPilot truncated this file for prompt budget]\n"
|
|
11
|
+
return text, truncated
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_co_sequence_prompt(
|
|
15
|
+
instruction,
|
|
16
|
+
sequence_path,
|
|
17
|
+
connection_table_path,
|
|
18
|
+
*,
|
|
19
|
+
project_context="",
|
|
20
|
+
max_file_chars=60000,
|
|
21
|
+
):
|
|
22
|
+
sequence_text, sequence_truncated = _read_limited(sequence_path, max_file_chars)
|
|
23
|
+
connection_text, connection_truncated = _read_limited(connection_table_path, max_file_chars)
|
|
24
|
+
return f"""
|
|
25
|
+
You are LabPilot Co-Sequence, a code editing assistant for labscript experiments.
|
|
26
|
+
You may propose changes ONLY to the selected sequence file and selected connection table file.
|
|
27
|
+
Do not propose changes to any other file. Do not run hardware. Do not include Markdown fences.
|
|
28
|
+
|
|
29
|
+
User instruction:
|
|
30
|
+
{instruction}
|
|
31
|
+
|
|
32
|
+
Project knowledge snippets:
|
|
33
|
+
{project_context or "No local knowledge snippets were retrieved."}
|
|
34
|
+
|
|
35
|
+
Selected editable files:
|
|
36
|
+
1. sequence_path={sequence_path}
|
|
37
|
+
truncated={sequence_truncated}
|
|
38
|
+
```python
|
|
39
|
+
{sequence_text}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. connection_table_path={connection_table_path}
|
|
43
|
+
truncated={connection_truncated}
|
|
44
|
+
```python
|
|
45
|
+
{connection_text}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Return strict JSON with this shape:
|
|
49
|
+
{{
|
|
50
|
+
"summary": "short summary",
|
|
51
|
+
"risk_level": "low|medium|high",
|
|
52
|
+
"files": [
|
|
53
|
+
{{"path": "...", "unified_diff": "unified diff for that exact file"}}
|
|
54
|
+
],
|
|
55
|
+
"consistency_checks": ["how sequence and connection table stay consistent"],
|
|
56
|
+
"color_tags": ["yellow: reason", "red: reason"],
|
|
57
|
+
"warnings": ["operator-facing warning"]
|
|
58
|
+
}}
|
|
59
|
+
|
|
60
|
+
Rules:
|
|
61
|
+
- Every file path must be exactly one of the two selected paths.
|
|
62
|
+
- Use unified diffs only. No full file replacement.
|
|
63
|
+
- Keep sequence and connection table device/channel/global names consistent.
|
|
64
|
+
- Prefer small, reviewable patches.
|
|
65
|
+
- If the request is unsafe or underspecified, return an empty files list and put the issue in warnings.
|
|
66
|
+
""".strip()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def mock_co_sequence_plan(instruction, sequence_path, connection_table_path):
|
|
70
|
+
return {
|
|
71
|
+
"summary": "Mock Co-Sequence did not modify code. Enable an API key to generate real diffs.",
|
|
72
|
+
"risk_level": "low",
|
|
73
|
+
"files": [],
|
|
74
|
+
"consistency_checks": [
|
|
75
|
+
"Mock mode keeps sequence and connection table unchanged.",
|
|
76
|
+
f"Sequence target: {Path(sequence_path).name if sequence_path else ''}",
|
|
77
|
+
f"Connection table target: {Path(connection_table_path).name if connection_table_path else ''}",
|
|
78
|
+
],
|
|
79
|
+
"color_tags": ["gray: mock"],
|
|
80
|
+
"warnings": ["No patch was generated in Mock LLM mode."],
|
|
81
|
+
"instruction": instruction,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def compact_plan_text(plan):
|
|
86
|
+
return json.dumps(plan or {}, ensure_ascii=False, indent=2)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
ALLOWED_ACTIONS = {
|
|
2
|
+
"set_global",
|
|
3
|
+
"set_blacs_manual",
|
|
4
|
+
"engage",
|
|
5
|
+
"get_globals",
|
|
6
|
+
"load_h5",
|
|
7
|
+
"load_h5_folder",
|
|
8
|
+
"run_single_lyse",
|
|
9
|
+
"run_multi_lyse",
|
|
10
|
+
"plot",
|
|
11
|
+
"fit",
|
|
12
|
+
"start_optimization",
|
|
13
|
+
"stop_optimization",
|
|
14
|
+
"tell_optimization_result",
|
|
15
|
+
"evaluate_optimization_result",
|
|
16
|
+
"generate_protocol",
|
|
17
|
+
"generate_report",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def action_summary(action: dict) -> str:
|
|
22
|
+
typ = action.get("type", "")
|
|
23
|
+
if typ in {"set_global", "set_blacs_manual"}:
|
|
24
|
+
return f"{typ}: {action.get('name')} = {action.get('value')!r}"
|
|
25
|
+
if typ == "start_optimization":
|
|
26
|
+
names = ", ".join((action.get("parameters") or {}).keys())
|
|
27
|
+
return f"{typ}: {action.get('method')} {action.get('mode')} {action.get('objective')} over {names}"
|
|
28
|
+
if typ in {"plot", "fit"}:
|
|
29
|
+
return f"{typ}: {action.get('plot_type', action.get('model', ''))}"
|
|
30
|
+
return typ
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
def advise_error(error_text: str) -> str:
|
|
2
|
+
text = (error_text or "").strip()
|
|
3
|
+
low = text.lower()
|
|
4
|
+
hints = []
|
|
5
|
+
|
|
6
|
+
if any(token in low for token in ["unicode", "utf-8", "codec", "mojibake", "encoding", "decode error", "�"]):
|
|
7
|
+
hints.append("Check source/document encoding. Save Python and Markdown files as UTF-8, then rerun compileall before release.")
|
|
8
|
+
if any(token in text for token in ["乱码", "鏄", "涓", "鎵", "寮"]):
|
|
9
|
+
hints.append("This looks like mojibake. Reopen the affected file as UTF-8, replace corrupted strings, and avoid mixed terminal encodings.")
|
|
10
|
+
if "not in global whitelist" in low or "global" in low and "whitelist" in low:
|
|
11
|
+
hints.append("Add the variable to configs/global_registry.yaml with type, range, risk, aliases, and description.")
|
|
12
|
+
if "importerror" in low or "modulenotfounderror" in low or "no module named" in low:
|
|
13
|
+
hints.append("Install the missing optional dependency, or disable the related feature in Settings before continuing.")
|
|
14
|
+
if "blacs" in low and ("connection" in low or "refused" in low or "bridge" in low or "localhost" in low):
|
|
15
|
+
hints.append("Check the BLACS localhost bridge, the registered channel name, and Mock BLACS mode before programming hardware.")
|
|
16
|
+
if "runmanager" in low and ("connection" in low or "timeout" in low or "remote" in low):
|
|
17
|
+
hints.append("Check that runmanager is running, remote access is available, and the timeout is long enough.")
|
|
18
|
+
if "h5py has already been imported" in low or "h5_lock" in low:
|
|
19
|
+
hints.append("Restart Python and ensure labpilot_ai.bootstrap_labscript.install_h5_lock() runs before importing h5py.")
|
|
20
|
+
if "cublas" in low or "cudnn" in low or "cuda runtime" in low:
|
|
21
|
+
hints.append("For GPU STT, run Diagnostics and verify CUDA/cuBLAS/cuDNN DLL paths; switch to CPU/int8 if the DLL stack is unstable.")
|
|
22
|
+
if "openmp" in low or "libiomp5md" in low or "omp: error" in low:
|
|
23
|
+
hints.append("Keep STT in isolated_release mode or CPU/int8 mode to isolate OpenMP runtime conflicts.")
|
|
24
|
+
if "pypdf" in low or "pdf import requires" in low or "pdf indexing requires" in low:
|
|
25
|
+
hints.append("Install PDF support with pip install -e .[docs] or pip install pypdf.")
|
|
26
|
+
if "module" in low and ("not registered" in low or "no module" in low or "path does not exist" in low):
|
|
27
|
+
hints.append("Register the single/multi analysis module in configs/lyse_registry.yaml and verify the module path.")
|
|
28
|
+
if "objective" in low and ("missing" in low or "unsafe" in low or "variable" in low):
|
|
29
|
+
hints.append("Choose an objective from lyse results or use a safe expression over registered result fields.")
|
|
30
|
+
if "scipy" in low or "curve_fit" in low:
|
|
31
|
+
hints.append("Install scipy fitting dependencies with pip install -e .[fit], or choose a simpler fit model.")
|
|
32
|
+
if "h5" in low and ("timeout" in low or "folder" in low or "not found" in low):
|
|
33
|
+
hints.append("Verify the H5 output folder, shot naming rule, and whether the experiment actually produced a new H5 file.")
|
|
34
|
+
if "knowledge" in low or "fts" in low or "sqlite" in low:
|
|
35
|
+
hints.append("Rebuild the Knowledge index and confirm the configured source folders are readable.")
|
|
36
|
+
|
|
37
|
+
if not hints:
|
|
38
|
+
hints.append("Check the traceback, then reproduce in Dry run or Mock mode before touching hardware.")
|
|
39
|
+
return "\n".join(f"- {hint}" for hint in hints)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def extract_json_object(text: str) -> dict:
|
|
5
|
+
text = (text or "").strip()
|
|
6
|
+
try:
|
|
7
|
+
return json.loads(text)
|
|
8
|
+
except json.JSONDecodeError:
|
|
9
|
+
pass
|
|
10
|
+
start = text.find("{")
|
|
11
|
+
end = text.rfind("}")
|
|
12
|
+
if start >= 0 and end > start:
|
|
13
|
+
return json.loads(text[start:end+1])
|
|
14
|
+
raise ValueError(f"无法解析 JSON:{text}")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from .co_sequence_prompt import build_co_sequence_prompt, mock_co_sequence_plan
|
|
5
|
+
from .json_parser import extract_json_object
|
|
6
|
+
from .prompt_builder import build_command_prompt
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LLMClient:
|
|
10
|
+
def __init__(self, api_key=None, base_url=None, model=None, use_thinking=True, mock=False):
|
|
11
|
+
self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY") or os.getenv("OPENAI_API_KEY")
|
|
12
|
+
self.base_url = base_url or os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com")
|
|
13
|
+
self.model = model or os.getenv("DEEPSEEK_MODEL", "deepseek-v4-flash")
|
|
14
|
+
self.use_thinking = use_thinking
|
|
15
|
+
self.mock = mock
|
|
16
|
+
|
|
17
|
+
def parse_command(self, user_text: str, global_registry: dict, blacs_registry=None, lyse_registry=None, project_context=None) -> dict:
|
|
18
|
+
if self.mock or not self.api_key:
|
|
19
|
+
result = self._mock_parse(user_text, global_registry)
|
|
20
|
+
if project_context:
|
|
21
|
+
result["project_context_used"] = True
|
|
22
|
+
return result
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
from openai import OpenAI
|
|
26
|
+
except ImportError as exc:
|
|
27
|
+
raise RuntimeError("OpenAI-compatible SDK is not installed. Install the project dependencies or enable Mock LLM.") from exc
|
|
28
|
+
|
|
29
|
+
client = OpenAI(api_key=self.api_key, base_url=self.base_url, timeout=60.0)
|
|
30
|
+
kwargs = dict(
|
|
31
|
+
model=self.model,
|
|
32
|
+
messages=[
|
|
33
|
+
{"role": "system", "content": build_command_prompt(global_registry, blacs_registry, lyse_registry, project_context=project_context)},
|
|
34
|
+
{"role": "user", "content": user_text},
|
|
35
|
+
],
|
|
36
|
+
stream=False,
|
|
37
|
+
temperature=0,
|
|
38
|
+
response_format={"type": "json_object"},
|
|
39
|
+
)
|
|
40
|
+
if self.use_thinking:
|
|
41
|
+
kwargs["reasoning_effort"] = "high"
|
|
42
|
+
kwargs["extra_body"] = {"thinking": {"type": "enabled"}}
|
|
43
|
+
response = client.chat.completions.create(**kwargs)
|
|
44
|
+
return extract_json_object(response.choices[0].message.content)
|
|
45
|
+
|
|
46
|
+
def _mock_parse(self, text: str, global_registry: dict) -> dict:
|
|
47
|
+
"""Small local parser for testing without API. It only handles common cases."""
|
|
48
|
+
actions = []
|
|
49
|
+
low = (text or "").lower()
|
|
50
|
+
no_run = any(token in low for token in ["do not run", "don't run", "no run", "not run", "dry run"])
|
|
51
|
+
no_run = no_run or any(token in (text or "") for token in ["不要运行", "不运行", "别运行", "不跑"])
|
|
52
|
+
|
|
53
|
+
if any(token in low for token in ["tof", "time of flight"]) or any(token in (text or "") for token in ["飞行时间"]):
|
|
54
|
+
m = re.search(r"(?:from|从)\s*([\d.]+)\s*(?:ms)?\s*(?:to|到)\s*([\d.]+)\s*(?:ms)?.*?(\d+)\s*(?:points|点)", text or "", re.I)
|
|
55
|
+
if m and "duration_tof_ms" in global_registry:
|
|
56
|
+
actions.append(
|
|
57
|
+
{
|
|
58
|
+
"type": "set_global",
|
|
59
|
+
"name": "duration_tof_ms",
|
|
60
|
+
"value": {"linspace": [float(m.group(1)), float(m.group(2)), int(m.group(3))]},
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
m = re.search(r"(?:tof|time of flight|飞行时间).*?(?:to|set to|设为|设置为|改成|=)\s*([\d.]+)", text or "", re.I)
|
|
65
|
+
if m and "duration_tof_ms" in global_registry:
|
|
66
|
+
actions.append({"type": "set_global", "name": "duration_tof_ms", "value": float(m.group(1))})
|
|
67
|
+
|
|
68
|
+
bool_map = {
|
|
69
|
+
"lyse_do_SG_F1_masked": ["sg mask", "遮罩"],
|
|
70
|
+
"lyse_update_centers_json": ["update centers", "centers_json", "centers json", "更新中心"],
|
|
71
|
+
"do_Rabi": ["rabi", "拉比"],
|
|
72
|
+
"do_pure": ["pure", "清除"],
|
|
73
|
+
"do_Ramsey": ["ramsey"],
|
|
74
|
+
}
|
|
75
|
+
for name, keys in bool_map.items():
|
|
76
|
+
if name in global_registry and any(key.lower() in low or key in (text or "") for key in keys):
|
|
77
|
+
val = not any(token in low for token in ["disable", "off", "turn off"]) and not any(
|
|
78
|
+
token in (text or "") for token in ["关闭", "关掉", "不要打开"]
|
|
79
|
+
)
|
|
80
|
+
actions.append({"type": "set_global", "name": name, "value": val})
|
|
81
|
+
|
|
82
|
+
if not no_run and (any(token in low for token in ["run", "submit", "engage"]) or any(token in (text or "") for token in ["运行", "跑一次", "提交"])):
|
|
83
|
+
actions.append({"type": "engage"})
|
|
84
|
+
if any(token in low for token in ["read", "current", "globals"]) or any(token in (text or "") for token in ["读取", "查看", "当前参数"]):
|
|
85
|
+
actions.append({"type": "get_globals"})
|
|
86
|
+
return {"actions": actions, "comment": "Mock LLM parser result. For serious use, enable API."}
|
|
87
|
+
|
|
88
|
+
def propose_co_sequence_patch(
|
|
89
|
+
self,
|
|
90
|
+
instruction: str,
|
|
91
|
+
sequence_path,
|
|
92
|
+
connection_table_path,
|
|
93
|
+
*,
|
|
94
|
+
project_context=None,
|
|
95
|
+
max_file_chars=60000,
|
|
96
|
+
) -> dict:
|
|
97
|
+
if self.mock or not self.api_key:
|
|
98
|
+
return mock_co_sequence_plan(instruction, sequence_path, connection_table_path)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
from openai import OpenAI
|
|
102
|
+
except ImportError as exc:
|
|
103
|
+
raise RuntimeError("OpenAI-compatible SDK is not installed. Install the project dependencies or enable Mock LLM.") from exc
|
|
104
|
+
|
|
105
|
+
client = OpenAI(api_key=self.api_key, base_url=self.base_url, timeout=90.0)
|
|
106
|
+
prompt = build_co_sequence_prompt(
|
|
107
|
+
instruction,
|
|
108
|
+
sequence_path,
|
|
109
|
+
connection_table_path,
|
|
110
|
+
project_context=project_context,
|
|
111
|
+
max_file_chars=max_file_chars,
|
|
112
|
+
)
|
|
113
|
+
kwargs = dict(
|
|
114
|
+
model=self.model,
|
|
115
|
+
messages=[
|
|
116
|
+
{"role": "system", "content": "You produce strict JSON patch plans for LabPilot Co-Sequence."},
|
|
117
|
+
{"role": "user", "content": prompt},
|
|
118
|
+
],
|
|
119
|
+
stream=False,
|
|
120
|
+
temperature=0,
|
|
121
|
+
response_format={"type": "json_object"},
|
|
122
|
+
)
|
|
123
|
+
if self.use_thinking:
|
|
124
|
+
kwargs["reasoning_effort"] = "high"
|
|
125
|
+
kwargs["extra_body"] = {"thinking": {"type": "enabled"}}
|
|
126
|
+
response = client.chat.completions.create(**kwargs)
|
|
127
|
+
return extract_json_object(response.choices[0].message.content)
|