agnos.is

The agnos.is blog

Published: 2024-03-27T11:38:23+01:00

Partly a copy of AI Game dev journal entry

I have succeeded in removing heap allocation and dynamic dispatch from the GBNF grammar-limiting feature of the AI game. This is a mouthful way of saying that it's now easier and more performant to limit the output values of the large language model used to drive the game's content.

Benefits

The code is much easier to read and write with these changes, and it takes advantage of Rust's powerful generics to avoid unnecessary heap allocation and dynamic dispatch, which is theoretically faster to execute. Not that dynamic dispatch would be particularly slow here.

As the codebase evolves further, I hope to be able to further obfuscate away the layers of abstraction and remove cloning, making limit generation even more performant.

Technical Explanation

After writing the previous dev journal yesterday about my attempts to remove the dynamic trait objects from GBNF limit creation, I have finally succeeded in figuring out the right set of trait bounds and associated types required to make it work properly. Initial implementation of removing the dynamic trait object was very easy, but I quickly ran into an issue with how “primitives” (i.e. single values like a number or string) vs “complex” (nested types with multiple fields) are handled.

This required creating two new types:

  • GbnfLimitedPritive
  • GbnfLimitedComplex

These two wrapper structs have a bunch of fancy trait bounds and associated types on them that allow instances to be created that hold the proper limit rule for the given field which is limited. That might be hard to understand, so here is a simplified example, directly from the game code.

#[derive(Debug, Serialize, Deserialize, Clone, Gbnf)] #[derive(Debug, Serialize, Deserialize, Clone, Gbnf)]

pub struct RawCommandEvent {
    pub event_name: String,

    #[gbnf_limit_primitive]
    pub applies_to: String,

    #[gbnf_limit_primitive]
    pub parameter: String,
}

// Limit creation
let applies_to = vec!["self", "uuid1", "uuid2", "uuid3"];
let all_uuids = vec!["uuid1", "uuid2", "uuid3"];
let event_limit = RawCommandEventGbnfLimit {
    applies_to: GbnfLimitedPrimitive::new(applies_to),
    parameter: GbnfLimitedPrimitive::new(all_uuids),
};

let limit = RawCommandExecutionGbnfLimit {
    event: GbnfLimitedComplex::new(event_limit),
};

In this code, the event itself has two limited fields:

  • applies_to: The UUID of the thing in the scene that the event originates from.
  • parameter: The UUID of the thing in the scene that is affected by this event.

These are single String values. GbnfLimitedPrimitive takes a Vec of allowed values, without any heap allocation or dynamic dispatch. In contrast, the main struct has an Option field that can contain a single event. The generated GBNF limit struct mirrors the creation of the regular struct, and takes only one instance of GbnfLimitedComplex, again with no heap allocation or dynamic dispatch.

This also makes the code much easier to read, as it no longer requires a bunch of janky Box::new or into() invocations.

Next Steps

I am satisfied with the current state of GBNF grammar limiting. The next major steps for the AI game will focus on implementing a basic interactive command beyond exploring the world. This will test the limits of the events system as-is, and likely force my planned modifications to it (removal of the applies_to field and reworking of how parameters to events are handled).

This will be a stepping stone to implementing a “one-of” feature in the GBNF grammar rules generator, which will then allow hyper-specific events with their own types and specific fields.

Filed under: opensource, ai-game License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-26T19:08:31+01:00

This is a copy of a journal entry from the AI game page.

The AI game can now limit output using the gbnf_limit feature, but it requires dynamic trait objects for this. Rather than generating a so-called “limit struct” with proper concrete types, the code relies on using dynamic typing of anything that can produce a GbnfLimit. This makes the code easier to understand, but creating limit structs does is not ergonomic:

  • Lots of Box::new.
  • Performance implications of dynamic dispatch.

I am trying to fix this on a separate branch that is not yet uploaded to the Git repository, because it's a giant mess. I have made some progress, but I'm running into the limitations of Rust's (very powerful) generics system. Namely, blanket traits are not so specific: an impl for Option also counts as an impl for Option>. This can be solved by something called “trait specialization,” but that's an unstable nightly-only feature and has its own set of issues.

I have almost worked out a way to make the concrete types work. But much like the initial implementation of the GBNF grammar generator, I've sort of hit a roadblock due to trying to remove dynamic dispatch.

