diff --git a/PROVIDER_CONTRACT.md b/PROVIDER_CONTRACT.md index 70e2304..69385f3 100644 --- a/PROVIDER_CONTRACT.md +++ b/PROVIDER_CONTRACT.md @@ -14,7 +14,7 @@ Optional: ```shell export PROVIDER=openai export MODEL=openai/gpt-4o-mini -export PREPROCESSOR_MODEL=openai/gpt-4o-mini +export DRAFTER_MODEL=openai/gpt-4o-mini export OPENAI_BASE_URL=... ``` @@ -48,8 +48,8 @@ Startup emits one concise config line showing resolved `mode`, `base_url`, `model`, and resolution `source` (`default`, `PROVIDER`, or `OPENAI_BASE_URL override`). -`MODEL` and `PREPROCESSOR_MODEL` use LiteLLM format: `/`. -`PREPROCESSOR_MODEL` is optional and defaults to `MODEL`. +`MODEL` and `DRAFTER_MODEL` use LiteLLM format: `/`. +`DRAFTER_MODEL` is optional and defaults to `MODEL`. The directive-drafter integration always uses heuristic-first processing with the configured fallback model when needed. diff --git a/python/examples/prompt_construction/litellm/with_directive_drafter.py b/python/examples/prompt_construction/litellm/with_directive_drafter.py index b648a72..432a1d9 100644 --- a/python/examples/prompt_construction/litellm/with_directive_drafter.py +++ b/python/examples/prompt_construction/litellm/with_directive_drafter.py @@ -111,7 +111,7 @@ def _build_trace_text( *, original_input: str, compiler_input: str, - preprocessor_output: str | None, + drafter_output: str | None, decision: Decision | DecisionKind, premise_before: str | None, policies_before: Mapping[str, PolicyValue], @@ -124,7 +124,7 @@ def _build_trace_text( "Context Compiler trace", f"- original_input: {original_input}", f"- compiler_input: {compiler_input}", - f"- preprocessor_output: {preprocessor_output if preprocessor_output is not None else '(none)'}", + f"- drafter_output: {drafter_output if drafter_output is not None else '(none)'}", f"- decision: {kind}", f"- llm_called: {'yes' if llm_called else 'no'}", ] @@ -224,20 +224,19 @@ def _create_directive_drafter( def _get_directive_drafter() -> DirectiveDrafter: config = resolve_provider_config(default_model="openai/gpt-4o-mini") - preprocessor_model = os.getenv("PREPROCESSOR_MODEL", "").strip() or config.model - return _create_directive_drafter( - preprocessor_model, config.api_key, config.base_url - ) + drafter_model = os.getenv("DRAFTER_MODEL", "").strip() + drafter_model = drafter_model or config.model + return _create_directive_drafter(drafter_model, config.api_key, config.base_url) -def _preprocess_user_input(message: str) -> str | None: +def _draft_user_input(message: str) -> str | None: try: drafted_result = _get_directive_drafter().draft_directive(message) - logger.debug("preprocessor: drafted_result=%r", drafted_result) + logger.debug("drafter: drafted_result=%r", drafted_result) return _extract_drafted_text(drafted_result) except Exception: # Safe no-op fallback: if drafter path fails, preserve basic behavior. - logger.debug("preprocessor: drafter_exception", exc_info=True) + logger.debug("drafter: exception", exc_info=True) return None return None @@ -258,7 +257,7 @@ def _append_trace( *, original_input: str, compiler_input: str, - preprocessor_output: str | None, + drafter_output: str | None, decision: Decision | DecisionKind, state_before: tuple[str | None, dict[str, PolicyValue]], state_after: tuple[str | None, dict[str, PolicyValue]], @@ -269,7 +268,7 @@ def _append_trace( trace_text = _build_trace_text( original_input=original_input, compiler_input=compiler_input, - preprocessor_output=preprocessor_output, + drafter_output=drafter_output, decision=decision, premise_before=state_before[0], policies_before=state_before[1], @@ -293,30 +292,30 @@ def handle_turn( approval_handler: ApprovalHandler = _default_approval_handler, ) -> str: state_before = (engine.premise, dict(engine.policies)) - preprocessd = _preprocess_user_input(user_input) - if preprocessd is None: + drafted_input = _draft_user_input(user_input) + if drafted_input is None: messages = _build_messages(user_input, engine) response_text = _call_litellm(messages) return _append_trace( response_text, original_input=user_input, compiler_input=user_input, - preprocessor_output=None, + drafter_output=None, decision=DecisionKind.NO_DIRECTIVE, state_before=state_before, state_after=(engine.premise, dict(engine.policies)), llm_called=True, ) - compile_input = preprocessd - logger.debug("preprocessor: engine_input=directive") + compile_input = drafted_input + logger.debug("drafter: engine_input=directive") approved = approval_handler(compile_input) if not approved: return _append_trace( "Directive rejected. No state change applied.", original_input=user_input, compiler_input=compile_input, - preprocessor_output=preprocessd, + drafter_output=drafted_input, decision=DecisionKind.NO_DIRECTIVE, state_before=state_before, state_after=(engine.premise, dict(engine.policies)), @@ -330,7 +329,7 @@ def handle_turn( kind = DECISION_UPDATE else: kind = DecisionKind.NO_DIRECTIVE.value - logger.debug("preprocessor: decision=%s", kind) + logger.debug("drafter: decision=%s", kind) if decision.kind == DecisionKind.ERROR: response_text = ( @@ -340,7 +339,7 @@ def handle_turn( response_text, original_input=user_input, compiler_input=compile_input, - preprocessor_output=preprocessd, + drafter_output=drafted_input, decision=decision, state_before=state_before, state_after=(engine.premise, dict(engine.policies)), @@ -352,7 +351,7 @@ def handle_turn( response_text, original_input=user_input, compiler_input=compile_input, - preprocessor_output=preprocessd, + drafter_output=drafted_input, decision=decision, state_before=state_before, state_after=(engine.premise, dict(engine.policies)), @@ -364,7 +363,7 @@ def handle_turn( response_text, original_input=user_input, compiler_input=compile_input, - preprocessor_output=preprocessd, + drafter_output=drafted_input, decision=decision, state_before=state_before, state_after=(engine.premise, dict(engine.policies)), diff --git a/python/reference_integrations/litellm_proxy/README.md b/python/reference_integrations/litellm_proxy/README.md index 1461198..f871e5e 100644 --- a/python/reference_integrations/litellm_proxy/README.md +++ b/python/reference_integrations/litellm_proxy/README.md @@ -201,10 +201,10 @@ python.reference_integrations.litellm_proxy.context_compiler_precall_hook.proxy_ Optional env vars for directive-drafter fallback: ```shell -export PREPROCESSOR_MODEL=openai/gpt-4o-mini +export DRAFTER_MODEL=openai/gpt-4o-mini ``` -`PREPROCESSOR_MODEL` is optional and defaults to `MODEL`. +`DRAFTER_MODEL` is optional and defaults to `MODEL`. The directive-drafter integration always uses heuristic-first processing with the configured fallback model when needed. @@ -213,7 +213,7 @@ the configured fallback model when needed. - Mixed-content user messages compile only text segments from the latest user turn. -- `MODEL` and `PREPROCESSOR_MODEL` use LiteLLM format: `/`. +- `MODEL` and `DRAFTER_MODEL` use LiteLLM format: `/`. - Corrupt or incompatible checkpoints fail clearly in persistent mode and do not silently reset state. - In the directive-drafter hook, drafter state context now comes from restored @@ -230,8 +230,7 @@ the configured fallback model when needed. explicit `stateless` mode - proxy starts but upstream calls fail: check `OPENAI_API_KEY` and upstream model/provider config in `config.example.yaml` -- directive-drafter fallback issues: `PREPROCESSOR_MODEL` defaults to `MODEL`; - set it explicitly only when using a separate fallback model +- directive-drafter fallback issues: `DRAFTER_MODEL` defaults to `MODEL` ## Opt-in Runtime Smoke Test diff --git a/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py b/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py index 2a46670..a9842df 100644 --- a/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py +++ b/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py @@ -76,17 +76,17 @@ def _create_directive_drafter( def _get_directive_drafter() -> DirectiveDrafter: - preprocessor_model = os.getenv("PREPROCESSOR_MODEL", "").strip() - if not preprocessor_model: - preprocessor_model = os.getenv("MODEL", "").strip() - if not preprocessor_model: + drafter_model = os.getenv("DRAFTER_MODEL", "").strip() + if not drafter_model: + drafter_model = os.getenv("MODEL", "").strip() + if not drafter_model: return DirectiveDrafter() api_key = os.getenv("OPENAI_API_KEY") if not api_key: return DirectiveDrafter() return _create_directive_drafter( - preprocessor_model, api_key, os.getenv("OPENAI_BASE_URL") or None + drafter_model, api_key, os.getenv("OPENAI_BASE_URL") or None ) @@ -98,7 +98,7 @@ def _draft_last_user_message(message: str) -> DraftResult: return DirectiveDrafter().draft_directive(message) -class ContextCompilerPreCallHookWithPreprocessor(CustomLogger): +class ContextCompilerPreCallHookWithDrafter(CustomLogger): async def async_pre_call_hook( self, user_api_key_dict: Any, @@ -181,4 +181,4 @@ async def async_pre_call_hook( return data -proxy_handler_instance = ContextCompilerPreCallHookWithPreprocessor() +proxy_handler_instance = ContextCompilerPreCallHookWithDrafter() diff --git a/python/reference_integrations/openwebui_pipe/README.md b/python/reference_integrations/openwebui_pipe/README.md index f155371..13d728f 100644 --- a/python/reference_integrations/openwebui_pipe/README.md +++ b/python/reference_integrations/openwebui_pipe/README.md @@ -2,7 +2,7 @@ Saved compiler state changes which turns the pipe handles locally and what it forwards downstream. These examples show Open WebUI pipe behavior with and -without Directive Drafter preprocessing. +without Directive Drafter drafting. ## Core behavior @@ -69,8 +69,8 @@ If using `open_webui_pipe_with_directive_drafter.py`: - Install directive-drafter support if needed: `pip install "context-compiler>=0.9.0dev13" "context-compiler-directive-drafter>=0.2.0dev5"` -- Optionally set `PREPROCESSOR_MODEL_ID` to use a separate fallback drafting model -- If `PREPROCESSOR_MODEL_ID` is unset, fallback uses `BASE_MODEL_ID` +- Optionally set `DRAFTER_MODEL_ID` to use a separate fallback drafting model +- If neither model id is set, fallback uses `BASE_MODEL_ID` Model fallback output is structurally validated before handoff. This does not prove that the model interpreted the user correctly. The automated fallback path is experimental pending a separate source-aware acceptance policy and reviewed drafting workflow. @@ -91,7 +91,7 @@ If frontmatter dependency installs are disabled, offline, or unavailable: ### Finding valid model ids Use the Open WebUI model picker/list to copy exact model ids for `BASE_MODEL_ID` -(and optional `PREPROCESSOR_MODEL_ID` for the directive-drafter pipe). +(and optional `DRAFTER_MODEL_ID` for the directive-drafter pipe). ## Verify behavior @@ -129,7 +129,7 @@ Advanced check: ### Directive-drafter pipe -Use this pipe when you want the same runtime behavior plus Directive Drafter preprocessing. +Use this pipe when you want the same runtime behavior plus Directive Drafter drafting. When the drafter produces a `CanonicalDirective`, the pipe uses Open WebUI's native `__event_call__` confirmation dialog for HITL approval. The lifecycle is: @@ -240,13 +240,13 @@ rejection flows. - `BASE_MODEL_ID is required`: set a valid Open WebUI model id in the function valves, or enable `ALLOW_MISSING_BASE_MODEL_FOR_DEBUG=true` only for local testing. - `BASE_MODEL_ID was not found in Open WebUI models`: copy the exact id from `Admin Panel → Settings → Models`. -- `PREPROCESSOR_MODEL_ID was not found in Open WebUI models`: set a valid fallback model id or leave it unset to default to `BASE_MODEL_ID`. -- `PREPROCESSOR_MODEL_ID must not match the selected pipe model id`: choose a real backend model id, not the pipe model id itself. -- `PREPROCESSOR_MODEL_ID is invalid or not configured in Open WebUI`: the fallback route hit a missing model; fix the configured fallback model or unset it to reuse `BASE_MODEL_ID`. +- `DRAFTER_MODEL_ID was not found in Open WebUI models`: set a valid fallback model id or leave it unset to default to `BASE_MODEL_ID`. +- `DRAFTER_MODEL_ID must not match the selected pipe model id`: choose a real backend model id, not the pipe model id itself. +- `DRAFTER_MODEL_ID is invalid or not configured in Open WebUI`: the fallback route hit a missing model; fix the configured fallback model or unset it to reuse `BASE_MODEL_ID`. - `ALLOW_MISSING_BASE_MODEL_FOR_DEBUG=true`: directive-only updates still run locally, but passthrough returns a deterministic debug message instead of calling a downstream model. - imports fail after function upload: install `context-compiler>=0.9.0dev13` in the Open WebUI runtime, and add `context-compiler-directive-drafter>=0.2.0dev5` only for the Directive Drafter pipe, because the copied function runs from a temp/cached location. ## Fallback notes -- Fallback drafting uses `PREPROCESSOR_MODEL_ID` first, while the main passthrough path still forwards with `BASE_MODEL_ID`. -- If the fallback model returns `model not found`, the pipe normalizes that into the deterministic `PREPROCESSOR_MODEL_ID` misconfiguration message above. +- Fallback drafting uses `DRAFTER_MODEL_ID`, while the main passthrough path still forwards with `BASE_MODEL_ID`. +- If the fallback model returns `model not found`, the pipe normalizes that into the deterministic `DRAFTER_MODEL_ID` misconfiguration message above. diff --git a/python/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py b/python/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py index 6ad328d..cbee6c3 100644 --- a/python/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py +++ b/python/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py @@ -2,7 +2,7 @@ title: Context Compiler Open WebUI Pipe (Directive Drafter) author: rlippmann author_url: https://github.com/rlippmann/context-compiler-example-integrations -version: 0.10.1 +version: 0.10.2 requirements: context-compiler>=0.9.0dev13, context-compiler-directive-drafter>=0.2.0dev5 Open WebUI integration with Context Compiler directive drafter. @@ -377,7 +377,7 @@ class Valves(BaseModel): "llama3.1:8b." ), ) - PREPROCESSOR_MODEL_ID: str | None = Field( + DRAFTER_MODEL_ID: str | None = Field( default=None, description=( "Optional model id for fallback drafting (defaults to BASE_MODEL_ID)." @@ -394,7 +394,7 @@ class Valves(BaseModel): def __init__(self) -> None: self.valves = self.Valves() - self._last_preprocessor_error: str | None = None + self._last_drafter_error: str | None = None def _allow_missing_base_model_for_debug(self) -> bool: return _is_truthy_bool( @@ -498,12 +498,12 @@ def _with_trace( state_before: object, state_after: object, llm_called: bool, - preprocessor_output: str | None = None, + drafter_output: str | None = None, state_injected: str = "no", ) -> Any: if not self._trace_enabled(): return response - del original_input, compiler_input, preprocessor_output + del original_input, compiler_input, drafter_output trace_text = _build_compact_trace_text( decision=decision, state_before=state_before, @@ -548,30 +548,34 @@ def _normalize_forward_exception(self, exc: Exception) -> str | None: ) return None - def _normalize_preprocessor_error(self, response: Any) -> str | None: + def _normalize_drafter_error(self, response: Any) -> str | None: if self._contains_model_not_found(response): return ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID is invalid or " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID is invalid or " "not configured in Open WebUI. Configure a valid model id in " "Admin Panel → Settings → Models." ) return None - def _normalize_preprocessor_exception(self, exc: Exception) -> str | None: + def _normalize_drafter_exception(self, exc: Exception) -> str | None: detail = getattr(exc, "detail", None) if self._contains_model_not_found(detail) or self._contains_model_not_found( str(exc) ): return ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID is invalid or " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID is invalid or " "not configured in Open WebUI. Configure a valid model id in " "Admin Panel → Settings → Models." ) return None - def _resolve_preprocessor_model_id(self, base_model_id: str | None) -> str | None: - preprocessor_model_id = _normalize_model_id(self.valves.PREPROCESSOR_MODEL_ID) - return preprocessor_model_id or base_model_id + def _resolve_drafter_model_id(self, base_model_id: str | None) -> str | None: + drafter_model_id = _normalize_model_id( + getattr(self.valves, "DRAFTER_MODEL_ID", None) + ) + if drafter_model_id: + return drafter_model_id + return base_model_id async def _validate_configured_model_ids( self, @@ -579,10 +583,10 @@ async def _validate_configured_model_ids( user_payload: dict[str, Any], *, base_model_id: str | None, - preprocessor_model_id: str | None, + drafter_model_id: str | None, ) -> str | None: base_model_id = _normalize_model_id(base_model_id) - preprocessor_model_id = _normalize_model_id(preprocessor_model_id) + drafter_model_id = _normalize_model_id(drafter_model_id) # Best-effort preflight: fail closed only for clear missing-model mismatches. # If model discovery fails, preserve runtime behavior and rely on call-path # normalization below. @@ -608,9 +612,9 @@ async def _validate_configured_model_ids( "Context Compiler pipe misconfigured: BASE_MODEL_ID was not found " "in Open WebUI models." ) - if preprocessor_model_id and preprocessor_model_id not in known_model_ids: + if drafter_model_id and drafter_model_id not in known_model_ids: return ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID was not found " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID was not found " "in Open WebUI models." ) return None @@ -623,7 +627,7 @@ async def _llm_fallback_candidate( user_payload: dict[str, Any], model_id: str | None, ) -> str | None: - self._last_preprocessor_error = None + self._last_drafter_error = None model_id = _normalize_model_id(model_id) if model_id is None: return None @@ -642,16 +646,16 @@ async def _llm_fallback_candidate( try: response = await generate_chat_completion(request, payload, user) except Exception as exc: - normalized_exception = self._normalize_preprocessor_exception(exc) + normalized_exception = self._normalize_drafter_exception(exc) if normalized_exception is not None: - self._last_preprocessor_error = normalized_exception - logger.warning("preprocessor: %s", normalized_exception) + self._last_drafter_error = normalized_exception + logger.warning("drafter: %s", normalized_exception) return None - normalized_error = self._normalize_preprocessor_error(response) + normalized_error = self._normalize_drafter_error(response) if normalized_error is not None: - self._last_preprocessor_error = normalized_error - logger.warning("preprocessor: %s", normalized_error) + self._last_drafter_error = normalized_error + logger.warning("drafter: %s", normalized_error) return None return _extract_completion_content(response) @@ -687,7 +691,7 @@ def _extract_drafted_text(self, drafted_result: DraftResult) -> str | None: return None return None - async def _preprocess_user_input( + async def _run_drafter( self, message: str, *, @@ -695,14 +699,14 @@ async def _preprocess_user_input( user_payload: dict[str, Any], model_id: str | None, ) -> tuple[DraftResult, str | None]: - self._last_preprocessor_error = None + self._last_drafter_error = None drafted_result = await self._draft_user_input( message, request=request, user_payload=user_payload, model_id=model_id, ) - return drafted_result, self._last_preprocessor_error + return drafted_result, self._last_drafter_error async def _forward_passthrough( self, @@ -758,7 +762,7 @@ async def _apply_approved_directive( state_before = _snapshot_engine_state(engine) engine_snapshot_json = engine.export_json() compile_input = directive.text - logger.debug("preprocessor: approved_input=%r", compile_input) + logger.debug("drafter: approved_input=%r", compile_input) decision = engine.apply_directive(directive) state_after = _snapshot_engine_state(engine) @@ -773,7 +777,7 @@ async def _apply_approved_directive( decision=decision, state_before=state_before, state_after=state_after, - preprocessor_output=compile_input, + drafter_output=compile_input, llm_called=False, ) if decision.kind == DecisionKind.UPDATE: @@ -784,7 +788,7 @@ async def _apply_approved_directive( decision=decision, state_before=state_before, state_after=state_after, - preprocessor_output=compile_input, + drafter_output=compile_input, llm_called=False, ) @@ -803,7 +807,7 @@ async def _apply_approved_directive( decision=decision, state_before=state_before, state_after=state_after, - preprocessor_output=compile_input, + drafter_output=compile_input, llm_called=base_model_id is not None, state_injected=state_injected, ) @@ -819,7 +823,7 @@ async def pipe( ) -> Any: # Open WebUI integration entrypoint: # 1) extract latest user input - # 2) run preprocess (heuristic -> LLM fallback) + # 2) run drafter (heuristic -> LLM fallback) # 3) pass directive or original input to engine.step(...) # 4) map decision back to Open WebUI response behavior raw_messages = body.get("messages") @@ -829,8 +833,7 @@ async def pipe( else [] ) base_model_id = _normalize_model_id(self.valves.BASE_MODEL_ID) - preprocessor_model_id = _normalize_model_id(self.valves.PREPROCESSOR_MODEL_ID) - effective_preprocessor_model = preprocessor_model_id or base_model_id + effective_drafter_model = self._resolve_drafter_model_id(base_model_id) current_model_id = str(body.get("model", "")).strip() if not base_model_id and not self._allow_missing_base_model_for_debug(): @@ -844,12 +847,12 @@ async def pipe( "the selected pipe model id to avoid recursive routing." ) if ( - effective_preprocessor_model + effective_drafter_model and current_model_id - and effective_preprocessor_model == current_model_id + and effective_drafter_model == current_model_id ): return ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID must not " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID must not " "match the selected pipe model id to avoid recursive routing." ) @@ -857,13 +860,13 @@ async def pipe( __request__, __user__, base_model_id=base_model_id, - preprocessor_model_id=effective_preprocessor_model, + drafter_model_id=effective_drafter_model, ) if preflight_error is not None: return preflight_error latest_user_text = _extract_latest_user_text(messages) - logger.debug("preprocessor: user_input_found=%s", latest_user_text is not None) + logger.debug("drafter: user_input_found=%s", latest_user_text is not None) if latest_user_text is None: return await self._forward_passthrough( @@ -883,17 +886,17 @@ async def pipe( return _render_show_state_summary(engine) state_before = _snapshot_engine_state(engine) - preprocess_error: str | None = None - drafted_result, preprocess_error = await self._preprocess_user_input( + drafter_error: str | None = None + drafted_result, drafter_error = await self._run_drafter( latest_user_text, request=__request__, user_payload=__user__, - model_id=effective_preprocessor_model, + model_id=effective_drafter_model, ) - if preprocess_error is not None: - return preprocess_error + if drafter_error is not None: + return drafter_error - logger.debug("preprocessor: drafted_result=%r", drafted_result) + logger.debug("drafter: drafted_result=%r", drafted_result) if not isinstance(drafted_result.result, CanonicalDirective): state_injected = ( "yes" if _has_non_empty_authoritative_state(engine) else "no" @@ -912,7 +915,7 @@ async def pipe( decision=DecisionKind.NO_DIRECTIVE, state_before=state_before, state_after=state_before, - preprocessor_output=None, + drafter_output=None, llm_called=base_model_id is not None, state_injected=state_injected, ) diff --git a/python/tests/test_litellm_proxy_with_directive_drafter.py b/python/tests/test_litellm_proxy_with_directive_drafter.py index d85acae..beb08e0 100644 --- a/python/tests/test_litellm_proxy_with_directive_drafter.py +++ b/python/tests/test_litellm_proxy_with_directive_drafter.py @@ -48,7 +48,7 @@ class _CustomLogger: def test_drafter_runs_only_for_current_turn(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_current_only") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() drafted_calls: list[tuple[str, dict[str, object]]] = [] def fake_draft(message: str) -> object: @@ -77,7 +77,7 @@ def fake_draft(message: str) -> object: def test_drafter_output_applies_to_current_turn_only(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_applies") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() directive = decompose_directive("prohibit docker") assert directive is not None monkeypatch.setattr( @@ -109,7 +109,7 @@ def test_persistent_mode_with_drafter_rejects_failed_application_without_persist ) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_failed_apply") module.CHECKPOINT_STORE.clear() - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() drafted_inputs: list[str] = [] def fake_draft(message: str) -> object: @@ -139,7 +139,7 @@ def fake_draft(message: str) -> object: def test_missing_session_key_fails_clearly_in_persistent_mode(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_missing_session") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() data = { "model": "demo", "context_compiler_mode": "persistent", @@ -154,7 +154,7 @@ def test_missing_session_key_fails_clearly_in_persistent_mode(monkeypatch) -> No def test_default_mode_is_stateless_and_requires_no_session_key(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_default_stateless") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() monkeypatch.setattr( module, "_draft_last_user_message", @@ -175,7 +175,7 @@ def test_default_mode_is_stateless_and_requires_no_session_key(monkeypatch) -> N def test_stateless_mode_has_no_cross_call_continuity(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_stateless") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() monkeypatch.setattr( module, "_draft_last_user_message", @@ -209,7 +209,7 @@ def test_persistent_mode_with_drafter_preserves_existing_checkpoint_on_failure( ) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_preserve_checkpoint") module.CHECKPOINT_STORE.clear() - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() def seed_draft(message: str) -> object: return module.DraftResult( @@ -271,7 +271,7 @@ def reject_draft(message: str) -> object: def test_normal_update_explicitly_saves_checkpoint(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_save_after_update") module.CHECKPOINT_STORE.clear() - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() directive = decompose_directive("prohibit peanuts") assert directive is not None monkeypatch.setattr( @@ -300,7 +300,7 @@ def test_normal_update_explicitly_saves_checkpoint(monkeypatch) -> None: def test_restore_happens_before_drafting(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_restore_first") module.CHECKPOINT_STORE.clear() - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() module.CHECKPOINT_STORE.save( "chat-restore-first", {"premise": None, "policies": {"peanuts": "prohibit"}, "version": 2}, @@ -331,7 +331,7 @@ def test_corrupt_checkpoint_fails_clearly(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_corrupt") module.CHECKPOINT_STORE.clear() module.CHECKPOINT_STORE.save("broken", {"checkpoint_version": 99}) - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() data = { "model": "demo", "context_compiler_mode": "persistent", @@ -347,7 +347,7 @@ def test_corrupt_checkpoint_fails_clearly(monkeypatch) -> None: def test_forwarded_messages_keep_original_user_prompt_text(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_forwarded_text") - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() directive = decompose_directive("use docker") assert directive is not None original_messages = [ @@ -379,7 +379,7 @@ def test_compound_directives_fall_through_to_normal_forwarding_when_not_applied( ) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_compound") module.CHECKPOINT_STORE.clear() - hook = module.ContextCompilerPreCallHookWithPreprocessor() + hook = module.ContextCompilerPreCallHookWithDrafter() monkeypatch.setattr( module, "_draft_last_user_message", @@ -405,7 +405,7 @@ def test_compound_directives_fall_through_to_normal_forwarding_when_not_applied( assert checkpoint["policies"] == {} -def test_fallback_adapter_receives_preprocessor_model(monkeypatch) -> None: +def test_fallback_adapter_receives_drafter_model(monkeypatch) -> None: module = _load_module(monkeypatch, "litellm_proxy_with_drafter_shared_prompt") monkeypatch.setenv("OPENAI_API_KEY", "dummy") monkeypatch.setenv("MODEL", "openai/demo-model") diff --git a/python/tests/test_litellm_with_directive_drafter.py b/python/tests/test_litellm_with_directive_drafter.py index 7ed0fdd..613d067 100644 --- a/python/tests/test_litellm_with_directive_drafter.py +++ b/python/tests/test_litellm_with_directive_drafter.py @@ -317,7 +317,7 @@ def test_call_litellm_logs_startup_config_once( assert "source=OPENAI_BASE_URL override" in message -def test_preprocessor_model_defaults_to_model(monkeypatch) -> None: +def test_drafter_model_defaults_to_model(monkeypatch) -> None: seen: dict[str, object] = {} def fallback_factory(**kwargs: Any): @@ -326,7 +326,7 @@ def fallback_factory(**kwargs: Any): monkeypatch.setenv("OPENAI_API_KEY", "dummy") monkeypatch.setenv("MODEL", "openai/main-model") - monkeypatch.delenv("PREPROCESSOR_MODEL", raising=False) + monkeypatch.delenv("DRAFTER_MODEL", raising=False) monkeypatch.setattr(module, "create_litellm_fallback", fallback_factory) module._create_directive_drafter.cache_clear() @@ -334,7 +334,7 @@ def fallback_factory(**kwargs: Any): assert seen["model"] == "openai/main-model" -def test_preprocessor_model_override_wins(monkeypatch) -> None: +def test_drafter_model_override_wins(monkeypatch) -> None: seen: dict[str, object] = {} def fallback_factory(**kwargs: Any): @@ -343,12 +343,12 @@ def fallback_factory(**kwargs: Any): monkeypatch.setenv("OPENAI_API_KEY", "dummy") monkeypatch.setenv("MODEL", "openai/main-model") - monkeypatch.setenv("PREPROCESSOR_MODEL", "openai/preprocessor-model") + monkeypatch.setenv("DRAFTER_MODEL", "openai/drafter-model") monkeypatch.setattr(module, "create_litellm_fallback", fallback_factory) module._create_directive_drafter.cache_clear() module._get_directive_drafter() - assert seen["model"] == "openai/preprocessor-model" + assert seen["model"] == "openai/drafter-model" def test_directive_shaped_malformed_inputs_can_fall_through_to_normal_turn_flow( @@ -400,7 +400,7 @@ def downstream(_messages: list[dict[str, str]]) -> str: def test_handle_turn_has_no_session_or_resume_behavior(monkeypatch) -> None: monkeypatch.setattr(module, "_call_litellm", lambda _messages: "ok") - monkeypatch.setattr(module, "_preprocess_user_input", lambda _text: None) + monkeypatch.setattr(module, "_draft_user_input", lambda _text: None) engine = Engine() diff --git a/python/tests/test_openwebui_pipe_with_directive_drafter.py b/python/tests/test_openwebui_pipe_with_directive_drafter.py index 97b5232..161456a 100644 --- a/python/tests/test_openwebui_pipe_with_directive_drafter.py +++ b/python/tests/test_openwebui_pipe_with_directive_drafter.py @@ -115,7 +115,7 @@ async def confirm(event: dict[str, object]) -> bool: monkeypatch.setattr(module.Pipe, "_draft_user_input", fake_draft) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" chat_id = "chat-native-confirmation" result = asyncio.run( @@ -169,7 +169,7 @@ async def fake_draft(*args, **kwargs): pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" chat_id = "chat-before-step" async def confirm(event: dict[str, object]) -> bool: @@ -223,7 +223,7 @@ def tracked_apply_directive(directive): monkeypatch.setattr(module, "Engine", Engine_with_tracking) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -289,7 +289,7 @@ async def confirm(event: dict[str, object]) -> bool: first_pipe = module.Pipe() first_pipe.valves.BASE_MODEL_ID = "base-model" - first_pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + first_pipe.valves.DRAFTER_MODEL_ID = "prep-model" chat_id = "chat-pending-lifecycle" first_result = asyncio.run( first_pipe.pipe( @@ -308,7 +308,7 @@ async def confirm(event: dict[str, object]) -> bool: module._ENGINES_BY_CHAT_KEY.clear() second_pipe = module.Pipe() second_pipe.valves.BASE_MODEL_ID = "base-model" - second_pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + second_pipe.valves.DRAFTER_MODEL_ID = "prep-model" second_result = asyncio.run( second_pipe.pipe( { @@ -344,7 +344,7 @@ def test_rejection_does_not_mutate_state(monkeypatch) -> None: ) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -404,7 +404,7 @@ def test_rejected_confirmation_does_not_affect_show_state(monkeypatch) -> None: module = _load_module("owui_with_drafter_pending_show_state", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -477,7 +477,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -563,7 +563,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -660,7 +660,7 @@ async def no_draft(*args, **kwargs): pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" result = asyncio.run( pipe.pipe( @@ -693,7 +693,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def update_draft(*args, **kwargs): return DraftResult( @@ -763,7 +763,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def no_draft(*args, **kwargs): return DraftResult( @@ -826,7 +826,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def compound_draft(*args, **kwargs): return DraftResult( @@ -871,7 +871,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" chat_id = "chat-passthrough" async def update_draft(*args, **kwargs): @@ -935,26 +935,26 @@ async def no_draft(*args, **kwargs): assert len(cc_messages) == 1 -def test_preprocessor_model_defaults_to_base_model(monkeypatch) -> None: +def test_drafter_model_defaults_to_base_model(monkeypatch) -> None: module = _load_module("owui_with_drafter_model_default", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = None + pipe.valves.DRAFTER_MODEL_ID = None - assert pipe._resolve_preprocessor_model_id("base-model") == "base-model" + assert pipe._resolve_drafter_model_id("base-model") == "base-model" -def test_preprocessor_model_override_wins(monkeypatch) -> None: +def test_drafter_model_override_wins(monkeypatch) -> None: module = _load_module("owui_with_drafter_model_override", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" - assert pipe._resolve_preprocessor_model_id("base-model") == "prep-model" + assert pipe._resolve_drafter_model_id("base-model") == "prep-model" -def test_invalid_preprocessor_model_id_from_model_list(monkeypatch) -> None: - module = _load_module("owui_with_drafter_invalid_preprocessor_model", monkeypatch) +def test_invalid_drafter_model_id_from_model_list(monkeypatch) -> None: + module = _load_module("owui_with_drafter_invalid_drafter_model", monkeypatch) pipe = module.Pipe() async def models(_: object, user: object = None) -> list[dict[str, str]]: @@ -968,21 +968,21 @@ async def models(_: object, user: object = None) -> list[dict[str, str]]: request=object(), user_payload={"id": "u1"}, base_model_id="base-model", - preprocessor_model_id="missing-prep-model", + drafter_model_id="missing-drafter-model", ) ) assert error == ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID was not found " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID was not found " "in Open WebUI models." ) -def test_recursion_guard_for_preprocessor_model_id(monkeypatch) -> None: +def test_recursion_guard_for_drafter_model_id(monkeypatch) -> None: module = _load_module("owui_with_drafter_recursion_guard", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "pipe-model" + pipe.valves.DRAFTER_MODEL_ID = "pipe-model" result = asyncio.run( pipe.pipe( @@ -993,7 +993,7 @@ def test_recursion_guard_for_preprocessor_model_id(monkeypatch) -> None: ) assert result == ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID must not " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID must not " "match the selected pipe model id to avoid recursive routing." ) @@ -1004,7 +1004,7 @@ def test_debug_mode_missing_base_model_returns_deterministic_message( module = _load_module("owui_with_drafter_debug_missing_base", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = None - pipe.valves.PREPROCESSOR_MODEL_ID = None + pipe.valves.DRAFTER_MODEL_ID = None pipe.valves.ALLOW_MISSING_BASE_MODEL_FOR_DEBUG = True async def no_draft(*args, **kwargs): @@ -1033,11 +1033,11 @@ async def no_draft(*args, **kwargs): ) -def test_preprocessor_model_not_found_is_normalized(monkeypatch) -> None: - module = _load_module("owui_with_drafter_preprocessor_not_found", monkeypatch) +def test_drafter_model_not_found_is_normalized(monkeypatch) -> None: + module = _load_module("owui_with_drafter_drafter_not_found", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def generate( _: object, payload: dict[str, object], __: object @@ -1055,24 +1055,24 @@ async def generate( }, __user__={"id": "u1"}, __request__=object(), - __chat_id__="chat-preprocessor-not-found", + __chat_id__="chat-drafter-not-found", ) ) assert result == ( - "Context Compiler pipe misconfigured: PREPROCESSOR_MODEL_ID is invalid or " + "Context Compiler pipe misconfigured: DRAFTER_MODEL_ID is invalid or " "not configured in Open WebUI. Configure a valid model id in " "Admin Panel → Settings → Models." ) -def test_fallback_uses_preprocessor_model_then_forward_uses_base_model( +def test_fallback_uses_drafter_model_then_forward_uses_base_model( monkeypatch, ) -> None: module = _load_module("owui_with_drafter_fallback_routing", monkeypatch) pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" calls: list[str] = [] async def generate( @@ -1138,7 +1138,7 @@ async def forward( module.generate_chat_completion = forward pipe = module.Pipe() pipe.valves.BASE_MODEL_ID = "base-model" - pipe.valves.PREPROCESSOR_MODEL_ID = "prep-model" + pipe.valves.DRAFTER_MODEL_ID = "prep-model" async def unknown_draft(*args, **kwargs): return DraftResult( @@ -1175,7 +1175,7 @@ async def get_user_by_id(user_id: object) -> dict[str, object]: request=object(), user_payload={"id": "u1"}, base_model_id="base-model", - preprocessor_model_id="prep-model", + drafter_model_id="prep-model", ) )