> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unleeshed.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Generate Commentary

> Complete guide to generating AI commentary

## Overview

This guide walks through the complete process of generating commentary from your licensed personas.

<Info>
  **Human-in-the-Loop Quality**: Unleeshed uses a human-in-the-loop workflow where creators inject their authentic perspective before AI generates commentary. This ensures high-fidelity responses but means generation is measured in hours, not seconds.
</Info>

## The Workflow

```mermaid theme={null}
flowchart LR
    A[Get Personas] --> B[Create Topic]
    B --> C[Poll Status]
    C --> D{Ready?}
    D -->|No| C
    D -->|Yes| E[Fetch Commentaries]
    E --> F[Display]
```

### What Happens Behind the Scenes

1. **Topic Created** → Creators receive email notification
2. **Opinion Injected** → Creator adds their unique perspective (human step)
3. **AI Generates** → Commentary generated with creator's voice
4. **Creator Approves** → Quality check before delivery (human step)
5. **Ready to Fetch** → Available via API

## Step 1: Get Your Licensed Personas

First, retrieve the personas you have access to:

```javascript theme={null}
async function getPersonas() {
  const response = await fetch('https://prod.api.unleeshed.ai/partner/v1/personas', {
    headers: { 'X-Api-Key': process.env.UNLEESHED_API_KEY }
  });
  
  const { data } = await response.json();
  return data;
}

const personas = await getPersonas();
// Store persona IDs for later use
const personaIds = personas.map(p => p.id);
```

<Tip>
  Cache persona data for 1 hour to reduce API calls.
</Tip>

## Step 2: Create a Topic

Submit your topic with selected personas:

```javascript theme={null}
async function createTopic(content, personaIds, options = {}) {
  const response = await fetch('https://prod.api.unleeshed.ai/partner/v1/topics', {
    method: 'POST',
    headers: {
      'X-Api-Key': process.env.UNLEESHED_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      content,
      persona_ids: personaIds,
      output_types: options.outputTypes || ['text'],
      metadata: options.metadata || {}
    })
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error.message);
  }
  
  const { data } = await response.json();
  return data;
}

const topic = await createTopic(
  "Should the Lakers trade Anthony Davis before the deadline?",
  personaIds,
  { 
    outputTypes: ['text'],
    metadata: { source: 'daily_poll' }
  }
);

console.log(`Topic created: ${topic.topic_id}`);
console.log(`Personas: ${topic.personas_sent} will generate commentary`);
```

### Handling Per-Persona Results

The response includes status for each persona:

```json theme={null}
{
  "topic_id": "abc123...",
  "personas_sent": 2,
  "results": [
    { "persona_id": "...", "status": "sent" },
    { "persona_id": "...", "status": "sent" }
  ]
}
```

| Status         | Meaning                    | Action              |
| -------------- | -------------------------- | ------------------- |
| `sent`         | Successfully queued        | Wait for generation |
| `not_licensed` | No active license          | Contact support     |
| `duplicate`    | Same topic sent within 24h | Use existing topic  |

## Step 3: Poll for Status

Since commentary generation involves human creators, it takes longer than instant AI. Use the lightweight status endpoint for efficient polling:

```javascript theme={null}
async function pollForCommentaries(topicId, options = {}) {
  const { 
    pollIntervalMs = 60000,  // Poll every 60 seconds (human timescale)
    allowPartial = false
  } = options;
  
  while (true) {
    // Use the lightweight status endpoint for polling
    const response = await fetch(
      `https://prod.api.unleeshed.ai/partner/v1/topics/${topicId}/status`,
      { headers: { 'X-Api-Key': process.env.UNLEESHED_API_KEY } }
    );
    
    const { data } = await response.json();
    
    // Log per-persona progress
    console.log(`Overall: ${data.overall_status}`);
    data.personas.forEach(p => {
      console.log(`  ${p.persona_name}: ${p.status}`);
    });
    
    // All done
    if (data.overall_status === 'completed') {
      return data;
    }
    
    // Some ready - can return partial results
    if (data.overall_status === 'partial' && allowPartial) {
      return data;
    }
    
    // Wait before next poll
    await new Promise(r => setTimeout(r, pollIntervalMs));
  }
}

