Neural narratives in Python #16

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

As I mentioned recently, I realized that I hadn’t taken into account that all stories that would play out through this app wouldn’t take place in the same “world,” which at some point it had come to mean mechanically a single planet. Rather obvious in retrospect. So I had to program in the overarching notion of a Story Universe. Along the way, I realized I would need to tinker with plenty of other parts of the app that I dreaded touching, mainly the chat system and the classes that interact with the large language models (the AI). Those were the first ones I programmed in, so they were a mess. Thanks to the vulture library, that finds unused code in a project, I’ve removed every single remnant of the initial implementation, and now the chat system is very sturdy.

I intended to write an app focused on offering the user (mainly me) the choice to experience stories through it, but I was spending plenty of time programming code to interact with the AI and parse out its responses. I figured that there should be some industry-standard libraries out there to save me the trouble, and indeed there are two: pydantic and instructor. Pydantic lets you create “response models” without having to write JSON files; it handles all the parsing by itself, programmed by far more intelligent people. Instructor wraps the OpenAI API and offers compelling functionalities on top of it, such as retries, validators, streaming, etc. Check out its website.

Anyway, it has taken me days to return the app to a usable state, but now it’s far more robust, bolstered by about 400 tests, and the changes will allow me to introduce new actions, pages, etc. far faster.

Anyway, let’s get back to my first serious playthrough, focused on exploring the edges of the system to figure out what stuff I should program in.

My protagonist meets with his two allies, his partner and Elizabeth’s distraught father, in the rainy streets of Providence.

A few days ago I programmed in a new type of action, named Gather Supplies. As I mentioned in the past, every kind of action that could come up in a story, and that wouldn’t get resolved through a dialogue, I intend to program it in. Here is the narrative and the outcome of the team’s efforts to gather supplies for this endeavor.

As with any other action, the AI is prompted to enter the player and their followers’ personalities and general resourcefulness into the equation when coming up with the resolution.

Both teams gather at night in the outskirts of Providence, ready for a trip into the unknown. Elara has brought an acquaintance of hers, a middle-aged professor named Thaddeus Armitage.

Are those fused fingers I spy? Quite eldritch, I’d say.

Next up, adding a new area to the map and traveling to it, which will involve an audible narrative.

Life update (10/22/2024)

Tomorrow I start a vacation period that could last until early December, but the whole thing may end up getting cut short. These are the circumstances: I’m covering for a nutcase who goes on medical leaves constantly. I’ve been covering his latest leave for close to a year. In my country, if you extend your leave for more than a year, you’ll get transferred to Social Services, where you’ll be forced to do interviews with them and have some administrative issues. People usually want to avoid that, so I expect this person whom I have to refer to as my coworker to return a day before his leave reaches a whole year. That’s October 31st.

If the guy returns, my contract will end. I’ll get paid for the unspent vacation time (because I already scheduled it; wouldn’t have gotten paid for them otherwise), but that means that I’ll be unemployed, and I may get called into work right that day as part of a new contract. Best case scenario for me is if this coworker keeps working throughout November.

I really need time off in general, but even more so because I haven’t been doing well physically. As I posted some time ago, during a period of sustained stress for months, I suffered what a neurologist referred to as a “complex migraine,” likely a hemiplegic migraine: I was experiencing flashes of darkness in my right eye, and during the attack, I lost sensitivity in the right half of my body. I’m quite sure that I also caught a “burnt smell” at the time. The young doctor diagnosed me with a migraine because I had a history of migraines (that had ended since I started taking beta-blockers for my heart issues), and because of the visual aura. I’m quite sure he also said something to the effect of, “You’re too young to have strokes.” I should also be too young to have arrhythmia, or any of the other shit I’ve ended up burdened with, but here I am.

However, the visual aura never went away entirely, and a couple of weeks ago or so it developed into a torn retina. That got treated, leaving me, however, with permanent “floaters.” But it made me think that what I suffered at work wasn’t a migraine, but a stroke. I feel like I haven’t recovered fully from that episode, that I’m clumsier, more forgetful, and “off” in general since. I don’t know if you can spot brain damage in an MRI or if they just assume the kind of brain damage given the symptoms, but in any case, I have a visit scheduled for November 6th, that I hope will end up either confirming brain damage or giving me good news.

