agnos.is

The agnos.is blog

Published: 2024-02-15T20:42:38+01:00

Update: HTML fonts now changed to sans-serif, with dotted underline links.

Thanks to the power of the wonderful tool gem-web-cli, the HTML version of this website is now accessible!

https://www.npmjs.com/package/gmi-web-cli

This tool does 99% of the heavy lifting. It converts the gmi files in-place to HTML that looks pretty much like the native Gemini rendering does. I kept the basic style, because I think it looks fine. I may change the fonts around, as I'm not a huge fan of the serifs. The CSS is simple yet doesn't look entirely out of place in the modern world, and it is mobile-friendly.

Anatomy of the Website Generation

I integrated gmi-web into the repository's Dockerfile and the justfile that does all the templating and building. The gmi-web does not handle every link on the site, so some light application of sed was required to get all the links properly converted to their corresponding HTTP versions. The gemlog and individual blog posts have its own specific sed fixes, and additionally a “hostname fix” is applied across all pages on the site to make sure that any agnos.is links point to the HTTPS site and not the Gemini site.

This makes gem-web-cli the third main tool used in the creation of this site, along with gempost and tera-cli. The justfile has grown to 115 lines, which is still miniscule compared to writing a full application to manage this in a more specific/“elegant” fashion. I may reach that point anyway, though, as tera-cli has some very specific limitations that make it annoying to template the page files. Namely, templating pages requires kludging them into a YAML format so the tera CLI tool will understand how to template them.

I might wind up creating just a more general version of the tera CLI tool to fit my needs, rather than a full-on set of changes. But in any case, with the HTML version of the website available, the main goals of this project are now complete! The website will continue to evolve and be updated, but I think I can go a long way with the current setup.

Features of this Site

Working with the site is still pleasant:

  • Fully static website, generated in both HTML and Gemtext.
  • Served over Gemini and HTTP.
  • All content managed in Git.
  • Updates deployed in seconds after push.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-13T21:08:04+01:00

Story: The Quiet Fire

I have finally gotten around to updating the writing and world-building page with a short story. This is an story I wrote recently. It paints a vague image of a desolate, apocalyptic future, but tinged with hope. The intention of the story is that it speaks multiple messages, some of which the reader themselves might have to discover in their own minds. I wrote the story for a specific reason, and it has a specific meaning to me. Several meanings, in fact. But hopefully, by sharing it, people can find their own meanings too. It is a melancholy story, but with a hopeful nod towards an undefined future.

I am also building up the list of campaign settings and other fictional worlds I've created over the years, which will be added to the writing section soon as well (very likely as links to external sites). Many of these campaign settings have been hosted on various forums or self-hosted wiki sites over the years, but I've taken them all down by this point, or they were turned off by the hosting provider. I would like to host their content on Gemini, but at least in the beginning, it will be far easier to get them up and running more or less in their original forms (Gollum wikis and statically generated websites).

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-14T22:36:15+01:00

It is so annoying to encode video in the modern age. There's dozens of different codecs, and dozens of different containers. I am using Pitivi to edit and encode a video that should run everywhere. The first attempt ran on my machine but not phones. The next attempt ran almost everywhere, except it doesn't have sound on Macs or iPhones. The third attempt was just janky and didn't work at all.

  • Attempt 1: Pitivi MP4 with H264 and MP2 audio
  • Attempt 2: Reencode above into MP4 using vLC
  • Attempt 3: MP4 with H264 and AC-3 audio

The fourth attempt will be MKV H264 converted to MP4 with ffmpeg. If THAT doesn't work, I will do something more exotic like WebM or even Ogg Theora! There is an Ogg JS plugin which can be embedded into web pages to play Ogg files...

This post will be updated once a successful combination of codecs and steps to produce a video that works everywhere has been found.

2024-02-19: Update – AAC Audio Works!

The video had to be encoded with AAC audio to work properly on Apple devices out of the box. The magic combination for the future is thus H.264 and AAC audio.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-10T22:19:42+01:00

After having spent the past few days getting the capsule set up and wiring up build tools to automate updates to the site, I am almost ready to declare the basic design of the capsule complete.

Organized!

With Gemini being as purposefully limited as it is, there is only so much “design” one can do. However, there are still cleanliness and organizational concerns for the content. I have grouped and arranged things in what I think is a logical manner, and will use this as a foundation for adding more content to the capsule.

I have a simple homepage that allows visitors to drill deeper quickly. I envision adding multiple top-level links to different interest categories, beyond just the focus on open source software. In addition to software development, I have an interest in micromobility, electric vehicles of all sorts, creative writing and world building, and more. As I hope to make this site my main digital presence in the world, I want to explore all of those things here.

What Do I Plan to Use Gemini For?

Some people use Gemini as a dumping ground for interesting stuff they find. Others use it as a new way to gather people together online in smaller, focused communities. Others are using it as a place to make posts that are more rough around the edges, and not ready for “prime time” on the regular Web.

