There are three ways to send traces to Infinium, from simplest to most detailed. Choose based on how much control and detail you need.

Comparison

MethodBest ForAutomatic TimingStep-Level DetailAuto-Send
send_task()Simple, single-step agentsNoNoYes
TaskData / create_task_data()Full control, structured dataNoYesNo
TraceBuilderReal agents with multiple stepsYesYesNo

1. send_task() — Simple, Inline

The quickest way. Call your LLM, then report what happened in a single call:

import time
from openai import OpenAI
from infinium import InfiniumClient

client = InfiniumClient(agent_id="...", agent_secret="...")
openai = OpenAI()

start = time.time()
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Classify this support ticket. Return JSON."},
        {"role": "user", "content": ticket_text},
    ],
    response_format={"type": "json_object"},
)
duration = time.time() - start
result = response.choices[0].message.content
usage = response.usage

response = client.send_task(
    name="Classify support ticket",
    description="Categorized an inbound support ticket by type and urgency.",
    duration=duration,
    input_summary=f"Customer ticket: {ticket_text[:200]}",
    output_summary=f"Classification: {result[:300]}",
    llm_usage={
        "model": "gpt-4o",
        "provider": "openai",
        "prompt_tokens": usage.prompt_tokens,
        "completion_tokens": usage.completion_tokens,
        "total_tokens": usage.total_tokens,
        "api_calls_count": 1,
    },
    customer={"customer_name": customer_name, "customer_email": customer_email},
)

Parameters

ParameterTypeRequiredDescription
namestrYesShort name for the task (max 500 chars)
descriptionstrYesWhat the agent did (max 10,000 chars)
durationfloatYesWall-clock time in seconds (0 to 86,400)
current_datetimestrNoISO 8601 timestamp (auto-generated if omitted)
input_summarystrNoSummary of the input the agent received
output_summarystrNoSummary of what the agent produced
llm_usagedict or LlmUsageNoToken counts, model, cost
stepslistNoExecution steps
expected_outcomedict or ExpectedOutcomeNoMaestro’s grading rubric
environmentdict or EnvironmentContextNoRuntime metadata
errorslistNoErrors encountered

send_task() also accepts any domain section as a keyword argument (e.g., customer=, sales=, research=). Raw dicts are automatically coerced to the corresponding dataclass.


2. TaskData / create_task_data() — Structured

When you need full control over every field, build a TaskData object. The static method create_task_data() validates inputs and coerces raw dicts to dataclasses:

from infinium import (
    InfiniumClient, ExecutionStep, LlmCall, LlmUsage,
    ExpectedOutcome, EnvironmentContext, ErrorDetail,
)

steps = []
errors = []
total_prompt = 0
total_completion = 0

# Step 1: Search
step1_start = time.time()
results = search_knowledge_base(query)
step1_ms = int((time.time() - step1_start) * 1000)

steps.append(ExecutionStep(
    step_number=1,
    action="tool_use",
    description="Search knowledge base for relevant documents",
    duration_ms=step1_ms,
    output_preview=f"Found {len(results)} results",
))

# Step 2: LLM analysis
step2_start = time.time()
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Analyze these findings..."},
        {"role": "user", "content": str(results)},
    ],
)
step2_ms = int((time.time() - step2_start) * 1000)
analysis = response.choices[0].message.content
usage = response.usage
total_prompt += usage.prompt_tokens
total_completion += usage.completion_tokens

steps.append(ExecutionStep(
    step_number=2,
    action="llm_inference",
    description="Analyze search results with GPT-4o",
    duration_ms=step2_ms,
    llm_call=LlmCall(
        model="gpt-4o",
        provider="openai",
        prompt_tokens=usage.prompt_tokens,
        completion_tokens=usage.completion_tokens,
        latency_ms=step2_ms,
    ),
    output_preview=analysis[:500],
))