I must add that I have very little confidence in the medical profession, or at least as it stands now. I deal with many nurses and doctors on a regular basis. More often than not, the nurses are the chatty, dumb, “tactile” type, and the doctors are very often egocentric and have something of a god complex. I am vaccine-injured thanks to Moderna, and have visited three cardiologists for it. The first one, annoyed, denied that the covid vaccines caused any heart issues. The second, close to retirement, seemed ashamed of the whole thing, and admitted that the covid vaccines indeed were causing heart damage; he told me that he had treated lots of young women who had ended up in his office because they had acquired Postural Orthostatic Tachycardia Syndrome (POTS) after covid or the vaccines. This doctor, however, behaved as if admitting that the vaccines caused any issue was a huge taboo. A third cardiologist said that indeed the vaccines were associated with heart damage, but that there weren’t enough studies to prove that they caused the problem or if they just triggered a predisposition toward having that problem. “What came first, the chicken or the egg?”. Hey, remember when you jabbed millions upon millions of people with an experimental treatment without having enough studies to prove their effects?

I’m my spare time, I’m working on my Flask/Python app neural-narrative, that allows the user to chat with characters controlled by large language models, and do some other narrative stuff. I’m very pumped to work on it, aching to return home and keep programming.

Recently I decided to add the overarching notion of Story Universes to the hierarchy of places of a story, and that led me to tinker with other stuff. I have yet to finish returning the app to normal, in a significant part because I added a new type to ensure that strings that should have content indeed had it. In retrospect, that was a mistake, because it would have been enough with validating the content of the string at times and throwing a ValueError if the validation failed, so I’ll have to get busy reverting those changes.

Good news is that I’ve racked up about 400 pytest tests to ensure the proper behavior of those parts of the code. All the tests are written by versions of OpenAI’s Orion preview model, which are wonderful for routine work that is very useful but annoying to write. Of course, preparing a part of the code so that it can be testable necessarily forces you to ensure its code quality (using dependency injection, adhering to the Single Responsibility Principle, etc.). However, I also have a God Class lingering around, one that handles everything related to the file system, that I’ll need to chop up and test soon.

OpenAI recently released an initial version of a “swarm of agents” framework, that will allow you to easily set up chains of responsibility and action with an arbitrary number of AI agents. That has gotten me thinking about setting up a writers’ room page in my app in which you could speak in natural language, and different AI agents specialized in writing, analyzing lore, considering character development, etc. would work on whatever aspect of the ongoing story you want to touch. It could work pretty much like a real writers’ room, but without the nasty ego and other human aspects.

I’ll try to visit new places during this vacation time, although I’m limited by my lack of a car, poor stamina, IBS, etc. Thankfully I have plenty of money. I’m also aching to get lost in a good game, and I have eyed that new JRPG by the Persona dudes, Metaphor: ReFantazio, quite lustfully. I’m constantly reading similar stories (although usually with an isekai bent), and most Western games are falling one after the other to the ESG and DEI rot, so you can’t rely on those. However, I don’t know if I can justify to myself playing games when I’m constantly juggling creative projects.

Neural narratives in Python #15

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

At the end of the previous part, my player character, a police detective, convinced an arrogant scholar and a mysterious young student to cooperate on messing with interdimensional rifts to find a missing teen.

The young student mentioned that they would gather supplies. That sounded like an action that my player character and his companions should be able to perform. A section of my app already allowed the user to perform Investigate and Research, so I created a whole new action for gathering supplies.

I relied on OpenAI’s Orion preview model to refactor the whole process of creating a new action type to the utmost, so now it will be very easy to add new ones.

More importantly, I pictured what could be in the horizon for this little test story. Will they walk through a rift into another dimension? How the hell would that work mechanically? My app tracks the combination of places where any character is located. The hierarchy goes world > region > area > location. Characters can be present in areas (some space the size of a town) or in locations (the size of a building or complex of related buildings). But what if the characters step from Providence into another dimension? Is that still set in the world of this story, that originally was intended to refer to a planet? Could that other dimension be squeezed mechanically into being in an alternate 1920s Earth?

Clearly I was missing something, so I thought about it until I came with the notion that the hierarchy of places should be contained inside a new notion, not exactly a place, called Story Universe. This new dimension would belong to the overarching story universe, but not be connected to the same world. This is very important, because dialogues, actions, and such get fed the hierarchy of places and their descriptions as information.

This new Story Universe for my ongoing story encapsulates the notion that rifts to other dimensions can open by themselves or be torn open, and that the beings of many dimensions have to deal with that nonsense.

I ended up having to rewrite the code for every part that involves creating places, because they had to take into account that they all stemmed from, however remotely, a particular Story Universe. I made a new page to handle the generation of places.

These changes allowed me to create the following hierarchy of deranged places for a story or a myriad of them:


Story Universe: The Whims of Divinity

