Feeding a massive text like Dracula into a 9B model requires a sliding window
approach. Gemma 2 9B has an 8k context window, meaning if you dump the whole
book in at once, it will either truncate the prompt or crash with an
out-of-memory error.
Testing one page (or about 500-800 words) at a time from the browser is the
perfect way to calibrate the pipeline before writing an automated ingestion
loop.
Because you are calling a local cluster over a mesh network IP
(100.111.184.107), you will hit Cross-Origin Resource Sharing (CORS) blocks if
you fire requests directly from a browser dashboard. To make the browser
script work, you must start your Ollama node with the environment variable
OLLAMA_ORIGINS="*".
Here is a browser-ready JavaScript function utilizing the OpenAI-compatible
/v1/chat/completions endpoint provided by Ollama. It wraps the Pass 1 prompt
we designed and enforces JSON output.
const OLLAMA_BASE_URL = "http://100.111.184.107:8080/v1";
const MODEL_NAME = "gemma-2-9b-it";
const API_KEY = "sk-local-no-key";
* Sends a single chunk of text to the local Gemma cluster to extract spatial
data.
* @param {string} pageText - The current page/chunk of the book.
* @param {string} previousContext - A summary of the last room to maintain
map continuity.
*/
async function extractTopologyPass(pageText, previousContext = "None") {
const systemPrompt = `You are a world-building spatial engine. Your task
is to read a chunk of narrative text and extract a topological map of the
locations described.
Rules:
1. Identify distinct rooms, areas, or zones mentioned in the text.
2. Create an uppercase ID for each (e.g., CH1_LIVING_ROOM).
3. Determine a logical grid size (w and h) for the 2D map.
4. Identify obvious physical connections (exits) and the cardinal
direction.
5. Output strict JSON with a root 'zones' array. Do NOT include characters
or events.`;
const userPrompt = `Previous Location Context: ${previousContext}
Current Text to Scan:
${pageText}`;
try {
console.log(`Sending page to ${MODEL_NAME}...`);
const response = await fetch(`${OLLAMA_BASE_URL}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: MODEL_NAME,
// Setting a low temperature forces the model to be
deterministic and stick to extraction
temperature: 0.1,
// If your Ollama/Gemma version supports strict JSON mode:
response_format: { type: "json_object" },
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt }
]
})
});
if (!response.ok) {
throw new Error(`Cluster Error: ${response.status}
${response.statusText}`);
}
const data = await response.json();
const extractedJson = JSON.parse(data.choices[0].message.content);
console.log("Extraction Complete:", extractedJson);
return extractedJson;
} catch (error) {
console.error("Pipeline failure:", error);
}
}
/ Example usage to hook into your prototype UI:
const testPage = `He walked out of the dusty living room and moved north into
a dark hallway.
The hallway was narrow and reeked of damp earth, eventually terminating into a
ruined kitchen.`;
/ extractTopologyPass(testPage, "Currently in: The Dusty Living Room");
The "Overlap" Strategy for the Full Book
When you move past testing a single page and start iterating through a text
file of Dracula, you cannot chop the text blindly by character count. If a cut
happens in the middle of a sentence describing a door, Gemma will miss the
exit.
You will need an iterator that chunks the book by paragraphs (splitting by
) until it hits a target word count (e.g., 600 words). Furthermore, you must
feed the model a previousContext variable. If page 14 describes a hallway, and
page 15 describes opening the door at the end of it, injecting Previous
Location Context: Hallway ensures Gemma links the new room to the existing
exit graph rather than hallucinating an isolated map fragment.