{"id":16905638,"url":"https://github.com/rzumer/blini","last_synced_at":"2025-08-18T11:07:36.289Z","repository":{"id":118805178,"uuid":"527048188","full_name":"rzumer/blini","owner":"rzumer","description":"Chatbot module based on second-order Markov chains","archived":false,"fork":false,"pushed_at":"2024-03-31T21:40:59.000Z","size":402,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-20T16:23:12.709Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rzumer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-20T21:56:19.000Z","updated_at":"2022-08-20T21:59:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"c31b48cd-d47f-41a0-8347-4be26c9b0557","html_url":"https://github.com/rzumer/blini","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rzumer/blini","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzumer%2Fblini","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzumer%2Fblini/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzumer%2Fblini/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzumer%2Fblini/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rzumer","download_url":"https://codeload.github.com/rzumer/blini/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzumer%2Fblini/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270982194,"owners_count":24679447,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T18:39:08.793Z","updated_at":"2025-08-18T11:07:36.261Z","avatar_url":"https://github.com/rzumer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Blini\r\n\r\nChatbot module based on second-order Markov chains.\r\n\r\n## Features\r\n* Text generation with two words of context (or fewer if the learned vocabulary is too small)\r\n* Japanese tokenization using [TinySegmenter](http://chasen.org/~taku/software/TinySegmenter/), including mixed Japanese/alphabetical strings\r\n* Index of image URLs (e.g. for overlaying text output on top of learned images)\r\n* Tagging support for tokens (e.g. for generating output based on metadata gathered during learning)\r\n* Optional persistent storage via [node-persist](https://github.com/simonlast/node-persist#readme), or any similar package that provides a `setItem()` function based on the [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)\r\n\r\n## Usage\r\n### Basic text generation\r\n```javascript\r\nconst blini = new (require('blini')).Blini();\r\nblini.processInput('hello world and hello blini');\r\nconsole.log(blini.generateOutput('')); // Generates output without context\r\nconsole.log(blini.generateOutput('hello')); // Generates output with a single word of context\r\nconsole.log(blini.generateOutput('hello world')); // Generates output with two words of context\r\nconsole.log(blini.generateOutput('goodbye world')); // Generates output with only the last word for context, because \"goodbye\" is not part of the learned vocabulary\r\nconsole.log(blini.generateOutput('goodbye and hello')); // Generates output with context taken from the last two words of the input\r\n```\r\n\r\nExample output:\r\n\r\n```\r\nhello world and hello blini\r\nhello blini\r\nhello world and hello blini\r\ngoodbye world and hello blini\r\ngoodbye and hello blini\r\n```\r\nOf course, the more natural text is provided for learning, the more varied and interesting the output will be.\r\n\r\n### Setting up persistent storage using `node-persist`\r\n```javascript\r\nconst blini = new (require('blini')).Blini();\r\nconst storage = require('node-persist');\r\n\r\nstorage.init().then(_ =\u003e {\r\n    blini.storage = storage;\r\n    storage.getItem('bliniDictionary').then(function(data) {\r\n        if (data) blini.dictionary = JSON.parse(data);\r\n        console.info(`Loaded Blini dictionary (${Object.keys(blini.dictionary).length} entries)!`);\r\n    });\r\n    storage.getItem('bliniImages').then(function(data) {\r\n        if (data) blini.images = JSON.parse(data);\r\n        console.info(`Loaded Blini images (${Object.keys(blini.images).length} entries)!`);\r\n    });\r\n});\r\n```\r\n\r\n### Image generation\r\nThere are some special requirements to enable image generation.\r\n* It needs filesystem write access to the `img/.attachments` path (relative to your application's working directory), to store generated images temporarily and delete them after your callback function has completed.\r\n* It needs its `imageFonts` property set to a list of fonts to use for overlaying text on top of images. You can specify multiple fonts to cover a larger range of character sets, for example latin characters, Chinese characters, emoji, etc.\r\n    * Fonts must be objects with `path` and `family` properties. You can use relative paths, based on your application's working directory, and copy the fonts you need manually for portability.\r\n\r\nTo ensure that overlaid text has enough space to be readable, images must have an aspect ratio no higher than 2.5 in either direction.\r\n```javascript\r\nconst blini = new (require('blini')).Blini();\r\nblini.imageFonts.push({ path: '.fonts/NotoSans-Bold.ttf', family: 'Noto Sans' });\r\n\r\nblini.processInput('hello world and hello blini');\r\nblini.processImage({ width: 985, height: 656, url: 'https://media.xiph.org/tdaede/a.png' });\r\nblini.generateImage('hello world', async function(imagePath) {\r\n    if (!imagePath) {\r\n        throw new Error('could not generate an image');\r\n    }\r\n\r\n    // Copy the generated image, as the temporary file will be deleted after this function returns.\r\n    fs.copyFile(imagePath, 'out.png', err =\u003e {\r\n        if (err) {\r\n            console.log(err);\r\n        } else {\r\n            console.log('Generated an image at out.png!');\r\n        }\r\n    });\r\n    return new Promise(null);\r\n});\r\n```\r\nExample output:\r\n\r\n![Sample image with generated text overlaid on top](example.png \"Example image\")\r\n\r\n### Tagging\r\nIt's possible to filter the vocabulary based on arbitrary metadata gathered at input time. One simple example is to tag chat messages by author, and generate output based on input from a specific author:\r\n```javascript\r\nconst blini = new (require('blini')).Blini();\r\nblini.processInput('hello world and hello blini', { author: 'rzumer' });\r\nblini.processInput('hello alfred and goodbye blini', { author: 'someguy' });\r\nconsole.log(blini.generateOutput('hello')); // Generates output without tag filtering\r\nconsole.log(blini.generateOutput('hello', 'author', 'rzumer')); // Generates output with tag filtering\r\nconsole.log(blini.generateOutput('goodbye', 'author', 'rzumer')); // Generates output with tag filtering, but context missing from the tagged author's vocabulary\r\n```\r\n\r\nExample output:\r\n\r\n```\r\nhello alfred and goodbye blini\r\nhello world and hello blini\r\ngoodbye?\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzumer%2Fblini","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frzumer%2Fblini","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzumer%2Fblini/lists"}