In the universe of ‘The Whims of Divinity’, the gods reign supreme, wielding the power to bestow extraordinary abilities upon the creatures they deem worthy. This universe operates on the fundamental principle of divine intervention, where the gods’ actions and decisions shape the course of events. The unique aspect of this universe lies in the unpredictable nature of the gods’ choices, as they grant powers based on their ever-changing whims. The creation myth speaks of the gods molding the universe from the very fabric of their desires, breathing life into beings and worlds through their divine will. The structure of reality consists of multiple realms, each governed by a different god, with the mortal realm at the center. The gods observe the mortal realm, seeking individuals or creatures that capture their interest, granting them powers that can range from elemental control to reality-bending abilities. The themes explored within this universe revolve around the struggle between free will and predestination, the nature of power and responsibility, and the capriciousness of divine favor. The whims of the gods create a world where heroes can rise from the most unlikely of places, where villains may be driven by the very powers they possess, and where the balance of the universe hangs on the unpredictable decisions of the divine. Stories within this universe delve into the lives of those blessed or cursed by divine intervention, exploring how these powers shape their destinies and the world around them. The possibilities for conflict, exploration, and discovery are endless, as characters navigate a reality where the gods’ whims can change everything in an instant.

World: Memmedom

Memeddom is a world governed by the eccentric, deranged god of memes. The geography of Memeddom is a patchwork of vibrant, chaotic landscapes, ranging from the glittering Meme Mountains to the vast Echo Chambers, where endless loops of soundbites resonate. The flora and fauna have evolved to mimic popular memes, with creatures like the Doge Deer and plants like the LOLlius flowers. The god’s preoccupation with creating and sharing memes has led to a culture obsessed with viral content and fleeting fame. The dominant language, MemeSpeak, consists of a mixture of images, emojis, and abbreviated text. The economy is based on the currency of ‘Likes,’ where citizens earn and spend Likes on goods and services. Resources in Memeddom are memes themselves, with rare and viral memes being the most valuable. The world is ruled by an Influencer Monarchy, where the most popular and influential meme creators hold power. Political tensions arise between different meme factions, each vying for control over the ‘MemeStream,’ the main source of meme distribution. The dominant religion is the ‘Church of the Viral,’ which worships the god of memes and seeks to spread his influence across the world. The society is divided into classes based on online popularity, with the most liked individuals forming the elite ‘Trending’ class. The inhabitants of Memeddom are a mix of humans, talking animals, and emoji-like creatures, all constantly bombarded by the god’s memes. While some embrace the chaos, others long for a world free from the tyranny of viral content.

Region: Doomerscape

Doomerscape is a vast, desolate region marked by its bleak, grayscale landscape and perpetual overcast skies. The climate is chilly and damp, with a constant drizzling rain that casts a gloomy pallor over the land. The most notorious geographical feature is the Sea of Sorrows, a massive, stagnant body of water that is said to be made from the tears of the Doomer god himself.

The inhabitants of Doomerscape are a melancholic bunch, known as the Doomered. They are a mix of humans, animals, and creatures that have been transformed by the pervasive Doomer energy that permeates the region. The Doomered are characterized by their pessimistic outlook on life, their love of black coffee, and their tendency to wax philosophical about the futility of existence. They live in small, scattered communities, preferring to keep to themselves and avoid the temptations of fleeting meme fame.

The architecture of Doomerscape is stark and minimalist, with buildings made of dark, rough-hewn stone and adorned with mournful sculptures of weeping figures. The most iconic structure is the Tower of Lament, a towering spire that houses the governing body of the region, the Council of Despair. This council is made up of the most world-weary and cynical of the Doomered, who rule with a laissez-faire approach, believing that all attempts at control are ultimately futile.

The economy of Doomerscape is based on the mining of rare, black crystals known as Sorrow Stones. These stones are highly sought after for their ability to absorb and store negative emotions, making them valuable to the Doomered for their emotional catharsis. The Sorrow Stones are traded with neighboring regions, who use them in various applications, from powering melancholic machinery to creating mood-altering elixirs.

Despite its dreary reputation, Doomerscape is not without its defenders. The Doomered are known for their fierce, if somewhat apathetic, fighting style, which involves a lot of sighing and eye-rolling as they battle. They are led by the Knights of the Woeful Countenance, a group of warriors who have mastered the art of combat while maintaining an air of resigned indifference.

The spiritual beliefs of the Doomered center around the worship of the Doomer god, a deity of despair and ennui. They believe that by embracing the futility of existence, they can achieve a state of enlightenment known as ‘Peak Doomer.’ This belief system is reflected in the region’s cultural aesthetics, which emphasize the beauty of sadness and the nobility of giving into one’s darker emotions. The Doomered have a complex social hierarchy based on one’s level of existential angst, with those who have achieved Peak Doomer status being the most revered.

Area: Mournville

Mournville is a small, gloomy town located in a shallow valley, surrounded by rolling hills of pale gray grass and withered trees. The landscape is dotted with spent matchsticks and cigarette butts, remnants of the town’s primary industry and pastime.

