Intermediate Steps_Oryx

Oryx provides components to render intermediate steps during agent execution, including tool calls, thinking/reasoning, and workflow progress. These help users understand what the agent is doing behind the scenes.

Showing the current stage

Use Oryx.CurrentStage to display the current workflow stage (e.g., "retrieval", "generation", "attribution").

tsx

"use client";

import { Oryx, useOryxCurrentStage, useOryxStatus } from "@contextualai/oryx-react";

function CurrentStageIndicator() {
  const { currentStage } = useOryxCurrentStage();
  const { isStreaming } = useOryxStatus();

  if (!isStreaming || !currentStage) {
    return null;
  }

  const stageLabels: Record<string, string> = {
    retrieval: "Retrieving documents...",
    generation: "Generating response...",
    attribution: "Adding attributions...",
    post_processing: "Post-processing...",
    finalization: "Finalizing...",
  };

  return (
    <div className="stage-indicator">
      {stageLabels[currentStage] ?? currentStage}
    </div>
  );
}

Alternatively, use the component directly with a render prop:

tsx

<Oryx.CurrentStage
  render={(stage) => (
    <span>{stage}</span>
  )}
/>

Showing tool calls

Use Oryx.ToolCalls.* to render lists of tool executions and Oryx.ToolCall.* to render per-item fields such as name, status, arguments, output, and errors.

tsx

"use client";

import { Oryx, useOryxToolCalls, useOryxToolCallItem } from "@contextualai/oryx-react";

// ---------- Tool call item ----------

function ToolCallItem() {
  const { toolCall } = useOryxToolCallItem();

  return (
    <div className="tool-call">
      <Oryx.ToolCall.Status
        render={(status) => (
          <span className={`status-${status}`}>{status}</span>
        )}
      />
      <Oryx.ToolCall.Name
        render={(name) => (
          <span className="tool-name">{name}</span>
        )}
      />
      <Oryx.ToolCall.Arguments
        render={(args) => (
          <pre>{JSON.stringify(args, null, 2)}</pre>
        )}
      />
      <Oryx.ToolCall.Output
        render={(output) => (
          <pre>{JSON.stringify(output, null, 2)}</pre>
        )}
      />
      <Oryx.ToolCall.Error
        render={(error) => (
          <span className="error">{error}</span>
        )}
      />
    </div>
  );
}

// ---------- Tool calls section ----------

export function ToolCallsSection() {
  const { hasToolCalls, toolCalls } = useOryxToolCalls();

  if (!hasToolCalls) {
    return null;
  }

  return (
    <div className="tool-calls-section">
      <h4>Tool Calls ({toolCalls.length})</h4>
      <Oryx.ToolCalls.List>
        <ToolCallItem />
      </Oryx.ToolCalls.List>
    </div>
  );
}

Showing thinking steps

Use Oryx.Thinking.* to render the agent's reasoning process. This is useful for showing users what the agent is considering while generating a response.

tsx

"use client";

import { Oryx, useOryxThinking, useOryxThinkingStepItem } from "@contextualai/oryx-react";

// ---------- Thinking step item ----------

function ThinkingStepItem() {
  const { thinkingStep } = useOryxThinkingStepItem();

  return (
    <div className="thinking-step">
      <Oryx.Thinking.Summary
        render={(summary) => (
          <div className="summary">{summary}</div>
        )}
      />
      <Oryx.Thinking.Content
        render={(content) => (
          <div className="content">{content}</div>
        )}
      />
      <Oryx.Thinking.Status
        render={(isCompleted) =>
          !isCompleted && <span className="thinking-indicator">Thinking...</span>
        }
      />
    </div>
  );
}

// ---------- Thinking section ----------

export function ThinkingSection() {
  const { hasThinking, currentThinking } = useOryxThinking();

  if (!hasThinking) {
    return null;
  }

  return (
    <div className="thinking-section">
      <h4>
        Thinking
        {currentThinking && !currentThinking.isCompleted && "..."}
      </h4>
      <Oryx.Thinking.List>
        <ThinkingStepItem />
      </Oryx.Thinking.List>
    </div>
  );
}

Showing workflow steps

Use Oryx.WorkflowSteps.* to render workflow progress for multi-step agent executions.

tsx

"use client";

import { Oryx, useOryxWorkflowSteps, useOryxWorkflowStepItem } from "@contextualai/oryx-react";

