feat(services): XOpt Optimizer Service infrastructure#89
feat(services): XOpt Optimizer Service infrastructure#89
Conversation
- Add ArtifactType enum (IMAGE, NOTEBOOK, COMMAND, HTML, FILE) - Create register_artifact() as single source of truth - Legacy methods delegate to new API with dual-write pattern - Add populate_legacy_fields_from_artifacts() for backward compatibility - Python capability uses new API directly with clean accumulation
- ArtifactGallery with keyboard navigation (Ctrl+a, j/k, Enter, o, Esc) - ArtifactViewer modal with type-specific details and external open - Native image rendering via textual-image (Sixel/Kitty protocols) - New/seen tracking with [NEW] badges for current turn artifacts - Integration with ChatDisplay for artifact state management
- Replace _extract_figures/commands/notebooks_for_cli with single method - Read from ui_artifacts directly instead of legacy fields - Support all artifact types: IMAGE, NOTEBOOK, COMMAND, HTML, FILE - Add type-specific formatting with icons and grouped display - Remove ~150 lines of redundant legacy extraction code
Enable approval_approved and approved_payload fields to be overwritten across multiple approval iterations. Without this, LangGraph's default LastValue channel throws errors when setting approval fields multiple times in multi-iteration workflows (e.g., XOpt optimization).
Display repeat approval requests in styled Panel matching the first approval prompt, rather than plain text. Ensures consistent visual treatment for multi-iteration approval workflows.
Implement XOpt-based autonomous machine parameter optimization service with complete workflow: state identification, strategy decision, YAML generation, human approval, and execution. Key components: - State identification: ReAct agent assesses machine readiness - Decision: LLM selects exploration vs optimization strategy - YAML generation: ReAct agent creates XOpt configurations - Approval: Human-in-the-loop with structured interrupt - Configurable modes: react (LLM) or mock (fast tests) Includes optimization capability, prompt builder, config templates, approval helpers, and comprehensive test suite.
Document how XOpt/Badger's Environment abstraction maps to Osprey's ConnectorFactory. The Environment defines variables/observables while the Interface (OspreyInterface) bridges to Osprey's connector system. This provides the architectural insight for XOpt integration without implementing domain-specific logic that requires expert input. Also includes ruff formatting fixes from quick_check.
|
@claude please review this PR, focus on if Xopt/Badger optimization ability should be added as a service to osprey, or should we have it as a subagent since it looks like a ReAct agent itself |
|
Claude finished @wenatuhs's task in 3m 1s —— View job Architectural Review: XOpt Service vs. Subagent PatternTL;DR Recommendation✅ The current service architecture is correct. The XOpt optimizer should remain a service, not be converted to a subagent. Detailed AnalysisUnderstanding the Architecture LayersThe Osprey framework has a clear separation of concerns:
Why XOpt Should Be a Service1. It's a Workflow, Not Just an AgentThe XOpt optimizer is not a single ReAct agent—it's an orchestrated workflow with 6 distinct stages: File references:
Each stage has different concerns:
Two of these six stages happen to use ReAct agents internally, but that doesn't make the whole system a ReAct agent. 2. Services Handle Complex Cross-Cutting ConcernsThe XOpt service manages capabilities that go beyond what a simple subagent can handle: Multi-iteration loops with state persistence: # src/osprey/services/xopt_optimizer/service.py:232-237
workflow.add_conditional_edges(
"analysis",
self._loop_router,
{"continue": "state_identification", "complete": "__end__"},
)LangGraph-native approval workflows:
Complex conditional routing:
Error tracking and recovery:
These features require a separate LangGraph instance with its own checkpointer—exactly what the service pattern provides. 3. Follows Established Framework PatternsThe XOpt service exactly mirrors the Python executor service:
File references:
Breaking this consistency would create architectural confusion. 4. Capability Gateway Pattern is IntentionalThe # src/osprey/capabilities/optimization.py:144-150
def _create_optimization_context(service_result: XOptServiceResult):
"""Create OptimizationResultContext from service result."""
return OptimizationResultContext(
run_artifact=service_result.run_artifact,
strategy=service_result.strategy.value,
# ... transforms service result to capability context
)The capability layer:
If you made XOpt a direct subagent in the main graph, you'd lose:
What About the ReAct Agents?The two ReAct agents are implementation details of the service: State Identification AgentFile: class StateIdentificationAgent:
"""ReAct agent for assessing machine readiness."""
async def assess_state(self, objective, additional_context):
# Uses ReAct pattern to:
# 1. Read reference docs about ready criteria
# 2. Check channel values via control system
# 3. Return structured assessmentThis agent is a node in the service workflow, not the whole service. YAML Generation AgentFile: class YamlGenerationAgent:
"""ReAct agent for generating XOpt YAML configs."""
async def generate_yaml(self, objective, strategy, additional_context):
# Uses ReAct pattern to:
# 1. Optionally read example YAMLs
# 2. Generate config based on strategy
# 3. Return validated YAMLAgain, this is one stage in a six-stage workflow. Neither agent has the complexity to justify being in the main orchestrator graph. They're specialized components that:
Hypothetical: What If It Were a Subagent?If you tried to make XOpt a direct subagent in the main graph: Problems You'd Face:
What You'd Gain:
The trade-off is terrible. You'd sacrifice clean architecture for minimal simplicity gains. CodeQL Findings (Bonus Review)Since I reviewed the code, here are the legitimate issues to fix: 1. Unused VariablesFile: These are legitimate—variables assigned but never used. Fix by either using them or calling the functions for side effects only. 2. Empty Exception HandlersFile: except Exception:
pass # CodeQL: empty handler with no commentAdd explanatory comments about why exceptions are intentionally swallowed (non-critical UI updates). 3. Non-iterable in for loopFile: This might be a false positive, but worth investigating. These are unrelated to the architectural question and should be fixed separately. Final RecommendationKeep the service architecture. Here's why:
The fact that the service uses ReAct agents internally doesn't mean the service should be a ReAct agent in the main graph. That's a category error—services orchestrate workflows; agents are components within those workflows. Additional CommentsThe PR description correctly labels this as infrastructure. That's exactly right. This is foundational service infrastructure that provides:
The "work in progress" nature (placeholder execution, mock components) is appropriate—you're building the scaffolding first, which is the right approach. The architecture is sound; you just need to connect the actual XOpt/Badger execution when ready. Files reviewed:
| |
# Conflicts: # CHANGELOG.md # src/osprey/approval/__init__.py # src/osprey/capabilities/python.py # src/osprey/infrastructure/respond_node.py # src/osprey/interfaces/tui/app.py # src/osprey/interfaces/tui/styles.tcss # src/osprey/interfaces/tui/widgets/__init__.py # src/osprey/interfaces/tui/widgets/artifact_viewer.py # src/osprey/interfaces/tui/widgets/artifacts.py # src/osprey/interfaces/tui/widgets/chat_display.py # src/osprey/prompts/defaults/__init__.py # src/osprey/prompts/loader.py # src/osprey/registry/registry.py # src/osprey/state/artifacts.py # tests/test_artifacts.py # tests/test_tui_artifacts.py
…d tuning API client
Summary
This PR adds the foundational agent infrastructure for an XOpt/Badger-based optimizer service. This is a work-in-progress - the agent scaffolding and workflow nodes are implemented, but the actual optimization routines are not yet connected.
What's implemented:
What's NOT yet implemented:
Test plan
pytest tests/services/xopt_optimizer/ -vpytest tests/ --ignore=tests/e2e -vmypy src/