Skip to main content

Dymium SDK for secure data and AI interactions

Project description

Dymium SDK

Dymium is a security SDK for tool-using LLM apps. It keeps sensitive values placeholderized across the model loop and only allows controlled exposure at tool boundaries.

Detection happens in the sanitizer layer using your configured PII detector (for example Hugging Face, Comprehend, Google DLP, Azure, or GhostPII). Sensitive values detected in user input and tool output are replaced with placeholders and tracked in runtime context, so agents can still reason over requests and drive tool workflows that depend on sensitive fields without ever seeing the raw values.

Each tool declares how that protected context is handled. tool_type="direct" is for non-agentic execution boundaries (local handlers, DB/API calls, deterministic services) and requires input_mode: use resolve when the trusted tool must receive originals at execution time, or protect when placeholders must remain in tool args. tool_type="delegated" is for agentic handoffs (sub-agents or remote secured runtimes): Dymium forwards protected input plus runtime context so the downstream secured runtime can continue safely. Tool outputs are re-sanitized before returning to the model, and the app receives deobfuscated output with a security summary.

This repo includes a framework-agnostic Sanitization module, an SDK-owned SecureRuntime orchestration loop, and integrations for LangChain, LangGraph, and LlamaIndex.


Install

pip install dymium

Supported Detectors (PII)

  • HuggingFacePIIDetector (local Transformers; Dymium flagship model)
  • ComprehendDetector (AWS)
  • GoogleDLPDetector (Google Cloud)
  • AzurePIIDetector (Azure)
  • GhostPIIDetector (Dymium Detect)
    • Dymium Detect (cloud API)
    • Dymium Detector (local) via HuggingFacePIIDetector
  • Optional regex_rules (configured with the detector) to supplement the selected detector

Configure detectors via RuntimeConfig(pii="...", pii_config={...}) or by directly instantiating Sanitizer.


Sanitization Module (Fits all Frameworks)

from dymium.sanitization import Sanitizer, SanitizationContext
from dymium.detectors.pii import HuggingFacePIIDetector

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(model_id="dymium/Dymium-NER-v1"),
)
ctx = SanitizationContext()

safe_text = sanitizer.sanitize_text("Email me at alice@example.com", ctx)
resolved = sanitizer.resolve_for_tool({"email": "PH_EMAIL_ABCDE"}, ctx)
safe_output = sanitizer.sanitize_tool_output({"email": "alice@example.com"}, ctx)
final_text = sanitizer.deobfuscate("Email sent to PH_EMAIL_ABCDE", ctx)

Sanitizer uses the built-in RedactionEngine by default; pass a custom redaction engine only when you need custom placeholder behavior.


Tool Types

Every tool must declare tool_type.

direct tools are non-agentic tools (local handlers, DB queries, constrained APIs).
For direct tools, input_mode is required:

  • resolve: Dymium resolves placeholders to originals at execution time only.
    Use this for trusted tools that need original values to function (for example a customer lookup API). Common resolve cases while keeping the LLM blind to originals:
    • exact-match identity/account lookups (email, phone, account id),
    • order/shipment/ticket retrieval APIs keyed by customer contact fields,
    • parameterized DB queries keyed by sensitive identifiers (email, phone, account id).
  • protect: Dymium keeps placeholders in args.
    Use this for broader or less constrained tools where leaking original PII via input is unacceptable (for example web/search/send-style tools).

delegated tools are agentic handoffs (sub-agent in-process or remote secured agent).
Unlike direct, a delegated handoff crosses into another LLM/tool loop outside the parent loop, so resolving originals at the parent boundary would break the security plane. Dymium therefore keeps inputs protected and forwards runtime context so the receiving secured agent can continue safely and resolve originals only at its own direct tool boundary.

This behavior is supported in SecureRuntime, LangChain, LangGraph, and LlamaIndex.

  • SecureRuntime: set policy on each tool definition (tool_type, input_mode for direct).
  • Framework integrations: set policy on each tool object via tool.metadata["dymium"]["tool_type"] and tool.metadata["dymium"]["input_mode"] (required for direct tools).

