Skip to content

Migrating from LangSmith to spanforge

LangSmith is a hosted observability service for LangChain applications. spanforge is an AI compliance platform — it provides tracing, but also regulatory mapping, HMAC-signed evidence packages, consent tracking, human-in-the-loop governance, model registry, and explainability coverage that LangSmith does not offer.

Why migrate?

LangSmith helps you monitor your AI. spanforge helps you govern it. If your team needs to demonstrate compliance with the EU AI Act, GDPR, SOC 2, HIPAA, or ISO 42001, spanforge provides the compliance infrastructure that LangSmith cannot.

Key differences

FeatureLangSmithspanforge
FocusLLM observabilityAI compliance platform
Data storageLangSmith cloud (or self-hosted)Your infrastructure — files, OTLP, Datadog, etc.
PricingPer-trace $$Free (MIT)
Dependencieslangsmith package + API keyZero required deps
Offline/air-gappedNoYes
HMAC audit chainNoYes
PII redactionNoYes (built-in)
OTel compatiblePartialFull OTLP export
Regulatory framework mappingNoEU AI Act, GDPR, SOC 2, HIPAA, ISO 42001, NIST AI RMF
Consent boundary trackingNoconsent.* events
Human-in-the-loop complianceNohitl.* events
Model registry governanceNomodel_registry.* events
Explainability coverage metricsNoexplanation.* events
Evidence packages + attestationsNoHMAC-signed, auditor-ready

Step 1: Remove LangSmith tracer

# Before (LangSmith)
from langsmith import traceable
from langchain.callbacks import LangChainTracer

tracer = LangChainTracer(project_name="my-project")
# ... chain.invoke(input, config={"callbacks": [tracer]})
# After (spanforge)
import spanforge
spanforge.configure(exporter="console", service_name="my-project")

Step 2: Replace @traceable decorator

LangSmith's @traceable records function calls. In spanforge, use the span context manager:

# Before (LangSmith)
from langsmith import traceable

@traceable(name="call-llm")
def call_llm(prompt: str) -> str:
    return openai_client.chat.completions.create(...)

# After (spanforge)
import spanforge

def call_llm(prompt: str) -> str:
    with spanforge.span("call-llm") as span:
        span.set_model(model="gpt-4o", system="openai")
        result = openai_client.chat.completions.create(...)
        span.set_token_usage(
            input=result.usage.prompt_tokens,
            output=result.usage.completion_tokens,
            total=result.usage.total_tokens,
        )
        span.set_status("ok")
        return result.choices[0].message.content

Step 3: Replace LangChain callback handler

If you use the spanforge LangChain integration, the callback handler replaces LangChainTracer entirely:

# Before (LangSmith)
from langsmith import LangChainTracer
tracer = LangChainTracer(project_name="my-project")
chain.invoke(input, config={"callbacks": [tracer]})

# After (spanforge)
import spanforge
from spanforge.integrations.langchain import SpanForgeCallbackHandler

spanforge.configure(exporter="jsonl", endpoint="events.jsonl", service_name="my-project")
handler = SpanForgeCallbackHandler()
chain.invoke(input, config={"callbacks": [handler]})

Step 4: Replace run/trace IDs

LangSmith uses run_id / trace_id UUIDs. spanforge uses standard trace_id / span_id fields:

# Accessing the current trace ID
import spanforge
with spanforge.span("my-op") as span:
    print(span.trace_id)   # replaces run_id
    print(span.span_id)    # replaces span_id

Step 5: Export your historical data (optional)

If you want to export LangSmith runs to JSONL for replay or archival:

from langsmith import Client
import json

client = Client()
with open("langsmith-export.jsonl", "w") as fh:
    for run in client.list_runs(project_name="my-project"):
        fh.write(json.dumps(run.dict()) + "\n")

Then use spanforge stats langsmith-export.jsonl for a summary.


Environment variable mapping

LangSmithspanforge equivalent
LANGSMITH_API_KEY(not needed — no cloud API)
LANGCHAIN_PROJECTSPANFORGE_SERVICE_NAME
LANGCHAIN_TRACING_V2=trueSPANFORGE_ENABLE_TRACE_STORE=1
LANGCHAIN_ENDPOINTSPANFORGE_ENDPOINT

See also