Working with Responses

Updated

Every call to the Chaos AI API returns a ChatCreateResponse. This guide covers how to inspect the response status, extract text and structured blocks, and evaluate risk information attached to transaction proposals.

ChatCreateResponse Structure

The response object has the following shape:

FieldTypeDescription
idstringUnique response identifier
kind"response"Always "response"
modelstringModel used for the response
status"completed" | "failed"Whether the request succeeded
messagesChaosSDKMessage[]Array of response messages (text, blocks, status, etc.)
errorResponseErrorError details when status is "failed"

The messages array contains different message types identified by their type field:

  • "text" -- Free-form text content
  • "block" -- Structured data (tables, charts, transactions, forms, etc.)
  • "status" -- Agent processing status updates
  • "follow_up_suggestions" -- Suggested follow-up questions
  • "input" -- User disambiguation requests

Extracting Text

extractText() collects all text messages from a response and joins them into a single string. This is the simplest way to get the conversational reply.

extract-text.ts
import { Chaos, WALLET_MODEL, extractText } from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'What is the current price of ETH?' }],
  metadata: { user_id: 'user-123', session_id: 'session-abc' },
});
 
if (response.status === 'completed') {
  const text = extractText(response);
  console.log(text);
} else {
  console.error('Request failed:', response.error);
}

Extracting Blocks

extractBlocks() returns all Block objects from the response. Blocks are structured data -- tables, charts, transaction proposals, markdown, forms, interactive elements, and more.

extract-blocks.ts
import { Chaos, WALLET_MODEL, extractBlocks } from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Show my portfolio breakdown.' }],
  metadata: { user_id: 'user-123', session_id: 'session-abc' },
});
 
const blocks = extractBlocks(response);
 
for (const block of blocks) {
  console.log(`Block type: ${block.type}`);
}
 
// Possible block types:
// "table" | "pie_chart" | "bar_chart" | "timeseries_chart" |
// "action" | "markdown" | "interactive" | "alert" | "code" | "form"
[@portabletext/react] Unknown block type "callout", specify a component for it in the `components.types` prop

Typed Block Extractors

The SDK provides specialized extractors that filter blocks by type and return properly typed arrays. Each returns only the matching block type, so you get full TypeScript inference.

Table Blocks

extractTableBlocks() returns TableBlock[]. Each table has tableHeaders (column names) and tableRows (2D data array). Use the companion utilities to convert tables to objects or query specific columns.

table-blocks.ts
import {
  Chaos,
  WALLET_MODEL,
  extractTableBlocks,
  tableToObjects,
  getTableColumn,
  findTableRow,
  getTableDimensions,
  findTableByTitle,
} from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Show my Aave positions.' }],
  metadata: { user_id: 'user-123', session_id: 'session-abc' },
});
 
// Get all table blocks
const tables = extractTableBlocks(response);
 
if (tables.length > 0) {
  const table = tables[0];
 
  // Dimensions
  const dims = getTableDimensions(table);
  console.log(`${dims.rows} rows x ${dims.columns} columns`);
 
  // Convert to array of objects
  interface Position { Asset: string; Balance: string; APY: string }
  const rows = tableToObjects<Position>(table);
  for (const row of rows) {
    console.log(`${row.Asset}: ${row.Balance} (${row.APY})`);
  }
 
  // Extract a single column
  const assets = getTableColumn(table, 'Asset');
  console.log('Assets:', assets);
 
  // Find a specific row
  const ethRow = findTableRow(table, 'Asset', 'ETH');
  console.log('ETH row:', ethRow);
}
 
// Search by title
const supplyTable = findTableByTitle(response, 'supply');
if (supplyTable) {
  console.log('Found supply table with', supplyTable.tableRows.length, 'rows');
}

Chart Blocks

extractChartBlocks() returns a ChartBlock[] union of PieChartBlock | BarChartBlock | TimeseriesChartBlock. Use the chart utility functions or subtype guards to work with specific chart types.

chart-blocks.ts
import {
  Chaos,
  WALLET_MODEL,
  extractChartBlocks,
  getChartData,
  getChartPercentages,
  getChartTotal,
  findChartByTitle,
  isPieChartBlock,
  isBarChartBlock,
  isTimeseriesChartBlock,
} from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Show my portfolio allocation.' }],
  metadata: { user_id: 'user-123', session_id: 'session-abc' },
});
 
const charts = extractChartBlocks(response);
 
for (const chart of charts) {
  if (isPieChartBlock(chart)) {
    // Pie chart utilities
    const data = getChartData(chart);
    const percentages = getChartPercentages(chart);
    const total = getChartTotal(chart);
 
    console.log(`Total: $${total}`);
    for (const segment of percentages) {
      console.log(`  ${segment.label}: ${segment.percentage.toFixed(1)}%`);
    }
  } else if (isBarChartBlock(chart)) {
    console.log('Bar chart:', chart.title);
  } else if (isTimeseriesChartBlock(chart)) {
    console.log('Timeseries chart:', chart.title);
  }
}
 