For delegated tools, Dymium manages dymium_context internally with:

  • placeholder_map
  • runtime context metadata (__dymium_runtime_context, __dymium_runtime_context_id)

Child runtimes return updated placeholder_map and security_summary in response dymium_context; Dymium validates and merges those back into the parent flow.

Delegated handoffs use transport-managed delegation (delegated_transport / DelegatedTransport). Delegated context is runtime-managed by Dymium.

For SecureRuntime, remote delegated handoffs can be automatic by defining a local delegated tool with delegated_transport (no custom handler required). Dymium forwards runtime context, including placeholderMap.

To include remote agents in the same security plane, the remote target must also run Dymium security (for example another SecureRuntime instance, or a framework agent wrapped with Dymium middleware/sanitization). Transport alone is not sufficient if the remote runtime is not secured.

config = RuntimeConfig(
    model="openai:gpt-5",
    pii="dymium_hf",
    model_config={"api_key": "..."},
    pii_config={"model_id": "dymium/Dymium-NER-v1"},
    tools=[
        {
            "name": "run_specialist",
            "description": "Delegate to a remote Dymium SecureRuntime instance.",
            "parameters": {
                "type": "object",
                "properties": {"handoff_request": {"type": "string"}},
                "required": ["handoff_request"],
            },
            "tool_type": "delegated",
            "delegated_transport": {
                "kind": "http",
                "url": "http://specialist-agent:8080/invoke",
                "prompt_arg": "handoff_request",
                "timeout_s": 30,
            },
        }
    ],
)

For framework integrations, use DelegatedTransport inside delegated tools so remote handoffs are automatic without manual payload plumbing:

from dymium import DelegatedTransport

remote = DelegatedTransport(
    {"kind": "http", "url": "http://specialist-agent:8080/invoke"},
    name="run_specialist",
)

# Tool args stay business-only; context injection is runtime-owned.
run_specialist = remote.as_tool_handler()

LangChain Integration

from dymium.integrations.langchain import DymiumMiddleware
from dymium.sanitization import Sanitizer
from dymium.detectors.pii import HuggingFacePIIDetector
from langchain.agents import create_agent

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(model_id="dymium/Dymium-NER-v1"),
)

tools = [...]  # Define tool objects first.

for t in tools:
    meta = dict(getattr(t, "metadata", {}) or {})
    meta["dymium"] = {
        "tool_type": "direct",
        "input_mode": "resolve",
    }
    t.metadata = meta

middleware = DymiumMiddleware(sanitizer, tools=tools).middleware()

agent = create_agent(
    model="openai:gpt-5",
    tools=tools,
    middleware=[middleware],
)

result = agent.invoke({"messages": [{"role": "user", "content": "Find orders for alice@example.com"}]})
messages = result.get("messages", [])
last_text = messages[-1].get("content", "") if messages else result.get("text", "")
print(last_text)
print(result.get("security_summary"))

result["messages"] is app-visible and deobfuscated.


LangGraph Integration

from dymium.integrations.langgraph import (
    create_sanitized_agent,
    DymiumMessagesState,
)
from dymium.sanitization import Sanitizer
from dymium.detectors.pii import HuggingFacePIIDetector
from langchain.chat_models import init_chat_model

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(model_id="dymium/Dymium-NER-v1"),
)

model = init_chat_model("openai:gpt-5")
tools = [...]

for t in tools:
    meta = dict(getattr(t, "metadata", {}) or {})
    meta["dymium"] = {
        "tool_type": "direct",
        "input_mode": "resolve",
    }
    t.metadata = meta

app = create_sanitized_agent(
    model=model,
    tools=tools,
    sanitizer=sanitizer,
    state_schema=DymiumMessagesState,
    max_tool_calls=10,
)

