Neural narratives in Python #23

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. Here’s the GitHub repo, that now even features a proper readme.

In the previous part, our team of heroes found themselves trapped in a pocket dimension created by the malicious alien Zha’thik, a dimension made from the memories of Elizabeth Harrow so she would stay inside in a sort of dream state. The team will attempt to collapse the pocket dimension to return to the containing dimension, the so-called Mirage Wastes.

They finally prepare themselves to do something as troublesome as collapsing a pocket dimension with themselves inside.

Intense episode.

Neural narratives in Python #22

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. Here’s the GitHub repo, that now even features a proper readme.

At the end of the previous part, we left our jaded protagonist boxing an eldritch horror while his long-dead teenage lover ran for her life.

I had no clue where Cassidy’s portal would lead, or even if it would work. I organized a session with my Writers’ Room to put together the following scene, that involved generating a couple of new characters.

Those noises are artifacts that rarely happen when generating voices. It’s odd that it happened three times in this segment, but they seemed to fit the eeriness, so I left them in. I apologize for minor errors such as using “his” when referring to Elizabeth. My brain is mostly mush at this point.

When this scene started, I had only included the main team in the dialogue, but I wanted Gideon’s deceased wife and the young version of their daughter to show up in the middle of the conversation. I had no such system implemented, so I wrote it in: a new collapsible section in the chat page that if there are characters present in the same place who aren’t involved in the dialogue, you can simply add them, and they’ll be included in the prompts to the large language model from then onwards. It was surprisingly easy to do, which I suppose is a testament to how mature the app is at this point.

Neural narratives in Python #21

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. Here’s the GitHub repo, that now even features a proper readme.

Here’s the next episode of the eldritch show. Last time, the sadistic alien Zha’thik prepared an unfathomable feast in a non-euclidean hall. Our jaded detective decided to take the first bite, only to realize that the morphing mass had turned into the corpse of Emilia, his long-lost teenage love. Now he’s bound to face his personal horror.

Well, that was poignant. I shed tears while “playing” through it, I shed tears while editing it. That’s good.

At the final part of that scene, I needed a fight “beat,” but I didn’t have anything programmed that would provide such a thing specifically. In addition, this fight would take part in the middle of a conversation. What to do, then? Create a new Confrontation action like the Investigate, Research, and Gather Supplies ones, which are meant to be isolated, and then stitch together two dialogues? It didn’t feel right. Besides, in that case, I would need to write the summary of the full conversation to include the physical confrontation with the eldritch horror. So instead, I came up with the notion of a Confrontation Round: the account, taking about ten seconds, of a confrontation of whatever kind, given the provided context. The prompt is focused on that very subject with no distractions, as seen below:


You are to generate a detailed narrative describing a brief segment (approximately ten seconds) of a confrontation within a rich, immersive world.
Use the provided information about the player, the other characters, and the place involved.
Your narrative should be engaging, realistic, and reflect the abilities and circumstances of the participants suggested by the user-provided context.

Instructions:
1. Viability and Realism Assessment:
Carefully consider the feasibility and dynamics of the confrontation based on:
- The information about the characters, including their skills, abilities, health, equipment, and emotional state.
- The characteristics of the opponents, including their skills, abilities, equipment, motivations, and weaknesses.
- The characteristics of the location, including terrain, obstacles, and any environmental factors.
- The time of day and how it might affect the confrontation (e.g., visibility, shadows, crowd presence).
- The context for the confrontation provided by the user, including any prior events leading up to this moment.
- Potential advantages or disadvantages, such as surprise, preparation, or environmental hazards.

2. Narrative of the Confrontation:
- Write a vivid, dynamic narrative that depicts approximately ten seconds of the confrontation, focusing on the immediate actions and reactions of the characters involved.
- Include specific actions taken by the characters on either side of the confrontation.
- Describe the use of skills, abilities, equipment, tactics, and any maneuvers.
- Illustrate the interactions between the characters, including attacks, defenses, dodges, or spells.
- Convey the emotions, thoughts, and physical sensations experienced by the characters.
- Ensure the depiction is consistent with the world's lore, the characters' personal histories, and their abilities.
- Determine the immediate results of this segment of the confrontation based on your assessment.
- Be specific about any successes or failures of actions taken, including hits landed, misses, blocks, or counterattacks.
- Describe any injuries sustained, advantages gained, positions changed, or other consequences.
- Reflect any costs incurred, such as stamina lost, equipment damage, or exposure to danger.
- The confrontation doesn't need to end in this segment, unless you can clearly determine that the confrontation should conclude now.

3. Tone and Style:
- Write in the third person, present tense, maintaining an engaging and immersive narrative style.
- Use rich, descriptive language to bring the scene to life, focusing on action and immediacy.
- Ensure consistency with the established world setting and the characters' personalities.

Provided Information:
Current Time: {hour} {time_of_day}
{local_information}
Facts Known:
{facts_known}
{relevant_characters_information}
----
Ongoing Dialogue:
{transcription}

Example Format:
"With a fierce battle cry, [Player Name] charges forward, swinging their sword toward [Opponent Name]..."