I've been spending my time creating gemfreely instead.

I hope to return to the AI game soon and get the dynamic dispatch fully removed from GBNF Limit code, so development on at least one interactive command can resume!

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-24T23:10:20+01:00

gemini://agnos.is/section/trackballs/

In the spirit of the small web, I have created a new section page about my favorite input device: the trackball mouse! Trackballs are a bit obscure in the modern age of touchpads and optical mice. But there is still a small yet dedicated group of computer users that use trackballs as their primary way of interacting with their machines. Personally, I switched to using a trackball along with a mouse a few years ago due to RSI and ergonomics issues. After trying 4 different trackballs, I finally found the perfect one for me.

For whatever reason, trackballs seem to be a bit more complicated when it comes to finding “the right one” compared to a mouse. Any mouse will do (though some are obviously better than others), but due to the way one holds a trackball, it needs to fit the hand “just right,” and the ball needs to move “just right,” and the buttons have to be within just the right distance.

My Perfect Trackball

My trackball of choice is the GameBall.

At risk of sounding like a shill for the GameBall, it truly is one of the best trackballs available. While its primary marketed use is gaming, it works perfectly fine for any regular computing task.

Other Brands

Other popular brands include Kensington and ELECOM. I can recommend both, although the ELECOM trackballs often have low-quality bearings that need replacing in order for the ball to move truly smoothly.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-24T16:19:58+01:00

I have made a small update for gemfreely, and have some plans for the roadmap ahead. I've been thinking about how to implement comment sync, as well better ways of serving the blog.

🦠 Germ Update

gemfreely has been updated to 0.1.7 in order to integrate some bug fixes from the underlying Gemini protocol library: germ.

Germ – 🦠 The Definitive Gemini Protocol Toolkit

This is the library that powers Gemfreely's connection to Gemini and parsing of Gemfeeds. It's very easy to use, and has now been updated to allow better detection of MIME types (Atom vs Gemfeed, for example), and I can remove the kludge fix for emojis in title headings.

🛣️ Roadmap

