Gemini API: Text Summarization

You will use Gemini API’s JSON capabilities to extract characters, locations, and summary of the plot from a story.

Setup

Install the Google GenAI SDK

Install the Google GenAI SDK from npm.

$ npm install @google/genai

Setup your API key

You can create your API key using Google AI Studio with a single click.

Remember to treat your API key like a password. Don’t accidentally save it in a notebook or source file you later commit to GitHub. In this notebook we will be storing the API key in a .env file. You can also set it as an environment variable or use a secret manager.

Here’s how to set it up in a .env file:

$ touch .env
$ echo "GEMINI_API_KEY=<YOUR_API_KEY>" >> .env
Tip

Another option is to set the API key as an environment variable. You can do this in your terminal with the following command:

$ export GEMINI_API_KEY="<YOUR_API_KEY>"

Load the API key

To load the API key from the .env file, we will use the dotenv package. This package loads environment variables from a .env file into process.env.

$ npm install dotenv

Then, we can load the API key in our code:

const dotenv = require("dotenv") as typeof import("dotenv");

dotenv.config({
  path: "../../.env",
});

const GEMINI_API_KEY = process.env.GEMINI_API_KEY ?? "";
if (!GEMINI_API_KEY) {
  throw new Error("GEMINI_API_KEY is not set in the environment variables");
}
console.log("GEMINI_API_KEY is set in the environment variables");
GEMINI_API_KEY is set in the environment variables
Note

In our particular case the .env is is two directories up from the notebook, hence we need to use ../../ to go up two directories. If the .env file is in the same directory as the notebook, you can omit it altogether.

│
├── .env
└── examples
    └── json_capabilities
        └── Text_Summarization.ipynb

Initialize SDK Client

With the new SDK, now you only need to initialize a client with you API key (or OAuth if using Vertex AI). The model is now set in each call.

const google = require("@google/genai") as typeof import("@google/genai");

const ai = new google.GoogleGenAI({ apiKey: GEMINI_API_KEY });

Select a model

Now select the model you want to use in this guide, either by selecting one in the list or writing it down. Keep in mind that some models, like the 2.5 ones are thinking models and thus take slightly more time to respond (cf. thinking notebook for more details and in particular learn how to switch the thiking off).

const tslab = require("tslab") as typeof import("tslab");

const MODEL_ID = "gemini-2.5-flash-preview-05-20";

Example

const response = await ai.models.generateContent({
  model: MODEL_ID,
  contents:
    "Generate a 10 paragraph fantasy story. Include at least 2 named characters and 2 named locations. Give as much detail in the story as possible.",
});

tslab.display.markdown(response.text ?? "");

The morning mists, usually swirling with the sweet scent of honeysuckle and damp earth, clung heavy and acrid around Elara as she ventured deeper into Whisperwind Glade. Her worn leather satchel, filled with freshly gathered moonpetal and sunroot, felt strangely light, a contrast to the growing weight in her stomach. Elara, an apprentice of the Grove Keepers, felt the ancient forest’s distress in her very bones. The vibrant emerald leaves of the elder-oaks were tinged with a sickly grey, and the usually melodious calls of the wind-whisper birds were replaced by an eerie silence. Something profoundly wrong was seeping into the heart of their sacred woods, an unnatural stillness that stole the vitality from everything it touched.

Just beyond the Glade’s central nexus, where the great Mother Tree hummed with life, Elara found the source of her unease. A patch of earth, the size of a small clearing, had been utterly consumed. The rich soil was cracked and dessicated, resembling a scorched desert, yet there was no charring, no ash, only an absence of life so absolute it felt like a scream. The very air around it was thin and cold, and a faint, almost imperceptible shimmer, like heat haze but colder, distorted the distant trees. This wasn’t a natural blight; this was an insidious drain, a parasitic void that whispered of ancient, forgotten terrors from the dawn of time.

Her mentor, Lyra, was away on a pilgrimage to the Sunstone Monasteries, leaving Elara as the most senior Keeper within the Glade. The problem was far beyond her fledgling skills with restorative spells and herbal poultices. This was a wound not just to the land, but to the very fabric of magic itself. She remembered whispered tales from Lyra, stories of a creeping shadow that had once threatened to consume the world, driven back only by the combined might of ancient druids and warrior-mages. A cold dread settled over her, chilling her even more than the blighted air. She needed help, and she knew just the recluse who might possess the forgotten knowledge required to face such an enigma.