Your Task:
Using the above instructions and information, craft a compelling narrative that captures this intense moment of confrontation, reflecting both the actions and immediate consequences, while advancing the story in a meaningful way.
Important: do not repeat verbatim previous confrontation rounds: there should always be a progress in the confrontation.

If the segment at the end of the previous audio was anything to go by, it worked perfectly. To be honest, I was dreading creating a Confrontation action, because I thought it would be the Next Big Part of the app, but turns out that at this point of the AI revolution, LLMs are intelligent enough to “get it.”

I need a breather after this goddamn episode.

Neural narratives in Python #20

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. Here’s the GitHub repo, that now even features a proper readme.

The last part ended with our team of heroes begrudgingly agreeing to partake in the trials of a shadowy, shady alien from the dimension they had ventured into.

For this part, I decided to implement a Grow Event button in the chat interface. Basically, the user writes the guideline for what they would like to happen at that point of the dialogue. Then, a dedicated prompt sent to a large language model will cause the AI to expand the suggested event into a literary form. There are two or three examples of that in the audio you may have listened to. Great for when you don’t want to bother behaving as both the narrator and the player character.

I’ve already played through most of the scene that will be featured in the next part. Man, it was emotionally taxing.

Neural narratives in Python #19

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. Here’s the GitHub repo, that now even features a proper readme.

In the previous episode of this thing, our team of Earthlings, having ventured through a rift into another dimension, encountered a half-buried building in an obsidian desert. Now, they enter it. The following audio is an episode-long account of what happened.

That’s it for today narrative-wise. By the way, this is the portrait that the app generated for Zha’thik.

As I was thinking what else I could do with character memories, which are used by the large language models to roleplay the characters better, I ended up implementing a system to generate any character’s worldview, his or her personal philosophies, influenced by their bio and their experiences. That means that when the user wants to deepen the roleplaying of any of the characters at an arbitrary point of the journey, their worldview can be generated, and it will reflect the journey up to that point. Some characters could start naive, only for their worldview to change after nasty experiences. And of course, the generation of self-reflections receives all the memories as input, including any possible worldview texts.

This is the worldview generated for the brilliant scholar Elara Thorn, based on her bio and experiences up to this point:

Elara Thorn views the world as a place full of untapped knowledge waiting to be discovered, even if it means venturing into dangerous and morally ambiguous territory. Her philosophical beliefs are rooted in the idea that the pursuit of truth and understanding justifies the risks and sacrifices made along the way. She believes that knowledge is power and that those who possess it have an obligation to use it for the greater good, even if it means pushing the boundaries of conventional morality. However, her worldview is not without its challenges and doubts. As she delves deeper into the mysteries of the multiverse, Elara is faced with the harsh realities of the consequences of her actions. She begins to question whether her obsession with knowledge has blind her to the cost of her pursuit, and whether the sacrifices she’s made have been worth it. Elara’s moral standing is complex and evolving. While she believes in the importance of doing what is right and protecting those who cannot protect themselves, she also recognizes the necessity of making difficult choices in the face of unimaginable danger. She struggles with the weight of her own humanity, constantly grappling with the question of whether her actions are truly justified or if she has become a monster in pursuit of her own desires. Despite the doubts and challenges that confront her, Elara remains steadfast in her belief that the pursuit of knowledge is worth the risks and sacrifices, and that she has a duty to use her brilliance to make a difference in the world, even if it means navigating the treacherous waters of the unknown.

My Writers’ Room has come up with an intriguing concept for the next episode. I can’t wait to play it out.

Neural narratives in Python #18

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. Here’s the GitHub repo, that now even features a proper readme.

We abandoned our heroes as they were about to approach an interdimensional rift, hoping to rescue a certain missing teenager.

Her comes a problem: although I had reworked the entire app to include an overarching Story Universe concept so that other worlds could be added, that doesn’t change the fact that the world on the other side of the rift isn’t connected to Earth, but to the Story Universe. I’m not sure how often this is going to happen in a story, so instead of programming the action of adding a new world, with its regions and areas, into the story universe, I just added that hierarchy manually in the JSON file that houses the map. As I use the app more, I’ll see if adding new worlds happens so often that it warrants programming it into the interface. What I did program was the simple notion of teleporting to another area from any area or location, as that could be useful from a storytelling perspective. A simple select to choose the area, then a button to teleport the player and his or her followers there.

The team dares to venture through an interdimensional rift, into somewhere else.

That’s all narrative-wise for today. Still twenty minutes of audio.

Being honest here, I wasn’t sure what the team might find inside the ruins. I wished I had implemented that Writers’ Room idea of mine, to rely on a little swarm of AI agents focused on aspects of storytelling so I could ask them questions about the story, and for them to elaborate on it. So I did, I programmed the Writers’ Room.

