Signature Conversion Analysis and Fix Strategy
Problem Overview
The Python bridge is now successfully starting and communicating, but integration tests are failing due to a signature data format mismatch. The Python bridge expects signature definitions as dictionaries but is receiving string values (likely module names).
Error Details
AttributeError: 'str' object has no attribute 'get'
Location: _create_signature_class()
method in dspy_bridge.py
at line 223:
inputs = signature_def.get('inputs', []) # signature_def is a string, not dict
Root Cause: The Elixir adapter is sending signature module names (e.g., "TestSignature"
) instead of converted signature dictionaries to the Python bridge.
Technical Analysis
Data Flow Problem
- Elixir Side: Tests pass signature modules like
TestSignature
- Adapter Layer: Should convert module to dictionary format
- Python Bridge: Expects
%{inputs: [...], outputs: [...]}
- Actual: Receives string module name instead
Current Conversion Points
The signature conversion happens in multiple places:
- Factory.prepare_signature_for_adapter/3 (
lib/dspex/adapters/factory.ex:132
) - TypeConverter.convert_signature_to_format/3 (
lib/dspex/adapters/type_converter.ex
) - PythonPort.convert_config/1 (
lib/dspex/adapters/python_port.ex:258
)
Integration Points Analysis
Test Layer Compatibility
- Layer 1 (Mock): Uses signature modules directly ✅
- Layer 2 (BridgeMock): Converts to wire format ✅
- Layer 3 (PythonPort): BROKEN - sends modules instead of dicts ❌
Adapter Factory Logic
# factory.ex:144-146 - PythonPort case
DSPex.Adapters.PythonPort ->
# PythonPort handles modules directly
{:ok, signature_module}
Problem: This assumes PythonPort can handle raw modules, but the Python side expects dictionaries.
Risk Assessment
High Risk Areas
- Breaking Existing Tests: Signature format changes could affect all test layers
- Type Conversion Complexity: Multiple conversion paths create maintenance burden
- Protocol Compatibility: Changes must maintain backward compatibility
Medium Risk Areas
- Performance Impact: Additional conversion overhead
- Error Handling: New failure modes in conversion pipeline
- Documentation Drift: Multiple conversion points need clear documentation
Low Risk Areas
- Mock Adapter: Should remain unaffected
- BridgeMock: Already handles conversions correctly
Solution Approaches
Approach 1: Fix Factory.prepare_signature_for_adapter (RECOMMENDED)
Strategy: Make PythonPort behave like BridgeMock for signature handling.
# factory.ex - CHANGE THIS:
DSPex.Adapters.PythonPort ->
# PythonPort handles modules directly
{:ok, signature_module}
# TO THIS:
DSPex.Adapters.PythonPort ->
# PythonPort needs converted format like BridgeMock
signature_data = TypeConverter.convert_signature_to_format(signature_module, :python, test_layer: test_layer)
{:ok, signature_data}
Pros:
- ✅ Minimal change scope
- ✅ Consistent with BridgeMock approach
- ✅ Uses existing TypeConverter infrastructure
- ✅ Clear separation of concerns
Cons:
- ❌ Requires updating PythonPort.convert_config to handle dicts
Approach 2: Fix PythonPort.convert_config
Strategy: Make convert_config handle both modules and dictionaries.
defp convert_config(config) do
# Handle signature conversion based on type
converted_signature = case Map.get(config, :signature) do
module when is_atom(module) ->
TypeConverter.convert_signature_to_format(module, :python)
dict when is_map(dict) ->
dict
other ->
other
end
# ... rest of conversion
end
Pros:
- ✅ Backward compatible
- ✅ Handles multiple input types gracefully
Cons:
- ❌ Increases complexity in PythonPort
- ❌ Inconsistent with other adapters
Approach 3: Fix Python Bridge to Accept Modules
Strategy: Modify Python bridge to detect and convert module names.
Pros:
- ✅ No Elixir changes needed
Cons:
- ❌ Requires complex Python-side module introspection
- ❌ Breaks separation of concerns
- ❌ Not maintainable
Recommended Implementation Plan
Phase 1: Factory Fix (Immediate)
Update Factory.prepare_signature_for_adapter:
- Change PythonPort case to convert signatures like BridgeMock
- Use existing TypeConverter.convert_signature_to_format
Update PythonPort.convert_config:
- Ensure it handles dictionary signatures correctly
- Add proper error handling for malformed signatures
Phase 2: Testing & Validation
Test Layer 3 specifically:
TEST_MODE=full_integration mix test --only=layer_3
Cross-layer compatibility:
- Ensure Layer 1 and Layer 2 still work
- Validate signature conversion at all layers
Integration test suite:
TEST_MODE=full_integration mix test.integration
Phase 3: Error Handling Enhancement
- Add conversion error handling
- Improve error messages for signature format issues
- Add logging for signature conversion process
Expectations and Success Criteria
Immediate Success Criteria
- ✅ Layer 3 tests pass signature creation
- ✅ Python bridge receives dictionary signatures
- ✅ No regression in Layer 1/Layer 2 tests
Long-term Success Criteria
- ✅ All integration tests pass
- ✅ Consistent signature handling across all adapters
- ✅ Clear error messages for signature format issues
- ✅ Maintainable conversion pipeline
Performance Expectations
- Conversion Overhead: < 1ms per signature conversion
- Memory Impact: Minimal (signatures are small objects)
- Test Runtime: No significant increase
Integration Considerations
Type Converter Integration
- Use existing
convert_signature_to_format(module, :python)
- Leverage test layer awareness
- Maintain format consistency
Error Handler Integration
- Wrap conversion errors with proper context
- Provide actionable error messages
- Support retry logic where appropriate
Factory Pattern Integration
- Maintain adapter isolation
- Use consistent preparation patterns
- Support test layer configuration
Implementation Steps
- Analyze current TypeConverter behavior for
:python
format - Update Factory.prepare_signature_for_adapter for PythonPort
- Test conversion pipeline with sample signatures
- Update PythonPort.convert_config if needed
- Run focused Layer 3 tests
- Validate cross-layer compatibility
- Run full integration test suite
Monitoring and Validation
Test Commands
# Test specific layer
TEST_MODE=full_integration mix test --only=layer_3
# Test signature conversion
TEST_MODE=full_integration mix test test/dspex/adapters/type_converter_test.exs
# Full integration suite
TEST_MODE=full_integration mix test.integration
Success Indicators
- No “str object has no attribute ‘get’” errors
- Python bridge receives dictionary signatures
- All adapter layers maintain compatibility
- Integration tests achieve > 95% pass rate
Next Steps
After writing this analysis, proceed with Approach 1 implementation:
- Fix Factory.prepare_signature_for_adapter
- Update PythonPort.convert_config
- Test Layer 3 integration
- Validate cross-layer compatibility