context-compiler 0.7.1__tar.gz → 0.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. {context_compiler-0.7.1 → context_compiler-0.7.2}/PKG-INFO +1 -1
  2. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/openwebui/README.md +4 -4
  3. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/openwebui/open_webui_pipe.py +44 -22
  4. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/openwebui/open_webui_pipe_with_preprocessor.py +39 -20
  5. {context_compiler-0.7.1 → context_compiler-0.7.2}/pyproject.toml +1 -1
  6. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/observability.py +1 -1
  7. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_openwebui_pipe.py +206 -0
  8. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_openwebui_preprocessor_pipe.py +199 -0
  9. {context_compiler-0.7.1 → context_compiler-0.7.2}/uv.lock +1 -1
  10. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  11. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  12. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/pull_request_template.md +0 -0
  13. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/workflows/ci.yml +0 -0
  14. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/workflows/publish-pypi.yml +0 -0
  15. {context_compiler-0.7.1 → context_compiler-0.7.2}/.github/workflows/stress-tests.yml +0 -0
  16. {context_compiler-0.7.1 → context_compiler-0.7.2}/.gitignore +0 -0
  17. {context_compiler-0.7.1 → context_compiler-0.7.2}/.pre-commit-config.yaml +0 -0
  18. {context_compiler-0.7.1 → context_compiler-0.7.2}/AGENTS.md +0 -0
  19. {context_compiler-0.7.1 → context_compiler-0.7.2}/CODE_OF_CONDUCT.md +0 -0
  20. {context_compiler-0.7.1 → context_compiler-0.7.2}/CONTRIBUTING.md +0 -0
  21. {context_compiler-0.7.1 → context_compiler-0.7.2}/LICENSE +0 -0
  22. {context_compiler-0.7.1 → context_compiler-0.7.2}/README.md +0 -0
  23. {context_compiler-0.7.1 → context_compiler-0.7.2}/SECURITY.md +0 -0
  24. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/01_llm_contradiction_clarify.py +0 -0
  25. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/02_llm_constraint_guardrail.py +0 -0
  26. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/03_llm_premise_guardrail.py +0 -0
  27. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/04_llm_tool_denylist_guardrail.py +0 -0
  28. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/05_llm_prompt_drift_vs_state.py +0 -0
  29. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/06_llm_context_compaction.py +0 -0
  30. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/07_llm_prompt_vs_state.py +0 -0
  31. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/08_llm_replacement_precondition.py +0 -0
  32. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/09_llm_pending_clarification.py +0 -0
  33. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/README.md +0 -0
  34. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/__init__.py +0 -0
  35. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/common.py +0 -0
  36. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/llm_client.py +0 -0
  37. {context_compiler-0.7.1 → context_compiler-0.7.2}/demos/run_demo.py +0 -0
  38. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/DescriptionAndMilestones.md +0 -0
  39. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/DesignPhilosophy.md +0 -0
  40. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/DirectiveGrammarSpec.md +0 -0
  41. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/README.md +0 -0
  42. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/demos-results.md +0 -0
  43. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/llm-preprocessor.md +0 -0
  44. {context_compiler-0.7.1 → context_compiler-0.7.2}/docs/multi-engine.md +0 -0
  45. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/litellm_proxy_additional_findings.md +0 -0
  46. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/litellm_proxy_behavioral_comparisons.md +0 -0
  47. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/README.md +0 -0
  48. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/RUBRIC.md +0 -0
  49. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/manifest.json +0 -0
  50. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/swe-bench.py +0 -0
  51. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/django__django-12453.json +0 -0
  52. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/django__django-13158.json +0 -0
  53. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/django__django-13964.json +0 -0
  54. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/django__django-15252.json +0 -0
  55. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/matplotlib__matplotlib-23299.json +0 -0
  56. {context_compiler-0.7.1 → context_compiler-0.7.2}/evals/swe-bench/tasks/psf__requests-1963.json +0 -0
  57. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/01_persistent_guardrails.py +0 -0
  58. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/02_configuration_and_correction.py +0 -0
  59. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/03_ambiguity_with_clarification.py +0 -0
  60. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/04_tool_governance_denylist.py +0 -0
  61. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/05_llm_integration_pattern.py +0 -0
  62. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/06_transcript_replay.py +0 -0
  63. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/07_single_policy_correction.py +0 -0
  64. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/README.md +0 -0
  65. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/_util.py +0 -0
  66. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/README.md +0 -0
  67. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm/README.md +0 -0
  68. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm/basic.py +0 -0
  69. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm/with_preprocessor.py +0 -0
  70. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm_proxy/README.md +0 -0
  71. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm_proxy/config.example.yaml +0 -0
  72. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm_proxy/context_compiler_precall_hook.py +0 -0
  73. {context_compiler-0.7.1 → context_compiler-0.7.2}/examples/integrations/litellm_proxy/context_compiler_precall_hook_with_preprocessor.py +0 -0
  74. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/__init__.py +0 -0
  75. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/README.md +0 -0
  76. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/__init__.py +0 -0
  77. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/constants.py +0 -0
  78. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/heuristic_preprocessor.py +0 -0
  79. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/output_validation.py +0 -0
  80. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/prompt_utils.py +0 -0
  81. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/prompts/default.txt +0 -0
  82. {context_compiler-0.7.1 → context_compiler-0.7.2}/experimental/preprocessor/prompts/llama.txt +0 -0
  83. {context_compiler-0.7.1 → context_compiler-0.7.2}/host_support/__init__.py +0 -0
  84. {context_compiler-0.7.1 → context_compiler-0.7.2}/host_support/confirmation.py +0 -0
  85. {context_compiler-0.7.1 → context_compiler-0.7.2}/host_support/provider_mode.py +0 -0
  86. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/__init__.py +0 -0
  87. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/const.py +0 -0
  88. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/controller.py +0 -0
  89. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/decision_constants.py +0 -0
  90. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/engine.py +0 -0
  91. {context_compiler-0.7.1 → context_compiler-0.7.2}/src/context_compiler/repl.py +0 -0
  92. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/README.md +0 -0
  93. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/api/public-api-v1.json +0 -0
  94. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/001_import_checkpoint_non_object_rejected.json +0 -0
  95. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/002_import_checkpoint_unsupported_version_rejected.json +0 -0
  96. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/003_import_checkpoint_invalid_pending_shape_rejected.json +0 -0
  97. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/004_import_checkpoint_invalid_replacement_shape_rejected.json +0 -0
  98. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/005_import_checkpoint_invalid_authoritative_state_rejected_atomically.json +0 -0
  99. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/006_import_checkpoint_pending_null_clears_existing_pending.json +0 -0
  100. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/checkpoint/007_import_checkpoint_pending_absent_clears_existing_pending.json +0 -0
  101. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/001_export_json_canonical_sorted_compact.json +0 -0
  102. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/002_import_json_invalid_json_rejected.json +0 -0
  103. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/003_import_json_non_object_rejected.json +0 -0
  104. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/004_import_json_unsupported_version_rejected.json +0 -0
  105. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/005_import_json_empty_normalized_policy_key_rejected_atomically.json +0 -0
  106. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/state-json/006_import_json_valid_normalized_policy_key_accepted.json +0 -0
  107. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/001_set_premise_update.json +0 -0
  108. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/002_use_item_normalization.json +0 -0
  109. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/003_conflict_prohibit_clarify.json +0 -0
  110. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/004_remove_policy_missing_idempotent_update.json +0 -0
  111. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/005_exact_prefix_passthrough_leading_space.json +0 -0
  112. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/006_near_miss_set_premise_to.json +0 -0
  113. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/007_near_miss_change_premise_missing_to.json +0 -0
  114. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/008_replace_missing_source_clarify_prompt.json +0 -0
  115. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/009_pending_affirmative_normalized_token.json +0 -0
  116. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/010_pending_negative_normalized_token.json +0 -0
  117. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/011_pending_unmatched_reuses_prompt.json +0 -0
  118. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/012_clear_premise_populated_update.json +0 -0
  119. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/013_clear_premise_already_null_update.json +0 -0
  120. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/014_reset_policies_populated_update.json +0 -0
  121. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/015_reset_policies_already_empty_update.json +0 -0
  122. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/016_clear_state_populated_update.json +0 -0
  123. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/017_clear_state_already_empty_update.json +0 -0
  124. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/018_pending_affirmative_punctuation_token.json +0 -0
  125. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/step/019_pending_negative_punctuation_token.json +0 -0
  126. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/transcript/001_user_only_replay_state.json +0 -0
  127. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/transcript/002_non_string_user_content_ignored.json +0 -0
  128. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/transcript/003_stops_at_first_clarify_later_yes.json +0 -0
  129. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/conformance/transcript/004_stops_at_first_clarify_later_no.json +0 -0
  130. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/controller/preview_clarify_no_mutation.json +0 -0
  131. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/controller/preview_idempotent_no_mutation.json +0 -0
  132. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/README.md +0 -0
  133. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/contradiction_clarify.json +0 -0
  134. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/pending_clarify_no.json +0 -0
  135. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/pending_clarify_unmatched.json +0 -0
  136. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/pending_clarify_yes.json +0 -0
  137. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/premise_lifecycle.json +0 -0
  138. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/expected/replacement_clarify.json +0 -0
  139. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/contradiction_clarify.json +0 -0
  140. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/pending_clarify_no.json +0 -0
  141. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/pending_clarify_unmatched.json +0 -0
  142. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/pending_clarify_yes.json +0 -0
  143. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/premise_lifecycle.json +0 -0
  144. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/engine-regression/structured/scenarios/replacement_clarify.json +0 -0
  145. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/admin-alias-remove-policies-unknown.json +0 -0
  146. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/admin-alias-reset-policy-unknown.json +0 -0
  147. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/ambiguous-directive-adjacent.json +0 -0
  148. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-bracket-wrapper.json +0 -0
  149. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-case-normalized-use-docker.json +0 -0
  150. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-clear-state-period.json +0 -0
  151. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-clear-state.json +0 -0
  152. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-paren-wrapper.json +0 -0
  153. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-prohibit-peanuts.json +0 -0
  154. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-quoted-payload-use-docker.json +0 -0
  155. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-reset-policies-bang.json +0 -0
  156. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/canonical-directive-whitespace-collapsed-use-docker.json +0 -0
  157. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/fenced-code-block-directive-unknown.json +0 -0
  158. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/inline-prose-code-directive-unknown.json +0 -0
  159. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/list-prefix-directive-unknown.json +0 -0
  160. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/malformed-replacement-instead-docker-unknown.json +0 -0
  161. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/meta-prefix-directive-unknown.json +0 -0
  162. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/mixed-directive-task-unknown.json +0 -0
  163. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/mixed-intent-unknown.json +0 -0
  164. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/modal-please-clear-state-unknown.json +0 -0
  165. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/multiline-multi-directive-unknown.json +0 -0
  166. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/natural-language-dont-use-unknown.json +0 -0
  167. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-change-premise-missing-to-unknown.json +0 -0
  168. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-change-premise-to-empty-unknown.json +0 -0
  169. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-prohibit-empty-unknown.json +0 -0
  170. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-remove-policy-empty-unknown.json +0 -0
  171. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-set-premise-empty-unknown.json +0 -0
  172. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-set-premise-to-unknown.json +0 -0
  173. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-use-empty-unknown.json +0 -0
  174. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-use-instead-of-missing-new-item-unknown.json +0 -0
  175. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/near-miss-use-instead-of-missing-old-item-unknown.json +0 -0
  176. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/nested-wrapper-clear-state-unknown.json +0 -0
  177. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/ordinary-non-directive.json +0 -0
  178. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-fenced-code-rejected.json +0 -0
  179. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-inline-prose-code-rejected.json +0 -0
  180. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-mixed-directive-task-rejected.json +0 -0
  181. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-multiline-rejected.json +0 -0
  182. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-question-form-rejected.json +0 -0
  183. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-quoted-payload-locked.json +0 -0
  184. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-reported-speech-rejected.json +0 -0
  185. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-safe-canonical-use-docker.json +0 -0
  186. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/parse-source-input-safe-canonicalization-use-docker.json +0 -0
  187. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/public-api-v1.json +0 -0
  188. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/question-can-you-use-docker-unknown.json +0 -0
  189. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/question-use-docker-unknown.json +0 -0
  190. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/quoted-exact-use-docker-backtick-unknown.json +0 -0
  191. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/quoted-exact-use-docker-single-unknown.json +0 -0
  192. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/quoted-exact-use-docker-unknown.json +0 -0
  193. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/quoted-reported-unknown.json +0 -0
  194. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/reported-speech-docs-say-unknown.json +0 -0
  195. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/sentence-adjacent-directive-unknown.json +0 -0
  196. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/unsupported-alias-unknown.json +0 -0
  197. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-invalid-json-shape-unknown.json +0 -0
  198. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-malformed-json-text-unknown.json +0 -0
  199. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-malformed-sentinel-unknown.json +0 -0
  200. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-malformed-text-unknown.json +0 -0
  201. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-multi-candidate-directive-unknown.json +0 -0
  202. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-sentinel-no-directive.json +0 -0
  203. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-source-input-allow-safe-directive.json +0 -0
  204. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-source-input-block-change-premise-rewrite.json +0 -0
  205. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-source-input-block-set-premise-rewrite.json +0 -0
  206. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/fixtures/preprocessor/validator-structured-json-directive.json +0 -0
  207. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_04_grammar_edge_cases.py +0 -0
  208. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_04_llm_tool_governance.py +0 -0
  209. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_07_llm_prompt_engineering_comparison.py +0 -0
  210. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_api_contract_fixture.py +0 -0
  211. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_controller.py +0 -0
  212. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_decision_constants.py +0 -0
  213. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_01_04_behavior.py +0 -0
  214. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_05_prompt_contract.py +0 -0
  215. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_07_output_clarity.py +0 -0
  216. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_08_09_behavior.py +0 -0
  217. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_compaction.py +0 -0
  218. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_demo_oracle_properties.py +0 -0
  219. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_engine.py +0 -0
  220. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_example_integrations_imports.py +0 -0
  221. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_examples.py +0 -0
  222. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_examples_behavior.py +0 -0
  223. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_examples_smoke.py +0 -0
  224. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_fixtures.py +0 -0
  225. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_host_confirmation.py +0 -0
  226. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_host_observability.py +0 -0
  227. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_litellm_checkpoint_integration.py +0 -0
  228. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_litellm_integration_error_paths.py +0 -0
  229. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_litellm_preprocessor_model_config.py +0 -0
  230. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_llm_client.py +0 -0
  231. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_llm_demos.py +0 -0
  232. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_precompiler_prompt_utils.py +0 -0
  233. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_api_contract_fixture.py +0 -0
  234. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_conformance.py +0 -0
  235. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_heuristic.py +0 -0
  236. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_heuristic_properties.py +0 -0
  237. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_output_validation.py +0 -0
  238. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_preprocessor_validator_properties.py +0 -0
  239. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_properties.py +0 -0
  240. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_provider_helper.py +0 -0
  241. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_repl.py +0 -0
  242. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_repl_coverage.py +0 -0
  243. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_repl_properties.py +0 -0
  244. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_run_demo.py +0 -0
  245. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_smoke.py +0 -0
  246. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_structured_regression.py +0 -0
  247. {context_compiler-0.7.1 → context_compiler-0.7.2}/tests/test_transcript_replay.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: context-compiler
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: Deterministic conversational state engine for LLM applications.
5
5
  Project-URL: Homepage, https://github.com/rlippmann/context-compiler