Her journey led her through the northern fringes of Whisperwind Glade, where the towering spires of ancient firs gave way to craggy outcrops and the first signs of harsher terrain. She followed a barely visible game trail, a path only known to those intimately familiar with the land. After nearly a day’s travel, as the first stars began to prickle the deepening twilight sky, a wisp of smoke curled from a secluded hollow. There, nestled amongst boulders overgrown with moss, was a small, ramshackle hut. This was the dwelling of Kael, a grizzled ranger whose face was a roadmap of scars and sun-creased lines, and whose eyes held the weary wisdom of a hundred winters.

Kael, cleaning a magnificent longbow carved from dark yew, grunted a greeting as Elara pushed open his creaking door. Without preamble, she laid out the severity of the blight, describing the chilling emptiness, the stolen vitality. Kael listened, his movements ceasing, his gaze sharpening to a hawk-like intensity. “The Shadow Blight,” he murmured, his voice a gravelly rumble. “It’s been centuries. Legend says it feeds on ambient magic, growing stronger, spreading like a disease across the land, until only desolation remains. Its source… it was sealed away long ago in the deepest chasms of the Obsidian Peaks.”

A chill not entirely from the evening air settled over Elara. The Obsidian Peaks were a desolate, perilous range, infamous for their jagged, razor-sharp summits and their reputation as a breeding ground for monstrous creatures and dark, forgotten magic. “Sealed?” she asked, her voice barely a whisper. Kael nodded gravely. “A binding ritual, performed by the First Keepers. But bindings weaken over time. If the Blight is spreading, it means the seal is failing, or worse, has been broken by someone, or something, seeking to unleash it anew.” They would need to venture into that cursed landscape, find the breach, and somehow, stop the drain.

Preparation was swift and grim. Kael exchanged his usual hunting arrows for those tipped with silver, meant for creatures of shadow. He packed dried meat, hardened bread, and a coil of sturdy rope. Elara, meanwhile, consulted her small collection of scrolls, finding ancient glyphs for warding and a potent formula for a light-casting elixir that might pierce the gloom of the peaks. Their supplies were meager, their hope a flickering flame, but the thought of Whisperwind Glade consumed by the blight spurred them onward. As the first light of dawn painted the eastern sky, they set out, their figures small against the towering shadow of the Obsidian Peaks.

The transition from the verdant slopes bordering Whisperwind Glade to the stark, unforgiving landscape of the Obsidian Peaks was jarring. The air grew thin and cold, carrying the sharp scent of volcanic rock and something metallic, like old blood. Jagged, obsidian spires clawed at the bruised sky, their surfaces gleaming with an unnerving, polished sheen. Loose scree shifted treacherously beneath their boots, and the wind, unimpeded, howled like a mournful spirit through the narrow canyons. They passed skeletal trees, their branches twisted into grotesque sculptures, and heard the distant, guttural cries of creatures best left undisturbed in the deeper ravines.

After three arduous days of climbing and navigating treacherous passes, they found it: a colossal fissure, jagged and black, splitting the very heart of the highest peak. The air here was thick with the suffocating emptiness of the blight, and the shimmering distortion was magnified, swirling with visible darkness. Deep within the chasm, nestled amidst ancient, crumbling runes that once glowed with protective light, was a pulsating, obsidian heart, roughly the size of a small boulder. It pulsed with a sickening, rhythmic throb, drawing all life and light into itself, radiating the cold, hungry void they had witnessed in Whisperwind Glade. This was the source, the ancient weapon of a forgotten evil, slowly awakening.

They acted in grim unison. Kael, drawing upon the forgotten lore he carried, began to chant, his voice echoing eerily in the vast chasm, while he positioned silver-tipped arrows into the weak points of the pulsing heart. Elara, her hands glowing with soft, emerald light, began to weave a complex web of binding spells, drawing on the remaining vitality of her own spirit and the very essence of the life-giving herbs she carried. The heart pulsed harder, emitting a shrill, piercing shriek that threatened to shatter their resolve. But Kael’s arrows struck true, embedding themselves deep, and Elara’s intricate web of magic tightened, slowly, painfully, coiling around the obsidian core. The screams of the Shadow Blight diminished, replaced by a low, defeated thrum.