const status = await pollForCommentaries(topic.topic_id, { allowPartial: true });
```

<Warning>
  **Realistic Expectations**: Human-in-the-loop generation typically takes **30 minutes to several hours**, not seconds. Set appropriate expectations in your UI and consider webhook notifications for production use.
</Warning>

### Per-Persona Status Values

| Status                 | Description                      | Has Commentary |
| ---------------------- | -------------------------------- | -------------- |
| `topic_sent`           | Waiting for creator              | No             |
| `opinion_injected`     | Creator responded, AI generating | No             |
| `commentary_generated` | Awaiting creator approval        | No             |
| `creator_approved`     | Ready to fetch                   | **Yes**        |
| `declined`             | Creator passed on this topic     | No             |
| `expired`              | Creator didn't respond in time   | No             |

### Overall Status Values

| Status        | Description                     |
| ------------- | ------------------------------- |
| `pending`     | All personas still waiting      |
| `in_progress` | At least one creator is working |
| `partial`     | Some ready, others in progress  |
| `completed`   | All personas finished           |

## Step 4: Fetch Commentaries

Once ready, get the commentary content:

```javascript theme={null}
async function getCommentaries(topicId) {
  const response = await fetch(
    `https://prod.api.unleeshed.ai/partner/v1/topics/${topicId}/commentaries`,
    { headers: { 'X-Api-Key': process.env.UNLEESHED_API_KEY } }
  );
  
  const { data } = await response.json();
  return data.commentaries;
}

const commentaries = await getCommentaries(topic.topic_id);

commentaries.forEach(c => {
  console.log(`${c.persona.name}: ${c.content.substring(0, 100)}...`);
});
```

## Complete Example

Here's a complete integration:

```javascript theme={null}
class UnleeshedClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://prod.api.unleeshed.ai/partner/v1';
  }
  
  async request(path, options = {}) {
    const response = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: {
        'X-Api-Key': this.apiKey,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });
    
    const data = await response.json();
    
    if (!response.ok) {
      throw new Error(data.error?.message || 'Request failed');
    }
    
    return data;
  }
  
  async getPersonas() {
    const { data } = await this.request('/personas');
    return data;
  }
  
  async createTopic(content, personaIds, outputTypes = ['text']) {
    const { data } = await this.request('/topics', {
      method: 'POST',
      body: JSON.stringify({ content, persona_ids: personaIds, output_types: outputTypes })
    });
    return data;
  }
  
  async getTopicWithCommentaries(topicId) {
    const { data } = await this.request(`/topics/${topicId}`);
    return data;
  }
  
  async generateCommentary(content, personaIds, options = {}) {
    // Create topic
    const topic = await this.createTopic(content, personaIds, options.outputTypes);
    
    // Wait for completion
    const maxWait = options.maxWaitMs || 180000;
    const pollInterval = options.pollIntervalMs || 10000;
    const start = Date.now();
    
    while (Date.now() - start < maxWait) {
      const result = await this.getTopicWithCommentaries(topic.topic_id);
      
      if (result.status === 'completed') {
        return result;
      }
      
      await new Promise(r => setTimeout(r, pollInterval));
    }
    
    throw new Error('Timeout');
  }
}

// Usage
const client = new UnleeshedClient(process.env.UNLEESHED_API_KEY);

const personas = await client.getPersonas();
const result = await client.generateCommentary(
  "Should the Lakers trade AD?",
  personas.map(p => p.id)
);

console.log(result.commentaries);
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Display Commentaries" icon="desktop" href="/guides/display-commentaries">
    Best practices for showing commentaries on your platform.
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle edge cases gracefully.
  </Card>
</CardGroup>
