.. _quickstart:

Quickstart
==========

This page walks you through creating your first event, signing an audit chain,
and exporting to OTLP — in under five minutes.

Installation
------------

.. code-block:: bash

   pip install llm-toolkit-schema

For optional features:

.. code-block:: bash

   pip install "llm-toolkit-schema[jsonschema]"   # JSON Schema validation
   pip install "llm-toolkit-schema[http]"         # Async OTLP/webhook export (httpx)
   pip install "llm-toolkit-schema[pydantic]"     # Pydantic v2 model layer

Python 3.9+ is required.

Creating your first event
--------------------------

Every interaction with an LLM tool is represented as an :class:`~llm_toolkit_schema.event.Event`.
The minimum required fields are ``event_type``, ``source``, and ``payload``:

.. code-block:: python

   from llm_toolkit_schema import Event, EventType

   event = Event(
       event_type=EventType.TRACE_SPAN_COMPLETED,
       source="my-tool@1.0.0",
       payload={"span_name": "run_agent", "status": "ok", "duration_ms": 312},
   )

   print(event.event_id)        # 01JPXXXXXXXXXXXXXXXXXXXXXXXX  (auto-generated ULID)
   print(event.schema_version)  # 1.0
   print(event.to_json())       # compact JSON

Full event with optional fields
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

   from llm_toolkit_schema import Event, EventType, Tags

   event = Event(
       event_type=EventType.TRACE_SPAN_COMPLETED,
       source="my-tool@1.0.0",
       payload={"span_name": "chat_completion", "status": "ok"},
       org_id="org_01HX",
       team_id="team_engineering",
       trace_id="a" * 32,        # 32-char hex OpenTelemetry trace ID
       span_id="b" * 16,         # 16-char hex span ID
       tags=Tags(env="production", model="gpt-4o"),
   )

Typed namespace payloads
-------------------------

Use the typed payload dataclasses from :mod:`llm_toolkit_schema.namespaces` to get
field validation and IDE auto-complete for each event namespace:

.. code-block:: python

   import dataclasses
   from llm_toolkit_schema import Event, EventType
   from llm_toolkit_schema.namespaces.trace import SpanCompletedPayload, TokenUsage, ModelInfo

   payload = SpanCompletedPayload(
       span_name="chat_completion",
       status="ok",
       duration_ms=250,
       token_usage=TokenUsage(prompt=120, completion=80, total=200),
       model_info=ModelInfo(name="gpt-4o", provider="openai"),
   )

   event = Event(
       event_type=EventType.TRACE_SPAN_COMPLETED,
       source="llm-trace@1.0.0",
       payload=dataclasses.asdict(payload),
   )

HMAC signing and audit chains
-------------------------------

Sign individual events or build a full tamper-evident chain:

.. code-block:: python

   from llm_toolkit_schema import Event, EventType
   from llm_toolkit_schema.signing import sign, verify, AuditStream

   # --- Single event ---
   event = Event(
       event_type=EventType.TRACE_SPAN_COMPLETED,
       source="my-tool@1.0.0",
       payload={"span_name": "chat"},
   )
   signed = sign(event, org_secret="my-secret")
   assert verify(signed, org_secret="my-secret")

   # --- Audit chain ---
   stream = AuditStream(org_secret="my-secret", source="my-tool@1.0.0")
   for i in range(5):
       evt = Event(
           event_type=EventType.TRACE_SPAN_COMPLETED,
           source="my-tool@1.0.0",
           payload={"index": i},
       )
       stream.append(evt)

   result = stream.verify()
   assert result.valid                   # cryptographically intact
   assert result.tampered_count == 0     # nothing altered
   assert result.gaps == []              # no deletions

PII redaction
--------------

Mark sensitive fields and apply a policy before storing or exporting events:

.. code-block:: python

   from llm_toolkit_schema import Event, EventType
   from llm_toolkit_schema.redact import Redactable, RedactionPolicy, Sensitivity

   policy = RedactionPolicy(min_sensitivity=Sensitivity.PII, redacted_by="policy:corp-v1")

   event = Event(
       event_type=EventType.PROMPT_SAVED,
       source="promptlock@1.0.0",
       payload={
           "prompt_text": Redactable("User email: alice@example.com", Sensitivity.PII, {"email"}),
           "model": "gpt-4o",
       },
   )
   result = policy.apply(event)
   # result.event.payload["prompt_text"] is now "[REDACTED]"

Exporting events
-----------------

.. code-block:: python

   import asyncio
   from llm_toolkit_schema import Event, EventType
   from llm_toolkit_schema.export.jsonl import JSONLExporter

   exporter = JSONLExporter("events.jsonl")
   events = [
       Event(event_type=EventType.TRACE_SPAN_COMPLETED, source="tool@1.0.0", payload={"i": i})
       for i in range(10)
   ]
   asyncio.run(exporter.export_batch(events))

See :doc:`user_guide/export` for OTLP and webhook export.

Next steps
----------

- :doc:`user_guide/index` — in-depth guide to all features
- :doc:`api/index` — full API reference
- :doc:`namespaces/index` — typed payload catalogue
- :doc:`cli` — ``llm-toolkit-schema check-compat`` command
