Visual NovelEditor

Documentation

AI characters in a scene

Ask a character for a live reply mid-scene. Every reply is a paid model call.

A novel can stop mid-scene, send the player's line to a model, and display what the character answers. The reply arrives in a normal Ren'Py variable, so the rest of the scene is written the way any other scene is written.

Each reply is one real model call billed to the author's account: the provider's cost for the tokens actually used, plus 20%, rounded up to whole credits (1 credit = $0.01). Nothing about this is free, and a loop that asks in a `while` block spends on every pass.

Install the SDK in the engine

The statement lives in the renpy-js package @renpyjs/ai. The parser must know the statement before a script is compiled, and the runtime must have the handler installed before the scene runs.

// once, where the player is created
import { installAI, registerAIStatement } from '@renpyjs/ai';

registerAIStatement();
const ai = installAI(controller.runtime, {
  project: 'vnp_your_project_id',
  characters: [{
    id: 'mara',
    name: 'Mara',
    persona: 'Night nurse on ward nine. Tired, blunt, kind under it.',
    greeting: 'You again.',
    fallback: 'Mara says nothing.',
  }],
});

A scene where the character answers freely

`ai <character> <expression> as <variable>` sends the expression as the player's line and writes the reply into the variable. The variable holds text that is already escaped, so a reply can never inject Ren'Py text tags.

define mara = Character("Mara")

label ward_nine:
    scene bg ward
    show mara neutral
    mara "You again. Ask, then let me work."

    $ question = renpy.input("Ask Mara anything", length=200)

    ai mara question as mara_reply

    if _ai_ok:
        mara "[mara_reply]"
    else:
        mara "..."
        "She is not listening. ([_ai_error])"

    menu:
        "Ask something else":
            jump ward_nine
        "Let her work":
            jump corridor

What the endpoint does

POST /api/visualnovel/ai/character takes project, either an inline persona (character, name, persona, greeting, scenario) or netwrck_character naming one of your own netwrck characters, plus player_input, optional scene, history, model, max_tokens and session. It answers with text, renpy_text, model, usage, cost, billed, latency_ms and job_id.

It requires a logged-in account, the project must belong to that account, and a netwrck character must belong to it too. A character marked adult is refused with 403 unless the account allows adult content. GET /api/visualnovel/ai/characters lists your characters and the current limits.

Default model is grok-4.5 at low reasoning effort. Overrides are limited to the models this server already routes; an unknown id is rejected rather than dispatched.

curl -X POST https://netwrck.com/api/visualnovel/ai/character \
  -H 'content-type: application/json' \
  -d '{"project":"vnp_x","character":"mara","name":"Mara",
       "persona":"Night nurse on ward nine.",
       "player_input":"Do you trust the captain?"}'

Price

Base model price plus 20%, on the tokens the call actually used, rounded up to whole credits. A short exchange on grok-4.5 is normally 1 credit; a long scene summary in the prompt costs more because the input tokens are larger.

The charge is written to the visual novel billing ledger with the provider cost, the markup and the job id, and is visible at /api/visualnovel/billing/ledger. If a reply comes back empty or unusable after the provider was already paid, that ledger row is refunded exactly once.

Every ai statement spends credits. Test with mode: 'offline' or dry_run: true, both of which are free.

Failure, timeout, rollback and saves

A failed or timed out call writes the character's fallback line and sets _ai_ok to False and _ai_error to the reason. The scene keeps running; nothing throws and nothing stalls. The server's deadline is 55 seconds and the SDK waits 75, so a call the server charged for is never abandoned by the client first.

Every request carries a request_id. The same request_id inside ten minutes is answered from the server's stored reply with replayed: true, without a second model call and without a second charge, so a retry or a lost response cannot bill twice.

A paid reply blocks rollback past it, so a player cannot rewind and be billed twice for the same moment. Identical questions inside one playthrough are answered from a memo cache instead of a second call, which also means the same question in the same scene gives the same answer.

A save stores the reply like any other variable, so loading shows the same line without calling again. Conversation memory is per playthrough and capped at 12 turns server side, 6 exchanges client side by default.

Rate limits

One request per second per account with a burst of four, and at most two calls in flight per account. Player input is capped at 2 KB, the scene summary at 4 KB, the persona at 6 KB, output at 600 tokens, and a reply is trimmed to four lines and 900 characters.

There is no daily or per-project spending cap: the limits above shape the rate, the account balance is the ceiling. The SDK's maxCalls and budgetCredits options are the per-playthrough guard, and they are client side.

Welcome back

Create your free account

Already have an account? Log in