6
6
  Project-URL: Repository, https://github.com/rlippmann/context-compiler
@@ -18,7 +18,7 @@ These examples support both sync (`0.8.x`) and async (`0.9.x`) user lookup.
18
18
  The minimal pipe path below is the easiest first-run flow and was runtime-validated in Docker via API flow with a real backend model.
19
19
 
20
20
  1. Import `open_webui_pipe.py` (recommended/default) as a Function by URL.
21
- 2. Open WebUI installs `context-compiler>=0.7.0` from the function frontmatter requirements.
21
+ 2. Open WebUI installs `context-compiler>=0.7.2` from the function frontmatter requirements.
22
22
  3. Enable the function.
23
23
  4. Set `BASE_MODEL_ID` to a valid Open WebUI model id (required).
24
24
  5. Select the pipe model in chat.
@@ -27,7 +27,7 @@ Open WebUI is a separate runtime and must already be installed/configured separa
27
27
  Open WebUI also needs at least one real backend model/provider configured (for example Ollama or OpenAI) so `BASE_MODEL_ID` resolves to an actual model.
28
28
  Note: The `PROVIDER` environment contract used in LiteLLM examples/demos does not apply to OpenWebUI. OpenWebUI manages providers via its own connection settings and model IDs.
29
29
 
30
- Checkpoint continuation in these examples requires `context-compiler>=0.7.0`.
30
+ Checkpoint continuation in these examples requires `context-compiler>=0.7.2`.
31
31
 
