diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fd210edad05..fc24a8ec7e7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -548,6 +548,34 @@ importers:
specifier: ^5.9.3
version: 5.9.3
+ templates/llm-chat-ts:
+ dependencies:
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ spacetimedb:
+ specifier: workspace:*
+ version: link:../../crates/bindings-typescript
+ devDependencies:
+ '@types/react':
+ specifier: ^18.3.18
+ version: 18.3.23
+ '@types/react-dom':
+ specifier: ^18.3.5
+ version: 18.3.7(@types/react@18.3.23)
+ '@vitejs/plugin-react':
+ specifier: ^5.0.2
+ version: 5.0.2(vite@7.1.5(@types/node@24.3.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.2))
+ typescript:
+ specifier: ~5.6.2
+ version: 5.6.3
+ vite:
+ specifier: ^7.1.5
+ version: 7.1.5(@types/node@24.3.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.2)
+
templates/nuxt-ts:
dependencies:
nuxt:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 8cf1969dbb3..bf4efb69d90 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -5,6 +5,7 @@ packages:
- 'templates/chat-react-ts'
- 'templates/react-ts'
- 'templates/basic-ts'
+ - 'templates/llm-chat-ts'
- 'templates/vue-ts'
- 'templates/tanstack-ts'
- 'templates/browser-ts'
diff --git a/templates/llm-chat-ts/.gitignore b/templates/llm-chat-ts/.gitignore
new file mode 100644
index 00000000000..e496aa04d3a
--- /dev/null
+++ b/templates/llm-chat-ts/.gitignore
@@ -0,0 +1,5 @@
+dist
+node_modules
+tsconfig.tsbuildinfo
+.env
+.env.local
diff --git a/templates/llm-chat-ts/.template.json b/templates/llm-chat-ts/.template.json
new file mode 100644
index 00000000000..d756c3a4da7
--- /dev/null
+++ b/templates/llm-chat-ts/.template.json
@@ -0,0 +1,14 @@
+{
+ "description": "Simple TypeScript chat app that calls an LLM API from a SpacetimeDB module",
+ "client_framework": "React",
+ "client_lang": "typescript",
+ "server_lang": "typescript",
+ "builtWith": [
+ "react",
+ "react-dom",
+ "vitejs",
+ "typescript",
+ "vite",
+ "spacetimedb"
+ ]
+}
diff --git a/templates/llm-chat-ts/README.md b/templates/llm-chat-ts/README.md
new file mode 100644
index 00000000000..8be7edc1781
--- /dev/null
+++ b/templates/llm-chat-ts/README.md
@@ -0,0 +1,83 @@
+Get a SpacetimeDB-backed LLM chat app running in under 5 minutes.
+
+## Prerequisites
+
+- [Node.js](https://nodejs.org/) 18+ installed
+- [SpacetimeDB CLI](https://spacetimedb.com/install) installed
+- An OpenRouter or OpenAI API key
+
+Install the [SpacetimeDB CLI](https://spacetimedb.com/install) before continuing.
+
+---
+
+## Create your project
+
+Run the `spacetime dev` command to create a new project with a SpacetimeDB
+module and React client.
+
+This will start the local SpacetimeDB server, publish your module, generate
+TypeScript bindings, and start the React development server.
+
+```bash
+spacetime dev --template llm-chat-ts
+```
+
+## Open your app
+
+Navigate to [http://localhost:5173](http://localhost:5173) to see your app
+running.
+
+Open the provider config modal, choose OpenRouter or OpenAI, enter an API key
+and model, then start a new chat.
+
+## Explore the project structure
+
+Your project contains both server and client code.
+
+Edit `spacetimedb/src/index.ts` to change tables, views, reducers, and
+procedures. Edit `src/App.tsx` to build the chat UI.
+
+```
+my-spacetime-app/
+├── spacetimedb/ # Your SpacetimeDB module
+│ └── src/
+│ ├── index.ts # Server-side tables, views, and reducers
+│ └── llm.ts # LLM provider request helpers
+├── src/
+│ ├── App.tsx # React chat UI
+│ └── module_bindings/ # Auto-generated types
+└── package.json
+```
+
+## Understand the module
+
+The module stores private chat threads, private chat messages, and private LLM
+configuration for each SpacetimeDB identity.
+
+The public `chat` and `message` views only expose rows owned by the connected
+identity. The `llm_config` table is private, and the API key is never returned
+through subscriptions or config status calls.
+
+The API key is still stored as module data. This template is not a secret
+manager: database operators can inspect module data, so use keys that are
+appropriate for your local or hackathon environment.
+
+## Configure models
+
+Defaults:
+
+- Provider: `openrouter`
+- Model: `openai/gpt-4o-mini`
+- Local database name: `llm-chat-ts`
+- New chats start with a clean context.
+
+Leaving the API key field blank keeps the saved key when editing the same
+provider. Switching providers requires entering a new key.
+
+Set `VITE_SPACETIMEDB_HOST` or `VITE_SPACETIMEDB_DB_NAME` if you publish to a
+different host or database name.
+
+## Next steps
+
+- See the [Chat App Tutorial](https://spacetimedb.com/docs/intro/tutorials/chat-app) for a complete example
+- Read the [TypeScript SDK Reference](https://spacetimedb.com/docs/intro/core-concepts/clients/typescript-reference) for detailed API docs
diff --git a/templates/llm-chat-ts/index.html b/templates/llm-chat-ts/index.html
new file mode 100644
index 00000000000..03d33f37afd
--- /dev/null
+++ b/templates/llm-chat-ts/index.html
@@ -0,0 +1,12 @@
+
+
+
+ );
+}
+
+export default App;
diff --git a/templates/llm-chat-ts/src/main.tsx b/templates/llm-chat-ts/src/main.tsx
new file mode 100644
index 00000000000..6015084c54e
--- /dev/null
+++ b/templates/llm-chat-ts/src/main.tsx
@@ -0,0 +1,35 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { Identity } from 'spacetimedb';
+import { SpacetimeDBProvider } from 'spacetimedb/react';
+import App from './App.tsx';
+import { DbConnection, type ErrorContext } from './module_bindings/index.ts';
+import './App.css';
+
+const HOST = import.meta.env.VITE_SPACETIMEDB_HOST ?? 'ws://localhost:3000';
+const DB_NAME = import.meta.env.VITE_SPACETIMEDB_DB_NAME ?? 'llm-chat-ts';
+const TOKEN_KEY = `${HOST}/${DB_NAME}/auth_token`;
+
+const connectionBuilder = DbConnection.builder()
+ .withUri(HOST)
+ .withDatabaseName(DB_NAME)
+ .withToken(localStorage.getItem(TOKEN_KEY) || undefined)
+ .onConnect((_conn: DbConnection, identity: Identity, token: string) => {
+ localStorage.setItem(TOKEN_KEY, token);
+ console.log('Connected as', identity.toHexString());
+ })
+ .onDisconnect(() => {
+ console.log('Disconnected from SpacetimeDB');
+ })
+ .onConnectError((_ctx: ErrorContext, err: Error) => {
+ console.error('Error connecting to SpacetimeDB:', err);
+ });
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+);
+
diff --git a/templates/llm-chat-ts/src/module_bindings/chat_table.ts b/templates/llm-chat-ts/src/module_bindings/chat_table.ts
new file mode 100644
index 00000000000..4b84514a5e5
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/chat_table.ts
@@ -0,0 +1,19 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export default __t.row({
+ id: __t.u64(),
+ owner: __t.identity(),
+ title: __t.string(),
+ createdAt: __t.timestamp().name("created_at"),
+ updatedAt: __t.timestamp().name("updated_at"),
+});
diff --git a/templates/llm-chat-ts/src/module_bindings/create_chat_procedure.ts b/templates/llm-chat-ts/src/module_bindings/create_chat_procedure.ts
new file mode 100644
index 00000000000..e6d8a724243
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/create_chat_procedure.ts
@@ -0,0 +1,15 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export const params = {
+};
+export const returnType = __t.u64()
\ No newline at end of file
diff --git a/templates/llm-chat-ts/src/module_bindings/delete_chat_reducer.ts b/templates/llm-chat-ts/src/module_bindings/delete_chat_reducer.ts
new file mode 100644
index 00000000000..6dc2f770ce9
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/delete_chat_reducer.ts
@@ -0,0 +1,15 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export default {
+ chatId: __t.u64(),
+};
diff --git a/templates/llm-chat-ts/src/module_bindings/get_llm_config_status_procedure.ts b/templates/llm-chat-ts/src/module_bindings/get_llm_config_status_procedure.ts
new file mode 100644
index 00000000000..0723531e1c5
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/get_llm_config_status_procedure.ts
@@ -0,0 +1,19 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+import {
+ LlmConfigStatus,
+} from "./types";
+
+export const params = {
+};
+export const returnType = LlmConfigStatus
\ No newline at end of file
diff --git a/templates/llm-chat-ts/src/module_bindings/index.ts b/templates/llm-chat-ts/src/module_bindings/index.ts
new file mode 100644
index 00000000000..35d3298c5c7
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/index.ts
@@ -0,0 +1,134 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 2.3.0 (commit 13a2ffd10f04ab1601f55e19f79cb2909ca44ab5).
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ DbConnectionBuilder as __DbConnectionBuilder,
+ DbConnectionImpl as __DbConnectionImpl,
+ SubscriptionBuilderImpl as __SubscriptionBuilderImpl,
+ TypeBuilder as __TypeBuilder,
+ Uuid as __Uuid,
+ convertToAccessorMap as __convertToAccessorMap,
+ makeQueryBuilder as __makeQueryBuilder,
+ procedureSchema as __procedureSchema,
+ procedures as __procedures,
+ reducerSchema as __reducerSchema,
+ reducers as __reducers,
+ schema as __schema,
+ t as __t,
+ table as __table,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type DbConnectionConfig as __DbConnectionConfig,
+ type ErrorContextInterface as __ErrorContextInterface,
+ type Event as __Event,
+ type EventContextInterface as __EventContextInterface,
+ type Infer as __Infer,
+ type QueryBuilder as __QueryBuilder,
+ type ReducerEventContextInterface as __ReducerEventContextInterface,
+ type RemoteModule as __RemoteModule,
+ type SubscriptionEventContextInterface as __SubscriptionEventContextInterface,
+ type SubscriptionHandleImpl as __SubscriptionHandleImpl,
+} from "spacetimedb";
+
+// Import all reducer arg schemas
+import DeleteChatReducer from "./delete_chat_reducer";
+import SetLlmConfigReducer from "./set_llm_config_reducer";
+
+// Import all procedure arg schemas
+import * as CreateChatProcedure from "./create_chat_procedure";
+import * as GetLlmConfigStatusProcedure from "./get_llm_config_status_procedure";
+import * as SendMessageProcedure from "./send_message_procedure";
+
+// Import all table schema definitions
+import ChatRow from "./chat_table";
+import MessageRow from "./message_table";
+
+/** Type-only namespace exports for generated type groups. */
+
+/** The schema information for all tables in this module. This is defined the same was as the tables would have been defined in the server. */
+const tablesSchema = __schema({
+ chat: __table({
+ name: 'chat',
+ indexes: [
+ ],
+ constraints: [
+ ],
+ }, ChatRow),
+ message: __table({
+ name: 'message',
+ indexes: [
+ ],
+ constraints: [
+ ],
+ }, MessageRow),
+});
+
+/** The schema information for all reducers in this module. This is defined the same way as the reducers would have been defined in the server, except the body of the reducer is omitted in code generation. */
+const reducersSchema = __reducers(
+ __reducerSchema("delete_chat", DeleteChatReducer),
+ __reducerSchema("set_llm_config", SetLlmConfigReducer),
+);
+
+/** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */
+const proceduresSchema = __procedures(
+ __procedureSchema("create_chat", CreateChatProcedure.params, CreateChatProcedure.returnType),
+ __procedureSchema("get_llm_config_status", GetLlmConfigStatusProcedure.params, GetLlmConfigStatusProcedure.returnType),
+ __procedureSchema("send_message", SendMessageProcedure.params, SendMessageProcedure.returnType),
+);
+
+/** The remote SpacetimeDB module schema, both runtime and type information. */
+const REMOTE_MODULE = {
+ versionInfo: {
+ cliVersion: "2.3.0" as const,
+ },
+ tables: tablesSchema.schemaType.tables,
+ reducers: reducersSchema.reducersType.reducers,
+ ...proceduresSchema,
+} satisfies __RemoteModule<
+ typeof tablesSchema.schemaType,
+ typeof reducersSchema.reducersType,
+ typeof proceduresSchema
+>;
+
+/** The tables available in this remote SpacetimeDB module. Each table reference doubles as a query builder. */
+export const tables: __QueryBuilder = __makeQueryBuilder(tablesSchema.schemaType);
+
+/** The reducers available in this remote SpacetimeDB module. */
+export const reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);
+
+/** The procedures available in this remote SpacetimeDB module. */
+export const procedures = __convertToAccessorMap(proceduresSchema.procedures);
+
+/** The context type returned in callbacks for all possible events. */
+export type EventContext = __EventContextInterface;
+/** The context type returned in callbacks for reducer events. */
+export type ReducerEventContext = __ReducerEventContextInterface;
+/** The context type returned in callbacks for subscription events. */
+export type SubscriptionEventContext = __SubscriptionEventContextInterface;
+/** The context type returned in callbacks for error events. */
+export type ErrorContext = __ErrorContextInterface;
+/** The subscription handle type to manage active subscriptions created from a {@link SubscriptionBuilder}. */
+export type SubscriptionHandle = __SubscriptionHandleImpl;
+
+/** Builder class to configure a new subscription to the remote SpacetimeDB instance. */
+export class SubscriptionBuilder extends __SubscriptionBuilderImpl {}
+
+/** Builder class to configure a new database connection to the remote SpacetimeDB instance. */
+export class DbConnectionBuilder extends __DbConnectionBuilder {}
+
+/** The typed database connection to manage connections to the remote SpacetimeDB instance. This class has type information specific to the generated module. */
+export class DbConnection extends __DbConnectionImpl {
+ /** Creates a new {@link DbConnectionBuilder} to configure and connect to the remote SpacetimeDB instance. */
+ static builder = (): DbConnectionBuilder => {
+ return new DbConnectionBuilder(REMOTE_MODULE, (config: __DbConnectionConfig) => new DbConnection(config));
+ };
+
+ /** Creates a new {@link SubscriptionBuilder} to configure a subscription to the remote SpacetimeDB instance. */
+ override subscriptionBuilder = (): SubscriptionBuilder => {
+ return new SubscriptionBuilder(this);
+ };
+}
+
diff --git a/templates/llm-chat-ts/src/module_bindings/message_table.ts b/templates/llm-chat-ts/src/module_bindings/message_table.ts
new file mode 100644
index 00000000000..eff0364983f
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/message_table.ts
@@ -0,0 +1,21 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export default __t.row({
+ id: __t.u64(),
+ chatId: __t.u64().name("chat_id"),
+ owner: __t.identity(),
+ role: __t.string(),
+ content: __t.string(),
+ isError: __t.bool().name("is_error"),
+ createdAt: __t.timestamp().name("created_at"),
+});
diff --git a/templates/llm-chat-ts/src/module_bindings/send_message_procedure.ts b/templates/llm-chat-ts/src/module_bindings/send_message_procedure.ts
new file mode 100644
index 00000000000..f8690ad238e
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/send_message_procedure.ts
@@ -0,0 +1,17 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export const params = {
+ chatId: __t.u64(),
+ content: __t.string(),
+};
+export const returnType = __t.unit()
\ No newline at end of file
diff --git a/templates/llm-chat-ts/src/module_bindings/set_llm_config_reducer.ts b/templates/llm-chat-ts/src/module_bindings/set_llm_config_reducer.ts
new file mode 100644
index 00000000000..9a53882eb9f
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/set_llm_config_reducer.ts
@@ -0,0 +1,18 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export default {
+ provider: __t.string(),
+ apiKey: __t.option(__t.string()),
+ model: __t.string(),
+ systemPrompt: __t.option(__t.string()),
+};
diff --git a/templates/llm-chat-ts/src/module_bindings/types.ts b/templates/llm-chat-ts/src/module_bindings/types.ts
new file mode 100644
index 00000000000..b390beb5bf7
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/types.ts
@@ -0,0 +1,76 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import {
+ TypeBuilder as __TypeBuilder,
+ t as __t,
+ type AlgebraicTypeType as __AlgebraicTypeType,
+ type Infer as __Infer,
+} from "spacetimedb";
+
+export const Chat = __t.object("Chat", {});
+export type Chat = __Infer;
+
+export const ChatMessage = __t.object("ChatMessage", {
+ id: __t.u64(),
+ chatId: __t.u64(),
+ owner: __t.identity(),
+ role: __t.string(),
+ content: __t.string(),
+ isError: __t.bool(),
+ createdAt: __t.timestamp(),
+});
+export type ChatMessage = __Infer;
+
+export const ChatThread = __t.object("ChatThread", {
+ id: __t.u64(),
+ owner: __t.identity(),
+ title: __t.string(),
+ createdAt: __t.timestamp(),
+ updatedAt: __t.timestamp(),
+});
+export type ChatThread = __Infer;
+
+export const ChatViewRow = __t.object("ChatViewRow", {
+ id: __t.u64(),
+ owner: __t.identity(),
+ title: __t.string(),
+ createdAt: __t.timestamp(),
+ updatedAt: __t.timestamp(),
+});
+export type ChatViewRow = __Infer;
+
+export const LlmConfig = __t.object("LlmConfig", {
+ owner: __t.identity(),
+ provider: __t.string(),
+ apiKey: __t.string(),
+ model: __t.string(),
+ systemPrompt: __t.option(__t.string()),
+ updatedAt: __t.timestamp(),
+});
+export type LlmConfig = __Infer;
+
+export const LlmConfigStatus = __t.object("LlmConfigStatus", {
+ configured: __t.bool(),
+ provider: __t.option(__t.string()),
+ model: __t.option(__t.string()),
+ systemPrompt: __t.option(__t.string()),
+});
+export type LlmConfigStatus = __Infer;
+
+export const Message = __t.object("Message", {});
+export type Message = __Infer;
+
+export const MessageViewRow = __t.object("MessageViewRow", {
+ id: __t.u64(),
+ chatId: __t.u64(),
+ owner: __t.identity(),
+ role: __t.string(),
+ content: __t.string(),
+ isError: __t.bool(),
+ createdAt: __t.timestamp(),
+});
+export type MessageViewRow = __Infer;
+
diff --git a/templates/llm-chat-ts/src/module_bindings/types/procedures.ts b/templates/llm-chat-ts/src/module_bindings/types/procedures.ts
new file mode 100644
index 00000000000..4f9af0da8f9
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/types/procedures.ts
@@ -0,0 +1,19 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import { type Infer as __Infer } from "spacetimedb";
+
+// Import all procedure arg schemas
+import * as CreateChatProcedure from "../create_chat_procedure";
+import * as GetLlmConfigStatusProcedure from "../get_llm_config_status_procedure";
+import * as SendMessageProcedure from "../send_message_procedure";
+
+export type CreateChatArgs = __Infer;
+export type CreateChatResult = __Infer;
+export type GetLlmConfigStatusArgs = __Infer;
+export type GetLlmConfigStatusResult = __Infer;
+export type SendMessageArgs = __Infer;
+export type SendMessageResult = __Infer;
+
diff --git a/templates/llm-chat-ts/src/module_bindings/types/reducers.ts b/templates/llm-chat-ts/src/module_bindings/types/reducers.ts
new file mode 100644
index 00000000000..9b254ed5b6b
--- /dev/null
+++ b/templates/llm-chat-ts/src/module_bindings/types/reducers.ts
@@ -0,0 +1,14 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+import { type Infer as __Infer } from "spacetimedb";
+
+// Import all reducer arg schemas
+import DeleteChatReducer from "../delete_chat_reducer";
+import SetLlmConfigReducer from "../set_llm_config_reducer";
+
+export type DeleteChatParams = __Infer;
+export type SetLlmConfigParams = __Infer;
+
diff --git a/templates/llm-chat-ts/tsconfig.json b/templates/llm-chat-ts/tsconfig.json
new file mode 100644
index 00000000000..01b2e5efb38
--- /dev/null
+++ b/templates/llm-chat-ts/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "types": ["vite/client"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src", "vite.config.ts"]
+}
diff --git a/templates/llm-chat-ts/vite.config.ts b/templates/llm-chat-ts/vite.config.ts
new file mode 100644
index 00000000000..5c594471f82
--- /dev/null
+++ b/templates/llm-chat-ts/vite.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 5173,
+ },
+});