{"_id":"@drincs/pixi-vn-ai","_rev":"2-81bd82526de561fcdacc269339cdff55","name":"@drincs/pixi-vn-ai","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@drincs/pixi-vn-ai","version":"0.1.0","keywords":["game","js","novel","pixi","pixi-vn","visual","visual-novel","ai","llm","genai","generative-ai","text-generation","image-generation","ai-sdk","webllm","comfyui","stable-diffusion"],"_id":"@drincs/pixi-vn-ai@0.1.0","maintainers":[{"name":"blackram","email":"DRincs.contact@gmail.com"}],"dist":{"shasum":"f55892e136d8cffe6cbf3f12e6d9338472740622","tarball":"https://registry.npmjs.org/@drincs/pixi-vn-ai/-/pixi-vn-ai-0.1.0.tgz","fileCount":24,"integrity":"sha512-uUtHghqEkNLXe0oODOsvreV3IaMdmPpdHiO3YSUgmR+8GdTLXxRbbjSPEIO0RZfatxPzWAN/AWz9XqkNBJsLjg==","signatures":[{"sig":"MEUCIBJdGKg+7SEhQ95Ik65QSKFaXY/ypBP5lHsmUXDna8gwAiEAjHIWmctGlYKw/dlvbJ1OhDss1HX85H9eHrQfZnx43vs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":74890},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","default":"./dist/index.mjs","require":"./dist/index.cjs"},"./prompt":{"types":"./dist/prompt.d.ts","import":"./dist/prompt.mjs","default":"./dist/prompt.mjs","require":"./dist/prompt.cjs"},"./comfyui":{"types":"./dist/comfyui.d.ts","import":"./dist/comfyui.mjs","default":"./dist/comfyui.mjs","require":"./dist/comfyui.cjs"},"./providers":{"types":"./dist/providers.d.ts","import":"./dist/providers.mjs","default":"./dist/providers.mjs","require":"./dist/providers.cjs"}},"gitHead":"d4e8155f252f22c07a09f6a340dda1d8d1269310","scripts":{"dev":"tsup --watch","docs":"typedoc","lint":"biome lint","test":"vitest","build":"tsup --config tsup.config.ts","check":"biome check","format":"biome format --write","playground":"npm run build && npm run dev --workspace=playground"},"_npmUser":{"name":"blackram","email":"DRincs.contact@gmail.com"},"workspaces":["playground"],"_npmVersion":"11.6.2","description":"This library is not to expose low-level LLM APIs, but to provide a high-level AI abstraction designed specifically for Pixi'VN","directories":{},"_nodeVersion":"24.12.0","dependencies":{"vitest":"^4.1.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^29.0.2","ts-node":"^10.9.2","typedoc":"^0.28.19","typescript":"^5.9.3","@biomejs/biome":"^2.4.12","vite-tsconfig-paths":"^6.1.1","typedoc-plugin-markdown":"^4.12.0","@stable-canvas/comfyui-client":"^1.5.9"},"peerDependencies":{"ai":">=7.0.31","@drincs/pixi-vn":">=1.8.25","@mlc-ai/web-llm":">=0.2.84","@stable-canvas/comfyui-client":">=1.5.9"},"peerDependenciesMeta":{"@stable-canvas/comfyui-client":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pixi-vn-ai_0.1.0_1784496561454_0.03972768008742622","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@drincs/pixi-vn-ai","description":"This library is not to expose low-level LLM APIs, but to provide a high-level AI abstraction designed specifically for Pixi'VN","version":"0.2.0","type":"module","main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","publishConfig":{"access":"public"},"workspaces":["playground"],"scripts":{"dev":"tsup --watch","build":"tsup --config tsup.config.ts","lint":"biome lint","check":"biome check","format":"biome format --write","test":"vitest","docs":"typedoc","playground":"npm run build && npm run dev --workspace=playground","generate-options-schema":"node scripts/generate-options-schema.mjs && biome format --write src/ink/options-schema.generated.ts"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs","default":"./dist/index.mjs"},"./prompt":{"types":"./dist/prompt.d.ts","import":"./dist/prompt.mjs","require":"./dist/prompt.cjs","default":"./dist/prompt.mjs"},"./providers":{"types":"./dist/providers.d.ts","import":"./dist/providers.mjs","require":"./dist/providers.cjs","default":"./dist/providers.mjs"},"./comfyui":{"types":"./dist/comfyui.d.ts","import":"./dist/comfyui.mjs","require":"./dist/comfyui.cjs","default":"./dist/comfyui.mjs"},"./ink":{"types":"./dist/ink.d.ts","import":"./dist/ink.mjs","require":"./dist/ink.cjs","default":"./dist/ink.mjs"}},"devDependencies":{"@biomejs/biome":"^2.4.12","@drincs/pixi-vn-ink":"^1.1.9","@drincs/pixi-vn-json":"^1.13.17","@stable-canvas/comfyui-client":"^1.5.9","jsdom":"^29.0.2","ts-node":"^10.9.2","tsup":"^8.5.1","typedoc":"^0.28.19","typedoc-plugin-markdown":"^4.12.0","typescript":"^5.9.3","vite-tsconfig-paths":"^6.1.1","zod":"^4.4.3"},"peerDependencies":{"@drincs/pixi-vn":">=1.8.25","@drincs/pixi-vn-ink":">=1.1.9","@mlc-ai/web-llm":">=0.2.84","@stable-canvas/comfyui-client":">=1.5.9","ai":">=7.0.31","zod":">=4.4.0"},"peerDependenciesMeta":{"@drincs/pixi-vn-ink":{"optional":true},"@stable-canvas/comfyui-client":{"optional":true},"zod":{"optional":true}},"dependencies":{"vitest":"^4.1.4"},"keywords":["game","js","novel","pixi","pixi-vn","visual","visual-novel","ai","llm","genai","generative-ai","text-generation","image-generation","ai-sdk","webllm","comfyui","stable-diffusion"],"gitHead":"f5205e28d51c043e8eeb27fcb2b61433f3751ac2","_id":"@drincs/pixi-vn-ai@0.2.0","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-A/bE68EQgVDdWIKyhCol+SWOr6rgXXb1WDJqbwarVJ4C3E6Y//xCD6CTBJGKXHdRmo+O0gMWnLpbIHsFF7XHIQ==","shasum":"0943bca10b4c5e4cd48adb3fa420d8f93827f3d1","tarball":"https://registry.npmjs.org/@drincs/pixi-vn-ai/-/pixi-vn-ai-0.2.0.tgz","fileCount":28,"unpackedSize":170036,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHTwoAM58sZKIPNRfIscpRvZcR6sj5HEzAqlNAW9292fAiBBxK/uTel9Uho2H4SPwH4cmHCMsa7wZehfNaqIV3vr+A=="}]},"_npmUser":{"name":"blackram","email":"DRincs.contact@gmail.com"},"directories":{},"maintainers":[{"name":"blackram","email":"DRincs.contact@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pixi-vn-ai_0.2.0_1784885761981_0.8882215399875659"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-19T21:29:21.346Z","modified":"2026-07-24T09:36:02.303Z","0.1.0":"2026-07-19T21:29:21.613Z","0.2.0":"2026-07-24T09:36:02.135Z"},"keywords":["game","js","novel","pixi","pixi-vn","visual","visual-novel","ai","llm","genai","generative-ai","text-generation","image-generation","ai-sdk","webllm","comfyui","stable-diffusion"],"description":"This library is not to expose low-level LLM APIs, but to provide a high-level AI abstraction designed specifically for Pixi'VN","maintainers":[{"name":"blackram","email":"DRincs.contact@gmail.com"}],"readme":"# @drincs/pixi-vn-ai\n\nAI-powered dialogue and image generation for [Pixi'VN](https://pixi-vn.com) visual novels.\n\nThis library is **not** a low-level LLM client. It's a small, high-level abstraction with three\nfunctions — `ai.text.generateDialog`, `ai.image.generateBackground`, `ai.image.generateElement` —\nthat hide all prompt engineering: you pass a natural-language request plus a few structured\noptions (scene, style, speaker, alignment, ...), and the library assembles the actual prompt sent\nto whatever model you configured (or a local fallback if you configured none).\n\nIt's designed to run entirely in the **browser**.\n\n## Installation\n\n```npm\nnpm install @drincs/pixi-vn-ai @drincs/pixi-vn ai @mlc-ai/web-llm\n```\n\n- `@drincs/pixi-vn` — the visual novel engine this library is built for.\n- `ai` ([AI SDK](https://ai-sdk.dev)) — used to talk to any AI SDK-compatible model (OpenAI,\n  Anthropic, Google, Ollama, ...).\n- `@mlc-ai/web-llm` — powers the built-in local fallback model (see below), so you never _have_ to\n  configure a provider to get started.\n\n## Initializing\n\nCall `ai.init(...)` once, at startup:\n\n```ts\nimport { ai } from \"@drincs/pixi-vn-ai\";\n\nawait ai.init();\n```\n\nWith no arguments, `ai.init()` downloads and runs a small local [WebLLM](https://github.com/mlc-ai/web-llm)\nmodel (`SmolLM2-360M-Instruct`) directly in the browser — no API key, no server, nothing to\nconfigure. This covers `ai.text.generateDialog` out of the box. **WebLLM cannot generate images**,\nso `ai.image.generateBackground`/`ai.image.generateElement` require an external model regardless.\n\nTo use a specific model instead, pass it through [`ai` (the AI SDK)](https://ai-sdk.dev/providers):\n\n```ts\nimport { ai } from \"@drincs/pixi-vn-ai\";\nimport { openai } from \"@ai-sdk/openai\";\n\nawait ai.init({\n  textProvider: openai(\"gpt-5\"),\n  imageProvider: openai.image(\"gpt-image-1\"),\n});\n```\n\n- `textProvider` is a [`LanguageModel`](https://ai-sdk.dev/docs/foundations/providers-and-models)\n  from any AI SDK provider. It drives `ai.text.generateDialog`, and is also used as a fallback for\n  image generation on multimodal models (e.g. Gemini's image generation).\n- `imageProvider` is an [`ImageModel`](https://ai-sdk.dev/docs/ai-sdk-core/image-generation) from\n  any AI SDK provider. It drives `ai.image.generateBackground`/`ai.image.generateElement`.\n\nYou don't need both: set only `textProvider` for dialogue (images keep needing one of the two, as\nabove), only `imageProvider` for images, or both.\n\n### Using ComfyUI for images\n\nIf you want to generate images with a self-hosted [ComfyUI](https://github.com/comfyanonymous/ComfyUI)\nserver instead, also install:\n\n```npm\nnpm install @stable-canvas/comfyui-client\n```\n\nIf you'd rather generate images with your own self-hosted [ComfyUI](https://github.com/comfyanonymous/ComfyUI)\nserver, pass a `ComfyUIImageModel` as `imageProvider`:\n\n```ts\nimport { ai } from \"@drincs/pixi-vn-ai\";\nimport { ComfyUIImageModel } from \"@drincs/pixi-vn-ai/comfyui\";\nimport myWorkflow from \"./my-workflow-api.json\"; // exported from ComfyUI's \"Save (API Format)\"\n\nawait ai.init({\n  imageProvider: new ComfyUIImageModel({\n    apiHost: \"127.0.0.1:8188\",\n    workflow: myWorkflow,\n    // the node/input in `myWorkflow` that holds the positive prompt (usually a CLIPTextEncode node)\n    promptNodeId: \"6\",\n    promptInputName: \"text\", // optional, defaults to \"text\"\n  }),\n});\n```\n\nSince ComfyUI runs an arbitrary node graph rather than accepting a plain prompt, `ComfyUIImageModel`\nonly injects the developer request into the node/input you point it at — everything else\n(checkpoint, sampler, seed, resolution, ...) is whatever your exported workflow already specifies.\n\n## Usage\n\n### `ai.text.generateDialog`\n\n```ts\nconst line = await ai.text.generateDialog(\n  \"The advisor reassures the king that the kingdom is safe.\",\n  {\n    history: true,\n    scene: \"The throne room, late at night, lit only by torches.\",\n    style: \"Tense, formal, a little melancholic.\",\n    language: \"English\",\n    context: \"The king has not slept in three days.\",\n    speaker: \"advisor\",\n    listeners: [\"king\"],\n  },\n);\n```\n\nGenerates a line of dialogue. `history` (on by default) pulls Pixi'VN's narrative history so the\nmodel has continuity; `scene`/`style`/`context` steer tone and setting; `language` fixes the output\nlanguage; `speaker`/`listeners` accept a character ID (resolved against Pixi'VN's\n`RegisteredCharacters`, or used as-is if not registered) or an object, single or array.\n\n<details>\n<summary>Prompt generated by the call above</summary>\n\n````text\n### Instructions\n\nYou are generating narrative dialogue for a visual novel.\nGenerate Markdown text.\nKeep the Markdown simple and readable.\nDo not use headings.\nDo not use tables.\nDo not use unnecessary lists.\nUse bold and italic only when they improve readability.\nHTML is allowed only when necessary; prefer <span> for styling (e.g. colors). Avoid excessive HTML usage.\nReturn only the generated content.\nDo not explain the generated result.\n\n### Developer Request\n\nThe advisor reassures the king that the kingdom is safe.\n\n### Narrative History\n\nThe narrative history so far, serialized as JSON.\n\n​```\n[\n  {\n    \"stepIndex\": 4,\n    \"dialogue\": {\n      \"character\": \"advisor\",\n      \"text\": \"Your Majesty, the council awaits your decision.\"\n    }\n  }\n]\n​```\n\n### Scene\n\nThe throne room, late at night, lit only by torches.\n\n### Style\n\nTense, formal, a little melancholic.\n\n### Language\n\nThe language the generated content must be written in.\n\n​```\nEnglish\n​```\n\n### Context\n\nThe king has not slept in three days.\n\n### Speaker\n\nThe character(s) speaking, serialized as JSON.\n\n​```\n[\n  {\n    \"id\": \"advisor\"\n  }\n]\n​```\n\n### Listeners\n\nThe character(s) receiving the dialogue, serialized as JSON.\n\n​```\n[\n  {\n    \"id\": \"king\"\n  }\n]\n​```\n````\n\n(`Narrative History` is read automatically from Pixi'VN — shown here as an example. `Speaker`/\n`Listeners` show the `{ id }` fallback for an unregistered character; a registered one would include\nits full data instead.)\n\n</details>\n\n### `ai.image.generateBackground`\n\n```ts\nconst background = await ai.image.generateBackground(\n  \"The throne room, lit by torches, a storm outside the windows.\",\n  {\n    history: false,\n    scene: \"Night, medieval castle interior.\",\n    style: \"Oil painting, warm color palette.\",\n    context:\n      \"This will be used as the main backdrop for the throne room scenes.\",\n    referenceImage: \"throne-room-sketch\",\n  },\n);\n```\n\nGenerates a background meant to fill the whole game canvas. Its size is read directly from Pixi'VN\nand included in the prompt automatically — you never pass it. `referenceImage` is a Pixi'VN asset\nalias (or a URL/data URI); when given, it's used as image-to-image guidance.\n\n<details>\n<summary>Prompt generated by the call above</summary>\n\n````text\n### Instructions\n\nYou are generating a background illustration for a visual novel scene.\nThis image fills the entire game canvas: match the canvas size given below and cover the full frame edge-to-edge, with no borders, letterboxing or unused margins.\nIf a reference image is provided, use it as the basis for the generated image.\nOtherwise, generate the image purely from the textual context below.\nMatch the requested scene, style and subjects as closely as possible.\n\n### Developer Request\n\nThe throne room, lit by torches, a storm outside the windows.\n\n### Scene\n\nNight, medieval castle interior.\n\n### Style\n\nOil painting, warm color palette.\n\n### Context\n\nThis will be used as the main backdrop for the throne room scenes.\n\n### Reference Image\n\nA reference image has been provided, encoded as a base64 data URI, and should be used as visual guidance.\n\n​```\ndata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... (truncated)\n​```\n\n### Canvas Size\n\n1920x1080\n````\n\n</details>\n\n### `ai.image.generateElement`\n\n```ts\nconst advisorSprite = await ai.image.generateElement(\n  \"The advisor, an elderly man with a long grey beard, wearing dark blue robes, worried expression.\",\n  {\n    history: false,\n    scene: \"Standing in the throne room.\",\n    style: \"Oil painting, warm color palette, matching the background.\",\n    context: \"This character will be reused across multiple scenes.\",\n    referenceImage: \"advisor-concept-art\",\n    backgroundImage: \"throne-room-background\",\n    align: { x: 0.75, y: 1 },\n  },\n);\n```\n\nGenerates a single element (typically a character) meant to be layered on top of other visuals,\nwith a transparent background. `backgroundImage` (a Pixi'VN asset alias, or `true` to capture\nwhatever is currently on the game canvas) gives the model visual context on what it's being\ncomposited over; `align` tells it where on screen it'll be placed, so it can compose accordingly.\nWhen both `referenceImage` and `backgroundImage` are set, both appear in the prompt, but only\n`referenceImage` is actually forwarded to the model as an image-to-image reference.\n\n<details>\n<summary>Prompt generated by the call above</summary>\n\n````text\n### Instructions\n\nYou are generating a single visual element (e.g. a character) for a visual novel, meant to be layered on top of a background.\nThe area behind the subject must be fully transparent: do not generate any background, ground or scenery of your own.\nIf a background reference image is provided, use it only to match lighting, perspective and scale; do not reproduce it.\nIf alignment values are provided, compose the subject so it reads naturally when placed at that position on screen.\nMatch the requested scene, style and subjects as closely as possible.\n\n### Developer Request\n\nThe advisor, an elderly man with a long grey beard, wearing dark blue robes, worried expression.\n\n### Scene\n\nStanding in the throne room.\n\n### Style\n\nOil painting, warm color palette, matching the background.\n\n### Context\n\nThis character will be reused across multiple scenes.\n\n### Reference Image\n\nA reference image has been provided, encoded as a base64 data URI, and should be used as visual guidance.\n\n​```\ndata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... (truncated)\n​```\n\n### Background Reference\n\nThe background image this element will be placed over, encoded as a base64 data URI: use it as visual guidance for lighting, perspective and scale.\n\n​```\ndata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... (truncated)\n​```\n\n### Alignment\n\nWhere the element will be positioned on the canvas: x and y are each a 0-1 fraction of the canvas' width/height, and that same fraction is also used as the element's own anchor point, so the value describes both where on the canvas the point sits and which point of the element is placed there. 0 = the element's left/top edge is flush against the canvas' left/top edge, 1 = the element's right/bottom edge is flush against the canvas' right/bottom edge, 0.5 = the element is centered on that axis.\n\n​```\n{\n  \"x\": 0.75,\n  \"y\": 1\n}\n​```\n````\n\n</details>\n\n## Customizing the prompt templates\n\nEvery generated prompt starts from a built-in \"Instructions\" template, which you can override per\nkind:\n\n```ts\nai.templates.dialog = { instructions: \"...\" };\nai.templates.image.background = { instructions: \"...\" };\nai.templates.image.element = { instructions: \"...\" };\n```\n","readmeFilename":"README.md"}