# Build TaskData
td = InfiniumClient.create_task_data(
    name="Research: quarterly earnings impact",
    description="Multi-step research with source gathering and LLM analysis",
    duration=time.time() - total_start,
    input_summary=f"Research query: {query}",
    output_summary=analysis[:500],
    steps=steps,
    errors=errors if errors else None,
    expected_outcome=ExpectedOutcome(
        task_objective="Produce sourced analysis of quarterly earnings impact",
        required_deliverables=["Source list", "Analysis", "Executive summary"],
        acceptance_criteria=["At least 3 sources", "Specific data points cited"],
    ),
    llm_usage=LlmUsage(
        model="gpt-4o",
        provider="openai",
        prompt_tokens=total_prompt,
        completion_tokens=total_completion,
        total_tokens=total_prompt + total_completion,
        api_calls_count=1,
    ),
    environment=EnvironmentContext(framework="custom", python_version="3.12"),
)

# Send
client.send_task_data(td)

Dict Coercion

You can pass raw dicts anywhere a dataclass is expected. create_task_data() auto-coerces them:

td = InfiniumClient.create_task_data(
    name="Quick task",
    description="Demo of dict coercion",
    duration=1.0,
    llm_usage={"model": "gpt-4o", "prompt_tokens": 100},  # Coerced to LlmUsage
    customer={"customer_name": "Acme"},                     # Coerced to Customer
)

TraceBuilder wraps your agent’s execution. Steps are context managers that automatically capture timing and catch exceptions:

from infinium import TraceBuilder, LlmUsage

trace = TraceBuilder(
    "Blog Post Generator",
    "Generate a publish-ready blog post on a given topic.",
)

trace.set_expected_outcome(
    task_objective="Produce a 600-word blog post with SEO metadata",
    required_deliverables=["Blog post", "SEO title", "Meta description"],
)
trace.set_input_summary(f"Topic: {topic}. Audience: {audience}.")

total_prompt = 0
total_completion = 0

# Step 1 -- auto-timed, exceptions auto-captured
with trace.step("llm_inference", "Create outline") as step:
    resp = openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Create a blog post outline."},
            {"role": "user", "content": topic},
        ],
    )
    outline = resp.choices[0].message.content
    total_prompt += resp.usage.prompt_tokens
    total_completion += resp.usage.completion_tokens
    step.set_output(outline[:500])
    step.record_llm_call("gpt-4o", provider="openai",
                         prompt_tokens=resp.usage.prompt_tokens,
                         completion_tokens=resp.usage.completion_tokens)

# Step 2
with trace.step("llm_inference", "Write full blog post") as step:
    resp = openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Write a complete blog post from this outline."},
            {"role": "user", "content": outline},
        ],
    )
    blog_post = resp.choices[0].message.content
    total_prompt += resp.usage.prompt_tokens
    total_completion += resp.usage.completion_tokens
    step.set_output(blog_post[:500])
    step.record_llm_call("gpt-4o", provider="openai",
                         prompt_tokens=resp.usage.prompt_tokens,
                         completion_tokens=resp.usage.completion_tokens)

trace.set_output_summary(f"Generated {len(blog_post.split())}-word blog post.")
trace.set_llm_usage(LlmUsage(
    model="gpt-4o", provider="openai",
    prompt_tokens=total_prompt, completion_tokens=total_completion,
    total_tokens=total_prompt + total_completion,
    api_calls_count=2,
))

task_data = trace.build()  # Auto-computes total duration from wall clock
client.send_task_data(task_data)

TraceBuilder Methods

MethodDescription
step(action, description)Create an auto-timed step (context manager)
set_input_summary(summary)What the agent received
set_output_summary(summary)What the agent produced
set_expected_outcome(...)Maestro’s grading rubric
set_environment(...)Runtime metadata
set_llm_usage(llm_usage)Aggregate token stats
set_section(name, value)Set a domain section
add_error(error)Record an error
build()Build the final TaskData

All setters return self for fluent chaining:

trace.set_input_summary("...").set_expected_outcome(...).set_environment(...)

StepContext Methods

Inside a with trace.step(...) as step: block:

MethodDescription
set_input(preview)Input preview (truncated to 500 chars)
set_output(preview)Output preview (truncated to 500 chars)
record_tool_call(tool_name, ...)Record a tool invocation
record_llm_call(model, ...)Record an LLM call
set_metadata(metadata)Attach arbitrary metadata

If an unhandled exception occurs inside a step, it is automatically captured as an ErrorDetail on the step.