The pulsating heart finally quieted, encased in Elara’s shimmering green bonds, its voracious hunger quelled, though not destroyed. Exhausted but triumphant, Kael and Elara watched as the suffocating gloom began to recede, replaced by the faint, crisp scent of clean air. The journey back to Whisperwind Glade was still long, but it was undertaken with a renewed sense of purpose. The threat was contained, for now. Elara knew the Shadow Blight was merely sleeping, its malevolent heart still beating, waiting for another chance to rise. But she also knew that the Glade had defenders, and that even the deepest darkness could be held at bay by the light of courage and ancient knowledge.

import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const characterSchema = z.object({
  name: z.string(),
  description: z.string(),
  alignment: z.string(),
});

const locationSchema = z.object({
  name: z.string(),
  description: z.string(),
});

const textSummarySchema = z.object({
  synopsis: z.string(),
  genres: z.array(z.string()),
  characters: z.array(characterSchema),
  locations: z.array(locationSchema),
});

const responseSchemaJson = zodToJsonSchema(textSummarySchema);
const prompt = `
  Generate summary of the story. With a list of genres locations and characters.
  ${response.text ?? ""}
`;
const summaryResponse = await ai.models.generateContent({
  model: MODEL_ID,
  contents: prompt,
  config: {
    responseMimeType: "application/json",
    responseJsonSchema: responseSchemaJson,
  },
});
console.log(JSON.stringify(JSON.parse(summaryResponse.text ?? "{}"), null, 2));
{
  "characters": [
    {
      "alignment": "Good",
      "description": "An apprentice of the Grove Keepers, she is observant, courageous, and possesses growing magical abilities, determined to protect her sacred home.",
      "name": "Elara"
    },
    {
      "alignment": "Good",
      "description": "A grizzled, reclusive ranger with deep knowledge of ancient lore and a formidable warrior, he possesses weary wisdom and helps Elara in her quest.",
      "name": "Kael"
    },
    {
      "alignment": "Good",
      "description": "Elara's mentor and a senior Grove Keeper, currently away on pilgrimage, her teachings and whispered tales guide Elara.",
      "name": "Lyra"
    }
  ],
  "genres": [
    "Fantasy",
    "Dark Fantasy",
    "Adventure",
    "Quest"
  ],
  "locations": [
    {
      "description": "A sacred, vibrant forest known for its elder-oaks and wind-whisper birds, it becomes the initial victim of the Shadow Blight.",
      "name": "Whisperwind Glade"
    },
    {
      "description": "A small, ramshackle dwelling nestled amongst boulders in the northern fringes of Whisperwind Glade, serving as Kael's secluded home.",
      "name": "Kael's Hut"
    },
    {
      "description": "A desolate and perilous mountain range characterized by jagged, razor-sharp summits and dark, forgotten magic, infamous as the source of the Shadow Blight.",
      "name": "Obsidian Peaks"
    },
    {
      "description": "A colossal, jagged black split in the heart of the highest peak within the Obsidian Peaks, serving as the chamber where the malevolent obsidian heart of the Shadow Blight is sealed.",
      "name": "The Fissure"
    }
  ],
  "synopsis": "Elara, an apprentice Grove Keeper, discovers a devastating 'Shadow Blight' consuming life and magic in Whisperwind Glade. Unable to combat it alone, she seeks out Kael, a reclusive ranger with ancient knowledge. Kael identifies it as a legendary blight whose source, an 'obsidian heart', was sealed long ago in the perilous Obsidian Peaks. Realizing the seal is failing, Elara and Kael journey to the Peaks, brave its dangers, and locate the pulsating heart within a colossal fissure. Combining Kael's lore and silver-tipped arrows with Elara's binding spells, they successfully contain the malevolent source, though they know the blight is only dormant, not destroyed."
}

Summary

In this example, you used the Gemini API to extract key information from a story. This information could be fed into a structured database or used as a prompt for other writers to create their own versions.

This technique of converting large open-ended text to structured data works across other formats too, not just stories.

Please see the other notebooks in this directory to learn more about how you can use the Gemini API for other JSON related tasks.