Launch Week 3: Five days of launches

Set Input/Output

Learn how to supply input and output of your LLM application in a trace

Overview

Both traces and spans have inputs and outputs. In most apps, init() captures them automatically from supported provider and framework integrations.

Use a trace context when you want to override trace I/O without creating another span. For spans you create yourself, use the span update helper to override the values captured from the function's arguments and return value.

Set Trace I/O

By default, a trace inherits the input and output captured on its root span. To set trace properties without introducing a wrapper span, open a trace context around the instrumented call:

main.py
from langchain_openai import ChatOpenAI
from confident_trace import init, trace_context

init()
model = ChatOpenAI(model="gpt-4o")

def llm_app(query: str):
    with trace_context(input=query):
        return model.invoke(query)

Here, the trace context sets the raw user text as the trace input while init() captures the LangChain call and its output automatically. The context creates neither a trace nor a span; the instrumented call starts the trace.

The input and output can be any JSON-serializable type, though strings usually produce the clearest display for conversation threads.

Set Span I/O

By default, a wrapped function's arguments become the span input and its return value becomes the output (withSpan callbacks capture only the return value). You can override either value while the span is active.

main.py
from confident_trace import init, span, update_span

init()

@span(type="retriever", name="retrieve")
def retrieve(query: str) -> list[str]:
    documents = vector_store.similarity_search(query, k=3)
    update_span(input=query, output=documents, retrieval_context=documents)
    return documents

The span update helper writes to the current span. In this example, it replaces the retriever function's default I/O and also records the returned documents as retrieval context.

Beyond input and output, both helpers accept the evaluation fields you'd normally put on a test case — metadata, context, retrieval_context, expected_output, tools_called, and expected_tools — as JSON-compatible data. Setting them doesn't run any metrics by itself; it makes the values available so your online evals have what they need.

I/O for Streamed Responses

If your function streams its response, the trace output won't be captured automatically — what your function returns is a generator, not the final text. Collect the streamed chunks and set the output explicitly once the stream is done:

main.py
from confident_trace import init, span, update_trace

init()

def stream_response(query: str):
    with span("stream_response", type="agent"):
        chunks = []
        for chunk in llm.stream(query):
            chunks.append(chunk)
            yield chunk

        update_trace(input=query, output="".join(chunks))

Without this, the trace will appear on Confident AI with no output. If you're running in a serverless environment, also make sure the stream has finished before you call flush() — see flush and shutdown.

I/O for Threads

For multi-turn AI apps that create a thread from the traces, it is highly recommended that you provide strings instead, where the input will represent the user input, and output representing the AI generated output. You can also leave out any input or output for consecutive user/LLM behaviors.

You will also need the input and output to run online evaluations on a thread, as these will be used as the turns for a conversational test case.

Next Steps

With your trace and span I/O configured, connect traces into conversations or start evaluating them.

Ready to monitor AI in production?Connect traces, alerts, dashboards, and evals in one production workflowBook a demo

Last updated on

Built byConfident AI