32
32
  ### Model configuration
33
33
 
@@ -64,8 +64,8 @@ If frontmatter dependency installs are disabled, offline, or unavailable:
64
64
  1. Open a shell in the Open WebUI container:
65
65
  - `docker exec -it <openwebui-container> sh`
66
66
  2. Install the package manually:
67
- - Minimal pipe: `pip install "context-compiler>=0.7.0"`
68
- - Preprocessor pipe: `pip install "context-compiler[experimental]>=0.7.0"`
67
+ - Minimal pipe: `pip install "context-compiler>=0.7.2"`
68
+ - Preprocessor pipe: `pip install "context-compiler[experimental]>=0.7.2"`
69
69
  3. Import and enable the function in Open WebUI, then configure valves.
70
70
 
71
71
  ### Finding valid model ids
@@ -3,8 +3,8 @@ title: Context Compiler Pipe
3
3
  author: rlippmann
4
4
  author_url: https://github.com/rlippmann/context-compiler
5
5
  funding_url: https://github.com/rlippmann/context-compiler
6
- version: 0.9.0
7
- requirements: context-compiler>=0.7.0
6
+ version: 0.9.1
7
+ requirements: context-compiler>=0.7.2
8
8
 
9
9
  Minimal Open WebUI Pipe integration for Context Compiler.
