Metadata-Version: 2.4
Name: krynix
Version: 0.1.0
Summary: Python SDK for Krynix — trust spine for agentic AI systems
Project-URL: Homepage, https://github.com/PROJECT-OBA/krynix-sdk-python
Project-URL: Repository, https://github.com/PROJECT-OBA/krynix-sdk-python
Project-URL: Issues, https://github.com/PROJECT-OBA/krynix-sdk-python/issues
Project-URL: Documentation, https://github.com/PROJECT-OBA/krynix-sdk-python#readme
Author: PROJECT-OBA
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Requires-Dist: typing-extensions>=4.5.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2.0; extra == 'langchain'
Description-Content-Type: text/markdown

# krynix

[![PyPI version](https://img.shields.io/pypi/v/krynix)](https://pypi.org/project/krynix/)
[![Python](https://img.shields.io/badge/python-%3E%3D3.9-blue)](https://pypi.org/project/krynix/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)

Python SDK for [Krynix](https://github.com/PROJECT-OBA/krynix) — the trust and observability spine for agentic AI systems.

Record tamper-evident trace events from your Python agents with hash chain integrity, policy evaluation support, and cross-language compatibility with the Krynix TypeScript CLI.

## Installation

```bash
pip install krynix
```

With LangChain integration:

```bash
pip install "krynix[langchain]"
```

## Requirements

- Python >= 3.9

---

## Quickstart

```python
from krynix import KrynixTracer

tracer = KrynixTracer(agent_id="my-agent", output_dir="./traces")

with tracer.session() as session:
    session.tool_call("web_search", arguments={"query": "krynix trust spine"})
    session.tool_result("web_search", output={"results": ["..."]}, duration_ms=230)

    session.llm_request(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Summarize the results."}],
        parameters={"max_tokens": 512},
    )
    session.llm_response(
        model="gpt-4o",
        content="Here are the results...",
        usage={"prompt_tokens": 150, "completion_tokens": 80},
    )
```

This writes a `.trace.jsonl` file to `./traces/` with SHA-256 hash chain integrity computed inline. Each event is linked to the previous via `prev_hash`, forming a tamper-evident chain.

Validate the trace with the Krynix CLI:

```bash
npx @krynix/cli replay --verify --trace ./traces/<session-id>.trace.jsonl
```

---

## How It Works

Every trace event is written as a single JSON line with:

- **Canonical JSON** serialization (sorted keys, deterministic output)
- **SHA-256 hash chain** linking each event to its predecessor
- **Schema version** for forward compatibility
- **Sequence numbers** for ordering guarantees

The output is a standard JSONL file that can be validated by the [Krynix CLI](https://github.com/PROJECT-OBA/krynix), evaluated against policies, or diffed for behavioral drift detection.

---

## Session API

The session methods are **programmatic calls made by your agent code** during execution — not manual data entry. Your agent already has access to tool names, arguments, LLM responses, and reasoning steps as part of its execution loop. The session methods capture these values as structured trace events.

For **LangChain agents**, use the [callback handler](#langchain-integration) instead — it captures all events automatically with zero per-event code.

For **custom agents**, wire the session methods into your agent's execution loop:

```python
# Example: inside your custom agent's run loop
with tracer.session() as session:
    # Your agent decides to call a tool — log it
    session.tool_call(name="web_search", arguments={"query": user_query})

    # Your agent executes the tool — log the result
    result = my_tool_runner.execute("web_search", {"query": user_query})
    session.tool_result(name="web_search", output=result, duration_ms=230)

    # Your agent sends a request to the LLM — log it
    session.llm_request(model="gpt-4o", messages=messages, parameters=params)

    # Your agent receives the LLM response — log it
    response = llm_client.chat(messages, **params)
    session.llm_response(
        model="gpt-4o",
        content=response.content,
        usage={"prompt_tokens": response.usage.input, "completion_tokens": response.usage.output},
    )
```

### Event Methods

| Method | Required Fields | Optional Fields |
|--------|----------------|-----------------|
| `session.tool_call(...)` | `name`, `arguments` | `approval_status`, `approved_by`, `approval_reason`, `parent_id`, `metadata`, `redacted` |
| `session.tool_result(...)` | `name`, `output`, `duration_ms` | `exit_code`, `parent_id`, `metadata`, `redacted` |
| `session.llm_request(...)` | `model`, `messages`, `parameters` | `parent_id`, `metadata`, `redacted` |
| `session.llm_response(...)` | `model`, `content`, `usage` | `finish_reason`, `is_streaming`, `parent_id`, `metadata`, `redacted` |
| `session.decision(...)` | `action`, `reasoning` | `confidence`, `parent_id`, `metadata`, `redacted` |
| `session.observation(...)` | `source`, `content` | `parent_id`, `metadata`, `redacted` |
| `session.error(...)` | `code`, `message`, `recoverable` | `parent_id`, `metadata`, `redacted` |

---

## Trace Verification

Read and validate traces programmatically in Python:

```python
from krynix import read_trace, verify_trace

# Verify hash chain integrity
result = verify_trace("./traces/my-session.trace.jsonl")
assert result["valid"] is True

# Read events for inspection
events = read_trace("./traces/my-session.trace.jsonl")
for event in events:
    print(event["event_type"], event["payload"])
```

Or use the Krynix CLI for policy evaluation:

```bash
# Verify hash chain integrity
npx @krynix/cli replay --verify --trace ./traces/my-session.trace.jsonl

# Evaluate against a policy
npx @krynix/cli evaluate ./traces/my-session.trace.jsonl --policy no-shell-exec.policy.yaml

# Compare two traces for behavioral drift
npx @krynix/cli diff ./traces/baseline.trace.jsonl ./traces/candidate.trace.jsonl
```

---

## Redacted Events

Pass `redacted=True` to any event method to strip the payload before it is written. The data never leaves the process.

```python
session.llm_request(
    model="gpt-4o",
    messages=[...],   # contains sensitive PII
    parameters={},
    redacted=True,    # payload written as {} — data stays local
)
```

The event is recorded with `redacted: true` and an empty payload.

---

## LangChain Integration

```python
from krynix import KrynixTracer
from krynix.integrations.langchain import KrynixCallbackHandler

tracer = KrynixTracer(agent_id="my-langchain-agent", output_dir="./traces")

with tracer.session() as session:
    handler = KrynixCallbackHandler(session)
    chain.invoke({"input": "..."}, config={"callbacks": [handler]})
```

Callback mapping:

| LangChain callback | Krynix event |
|--------------------|--------------|
| `on_llm_start` | `llm_request` |
| `on_llm_end` | `llm_response` |
| `on_llm_error` | `error` |
| `on_tool_start` | `tool_call` |
| `on_tool_end` | `tool_result` |
| `on_tool_error` | `error` |
| `on_agent_action` | `decision` |
| `on_chain_start` | `observation` |

---

## Configuration

```python
tracer = KrynixTracer(
    agent_id="my-agent",
    output_dir="./traces",      # directory for trace files (default: ./traces)
    replay_seed=42,             # optional: deterministic event IDs for replay
)
```

---

## Event Types Reference

| Method | `event_type` | Description |
|--------|--------------|-------------|
| `session.tool_call(...)` | `tool_call` | Agent invokes a tool |
| `session.tool_result(...)` | `tool_result` | Tool returns a result |
| `session.llm_request(...)` | `llm_request` | Request sent to an LLM |
| `session.llm_response(...)` | `llm_response` | Response received from an LLM |
| `session.decision(...)` | `decision` | Agent internal reasoning/decision |
| `session.observation(...)` | `observation` | Data observed from the environment |
| `session.error(...)` | `error` | Error encountered during execution |
| *(automatic)* | `lifecycle` | Session start/end, managed by tracer |

---

## Cross-Language Compatibility

Traces generated by this Python SDK are fully compatible with the [Krynix TypeScript CLI](https://github.com/PROJECT-OBA/krynix). The canonical JSON serialization and SHA-256 hash chain algorithms produce byte-identical output across both implementations, verified via shared golden trace fixtures.

---

## Development

```bash
# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
PYTHONPATH=src pytest

# Type checking
mypy src/

# Linting
ruff check src/ tests/

# Full CI gate (run before every commit)
ruff check src/ tests/ && ruff format --check src/ tests/ && mypy src/ && PYTHONPATH=src pytest
```

---

## License

Apache 2.0
