Check out the newest way to compare different models for a task/agent harness: AutoEvals
Frameworks

Traces

Langfuse Traces

Send Langfuse SDK traces to the Inference platform without changing your tracing code.

If your application already uses Langfuse, this is just an env-var switch. Keep all of your existing Langfuse tracing code and point the Langfuse SDK at the Inference platform by changing three environment variables. Your spans stream to the Inference platform Traces dashboard with no SDK swap and no code changes. Inference platform accepts the Langfuse ingestion and OTEL endpoints and converts traces, generations, spans, usage, costs, inputs, outputs, metadata, users, sessions, and tags into platform spans.

This is the fastest way to try Inference platform: flip the env vars, see your traces, and switch over fully later for more control. Spans show up in the Traces dashboard out of the box but to get the most out of it, set an agent identity on your top-level span so runs group in the Agents dashboard and become available to Halo. See Group by agent.

Configure Langfuse

Use your Inference API key as the Langfuse secret key. The public key is only present for Langfuse SDK compatibility and its value is never used. Set it to pk-catalyst, but any non-empty value works.

export LANGFUSE_HOST="https://telemetry.inference.net"
export LANGFUSE_PUBLIC_KEY="pk-catalyst"
export LANGFUSE_SECRET_KEY="<your-inference-api-key>"

The Inference API key must have write access to the project that should receive the traces.

Python

Python
from langfuse import Langfuse, observe, get_client

langfuse = Langfuse(
    public_key="pk-catalyst",
    secret_key="<your-inference-api-key>",
    base_url="https://telemetry.inference.net",
)

@observe(as_type="generation", name="answer-question")
def answer_question(question: str) -> str:
    client = get_client()
    # Set identity the Langfuse way. These map to user.id, session.id, and tags in the Inference platform.
    client.update_current_trace(
        user_id="user-123",
        session_id="session-abc",
        tags=["prod"],
    )
    client.update_current_generation(
        model="gpt-4o-mini",
        usage_details={"input": 12, "output": 8, "total": 20},
    )
    return f"Answer: {question}"

print(answer_question("What is Inference platform?"))
langfuse.flush()
langfuse.shutdown()

TypeScript

TypeScript
import { observe, getClient } from "@langfuse/tracing";

process.env.LANGFUSE_HOST = "https://telemetry.inference.net";
process.env.LANGFUSE_PUBLIC_KEY = "pk-catalyst";
process.env.LANGFUSE_SECRET_KEY = "<your-inference-api-key>";

const answerQuestion = observe(
  async (question: string) => {
    const client = getClient();
    // Set identity the Langfuse way. These map to user.id, session.id, and tags in the Inference platform.
    client.updateCurrentTrace({
      userId: "user-123",
      sessionId: "session-abc",
      tags: ["prod"],
    });
    client.updateCurrentGeneration({
      model: "gpt-4o-mini",
      usageDetails: { input: 12, output: 8, total: 20 },
    });
    return `Answer: ${question}`;
  },
  { name: "answer-question", asType: "generation" },
);

console.log(await answerQuestion("What is Inference platform?"));
await getClient().flushAsync();

Langfuse's OpenTelemetry-based SDKs are also supported with the same host, public key, and secret key settings above. If you are not using Langfuse and are configuring a generic OpenTelemetry exporter, use Inference platform's standard OTLP endpoint instead.

Send to Both Langfuse and Inference platform

The setup above points the Langfuse SDK entirely at the Inference platform, so all of that SDK's traffic comes to us. If you want to keep your existing Langfuse pipeline and mirror the same traces to the Inference platform, you can fan out to both at once. This is the lowest-risk way to evaluate Inference platform: Langfuse keeps working untouched while Inference platform receives a copy of everything.

TypeScript: add a second span processor

The Langfuse JS/TS SDK is OpenTelemetry-based, and OpenTelemetry broadcasts every span to every registered span processor by default. Keep your existing LangfuseSpanProcessor pointed at Langfuse and add a second one pointed at Inference platform. Your observe calls and the rest of your tracing code do not change.

instrumentation.ts
import { LangfuseSpanProcessor } from "@langfuse/otel";
import { NodeSDK } from "@opentelemetry/sdk-node";

const sdk = new NodeSDK({
  spanProcessors: [
    // Your existing Langfuse destination.
    new LangfuseSpanProcessor({
      baseUrl: "https://cloud.langfuse.com",
      publicKey: process.env.LANGFUSE_PUBLIC_KEY,
      secretKey: process.env.LANGFUSE_SECRET_KEY,
    }),
    // Inference platform destination. The secret key is your Inference API key. The public
    // key is required by the SDK but ignored by the Inference platform, so any non-empty
    // value works.
    new LangfuseSpanProcessor({
      baseUrl: "https://telemetry.inference.net",
      publicKey: "pk-catalyst",
      secretKey: "<your-inference-api-key>",
    }),
  ],
});

sdk.start();

Every span now streams to both backends. Each processor batches and flushes independently, so a delay or outage on one does not block the other.

Python: fan out with an OpenTelemetry Collector