result = app.invoke(
    {"messages": [{"role": "user", "content": "Find orders for alice@example.com"}]},
    {"recursion_limit": 12},
)
messages = result.get("messages", [])
last_text = messages[-1].get("content", "") if messages else ""
print(last_text)
print(result.get("security_summary"))

LlamaIndex Integration

import asyncio

from dymium.integrations.llamaindex import create_sanitized_agent_workflow
from dymium.sanitization import Sanitizer, SanitizationContext
from dymium.detectors.pii import HuggingFacePIIDetector
from llama_index.llms.openai import OpenAI as LlamaOpenAI

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(model_id="dymium/Dymium-NER-v1"),
)
ctx = SanitizationContext()

llm = LlamaOpenAI(model="gpt-5")

def lookup_customer(email: str) -> dict:
    return {"customer_id": "CUST-1001", "email": email}

lookup_customer.metadata = {
    "dymium": {
        "tool_type": "direct",
        "input_mode": "resolve",
    }
}

workflow = create_sanitized_agent_workflow(
    tools_or_functions=[lookup_customer],
    llm=llm,
    sanitizer=sanitizer,
    ctx=ctx,
)

async def _run():
    return await workflow.run(user_msg="Find customer details for alice@example.com")

result = asyncio.run(_run())
text = getattr(getattr(result, "response", None), "content", "") or str(result)
print(text)
print(ctx.security_summary)

The returned LlamaIndex response object is app-visible and deobfuscated.


LLM Providers (SecureRuntime)

  • openai
  • anthropic
  • gemini
  • ghostllm (Dymium LLM gateway)

Preferred config style: RuntimeConfig(model="provider:model", model_config={...}). Legacy style also works: RuntimeConfig(llm="provider", llm_config={"model": "...", ...}).

Framework integrations (LangChain/LangGraph/LlamaIndex) use the framework’s own LLM objects; Dymium supplies the sanitization boundary and tool wrapping.


SecureRuntime (SDK‑Owned Loop)

from dymium import RuntimeConfig, SecureRuntime

def lookup_customer(email: str) -> dict:
    "Lookup customer by email."
    return {"customer_id": "CUST-1001", "email": email}

