@seriousgaming/sdk-tools 0.0.0-stage → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +93 -0
- package/README.md +36 -2
- package/dist/bundling/logic-exports.d.ts +34 -0
- package/dist/bundling/node-builtins.d.ts +21 -0
- package/dist/bundling/transpiler.d.ts +7 -0
- package/dist/cli.js +27368 -0
- package/dist/client/dev-server.d.ts +59 -0
- package/dist/client/index.d.ts +28 -0
- package/dist/client/stub-services.d.ts +32 -0
- package/dist/client/test-session.d.ts +195 -0
- package/dist/client.js +16884 -0
- package/dist/core/asset-reference-translator.d.ts +46 -0
- package/dist/core/clock.d.ts +57 -0
- package/dist/core/faults.d.ts +65 -0
- package/dist/core/game-settings.d.ts +65 -0
- package/dist/core/history.d.ts +89 -0
- package/dist/core/listen-with-fallback.d.ts +30 -0
- package/dist/core/mutation-queue.d.ts +61 -0
- package/dist/core/package-scenario.d.ts +89 -0
- package/dist/core/participant-registry.d.ts +109 -0
- package/dist/core/pending-changes.d.ts +14 -0
- package/dist/core/protocol.d.ts +83 -0
- package/dist/core/scenario-drafts.d.ts +32 -0
- package/dist/core/session-endpoints.d.ts +108 -0
- package/dist/core/session-gates.d.ts +75 -0
- package/dist/core/session-host.d.ts +94 -0
- package/dist/core/session-refusals.d.ts +56 -0
- package/dist/core/session-rewind.d.ts +32 -0
- package/dist/core/session-roster.d.ts +128 -0
- package/dist/core/session-surfaces.d.ts +32 -0
- package/dist/core/session-ticks.d.ts +18 -0
- package/dist/core/session-timers.d.ts +84 -0
- package/dist/core/session-writes.d.ts +40 -0
- package/dist/core/session.d.ts +385 -0
- package/dist/core/settings-locks.d.ts +22 -0
- package/dist/core/team-names.d.ts +52 -0
- package/dist/core/team-state-store.d.ts +209 -0
- package/dist/core/tick-timer.d.ts +75 -0
- package/dist/dev/blob-links.d.ts +10 -0
- package/dist/dev/console/assets/index-DgFglsnA.js +204 -0
- package/dist/dev/console/index.html +12 -0
- package/dist/dev/console-path.d.ts +19 -0
- package/dist/dev/lockfile.d.ts +124 -0
- package/dist/dev/package-watcher.d.ts +21 -0
- package/dist/dev/routes.d.ts +67 -0
- package/dist/dev/shell.d.ts +260 -0
- package/dist/dev/surfaces.d.ts +41 -0
- package/dist/dev/tools-identity.d.ts +23 -0
- package/dist/dev/view/batch.d.ts +172 -0
- package/dist/dev/view/commands.d.ts +75 -0
- package/dist/dev/view/route.d.ts +58 -0
- package/dist/dev/view/view.d.ts +78 -0
- package/dist/dev/view/viewer.d.ts +32 -0
- package/dist/discovery/config.d.ts +9 -0
- package/dist/discovery/manifest-paths.d.ts +21 -0
- package/dist/discovery/removed-fields.d.ts +90 -0
- package/dist/docs/api/deck.md +386 -0
- package/dist/docs/api/index.md +67 -0
- package/dist/docs/api/sdk-commands.md +286 -0
- package/dist/docs/api/sdk-e2e.md +433 -0
- package/dist/docs/api/sdk-projection.md +207 -0
- package/dist/docs/api/sdk-testing.md +214 -0
- package/dist/docs/api/sdk-tools-client.md +560 -0
- package/dist/docs/api/sdk-ui-testing.md +231 -0
- package/dist/docs/api/sdk-ui.md +1269 -0
- package/dist/docs/api/sdk.md +2140 -0
- package/dist/docs/changelog-0.x.md +1156 -0
- package/dist/docs/changelog.md +236 -0
- package/dist/docs/cli-and-dev-server.md +217 -0
- package/dist/docs/cli-error-codes.md +154 -0
- package/dist/docs/cli-reference.md +2488 -0
- package/dist/docs/cli-simulation-testing.md +222 -0
- package/dist/docs/concepts/index.md +46 -0
- package/dist/docs/concepts/terminology.md +322 -0
- package/dist/docs/environment-variables.md +102 -0
- package/dist/docs/getting-started.md +145 -0
- package/dist/docs/guides/ai-facilitation.md +220 -0
- package/dist/docs/guides/capabilities.md +460 -0
- package/dist/docs/guides/endpoints.md +335 -0
- package/dist/docs/guides/engine-testing.md +477 -0
- package/dist/docs/guides/engine.md +527 -0
- package/dist/docs/guides/file-handling.md +405 -0
- package/dist/docs/guides/games.md +176 -0
- package/dist/docs/guides/global-settings.md +94 -0
- package/dist/docs/guides/guide.md +123 -0
- package/dist/docs/guides/hooks.md +183 -0
- package/dist/docs/guides/index.md +51 -0
- package/dist/docs/guides/internationalization.md +288 -0
- package/dist/docs/guides/llm-integration.md +635 -0
- package/dist/docs/guides/multi-team-games.md +586 -0
- package/dist/docs/guides/patterns.md +391 -0
- package/dist/docs/guides/presentation.md +196 -0
- package/dist/docs/guides/production-constraints.md +130 -0
- package/dist/docs/guides/scenario-editor.md +143 -0
- package/dist/docs/guides/scenarios.md +515 -0
- package/dist/docs/guides/session.md +972 -0
- package/dist/docs/guides/settings.md +458 -0
- package/dist/docs/guides/setup.md +193 -0
- package/dist/docs/guides/speech-generation.md +233 -0
- package/dist/docs/guides/starter-game.md +164 -0
- package/dist/docs/guides/testing.md +575 -0
- package/dist/docs/guides/tick-controller.md +569 -0
- package/dist/docs/guides/type-safety.md +516 -0
- package/dist/docs/guides/ui.md +1389 -0
- package/dist/docs/guides/upgrading.md +636 -0
- package/dist/docs/guides/validators.md +217 -0
- package/dist/docs/guides/waiting-room.md +244 -0
- package/dist/docs/index.md +41 -0
- package/dist/docs/recipes/act-on-the-session-from-an-endpoint.md +95 -0
- package/dist/docs/recipes/adapt-a-game-for-walkthroughs.md +133 -0
- package/dist/docs/recipes/adapt-ui-for-observers.md +74 -0
- package/dist/docs/recipes/call-endpoint-from-ui.md +109 -0
- package/dist/docs/recipes/check-a-package-from-a-script.md +117 -0
- package/dist/docs/recipes/create-capability.md +66 -0
- package/dist/docs/recipes/create-endpoint.md +127 -0
- package/dist/docs/recipes/create-engine.md +111 -0
- package/dist/docs/recipes/create-game.md +70 -0
- package/dist/docs/recipes/create-guide.md +75 -0
- package/dist/docs/recipes/create-hook.md +80 -0
- package/dist/docs/recipes/create-presentation.md +142 -0
- package/dist/docs/recipes/create-scenario-editor.md +102 -0
- package/dist/docs/recipes/create-scenario.md +98 -0
- package/dist/docs/recipes/create-session-ui.md +116 -0
- package/dist/docs/recipes/create-settings.md +88 -0
- package/dist/docs/recipes/create-setup.md +115 -0
- package/dist/docs/recipes/create-tick-controller.md +65 -0
- package/dist/docs/recipes/create-user-interface.md +96 -0
- package/dist/docs/recipes/create-validator.md +67 -0
- package/dist/docs/recipes/create-waiting-room-ui.md +109 -0
- package/dist/docs/recipes/declare-api-uses.md +67 -0
- package/dist/docs/recipes/design-state-for-concurrent-writes.md +96 -0
- package/dist/docs/recipes/distribute-package.md +80 -0
- package/dist/docs/recipes/e2e-testing.md +116 -0
- package/dist/docs/recipes/end-game-on-condition.md +154 -0
- package/dist/docs/recipes/fix-duplicate-packages.md +101 -0
- package/dist/docs/recipes/index.md +86 -0
- package/dist/docs/recipes/make-an-endpoint-external.md +101 -0
- package/dist/docs/recipes/negotiate-across-teams.md +164 -0
- package/dist/docs/recipes/observe-other-teams-state.md +119 -0
- package/dist/docs/recipes/publish-a-package.md +139 -0
- package/dist/docs/recipes/remember-a-viewers-choice.md +55 -0
- package/dist/docs/recipes/review-a-past-day.md +73 -0
- package/dist/docs/recipes/share-assets-across-scenarios.md +120 -0
- package/dist/docs/recipes/share-logic-between-engine-and-ui.md +130 -0
- package/dist/docs/recipes/show-all-teams-participants.md +109 -0
- package/dist/docs/recipes/start-from-an-empty-package.md +169 -0
- package/dist/docs/recipes/take-a-file-in-an-endpoint.md +82 -0
- package/dist/docs/recipes/test-a-multi-role-game.md +51 -0
- package/dist/docs/recipes/test-user-interface.md +95 -0
- package/dist/docs/recipes/test-with-engine.md +115 -0
- package/dist/docs/recipes/upload-and-display-files.md +148 -0
- package/dist/docs/recipes/use-request-play.md +65 -0
- package/dist/docs/recipes/use-request-tick.md +75 -0
- package/dist/docs/troubleshooting.md +816 -0
- package/dist/execution/blob-inputs.d.ts +57 -0
- package/dist/execution/bundle-reload.d.ts +42 -0
- package/dist/execution/config-resolver.d.ts +127 -0
- package/dist/execution/configuration-builder.d.ts +66 -0
- package/dist/execution/context-composition.d.ts +161 -0
- package/dist/execution/endpoint-actions.d.ts +9 -0
- package/dist/execution/endpoint-payloads.d.ts +32 -0
- package/dist/execution/endpoint-runner.d.ts +129 -0
- package/dist/execution/engine-state.d.ts +40 -0
- package/dist/execution/executor-callbacks.d.ts +56 -0
- package/dist/execution/executor.d.ts +436 -0
- package/dist/execution/gates.d.ts +45 -0
- package/dist/execution/hook-chain.d.ts +25 -0
- package/dist/execution/logic-calls.d.ts +190 -0
- package/dist/execution/logic-failure.d.ts +84 -0
- package/dist/execution/logic-loader.d.ts +54 -0
- package/dist/execution/logic-result.d.ts +76 -0
- package/dist/execution/tick-controller-call.d.ts +32 -0
- package/dist/execution/types.d.ts +48 -0
- package/dist/generation/ts-type.d.ts +39 -0
- package/dist/guide/locale.d.ts +12 -0
- package/dist/guide/video-registry.d.ts +42 -0
- package/dist/help-examples.d.ts +125 -0
- package/dist/host/adapter.d.ts +67 -0
- package/dist/host/host-channel.d.ts +222 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/protocol.d.ts +783 -0
- package/dist/host/surfaces.d.ts +42 -0
- package/dist/host/theme.d.ts +55 -0
- package/dist/host.js +583 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/schema-builder.d.ts +29 -0
- package/dist/prompts/types.d.ts +108 -0
- package/dist/prompts/validators.d.ts +32 -0
- package/dist/runtime/capability-scope.d.ts +6 -0
- package/dist/runtime/context-merge.d.ts +29 -0
- package/dist/runtime/context-projection.d.ts +41 -0
- package/dist/runtime/guarded-callbacks.d.ts +15 -0
- package/dist/runtime/index.d.ts +27 -0
- package/dist/runtime/manifest-version.d.ts +55 -0
- package/dist/runtime/tick-validation.d.ts +25 -0
- package/dist/runtime.js +346 -0
- package/dist/schema.json +1546 -0
- package/dist/server/vite-plugin.d.ts +46 -0
- package/dist/services/tts/client.d.ts +51 -0
- package/dist/services/tts/elevenlabs.d.ts +24 -0
- package/dist/services/tts/types.d.ts +44 -0
- package/dist/storage/asset-resolver.d.ts +49 -0
- package/dist/storage/blob-store.d.ts +41 -0
- package/dist/templates/endpoint/logic.test.ts.liquid +61 -0
- package/dist/templates/endpoint/logic.ts.liquid +25 -0
- package/dist/templates/engine/init.json.liquid +7 -0
- package/dist/templates/engine/logic-aggregate.test.ts.liquid +90 -0
- package/dist/templates/engine/logic-aggregate.ts.liquid +46 -0
- package/dist/templates/engine/logic.test.ts.liquid +61 -0
- package/dist/templates/engine/logic.ts.liquid +32 -0
- package/dist/templates/engine/run.json.liquid +7 -0
- package/dist/templates/guide/01-at-a-glance.md.liquid +19 -0
- package/dist/templates/guide/02-preparation.md.liquid +18 -0
- package/dist/templates/guide/03-running-the-session.md.liquid +37 -0
- package/dist/templates/hook/logic.test.ts.liquid +46 -0
- package/dist/templates/hook/logic.ts.liquid +12 -0
- package/dist/templates/package/CLAUDE.md.template +338 -0
- package/dist/templates/package/DESIGN.md.liquid +144 -0
- package/dist/templates/package/README.md.liquid +143 -0
- package/dist/templates/package/claude/rules/workflow-create-capability.md +26 -0
- package/dist/templates/package/claude/rules/workflow-create-endpoint.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-engine.md +35 -0
- package/dist/templates/package/claude/rules/workflow-create-guide.md +40 -0
- package/dist/templates/package/claude/rules/workflow-create-presentation.md +37 -0
- package/dist/templates/package/claude/rules/workflow-create-user-interface.md +31 -0
- package/dist/templates/package/claude/rules/workflow-publish-package.md +54 -0
- package/dist/templates/package/claude/rules/workflow-test-engine-or-ui.md +30 -0
- package/dist/templates/package/claude/rules/workflow-update-sdk-version.md +43 -0
- package/dist/templates/package/claude/settings.json.template +16 -0
- package/dist/templates/package/gitignore.template +15 -0
- package/dist/templates/package/global-setup.ts.template +21 -0
- package/dist/templates/package/globals.d.ts.template +2 -0
- package/dist/templates/package/oxfmtrc.json.template +14 -0
- package/dist/templates/package/oxlintrc.json.template +238 -0
- package/dist/templates/package/package.json.liquid +65 -0
- package/dist/templates/package/playwright.config.ts.liquid +19 -0
- package/dist/templates/package/pnpm-workspace.yaml.template +27 -0
- package/dist/templates/package/test-setup.ts.liquid +7 -0
- package/dist/templates/package/tsconfig.json.liquid +15 -0
- package/dist/templates/package/vite.config.ts.liquid +19 -0
- package/dist/templates/package/vitest.config.ts.liquid +25 -0
- package/dist/templates/package/vitest.engine.config.ts.liquid +12 -0
- package/dist/templates/presentation/index.html.liquid +20 -0
- package/dist/templates/presentation/src/App.tsx.liquid +49 -0
- package/dist/templates/presentation/src/Slides.tsx.liquid +81 -0
- package/dist/templates/presentation/src/i18n.ts.liquid +54 -0
- package/dist/templates/presentation/src/main.tsx.liquid +14 -0
- package/dist/templates/scenario_editor/src/App.test.tsx.liquid +30 -0
- package/dist/templates/scenario_editor/src/App.tsx.liquid +95 -0
- package/dist/templates/scenario_editor/src/i18n.ts.liquid +44 -0
- package/dist/templates/session/src/App.test.tsx.liquid +60 -0
- package/dist/templates/session/src/App.tsx.liquid +128 -0
- package/dist/templates/session/src/i18n.ts.liquid +53 -0
- package/dist/templates/session/src/main.tsx.liquid +48 -0
- package/dist/templates/session/src/theme.ts.liquid +46 -0
- package/dist/templates/settings-editor/index.html.liquid +12 -0
- package/dist/templates/settings-editor/src/main.tsx.liquid +48 -0
- package/dist/templates/settings-editor/src/theme.ts.liquid +24 -0
- package/dist/templates/setup/src/App.test.tsx.liquid +39 -0
- package/dist/templates/setup/src/App.tsx.liquid +102 -0
- package/dist/templates/setup/src/i18n.ts.liquid +47 -0
- package/dist/templates/starter-game/capabilities/briefing/capability.sdk.json +18 -0
- package/dist/templates/starter-game/capabilities/plan/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/results/capability.sdk.json +61 -0
- package/dist/templates/starter-game/capabilities/roles/capability.sdk.json +17 -0
- package/dist/templates/starter-game/capabilities/weather/capability.sdk.json +23 -0
- package/dist/templates/starter-game/engine/defaults.ts +81 -0
- package/dist/templates/starter-game/engine/engine.sdk.json +12 -0
- package/dist/templates/starter-game/engine/logic.test.ts +221 -0
- package/dist/templates/starter-game/engine/logic.ts +149 -0
- package/dist/templates/starter-game/engine/test-helpers.ts +74 -0
- package/dist/templates/starter-game/engine/types.ts +139 -0
- package/dist/templates/starter-game/guide/content/en/01-at-a-glance.md +51 -0
- package/dist/templates/starter-game/guide/content/en/02-preparation.md +35 -0
- package/dist/templates/starter-game/guide/content/en/03-running-the-day.md +58 -0
- package/dist/templates/starter-game/guide/guide.sdk.json +10 -0
- package/dist/templates/starter-game/guide/guide.test.ts +108 -0
- package/dist/templates/starter-game/hooks/assignproducts/defaults.ts +37 -0
- package/dist/templates/starter-game/hooks/assignproducts/hook.sdk.json +13 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.test.ts +77 -0
- package/dist/templates/starter-game/hooks/assignproducts/logic/logic.ts +47 -0
- package/dist/templates/starter-game/hooks/assignproducts/types.ts +55 -0
- package/dist/templates/starter-game/package.json +68 -0
- package/dist/templates/starter-game/playwright.config.ts +19 -0
- package/dist/templates/starter-game/scenarios/heatwave/scenario.sdk.json +20 -0
- package/dist/templates/starter-game/scenarios/ordinary_week/scenario.sdk.json +12 -0
- package/dist/templates/starter-game/seriousgaming.sdk.json +23 -0
- package/dist/templates/starter-game/session/defaults.ts +80 -0
- package/dist/templates/starter-game/session/index.html +12 -0
- package/dist/templates/starter-game/session/session.sdk.json +11 -0
- package/dist/templates/starter-game/session/src/App.test.tsx +307 -0
- package/dist/templates/starter-game/session/src/App.tsx +309 -0
- package/dist/templates/starter-game/session/src/Section.tsx +37 -0
- package/dist/templates/starter-game/session/src/TeamDoor.tsx +96 -0
- package/dist/templates/starter-game/session/src/format.ts +8 -0
- package/dist/templates/starter-game/session/src/i18n.ts +51 -0
- package/dist/templates/starter-game/session/src/main.tsx +48 -0
- package/dist/templates/starter-game/session/src/theme.ts +45 -0
- package/dist/templates/starter-game/session/types.ts +135 -0
- package/dist/templates/starter-game/settings/engine/settings.sdk.json +50 -0
- package/dist/templates/starter-game/settings/menu/settings.sdk.json +78 -0
- package/dist/templates/starter-game/settings/session/settings.sdk.json +23 -0
- package/dist/templates/starter-game/settings/ui/settings.sdk.json +25 -0
- package/dist/templates/starter-game/test-setup.ts +7 -0
- package/dist/templates/starter-game/testing/e2e/game.spec.ts +139 -0
- package/dist/templates/starter-game/testing/e2e/global-setup.ts +21 -0
- package/dist/templates/starter-game/testing/engine/game.engine.test.ts +171 -0
- package/dist/templates/starter-game/tsconfig.json +19 -0
- package/dist/templates/starter-game/ui/defaults.ts +60 -0
- package/dist/templates/starter-game/ui/index.html +12 -0
- package/dist/templates/starter-game/ui/src/App.test.tsx +138 -0
- package/dist/templates/starter-game/ui/src/App.tsx +175 -0
- package/dist/templates/starter-game/ui/src/DayBar.test.tsx +78 -0
- package/dist/templates/starter-game/ui/src/DayBar.tsx +72 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.test.tsx +74 -0
- package/dist/templates/starter-game/ui/src/HistoryPanel.tsx +120 -0
- package/dist/templates/starter-game/ui/src/MakeRow.tsx +129 -0
- package/dist/templates/starter-game/ui/src/Section.tsx +37 -0
- package/dist/templates/starter-game/ui/src/ThemeToggle.tsx +49 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.test.tsx +179 -0
- package/dist/templates/starter-game/ui/src/TodayPanel.tsx +92 -0
- package/dist/templates/starter-game/ui/src/YesterdayPanel.tsx +142 -0
- package/dist/templates/starter-game/ui/src/format.ts +12 -0
- package/dist/templates/starter-game/ui/src/i18n.ts +95 -0
- package/dist/templates/starter-game/ui/src/main.tsx +48 -0
- package/dist/templates/starter-game/ui/src/menu.ts +18 -0
- package/dist/templates/starter-game/ui/src/test-helpers.ts +58 -0
- package/dist/templates/starter-game/ui/src/theme.ts +45 -0
- package/dist/templates/starter-game/ui/src/writes.ts +43 -0
- package/dist/templates/starter-game/ui/types.ts +137 -0
- package/dist/templates/starter-game/ui/ui.sdk.json +11 -0
- package/dist/templates/starter-game/validators/capacity/defaults.ts +37 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.test.ts +96 -0
- package/dist/templates/starter-game/validators/capacity/logic/logic.ts +45 -0
- package/dist/templates/starter-game/validators/capacity/types.ts +55 -0
- package/dist/templates/starter-game/validators/capacity/validator.sdk.json +11 -0
- package/dist/templates/starter-game/vite.config.ts +19 -0
- package/dist/templates/starter-game/vitest.config.ts +15 -0
- package/dist/templates/starter-game/vitest.engine.config.ts +19 -0
- package/dist/templates/starter-game/waiting_room/defaults.ts +40 -0
- package/dist/templates/starter-game/waiting_room/index.html +12 -0
- package/dist/templates/starter-game/waiting_room/src/App.test.tsx +102 -0
- package/dist/templates/starter-game/waiting_room/src/App.tsx +194 -0
- package/dist/templates/starter-game/waiting_room/src/i18n.ts +33 -0
- package/dist/templates/starter-game/waiting_room/src/main.tsx +48 -0
- package/dist/templates/starter-game/waiting_room/src/theme.ts +45 -0
- package/dist/templates/starter-game/waiting_room/types.ts +59 -0
- package/dist/templates/starter-game/waiting_room/waiting_room.sdk.json +10 -0
- package/dist/templates/tick-controller/logic.ts.liquid +8 -0
- package/dist/templates/ui/index.html.liquid +12 -0
- package/dist/templates/ui/src/App.test.tsx.liquid +41 -0
- package/dist/templates/ui/src/App.tsx.liquid +103 -0
- package/dist/templates/ui/src/i18n.ts.liquid +47 -0
- package/dist/templates/ui/src/main.tsx.liquid +48 -0
- package/dist/templates/ui/src/theme.ts.liquid +46 -0
- package/dist/templates/validator/logic.test.ts.liquid +42 -0
- package/dist/templates/validator/logic.ts.liquid +21 -0
- package/dist/templates/waiting-room/index.html.liquid +12 -0
- package/dist/templates/waiting-room/src/App.test.tsx.liquid +57 -0
- package/dist/templates/waiting-room/src/App.tsx.liquid +118 -0
- package/dist/templates/waiting-room/src/i18n.ts.liquid +44 -0
- package/dist/templates/waiting-room/src/main.tsx.liquid +48 -0
- package/dist/templates/waiting-room/src/theme.ts.liquid +46 -0
- package/dist/templating/resource-metadata.d.ts +131 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/atomic-write.d.ts +40 -0
- package/dist/utils/cli-error.d.ts +64 -0
- package/dist/utils/document-parser-client.d.ts +20 -0
- package/dist/utils/env-loader.d.ts +40 -0
- package/dist/utils/error-codes.d.ts +335 -0
- package/dist/utils/errors.d.ts +19 -0
- package/dist/utils/global-settings.d.ts +65 -0
- package/dist/utils/llm-client.d.ts +34 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/output.d.ts +109 -0
- package/dist/utils/platform.d.ts +14 -0
- package/dist/utils/prompt.d.ts +36 -0
- package/dist/utils/real-path.d.ts +6 -0
- package/dist/utils/schema-properties.d.ts +35 -0
- package/dist/validation/ajv.d.ts +17 -0
- package/dist/validation/facilitator-guide-validator.d.ts +27 -0
- package/dist/validation/findings.d.ts +40 -0
- package/dist/validation/game-constraints.d.ts +10 -0
- package/dist/validation/game-validator.d.ts +34 -0
- package/dist/validation/package-validation.d.ts +41 -0
- package/dist/validation/package-validator.d.ts +19 -0
- package/dist/validation/resource-validator.d.ts +79 -0
- package/dist/validation/schema-defaults-validator.d.ts +29 -0
- package/dist/validation/schema-validation.d.ts +115 -0
- package/dist/validation/semver.d.ts +9 -0
- package/package.json +122 -3
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
# LLM integration guide
|
|
2
|
+
|
|
3
|
+
The Serious Gaming SDK generates text through the Serious Gaming platform's LLM pipeline, the same `generateText` path production game code runs. The engine can generate content dynamically using LLMs without configuring any provider credentials: all you need is a platform API key. This guide covers setup, usage, and best practices.
|
|
4
|
+
|
|
5
|
+
## Table of contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Configuration](#configuration)
|
|
9
|
+
- [LLM tiers](#llm-tiers)
|
|
10
|
+
- [Response shape](#response-shape)
|
|
11
|
+
- [Using LLMs in engines](#using-llms-in-engines)
|
|
12
|
+
- [Tool use](#tool-use)
|
|
13
|
+
- [Error handling](#error-handling)
|
|
14
|
+
- [Declare the APIs a resource uses](#declare-the-apis-a-resource-uses)
|
|
15
|
+
- [Using LLMs in user interfaces](#using-llms-in-user-interfaces)
|
|
16
|
+
- [Best practices](#best-practices)
|
|
17
|
+
- [Limitations](#limitations)
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
LLM integration allows you to:
|
|
22
|
+
|
|
23
|
+
- Generate reports based on game data
|
|
24
|
+
- Create dynamic content during game execution
|
|
25
|
+
- Process context and generate insights
|
|
26
|
+
- Structure responses as JSON via JSON schemas
|
|
27
|
+
|
|
28
|
+
### Key points
|
|
29
|
+
|
|
30
|
+
- LLM calls are **async** and only work in async functions
|
|
31
|
+
- Text generation runs through the Serious Gaming platform, authenticated with a platform API key
|
|
32
|
+
- LLM client is automatically instantiated and passed as callbacks
|
|
33
|
+
- Three tiers are available: `quick`, `standard`, and `expert`; the platform resolves each tier to a configured model
|
|
34
|
+
- All errors have clear configuration instructions
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
The only required setting is a Serious Gaming platform API key. Issue one from your organisation's settings page on the platform; the key is shown exactly once at creation. The key acts as the account that created it, and that account needs access to the platform LLM API, which Business Simulations grants: ask your contact there to enable it. The same key is used by `package publish`.
|
|
39
|
+
|
|
40
|
+
Configuration follows this priority (highest to lowest):
|
|
41
|
+
|
|
42
|
+
1. **CLI flags** - Highest priority, override everything
|
|
43
|
+
2. **Environment variables** - Override .env file
|
|
44
|
+
3. **.env file** - Overrides global settings
|
|
45
|
+
4. **Global settings file** - Lowest priority
|
|
46
|
+
|
|
47
|
+
### Method 1: global settings (recommended)
|
|
48
|
+
|
|
49
|
+
Set the key once and every project picks it up:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm run sdk config set platform.apiKey sg_xxx
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Method 2: environment variables
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export SERIOUSGAMING_API_KEY=sg_xxx
|
|
59
|
+
export SERIOUSGAMING_API_HOST=https://app.businesssimulations.com # optional
|
|
60
|
+
|
|
61
|
+
pnpm run sdk dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Method 3: .env file
|
|
65
|
+
|
|
66
|
+
Create `.env` in your project root:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
SERIOUSGAMING_API_KEY=sg_xxx
|
|
70
|
+
SERIOUSGAMING_API_HOST=https://app.businesssimulations.com # optional
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then run:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm run sdk dev
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Method 4: CLI flags
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm run sdk dev --api-key sg_xxx
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Prefer the env var or global settings so the secret stays out of shell history.
|
|
86
|
+
|
|
87
|
+
### Configuration options
|
|
88
|
+
|
|
89
|
+
| Variable | Required | Default | Description |
|
|
90
|
+
| ------------------------ | -------- | ------------------------------------- | ------------------------------------ |
|
|
91
|
+
| `SERIOUSGAMING_API_KEY` | Yes | None | Your Serious Gaming platform API key |
|
|
92
|
+
| `SERIOUSGAMING_API_HOST` | No | `https://app.businesssimulations.com` | Platform origin to call |
|
|
93
|
+
|
|
94
|
+
Which model each tier maps to is configured on the platform, not in your project. Raw model names are not accepted; calls select a model by `tier` only.
|
|
95
|
+
|
|
96
|
+
## LLM tiers
|
|
97
|
+
|
|
98
|
+
Each `generateText` call can specify a **tier** that controls which model is used. This lets you balance cost and capability per call rather than setting a single model for everything.
|
|
99
|
+
|
|
100
|
+
### Available tiers
|
|
101
|
+
|
|
102
|
+
| Tier | When to use |
|
|
103
|
+
| ---------- | -------------------------------------------------------------------------- |
|
|
104
|
+
| `quick` | Fast, low-cost tasks: classification, simple lookups, short labels |
|
|
105
|
+
| `standard` | Typical generation: summaries, reports, moderate reasoning |
|
|
106
|
+
| `expert` | Complex reasoning: deep analysis, multi-step evaluation, nuanced judgement |
|
|
107
|
+
|
|
108
|
+
### Specifying a tier
|
|
109
|
+
|
|
110
|
+
Pass `tier` in the options object. Omitting it defaults to `'quick'`.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// quick (the default: omit tier for lowest cost)
|
|
114
|
+
const label = await callbacks.generateText('Classify as: positive, neutral, or negative: ' + text);
|
|
115
|
+
|
|
116
|
+
// standard
|
|
117
|
+
const summary = await callbacks.generateText({
|
|
118
|
+
prompt: 'Summarise the market situation in one paragraph.',
|
|
119
|
+
tier: 'standard',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// expert
|
|
123
|
+
const analysis = await callbacks.generateText({
|
|
124
|
+
prompt: 'Provide a deep strategic analysis of these business metrics...',
|
|
125
|
+
tier: 'expert',
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Fallback behaviour
|
|
130
|
+
|
|
131
|
+
Tier-to-model resolution happens on the platform. If a tier is not configured there, it falls back down the chain:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
expert → standard → quick
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This means game code can request any tier without worrying about platform configuration; the call always resolves to a model.
|
|
138
|
+
|
|
139
|
+
## Response shape
|
|
140
|
+
|
|
141
|
+
`generateText` returns a content-block array plus a `stopReason`:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface LLMResponse {
|
|
145
|
+
content: Array<
|
|
146
|
+
| { type: 'text'; text: string }
|
|
147
|
+
| { type: 'toolUse'; id: string; name: string; input: unknown }
|
|
148
|
+
>;
|
|
149
|
+
stopReason: 'endTurn' | 'toolUse' | 'maxTokens' | 'refusal';
|
|
150
|
+
usage?: { inputTokens: number; outputTokens: number };
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The blocks let one response carry text and one or more tool-use intents. The stop reason distinguishes normal completion (`'endTurn'`), tool use (`'toolUse'`), truncation (`'maxTokens'`), and refusal (`'refusal'`). When the provider reports token counts, `usage` carries the input and output token totals for the call.
|
|
155
|
+
|
|
156
|
+
For the common case where you just want the assistant's text, use `getText`:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { getText } from '@seriousgaming/sdk';
|
|
160
|
+
|
|
161
|
+
const response = await callbacks.generateText('Summarise this: ' + text);
|
|
162
|
+
const summary = getText(response); // concatenates every text block
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A `stopReason` of `'refusal'` indicates the provider declined to generate. Callers typically want a different branch (rewrite the prompt, escalate to a human) than a normal completion, so do not treat refusals as ordinary text.
|
|
166
|
+
|
|
167
|
+
## Using LLMs in engines
|
|
168
|
+
|
|
169
|
+
### Basic usage
|
|
170
|
+
|
|
171
|
+
All engine functions are async and receive a `callbacks` parameter:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { RunFunction } from '@seriousgaming/sdk';
|
|
175
|
+
import type { Context, Settings } from './types';
|
|
176
|
+
|
|
177
|
+
import { getText } from '@seriousgaming/sdk';
|
|
178
|
+
|
|
179
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings, callbacks }) => {
|
|
180
|
+
// Simple text generation (quick tier by default)
|
|
181
|
+
const response = await callbacks.generateText('Your prompt here');
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
capabilities: {
|
|
185
|
+
...context,
|
|
186
|
+
report: {
|
|
187
|
+
ReportContent: getText(response),
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Structured JSON responses
|
|
195
|
+
|
|
196
|
+
Pass a `jsonSchema` to get predictable, structured output. The reply still arrives as content blocks, so walk it to text with `getText` before parsing:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, callbacks }) => {
|
|
200
|
+
const response = await callbacks.generateText({
|
|
201
|
+
prompt: 'Analyze the business metrics and provide insights',
|
|
202
|
+
tier: 'expert',
|
|
203
|
+
jsonSchema: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
overallHealth: { type: 'string', enum: ['excellent', 'good', 'fair', 'poor'] },
|
|
207
|
+
topConcern: { type: 'string' },
|
|
208
|
+
recommendation: { type: 'string' },
|
|
209
|
+
riskLevel: { type: 'number', minimum: 0, maximum: 10 },
|
|
210
|
+
},
|
|
211
|
+
required: ['overallHealth', 'topConcern', 'recommendation', 'riskLevel'],
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const analysis = JSON.parse(getText(response));
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
capabilities: {
|
|
219
|
+
...context,
|
|
220
|
+
analysis: {
|
|
221
|
+
OverallHealth: analysis.overallHealth,
|
|
222
|
+
TopConcern: analysis.topConcern,
|
|
223
|
+
Recommendation: analysis.recommendation,
|
|
224
|
+
RiskLevel: analysis.riskLevel,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`JSON.parse` throws if the model returns something that is not valid JSON, so wrap it in the same `try` as the call when a failed parse should not fail the tick (see [Error handling](#error-handling)).
|
|
232
|
+
|
|
233
|
+
### Data-driven report generation
|
|
234
|
+
|
|
235
|
+
Extract context data and pass to LLM:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, settings, callbacks }) => {
|
|
239
|
+
const production = context.production.Production;
|
|
240
|
+
const demand = context.demand.Demand;
|
|
241
|
+
const sales = context.sales.Sales;
|
|
242
|
+
const revenue = context.revenue.Revenue;
|
|
243
|
+
const costs = context.costs.Costs;
|
|
244
|
+
const profit = context.profit.Profit;
|
|
245
|
+
|
|
246
|
+
const prompt = `Generate a professional business summary based on:
|
|
247
|
+
- Production: ${production} units
|
|
248
|
+
- Demand: ${demand} units
|
|
249
|
+
- Sales: ${sales} units
|
|
250
|
+
- Revenue: $${revenue}
|
|
251
|
+
- Costs: $${costs}
|
|
252
|
+
- Profit: $${profit}
|
|
253
|
+
|
|
254
|
+
Create a markdown report with:
|
|
255
|
+
1. Executive summary
|
|
256
|
+
2. Key metrics table
|
|
257
|
+
3. Performance analysis
|
|
258
|
+
4. Recommendations`;
|
|
259
|
+
|
|
260
|
+
const report = await callbacks.generateText({ prompt, tier: 'standard' });
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
capabilities: {
|
|
264
|
+
...context,
|
|
265
|
+
report: {
|
|
266
|
+
ReportContent: getText(report),
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Mixed tiers in one engine
|
|
274
|
+
|
|
275
|
+
Use the cheapest tier that does the job, and reserve `expert` for genuinely complex reasoning:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, callbacks }) => {
|
|
279
|
+
// quick: simple classification
|
|
280
|
+
const sentiment = await callbacks.generateText(
|
|
281
|
+
`Classify as positive, neutral, or negative: "${context.feedback.Text}"`
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// standard: prose summary
|
|
285
|
+
const summary = await callbacks.generateText({
|
|
286
|
+
prompt: `Summarise this production data in one paragraph: Production=${context.production.Production}`,
|
|
287
|
+
tier: 'standard',
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// expert: complex strategic analysis
|
|
291
|
+
const strategy = await callbacks.generateText({
|
|
292
|
+
prompt: `Given these metrics, recommend a long-term pricing strategy with risk analysis:
|
|
293
|
+
Summary: ${getText(summary)}
|
|
294
|
+
Sentiment: ${getText(sentiment)}`,
|
|
295
|
+
tier: 'expert',
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
capabilities: {
|
|
300
|
+
...context,
|
|
301
|
+
report: {
|
|
302
|
+
Sentiment: getText(sentiment),
|
|
303
|
+
Summary: getText(summary),
|
|
304
|
+
Strategy: getText(strategy),
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Tool use
|
|
312
|
+
|
|
313
|
+
`generateText` accepts a `tools` array. When set, the model may emit `toolUse` content blocks instead of (or alongside) text. The SDK never executes anything itself: your code runs the tool and feeds the result back as a `toolResult` block on the next call.
|
|
314
|
+
|
|
315
|
+
The loop is:
|
|
316
|
+
|
|
317
|
+
1. Invoke `generateText({ messages, tools })`.
|
|
318
|
+
2. If `stopReason === 'toolUse'`, run the tool(s) in your own code and build `toolResult` blocks.
|
|
319
|
+
3. Invoke `generateText` again with the prior messages plus the assistant response plus a user message carrying the `toolResult` blocks.
|
|
320
|
+
4. Repeat until `stopReason !== 'toolUse'`.
|
|
321
|
+
|
|
322
|
+
Always cap iterations: the loop lives in your code and runaway loops are your responsibility.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { getText, LLMMessage, LLMTool } from '@seriousgaming/sdk';
|
|
326
|
+
|
|
327
|
+
const MAX_ITERATIONS = 10;
|
|
328
|
+
|
|
329
|
+
const tools: LLMTool[] = [
|
|
330
|
+
{
|
|
331
|
+
name: 'lookup_user',
|
|
332
|
+
description: 'Look up a user by ID',
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: { userId: { type: 'string' } },
|
|
336
|
+
required: ['userId'],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
async function runLookup(name: string, input: unknown): Promise<string> {
|
|
342
|
+
// Your handler. Tool results are strings; serialise structured data as JSON.
|
|
343
|
+
if (name === 'lookup_user') {
|
|
344
|
+
const { userId } = input as { userId: string };
|
|
345
|
+
return JSON.stringify({ id: userId, name: 'Alex' });
|
|
346
|
+
}
|
|
347
|
+
return JSON.stringify({ error: `unknown tool: ${name}` });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const messages: LLMMessage[] = [{ role: 'user', content: 'Who is u-1?' }];
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
353
|
+
const response = await callbacks.generateText({ messages, tools });
|
|
354
|
+
|
|
355
|
+
// Append the assistant turn verbatim so the next call has full context.
|
|
356
|
+
messages.push({ role: 'assistant', content: response.content });
|
|
357
|
+
|
|
358
|
+
if (response.stopReason !== 'toolUse') {
|
|
359
|
+
const finalText = getText(response);
|
|
360
|
+
// …use finalText…
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const toolResults = await Promise.all(
|
|
365
|
+
response.content
|
|
366
|
+
.filter((b) => b.type === 'toolUse')
|
|
367
|
+
.map(async (b) => {
|
|
368
|
+
const result = await runLookup(b.name, b.input);
|
|
369
|
+
return {
|
|
370
|
+
type: 'toolResult' as const,
|
|
371
|
+
toolUseId: b.id,
|
|
372
|
+
content: result,
|
|
373
|
+
};
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
messages.push({ role: 'user', content: toolResults });
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Tool names must match `^[a-zA-Z0-9_-]{1,64}$`. The dev server validates this at the boundary and throws an `LLMClientError` before making a request, so authoring mistakes fail fast.
|
|
382
|
+
|
|
383
|
+
Tool handlers run in caller code. The SDK boundary is unchanged: declaring `generateText` in `uses` is enough; there is no separate `ApiUse` gate for tools.
|
|
384
|
+
|
|
385
|
+
## Error handling
|
|
386
|
+
|
|
387
|
+
### When LLM is not configured
|
|
388
|
+
|
|
389
|
+
If no platform API key is configured, `generateText` throws: 'generateText needs a Serious Gaming platform API key, and none is configured.' A CLI command whose tick or endpoint call failed this way exits 5 with the code `LLM_NOT_CONFIGURED`, and its hint lists the ways to set a key (see [Configuration](#configuration)). Restart the dev server after setting one.
|
|
390
|
+
|
|
391
|
+
A 401 response means the key is invalid or has been revoked. A 403 response means the key works but the account that created it does not have access to the platform LLM API. Ask Business Simulations to grant it.
|
|
392
|
+
|
|
393
|
+
### Handling API errors
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, callbacks }) => {
|
|
397
|
+
try {
|
|
398
|
+
const response = await callbacks.generateText({ prompt: 'Your prompt', tier: 'standard' });
|
|
399
|
+
return {
|
|
400
|
+
capabilities: {
|
|
401
|
+
...context,
|
|
402
|
+
report: {
|
|
403
|
+
ReportContent: getText(response),
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error('LLM generation failed:', error);
|
|
409
|
+
// Gracefully handle failure
|
|
410
|
+
return {
|
|
411
|
+
capabilities: {
|
|
412
|
+
...context,
|
|
413
|
+
report: {
|
|
414
|
+
ReportContent: 'Report generation failed',
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Declare the APIs a resource uses
|
|
423
|
+
|
|
424
|
+
`generateText` is one of four callbacks that reach a service outside the game. Every logic-bearing resource (the engine, a hook, the tick controller, an endpoint) lists the ones it calls in its bundle's `uses` array:
|
|
425
|
+
|
|
426
|
+
```json
|
|
427
|
+
{
|
|
428
|
+
"definition": {
|
|
429
|
+
"uses": ["generateText"]
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
The declaration does three jobs at once:
|
|
435
|
+
|
|
436
|
+
- **A safety boundary.** A resource that was not written to call a model cannot start doing so by accident or through a compromised dependency
|
|
437
|
+
- **Operator visibility.** The built package's `seriousgaming.json` carries every bundle's `uses`, so an operator can forecast LLM cost, review what reaches a third-party service, or see that a game needs document parsing, without reading its source
|
|
438
|
+
- **A guarantee.** A callback a resource declares is always there when it runs
|
|
439
|
+
|
|
440
|
+
The mental model is a phone app's permissions: a static declaration, enforced at run time, visible without inspecting code.
|
|
441
|
+
|
|
442
|
+
### The APIs it gates
|
|
443
|
+
|
|
444
|
+
| API | What it does |
|
|
445
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
446
|
+
| `generateText` | LLM text generation, including [tool use](#tool-use) |
|
|
447
|
+
| `extractText` | Document text extraction (currently PDF); see [File handling](./file-handling.md) |
|
|
448
|
+
| `generateSpeech` | Text-to-speech, returning an audio blob; see [Speech generation](./speech-generation.md) |
|
|
449
|
+
| `writeBlob` | Writes raw bytes to the blob store and returns a `blob:<id>` reference; most useful in an endpoint that returns a file |
|
|
450
|
+
|
|
451
|
+
The list is closed and fixed by the SDK (see [`ApiUse`](../api/sdk.md#apiuse)). Every other callback is runtime plumbing and is never gated: logic always has `readAsset` and `getPastState`, and a UI's `getAssetUrl`, `uploadBlob`, `requestTick` and the rest are always there when the surface exposes them.
|
|
452
|
+
|
|
453
|
+
### What happens when a resource calls one it did not declare
|
|
454
|
+
|
|
455
|
+
The runtime wraps each resource's `callbacks`, and calling a gated callback the bundle did not declare throws an `UndeclaredApiUseError` naming the resource and the callback. Enforcement is the same in dev and production; there is no soft mode.
|
|
456
|
+
|
|
457
|
+
The generated `types.ts` catches it earlier. Its `Callbacks` alias holds the declared callbacks, each required rather than optional, plus the ungated ones:
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
// uses: ['generateText']
|
|
461
|
+
export type Callbacks = LogicCallbacks<'generateText'>;
|
|
462
|
+
|
|
463
|
+
// uses: [] (or omitted)
|
|
464
|
+
export type Callbacks = LogicCallbacks;
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Type `Init`, `Run`, a `HookFunction` or a `GetTickControllerFunction` with it, and an undeclared call fails at type-check. A declared one needs no `?.` or `!`: call `callbacks.generateText(...)` directly, never behind an `if (callbacks.generateText)` check.
|
|
468
|
+
|
|
469
|
+
Only the resource that owns the `callbacks` argument declares. A helper function that calls `callbacks.generateText` has no bundle of its own, so the resource that calls the helper declares `'generateText'`.
|
|
470
|
+
|
|
471
|
+
### A declared service is always present
|
|
472
|
+
|
|
473
|
+
The other half of the declaration is a promise from the runtime. A runtime that cannot provide a service a package declares refuses the whole package when it loads it, naming the service. The dev server provides all four, so it refuses nothing on this ground.
|
|
474
|
+
|
|
475
|
+
A service that is provided but fails when called is different: the package loads, and that call rejects with a typed error. A missing key is the usual case: `generateText` without `SERIOUSGAMING_API_KEY` rejects with `LLMClientError`, and `generateSpeech` without an ElevenLabs key with `TtsClientError` (see [Environment variables](../environment-variables.md)). Handle those as you would any failed call ([Error handling](#error-handling)).
|
|
476
|
+
|
|
477
|
+
### User interfaces declare no `uses`
|
|
478
|
+
|
|
479
|
+
A UI bundle has no `uses` field, and loading or building a package that gives one to any user interface fails, pointing at endpoints. A UI reaches these services by calling an [endpoint](./endpoints.md) whose own bundle declares them, as the next section shows.
|
|
480
|
+
|
|
481
|
+
## Using LLMs in user interfaces
|
|
482
|
+
|
|
483
|
+
User interfaces cannot call `generateText` directly. A UI reaches the LLM through an [endpoint](./endpoints.md) that wraps `generateText` server-side, and invokes it through `callbacks.endpoints['<endpoint-id>']`. Tier resolution happens on the platform and applies to endpoints exactly as it does to the engine.
|
|
484
|
+
|
|
485
|
+
### The endpoint
|
|
486
|
+
|
|
487
|
+
The endpoint bundle declares `"uses": ["generateText"]` and runs the LLM call server-side:
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// endpoints/chat/logic/logic.ts
|
|
491
|
+
import { getText } from '@seriousgaming/sdk';
|
|
492
|
+
import type { Handler } from '../types';
|
|
493
|
+
|
|
494
|
+
export const Handle: Handler = async ({ request, callbacks }) => {
|
|
495
|
+
const response = await callbacks.generateText({
|
|
496
|
+
messages: request.messages,
|
|
497
|
+
systemPrompt:
|
|
498
|
+
request.systemPrompt ?? 'You are a helpful business advisor. Provide concise guidance.',
|
|
499
|
+
tier: 'standard',
|
|
500
|
+
});
|
|
501
|
+
return { reply: getText(response) };
|
|
502
|
+
};
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Declare the endpoint in the UI bundle's `endpoints` array. The endpoint validates the request against its `requestSchema` and the reply against its `responseSchema`, so both sides stay typed.
|
|
506
|
+
|
|
507
|
+
Engines and endpoints receive the session locale as `meta.locale`, so either can ask the model to answer in the session's language on its own. When a UI needs its own localised system prompt, translate it in the UI and pass it to the endpoint in the request.
|
|
508
|
+
|
|
509
|
+
The [user interfaces guide](./ui.md#using-llms-in-user-interfaces) carries the UI half in full: a chat component that persists its conversation in a capability, localised prompts, markdown rendering, and error handling. To build the endpoint, follow [Create an endpoint](../recipes/create-endpoint.md), and see [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md) for the call itself.
|
|
510
|
+
|
|
511
|
+
## Best practices
|
|
512
|
+
|
|
513
|
+
### 1. Default to `quick`, opt up deliberately
|
|
514
|
+
|
|
515
|
+
Every call defaults to `quick` when no tier is specified. Escalate only when the task genuinely requires it, which keeps costs down: `standard` for a paragraph-length summary, `expert` sparingly, for tasks where reasoning quality visibly matters. See [Mixed tiers in one engine](#mixed-tiers-in-one-engine).
|
|
516
|
+
|
|
517
|
+
### 2. Keep prompts specific
|
|
518
|
+
|
|
519
|
+
❌ Bad:
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
const response = await callbacks.generateText('Generate something');
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
✅ Good:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
const response = await callbacks.generateText(
|
|
529
|
+
`Generate a business report for production=${production} units, demand=${demand} units`
|
|
530
|
+
);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### 3. Structure responses when possible
|
|
534
|
+
|
|
535
|
+
Use a `jsonSchema` for predictable, structured output whenever your code reads the reply rather than displaying it. See [Structured JSON responses](#structured-json-responses).
|
|
536
|
+
|
|
537
|
+
### 4. Extract context data clearly
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// Good: Extract all needed data upfront
|
|
541
|
+
const { Production } = context.production;
|
|
542
|
+
const { Demand } = context.demand;
|
|
543
|
+
|
|
544
|
+
const prompt = `Production: ${Production}, Demand: ${Demand}`;
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### 5. Localise prompts
|
|
548
|
+
|
|
549
|
+
Every participant in a session shares one locale, and logic receives it as `meta.locale`. Ask for prose in that language rather than generating it in one language and translating later:
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
export const Run: RunFunction<Context, Settings> = async ({ context, meta, callbacks }) => {
|
|
553
|
+
const response = await callbacks.generateText({
|
|
554
|
+
systemPrompt: `Write in the language with BCP 47 tag ${meta.locale}.`,
|
|
555
|
+
prompt: `Summarise this round: ${JSON.stringify(context.sales)}`,
|
|
556
|
+
});
|
|
557
|
+
// ...
|
|
558
|
+
};
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Fixed interface text still belongs in the UI's i18n resources.
|
|
562
|
+
|
|
563
|
+
### 6. Put stable content in `systemPrompt`
|
|
564
|
+
|
|
565
|
+
The platform's LLM pipeline applies prompt caching automatically: when the provider supports it, the stable prefix is stored on the first call and reused on subsequent calls within the cache window, cutting input cost and time to first token.
|
|
566
|
+
|
|
567
|
+
For repeated calls (iterate-style flows, multi-turn assistants, batched analyses), keep your stable content (instructions, role description, reference data, JSON schema scaffolds) in `systemPrompt` and put only the per-call variation in `prompt` or the trailing `messages` entries.
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// Cache-friendly: long stable prefix in systemPrompt, short variation in prompt
|
|
571
|
+
const response = await callbacks.generateText({
|
|
572
|
+
systemPrompt: STABLE_INSTRUCTIONS_AND_REFERENCE_DATA, // identical across turns
|
|
573
|
+
prompt: `Latest observation: ${observation}`,
|
|
574
|
+
tier: 'standard',
|
|
575
|
+
});
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
Providers that don't support prompt caching silently ignore the annotation, so the same code stays fast everywhere.
|
|
579
|
+
|
|
580
|
+
### 7. Cache or limit LLM calls
|
|
581
|
+
|
|
582
|
+
Don't call LLM every tick for the same data:
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
// Bad: Called every tick, expensive
|
|
586
|
+
export const Run: RunFunction = async ({ context, callbacks }) => {
|
|
587
|
+
const report = await callbacks.generateText(...);
|
|
588
|
+
return { capabilities: { ...context, report } };
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// Good: Call only when data changes
|
|
592
|
+
export const Run: RunFunction = async ({ context, callbacks }) => {
|
|
593
|
+
const hasDataChanged = context.production.Production !==
|
|
594
|
+
context['previousProduction'].Production;
|
|
595
|
+
|
|
596
|
+
if (hasDataChanged) {
|
|
597
|
+
const report = await callbacks.generateText({ prompt: '...', tier: 'standard' });
|
|
598
|
+
return { capabilities: { ...context, report } };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return { capabilities: context };
|
|
602
|
+
};
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
## Limitations
|
|
606
|
+
|
|
607
|
+
### Server-side only
|
|
608
|
+
|
|
609
|
+
- LLM generation runs in the engine, hooks, tick controllers, or endpoints, never in a UI directly
|
|
610
|
+
- Generated prose follows the session locale in `meta.locale`; fixed interface text is localised by UIs
|
|
611
|
+
- A session has one locale, so each call generates in one language
|
|
612
|
+
|
|
613
|
+
### API limitations
|
|
614
|
+
|
|
615
|
+
- Subject to the platform's rate limits and the underlying provider's costs
|
|
616
|
+
- Calls require a platform API key from an account with LLM access, which Business Simulations grants
|
|
617
|
+
- No offline support (requires a connection to the platform)
|
|
618
|
+
- Responses may vary slightly (non-deterministic)
|
|
619
|
+
|
|
620
|
+
### Performance
|
|
621
|
+
|
|
622
|
+
- LLM calls add latency to tick execution
|
|
623
|
+
- `quick` calls are typically fastest; `expert` calls may take several seconds
|
|
624
|
+
- Consider generating reports less frequently
|
|
625
|
+
- Default to `quick` tier where output quality is not critical
|
|
626
|
+
|
|
627
|
+
## See also
|
|
628
|
+
|
|
629
|
+
- [The CLI and the dev server](../cli-and-dev-server.md) - what the dev server is and how to drive it
|
|
630
|
+
- [Environment variables](../environment-variables.md) - Full env-var reference (LLM, TTS, publishing)
|
|
631
|
+
- [User interfaces guide](./ui.md#using-llms-in-user-interfaces) - Building LLM-powered UIs through endpoints
|
|
632
|
+
- [Call an endpoint from a UI](../recipes/call-endpoint-from-ui.md) - how a UI reaches LLM, document, and speech services
|
|
633
|
+
- [Engine development](./engine.md) - Engine creation and patterns
|
|
634
|
+
- [Service callbacks in tests](./engine-testing.md#service-callbacks-are-stubbed) - The offline `generateText` stub, and choosing its answer in a test
|
|
635
|
+
- [Declare API uses](../recipes/declare-api-uses.md) - Step-by-step `uses` declaration
|