// Search by title
const allocationChart = findChartByTitle(response, 'allocation');
if (allocationChart) {
  console.log('Found allocation chart');
}

Transaction Action Blocks

extractTransactionBlocks() returns ActionBlock[]. These represent proposed DeFi transactions with primitives (the specific operations), risk assessments, and optional transaction sequences.

transaction-blocks.ts
import {
  Chaos,
  WALLET_MODEL,
  extractTransactionBlocks,
  extractPrimitives,
  extractPrimitivesByType,
  getPrimitiveTypes,
  findTransactionsByPrimitive,
} from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Deposit 1 ETH into Aave on Ethereum.' }],
  metadata: {
    user_id: 'user-123',
    session_id: 'session-abc',
    wallets: [{ address: '0xABC...', chain: 'ethereum' }],
  },
});
 
// Get all transaction blocks
const txBlocks = extractTransactionBlocks(response);
 
for (const block of txBlocks) {
  console.log('Needs confirmation:', block.needs_confirmation);
  console.log('Notes:', block.notes);
  console.log('Risk level:', block.risks.level);
  console.log('Primitives:', block.primitives.length);
}
 
// Extract all primitives across all transaction blocks
const primitives = extractPrimitives(response);
for (const p of primitives) {
  console.log(`${p.type}: ${JSON.stringify(p.params)}`);
}
 
// Get unique primitive types
const types = getPrimitiveTypes(response);
console.log('Primitive types used:', types);
 
// Filter by specific type
const deposits = extractPrimitivesByType(response, 'lending_deposit');
console.log(`Found ${deposits.length} deposit primitive(s)`);
 
// Find transaction blocks containing a specific primitive
const depositBlocks = findTransactionsByPrimitive(response, 'lending_deposit');
console.log(`Found ${depositBlocks.length} block(s) with deposit primitives`);

Risk Assessment

Transaction action blocks include a risks object with a severity level and arrays of warnings and blockers. The SDK provides functions to inspect risk information across all transaction blocks in a response.

Checking for Risks

hasRisks() returns true if any transaction block has a risk level above "low". hasBlockers() returns true if any block has "high" or "critical" risk level, or contains blockers.

risk-checks.ts
import { Chaos, WALLET_MODEL, hasRisks, hasBlockers, extractText } from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Swap 100 ETH for USDC.' }],
  metadata: {
    user_id: 'user-123',
    session_id: 'session-abc',
    wallets: [{ address: '0xABC...', chain: 'ethereum' }],
  },
});
 
if (hasBlockers(response)) {
  console.log('BLOCKED: This transaction has critical risks. Do not proceed.');
} else if (hasRisks(response)) {
  console.log('WARNING: This transaction has elevated risks. Review before proceeding.');
} else {
  console.log('Low risk. Safe to proceed.');
}
 
console.log(extractText(response));

Inspecting Warnings and Blockers

getAllWarnings() and getAllBlockers() aggregate RiskInfoItem objects from all transaction blocks. Each item has optional title, message, severity, and impact fields.

risk-details.ts
import {
  Chaos,
  WALLET_MODEL,
  getAllWarnings,
  getAllBlockers,
  getHighestRiskLevel,
} from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Borrow 50000 USDC against my ETH on Aave.' }],
  metadata: {
    user_id: 'user-123',
    session_id: 'session-abc',
    wallets: [{ address: '0xABC...', chain: 'ethereum' }],
  },
});
 
// Overall risk level across all transactions
const riskLevel = getHighestRiskLevel(response);
console.log('Highest risk level:', riskLevel); // "low" | "medium" | "high" | "critical" | undefined
 
// Inspect warnings
const warnings = getAllWarnings(response);
for (const w of warnings) {
  console.log(`[WARNING] ${w.title}: ${w.message}`);
}
 
// Inspect blockers
const blockers = getAllBlockers(response);
for (const b of blockers) {
  console.log(`[BLOCKER] ${b.title}: ${b.message}`);
}
[@portabletext/react] Unknown block type "callout", specify a component for it in the `components.types` prop

Block Statistics

The SDK provides utility functions to summarize the block composition of a response.

block-stats.ts
import {
  Chaos,
  WALLET_MODEL,
  countBlocksByType,
  hasBlocks,
  hasBlockType,
} from '@chaoslabs/ai-sdk';
 
const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
const response = await chaos.chat.responses.create({
  model: WALLET_MODEL,
  input: [{ type: 'message', role: 'user', content: 'Analyze my full portfolio.' }],
  metadata: { user_id: 'user-123', session_id: 'session-abc' },
});
 