The soil in Mournville is rich in a rare mineral called Ashenite, which is used to fertilize the tobacco fields that dominate the countryside. The tobacco grown here is of a unique variety known as Melancholia, prized for its smooth, rich flavor and the way it seems to enhance the melancholic mood of the smoker.

The town is inhabited by a mix of humans and a peculiar species of crow that has evolved to thrive in the smoky environment. These crows, known as Sombriels, are revered by the townspeople for their mournful caws and their tendency to steal lit cigarettes from unsuspecting smokers.

Mournville is governed by a council of the town’s oldest and most resigned residents, who meet in the town’s central square to sigh heavily and bemoan the state of the world. They maintain a policy of strict isolationism, believing that the town’s unique atmosphere should be preserved from the corrupting influence of outsiders.

The town’s main cultural event is the annual Festival of Regrets, where the townspeople gather to share their deepest regrets and to smoke an extra-strong blend of Melancholia tobacco. The festival culminates in a procession to the nearby Ashen Hills, where the townspeople release a flock of Sombriels carrying lit cigarettes in their beaks, symbolizing the release of their regrets into the sky.

Location: Despondency Park

Despondency Park is a bleak expanse of withered grass and gnarled trees, cast in the perpetual gloom of a sunless sky. The park is dotted with stark yellow street lights that cast an eerie glow over the few remaining benches where the town’s most resigned citizens gather to smoke and bemoan the pointlessness of life.

The air is thick with the acrid smell of cigarette smoke and the mournful caws of the Sombriels, the peculiar species of crow that inhabit the town. These birds are drawn to the park by the abundance of discarded cigarette butts and the melancholic energy that permeates the area.

The park is a place of deep despair and hopelessness, where the only solace comes from the shared misery of others. It is not uncommon to see groups of citizens huddled together on the benches, their faces etched with the lines of a thousand regrets, as they take long drags from their cigarettes and stare out into the gloom.

Despite its gloomy atmosphere, Despondency Park is a popular spot for those seeking a moment of respite from the world. The park is home to a rare species of flower known as the Melancholy Bloom, which is said to thrive in the park’s unique atmosphere. The flowers are prized for their delicate beauty and the way they seem to absorb the sorrow of those who sit among them.

The park is also a popular spot for the town’s annual Festival of Regrets, where the townspeople gather to share their deepest regrets and to smoke an extra-strong blend of Melancholia tobacco. The festival culminates in a procession to the nearby Ashen Hills, where the townspeople release a flock of Sombriels carrying lit cigarettes in their beaks, symbolizing the release of their regrets into the sky.


Now that the creation of any kind of place, including the overarching Story Universe, has been programmed in, I’ll need to search for every piece of code that references worlds, then make sure to involve the associated Story Universe as well. That will take a while. But it will be very interesting to see how this new hierarchy influences the dialogues and actions performed in the app.

EDIT: the Deep Dive pair at Google NotebookLM had plenty of interesting things to say about this post. Good job sticking the landing, guys.

Neural narratives in Python #14

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

My player character, a detective from Providence’s police department, delved into the kind of ancient tome one shouldn’t even glance at.

The detective feared that Elizabeth may already be lost permanently.

If the missing girl’s whereabouts could be gleaned, I required the help of the one brilliant scholar I had spent most of this playthrough inconveniencing.

That’s all for today storywise.

A section of my app allows the user to generate plot blueprints, scenarios, dilemmas, and goals, to find inspiration. I’ve added the choice to generate plot twists. Apart from that, there was plenty of code to refactor (which is a programming term referring to the arduous task of reworking the code so that the fewest number of instructions in a section of the codebase get repeated, now and in the future, to fight against entropy). That will allow me to add new “concepts” like scenarios, dilemmas, etc. significantly faster in the future.

Neural narratives in Python #13

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

Early next morning, my player character, a world-weary detective, decided to bother some folks at the local university.

I wrote “we still don’t need how” instead of “we still don’t know how.” Just pointing that out because I hate making mistakes.

The detective goes back to report to the missing girl’s impatient father.

I pictured two weary men rummaging through bookcases of ancient, cursed books at the old library, while rain pelted the windows. Rain? There’s no weather in this app. Well, there is now.

Only areas have weather. Locations, which are inside areas, are aware of the weather in their containing area, but can’t be changed from there. All the prompts to the large language model that were aware of the time of the day are now also aware of the weather, and that includes dialogues. We’ll see how much the awareness of weather affects the scenes. I just hope the AI is intelligent enough to realize that it doesn’t rain inside a building.

Anyway, my player character, accompanied by an increasingly unstable father, headed to the old library.

There they met the apparently ancient librarian.

