FITracer & custom spans
Create custom spans, set span kinds, and use FITracer decorators.
Beyond auto-instrumentation, FITracer lets you create custom spans for your own logic - agent steps, chain stages, tool calls, or any operation you want to trace.
Span Kinds
All languages share the same span kinds:
| Kind | Use for |
|---|---|
LLM | Language model inference calls |
CHAIN | Sequential pipeline steps |
AGENT | Autonomous agent actions |
TOOL | Tool/function calls |
EMBEDDING | Vector generation |
RETRIEVER | Document retrieval (RAG) |
RERANKER | Re-ranking operations |
GUARDRAIL | Safety/validation checks |
EVALUATOR | Quality scoring |
UNKNOWN | Unspecified or unexpected span type |
WORKFLOW | Custom pipeline steps (Java only) |
CONVERSATION | Voice/conversational AI (Java/C#) |
VECTOR_DB | Vector database operations (Java/C#) |
Decorators and Convenience Methods
Python’s FITracer provides decorators for clean span creation:
from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
trace_provider = register(
project_name="my-project",
project_type=ProjectType.OBSERVE,
)
tracer = trace_provider.get_tracer(__name__)
# Use the FITracer wrapper for decorators
from fi_instrumentation import FITracer
fi_tracer = FITracer(tracer)
@fi_tracer.agent(name="research-agent")
def research_agent(query):
# This entire function becomes an AGENT span
results = search(query)
return summarize(results)
@fi_tracer.chain(name="rag-pipeline")
def rag_pipeline(question):
docs = retrieve(question)
return generate(question, docs)
@fi_tracer.tool(
name="web-search",
description="Searches the web",
parameters={"query": {"type": "string"}}
)
def web_search(query):
return requests.get(f"https://api.search.com?q={query}").json()You can also use context managers for manual span creation:
from fi_instrumentation.fi_types import FiSpanKindValues
with fi_tracer.start_as_current_span(
"llm-call",
fi_span_kind=FiSpanKindValues.LLM,
) as span:
span.set_input(value="What is Python?")
response = call_llm("What is Python?")
span.set_output(value=response)
span.set_attributes({
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": 10,
"gen_ai.usage.output_tokens": 150,
}) TypeScript uses OpenTelemetry’s standard startActiveSpan pattern:
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("my-app");
// Manual span creation
tracer.startActiveSpan("rag-pipeline", (span) => {
span.setAttribute("gen_ai.span.kind", "CHAIN");
span.setAttribute("input.value", question);
const docs = retrieve(question);
const result = generate(question, docs);
span.setAttribute("output.value", result);
span.end();
return result;
});Context management functions let you set session, user, and metadata:
import {
setSession, setUser, setMetadata, setTags,
getAttributesFromContext
} from "@traceai/fi-core";
import { context } from "@opentelemetry/api";
const ctx = setSession(context.active(), { sessionId: "sess-123" });
const ctx2 = setUser(ctx, { userId: "user-456" });
context.with(ctx2, () => {
// All spans created here inherit session and user
tracer.startActiveSpan("operation", (span) => {
// span automatically gets session.id and user.id
span.end();
});
}); Java offers both lambda-based and manual span creation:
import ai.traceai.FITracer;
import ai.traceai.FISpanKind;
FITracer tracer = TraceAI.getTracer();
// Lambda-based - auto-manages span lifecycle
String result = tracer.trace("rag-pipeline", FISpanKind.CHAIN, (span) -> {
tracer.setInputValue(span, question);
String docs = tracer.trace("retrieve", FISpanKind.RETRIEVER, (rSpan) -> {
tracer.setInputValue(rSpan, question);
var retrieved = vectorDb.search(question);
tracer.setOutputValue(rSpan, tracer.toJson(retrieved));
return retrieved;
});
String answer = tracer.trace("generate", FISpanKind.LLM, (lSpan) -> {
tracer.setInputMessages(lSpan, List.of(
tracer.message("system", "Answer using the context."),
tracer.message("user", question)
));
var resp = llm.generate(question, docs);
tracer.setOutputMessages(lSpan, List.of(
tracer.message("assistant", resp)
));
tracer.setTokenCounts(lSpan, 50, 200, 250);
return resp;
});
tracer.setOutputValue(span, answer);
return answer;
});Manual span creation for more control:
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
Span span = tracer.startSpan("tool-call", FISpanKind.TOOL);
try {
tracer.setInputValue(span, inputJson);
String result = executeTool(inputJson);
tracer.setOutputValue(span, result);
span.setStatus(StatusCode.OK);
} catch (Exception e) {
tracer.setError(span, e);
} finally {
span.end();
} C# provides typed convenience methods for each span kind:
var tracer = TraceAI.Register(opts =>
{
opts.ProjectName = "my-project";
opts.ProjectType = ProjectType.Observe;
});
// Convenience methods for each span kind
var result = tracer.Chain("rag-pipeline", span =>
{
span.SetInput("What is quantum computing?");
var docs = tracer.Tool("vector-search", toolSpan =>
{
toolSpan.SetTool("search", "Searches vector DB");
toolSpan.SetInput("quantum computing");
var results = vectorDb.Search("quantum computing");
toolSpan.SetOutput(results);
return results;
});
var answer = tracer.Llm("generate", llmSpan =>
{
llmSpan.SetAttribute(SemanticConventions.GenAiRequestModel, "gpt-4o");
llmSpan.SetInputMessages(new List<Dictionary<string, string>>
{
FITracer.Message("user", "What is quantum computing?")
});
var resp = llm.Generate("What is quantum computing?", docs);
llmSpan.SetOutputMessages(new List<Dictionary<string, string>>
{
FITracer.Message("assistant", resp)
});
llmSpan.SetTokenCounts(50, 200, 250);
return resp;
});
span.SetOutput(answer);
return answer;
});
// Async variants
await tracer.AgentAsync("research-agent", async span =>
{
span.SetInput("Research topic X");
var result = await RunResearchAsync("topic X");
span.SetOutput(result);
});Manual span creation:
using var span = tracer.StartSpan("custom-op", FISpanKind.Chain);
span.SetInput("input data");
span.SetOutput("output data");
// span.Dispose() ends the span automatically FISpan Methods
All languages provide methods on the span object for setting structured data:
| Method | Description | Available in |
|---|---|---|
set_input(value, mime_type=) / SetInput(value, mimeType) | Set span input value (text or JSON). mime_type accepts "text/plain" or "application/json" | Python, C# |
set_output(value, mime_type=) / SetOutput(value, mimeType) | Set span output value | Python, C# |
set_tool(name, description, parameters) / SetTool(...) | Attach tool metadata | Python, C# |
set_attributes(dict) / SetAttribute(key, value) | Set custom attributes | All |
setInputValue(span, value) | Set input on span | Java |
setOutputValue(span, value) | Set output on span | Java |
setInputMessages(span, messages) / SetInputMessages(messages) | Set chat message history | Java, C# |
setOutputMessages(span, messages) / SetOutputMessages(messages) | Set response messages | Java, C# |
setTokenCounts(span, in, out, total) / SetTokenCounts(in, out, total) | Set token usage | Java, C# |
setError(span, exception) / SetError(exception) | Record an exception | Java, C# |
Note
In Java, these methods live on FITracer and take the span as the first argument (e.g. tracer.setInputValue(span, value)). In Python and C#, they’re called directly on the span object.
Questions & Discussion