You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Longer warm timeout means faster responses but more compute usage. Set to `0` to suspend immediately after each turn (minimum latency cost, slight delay on next message).
779
779
</Info>
780
780
781
+
## Per-run data with `chat.local`
782
+
783
+
Use `chat.local` to create typed, run-scoped data that persists across turns and is accessible from anywhere — the run function, tools, nested helpers. Each run gets its own isolated copy, and locals are automatically cleared between runs.
784
+
785
+
### Declaring and initializing
786
+
787
+
Declare locals at module level, then initialize them inside a lifecycle hook where you have context (chatId, clientData, etc.):
788
+
789
+
```ts
790
+
import { chat } from"@trigger.dev/sdk/ai";
791
+
import { streamText, tool } from"ai";
792
+
import { openai } from"@ai-sdk/openai";
793
+
import { z } from"zod";
794
+
import { db } from"@/lib/db";
795
+
796
+
// Declare at module level — multiple locals can coexist
return { error: "This feature requires a Pro plan." };
843
+
}
844
+
// ... premium logic
845
+
},
846
+
});
847
+
```
848
+
849
+
### Dirty tracking and persistence
850
+
851
+
The `hasChanged()` method returns `true` if any property was set since the last check, then resets the flag. Use it in lifecycle hooks to only persist when data actually changed:
852
+
853
+
```ts
854
+
onTurnComplete: async ({ chatId }) => {
855
+
if (userContext.hasChanged()) {
856
+
awaitdb.user.update({
857
+
where: { id: userContext.get().userId },
858
+
data: {
859
+
messageCount: userContext.messageCount,
860
+
},
861
+
});
862
+
}
863
+
},
864
+
```
865
+
866
+
### API reference
867
+
868
+
| Method | Description |
869
+
|--------|-------------|
870
+
|`chat.local<T>()`| Create a typed local (declare at module level) |
871
+
|`local.init(value)`| Initialize with a value (call in hooks or `run`) |
872
+
|`local.hasChanged()`| Returns `true` if modified since last check, resets flag |
873
+
|`local.get()`| Returns a plain object copy (for serialization) |
874
+
|`local.property`| Direct property access (read/write via Proxy) |
875
+
876
+
<Note>
877
+
Locals use shallow proxying. Nested object mutations like `local.prefs.theme = "dark"` won't trigger the dirty flag. Instead, replace the whole property: `local.prefs = { ...local.prefs, theme: "dark" }`.
878
+
</Note>
879
+
781
880
## Frontend reference
782
881
783
882
### TriggerChatTransport options
@@ -897,6 +996,7 @@ See [onTurnComplete](#onturncomplete) for the full field reference.
897
996
|--------|-------------|
898
997
|`chat.task(options)`| Create a chat task |
899
998
|`chat.pipe(source, options?)`| Pipe a stream to the frontend (from anywhere inside a task) |
999
+
|`chat.local<T>()`| Create a per-run typed local (see [Per-run data](#per-run-data-with-chatlocal)) |
900
1000
|`chat.createAccessToken(taskId)`| Create a public access token for a chat task |
901
1001
|`chat.setTurnTimeout(duration)`| Override turn timeout at runtime (e.g. `"2h"`) |
902
1002
|`chat.setTurnTimeoutInSeconds(seconds)`| Override turn timeout at runtime (in seconds) |
0 commit comments