Introduction to GnatWriter - applebiter/gnatwriter GitHub Wiki

GnatWriter is a framework, or back end, ready to extend or integrate into another application to provide a simple literary authoring tool that is ready to communicate with your existing Ollama server for writing assistance. The name comes from my frustration with trying to name this project. The "gnat" in GnatWriter stands for Good Names All Taken.

The developer is only ever meant to directly use an instance of gnatwriter.application.GnatWriter, and to get it, pass the path to a configuration file to the constructor (an example configuration file is included in the repo):

from gnatwriter import GnatWriter

gnatwriter = GnatWriter('/path/to/gnatwriter.cfg')

The code is separated into controllers and models. The developer is meant to use controllers to access all of the application's functionality, and is meant to use the objects as return values that can be used as-is or serialized into dictionaries. Models are defined for the sake of persistence using SQLAlchemy's ORM. One of the many benefits of using that library is that it will automatically generate the schema and create your tables if you pass it the path where a SQLite database should be saved, or the credentials to a PostgreSQL or MySQL database. The models are not meant to be used directly for writing to the database, and indeed will not work that way.

Controllers

To access a controller, its shorthand name is passed to the application instance along with a method call to that controller attached. For example, if you wanted to access the StoryController in order to create a new story, you would call it this way:

story = gnatwriter("story").create_story(title="The Best Story Ever", description="Inarticulable.")

You might want to go ahead and append an author to the story- a pseudonym, perhaps:

author = gnatwriter("author").create_author(
    name="Tilda Hale", initials="T.H.", is_pseudonym=True
)
story = gnatwriter("story").append_authors_to_story(story_id=story.id, author_ids=[author.id])

Next, let's go ahead and create the first chapter and the first scene in it:

chapter1 = gnatwriter("chapter").create_chapter(story_id=story.id, title="Chapter One")
scene1 = gnatwriter("scene").create_scene(story_id=story.id, chapter_id=chapter1.id, title="Scene 1", content="It was a dark and stormy night.")

The new chapter and scene do exist and they are now associated with the story in the database, but you will not find them in the story object until you fetch the story from the database again. Most operations will return the updated object on execution, and usually it's when a method on the controller associated with that object is used. So, for example, a call to a method on the "story" controller is more likely to return a Story object.

The controllers only ever take scalar values as arguments or lists containing scalars, and they usually return an object. This is important to remember because there is no method such as story.save(), and any manipulation to an object's attributes in the database happens inside of the controller. Here is the list of controllers and their shorthand name for accessing them:

  1. "activity" is shorthand for the ActivityController
  2. "assistant" is shorthand for the AssistantController
  3. "author" is shorthand for the AuthorController
  4. "bibliography" is shorthand for the BibliographyController
  5. "chapter" is shorthand for the ChapterController
  6. "character" is shorthand for the CharacterController
  7. "event" is shorthand for the EventController
  8. "export" is shorthand for the ExportController
  9. "image" is shorthand for the ImageController
  10. "link" is shorthand for the LinkController
  11. "location" is shorthand for the LocationController
  12. "note" is shorthand for the NoteController
  13. "ollama-model" is shorthand for the OllamaModelController
  14. "scene" is shorthand for the SceneController
  15. "story" is shorthand for the StoryController
  16. "submission" is shorthand for the SubmissionController
  17. "user" is shorthand for the UserController

At this time there is no error logging, however, there is an activity logger that logs very brief messages to the database upon every interaction with the database or interactions with LLM Assistants or the export controller (e.g. "gnatwriter deleted a note").

Models

Models are returned by controller methods and are usually complex objects that are eager-loaded so they already contain hierarchically-organized associated objects, and all of the SQL heavy-lifting is done in the background. Models can be used as they are given. Change the attribute values in the front end if necessary, as such changes will not be saved to the database. Alternately, every object can be serialized into a nested dictionary with all hierarchical structure and data preserved. All date and datetime objects stored in the objects will be flattened into formatted text strings, the format for which is defined in the GnatWriter configuration file. Turn a whole story into a single JSON string with the following call:

json_string = json.dumps(gnatwriter("story").get_story_by_id(1).serialize())