← Back to Docs

SIGNATURE CONVERSION ANALYSIS

Documentation for SIGNATURE_CONVERSION_ANALYSIS from the Dspex repository.

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

  1. Elixir Side: Tests pass signature modules like TestSignature
  2. Adapter Layer: Should convert module to dictionary format
  3. Python Bridge: Expects %{inputs: [...], outputs: [...]}
  4. Actual: Receives string module name instead

Current Conversion Points

The signature conversion happens in multiple places:

  1. Factory.prepare_signature_for_adapter/3 (lib/dspex/adapters/factory.ex:132)
  2. TypeConverter.convert_signature_to_format/3 (lib/dspex/adapters/type_converter.ex)
  3. 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

  1. Breaking Existing Tests: Signature format changes could affect all test layers
  2. Type Conversion Complexity: Multiple conversion paths create maintenance burden
  3. Protocol Compatibility: Changes must maintain backward compatibility

Medium Risk Areas

  1. Performance Impact: Additional conversion overhead
  2. Error Handling: New failure modes in conversion pipeline
  3. Documentation Drift: Multiple conversion points need clear documentation

Low Risk Areas

  1. Mock Adapter: Should remain unaffected
  2. BridgeMock: Already handles conversions correctly

Solution Approaches

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

Phase 1: Factory Fix (Immediate)

  1. Update Factory.prepare_signature_for_adapter:

    • Change PythonPort case to convert signatures like BridgeMock
    • Use existing TypeConverter.convert_signature_to_format
  2. Update PythonPort.convert_config:

    • Ensure it handles dictionary signatures correctly
    • Add proper error handling for malformed signatures

Phase 2: Testing & Validation

  1. Test Layer 3 specifically:

    TEST_MODE=full_integration mix test --only=layer_3
    
  2. Cross-layer compatibility:

    • Ensure Layer 1 and Layer 2 still work
    • Validate signature conversion at all layers
  3. Integration test suite:

    TEST_MODE=full_integration mix test.integration
    

Phase 3: Error Handling Enhancement

  1. Add conversion error handling
  2. Improve error messages for signature format issues
  3. 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

  1. Analyze current TypeConverter behavior for :python format
  2. Update Factory.prepare_signature_for_adapter for PythonPort
  3. Test conversion pipeline with sample signatures
  4. Update PythonPort.convert_config if needed
  5. Run focused Layer 3 tests
  6. Validate cross-layer compatibility
  7. 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:

  1. Fix Factory.prepare_signature_for_adapter
  2. Update PythonPort.convert_config
  3. Test Layer 3 integration
  4. Validate cross-layer compatibility