# Handling Sources and Steps

:::warning Frontend Implementation Only
This guide describes **frontend logic** for parsing data returned by the API. The API handles the citation generation. Your goal is to render this data user-friendly (e.g., as clickable `[1]` badges and "Thinking..." steps).
:::

:::tip Start with the Vanilla JS Demo
**It's framework-agnostic** and demonstrates all the core concepts (step rendering, citation parsing, source cards) in ~200 lines of code. [View the demo →](https://github.com/BizoraAI/api-integraion-examples/tree/main/vanilla-js-parsing-mock)

<br />
![Vanilla JS Demo](./assets/vanilla_demo.webp)
:::

## 1. The Data Stream

The Bizora API streams responses using Server-Sent Events (SSE). You will receive different types of messages in the `custom_data` field of the chunks.

### Key Message Types

| Type | Description |
| :--- | :--- |
| `step_message` | Represents a distinct step in the AI's reasoning or action process (e.g., "Planning", "Querying Database"). |
| `source_message` | Contains a batch of sources used by the AI. |
| `ai_metadata` | Final metadata about the response. |

---

## 2. Handling Steps (Reasoning)

Steps allow you to show the user what the AI is doing before it answers. This builds trust (e.g., showing "Searching tax records...").

### Step Object Structure
```json
{
  "type": "step_message",
  "title": "Federal Tax Bill",
  "description": "Searching for recent amendments to Section 179..."
}
```

### UI Recommendation
Display these as a collapsing "Thinking" section or a list of status updates above the final answer.

---

## 3. Handling Sources (Citations)

The API links text to sources using **Node Annotations**.

### The Annotation Format
The API embeds unique Node IDs (UUIDs) in the text response:
```text
The limit is $1,220,000 [8a4b9bf1-7940-4c49-a913-807fa1244966].
```

### The Source Object
The `source_message` event provides the details for these IDs:
```json
{
  "type": "source_message",
  "content": [
    {
      "node_id": "8a4b9bf1-7940-4c49-a913-807fa1244966",
      "tool": "taxes_federal_internal_revenue_code",
      "s3_file_path": "https://s3.../usc26.xml",
      "text": "The aggregate cost which may be taken...",
      "page_label": "1"
    }
  ]
}
```

---

## 4. Frontend Extraction Logic

To render citations, you need to:
1.  **Collect** all sources from `source_message` events.
2.  **Match** the UUIDs in the text (using Regex) to these collected sources.
3.  **Replace** the UUID with a sequential number (e.g., `[1]`).

### Example Logic (JavaScript)

```javascript
function extractSources(streamData) {
  const allSources = [];
  const seenIds = new Set();

  streamData.forEach(chunk => {
    // 1. Handle Source Messages
    if (chunk.custom_data?.type === 'source_message') {
      const sources = chunk.custom_data.content || [];
      sources.forEach(source => {
        if (!seenIds.has(source.node_id)) {
          seenIds.add(source.node_id);
          allSources.push(source);
        }
      });
    }
  });

  return allSources;
}
```

### Rendering Markdown

The API response text returns standard Markdown. To ensure correct display (headers, lists, etc.) while keeping citations interactive:

1.  **Parse Markdown to HTML** first (using a library like `marked`).
2.  **Replace Citation IDs** in the resulting HTML.

```javascript
// Example using 'marked' library
function renderContent(rawText, sources) {
    // 1. Convert Markdown -> HTML
    const htmlContent = marked.parse(rawText);

    // 2. Replace [uuid] patterns with interactive buttons
    return htmlContent.replace(/\[([a-f0-9\-]{36})\]/g, (match, nodeId) => {
        const number = getCitationNumber(nodeId, sources);
        return `<button onclick="showSource('${nodeId}')">${number}</button>`;
    });
}
```

---

## 5. Scrolling Best Practices

Auto-scrolling during streaming can frustrate users who scroll up to read earlier content. **Recommended approach**: Avoid mid-stream auto-scrolling entirely.

```javascript
// During streaming: NO auto-scroll - let user read freely
for (const chunk of streamData) {
    renderChunk(chunk);
    // Do NOT call scrollToBottom() here
}

// After streaming completes: optionally scroll once
scrollToBottom();
```

This respects user intent and prevents the jarring experience of being yanked to the bottom repeatedly.

---

## Integration Examples

- **[Vanilla JS Demo](https://github.com/BizoraAI/api-integraion-examples/tree/main/vanilla-js-parsing-mock)**: Zero-dependency reference implementation
- **[React Example](https://github.com/BizoraAI/api-integraion-examples/tree/main/ai-sdk_react)**: Full React application using the Vercel AI SDK