I want to use Gemini as a place to create a content-driven digital presence. This sounds lofty, but all it really means is that I intend for it to be my primary means of presenting myself to the online world. It's a cross between marketing myself, getting my thoughts into the open, journaling away troubles, and having something to keep idle hands busy (but not too busy).

During the entirety of this design and setup phase, I noticed that my focus is still mainly content-driven. I have spent more time thinking about -WHAT- to put up here, and gathering the motivation to do it, than thinking about -HOW- to arrange what I've put here. And I consider that a win.

Filed under: capsule, gemini

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-12T00:26:51+01:00

The Astroponic Garden section of my capsule now has its own dedicated Gemfeed.

The Astroponic Garden

The garden is my approach to “digital gardening” in Geminispace. I use it as a small stream of consciousness to think up topics or tasks for the capsule. The addition of the dedicated Gemfeed was achieved by creating a secondary gempost config file and pointing it to a new posts directory for the garden, and then building the capsule using the main config file along with the secondary config file. This was slightly tricky, as simply running gempost build will wipe out the output directory.

  • First, I run gempost build using the main config file.
  • I had to make a separate output directory for the garden.
  • Then I rsync it into the main output directory.

I also had to add a .gitignore file to garden/static, which is the directory that holds any static files for the astroponic garden (there are none). The static files directory is required, so I had to give it something, and giving it the main static directory will copy all of the site's content over twice.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-05T19:55:51+01:00

The AI-driven text-based adventure game has finally hit an important milestone: the implementation of hyper-specific GBNF grammar constraints at runtime!

https://git.agnos.is/projectmoon/ai-game

This allows restricting the output of the LLM to very specific values at a per-field level. It took quite a long time to implement this properly, as I don't have that much free time on my hands due to family obligations and job searching. The implementation went through several major iterations before I finally got it right.

  • Basic limiting of primitive fields.
  • Limiting fields but comparing based on rule text.
  • Proper recursive output of rules and sorting to de-duplicate.

The addition of this feature also addresses a bunch of shortcomings in the original implementation of the derive macro. Overall, I am happy with how it works, though not necessarily happy with how long it took.

How the GBNF Derive Macro Works

Before explaining how the limiting works, a quick overview of the derive macro itself is useful. The macro, when added as a derive attribute on a struct, will add a function to the type itself called to_grammar. This function produces a set of valid GBNF rules that allows the LLM to output values according to the shape of the annotated type.

Example:

[derive(Gbnf)]

pub struct CommandResponse {
    pub valid: bool,
    pub text: String
}

//...

let grammar: &'static str = CommandResponse::to_grammar();

When this grammar is given to the LLM, its output will be a JSON value that matches the struct, which can be directly deserialized by serde. This is very powerful, but suffers from a big limitation, which also plagued the original hand-rolled GBNF rules: With a string value, the LLM could still output nonsense, even though it was constrained to a JSON format. So, if it was instructed to set a field to an ID in the scene, it might do that, or it might not.

This is where the new helper attribute #[gbnf_limit] comes in. By adding this helper attribute to a field on a struct annotated with #[derive(Gbnf)], A corresponding “limit struct” is created, which allows the developer to give a set of valid values the LLM can produce for the given fields.

The New GBNF Limit Implementation

The final implementation of this feature makes use of the following:

  • A dynamic trait called GbnfLimitedField.
  • A custom-generated limit struct, created by the derive macro.
  • A new addition to the GBNF types: GbnfLimit.

The GbnfLimit type is a nested enum that mirrors the structure of the type it was derived from. This limit is passed into the initial GbnfComplex type at the root of the GBNF rule hierarchy, and as rules are recursively generated, each rule type looks into the limit struct (or its nested descendants) to see if its values should be limited. If so, the type is wrapped in an opaque “limited GBNF type” that implements the standard AsGrammar trait. These wrapper trait implementations reference the underlying type to create a limit rule, which is either the actual limited values, or a set of nested limited values (if a field of a subtype is limited).

The derive macro takes care of generating the limit struct itself, along with its implementation to convert the limit struct into a GbnfLimit instance. This was the easy part. Almost all of the complexity lay in the code for actually turning GBNF rules into the final text that would be fed to the LLM.

Implementing the limitation feature made very clear numerous flaws in the original GBNF macro implementation, which have now been addressed. Most importantly, the GBNF rules are now generated recursively, with each rule 0 or more dependent rules. This allows proper handling of optional fields, list fields, and of course, limited fields (as well as optional limited fields, and list limited fields!).

Next Steps

The next steps are pretty straightforward:

  • Spin the GBNF code into its own crate, to make it available to others.
  • Attempt to get rid of the requirement to use Boxed dynamic traits for the limit struct.
  • Improve the error reporting from the derive macro.
  • Make proper use of the limiting feature in the game itself!
  • Tests! Game is getting complicated enough that it needs them.

