Adapter Authoring
Adapter Authoring is for teams integrating frameworks that VeriProof does not support out of the box. Instead of adding manual SDK calls throughout your application, you can hook into the framework lifecycle and let the adapter open, record, and close sessions automatically.
Use this when you want consistent instrumentation across a custom runtime, middleware layer, or internal AI platform without duplicating session logic in every service.
Adapter Authoring
VeriProof ships built-in adapters for popular frameworks across Python (LangGraph, CrewAI, OpenAI Agents SDK, LlamaIndex, Pydantic AI, Google ADK, Anthropic), TypeScript (Vercel AI SDK, LangChain.js, LlamaIndex.TS, Next.js, Express, Fastify, Anthropic, OpenAI Agents SDK), and .NET (Semantic Kernel, AutoGen). See the SDK overview for the full list. If your framework is not covered, you can author a custom adapter that integrates VeriProof session recording directly into your framework’s execution lifecycle — so every model call, tool invocation, and agent delegation is captured automatically without manual annotation in application code.
Custom adapters are framework middleware, not VeriProof product extensions. They use the same public SDK API as manual instrumentation — they simply call it automatically on behalf of the application developer.
Adapter Architecture
An adapter intercepts the AI framework’s execution at a framework-specific extension point (middleware, hook, event, or decorator), then calls the VeriProof SDK to:
- Open a
VeriproofSessionat the start of an AI interaction - Record individual steps (LLM calls, tool calls, guardrail events) as they occur
- Set top-level governance annotations (decision outcome, risk level, intent) either from framework signals or based on configuration
- Close the session when the interaction completes
The adapter is registered with the framework once during application startup — application code then requires no VeriProof imports.
Implementing an Adapter
Python
from opentelemetry import trace
from veriproof_sdk.enums import StepType
class MyFrameworkAdapter:
"""VeriProof adapter for MyFramework."""
def __init__(self, tracer_name: str = "MyFrameworkAdapter"):
self._tracer = trace.get_tracer(tracer_name)
def register(self, framework_instance):
framework_instance.on_before_invoke(self._on_before_invoke)
framework_instance.on_after_invoke(self._on_after_invoke)
framework_instance.on_tool_call(self._on_tool_call)
def _on_before_invoke(self, context):
span = self._tracer.start_span(context.request_id)
span.set_attribute("veriproof.session.id", context.request_id)
context._vp_span = span
context._vp_token = trace.use_span(span, end_on_exit=False).__enter__()
def _on_after_invoke(self, context, result):
span = context._vp_span
# Record the LLM step as a child span
with self._tracer.start_as_current_span("llm-call") as step:
step.set_attribute("gen_ai.system", result.provider)
step.set_attribute("gen_ai.request.model", result.model)
step.set_attribute("veriproof.step.type", StepType.LLM_CALL.value)
# Set top-level governance annotations
span.set_attribute("veriproof.decision", result.decision)
span.set_attribute("veriproof.risk.level", self._compute_risk(result))
span.end()
def _on_tool_call(self, context, tool_name, tool_result):
with self._tracer.start_as_current_span(f"tool:{tool_name}") as step:
step.set_attribute("veriproof.step.type", StepType.TOOL_CALL.value)
step.set_attribute("gen_ai.tool.name", tool_name)Required Adapter Behaviors
Every adapter must implement these behaviors to pass the adapter test suite:
Session lifecycle management
Open exactly one session per top-level AI interaction. Close the session — even on error — to prevent orphaned sessions from accumulating. Use try/finally or equivalent to guarantee close on exception paths.
Step recording completeness
Record every LLM call, tool call, and agent delegation as a step. Missing steps result in incomplete governance timelines and may cause low governance scores.
Error resilience
SDK calls must not throw exceptions that propagate into application code. Wrap all SDK calls in try/catch. If the VeriProof ingest endpoint is unreachable, the circuit breaker handles buffering — your adapter should not implement its own retry logic.
Thread and concurrency safety
If the framework executes steps concurrently (parallel tool calls, fan-out agents), associate each step with the correct session. Use the span handle stored in the framework context directly rather than via a global or thread-local.
Testing Your Adapter
To test your adapter without sending data to VeriProof’s ingest API, pass a custom VeriproofIngestTransport implementation that captures payloads in memory. The simplest approach is to mock the HTTP transport:
Python
from unittest.mock import MagicMock, patch
from veriproof_sdk import VeriproofClientOptions, configure_veriproof
# Patch the HTTP client so no real requests are made
with patch('httpx.Client') as mock_http:
mock_http.return_value.post.return_value.status_code = 200
mock_http.return_value.post.return_value.json.return_value = {"merkle_root": "abc"}
provider, exporter = configure_veriproof(
VeriproofClientOptions(api_key="test-key", application_id="test"),
service_name="test",
set_global=True,
)
adapter = MyFrameworkAdapter()
# ... run a test interaction ...
provider.force_flush()
# Assert via the captured spans on the mock_http callRelated Documentation
Manual instrumentation reference — the same API your adapter calls.
SDK InstrumentationCapability comparison across the three SDK languages.
SDK Feature MatrixRunning multiple adapters simultaneously for complex architectures.
Multi-Adapter SetupDebug custom adapter session recording and export issues.
SDK Troubleshooting