10
10
 
@@ -193,6 +193,12 @@ def _normalize_state(value: object) -> State:
193
193
  return {"premise": None, "policies": {}, "version": 2}
194
194
 
195
195
 
196
+ def _has_non_empty_authoritative_state(state: State) -> bool:
197
+ if get_premise_value(state) is not None:
198
+ return True
199
+ return bool(get_policy_items(state, "use") or get_policy_items(state, "prohibit"))
200
+
201
+
196
202
  def _build_compact_trace_text(
197
203
  *,
198
204
  decision: object,
@@ -229,6 +235,25 @@ def _strip_trace_blocks_from_messages(messages: list[dict[str, Any]]) -> list[di
229
235
  return cleaned
230
236
 
231
237
 
238
+ def _build_forward_messages(
239
+ raw_messages: object,
240
+ *,
241
+ state: State | None = None,
242
+ ) -> list[dict[str, Any]]:
243
+ """Build forwarded messages with trace stripping and optional state injection."""
244
+ messages = (
245
+ _strip_trace_blocks_from_messages([msg for msg in raw_messages if isinstance(msg, dict)])
246
+ if isinstance(raw_messages, list)
247
+ else []
248
+ )
249
+ if state is not None and _has_non_empty_authoritative_state(state):
250
+ return _replace_compiler_system_message(
251
+ messages,
252
+ _render_compiler_state_block(state),
253
+ )
254
+ return messages
255
+
256
+
232
257
  def _strip_existing_trace_from_chunk(chunk: object) -> object:
233
258
  if isinstance(chunk, str):
234
259
  return _strip_trace_block_from_text(chunk)
@@ -475,15 +500,13 @@ class Pipe:
475
500
  body: dict[str, Any],
476
501
  user_payload: dict[str, Any],
477
502
  request: Request,
503
+ *,
504
+ state: State | None = None,
478
505
  ) -> Any:
479
- """Forward with a shallow body copy and model override only."""
506
+ """Forward with model override and optional compiler-owned state injection."""
480
507
  payload = {**body}
481
508
  payload["model"] = self.valves.BASE_MODEL_ID
482
- raw_messages = body.get("messages")
483
- if isinstance(raw_messages, list):
484
- payload["messages"] = _strip_trace_blocks_from_messages(
485
- [msg for msg in raw_messages if isinstance(msg, dict)]
486
- )
509
+ payload["messages"] = _build_forward_messages(body.get("messages"), state=state)
487
510
  user = Users.get_user_by_id(user_payload["id"])
488
511
  if inspect.isawaitable(user):
489
512
  user = await user
@@ -514,18 +537,7 @@ class Pipe:
514
537
  payload = {**body}
515
538
  payload["model"] = self.valves.BASE_MODEL_ID
516
539
 
517
- raw_messages = body.get("messages")
518
- messages = (
519
- _strip_trace_blocks_from_messages(
520
- [msg for msg in raw_messages if isinstance(msg, dict)]
521
- )
522
- if isinstance(raw_messages, list)
523
- else []
524
- )
525
- payload["messages"] = _replace_compiler_system_message(
526
- messages,
527
- _render_compiler_state_block(state),
528
- )
540
+ payload["messages"] = _build_forward_messages(body.get("messages"), state=state)
529
541
 
530
542
  user = Users.get_user_by_id(user_payload["id"])
531
543
  if inspect.isawaitable(user):
@@ -625,7 +637,11 @@ class Pipe:
625
637
  llm_called=False,
626
638
  )
