Skip to content

Commit 2060958

Browse files
committed
Document telemetry state serialization controls
1 parent 4f56119 commit 2060958

3 files changed

Lines changed: 108 additions & 0 deletions

File tree

docs/hooks.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ Hook callbacks receive one immutable event object. Useful fields include:
8383
* ``event.error``: original exception on failure hooks
8484
* ``event.reason``: cancellation reason on cancellation hooks
8585

86+
Hook event state payloads are Python-side lifecycle data, not serialized
87+
OpenTelemetry payloads. ``event.state`` is a detached copied state object for
88+
in-process inspection inside your callback. That means telemetry-oriented state
89+
serialization choices such as field exclusion or truncation do not
90+
automatically change what appears on ``event.state``.
91+
8692
For ``on_state_changed``, the executable identity fields describe the active
8793
workflow, subflow, node, or concurrent executable that actually triggered the
8894
state update. The parent executable fields describe the containing execution

docs/opentelemetry.rst

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,80 @@ below:
177177
Cancelled spans do not set ``error.type`` and are not marked with ``Error``
178178
status unless they actually fail.
179179

180+
State Serialization And Telemetry
181+
=================================
182+
183+
Junjo intentionally records rich workflow state in telemetry by default. This
184+
is a debugging-oriented design choice: many AI workflows need full prompts,
185+
tool inputs, tool outputs, and intermediate state to be visible in traces.
186+
187+
Workflow state telemetry is derived from your state model's normal Pydantic
188+
serialization:
189+
190+
- ``junjo.workflow.state.start`` and ``junjo.workflow.state.end`` use the
191+
serialized state JSON
192+
- ``junjo.state_json_patch`` is built from serialized before/after state dumps
193+
194+
This means your state model controls what appears in OpenTelemetry state
195+
payloads. If you want to exclude, redact, or truncate fields for telemetry,
196+
shape that behavior in your state model serialization.
197+
198+
This does **not** apply to ``junjo.workflow.execution_graph_snapshot``, which
199+
is generated from the compiled graph rather than from state serialization.
200+
201+
Controlling Telemetry State Payloads
202+
------------------------------------
203+
204+
If you need to keep a field in runtime state but remove it from serialized
205+
telemetry payloads, exclude it from Pydantic serialization:
206+
207+
.. code-block:: python
208+
209+
from pydantic import Field
210+
from junjo import BaseState
211+
212+
213+
class ChatWorkflowState(BaseState):
214+
user_message: str
215+
llm_response: str | None = None
216+
raw_api_key: str | None = Field(default=None, exclude=True)
217+
218+
In this example, ``raw_api_key`` remains available in runtime state, but it is
219+
omitted from serialized OpenTelemetry state snapshots and JSON patches.
220+
221+
If you want to keep a field but truncate or reshape it for telemetry, use a
222+
serializer on the state model:
223+
224+
.. code-block:: python
225+
226+
from pydantic import field_serializer
227+
from junjo import BaseState
228+
229+
230+
class PromptWorkflowState(BaseState):
231+
prompt: str
232+
final_answer: str | None = None
233+
234+
@field_serializer("prompt")
235+
def serialize_prompt_for_telemetry(self, value: str) -> str:
236+
if len(value) <= 2000:
237+
return value
238+
return value[:2000] + "...[truncated]"
239+
240+
In this example, runtime state still holds the full prompt, but Junjo's
241+
OpenTelemetry state fields and patches use the truncated serialized form.
242+
243+
Hook Events Use Copied State Objects
244+
------------------------------------
245+
246+
Hook event state payloads are separate from OpenTelemetry serialization.
247+
248+
- OpenTelemetry state fields use serialized state from your model
249+
- hook ``event.state`` values use a copied in-memory state object
250+
251+
So excluding or truncating a field for telemetry serialization does **not**
252+
automatically remove it from ``event.state`` inside hook callbacks.
253+
180254
Workflow/Subflow Span Attributes
181255
---------------------------------
182256

docs/state_management.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,34 @@ The `BaseState` class, which is a Pydantic `BaseModel`, is used to define the st
3434
3535
In this example, we've defined a state for a chat application. Any workflow that uses this state will have access to these fields, and Pydantic will ensure that the data conforms to the specified types.
3636

37+
Because Junjo uses your state's normal Pydantic serialization for workflow
38+
state telemetry, this is also the place where you control telemetry-facing
39+
serialization behavior. If a field should be excluded, redacted, or truncated
40+
for OpenTelemetry state payloads, implement that at the state model layer.
41+
42+
.. code-block:: python
43+
44+
from pydantic import Field, field_serializer
45+
from junjo import BaseState
46+
47+
48+
class AgentState(BaseState):
49+
prompt: str
50+
provider_api_key: str | None = Field(default=None, exclude=True)
51+
52+
@field_serializer("prompt")
53+
def serialize_prompt_for_telemetry(self, value: str) -> str:
54+
if len(value) <= 2000:
55+
return value
56+
return value[:2000] + "...[truncated]"
57+
58+
In this example:
59+
60+
- ``provider_api_key`` stays in runtime state but is omitted from serialized
61+
telemetry state
62+
- ``prompt`` stays complete in runtime state but is truncated in serialized
63+
telemetry state
64+
3765
BaseStore: Managing Your State
3866
==============================
3967

0 commit comments

Comments
 (0)