The Python SDK does not broadcast across destinations. Its multi-client mode routes each span to a single project based on a public-key attribute, so it is not a true dual-send: you have to thread langfuse_public_key through every @observe, OpenAI wrapper, and Langchain handler call to pick the destination, and spans without that attribute land everywhere by accident. It is fragile and not the documented path for sending to both.

The clean, SDK-agnostic way to dual-send from Python (or any runtime) is an OpenTelemetry Collector with two OTLP exporters. Both Langfuse and Inference platform accept OTLP, so point your app at a local collector and let the collector ship to both. Your application code does not change.

otel-collector.yaml
receivers:
  otlp:
    protocols:
      http:
      grpc:

exporters:
  # base64-encode "<langfuse-public-key>:<langfuse-secret-key>" for the header.
  otlphttp/langfuse:
    endpoint: https://cloud.langfuse.com/api/public/otel
    headers:
      Authorization: "Basic <base64 of langfuse-public-key:langfuse-secret-key>"
  # base64-encode "pk-catalyst:<your-inference-api-key>" for the header.
  otlphttp/inference:
    endpoint: https://telemetry.inference.net/api/public/otel
    headers:
      Authorization: "Basic <base64 of pk-catalyst:your-inference-api-key>"

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp/langfuse, otlphttp/inference]

Point your Langfuse SDK (or any OTLP exporter) at the collector's OTLP endpoint and every trace is mirrored to both backends. This is the same fan-out pattern Inference platform uses internally to ship telemetry to multiple sinks.

The collector approach works for any OTLP trace source, not just Langfuse. If you already export OpenTelemetry traces, add a second OTLP exporter for Inference platform and you are done.

What Maps Into the Inference platform

Langfuse fieldInference field
Trace IDStable Inference trace ID derived from the Langfuse trace ID
Observation IDStable Inference span ID derived from the Langfuse observation ID
Trace namelangfuse.trace.name (shown in the Traces view; does not create an agent on its own — see Group by agent)
User and sessionuser.id, session.id
Agent name / idagent.name, agent.id — only when you set them explicitly (see Group by agent)
Generation modelllm.model_name, llm.provider when inferable
Usage and costPrompt/completion/total token columns and cost columns
Input and outputSpan input/output and LLM message columns
Metadata and tagsPreserved under langfuse.* and halo.source.* attributes
Errors and warningsSpan status and status message

This is v1 of the Langfuse integration — scores, evaluations, and richer mapping are coming later. Langfuse scores and evaluations are not supported at this time. Pointing LANGFUSE_HOST at the Inference platform routes all of that SDK's traffic to us. To keep sending to Langfuse as well, see Send to both Langfuse and Inference platform. Scores emitted through the SDK are not ingested at this point in time.

Group by agent

Pointing the base URL at the Inference platform sends your spans — but Inference platform does not treat every trace as an agent. A trace name, or even a Langfuse agent-type observation, is not enough on its own; otherwise every traced workload would flood the Agents dashboard. Agent grouping is opt-in: set agent.name (and optionally a stable agent.id) on the top-level span of your agent, and Inference platform groups that whole trace — and every child span — under it in the Agents dashboard. This is also what makes the trace available to Halo, which operates on agents.

The Langfuse SDKs are OpenTelemetry-based and forward raw OTel attributes untouched, so set the two attributes directly on the active span. Do this only on the top-level agent span — children inherit grouping through the trace and should not carry their own agent identity.

  • agent.name — the human-readable label the Agents dashboard groups on.
  • agent.id — a stable identifier that survives display-name changes. When set, it wins over agent.name, so renaming the agent doesn't split its history. Omit it if you don't need stable grouping.
TypeScript
import { observe } from "@langfuse/tracing";
import { trace } from "@opentelemetry/api";

const runAgent = observe(
  async (question: string) => {
    // Mark this top-level span as an agent. Set ONLY here, not on children.
    trace.getActiveSpan()?.setAttributes({
      "agent.name": "deep-research",
      "agent.id": "deep-research-v1", // stable id; optional
    });
    // ... your agent loop, tool calls, and nested generations ...
    return "answer";
  },
  { name: "deep-research", asType: "agent" },
);
Python
from langfuse import observe
from opentelemetry import trace

@observe(as_type="agent", name="deep-research")
def run_agent(question: str) -> str:
    # Mark this top-level span as an agent. Set ONLY here, not on children.
    span = trace.get_current_span()
    span.set_attribute("agent.name", "deep-research")
    span.set_attribute("agent.id", "deep-research-v1")  # stable id; optional
    # ... your agent loop, tool calls, and nested generations ...
    return "answer"

View and Analyze

Once traces arrive, open the Inference.net dashboard and use the Traces tab to inspect individual trace trees. Traces whose top-level span carries an agent.name / agent.id (see Group by agent) are grouped in the Agents dashboard, where Halo can run analysis over them.

For the best Halo reports, keep agent.id (or agent.name) stable per agent or workflow and set userId / sessionId when available.

Want More Control?

The Langfuse drop-in is the easy on-ramp: no code changes, just env vars. If you want finer control over how traces are grouped, such as stable agent IDs, explicit agent and manual spans, and a fully shaped trace tree, switch to the first-party Inference Tracing SDK. Start with the Quickstart, then see Agent identity for grouping executions in the Agents dashboard.

On this page