SDK

policy

Policy Usage Guide

This document explains how to use the policy field in the Nora Observability SDK.
policy is metadata that assigns a policy identifier to a specific span so it can be included in the policy evaluation flow.


Key Summary

  • policy is an option that exists only in nora.trace(...).

  • The policy value is a string, and there are no enforced format rules.

  • Spans with a policy are sent together with the policy identifier when decisions are generated.

  • After decisions are created, the policy judgment endpoint evaluates them based on the decision IDs.


When Should You Use policy?

Use policy when:

  • A specific step requires policy compliance evaluation

  • You need to distinguish different policy types within the same workflow

  • You want to tag safety, security, or compliance evaluations separately

For example, steps such as content toxicity checks, PII masking, or risk scoring can be distinguished in the policy judgment flow by assigning a policy.


Understanding the Basic Flow

The policy is propagated through the following flow:

  1. A span created with nora.trace(...) is assigned a policy.

  2. When that span is included in the decision creation flow, the policy is added to the decision payload.

  3. After decisions are created, an evaluation request is sent to /decision/policy/judge.

This flow is handled automatically inside the SDK.
Users only need to set the policy value.


Basic Usage

1) Assigning a Policy with a Context Manager

import nora
from openai import OpenAI

nora_client = nora.init(api_key="your-nora-api-key")
client = OpenAI(api_key="your-openai-key")

with nora_client.trace_group(name="policy_flow"):
    with nora_client.trace(
        span_kind="policy_eval",
        name="content_policy_check",
        policy="content_safety:v1"
    ):
        response = client.responses.create(
            model="gpt-5",
            input=[
                {"role": "user", "content": "Check this content for safety risks."}
            ]
        )
        print(response.output_text)

2) Assigning a Policy with a Decorator

import nora

nora_client = nora.init(api_key="your-nora-api-key")

@nora_client.trace(
    span_kind="policy_eval",
    name="pii_scan",
    policy="privacy:pii_scan:v2"
)
def scan_pii(text: str) -> str:
    # In a real implementation, this would run detection logic or an LLM call.
    return "ok"

with nora_client.trace_group(name="privacy_flow"):
    result = scan_pii("My phone number is 010-1234-5678")
    print(result)

3) Traces Work Without a Policy

import nora

nora_client = nora.init(api_key="your-nora-api-key")

@nora_client.trace(span_kind="retrieval", name="fetch_context")
def fetch_context():
    return {"data": "..."}

with nora_client.trace_group(name="no_policy_flow"):
    fetch_context()

The policy field is optional.
Even without it, traces are recorded normally.


Policy Value Design Guidelines

The SDK does not restrict the format of the policy string, so it is recommended to define conventions that fit your team’s needs.

Recommended Pattern Examples

  • content_safety:v1

  • privacy:pii_scan:v2

  • compliance:gdpr:masking

  • risk:prompt_injection:v3

In production environments, including a version or policy domain makes regression tracking and comparisons easier.


Relationship with span_kind

policy becomes clearer when used together with span_kind.
Common combinations include:

  • span_kind="policy_eval" + policy="..."

  • span_kind="router" + policy="..."

  • span_kind="retrieval" + policy="..."

span_kind is the standard SDK API field.
span_type is not used.


Streaming and policy

Streaming responses must be handled inside a trace_group.
This also applies when using policy.

import nora
import ollama

nora_client = nora.init(api_key="your-nora-api-key")

with nora_client.trace_group(name="streaming_policy"):
    with nora_client.trace(
        span_kind="policy_eval",
        name="stream_policy_check",
        policy="content_safety:v1"
    ):
        stream = ollama.chat(
            model="llama2",
            messages=[{"role": "user", "content": "Stream and check policy"}],
            stream=True
        )

        for chunk in stream:
            content = chunk.get("message", {}).get("content", "")
            if content:
                print(content, end="", flush=True)

Frequently Asked Questions

Q1. Is policy attached automatically?

No.
policy is not inferred automatically. You must explicitly specify it in nora.trace(...).


Q2. How do I attach a policy to an LLM call?

Wrap the LLM call in a span created with nora.trace(...) and specify the policy there.
Automatically traced LLM calls alone do not receive a policy.


Q3. What happens if the policy value is empty?

If no value is specified, the policy is not sent.
No functional errors occur.


Q4. Which decisions are evaluated by policy judgment?

Decisions generated from spans that include a policy are evaluated.
After decision creation, the SDK requests policy judgment using the corresponding decision IDs.


Was this page helpful?