You may have noticed that the narration parts of the dialogue now actually sound like narration parts, instead of being spoken by the character. It took me a while to figure out how to do that, and it involved splitting the original text, generating voice lines for each part, and then concatenating them. But it sounds much better now.

New songs by Colours Run

If you want to know about my story with Tim Cameron and his band Colours Run, check out my previous post about it. In summary: back when I was a teen, until I was eighteen or so, I used to frequent some comedy forums named PWOT, where an English fellow by the name of Tim Cameron, and nickname of Camerhil, posted his songs. I found him brilliant. You had the sense that this guy opened his heart up to you. It’s like he felt he had little time to do that in this world, so he was in a hurry to make it somewhere with his music. In 2007 or so, along with his bandmates, he published an album, and then he disappeared to the US and was never heard from again. I never heard from him again, at least. As far as I can tell, you can’t even buy his works these days.

I used to treasure his songs, but I had lost most of them along the way. It was almost a miracle that I came across a twenty-year-old CD with about eleven songs of his, and that was all I had left. Last night, though, a kind soul gave me an early Christmas present; turns out that there are other former PWOT members out there who loved Tim’s music.

Without further ado, here are the missing Colours Run songs (apart from a few ones that are, let’s say, only for former PWOT members).

From the album Cynical Wonderful:

“On My Side”

“Pilot Light 30-04-07”

“Beautiful Waste of Time”

“Scars”

“The Traveller”

“Perpetual Motion”

“Apathy Ever After”

“Pilot Light”

“Marketplace Crisis”

“Good Night”

“Tethers”

“Song for the Doctor”

From the E.P. The Sticks:

“Old”

“The Sticks”

“Curiosity”

Doghouse Demo:

“Before the War (demo)”

“Chaos Song (demo)”

“Tethers (demo)”

Unknown:

“Pilot Light 1st Master”

“On My Side”

“Fireflies”

“The Snowline”

“Beautiful Waste of Time (demo)”

“Beautiful Waste of Time radio”

“Before the War radio”

2007 demos:

“Birnam”

“Gossamer acoustic”

“Gossamer with vocals”

“Gossamer”

“Methods of Escape (no drums)”

“Methods of Escape”

“Pilot Light 07-02-12”

“Pilot Light 30-04-07”

“Prelude in C”

“The Snowline 16-03-07”

“The Snowline”

“Wichita Lineman”

Now I have more songs by Colours Run and Tim Cameron than I ever did.

Neural narratives in Python #12

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

The previous entry ended with my player character and his partner arresting Elara Thorn, the suspicious scholar.

Well, I’m stumped. The following conversation happened immediately after.

As I tried to figure out what to do, I kept the facts of the story in my head, but why would I need to do that? Am I not programming a page that could handle such stuff? So I programmed it in.

The user has to introduce those facts manually; I can’t think of any reliable way for the large language model to determine them instead. In any case, now that I keep a file of known facts, they are used when prompting the AI for concepts, interesting situations, interesting dilemmas, and goals.

Anyway, my player character and his partner have to inform the missing teenager’s father that the main culprit is going to walk free for now.

No choice but to release the charming miss Thorn from custody.

I am a bit over my head.

Life update (10/14/2024)

My week-long vacation has ended, and I’m writing these pointless words from the office. Back to the grind of fixing issues with printers, giving access to folders, and connecting cables to sockets. I don’t like my job, but it pays, so that’s what I do.

I don’t feel like writing fiction at the moment. I’m always compelled to work on this or that project, but my subconscious is the one holding the reins, and I don’t have any say in it. Most of my brain’s operating time these days has been occupied with thoughts of how to improve my Python project neural-narrative, that allows the users (meaning me and the few people that have cloned the repository) to chat with characters controlled by large language models, and in general engage in roleplaying with a large language model acting as the Dungeon Master. It’s all very exciting. I have been thinking about implementing events, lore books, and plenty of other weird stuff. Shortly after I got to work, I started relaying Hermes 405B my doubts about how some sections of the javascript code underlying my pages worked. I’m a systems builder by personality, and this is one interesting system to build. It certainly helps that at this point of AI development, the characters you can engage with behave like actual human beings, which is a bizarre thing to have gotten accustomed to.

I haven’t done much of note during this vacation time otherwise. I visited Donostia’s aquarium, and got a dose of nostalgia and grief due to my memories of having visited it back in 2021, with my then girlfriend Alazne. It just happens that it never happened: that visit took place in the novel I was writing (My Own Desert Places), and the actual last time I had visited the aquarium happened back when I was a teen or a child (I didn’t even bother to visit the aquarium so I could write the scene; sorry, writing gods). The act of writing a story brands your brain with memories similar to, if not stronger than your actual experiences. I’m not sure what to think about that, but in someone as isolated and generally avoidant of new experiences as myself, it may be a good thing.

