Delivery
The runtime returns the exact text, its language and a delivery ID. Your app speaks or displays it, then confirms the outcome. The HTTP server refuses a server-side speaker outright.
One state machine, reached over JSON. Your app calls it with the key we issue — from Swift, Kotlin, Python, a browser or a Node backend — and keeps its own symbols, voice and interface.
Import the factory and own the rendering. Nothing fetches or draws your artwork for you.
import { createTinkySpeak } from 'tinkyspeak-runtime';
const session = createTinkySpeak({
provider: { kind: 'tinkymind' },
language: 'en', partnerLanguage: 'en',
profile: { age: 7, level: 'sentence', choices: 6 },
symbols: choice => yourArtworkFor(choice),
});
const state = await session.hear({ text: 'Would you like a drink?' });
renderYourTiles(state.choices);
// In YOUR user-selection handler:
const selected = session.select({ choiceId: chosenTileId });
const pending = await session.speak({ draftId: selected.draft.id });
// Your app delivers pending.speech.text in pending.speech.language.
session.confirmSpeech({ speechId: pending.speech.id, outcome: 'spoken' });Run tinkyspeak serve and the whole engine is a local JSON API on 127.0.0.1:7437. The included fetch client has no Node imports, so it bundles straight into a web app.
import { createTinkySpeakClient } from 'tinkyspeak-runtime/client';
const api = createTinkySpeakClient({ url: 'http://127.0.0.1:7437' });
const session = await api.createSession({ language: 'en', partnerLanguage: 'en' });
const state = await session.hear({ text: 'Are you hungry?' });
renderYourTiles(state.choices);
const selected = await session.select({ choiceId: chosenTileId });
const pending = await session.speak({ draftId: selected.draft.id });
await yourSpeechEngine(pending.speech.text, pending.speech.language);
await session.confirmSpeech({ speechId: pending.speech.id, outcome: 'spoken' });| Method and route | What it does |
|---|---|
GET /v1/capabilities | Model language metadata, session-memory limits and output ownership. |
POST /v1/sessions | Optional language, partnerLanguage, profile, context; returns state with an id. |
POST /v1/sessions/ID/hear | What the partner said; returns the state with choices. |
POST /v1/sessions/ID/select | A chosen choiceId, optionally edited text; creates a draft. |
POST /v1/sessions/ID/compose | The user's own words; creates a draft. |
POST /v1/sessions/ID/speak | A draftId; returns an app-owned delivery request in speech. |
POST /v1/sessions/ID/confirm-speech | spoken, displayed, failed or cancelled. |
POST /v1/sessions/ID/scan | Base64 images and a mode; returns observations and selectable tiles. |
POST /v1/sessions/ID/languages | Partner and user language, validated against the model. |
DELETE /v1/sessions/ID | Closes the session and cancels its work. |
Selection, draft and speech IDs prevent stale or repeated actions. Concurrent actions return 409 session_busy. Errors come back as {"error":{"code":"...","message":"..."}}. No conversation request bodies are logged.
The runtime returns the exact text, its language and a delivery ID. Your app speaks or displays it, then confirms the outcome. The HTTP server refuses a server-side speaker outright.
Each choice carries a sentence, label, emoji and ID. Supply your own symbol or photo through symbols(choice), or map exact sentences in config. Emoji remain the fallback.
This is one trusted app's API. Your backend owns accounts, authorization and session ownership when serving many users.
The runtime ships adapters, not weights.
{ kind: 'tinkymind' } // installed local child model; English only
{ kind: 'gemini', model: 'YOUR_MODEL', apiKeyEnv: 'GEMINI_API_KEY' }
{ kind: 'ollama', model: 'YOUR_INSTALLED_MODEL' }
{ kind: 'compatible', endpoint: 'https://your-model.example/v1', model: 'YOUR_MODEL' }
{ kind: 'board', tiles: yourAuthoredTiles } // no generative model at all