Skip to main content

Share step-by-step reasoning

Stream status updates with reasoning_step so users can track multi‑stage actions (fetching data, running tools, post‑processing) as tokens arrive.

Reference implementation in this GitHub repository.

Reasoning

Architecture​

Stream status updates alongside tokens so users see what the agent is doing.

agents.json configuration:

return JSONResponse(content={
"vanilla_agent_reasoning_steps": {
"endpoints": {"query": "http://localhost:7777/v1/query"},
"features": {
"streaming": True,
"widget-dashboard-select": False,
"widget-dashboard-search": False,
},
}
})

Query flow​

  • Parse QueryRequest.messages and convert to OpenAI-compatible format
  • Add system message to define agent role and capabilities
  • Emit reasoning_step() at key processing stages:
    • Before starting LLM processing
    • During data preparation or analysis steps
    • After completing major operations
  • Stream LLM response tokens with message_chunk()
  • Send final reasoning step upon completion

OpenBB AI SDK​

  • reasoning_step(event_type, message, details): Creates StatusUpdateSSE events
    • event_type: "INFO", "SUCCESS", "WARNING", "ERROR"
    • message: Human-readable status description
    • details: Optional dictionary with key-value pairs for additional context
  • message_chunk(text): Creates MessageChunkSSE for streaming LLM output
  • LlmClientMessage: Handles message conversion between formats

Core logic​

from openbb_ai import reasoning_step, message_chunk
from openbb_ai.models import QueryRequest, LlmClientMessage
from openai.types.chat import ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam

async def query(request: QueryRequest) -> EventSourceResponse:
# Convert messages to OpenAI format
openai_messages = [
ChatCompletionSystemMessageParam(
role="system",
content="You are a helpful financial assistant."
)
]

for message in request.messages:
if message.role == "human":
openai_messages.append(
ChatCompletionUserMessageParam(role="user", content=message.content)
)

async def execution_loop():
# Pre-processing reasoning
yield reasoning_step(
event_type="INFO",
message="Processing your request...",
details={"total_messages": len(request.messages)}
).model_dump()

# Stream LLM response
yield reasoning_step(
event_type="INFO",
message="Generating response..."
).model_dump()

async for event in await client.chat.completions.create(
model="gpt-4o",
messages=openai_messages,
stream=True
):
if chunk := event.choices[0].delta.content:
yield message_chunk(chunk).model_dump()

# Completion reasoning
yield reasoning_step(
event_type="SUCCESS",
message="Response generated successfully!"
).model_dump()

return EventSourceResponse(execution_loop(), media_type="text/event-stream")