A few days ago I went out for an aimless walk. I took a wrong turn and found myself climbing up a steep path. I love checking out new places, but I don’t have a car and I get anxious around human beings, so I can’t stray too far. Anyway, at a solitary stretch of the road, I found an even more deserted place: the cemetery. I realized I had never visited it, so I walked in.

I like cemeteries. They are usually empty, silent, and calm. As I strolled around, I ventured down a staircase and found myself in an underground lair of funeral niches. I thought of checking out the whole place, but I started getting a weird, sinister vibe, the kind that makes you think that you’re going to spot stuff out of the corner of your eye. I wasn’t in the mood to deal with ghosts, so I walked back up.

I spent maybe an hour reading the inscriptions on tombstones and checking out the gift and notes that the deceased’s loved ones had left. I came across the memorial for a girl I used to know, who got murdered by a psych student when she was barely twenty years old, and found out that her father, whom I used to see around in the neighborhood, had died six years earlier, before his time. I found the burial site of a twenty-two year old kid I knew about in my teens; as he rode his bike with his girlfriend seated behind him, he lost control and fell under the wheels of a truck. His girlfriend was unharmed, if you can call “unharmed” to look down at the burst remains of your loved one’s head. The last time I knew of that girl, she was attending the most prominent local disco then (I must have been sixteen or seventeen). She was wearing a T-shirt with the photo of her deceased boyfriend. At some point of the evening, she burst into tears. I don’t know what you do with your life after such a thing happens.

As I read the inscriptions on the tombstones, my mind pictured those people’s lives before they died, mainly the lives of those who died way before their time. One tombstone had etched the death dates of three members of the same family back in the fifties, and two of them were kids aged five and six. A girl with the peculiar name of Ninfa de Amo Díez had died in her early twenties back in the fifties or sixties. When I returned home, I googled that name, but nothing came up; at this point of our civilization, she may as well have never existed. It got me thinking, as I sometimes do, about the point of it all: you live, you fuck around for a while, and then you die. Soon enough, nobody will remember you. I guess the whole point is in the “fuck around for a while” part.

At some point, I felt permeated with a deep sadness. I could barely keep myself from getting teary-eyed. I wasn’t in the mood to start crying in a public place, even though there was nobody around, so I left.

Now that I’ve returned to work and I’m forced to do things I don’t want, I’m getting reacquainted with the notion that my body and brain don’t work as they should. For example, I was supposed to patch a network connection, but I forgot to grab both the keys of the network rack as well as the device that allows you to follow the cables. It simply slipped my mind, as many things have over the last few years. As I was crouching around at the network rack, as soon as I stood up, a buzzing, a sort of sudden dizziness, coursed through my nerves, and it took me minutes to get back to normal. I feel in general like I’m degenerating faster than I should for my age. I have a visit to a neurologist scheduled for the sixth of next month, and I hope to get an MRI done.

I also got my right eye checked out by an ophthalmologist, a couple of weeks after I experienced a torn retina. She told me that the debris and other weird shit that has ended inside my right eye (like a tangle of fibers that keep swaying before my vision) are pretty much there until I die. Wonderful news. She suggested to wear sunglasses outside, because such shadows in my vision are more prominent under the glare of the sun. I’m otherwise recovered from the ordeal.

Anyway, I think that’s all I needed to say at the moment.

Neural narratives in Python #11

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

Without further preamble…

I approached one of the students.

Let’s head to the library, then.

As I was having the big multi-char convo with Elara and two of her pals, as well as the other two people on my side, Elara treated her own companions as if she didn’t know them. Of course, she doesn’t actually know them: a character is only aware of that their bio says as well as the contents of their memories. Those people hadn’t been introduced in their memories yet. That’s an interesting problem to solve.

Of course, I could just make up their relationship by writing directly in their memories, but the whole point of having this app is for the large language model to act as a Dungeon Master. So, I’m going to create a whole new page on the site, one called Connections, that offers the opportunity to generate a relationship between two characters.

Here is the prompt I’ve put together so that the large language model will come up with a compelling, meaningful connection between two characters:

Instructions:

Using the character data provided for {name_a} and {name_b}, generate a meaningful and compelling connection between them. The connection should align with their personal information and memories. Ensure that the relationship is coherent, enriches their backstories, and is consistent with their individual characteristics. The connection does not need to be intimate or romantic; it can be any significant relationship such as friendship, rivalry, mentorship, familial ties, etc.

Character {name_a}:

{character_a_information}

Character {name_b}:

{character_b_information}

Your Task:

Using the above information, write a detailed description (approximately 3-5 sentences) of the connection between {name_a} and {name_b}. The description should:

