MCPcopy Create free account
hub / github.com/Emmimal/control-layer / ControlLayer

Class ControlLayer

control_layer.py:829–1076  ·  view source on GitHub ↗

The orchestrator. Sits between your application logic and the LLM. Composes all eight components: InputGuard → TokenBudget → PromptBuilder → CircuitBreaker → LLMCaller (timeout) → ResponseValidator → RetryEngine (jittered backoff) → FallbackRouter → AuditLogger Swap

Source from the content-addressed store, hash-verified

827# =============================================================================
828
829class ControlLayer:
830 """
831 The orchestrator. Sits between your application logic and the LLM.
832
833 Composes all eight components:
834 InputGuard → TokenBudget → PromptBuilder →
835 CircuitBreaker → LLMCaller (timeout) → ResponseValidator →
836 RetryEngine (jittered backoff) → FallbackRouter → AuditLogger
837
838 Swap the LLM in one line:
839 layer = ControlLayer(llm_fn=your_api_call, ...)
840
841 The ControlPacket returned contains the final response, attempt count,
842 total latency, token usage, and a full audit ID for tracing.
843 """
844
845 def __init__(
846 self,
847 llm_fn: Callable[[str], str],
848 system_prompt: str,
849 schema: Optional[ResponseSchema] = None,
850 config: Optional[ControlLayerConfig] = None,
851 # Legacy compat: accept total_tokens and max_attempts directly
852 total_tokens: Optional[int] = None,
853 max_attempts: Optional[int] = None,
854 ):
855 self.config = config or ControlLayerConfig(
856 total_tokens=total_tokens or 800,
857 max_attempts=max_attempts or 3,
858 )
859 self.schema = schema or ResponseSchema()
860
861 self.input_guard = InputGuard(self.config.max_input_chars)
862 self.prompt_builder = PromptBuilder(system_prompt, self.config)
863 self.validator = ResponseValidator()
864 self.retry_engine = RetryEngine(self.config)
865 self.fallback_router = FallbackRouter()
866 self.circuit_breaker = CircuitBreaker(
867 self.config.cb_failure_threshold,
868 self.config.cb_recovery_seconds,
869 )
870 self.llm_caller = LLMCaller(llm_fn, self.config.timeout_seconds)
871 self.audit = AuditLogger(self.config.audit_log_path)
872
873 def register_fallback(self, name: str, fn: Callable[[str], str]) -> None:
874 self.fallback_router.register(name, fn)
875
876 def run(
877 self,
878 user_input: str,
879 constraints: Optional[List[str]] = None,
880 context: str = "",
881 ) -> ControlPacket:
882 constraints = constraints or []
883 audit_id = self._make_audit_id(user_input)
884 start = time.perf_counter()
885
886 # ── Input Guard ────────────────────────────────────────────────────

Calls

no outgoing calls