627
639
  if kind == DECISION_PASSTHROUGH:
628
- response = await self._forward_passthrough(body, __user__, __request__)
640
+ compiled_state = _normalize_state(state_after)
641
+ state_injected = "yes" if _has_non_empty_authoritative_state(compiled_state) else "no"
642
+ response = await self._forward_passthrough(
643
+ body, __user__, __request__, state=compiled_state
644
+ )
629
645
  return self._with_trace(
630
646
  response,
631
647
  original_input=latest_user_text,
@@ -634,6 +650,7 @@ class Pipe:
634
650
  state_before=state_before,
635
651
  state_after=state_after,
636
652
  llm_called=True,
653
+ state_injected=state_injected,
637
654
  )
638
655
  if kind == DECISION_UPDATE:
639
656
  _CHECKPOINTS_BY_CHAT_KEY[chat_key] = engine.export_checkpoint_json()
@@ -647,7 +664,11 @@ class Pipe:
647
664
  llm_called=False,
648
665
  )
649
666
 
650
- response = await self._forward_passthrough(body, __user__, __request__)
667
+ compiled_state = _normalize_state(state_after)
668
+ state_injected = "yes" if _has_non_empty_authoritative_state(compiled_state) else "no"
669
+ response = await self._forward_passthrough(
670
+ body, __user__, __request__, state=compiled_state
671
+ )
651
672
  return self._with_trace(
652
673
  response,
653
674
  original_input=latest_user_text,
@@ -656,4 +677,5 @@ class Pipe:
656
677
  state_before=state_before,
657
678
  state_after=state_after,
658
679
  llm_called=True,
680
+ state_injected=state_injected,
659
681
  )