The next major thing I want to add to gemfreely is the ability to sync comments coming in to the blog post from the Fediverse. This is somewhat difficult, because WriteFreely does not really do anything with replies to comments (it's a request feature). The recommended workaround is to add a separate account to the signature of the post, so that account gets notified by replies from other users. This is the path I will try first.

  • Integrate a Mastodon/Misskey API client.
  • Listen to notification stream, and write comments to Gemtext files.

I have also been considering integrating Fluffer, an experimental Gemini web app SDK for Rust. This would allow gemfreely to run in an actual server mode, and act as a proxy to WriteFreely itself. I'm not sure how I'll apply that yet, though.

Fluffer – an experimental crate that aims to make writing Gemini apps fun and easy

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-22T19:21:13+01:00

Linux-libre 5.15.151 Gentoo dist-kernel

The stable 5.15.x dist-kernel for Gentoo has updated to 5.15.151, and a new Linux-libre dist-kernel release follows it on my personal overlay.

gemini://agnos.is/projects/linux-libre-gentoo/

gemfreely 0.1.6

I have also updated gemfreely to 0.1.6:

  • Static binary releases (for Linux)!
  • Gemfeeds whose titles start with an emoji are now handled correctly.
  • Changes to internal code structure and cleanup (removal of unused files).

The most important change for in this release is that new releases are now available as statically compiled executables. You can just download and run gemfreely without building it from source using cargo.

Grab the new release here What is Gemfreely?

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-21T21:19:34+01:00

There is now a page on the capsule for the gemfreely tool.

gemini://agnos.is/projects/gemfreely/

This contains an intro to the tool and basic documentation.

License: CC-BY-SA-4.0.

Written by: @[email protected]

Published: 2024-03-21T14:22:58+01:00

The gemfreely command supports sanitizing the generated Markdown files by stripping out text before and after certain points in the file, based on a string marker. The Justfile accidentally had a space in both markers, and thus it was missing the markers in the gemtext files. This should be fixed as of this post... I hope. It's time to add some tests to gemfreely.

gemfreely (crates.io)

License: CC-BY-SA-4.0.

Written by: @[email protected]

Gemfreely now on crates.io

Return to Posts

Published: 2024-03-21T14:18:49+01:00

The Gemfreely tool is now available to install on crates.io:

cargo install gemfreely

It also now supports basic error reporting, along with an optional date format override for Gemlog Atom feed date parsing (the default format is the one used by the atom.xml of this site). I will next add documentation to it, and then move on to better error reporting, which actually includes context of the validation failures. Right now, the error doesn't really give the user a clue where it happened.

The CI pipeline that builds the Gemini capsule will now also automatically synchronize to the Fediverse via WriteFreely, so this post SHOULD show up in WriteFreely shortly after it's posted to the Gemini site... We hope.

The next step is to somehow redirect the WriteFreely instance to my static site, as WriteFreely is only really used to push things to the Fediverse; I'm not necessarily interested in exposing it as a separate site.

License: CC-BY-SA-4.0. ‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗ Back :: /posts Home

Written by: @[email protected]

Gemfreely: Synchronize Gemlogs to WriteFreely

Return to Posts

Published: 2024-03-21T12:10:52+01:00

I have created a tool to enable “integration” of this Gemini site into the wider Fediverse. It is called Gemfreely, and it performs a one-way sync of a Gemlog feed to a WriteFreely instance. The tool was created for my specific requirements, but should be applicable to many/most Gemlogs.

The code can be found here:

https://git.agnos.is/projectmoon/gemfreely

Documentation is (or will) be found at the associated Git repo. The next major items on the roadmap are:

  • Some kind of comment integration.
  • Sync updates to Gemlog posts as WriteFreely updates.
  • Broader compatibility with more Gemlogs, as there's no doubt some bugs.

The tool will remain a one-way sync from Gemini to WriteFreely, aside from the comment integration (when implemented). It will not sync WriteFreely posts to a Gemlog. At least, I will not write that code! Someone is welcome to contribute that.

License: CC-BY-SA-4.0. ‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗‗ Back :: /posts Home

Written by: @[email protected]

Published: 2024-02-10T12:33:58+01:00

Words have power, and the paradoxical nature of the vocabulary used in Geminispace is symbolic of a greater underlying change that society must embrace.

Surrealism

I have noticed that a lot of the naming and vocabulary in Geminispace is based on astronomical terminology. And I like it. Much of Geminispace has a slightly surreal feeling to it, one in which I am eager to participate. The feeling that you are floating in space, a vast universe before you, with only you and the fundamental forces of nature as your companions, is manifest in the way we write the words presented on capsules. That feeling appears in the name of the protocol, the standard port (1965), and in many of the capsule names used.

It evokes a specific feeling, one that for me represents the hope of something new, an exploration of deep possibility and potential. The feeling of vastness is paradoxically coupled with the close-knit nature of the small web (let's face it: Gemini is not well-known or easily accessible to the average user). Those of us who are in Geminispace are all explorers of a new method of thinking and doing things with technology. Words have power, processes have power. Technologically speaking, Gemini is a reaction of sorts to what the Web has become, but there is a powerful symbolism behind it, and that symbolism is attached to an ever-growing movement. Gopher, Gemini, the Fediverse, Matrix, XMPP, and other technologies are different facets of a larger current of social change.

The Cathedral and the Bazaar

Large corporations have built the cathedral and locked it down, while we continue to mingle in the bazaar. We in the bazaar do not always agree on “the right way” to do things (and in fact, that disagreement can sometimes be a hindrance), nor do we even all have the same opinions or ideologies. But what matters is the unified attempt to reclaim and redefine technology in a way that works for us, rather than exploiting us.

One of the most important things we can hope to achieve from these disparate movements is an embrace of digital democracy, where technology works for the interests of society and a free people. This is even more important in an era where generative AI and deepfakes are causing disinformation to rocket out of control. Projects like Gemini are a simple manifestation of a much greater obligation that we have. The promotion of innovations like Gemini pushes the idea of digital democracy, privacy by design, and decentralized control over our own data into the public mindset. Telling a friend about something like Gemini might seem inconsequential (and when measured alone, it probably is!), but it furthers the greater behind it all, even if in the most minute way.

Gemini isn't going to be the thing that helps people take back the internet. But it will be one building block in an optimistic future where user freedom and privacy reign supreme. Technology alone will not bring about this future: it's about mindshare, cultural shifts, and regulation, too. These things are arguably even more important than the software itself.

Filed under: opensource, ideology, gemini

License: CC-BY-SA-4.0.

Written by: @[email protected]