Chapter 18
What Actually Happened? — Full Result Objects
Metadata: tokens, provider, model
Every generate_text() call in this book has given you exactly one thing: the answer. That’s been enough so far. But at some point you’ll want to know more, how many tokens that specific call used, which AI provider actually handled it, which specific model responded. generate_text() doesn’t tell you any of that. This chapter is about the version of these methods that does.
Think of it like an itemized phone bill
Every generation method has a _result version. generate_text_result() instead of generate_text(). generate_image_result() instead of generate_image(). Same prompt, same configuration, only the ending of the method name changes.
Here’s the plain version:
$text = wp_ai_client_prompt( 'Write a haiku about WordPress.' )
->generate_text();
generate_text() hands you a string, nothing else. It’s a phone call where you get your answer and hang up, no record of how long it took, which carrier connected you, or who you actually spoke to.
Now the exact same call, _result version:
$result = wp_ai_client_prompt( 'Write a haiku about WordPress.' )
->generate_text_result();
Same prompt. Instead of a plain string, you get back an object, called GenerativeAiResult, that holds the answer and an itemized bill for the call that produced it. You never build one of these yourself, it’s handed back to you automatically, the same way generate_text() hands you a plain string without you constructing anything.
Reading the bill
Three methods on that object matter:
getTokenUsage(), how many tokens the call used, the “minutes” line on the bill.getProviderMetadata(), which connector answered, the “carrier” in this metaphor, OpenAI, Gemini, or Anthropic.getModelMetadata(), which specific model, the “agent” who actually took the call.
Called directly, with nothing else going on, that’s just three lines:
$token_usage = $result->getTokenUsage();
$provider_metadata = $result->getProviderMetadata();
$model_metadata = $result->getModelMetadata();
Each one returns its own object with more detail inside it, which the full example further down actually prints out.
Getting the haiku itself back out
The GenerativeAiResult object doesn’t hand you the text directly. It holds a message made up of parts, and each part could be text or a file. You loop over the parts and check which kind you’re holding:
foreach ( $result->toMessage()->getParts() as $part ) {
if ( null !== $part->getText() ) {
echo wp_kses_post( $part->getText() );
}
}
A haiku is plain text, so this only needs to check for text parts. The full example below wraps this loop in try/catch, since calling a method that doesn’t behave the way you expect throws a genuine PHP error, the kind is_wp_error() alone doesn’t catch.
Seeing the bill for real
Create chapter-18-full-result-objects.php inside includes:
<?php
/**
* Chapter 18: What Actually Happened?, Full Result Objects
* Usage: add [ai_course_ch18] to any page or post to see the output.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No direct access.
}
function ai_course_ch18_full_result() {
$result = wp_ai_client_prompt( 'Write a haiku about WordPress.' )
->generate_text_result();
if ( is_wp_error( $result ) ) {
return 'Could not generate right now: ' . esc_html( $result->get_error_message() );
}
$text = '';
try {
foreach ( $result->toMessage()->getParts() as $part ) {
if ( null !== $part->getText() ) {
$text .= $part->getText();
}
}
} catch ( Throwable $e ) {
return 'Something went wrong reading the response: ' . esc_html( $e->getMessage() );
}
$output = '<p>' . wp_kses_post( $text ) . '</p>';
$output .= '<p><strong>The bill:</strong></p>';
$output .= '<pre style="white-space:pre-wrap; background:#f0f0f0; padding:12px;">';
$token_usage = $result->getTokenUsage();
$provider_metadata = $result->getProviderMetadata();
$model_metadata = $result->getModelMetadata();
$output .= 'Token usage: ' . esc_html( print_r( $token_usage, true ) ) . "\n";
$output .= 'Provider: ' . esc_html( print_r( $provider_metadata, true ) ) . "\n";
$output .= 'Model: ' . esc_html( print_r( $model_metadata, true ) );
$output .= '</pre>';
return $output;
}
add_shortcode( 'ai_course_ch18', 'ai_course_ch18_full_result' );
Add [ai_course_ch18] to a page and load it. You get the haiku up top, same as always. Below it, a plain text dump of everything the call actually involved.
That dump is raw PHP, print_r() printing whatever’s actually inside those three objects, not a polished display. Skim past the PHP object syntax and look for the numbers and names, you’ll spot the token counts and the provider/model names without much trouble.
For reference, here’s what those three objects actually hold, confirmed from a real response:
getTokenUsage():promptTokens,completionTokens,totalTokens.getProviderMetadata(): anidandname("openai"/"OpenAI", for example), plus a few other descriptive fields.getModelMetadata(): the model’s ownidandname("gpt-5.4", for example), plus which capabilities and options that specific model supports.
Where this comes back
Two chapters coming up soon put this to direct use.
Chapter 19 covers requesting a specific model without requiring it. The way you’ll actually confirm which model answered is this same getProviderMetadata()/getModelMetadata() pair.
Chapter 20 builds a REST endpoint for AI features. A GenerativeAiResult can be handed straight to WordPress’s REST response helper with no extra work, useful the moment you’re building an endpoint around one.
Try it yourself
Run this chapter’s shortcode a few times and compare the token counts. Try a short prompt against a longer, more detailed one, and watch the numbers actually move. If your connected model does any invisible reasoning before answering, check whether a “thinking” token count shows up too.