I have created a small team of AI agents. The user’s request first lands on the showrunner agent, that considers if he/she/it can handle it. If not, the showrunner delegates the task to more specialized agents, namely the story editor, the character development agent, the world-building agent, the continuity manager, the researcher, the theme agent, the plot development agent, and the pacing agent. The code provides each agent with data from other corners of the app; for example, the continuity agent receives the list of facts that one can introduce into the Story Hub to keep track of what’s going on, while the plot development agent receives all the goals, plot twists, and scenarios that have been generated in the Story Hub. It all works surprisingly well. Fun times!

Life update (10/29/2024)

As of yesterday, I’m unemployed. I was enjoying some vacation time from the Saturdays I had worked, as well as days they owed me from last year, but I had also taken almost the entirety of November as vacation time. Unfortunately, the guy whose medical leave I was covering has returned to work (technically he will return on Monday, but administratively he’s back). That means that my contract has ended, and I’ll have to call HR and ensure that my scheduled vacation time will show up as extra money in my last paycheck.

Worse yet, I have returned to the dreadful wasteland of not knowing when I’ll get recalled to work. I may get woken up at eight in the morning any given day (even on a Saturday), and told to show up at the office a couple of hours later. And it’s a really bad time for me to return to work, because next month, the project to replace every goddamn computer in the hospital complex starts, and my boss had already informed me that he intended to involve me.

In case you haven’t been following my posts, I’ve dealt with stress-induced medical issues, including arrhythmia, as well as an episode that sent me to the ER and got me diagnosed with a complex migraine, likely a hemiplegic migraine, although I suspect it may have been a small stroke: the issues with my vision, the main symptom for which they diagnosed me with a migraine, ended up being related to a detached vitreous gel that a couple of weeks later developed into a torn retina; I had to hurry to the ER and get patched up with a laser (and my vision has ended up permanently fucked up from that episode).

Honestly, I don’t think I’m suited for full-time work. I can work tirelessly at my own stuff, and do so, sometimes even at night until I can’t think anymore, but working at an office, surrounded by human beings, is far too much for an autistic nutcase like me. Likely, though, any other job that would pay similarly would be far worse for me; I already tried to work in the private sector, more often than not for free, and I was a prime candidate to be let go or not hired because someone in charge considered me weird. Even had a direct boss argue with his superior that I was doing great technical work, only for that superior to let me go because I wouldn’t “fit in.”

Anyway, I’m busy with my programming project, but I also want to get my mind off this sudden change, so I’ll likely spend the whole afternoon in the beautiful city of Donostia, walking around or sitting at some outside table to read manga.

Being an adult sucks so much. Don’t ever do it, kids.

Neural narratives in Python #17

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. Here’s the GitHub repo, that now even features a proper readme.

Three or four days ago, the RunPod templates that render voices stopped working. At least one of them is operative again, so that’s great. In the meantime, it was off-putting enough for me to use the system with silent characters that I took advantage of the break to improve fundamental parts of the app. I’ve forgotten most of what I’ve worked on, but the point is that I’m back, so here’s more voiced narrative.

I implemented a Travel action that is only accessible when one moves from an area (such as a city) to another. The ongoing adventure in this eldritch setting had led us away from Providence toward some interdimensional rifts in the wilderness, to where a missing girl could have traveled or have been brought.

After rendering those voice lines, I realized that the travel audio should be produced in the player’s voice, not the narrator’s. Oops. Anyway, fixed for the next time.

Such long voice lines require about forty seconds in total to render. I thought that they were perfect candidates for parallelism, which Python isn’t particularly fond of, but after implementing it, turns out that the RunPod server that handles voice generation isn’t fond of multiprocessing either, so I had to revert all that programming.

My player character had some observations to make about his new surroundings.

The team gathers in the wilderness, an unusual setting for scholars and police detectives.

I usually discover new features to implement as I’m playing, whenever I realize I’d love for some functionality to be present. At times, as I was chatting, I wanted for the AI to give me a “push,” to move the narrative just a few sentences forward based on the ongoing dialogue and what it knows about the characters. So I implemented a button that injects a narrative beat. That part at 2:55 in the previous audio is an example of it.

I also had to redesign the interface of the chat page. Those buttons are showing spinners because the server is processing a speech part for that form. The first button sends the speech written above. The second button produces ambient narration, and the third a narrative beat. The part below allows the user to introduce an event; sometimes I just want to state that the characters are doing something without having to trigger a dialogue response from other characters. The event gets voiced, and injected into the transcription so the rest of the characters can react to it.

Before we continue with the narrative, which will involve the results of an Investigate action, I wanted to showcase some character portraits that the app has generated these past few days for other test stories.

It’s so strange to have gotten used to this level of quality coming from artificial intelligence. My reaction to seeing those pictures generated for a newly created character was, “Oh, that looks good. Moving on.”

Anyway, here’s the result of the Investigate action:

I considered for a while if I should create a Track action, but that isn’t different enough from an investigation, as far as I’m concerned. One of the great things of this LLM-led system is that I had no idea what was going to happen, and now it’s “canon” that Elara Thorn has found a rift, and also discovered an artifact that could guide them to rifts on either side of them.

Forgive my player character for his inability to say “vicinity” correctly. He’s way over his head.

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.