@@ -1,10 +1,10 @@
1
1
  """
2
- title: Context Compiler Preprocessor Pipe
2
+ title: Context Compiler Pipe (Preprocessor)
3
3
  author: rlippmann
4
4
  author_url: https://github.com/rlippmann/context-compiler
5
5
  funding_url: https://github.com/rlippmann/context-compiler
6
- version: 0.9.0
7
- requirements: context-compiler[experimental]>=0.7.0
6
+ version: 0.9.1
7
+ requirements: context-compiler[experimental]>=0.7.2
8
8
 
9
9
  Open WebUI integration with Context Compiler preprocessor.
10
10
 
@@ -198,6 +198,12 @@ def _normalize_state(value: object) -> State:
198
198
  return {"premise": None, "policies": {}, "version": 2}
199
199
 
200
200
 
201
+ def _has_non_empty_authoritative_state(state: State) -> bool:
202
+ if get_premise_value(state) is not None:
203
+ return True
204
+ return bool(get_policy_items(state, "use") or get_policy_items(state, "prohibit"))
205
+
206
+
201
207
  def _build_compact_trace_text(
202
208
  *,
203
209
  decision: object,
@@ -234,6 +240,25 @@ def _strip_trace_blocks_from_messages(messages: list[dict[str, Any]]) -> list[di
234
240
  return cleaned
235
241
 
236
242
 
243
+ def _build_forward_messages(
244
+ raw_messages: object,
245
+ *,
246
+ state: State | None = None,
247
+ ) -> list[dict[str, Any]]:
248
+ """Build forwarded messages with trace stripping and optional state injection."""
249
+ messages = (
250
+ _strip_trace_blocks_from_messages([msg for msg in raw_messages if isinstance(msg, dict)])
251
+ if isinstance(raw_messages, list)
252
+ else []
253
+ )
254
+ if state is not None and _has_non_empty_authoritative_state(state):
255
+ return _replace_compiler_system_message(
256
+ messages,
257
+ _render_compiler_state_block(state),
258
+ )
259
+ return messages
260
+
261
+
237
262
  def _strip_existing_trace_from_chunk(chunk: object) -> object:
238
263
  if isinstance(chunk, str):
239
264
  return _strip_trace_block_from_text(chunk)
@@ -693,6 +718,7 @@ class Pipe:
693
718
  request: Request,
694
719
  *,
695
720
  base_model_id: str | None,
721
+ state: State | None = None,
696
722
  ) -> Any:
697
723
  if base_model_id is None:
698
724
  if self._allow_missing_base_model_for_debug():
@@ -706,11 +732,7 @@ class Pipe:
706
732
  )
707
733
  payload = {**body}
708
734
  payload["model"] = base_model_id
709
- raw_messages = body.get("messages")
710
- if isinstance(raw_messages, list):
711
- payload["messages"] = _strip_trace_blocks_from_messages(
712
- [msg for msg in raw_messages if isinstance(msg, dict)]
713
- )
735
+ payload["messages"] = _build_forward_messages(body.get("messages"), state=state)
714
736
  user = Users.get_user_by_id(user_payload["id"])
715
737
  if inspect.isawaitable(user):
716
738
  user = await user
@@ -748,18 +770,7 @@ class Pipe:
748
770
  payload = {**body}
749
771
  payload["model"] = base_model_id
750
772
 
751
- raw_messages = body.get("messages")
752
- messages = (
753
- _strip_trace_blocks_from_messages(
754
- [msg for msg in raw_messages if isinstance(msg, dict)]
755
- )
756
- if isinstance(raw_messages, list)
757
- else []
758
- )
759
- payload["messages"] = _replace_compiler_system_message(
760
- messages,
761
- _render_compiler_state_block(state),
762
- )
773
+ payload["messages"] = _build_forward_messages(body.get("messages"), state=state)
763
774
 
764
775
  user = Users.get_user_by_id(user_payload["id"])
765
776
  if inspect.isawaitable(user):
@@ -906,11 +917,14 @@ class Pipe:
906
917
  llm_called=False,
907
918
  )
908
919
  if kind == DECISION_PASSTHROUGH:
920
+ compiled_state = _normalize_state(state_after)
921
+ state_injected = "yes" if _has_non_empty_authoritative_state(compiled_state) else "no"
909
922
  response = await self._forward_passthrough(
910
923
  body,
911
924
  __user__,
912
925
  __request__,
913
926
  base_model_id=base_model_id,
927
+ state=compiled_state,
914
928
  )
915
929
  return self._with_trace(
916
930
  response,
@@ -921,6 +935,7 @@ class Pipe:
921
935
  state_after=state_after,
922
936
  preprocessor_output=preprocessd,
923
937
  llm_called=base_model_id is not None,
938
+ state_injected=state_injected,
924
939
  )
925
940
  if kind == DECISION_UPDATE:
926
941
  _CHECKPOINTS_BY_CHAT_KEY[chat_key] = engine.export_checkpoint_json()
@@ -935,11 +950,14 @@ class Pipe:
935
950
  llm_called=False,
936
951
  )
937
952
 
953
+ compiled_state = _normalize_state(state_after)
954
+ state_injected = "yes" if _has_non_empty_authoritative_state(compiled_state) else "no"
938
955
  response = await self._forward_passthrough(
939
956
  body,
940
957
  __user__,
941
958
  __request__,
942
959
  base_model_id=base_model_id,
960
+ state=compiled_state,
943
961
  )
944
962
  return self._with_trace(
945
963
  response,
@@ -950,4 +968,5 @@ class Pipe:
950
968
  state_after=state_after,
951
969
  preprocessor_output=preprocessd,
952
970
  llm_called=base_model_id is not None,
971
+ state_injected=state_injected,
953
972
  )
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "context-compiler"
7
- version = "0.7.1"
7
+ version = "0.7.2"
8
8
  description = "Deterministic conversational state engine for LLM applications."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -162,7 +162,7 @@ def build_compact_trace_text(
162
162
 
163
163
  lines.append(f"active state: {_active_state_summary(state_after)}")
164
164
  lines.append(f"downstream LLM call: {'yes' if llm_called else 'no'}")
165
- lines.append("state injected: no")
165
+ lines.append(f"state injected: {state_injected}")
166
166
  return "\n".join(lines)
167
167
 
168
168
 
@@ -615,6 +615,56 @@ def test_pipe_near_miss_directives_return_deterministic_clarify_without_downstre
615
615
  assert downstream_calls == 0
616
616
 
617
617
 
618
+ def test_pipe_incomplete_replacement_clarify_has_no_model_call(monkeypatch) -> None:
619
+ module = _load_module_with_openwebui_stubs(
620
+ "owui_pipe_incomplete_replacement_clarify", monkeypatch
621
+ )
622
+ module._ENGINES_BY_CHAT_KEY.clear()
623
+ module._CHECKPOINTS_BY_CHAT_KEY.clear()
624
+
625
+ downstream_calls = 0
626
+
627
+ async def _track_downstream(
628
+ _: object, payload: dict[str, object], __: object
629
+ ) -> dict[str, object]:
630
+ nonlocal downstream_calls
631
+ downstream_calls += 1
632
+ raise AssertionError(f"downstream model should not be called: {payload.get('model')}")
633
+
634
+ module.generate_chat_completion = _track_downstream
635
+
636
+ pipe = module.Pipe()
637
+ pipe.valves.BASE_MODEL_ID = "base-model"
638
+ chat_id = "chat-incomplete-replacement-clarify"
639
+
640
+ result = asyncio.run(
641
+ pipe.pipe(
642
+ {
643
+ "model": "pipe-model",
644
+ "messages": [{"role": "user", "content": "use docker instead of"}],
645
+ },
646
+ __user__={"id": "u1"},
647
+ __request__=object(),
648
+ __chat_id__=chat_id,
649
+ )
650
+ )
651
+ assert result == (
652
+ "Replacement requires both new and old items.\n"
653
+ "Use 'use <new item> instead of <old item>' with non-empty values."
654
+ )
655
+ assert downstream_calls == 0
656
+
657
+ state_view = asyncio.run(
658
+ pipe.pipe(
659
+ {"model": "pipe-model", "messages": [{"role": "user", "content": "show state"}]},
660
+ __user__={"id": "u1"},
661
+ __request__=object(),
662
+ __chat_id__=chat_id,
663
+ )
664
+ )
665
+ assert state_view == "Premise: none\nUse: none\nProhibit: none\nPending clarification: no"
666
+
667
+
618
668
  @pytest.mark.parametrize(
619
669
  ("confirmation", "expected_policies"),
620
670
  [
@@ -1186,3 +1236,159 @@ def test_pipe_clear_state_strips_preexisting_contradictory_trace_from_model_outp
1186
1236
  assert second_content.count("Context Compiler trace") == 1
1187
1237
  assert "downstream LLM call: no" in second_content
1188
1238
  assert "downstream LLM call: yes" not in second_content
1239
+
1240
+
1241
+ def test_pipe_passthrough_injects_active_state_and_trace_reports_yes(monkeypatch) -> None:
1242
+ module = _load_module_with_openwebui_stubs("owui_pipe_passthrough_state_injection", monkeypatch)
1243
+ module._ENGINES_BY_CHAT_KEY.clear()
1244
+ module._CHECKPOINTS_BY_CHAT_KEY.clear()
1245
+
1246
+ forwarded_payloads: list[dict[str, object]] = []
1247
+
1248
+ async def _chat_completion(
1249
+ _: object, payload: dict[str, object], __: object
1250
+ ) -> dict[str, object]:
1251
+ forwarded_payloads.append(payload)
1252
+ return {"choices": [{"message": {"content": "answer"}}]}
1253
+
1254
+ monkeypatch.setattr(module, "generate_chat_completion", _chat_completion)
1255
+
1256
+ pipe = module.Pipe()
1257
+ pipe.valves.BASE_MODEL_ID = "base-model"
1258
+ pipe.valves.SHOW_CONTEXT_COMPILER_TRACE = True
1259
+ chat_id = "chat-passthrough-injected-state"
1260
+
1261
+ update = asyncio.run(
1262
+ pipe.pipe(
1263
+ {"model": "pipe-model", "messages": [{"role": "user", "content": "use docker"}]},
1264
+ __user__={"id": "u1"},
1265
+ __request__=object(),
1266
+ __chat_id__=chat_id,
1267
+ )
1268
+ )
1269
+ assert "State updated: Use docker." in update
1270
+ assert len(forwarded_payloads) == 0
1271
+
1272
+ passthrough = asyncio.run(
1273
+ pipe.pipe(
1274
+ {
1275
+ "model": "pipe-model",
1276
+ "messages": [{"role": "user", "content": "what container runtime should i use?"}],
1277
+ },
1278
+ __user__={"id": "u1"},
1279
+ __request__=object(),
1280
+ __chat_id__=chat_id,
1281
+ )
1282
+ )
1283
+ assert isinstance(passthrough, dict)
1284
+ assert len(forwarded_payloads) == 1
1285
+ messages = forwarded_payloads[0]["messages"]
1286
+ assert isinstance(messages, list)
1287
+ assert any(
1288
+ isinstance(msg, dict)
1289
+ and msg.get("role") == "system"
1290
+ and isinstance(msg.get("content"), str)
1291
+ and msg["content"].startswith("[[cc_state]]")
1292
+ and "Use: docker" in msg["content"]
1293
+ for msg in messages
1294
+ )
1295
+ content = passthrough["choices"][0]["message"]["content"]
1296
+ assert "state injected: yes" in content
1297
+
1298
+
1299
+ def test_pipe_empty_state_passthrough_does_not_inject_and_trace_reports_no(monkeypatch) -> None:
1300
+ module = _load_module_with_openwebui_stubs("owui_pipe_empty_state_passthrough", monkeypatch)
1301
+ module._ENGINES_BY_CHAT_KEY.clear()
1302
+ module._CHECKPOINTS_BY_CHAT_KEY.clear()
1303
+
1304
+ forwarded_payloads: list[dict[str, object]] = []
1305
+
1306
+ async def _chat_completion(
1307
+ _: object, payload: dict[str, object], __: object
1308
+ ) -> dict[str, object]:
1309
+ forwarded_payloads.append(payload)
1310
+ return {"choices": [{"message": {"content": "answer"}}]}
1311
+
1312
+ monkeypatch.setattr(module, "generate_chat_completion", _chat_completion)
1313
+
1314
+ pipe = module.Pipe()
1315
+ pipe.valves.BASE_MODEL_ID = "base-model"
1316
+ pipe.valves.SHOW_CONTEXT_COMPILER_TRACE = True
1317
+
1318
+ passthrough = asyncio.run(
1319
+ pipe.pipe(
1320
+ {"model": "pipe-model", "messages": [{"role": "user", "content": "hello"}]},
1321
+ __user__={"id": "u1"},
1322
+ __request__=object(),
1323
+ __chat_id__="chat-empty-state-passthrough",
1324
+ )
1325
+ )
1326
+ assert isinstance(passthrough, dict)
1327
+ assert len(forwarded_payloads) == 1
1328
+ messages = forwarded_payloads[0]["messages"]
1329
+ assert isinstance(messages, list)
1330
+ assert not any(
1331
+ isinstance(msg, dict)
1332
+ and msg.get("role") == "system"
1333
+ and isinstance(msg.get("content"), str)
1334
+ and msg["content"].startswith("[[cc_state]]")
1335
+ for msg in messages
1336
+ )
1337
+ content = passthrough["choices"][0]["message"]["content"]
1338
+ assert "downstream LLM call: yes" in content
1339
+ assert "state injected: no" in content
1340
+
1341
+
1342
+ def test_pipe_repeated_passthrough_does_not_duplicate_compiler_state_prompt(monkeypatch) -> None:
1343
+ module = _load_module_with_openwebui_stubs("owui_pipe_repeated_passthrough_no_dup", monkeypatch)
1344
+ module._ENGINES_BY_CHAT_KEY.clear()
1345
+ module._CHECKPOINTS_BY_CHAT_KEY.clear()
1346
+
1347
+ forwarded_payloads: list[dict[str, object]] = []
1348
+
1349
+ async def _chat_completion(
1350
+ _: object, payload: dict[str, object], __: object
1351
+ ) -> dict[str, object]:
1352
+ forwarded_payloads.append(payload)
1353
+ return {"choices": [{"message": {"content": "answer"}}]}
1354
+
1355
+ monkeypatch.setattr(module, "generate_chat_completion", _chat_completion)
1356
+
1357
+ pipe = module.Pipe()
1358
+ pipe.valves.BASE_MODEL_ID = "base-model"
1359
+ chat_id = "chat-repeated-passthrough-no-dup"
1360
+
1361
+ _ = asyncio.run(
1362
+ pipe.pipe(
1363
+ {"model": "pipe-model", "messages": [{"role": "user", "content": "use docker"}]},
1364
+ __user__={"id": "u1"},
1365
+ __request__=object(),
1366
+ __chat_id__=chat_id,
1367
+ )
1368
+ )
1369
+ for idx in range(2):
1370
+ _ = asyncio.run(
1371
+ pipe.pipe(
1372
+ {
1373
+ "model": "pipe-model",
1374
+ "messages": [{"role": "user", "content": f"question {idx}"}],
1375
+ },
1376
+ __user__={"id": "u1"},
1377
+ __request__=object(),
1378
+ __chat_id__=chat_id,
1379
+ )
1380
+ )
1381
+
1382
+ assert len(forwarded_payloads) == 2
1383
+ for payload in forwarded_payloads:
1384
+ messages = payload.get("messages")
1385
+ assert isinstance(messages, list)
1386
+ cc_messages = [
1387
+ msg
1388
+ for msg in messages
1389
+ if isinstance(msg, dict)
1390
+ and msg.get("role") == "system"
1391
+ and isinstance(msg.get("content"), str)
1392
+ and msg["content"].startswith("[[cc_state]]")
1393
+ ]
1394
+ assert len(cc_messages) == 1