[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81349":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":15,"starSnapshotCount":15,"syncStatus":13,"lastSyncTime":25,"discoverSource":26},81349,"the-world","LyalinDotCom\u002Fthe-world","LyalinDotCom","Experimental Project: What if Gemma had an SDK for web developers to builds games around it as the game brain?","",null,"TypeScript",42,2,41,0,1,3,1.43,false,"main",[],"2026-06-12 02:04:14","# The World\n\n**The World** is a prototype SDK for game-native local AI plus a playable Electron demo that proves the runtime path works with Gemma through Ollama, oMLX, and experimental LiteRT-LM.\n\nThe core promise:\n\n> Dynamic game text without surrendering control of your game.\n\nThis repo is not mainly a game. The game is the showcase. The main product idea is a TypeScript toolkit that lets developers turn local LLMs into controlled, schema-bound, lore-aware game systems: NPC dialogue, area events, ambient barks, overheard conversations, memory, policy checks, cache-first runtime behavior, diagnostics, and safe Electron IPC.\n\nThe playable layer still needs to feel like something you can inhabit. Light combat exists for that reason: it makes generated threats, constable calls, bandit ambushes, escape, damage, and recovery testable as a player experience instead of as isolated model outputs. Gemma can propose danger and character intent, but the game owns combat rules, health, damage, defeat, inventory, and all authoritative state.\n\n![The World playable demo](docs\u002Fthe-world-demo.png)\n\n## What Developers Get\n\n- `@game-llm\u002Fcore`: NPC definitions, scene context, schema-bound recipes, area-event triggers, prompt compilation, memory, cache, validation, repair, fallback behavior, cancellation, and a deterministic mock provider for tests.\n- `@game-llm\u002Follama`: local Ollama provider with health checks, installed-model discovery, warmup, structured JSON calls, Gemma-friendly defaults, timing\u002Ftoken metrics, runtime options, and `AbortSignal` support.\n- `@game-llm\u002Fomlx`: local oMLX provider for OpenAI-compatible Gemma models. Generation uses Vercel AI SDK's `@ai-sdk\u002Fopenai-compatible` provider instead of owning the chat-completions client code.\n- `@game-llm\u002Flitert-lm`: experimental LiteRT-LM provider with imported-model health checks, warmup, structured output cleanup, and a persistent Python bridge that keeps the LiteRT engine loaded between requests.\n- `@game-llm\u002Felectron`: safe IPC bridge so a renderer can request dialogue, area events, barks, overheard exchanges, warmup, pregeneration, and cancellation without arbitrary model access.\n- `apps\u002Fthe-world`: a playable canvas\u002FElectron demo that uses the SDK under real runtime pressure.\n\nThe package boundary is deliberate: **core knows games, providers know models, apps know their own lore**.\n\n## SDK Quick Start\n\nThese packages are local workspace packages right now. In this repo, import directly from the workspaces.\n\n```ts\nimport { createGameAI } from '@game-llm\u002Fcore';\nimport { ollamaProvider } from '@game-llm\u002Follama';\n\nconst ai = createGameAI({\n  provider: ollamaProvider({\n    model: 'gemma4:e4b',\n    host: 'http:\u002F\u002F127.0.0.1:11434',\n    keepAlive: '30m',\n    think: false,\n    runtimeOptions: {\n      numCtx: 4096,\n      numBatch: 128\n    }\n  }),\n  world: {\n    id: 'emberfall',\n    name: 'Emberfall',\n    lore: [\n      'Rivergate avoids the old mill after dark.',\n      'The Iron Crows are mercenaries, not royal soldiers.'\n    ],\n    styleGuide: 'Grounded low fantasy. Short sentences. No modern slang.'\n  },\n  runtime: {\n    mode: 'local-only',\n    cache: 'session',\n    maxLatencyMs: 24_000,\n    pregeneration: {\n      enabled: true,\n      cacheOnlyRuntimeRecipes: ['npc.bark', 'npc.overhear']\n    }\n  },\n  policies: {\n    canonOnly: true,\n    noQuestMutationWithoutTool: true,\n    noRewardCreation: true,\n    contentRating: 'T'\n  }\n});\n\nawait ai.provider.warmup?.({\n  prompt: 'Warm up for short schema-bound NPC dialogue.',\n  timeoutMs: 45_000\n});\n```\n\n## Define An NPC\n\nThe SDK API exposes game concepts, not raw chat.\n\n```ts\nconst blacksmith = ai.npc({\n  id: 'npc.blacksmith.orin',\n  persona: {\n    name: 'Orin',\n    role: 'village blacksmith',\n    traits: ['gruff', 'protective', 'superstitious'],\n    mood: 'wary',\n    speechStyle: 'Short practical replies. Dry humor. No modern slang.',\n    knows: [\n      'Rivergate locals fear the old mill.',\n      'The mill wheel turns on still nights.'\n    ],\n    doesNotKnow: [\n      'the true cause beneath the old mill'\n    ],\n    rules: [\n      'Answer direct questions plainly before adding color.',\n      'Do not invent towns, factions, rewards, or player actions.',\n      'Do not reveal hidden causes before the player earns trust.'\n    ]\n  },\n  memory: {\n    scope: 'npc',\n    maxEntries: 50\n  }\n});\n```\n\n## Run Dialogue\n\nEvery dialogue request carries structured scene and player context. The model returns a typed `DialogueTurn`, not arbitrary prose.\n\n```ts\nconst controller = new AbortController();\n\nconst turn = await blacksmith.respond({\n  playerText: 'Why is everyone scared of the old mill?',\n  scene: {\n    location: 'Rivergate',\n    biome: 'river road',\n    timeOfDay: 'late afternoon',\n    weather: 'thin cloud',\n    nearbyCharacters: ['npc.guard.elda'],\n    visibleFeatures: ['The Old Mill', 'river bridge', 'waystone'],\n    contextualFacts: [\n      'The Old Mill creaks after sunset even when the air is still.',\n      'Locals lower their voices when they talk about the mill.'\n    ],\n    coordinates: { x: -1605, y: -652 }\n  },\n  player: {\n    id: 'player',\n    knownFacts: ['The old mill is avoided after dark.'],\n    visibleEquipment: ['travel cloak', 'worn boots']\n  },\n  relationship: 'new acquaintance',\n  npcState: {\n    mood: 'wary',\n    disposition: 10,\n    willTalkAgain: true\n  }\n}, {\n  assess: true,\n  timeoutMs: 20_000,\n  signal: controller.signal\n});\n\nconsole.log(turn.text);\nconsole.log(turn.mood);\nconsole.log(turn.events);\n```\n\n`DialogueTurn` includes:\n\n- `text`: the line to show in the game.\n- `emotion` and `animationHint`: presentation hints.\n- `mood`, `attitudeDelta`, `willTalkAgain`, and `refusalReason`: session state.\n- `events`: typed game events such as `dialogue.say`, `npc.emotion`, or `npc.callForHelp`.\n- `memoryWrites`: durable facts the game may store.\n- `safetyFlags`: warnings or blocks.\n- `trace`: prompt, provider, model, latency, cache status, raw output, and schema repair details.\n\nThe game engine still owns authority. The model can propose events; the game decides what to accept.\n\n## Runtime Stacks\n\nThe playable demo asks which Gemma stack to use on startup:\n\n- **Ollama Gemma4 E4B**: the default path. It uses `gemma4:e4b`, `think:false`, `num_ctx:4096`, `num_batch:128`, and `keep_alive:30m`.\n- **oMLX Gemma4 E4B MLX 8-bit**: local OpenAI-compatible oMLX server support through AI SDK. The default endpoint is `http:\u002F\u002F127.0.0.1:8000\u002Fv1`, API key `1234`, model `gemma-4-E4B-it-MLX-8bit`.\n- **LiteRT-LM Gemma4 E4B**: experimental. It uses the imported `gemma4-e4b-litert` model through a persistent GPU bridge. Warmup and tiny prompts are fast, but the current full SDK dialogue prompt is still noticeably slower than Ollama in the game.\n\nYou can skip the startup chooser with:\n\n```sh\nTHE_WORLD_AI_STACK=ollama npm start\nTHE_WORLD_AI_STACK=omlx npm start\nTHE_WORLD_AI_STACK=litert-lm npm start\n```\n\nImportant runtime environment variables:\n\n```sh\nTHE_WORLD_MODEL=gemma4:e4b\nTHE_WORLD_KEEP_ALIVE=30m\nTHE_WORLD_THINK=false\nTHE_WORLD_NUM_CTX=4096\nTHE_WORLD_NUM_BATCH=128\nTHE_WORLD_TIMEOUT_MS=30000\n\nTHE_WORLD_OMLX_BASE_URL=http:\u002F\u002F127.0.0.1:8000\u002Fv1\nTHE_WORLD_OMLX_API_KEY=1234\nTHE_WORLD_OMLX_MODEL=gemma-4-E4B-it-MLX-8bit\nTHE_WORLD_OMLX_STRUCTURED_OUTPUT=json_schema\n\nTHE_WORLD_LITERT_MODEL=gemma4-e4b-litert\nTHE_WORLD_LITERT_BACKEND=gpu\nTHE_WORLD_LITERT_MAX_TOKENS=4096\nTHE_WORLD_LITERT_LM_BIN=.venv\u002Flitert-lm\u002Fbin\u002Flitert-lm\n```\n\nLiteRT-LM setup is local to your machine:\n\n```sh\nnpm run install:litert-lm\nnpm run import:litert-gemma4\n```\n\nThe imported LiteRT model lives under `~\u002F.litert-lm\u002Fmodels`, not in this repo. The local `.venv\u002Flitert-lm` install is ignored by git.\n\noMLX setup is external to this repo. Start oMLX with the OpenAI-compatible API enabled, then choose the oMLX stack in the startup dialog or set `THE_WORLD_AI_STACK=omlx`. `THE_WORLD_OMLX_STRUCTURED_OUTPUT` accepts `json_schema`, `json_object`, or `none`; `json_schema` uses AI SDK structured output plus the core SDK's normal JSON validation path.\n\n## Dialogue Assessment And Actions\n\nThe demo can use three small model calls for player conversations:\n\n1. Generate the NPC reply.\n2. Assess mood and danger.\n3. Decide whether the NPC should continue, end the conversation, or call for help.\n\nThat split is intentional. Small local models are more reliable when each pass has one simple job. The action pass returns an enum, not arbitrary tool execution:\n\n```ts\ntype DialogueActionType = 'none' | 'endConversation' | 'callForHelp';\n```\n\nIn the sample game, `callForHelp` spawns constables on nearby roads, sends them toward the incident, and ends the conversation. In a real game, the same event would go through the game's validation and authority layer.\n\nFor responsiveness, the playable demo now skips the mood\u002Faction assessment pass for ordinary low-risk lines and keeps it on for threat-like or guard-relevant dialogue. That keeps a normal greeting to one model call while still testing Gemma's judgment when the player says something risky.\n\n## Area Events\n\nThe SDK also exposes a `world.areaEvent` recipe for special building triggers. The model returns a typed `AreaEvent`, not a free-form script:\n\n```ts\nconst event = await ai.areaEvent({\n  area: {\n    id: 'old-mill',\n    name: 'The Old Mill',\n    kind: 'mill',\n    lore: 'The Old Mill turns its wheel on windless nights.',\n    rumor: 'folk lower their voices when the Old Mill creaks after sunset'\n  },\n  scene: {\n    location: 'The Old Mill',\n    visibleFeatures: ['The Old Mill', 'river road'],\n    contextualFacts: ['Nobody brought grain to the mill today.']\n  },\n  allowedKinds: ['banditAmbush']\n}, {\n  cacheKey: 'area-event:old-mill:default',\n  refresh: true,\n  timeoutMs: 30_000\n});\n```\n\n`AreaEvent.kind` is one of:\n\n- `banditAmbush`: Gemma creates bandits and building-specific threat lines. The game may spawn actors, but Gemma does not decide damage, rewards, inventory, or quest completion.\n- `mysteriousBeing`: Gemma creates a place-bound being with a name, description, greeting, mood, and speech style. The demo turns it into an NPC conversation.\n- `strangeSounds`: Gemma creates short sensory lines near the structure with no direct interaction.\n\nIn the playable demo, the four authored landmarks preload their area events after model warmup. The app constrains each landmark to a specific event lane so every structure gets a distinct trigger instead of four independent random rolls. If a player reaches a landmark before preload finishes, the trigger visibly waits for Gemma instead of using canned content. Failed event generation is shown once with a backoff instead of silently retrying forever.\n\nCombat is intentionally renderer-owned demo scaffolding around these events. It is there so developers can play through Gemma-shaped scenarios and judge whether the generated setup actually works under pressure: bandits rushing the player, constables arriving after a risky conversation, villagers witnessing violence, and the player having enough agency to survive or disengage. The model does not award kills, remove health, decide loot, or complete quests.\n\nBow use is an explicit aim mode: click the Bow button to enter aiming, use the reticle to fire, then click Bow again to return to normal click-to-talk behavior. Bow shots can kill ordinary villagers, leaving persistent bodies in the world and sending nearby witnesses into Gemma-authored reactions such as panic, fleeing, screaming, or charging the player. Sword use is defensive and immediate: pressing Space swings at the nearest active enemy only, such as a charging villager, constable, or hostile event actor. It cannot target uninvolved villagers.\n\nThe player also has a small character sheet. Karma starts slightly positive and moves negative after violence, especially killing villagers or fighting constables. Dialogue, witness reactions, ambient barks, and area-event prompts include that sheet as player context, so Gemma can decide how a suspicious or frightened local should treat a known killer. The renderer records the facts; Gemma owns the social tone.\n\n## Runtime Controls\n\nLocal models can be expensive during play, so the SDK is built around control points.\n\n**Warmup**\n\n```ts\nawait ai.provider.warmup?.({ timeoutMs: 45_000 });\n```\n\nThis keeps the model loaded before the player needs the first real reply.\n\n**Cache-first runtime recipes**\n\n```ts\nruntime: {\n  cache: 'session',\n  pregeneration: {\n    enabled: true,\n    cacheOnlyRuntimeRecipes: ['npc.bark', 'npc.overhear']\n  }\n}\n```\n\nFor ambient text, the shipped game pre-generates barks and overheard exchanges, then uses cache-only reads while the player walks. That prevents background Gemma calls from dragging down movement and frame rate.\n\nIn the current demo, ambient pregeneration no longer blocks the player from entering the world. The loading screen waits for runtime health and model warmup, then schedules landmark area-event preload first and a small capped ambient cache job after that.\n\n**Refresh pregenerated content**\n\n```ts\nawait npc.bark(request, {\n  cacheKey: 'bark:npc.guard.elda:rivergate:warning',\n  refresh: true,\n  writeMemory: false\n});\n```\n\n**Cancel abandoned requests**\n\n```ts\nconst controller = new AbortController();\nconst promise = npc.respond(request, { signal: controller.signal });\n\ncontroller.abort();\nawait promise;\n```\n\nThe Ollama adapter receives the signal. Canceled requests are not converted into fake fallback dialogue.\n\n## Electron Integration\n\nThe Electron package keeps model access in the main process. The renderer gets a narrow bridge.\n\nMain process:\n\n```ts\nimport { createGameAI } from '@game-llm\u002Fcore';\nimport { registerGameAIIpc } from '@game-llm\u002Felectron';\nimport { ollamaProvider } from '@game-llm\u002Follama';\n\nconst ai = createGameAI({\n  provider: ollamaProvider({ model: 'gemma4:e4b' }),\n  world: { id: 'the-world' },\n  runtime: { cache: 'session' }\n});\n\nconst cleanup = registerGameAIIpc(ipcMain, ai);\n```\n\nPreload:\n\n```ts\nimport { contextBridge, ipcRenderer } from 'electron';\nimport { exposeGameAIBridge } from '@game-llm\u002Felectron';\n\nexposeGameAIBridge(contextBridge, ipcRenderer);\n```\n\nRenderer:\n\n```ts\nconst requestId = `dialogue:${npc.id}:${Date.now()}`;\n\nconst turn = await window.gameAI.dialogue({\n  requestId,\n  npc,\n  request,\n  options: { assess: true }\n});\n\nawait window.gameAI.cancel({ requestId });\n```\n\nThe bridge also exposes `window.gameAI.areaEvent(...)` and accepts `areaEvent` jobs in `preGenerate(...)`. Renderer payloads are runtime validated before reaching `GameAI`.\n\nThe IPC bridge validates payloads with the shared Zod schemas from `@game-llm\u002Fcore`.\n\n## The Playable Demo\n\n`apps\u002Fthe-world` is a stress test for the SDK ideas:\n\n- Fixed large 2D map with Rivergate, Mosswake, Cindervale, woods, roads, houses, and marked landmarks.\n- Startup stack chooser for Ollama, oMLX, or experimental LiteRT-LM, with `THE_WORLD_AI_STACK` for deterministic runs.\n- Loading screen that checks runtime health, warms the selected Gemma model, and shows failures plainly.\n- Random spawn near one of the towns.\n- Left-side click-to-talk panel with speaker portraits, chat history, immediate local goodbye, and a visible thinking animation while Gemma is generating.\n- NPC mood\u002Fdisposition state and Gemma-controlled refusal\u002Fend-conversation behavior for lines that need assessment.\n- Basic renderer-owned combat for making generated scenarios playable: health that slowly recovers, Bow-button aim mode with ten arrows, Space-bar sword slashes, road-spawned constables, hostile event actors, persistent bodies, a karma\u002Freputation character sheet, and Gemma-authored villager witness reactions. It is not the SDK product; it is a thin experience layer that lets us evaluate Gemma-generated situations through play.\n- Gemma-generated landmark area events for the Old Mill, Abandoned Castle, Sunken Chapel, and Black Bell Tower.\n- Typed trigger execution for generated bandit ambushes, mysterious beings, and strange structure sounds.\n- Private NPC groups that speak to each other and refuse interruption.\n- Ambient stage manager that caps visible ambient speakers, rotates topics, and moves NPCs together before short exchanges.\n- Small background ambient cache so walking does not constantly call Gemma or block startup.\n- Compact top HUD, single location chip, minimap, invisible map walls, building collision, walking effects, and live FPS.\n- Diagnostics panel for provider\u002Fmodel\u002Fcache\u002Ffallback, prompt traces, CPU, GPU, GPU memory, machine memory, app memory, and optional JSONL performance logs.\n\nThe renderer intentionally has no playable fake-AI fallback. If the Electron\u002FGemma path is broken, the demo should make that obvious.\n\n## Run The Demo\n\n```sh\nnpm install\nnpm start\n```\n\nFor renderer development with Vite:\n\n```sh\nnpm run dev\n```\n\nThe Vite dev server uses `127.0.0.1:5179` with a strict port so Electron does not accidentally attach to another local game running on the usual Vite port.\n\nThe demo defaults to Ollama `gemma4:e4b`. Override the model with:\n\n```sh\nTHE_WORLD_MODEL=gemma4:e2b npm start\n```\n\nExpected startup log:\n\n```txt\nThe World: renderer loaded.\nThe World: Gemma IPC bridge ready.\nThe World: ollama-gemma4-e4b warmup ready.\n```\n\n## Gemma Performance Notes\n\nThe benchmark harness runs the real SDK path against concrete runtime + model artifact + config combinations:\n\n```sh\nnpm run benchmark:gemma\n```\n\nBenchmark artifacts are written to `docs\u002Fgemma-runtime-benchmark*.json` and `docs\u002Fgemma-runtime-benchmark*.md`. The file names are historical; the contents compare runtime + model combinations. Running notes and measurements live in `testing\u002Fperf-testing.md`.\n\nCurrent preference from runtime + model testing:\n\n- Choose Ollama + `gemma4:e4b` GGUF Q4 first in the startup chooser when testing the most reliable current stack.\n- Keep `think:false` for Gemma NPC interactions; thinking mode is too slow for short game turns here.\n- Use `num_ctx:4096` as the realistic conversation context target for town NPCs.\n- Keep ambient life cache-first and capped; generation is too slow to run freely while the player walks.\n- Keep LiteRT-LM available as an experimental stack; the current benchmark does not justify choosing it first for full dialogue prompts.\n\n## Verification\n\n```sh\nnpm run typecheck\nnpm test\nnpm run build\n```\n\nThe current Electron dependency is `42.0.0`. The upgrade removes noisy macOS native menu logging from older Electron versions and keeps the demo on the current stable Electron major.\n\n## Repository Layout\n\n```txt\npackages\u002Fcore\n  Runtime types, schemas, recipes, prompt compiler, cache, memory,\n  repair, typed area events, mock provider, and tests.\n\npackages\u002Follama\n  Ollama provider, model discovery, warmup, structured JSON generation,\n  request cancellation, and tests.\n\npackages\u002Flitert-lm\n  Experimental LiteRT-LM provider, imported-model health checks,\n  persistent GPU bridge, structured output cleanup, and tests.\n\npackages\u002Fomlx\n  oMLX provider using AI SDK's OpenAI-compatible adapter, model discovery,\n  warmup, schema-guided generation, and tests.\n\npackages\u002Felectron\n  Main\u002Fpreload IPC bridge, shared schema validation, request cancellation,\n  area-event bridge, pregeneration bridge, and tests.\n\napps\u002Fthe-world\n  Electron shell, canvas renderer, procedural world, ambient director,\n  area-event triggers, diagnostics, performance logging, and playable demo.\n```\n","The World 是一个实验性项目，旨在为Web开发者提供一个SDK，以便围绕Gemma构建游戏作为游戏的大脑。其核心功能是通过TypeScript工具包将本地大语言模型（LLM）转化为受控的、模式绑定的游戏系统，支持NPC对话、区域事件、环境喊话等，并且保证游戏文本动态生成的同时不失去对游戏的控制。该项目特别适合需要在游戏中引入智能交互但又希望保持游戏规则和状态权威性的场景。此外，它还提供了Electron示例应用来展示其实时运行能力。","2026-06-11 04:04:43","CREATED_QUERY"]