// Check if response contains any blocks
if (hasBlocks(response)) {
  // Check for specific block types
  if (hasBlockType(response, 'table')) {
    console.log('Response includes table data');
  }
  if (hasBlockType(response, 'pie_chart')) {
    console.log('Response includes pie charts');
  }
 
  // Get counts for all block types
  const counts = countBlocksByType(response);
  console.log('Block counts:', counts);
  // { table: 2, pie_chart: 1, bar_chart: 0, timeseries_chart: 1,
  //   action: 0, markdown: 1, interactive: 0, alert: 0, code: 0, form: 0 }
}

Complete Example: Response Lifecycle

Here is an end-to-end example showing the full lifecycle of inspecting a response -- from status checking through text extraction, block processing, and risk evaluation.

full-lifecycle.ts
import {
  Chaos,
  WALLET_MODEL,
  extractText,
  extractBlocks,
  extractTableBlocks,
  extractChartBlocks,
  extractTransactionBlocks,
  tableToObjects,
  hasRisks,
  hasBlockers,
  getAllWarnings,
  getAllBlockers,
  getHighestRiskLevel,
  countBlocksByType,
} from '@chaoslabs/ai-sdk';
 
async function analyzeResponse() {
  const chaos = new Chaos({ apiKey: process.env.CHAOS_API_KEY! });
 
  const response = await chaos.chat.responses.create({
    model: WALLET_MODEL,
    input: [
      {
        type: 'message',
        role: 'user',
        content: 'Deposit 5 ETH into Aave and show my updated positions.',
      },
    ],
    metadata: {
      user_id: 'user-123',
      session_id: 'session-abc',
      wallets: [{ address: '0xABC...', chain: 'ethereum' }],
    },
  });
 
  // Step 1: Check status
  if (response.status === 'failed') {
    console.error('Request failed:', response.error);
    return;
  }
 
  console.log(`Response ${response.id} (model: ${response.model})`);
 
  // Step 2: Extract and display text
  const text = extractText(response);
  if (text) {
    console.log('\n--- AI Response ---');
    console.log(text);
  }
 
  // Step 3: Summarize blocks
  const counts = countBlocksByType(response);
  console.log('\n--- Block Summary ---');
  for (const [type, count] of Object.entries(counts)) {
    if (count > 0) console.log(`  ${type}: ${count}`);
  }
 
  // Step 4: Process tables
  const tables = extractTableBlocks(response);
  for (const table of tables) {
    console.log(`\nTable: ${table.title ?? '(untitled)'}`);
    const rows = tableToObjects(table);
    console.table(rows);
  }
 
  // Step 5: Process charts
  const charts = extractChartBlocks(response);
  for (const chart of charts) {
    console.log(`\nChart: ${chart.title ?? '(untitled)'} [${chart.type}]`);
  }
 
  // Step 6: Evaluate transaction risks
  const txBlocks = extractTransactionBlocks(response);
  if (txBlocks.length > 0) {
    console.log(`\n--- Transactions (${txBlocks.length}) ---`);
    console.log('Highest risk:', getHighestRiskLevel(response) ?? 'none');
 
    if (hasBlockers(response)) {
      console.log('\nBLOCKERS:');
      for (const b of getAllBlockers(response)) {
        console.log(`  - ${b.title}: ${b.message}`);
      }
    } else if (hasRisks(response)) {
      console.log('\nWARNINGS:');
      for (const w of getAllWarnings(response)) {
        console.log(`  - ${w.title}: ${w.message}`);
      }
    } else {
      console.log('Risk assessment: LOW -- safe to proceed');
    }
  }
}
 
analyzeResponse().catch(console.error);

Quick Reference

FunctionReturnsDescription
extractText(response)stringAll text content joined by newlines
extractBlocks(response)Block[]All blocks of any type
extractTableBlocks(response)TableBlock[]Table blocks only
extractChartBlocks(response)ChartBlock[]Pie, bar, and timeseries chart blocks
extractTransactionBlocks(response)ActionBlock[]Transaction action blocks
extractMarkdownBlocks(response)MarkdownBlock[]Markdown blocks
extractInteractiveBlocks(response)InteractiveBlock[]Interactive blocks
extractInfoBlocks(response)AlertBlock[]Alert/info blocks
extractCodeBlocks(response)CodeBlock[]Code blocks
extractFormBlocks(response)FormBlock[]Form blocks
hasRisks(response)booleanAny transaction with risk above "low"
hasBlockers(response)booleanAny transaction with high/critical risk or blockers
getAllWarnings(response)RiskInfoItem[]All warnings across transactions
getAllBlockers(response)RiskInfoItem[]All blockers across transactions
getHighestRiskLevel(response)RiskLevel | undefinedHighest risk: "low" | "medium" | "high" | "critical"
countBlocksByType(response)BlockTypeCountsCount of each block type
hasBlocks(response)booleanWhether the response contains any blocks
hasBlockType(response, type)booleanWhether the response contains a specific block type
Was this helpful?