Current section

Files

Jump to
snakepit priv python snakepit_bridge variable_aware_mixin.py
Raw

priv/python/snakepit_bridge/variable_aware_mixin.py

"""
VariableAwareMixin for Python Integration
Provides variable management capabilities for any Python class, enabling
automatic synchronization with Elixir-managed session variables.
This mixin can be used with:
- Machine learning libraries (DSPy, scikit-learn, PyTorch, etc.)
- Data processing tools (Pandas, NumPy)
- Any Python class that benefits from external configuration
Originally designed for DSPy but generic enough for universal use.
"""
from typing import Any, Dict, Optional, Callable
import grpc
import snakepit_bridge_pb2
import snakepit_bridge_pb2_grpc
from .serialization import TypeSerializer
class VariableAwareMixin:
"""
Mixin to make DSPy modules aware of the variable system.
This will be used in Stage 2 to integrate variable management
into DSPy signatures and modules.
"""
def __init__(self, channel: grpc.Channel, session_id: str):
"""Initialize the mixin with gRPC channel and session."""
self._channel = channel
self._session_id = session_id
self._stub = snakepit_bridge_pb2_grpc.BridgeServiceStub(channel)
self._watchers = {}
def get_variable(self, name: str) -> Any:
"""Get a variable by name."""
request = snakepit_bridge_pb2.GetVariableRequest(
session_id=self._session_id,
name=name
)
try:
response = self._stub.GetVariable(request)
if response.variable:
return TypeSerializer.decode_any(response.variable.value)
return None
except grpc.RpcError as e:
raise RuntimeError(f"Failed to get variable {name}: {e.details()}")
def set_variable(self, name: str, value: Any, metadata: Optional[Dict] = None) -> None:
"""Set a variable value."""
# Infer type from value
var_type = self._infer_type(value)
# Encode value
any_value = TypeSerializer.encode_any(value, var_type)
request = snakepit_bridge_pb2.SetVariableRequest(
session_id=self._session_id,
name=name,
value=any_value,
metadata=metadata or {}
)
try:
self._stub.SetVariable(request)
except grpc.RpcError as e:
raise RuntimeError(f"Failed to set variable {name}: {e.details()}")
def register_variable(self, name: str, var_type: str, initial_value: Any,
constraints: Optional[Dict] = None,
metadata: Optional[Dict] = None) -> str:
"""Register a new variable."""
# Encode value
any_value = TypeSerializer.encode_any(initial_value, var_type)
# Encode constraints
constraints_json = ""
if constraints:
import json
constraints_json = json.dumps(constraints)
request = snakepit_bridge_pb2.RegisterVariableRequest(
session_id=self._session_id,
name=name,
type=var_type,
initial_value=any_value,
constraints=constraints_json,
metadata=metadata or {}
)
try:
response = self._stub.RegisterVariable(request)
return response.variable_id
except grpc.RpcError as e:
raise RuntimeError(f"Failed to register variable {name}: {e.details()}")
def watch_variable(self, name: str, callback: Callable[[str, Any, Any, Dict], None]) -> None:
"""
Watch a variable for changes.
callback receives: (name, old_value, new_value, metadata)
"""
# Implementation will be completed in Stage 1 with streaming support
self._watchers[name] = callback
def _infer_type(self, value: Any) -> str:
"""Infer variable type from Python value."""
if isinstance(value, bool):
return "boolean"
elif isinstance(value, int):
return "integer"
elif isinstance(value, float):
return "float"
elif isinstance(value, str):
return "string"
elif isinstance(value, list) and all(isinstance(x, (int, float)) for x in value):
return "embedding"
elif isinstance(value, dict) and "shape" in value and "data" in value:
return "tensor"
else:
return "string" # Default to string
def __enter__(self):
"""Context manager support."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Cleanup on context exit."""
# Future: close any active streams
pass