config = RuntimeConfig(
    model="openai:gpt-5",
    pii="dymium_hf",
    model_config={"api_key": "..."},
    pii_config={
        "base_url": "https://pii.example.internal",
        "regex_rules": [{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    },
    tools=[
        {
            "name": "lookup_customer",
            "description": "Lookup customer by email.",
            "parameters": {
                "type": "object",
                "properties": {"email": {"type": "string"}},
                "required": ["email"],
            },
            "tool_type": "direct",
            "input_mode": "resolve",
            "handler": lookup_customer,
        }
    ],
)

# Optional: add MCP alongside local tools.
# config.mcp = {"base_url": "http://127.0.0.1:40623/mcp"}

runtime = SecureRuntime.from_config(config)

request = {
    "messages": [
        {"role": "user", "content": "Find orders for alice@example.com"}
    ],
    "recursion_limit": 5,
}

result = runtime.invoke(request)
messages = result.get("messages", [])
last_text = messages[-1].get("content", "") if messages else result.get("text", "")
print(last_text)
print(result.get("security_summary"))

result["messages"] is app-visible and deobfuscated.


Detector Configuration (Sanitization Module / Integrations)

Use detector instances directly in Sanitizer(...) for framework integrations and custom loops. For brevity, non-Hugging Face snippets reuse Sanitizer imports from the Hugging Face example.

Hugging Face (Dymium flagship)

from dymium.sanitization import Sanitizer
from dymium.detectors.pii import HuggingFacePIIDetector

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(
        model_id="dymium/Dymium-NER-v1",
        score_threshold=0.5,
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

GhostPII (Dymium Detect)

from dymium.detectors.pii import GhostPIIDetector

sanitizer = Sanitizer(
    pii=GhostPIIDetector(
        # either service root (SDK appends /v1/detect/pii)...
        base_url="https://spoofcorp.llm.dymium.home:3000",
        # ...or full endpoint URL ending in /v1/detect/pii
        api_key="...",  # required
        timeout_s=10,
        entity_types=["ID_REF", "EMAIL", "URL"],  # optional allow-list; defaults to all 13
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

AWS Comprehend

from dymium.detectors.pii import ComprehendDetector

sanitizer = Sanitizer(
    pii=ComprehendDetector(
        region="us-east-1",
        credentials={
            "access_key_id": "...",
            "secret_access_key": "...",
            "session_token": "...",  # optional
        },
        endpoint_url=None,  # optional custom endpoint
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

Google Cloud DLP

from dymium.detectors.pii import GoogleDLPDetector

sanitizer = Sanitizer(
    pii=GoogleDLPDetector(
        project_id="my-gcp-project",
        location_id="us",  # optional regional parent
        credentials={
            "token": "...",  # or api_key
            # "api_key": "...",
        },
        base_url="https://dlp.googleapis.com",
        timeout_s=10,
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

Azure PII

from dymium.detectors.pii import AzurePIIDetector

sanitizer = Sanitizer(
    pii=AzurePIIDetector(
        endpoint="https://my-language-resource.cognitiveservices.azure.com",
        api_key="...",  # or bearer_token
        bearer_token=None,  # optional
        api_version="2022-05-01",
        use_legacy_endpoint=False,
        timeout_s=10,
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

Hugging Face (local Transformers)

Install optional local inference dependencies:

pip install "dymium[hf]"

Use the built-in detector directly with Sanitizer:

from dymium.detectors.pii import HuggingFacePIIDetector
from dymium.sanitization import Sanitizer

sanitizer = Sanitizer(
    pii=HuggingFacePIIDetector(
        model_id="dymium/Dymium-NER-v1",
        score_threshold=0.5,
        # device=0,  # optional GPU index
        regex_rules=[{"pattern": "ORD-\\d+", "type": "ORDER_ID"}],
    ),
)

Use with SecureRuntime.from_config(...):

from dymium import RuntimeConfig, SecureRuntime

runtime = SecureRuntime.from_config(
    RuntimeConfig(
        model="openai:gpt-5",
        pii="huggingface",  # alias: "dymium_hf"
        model_config={"api_key": "..."},
        pii_config={
            "model_id": "dymium/Dymium-NER-v1",
            "score_threshold": 0.5,
        },
        tools=[...],
    )
)

Detection behavior should be configured when creating the detector instance (for example language defaults, provider filters, and thresholds).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dymium-0.1.4.tar.gz (67.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dymium-0.1.4-py3-none-any.whl (96.6 kB view details)

Uploaded Python 3

File details

Details for the file dymium-0.1.4.tar.gz.

File metadata

  • Download URL: dymium-0.1.4.tar.gz
  • Upload date:
  • Size: 67.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dymium-0.1.4.tar.gz
Algorithm Hash digest
SHA256 5e23d894cbd9e3025dbd20341a9966a08b0568daec495d06f155b61c6c9e8368
MD5 60d3d4a4b441e413cca75adfda03bbe0
BLAKE2b-256 256d219833c903a5d78e624aae185c9fa9782597e586fe19cefe3a24c79b6200

See more details on using hashes here.

Provenance

The following attestation bundles were made for dymium-0.1.4.tar.gz:

Publisher: release-python.yml on dymium-io/Dymium-SDK

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dymium-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: dymium-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 96.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dymium-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1fb93adb02ec0b551c694ed89fde3f23e1311ec36e8a37c3a31f3d410a09c7b2
MD5 1b9bc57b2190b1133a7a5efc1de1d549
BLAKE2b-256 2cde0a9ad43565a81ad4e17638347bed1f68ecd252140f8eea0a2737b481aaa2

See more details on using hashes here.

Provenance

The following attestation bundles were made for dymium-0.1.4-py3-none-any.whl:

Publisher: release-python.yml on dymium-io/Dymium-SDK

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page