The initial use of the feature in the game itself will simply constrain the LLM to pick from any IDs present in the scene. Later, some prompts will be rewritten, and a more accurate list of IDs can be given to the LLM.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-12T21:59:20+01:00

I have been hard at work enhancing my justfile (for the just build tool/command runner) into a barely-readable mess so that it can template the pages of the capsule. This gives a mostly-proper navigation experience throughout all pages on the capsule. There should be a link at the bottom of every page called “Return Home” or “Go Back” or something like that.

The templating of pages is still very limited, so right now the build process only adds the Go Back link to every page. Eventually, I will try to expand the templating to have per-page information and per-page templates (I would probably want a different template for the section pages, for example).

The next large item on the agenda is to get the capsule accessible over HTTP. I am debating on writing my own converter, or attempting to use/modify one that already exists. So far, all of the ones I've evaluated are either too complicated, or do not work exactly how I want them to. I need a converter, because the HTTPS homepage of agnos.is is a static file (installed by the matrix-ansible-deploy playbook). I do not want to mess with that setup, so running a transparent Gemini –> HTML proxy is currently out of the question.

Finally, as part of this update, I've started categorizing the blog posts. This is not really exciting, but could be useful later for building pages that list articles by category.

Filed under: capsule, gemini

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-26T20:31:26+01:00

Technoethical Bluetooth 4.0 Adapter

This Bluetooth adapter is one of the only dongles on the market that can run on a fully free OS (AKA, Linux-libre kernel) and isn't stuck on an ancient version of the Bluetooth protocol. I used one for two years before my repeated abuse of it made it completely and utterly fall apart (I bent it a lot and accidentally ripped it out of the USB port one too many times). I have now acquired a replacement.

I found it via the Free Software Foundation's Respects Your Freedom initiative, a list of products that are certified to work on fully free hardware. It's surprisingly hard to find out if Bluetooth dongles work on the libre kernel. Many Bluetooth dongles, even the ones that explicitly support Linux, actually require non-free firmware.

Respects Your Freedom

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-02-09T19:16:00+01:00

I have long thought I needed to launch a new website. It's been a long time since I actually hosted a home page of any sort. Currently, the main HTTP website of agnos.is points at a static “Hello World” page, which isn't super useful to tell people about. So, now I have decided to put up a basic web page about myself and my interests. But of course, we can't do it normally.

No no.

This is my first foray into alternate protocols, and my first real interaction with Geminispace. I hope to eventually mirror this website as static HTML, running on HTTP.

https://agnos.is

License: CC-BY-SA-4.0.

Written by: @[email protected]

  • Return to Posts

Published: 2024-02-16T21:35:28+00:00

What is Home?

Home is a place. Home is a feeling. Home is more than one place. Home is where you live currently. Home is where you came from. Home is where your parents or family are. Home is something we can concretely point to, yet also something transient and elusive.

Transience of “Home”

As someone who's lived in 3 countries, home is a VERY transient concept. I call many places home. Some I prefer to call home more than others. Is home the place I've lived the longest? Is it the place I have the strongest connection to? Or is it where my family is? The problem, for me, is that I'm not sure that is where home actually is. We live in a transient situation, in a rental which we cannot stay for much longer. It's too expensive. Everything feels like an “in-between.”

I return often to the place I think of as home. But it feels weirdly emptier each time I go there. Is it a sign that I am finally moving on? That a new place is becoming “home?” But then the old place feels less empty, and the new place more empty. And then it doesn't. Rinse, repeat. I am still stuck in the In-Between. Both physically and metaphorically. This in-between place is the place where two (or more!) choices dangle in front of you, and they continue to hang there, just out of sight, sometimes with fleeting glances in the periphery of your vision.

In this In-Between, you can spend time either running TO the choices, or running AWAY from them. I have been running away from them for a long time, but eventually, they will always catch up to you. And that's happening to me now. Eventually, circumstances of life force one to make a choice.

Acceptance of the Future

The future brings what it brings. We eventually have to make the choice we've been running to or from. Once we make the choice, we have to live with the consequences. They could be good, or bad, or a mix. But what if we have the problem of not wanting to accept any of the consequences or the choices we have before us?

I have to make a decision where to live, soon, and I'm not sure what the best answer is. Many different potential futures will spawn from this one choice, and there are a mixture of good and bad consequences in each choice, at least that I can easily perceive. There's also plenty of unforeseen developments that will result depending on the choice I make. Thinking about this choice and making it a cause of existential dread.

But nevertheless, in the end, the choice must be made. Any number of ways of analyzing the choice can be done. Greatest amount of self-happiness? Greatest amount of net happiness? Purely pragmatic factors like finances or education? All of these factors are important, but also maintain a careful balancing act with emotion and sentiment. In the end, I think home is what we make of ourselves and our situation. The word still can have many meanings, but it is up to us to define what the word means for us, in the current part of our lives. The meaning of “Home” likely changes throughout the decades of our existence.

The only constant is change.

License: CC-BY-SA-4.0.

‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗

Written by: @[email protected]