1"""Observability hook for PromptService.
2
3Apps plug in their own observer to capture render events for A/B testing,
4telemetry, or prompt auditing. The default observer is a no-op.
5"""
6
7from __future__ import annotations
8
9from typing import Any, Protocol, runtime_checkable
10
11from lexigram.ai.prompt.rendering.engine import RenderFormat
12
13
14@runtime_checkable
15class PromptObserverProtocol(Protocol):
16 """Called by :class:`~lexigram.ai.prompt.service.service.PromptService`
17 after every successful render.
18
19 Implement this protocol and pass your instance to
20 :class:`~lexigram.ai.prompt.service.service.PromptService` to receive
21 render events.
22
23 The call is synchronous and must not raise — any exception will be
24 swallowed by the service and logged as a warning.
25 """
26
27 def on_render(
28 self,
29 name: str,
30 version: str,
31 variables: dict[str, Any],
32 rendered_output: str,
33 render_format: RenderFormat,
34 ) -> None:
35 """Invoked after a template is successfully rendered.
36
37 Args:
38 name: Template name that was rendered.
39 version: Concrete version that was used.
40 variables: The resolved variable mapping (post-defaults).
41 rendered_output: The final rendered prompt string.
42 render_format: The format used to render the template.
43 """
44 ...
45
46
47class NoOpPromptObserver:
48 """Default no-op observer. Does nothing on every render event."""
49
50 def on_render(
51 self,
52 name: str,
53 version: str,
54 variables: dict[str, Any],
55 rendered_output: str,
56 render_format: RenderFormat,
57 ) -> None:
58 """No-op: discard all render events."""
59
60
61__all__ = ["NoOpPromptObserver", "PromptObserverProtocol"]