// ---------- Workflow step item ----------

function WorkflowStepItem() {
  const { workflowStep } = useOryxWorkflowStepItem();

  return (
    <div className="workflow-step">
      <Oryx.WorkflowStep.Name
        render={(name) => (
          <span className="step-name">{name}</span>
        )}
      />
      <Oryx.WorkflowStep.Type
        render={(type) => (
          <span className="step-type">{type}</span>
        )}
      />
      <Oryx.WorkflowStep.Status
        render={(status) => (
          <span className={`status-${status}`}>{status}</span>
        )}
      />
    </div>
  );
}

// ---------- Workflow steps section ----------

export function WorkflowStepsSection() {
  const { hasWorkflowSteps, currentStep } = useOryxWorkflowSteps();

  if (!hasWorkflowSteps) {
    return null;
  }

  return (
    <div className="workflow-steps-section">
      <h4>Workflow Progress</h4>
      <Oryx.WorkflowSteps.List>
        <WorkflowStepItem />
      </Oryx.WorkflowSteps.List>
    </div>
  );
}

Combining all intermediate steps

You can combine all intermediate step components into a single section that appears before the agent message. The useOryxIntermediateSteps hook provides a unified view of all steps in chronological order.

tsx

"use client";

import { Oryx, useOryxIntermediateSteps } from "@contextualai/oryx-react";

export function IntermediateStepsSection() {
  const { steps, hasSteps, currentStage } = useOryxIntermediateSteps();

  if (!hasSteps) {
    return null;
  }

  return (
    <div className="intermediate-steps">
      {currentStage && (
        <div className="current-stage">Stage: {currentStage}</div>
      )}

      {steps.map((step, index) => (
        <div key={index} className={`step step-${step.type}`}>
          {step.type === "tool_call" && (
            <span>Tool: {step.data.name} ({step.data.status})</span>
          )}
          {step.type === "thinking" && (
            <span>Thinking: {step.data.summary ?? "..."}</span>
          )}
          {step.type === "workflow" && (
            <span>Step: {step.data.name} ({step.data.status})</span>
          )}
        </div>
      ))}
    </div>
  );
}

Putting it together

Place intermediate step components inside your message list, typically before Oryx.Message.Agent:

tsx

<Oryx.Messages.List>
  <Oryx.Message.User />
  <IntermediateStepsSection />
  <Oryx.Message.Agent render={(content) => <MarkdownRenderer content={content} />} />
</Oryx.Messages.List>

Check out the complete working example for a styled implementation of intermediate steps.

Backend SSE events

Note: These are the SSE events that Oryx listens for to populate intermediate step state. Your backend must emit these events for the components above to render data.

Stage events

The stepping event signals a workflow stage change. Oryx uses this to update currentStage exposed by useOryxCurrentStage.

json

{
  "type": "retrieval" | "generation" | "attribution" | "post_processing" | "finalization"
}

Tool call events

Tool calls use a start/end event pair. The tool_id links them together.

tool_call_start — emitted when a tool begins executing.

json

{
  "tool_id": "unique-id",
  "tool_name": "search_documents",
  "tool_args": "{\"query\": \"example\"}"
}

tool_call_end — emitted when a tool finishes.

json

{
  "tool_id": "unique-id",
  "tool_output": "result data",
  "successful": true,
  "error": ""
}

Thinking events

Thinking events support streaming content via deltas. The think_id links related events.

thinking_start — emitted when reasoning begins.

json

{
  "think_id": "unique-id"
}

thinking_delta — emitted for each chunk of thinking content.

json

{
  "think_id": "unique-id",
  "delta": "Let me consider..."
}

thinking_end — emitted when reasoning completes.

json

{
  "think_id": "unique-id",
  "thinking_summary": "Analyzed the query and determined the best approach."
}

Workflow step events

Workflow steps use a start/end event pair. The step_id links them together.

step_start — emitted when a workflow step begins.

json

{
  "step_id": "unique-id",
  "step_name": "Document Retrieval",
  "step_type": "retrieval"
}

step_end — emitted when a workflow step finishes.

json

{
  "step_id": "unique-id",
  "status": "completed" | "failed" | "cancelled"
}

Backtrack

Retrievals

Read Next

Composition Guide

© 2025 Contextual AI, Inc.

Proudly open sourced under the Apache 2.0 license.