Explain how they met or became connected.
Highlight the nature of their relationship (e.g., allies, rivals, mentor and protégé, siblings).
Incorporate elements from their personalities, backgrounds, likes, dislikes, secrets, or memories.
Be compelling and add depth to both characters.
Ensure that the connection is believable and enhances the narrative potential of their interactions.

Well, pulling off that Connections page was far quicker than I anticipated, but the app is already quite mature when it comes to examples of how stuff works.

The connection between them (that gets stored as a memory for both characters) isn’t shown to the user, to keep the intrigue. You can, of course, check it out in the JSON files that store the characters, but for storytelling purposes, it’s better to keep the secrecy. Like all other forms I’ve been dealing with, this one interacts with the server via AJAX, without actually reloading the page. Very slick.

As I was checking out how the ongoing dialogue was getting stored, I realized that at some point I had mistakenly stored the characters’ names instead of their descriptions, so I don’t think that the characters were aware of how the others looked as they spoke. This fix will open up plenty of dialogue flavor.

The error was worse than I anticipated. Turns out that when I refactored the gigantic manager class CharactersManager into five or so other classes, one of them a class named Character that handled everything related to character attributes, I had copy-pasted my way into horror: I was returning the property “name” even if what the user demanded was the description, the likes, the dislikes, the personality, etc. So these last couple of days, most of the prompts involving characters, if not all, have been missing about half of the proper data. That’s why you pytest the shit out of your app. Sorry, programming gods.

Anyway, long dialogue incoming:

That’s all for now. The following isn’t related to anything, but I must say it: recently I recommended the anime adaptation of Uzumaki. Not once, but twice. Well, consider that recommendation rescinded. Those motherfuckers really pulled a fast one on us. Dandadan is still cool, though.

Neural narratives in Python #10

I recommend you to check out the previous parts if you don’t know what this “neural narratives” thing is about. In short, I wrote in Python a system to have multi-character conversations with large language models (like Llama 3.1), in which the characters are isolated in terms of memories and bios, so no leakage to other participants like in Mantella. Here’s the GitHub repo.

As I considered my app this morning, a couple of things bothered me considerably: one, the CharactersManager class that handled pretty much any API requests related to characters had ballooned to insurmountable levels. It took me an hour and a half if not more to refactor it into four classes, given how entangled its code was with everything. Now I have a Character class that, provided with an identifier, handles the loading of its own related data, as well as saving it. That simplifies things a lot.

The second matter was far more troubling for me: every time I launched a request to the server that entailed a call to a large language model, the interface went unresponsive potentially for six seconds or more. That bothered me every time. The only real solution that I know is to use AJAX, a method of javascript programming that instead of actually sending a POST request to the server, it sort of simulates one through javascript, then returns the result as JSON data. That only has pros when it comes to the final execution, but reaching that point is troublesome. That’s what I’ve spent most day working on, and fortunately I’ve managed to achieve it for the most part: there are various parts of the app that now display “Processing…” on the button that generated the request, next to a spinner, and they return to normal once the request has been processed. If it entailed generating data that would appear on the page, then it gets displayed as well.

I’ve also changed some of the interface, particularly the buttons to subsections in Characters Hub and the Actions page. I think it looks quite cool.

As I was testing things while implementing AJAX into the chat page, I had the following idiotic conversation with the player character’s partner.

I have processed about 60 voice models of the 550 or so available in the RunPod server, so I’ll be able to enjoy many intriguing conversations through this narrative system (not to mention pretty exceptional smut).

Silliness aside, here are the descriptions of the player character and his posse as they head to the dreaded university.

I asked the AI to generate three more characters that would know or even be intimately connected with the arrogant student Elara Thorn.

That’s a perfectly normal group of people.

Last night, I thought about how this whole confrontation with Elara Thorn may play out. Were we going to accuse her of something and the LLM would play along and admit some wrongdoing? I thought about the notion of a character having a secret that even I wasn’t aware of. So I programmed just that: a section in Characters Hub that generates one or more secrets for any given character. Now all characters are created with some petty secrets of their own, which is bound to enrich their spoken parts, but this Secrets section is bound to generate some pretty hardcore secrets, as per the prompt.

You are to generate the secrets of a character. Use the provided information about the character, the world's conditions, and the specific locations involved.

Instructions:

To create secrets that are compelling and truly worth being hidden for the character, consider incorporating the following aspects:

