Integration guide

HuggingFace Smolagents Observability

HuggingFace Smolagents is a lightweight agent framework — a minimal API for tool-calling and code-executing agents. This guide shows how to add full distributed tracing to Smolagents runs with Nexus: per-run traces, step-level spans for CodeAgent, and tool call spans for ToolCallingAgent.

Install

Install both the Smolagents framework and the Nexus Python SDK:

pip install smolagents nexus-agent

Get your API key from the dashboard and set it as an environment variable:

export NEXUS_API_KEY="nxs_your_key_here"

Basic trace wrapping

The simplest integration wraps the entire agent run in a trace. Every run appears in the dashboard with its status and duration:

from smolagents import ToolCallingAgent, HfApiModel, tool
from nexus_agent import NexusClient
import os

nexus = NexusClient(
    api_key=os.environ["NEXUS_API_KEY"],
    agent_id="my-smolagent",
)

@tool
def get_weather(location: str) -> str:
    """Get current weather for a location."""
    return f"Sunny, 22C in {location}"

model = HfApiModel(model_id="Qwen/Qwen2.5-72B-Instruct")
agent = ToolCallingAgent(tools=[get_weather], model=model)

task = "What is the weather in Paris?"

trace = nexus.start_trace(
    name=f"agent: {task[:60]}",
    metadata={"task": task, "model": "Qwen2.5-72B-Instruct"},
)
try:
    result = agent.run(task)
    nexus.end_trace(trace["id"], status="success", output={"result": result})
except Exception as e:
    nexus.end_trace(trace["id"], status="error", error=str(e))
    raise

Step-level spans (CodeAgent)

For CodeAgent, each reasoning step is the interesting unit. Subclass CodeAgent and override step() to create per-step spans:

from smolagents import CodeAgent, HfApiModel
from nexus_agent import NexusClient
import os

nexus = NexusClient(
    api_key=os.environ["NEXUS_API_KEY"],
    agent_id="code-agent",
)

class TracedCodeAgent(CodeAgent):
    def __init__(self, *args, trace_id=None, **kwargs):
        super().__init__(*args, **kwargs)
        self._trace_id = trace_id
        self._step_count = 0

    def step(self, memory):
        self._step_count += 1
        span = nexus.add_span(
            trace_id=self._trace_id,
            name=f"step-{self._step_count}",
            metadata={"step": self._step_count},
        ) if self._trace_id else None

        try:
            result = super().step(memory)
            if span:
                nexus.end_span(span["id"], status="success")
            return result
        except Exception as e:
            if span:
                nexus.end_span(span["id"], status="error", error=str(e))
            raise

model = HfApiModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct")
task = "Write a function to sort a list of dicts by a given key."

trace = nexus.start_trace(name=f"code-agent: {task[:60]}", metadata={"task": task})
agent = TracedCodeAgent(tools=[], model=model, trace_id=trace["id"])

try:
    result = agent.run(task)
    nexus.end_trace(trace["id"], status="success", output={"result": result})
except Exception as e:
    nexus.end_trace(trace["id"], status="error", error=str(e))
    raise

Tool call spans (ToolCallingAgent)

For ToolCallingAgent, wrap individual tool functions to get per-tool spans. A decorator makes this reusable across all your tools:

import functools
from smolagents import tool
from nexus_agent import NexusClient
import os

nexus = NexusClient(
    api_key=os.environ["NEXUS_API_KEY"],
    agent_id="tool-calling-agent",
)

def traced_tool(trace_id: str):
    """Decorator: wraps a smolagents tool with a Nexus span."""
    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            span = nexus.add_span(
                trace_id=trace_id,
                name=f"tool:{fn.__name__}",
                input={"args": list(args), "kwargs": kwargs},
                metadata={"tool_name": fn.__name__},
            )
            try:
                result = fn(*args, **kwargs)
                nexus.end_span(span["id"], status="success", output={"result": result})
                return result
            except Exception as e:
                nexus.end_span(span["id"], status="error", error=str(e))
                raise
        return wrapper
    return decorator

# Wrap tools at trace creation time:
trace = nexus.start_trace(name="weather-lookup")

@tool
@traced_tool(trace["id"])
def get_weather(location: str) -> str:
    """Get current weather for a location."""
    return f"Sunny, 22C in {location}"

Metadata best practices

Use trace metadata to make runs searchable and comparable in the dashboard:

nexus.start_trace(
    name=f"agent: {task[:60]}",
    metadata={
        "task": task,
        "model_id": "Qwen/Qwen2.5-72B-Instruct",
        "agent_type": "ToolCallingAgent",   # or CodeAgent
        "step_budget": 10,                  # max_steps passed to agent.run()
        "tool_count": len(tools),
        "environment": "production",        # or "staging", "dev"
    },
)

With agent_type and model_id in metadata, you can filter traces in the Nexus dashboard to compare different agent configurations on the same tasks.

Ready to instrument your Smolagents?

Free tier, no credit card. Add tracing to your first Smolagents run in minutes.