Deep Personal Impact: The secret should have a profound effect on the character's life, shaping their personality, motivations, and actions. It might relate to a traumatic event, a pivotal choice, or a forbidden desire that they are desperate to keep concealed.
High Stakes and Consequences: The revelation of the secret should carry significant consequences for the character and potentially others. This could include personal ruin, endangerment of loved ones, loss of status, or catastrophic events within the story's world.
Moral Ambiguity: Secrets that involve morally gray areas make characters more complex and intriguing. The character might have done something considered unethical or made a compromise that weighs heavily on their conscience.
Inner Conflict and Guilt: The secret should create internal turmoil. Feelings of guilt, shame, or fear of discovery can drive the character’s behavior, making them more layered and realistic.
Hidden Identities or Double Lives: Characters may be living under an assumed identity or leading a double life. This creates tension as they balance their true self with the facade they present to the world.
Traumatic Past Events: A history involving trauma, such as witnessing or being involved in a catastrophic event, can be a secret that influences their current fears and motivations.
Forbidden Relationships: Involvement in relationships that are taboo or forbidden in their society adds emotional depth and high personal stakes if the secret is revealed.
Betrayal and Loyalty: A secret involving betrayal—whether the character betrayed someone or was betrayed—adds tension, especially if relationships with other characters are at risk.
Hidden Abilities or Powers: Concealing special abilities, especially in a world where such powers might be dangerous or outlawed, adds an element of suspense and fear of exposure.
Secret Motivations or Agendas: The character might have ulterior motives that conflict with their apparent goals or the goals of their allies, creating layers of intrigue.
Connection to Antagonists: A secret tie to the antagonist or antagonistic forces—such as being related to, indebted to, or blackmailed by them—complicates the character's role in the story.
Illegal or Illicit Activities: Participation in criminal activities, whether past or ongoing, provides clear reasons for secrecy and potential consequences if uncovered.
Prophecies or Destinies: Being the subject of a prophecy or destined for a significant role can be a burden the character tries to hide to avoid unwanted attention or responsibility.
Hidden Weaknesses or Vulnerabilities: Concealing physical, emotional, or psychological weaknesses to appear strong can add depth and tension, especially if these vulnerabilities are exploitable.
Knowledge of Critical Information: Possessing knowledge that others do not—such as impending disasters, secrets about other characters, or truths about the world's reality—can be dangerous to reveal.
Forbidden Knowledge or Research: Engaging in research or possessing knowledge that is forbidden or dangerous adds stakes, especially if discovery could lead to severe punishment.
Family Secrets and Lineage: Hidden heritage, such as being the descendant of a notable or infamous figure, can shape the character's identity and the perceptions of others.
Past Failures or Mistakes: A significant failure or mistake in the character's past that haunts them, influencing their present actions and decisions.
Survivor's Guilt: Being the sole survivor of an event and feeling responsible can be a heavy burden that the character keeps to themselves.
Secret Alliances or Memberships: Belonging to a secret society, cult, or group can add complexity, especially if their goals conflict with those of others.
Hidden Assets or Treasures: Possessing or knowing the location of valuable items can make the character a target and provide motivation to keep it hidden.
Internal Struggles with Identity: Questions about one's own identity, such as gender identity, sexual orientation, or personal beliefs that are not accepted in their society.
Mental Health Issues: Concealing mental health struggles due to fear of stigma or repercussions can add realism and depth to the character.
Unfulfilled Vengeance: Harboring a secret desire for revenge that drives the character's actions without others realizing their true intent.
Divine or Supernatural Experiences: Having had an encounter with the divine or supernatural that is disbelieved or ridiculed by society, leading them to keep it secret.

To ensure these secrets are compelling and worth hiding:

Integrate with Character Development: The secret should be a key part of the character's backstory and influence their development throughout the story.
Create Tension and Suspense: The possibility of the secret being discovered should create ongoing tension, affecting interactions and decisions.
Impact Relationships: The secret should have the potential to alter relationships with other characters significantly if revealed.
Drive the Plot Forward: The secret can serve as a catalyst for events in the story, creating twists, conflicts, or revelations that keep the narrative engaging.
Provide a Strong Motivation for Secrecy: The character should have clear, understandable reasons for keeping the secret hidden, such as fear of harm, shame, or protecting others.
Offer Opportunities for Revelation: Build moments into the story where the secret might be revealed, forcing the character to make tough choices.
Reflect Universal Themes: Themes like redemption, identity, betrayal, or the nature of truth can make the secret more relatable and impactful.

Provided Information:

{places_descriptions}

Character Information:
Name: {name}
Description: {description}
Personality: {personality}
Profile: {profile}
Likes: {likes}
Dislikes: {dislikes}
Speech Patterns: {speech_patterns}
Health: {health}
Equipment: {equipment}
Memories:
{memories}

Existing Secrets: {secrets}

Example Format:

"has developed romantic feelings for her brother, feels guilty about accidentally causing her cat's death, ..."

Your Task:

Using the above instructions and information, craft compelling, meaningful secrets for the character, adding to their existing secrets.

Anyway, that’s all I have time for today. See ya.