lemma-sdk 0.2.23 → 0.2.25
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/README.md +156 -511
- package/dist/auth.js +0 -1
- package/dist/browser/lemma-client.js +196 -112
- package/dist/namespaces/assistants.d.ts +21 -3
- package/dist/namespaces/assistants.js +13 -7
- package/dist/namespaces/files.d.ts +9 -4
- package/dist/namespaces/files.js +52 -14
- package/dist/namespaces/records.d.ts +10 -2
- package/dist/namespaces/records.js +15 -9
- package/dist/openapi_client/index.d.ts +7 -7
- package/dist/openapi_client/index.js +2 -4
- package/dist/openapi_client/models/ColumnSchema.d.ts +4 -0
- package/dist/openapi_client/models/CreateConversationRequest.d.ts +1 -1
- package/dist/openapi_client/models/CreateFolderRequest.d.ts +1 -2
- package/dist/openapi_client/models/CreateTableRequest.d.ts +1 -1
- package/dist/openapi_client/models/CreateTriggerRequest.d.ts +0 -1
- package/dist/openapi_client/models/DatastoreFileUploadRequest.d.ts +1 -1
- package/dist/openapi_client/models/DatastoreQueryRequest.d.ts +9 -0
- package/dist/openapi_client/models/DatastoreQueryResponse.d.ts +7 -0
- package/dist/openapi_client/models/DirectoryTreeNode.d.ts +7 -0
- package/dist/openapi_client/models/DirectoryTreeResponse.d.ts +6 -0
- package/dist/openapi_client/models/DirectoryTreeResponse.js +1 -0
- package/dist/openapi_client/models/FileResponse.d.ts +4 -6
- package/dist/openapi_client/models/FileSearchRequest.d.ts +5 -3
- package/dist/openapi_client/models/FileSearchResultSchema.d.ts +1 -0
- package/dist/openapi_client/models/FileSearchScopeMode.d.ts +4 -0
- package/dist/openapi_client/models/FileSearchScopeMode.js +9 -0
- package/dist/openapi_client/models/PodCreateRequest.d.ts +0 -4
- package/dist/openapi_client/models/PodMemberDetailResponse.d.ts +14 -0
- package/dist/openapi_client/models/PodMemberDetailResponse.js +1 -0
- package/dist/openapi_client/models/PodMemberResponse.d.ts +3 -3
- package/dist/openapi_client/models/PodResponse.d.ts +0 -5
- package/dist/openapi_client/models/PodUpdateRequest.d.ts +0 -4
- package/dist/openapi_client/models/TableResponse.d.ts +1 -1
- package/dist/openapi_client/models/TriggerResponse.d.ts +0 -1
- package/dist/openapi_client/models/update.d.ts +2 -2
- package/dist/openapi_client/services/ConversationsService.d.ts +3 -2
- package/dist/openapi_client/services/ConversationsService.js +5 -3
- package/dist/openapi_client/services/FilesService.d.ts +34 -25
- package/dist/openapi_client/services/FilesService.js +75 -47
- package/dist/openapi_client/services/PodMembersService.d.ts +14 -4
- package/dist/openapi_client/services/PodMembersService.js +29 -8
- package/dist/openapi_client/services/QueryService.d.ts +14 -0
- package/dist/openapi_client/services/QueryService.js +26 -0
- package/dist/openapi_client/services/RecordsService.d.ts +7 -13
- package/dist/openapi_client/services/RecordsService.js +12 -26
- package/dist/react/components/AssistantEmbedded.d.ts +1 -1
- package/dist/react/components/AssistantEmbedded.js +2 -1
- package/dist/react/index.d.ts +0 -2
- package/dist/react/index.js +0 -1
- package/dist/react/useAssistantController.d.ts +5 -1
- package/dist/react/useAssistantController.js +7 -3
- package/dist/react/useAssistantSession.d.ts +12 -0
- package/dist/react/useAssistantSession.js +24 -5
- package/dist/react/useTaskSession.js +145 -73
- package/dist/task-events.d.ts +2 -1
- package/dist/task-events.js +38 -1
- package/dist/types.d.ts +10 -4
- package/package.json +1 -1
- package/dist/openapi_client/models/PodStatus.d.ts +0 -4
- package/dist/openapi_client/models/PodStatus.js +0 -9
- package/dist/openapi_client/models/PodType.d.ts +0 -6
- package/dist/openapi_client/models/PodType.js +0 -11
- package/dist/openapi_client/models/RecordFilter.d.ts +0 -15
- package/dist/openapi_client/models/RecordFilterOperator.d.ts +0 -10
- package/dist/openapi_client/models/RecordFilterOperator.js +0 -15
- package/dist/openapi_client/models/RecordQueryRequest.d.ts +0 -20
- package/dist/openapi_client/models/RecordSort.d.ts +0 -11
- package/dist/openapi_client/models/RecordSortDirection.d.ts +0 -4
- package/dist/openapi_client/models/RecordSortDirection.js +0 -9
- package/dist/react/useAgentRun.d.ts +0 -17
- package/dist/react/useAgentRun.js +0 -66
- /package/dist/openapi_client/models/{RecordFilter.js → DatastoreQueryRequest.js} +0 -0
- /package/dist/openapi_client/models/{RecordQueryRequest.js → DatastoreQueryResponse.js} +0 -0
- /package/dist/openapi_client/models/{RecordSort.js → DirectoryTreeNode.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Lemma TypeScript SDK (`lemma-sdk`)
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for Lemma APIs with
|
|
3
|
+
Official TypeScript SDK for Lemma APIs with:
|
|
4
|
+
|
|
5
|
+
- Pod-scoped namespaces (`tables`, `records`, `files`, `assistants`,`agents` , `workflows`, `tasks`, .)
|
|
6
|
+
- Built-in auth/session handling
|
|
7
|
+
- SSE streaming helpers
|
|
8
|
+
- React hooks and assistant UI components
|
|
4
9
|
|
|
5
10
|
## Install
|
|
6
11
|
|
|
@@ -8,26 +13,14 @@ Official TypeScript SDK for Lemma APIs with pod-scoped namespaces, auth helpers,
|
|
|
8
13
|
npm i lemma-sdk
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
For local workspace development against the checked-out SDK instead of npm:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm i file:../lemma-typescript
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
If you want to import as `lemma`, use npm aliasing:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm i lemma@npm:lemma-sdk
|
|
21
|
-
```
|
|
22
|
-
|
|
23
16
|
## Quick Start
|
|
24
17
|
|
|
25
18
|
```ts
|
|
26
19
|
import { LemmaClient } from "lemma-sdk";
|
|
27
20
|
|
|
28
21
|
const client = new LemmaClient({
|
|
29
|
-
apiUrl: "https://api
|
|
30
|
-
authUrl: "https://auth.
|
|
22
|
+
apiUrl: "https://api.lemma.work",
|
|
23
|
+
authUrl: "https://auth.lemma.work/auth",
|
|
31
24
|
podId: "<pod-id>",
|
|
32
25
|
});
|
|
33
26
|
|
|
@@ -38,588 +31,240 @@ const assistants = await client.assistants.list({ limit: 20 });
|
|
|
38
31
|
const supportAssistant = await client.assistants.get("support_assistant");
|
|
39
32
|
```
|
|
40
33
|
|
|
41
|
-
##
|
|
34
|
+
## Configuration
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
- Namespace APIs (`client.agents`, `client.tasks`, `client.conversations`, etc.) for typed operations.
|
|
45
|
-
- `client.request(method, path, options)` escape hatch for endpoints not yet modeled.
|
|
46
|
-
- `client.resources` for generic file resource APIs (`conversation`, `assistant`, `task`, etc.).
|
|
47
|
-
- Ergonomic type aliases exported at top level: `Agent`, `Assistant`, `Conversation`, `Task`, `TaskMessage`, `CreateAgentInput`, `CreateAssistantInput`, etc.
|
|
48
|
-
- `client.withPod(podId)` returns a pod-scoped client that shares auth state with the parent client.
|
|
36
|
+
`LemmaClient` config resolution order:
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
1. Constructor overrides
|
|
39
|
+
2. `window.__LEMMA_CONFIG__`
|
|
40
|
+
3. Environment variables
|
|
41
|
+
4. Defaults
|
|
51
42
|
|
|
52
|
-
|
|
43
|
+
Supported env keys:
|
|
53
44
|
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
45
|
+
- `VITE_LEMMA_API_URL`, `REACT_APP_LEMMA_API_URL`, `LEMMA_API_URL`
|
|
46
|
+
- `VITE_LEMMA_AUTH_URL`, `REACT_APP_LEMMA_AUTH_URL`, `LEMMA_AUTH_URL`
|
|
47
|
+
- `VITE_LEMMA_POD_ID`, `REACT_APP_LEMMA_POD_ID`, `LEMMA_POD_ID`
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
Defaults when unset:
|
|
58
50
|
|
|
59
|
-
|
|
51
|
+
- `apiUrl`: `http://localhost:8000`
|
|
52
|
+
- `authUrl`: `http://localhost:3000`
|
|
60
53
|
|
|
61
|
-
|
|
54
|
+
## Pod Scoping
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
import {
|
|
65
|
-
TableAccessMode,
|
|
66
|
-
type CreateFunctionRequest,
|
|
67
|
-
type CreateAgentInput,
|
|
68
|
-
type CreateAssistantInput,
|
|
69
|
-
} from "lemma-sdk";
|
|
70
|
-
|
|
71
|
-
const functionPayload: CreateFunctionRequest = {
|
|
72
|
-
name: "expense_summary",
|
|
73
|
-
code: "def handler(ctx):\n return {'ok': True}",
|
|
74
|
-
config: {},
|
|
75
|
-
accessible_tables: [
|
|
76
|
-
{ table_name: "expenses", mode: TableAccessMode.READ },
|
|
77
|
-
{ table_name: "expense_summaries", mode: TableAccessMode.WRITE },
|
|
78
|
-
],
|
|
79
|
-
accessible_folders: ["/reports"],
|
|
80
|
-
accessible_applications: [],
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const agentPayload: CreateAgentInput = {
|
|
84
|
-
name: "expense-summarizer",
|
|
85
|
-
instruction: "Summarize expenses without mutating data.",
|
|
86
|
-
tool_sets: [],
|
|
87
|
-
accessible_tables: [
|
|
88
|
-
{ table_name: "expenses", mode: TableAccessMode.READ },
|
|
89
|
-
{ table_name: "expense_notes", mode: TableAccessMode.WRITE },
|
|
90
|
-
],
|
|
91
|
-
accessible_folders: [],
|
|
92
|
-
accessible_applications: [],
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const assistantPayload: CreateAssistantInput = {
|
|
96
|
-
name: "expense_assistant",
|
|
97
|
-
instruction: "Answer expense questions and save approved notes.",
|
|
98
|
-
tool_sets: [],
|
|
99
|
-
accessible_tables: [
|
|
100
|
-
{ table_name: "expenses", mode: TableAccessMode.READ },
|
|
101
|
-
{ table_name: "expense_notes", mode: TableAccessMode.WRITE },
|
|
102
|
-
],
|
|
103
|
-
accessible_folders: ["/notes"],
|
|
104
|
-
accessible_applications: [],
|
|
105
|
-
};
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Auth Helpers
|
|
56
|
+
Most namespaces are pod-scoped. You can set pod scope in three ways:
|
|
109
57
|
|
|
110
58
|
```ts
|
|
111
|
-
|
|
112
|
-
LemmaClient,
|
|
113
|
-
buildAuthUrl,
|
|
114
|
-
buildFederatedLogoutUrl,
|
|
115
|
-
resolveSafeRedirectUri,
|
|
116
|
-
} from "lemma-sdk";
|
|
117
|
-
|
|
118
|
-
const client = new LemmaClient({
|
|
119
|
-
apiUrl: "https://api-next.asur.work",
|
|
120
|
-
authUrl: "https://auth.asur.work/auth",
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Build auth URLs (server/client)
|
|
124
|
-
const loginUrl = buildAuthUrl(client.authUrl, { redirectUri: "https://app.asur.work/" });
|
|
125
|
-
const signupUrl = buildAuthUrl(client.authUrl, { mode: "signup", redirectUri: "https://app.asur.work/" });
|
|
126
|
-
|
|
127
|
-
// Redirect safety helper for auth route handlers
|
|
128
|
-
const safeRedirect = resolveSafeRedirectUri("/pod/123", {
|
|
129
|
-
siteOrigin: "https://app.asur.work",
|
|
130
|
-
fallback: "/",
|
|
131
|
-
});
|
|
59
|
+
client.setPodId("pod_a");
|
|
132
60
|
|
|
133
|
-
|
|
134
|
-
await client.auth.checkAuth();
|
|
135
|
-
await client.auth.signOut();
|
|
136
|
-
const token = await client.auth.getAccessToken();
|
|
137
|
-
const refreshed = await client.auth.refreshAccessToken();
|
|
138
|
-
client.auth.redirectToAuth({ mode: "signup", redirectUri: safeRedirect });
|
|
61
|
+
const podBClient = client.withPod("pod_b");
|
|
139
62
|
|
|
140
|
-
|
|
141
|
-
const federatedLogoutUrl = buildFederatedLogoutUrl(client.authUrl, {
|
|
142
|
-
redirectUri: safeRedirect,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Browser: sign out locally, then clear upstream SSO and return to app
|
|
146
|
-
await client.auth.redirectToFederatedLogout({ redirectUri: safeRedirect });
|
|
63
|
+
const conversations = await client.conversations.list({ pod_id: "pod_c" });
|
|
147
64
|
```
|
|
148
65
|
|
|
149
|
-
|
|
66
|
+
## Namespace Overview
|
|
150
67
|
|
|
151
|
-
|
|
152
|
-
This is the only supported browser token-injection path.
|
|
68
|
+
Common pod-scoped namespaces:
|
|
153
69
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await client.initialize();
|
|
166
|
-
|
|
167
|
-
clearTestingToken();
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Equivalent manual browser setup:
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
localStorage.setItem("lemma_token", "<access-token>");
|
|
174
|
-
window.location.reload();
|
|
175
|
-
```
|
|
70
|
+
- `client.tables`
|
|
71
|
+
- `client.records`
|
|
72
|
+
- `client.files`
|
|
73
|
+
- `client.functions`
|
|
74
|
+
- `client.agents`
|
|
75
|
+
- `client.tasks`
|
|
76
|
+
- `client.assistants`
|
|
77
|
+
- `client.workflows`
|
|
78
|
+
- `client.desks`
|
|
79
|
+
- `client.integrations`
|
|
80
|
+
- `client.resources`
|
|
176
81
|
|
|
177
|
-
|
|
82
|
+
Org/user-level namespaces:
|
|
178
83
|
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
84
|
+
- `client.users`
|
|
85
|
+
- `client.icons`
|
|
86
|
+
- `client.pods`
|
|
87
|
+
- `client.podMembers`
|
|
88
|
+
- `client.organizations`
|
|
89
|
+
- `client.podSurfaces`
|
|
182
90
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### React assistant UI
|
|
186
|
-
|
|
187
|
-
`lemma-sdk/react` ships the assistant controller, the default assistant experience, and the lower-level UI primitives used to build custom shells.
|
|
188
|
-
|
|
189
|
-
Import the bundled stylesheet once anywhere in your app:
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
import "lemma-sdk/react/styles.css";
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
The stylesheet includes the SDK theme tokens and the complete semantic assistant UI. The assistant components do not depend on the host app's Tailwind version or Tailwind content scanning.
|
|
196
|
-
|
|
197
|
-
If you alias the package to local SDK source in Vite, make sure the alias points at the React source and stylesheet:
|
|
91
|
+
Escape hatch for unmapped endpoints:
|
|
198
92
|
|
|
199
93
|
```ts
|
|
200
|
-
|
|
201
|
-
import path from "node:path";
|
|
202
|
-
|
|
203
|
-
export default {
|
|
204
|
-
resolve: {
|
|
205
|
-
alias: {
|
|
206
|
-
"lemma-sdk/react/styles.css": path.resolve(__dirname, "../lemma-typescript/src/react/styles.css"),
|
|
207
|
-
"lemma-sdk/react": path.resolve(__dirname, "../lemma-typescript/src/react/index.ts"),
|
|
208
|
-
"lemma-sdk": path.resolve(__dirname, "../lemma-typescript/src/index.ts"),
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Quick checklist for developers:
|
|
215
|
-
|
|
216
|
-
- import `lemma-sdk/react/styles.css` once
|
|
217
|
-
- give the assistant container a real height
|
|
218
|
-
- if the assistant is inside flex/grid, add `min-height: 0` on the relevant parent
|
|
219
|
-
- if you use `AssistantEmbedded`, pass `theme` directly there
|
|
220
|
-
- if you use `AssistantExperienceView`, wrap it in `AssistantThemeScope`
|
|
221
|
-
|
|
222
|
-
The assistant UI renders markdown by default:
|
|
223
|
-
|
|
224
|
-
- GitHub-flavored markdown is enabled for assistant and user messages
|
|
225
|
-
- raw HTML is not rendered
|
|
226
|
-
- links open safely in a new tab by default
|
|
227
|
-
- lists, tables, blockquotes, inline code, and fenced code blocks are styled out of the box
|
|
228
|
-
|
|
229
|
-
#### Recommended path
|
|
230
|
-
|
|
231
|
-
For most apps, start with `AssistantEmbedded`.
|
|
232
|
-
|
|
233
|
-
- use `AssistantEmbedded` when you want the SDK to handle the controller lifecycle and render the ready-made assistant UI
|
|
234
|
-
- use `AssistantExperienceView` when you still want the SDK's default assistant UI, but you need to own the controller lifecycle yourself
|
|
235
|
-
- use `useAssistantController` plus primitives only when you are intentionally building a custom shell or custom layout
|
|
236
|
-
|
|
237
|
-
If you are unsure, use `AssistantEmbedded` first. It is the path we recommend and the one we expect most SDK consumers to ship.
|
|
238
|
-
|
|
239
|
-
#### Choose an integration level
|
|
240
|
-
|
|
241
|
-
##### 1. `AssistantEmbedded` for the fastest setup
|
|
242
|
-
|
|
243
|
-
Use `AssistantEmbedded` when you want a ready-made assistant surface with the SDK defaults.
|
|
244
|
-
This is the recommended integration for most users.
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
import "lemma-sdk/react/styles.css";
|
|
248
|
-
import { AssistantEmbedded } from "lemma-sdk/react";
|
|
249
|
-
|
|
250
|
-
function SupportAssistant() {
|
|
251
|
-
return (
|
|
252
|
-
<div style={{ height: 720, minHeight: 0 }}>
|
|
253
|
-
<AssistantEmbedded
|
|
254
|
-
client={client}
|
|
255
|
-
podId="pod_123"
|
|
256
|
-
assistantId="uuid"
|
|
257
|
-
title="Support Assistant"
|
|
258
|
-
subtitle="Ask questions about this pod."
|
|
259
|
-
placeholder="Message Support Assistant"
|
|
260
|
-
emptyStateSuggestions={[
|
|
261
|
-
{ text: "Summarize this conversation", icon: "✦" },
|
|
262
|
-
{ text: "Help me draft a response", icon: "✎" },
|
|
263
|
-
{ text: "List the next steps", icon: "→" },
|
|
264
|
-
]}
|
|
265
|
-
showConversationList
|
|
266
|
-
showModelPicker={false}
|
|
267
|
-
radius="lg"
|
|
268
|
-
theme="auto"
|
|
269
|
-
/>
|
|
270
|
-
</div>
|
|
271
|
-
);
|
|
272
|
-
}
|
|
94
|
+
const result = await client.request("GET", "/models");
|
|
273
95
|
```
|
|
274
96
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
- `theme` accepts `"auto" | "light" | "dark"`
|
|
278
|
-
- `radius` lets you pick the built-in rounding scale from `"none"` through `"xl"`
|
|
279
|
-
- `showModelPicker={false}` hides the built-in model chooser when you do not want model controls visible
|
|
280
|
-
- `theme="auto"` follows the host app when it uses common selectors like `.dark`, `[data-theme="dark"]`, `[data-mode="dark"]`, and also falls back to `prefers-color-scheme`
|
|
281
|
-
- the parent container must have a real height; if it lives inside flex/grid, `min-height: 0` is usually needed too
|
|
282
|
-
- attachments are queued into the composer and sent with the next message by default
|
|
283
|
-
- `emptyStateSuggestions` lets you replace the built-in prompt chips shown before the first message
|
|
284
|
-
- prefer this component unless you specifically need to own controller state or replace the built-in layout
|
|
285
|
-
|
|
286
|
-
##### 2. `AssistantExperienceView` for the default UI with your own controller
|
|
287
|
-
|
|
288
|
-
Use `AssistantExperienceView` when you want the built-in assistant layout, but you need to own the controller lifecycle yourself.
|
|
289
|
-
This is the second-best default when `AssistantEmbedded` is too opinionated for your integration.
|
|
97
|
+
## CRUD Examples
|
|
290
98
|
|
|
291
|
-
|
|
292
|
-
import "lemma-sdk/react/styles.css";
|
|
293
|
-
import {
|
|
294
|
-
AssistantExperienceView,
|
|
295
|
-
AssistantThemeScope,
|
|
296
|
-
useAssistantController,
|
|
297
|
-
} from "lemma-sdk/react";
|
|
298
|
-
|
|
299
|
-
function ControlledAssistant() {
|
|
300
|
-
const assistant = useAssistantController({
|
|
301
|
-
client,
|
|
302
|
-
podId: "pod_123",
|
|
303
|
-
assistantId: "uuid",
|
|
304
|
-
});
|
|
99
|
+
### Tables + Records
|
|
305
100
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
<AssistantExperienceView
|
|
309
|
-
controller={assistant}
|
|
310
|
-
title="Support Assistant"
|
|
311
|
-
subtitle="Direct use of the default assistant experience."
|
|
312
|
-
placeholder="Message Support Assistant"
|
|
313
|
-
emptyStateSuggestions={[
|
|
314
|
-
{ text: "Summarize the current context" },
|
|
315
|
-
{ text: "Help me write a reply" },
|
|
316
|
-
{ text: "What should I do next?" },
|
|
317
|
-
]}
|
|
318
|
-
showConversationList
|
|
319
|
-
chromeStyle="subtle"
|
|
320
|
-
statusPlacement="inline"
|
|
321
|
-
/>
|
|
322
|
-
</AssistantThemeScope>
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
Useful props on `AssistantExperienceView`:
|
|
328
|
-
|
|
329
|
-
- `showConversationList`: show the built-in conversation sidebar
|
|
330
|
-
- `chromeStyle`: `"elevated" | "subtle" | "flat"`
|
|
331
|
-
- `statusPlacement`: `"inline" | "composer" | "none"`
|
|
332
|
-
- `radius`: `"none" | "sm" | "md" | "lg" | "xl"`
|
|
333
|
-
- `showModelPicker`: show or hide the built-in model selector
|
|
334
|
-
- `showNewConversationButton`: show or hide the built-in reset/new-conversation button
|
|
335
|
-
- `emptyStateSuggestions`: replace the built-in generic prompt suggestions used by the default empty state
|
|
336
|
-
- `renderMessageContent`: override markdown rendering for custom message content
|
|
337
|
-
- `renderToolInvocation`: replace the default tool activity renderer
|
|
338
|
-
- `renderPresentedFile` and `renderPendingFile`: customize attachment rendering
|
|
339
|
-
- prefer this over building from primitives if you still want the SDK's default assistant experience
|
|
340
|
-
|
|
341
|
-
##### 3. `useAssistantController` + primitives for a custom shell
|
|
342
|
-
|
|
343
|
-
Use the primitives when you want full control over layout and app chrome.
|
|
344
|
-
This is the advanced path and should be the exception, not the starting point.
|
|
345
|
-
|
|
346
|
-
```tsx
|
|
347
|
-
import "lemma-sdk/react/styles.css";
|
|
348
|
-
import {
|
|
349
|
-
AssistantComposer,
|
|
350
|
-
AssistantHeader,
|
|
351
|
-
AssistantMessageViewport,
|
|
352
|
-
AssistantShellLayout,
|
|
353
|
-
AssistantThemeScope,
|
|
354
|
-
EmptyState,
|
|
355
|
-
MessageGroup,
|
|
356
|
-
PlanSummaryStrip,
|
|
357
|
-
ThinkingIndicator,
|
|
358
|
-
buildDisplayMessageRows,
|
|
359
|
-
getActiveToolBanner,
|
|
360
|
-
latestPlanSummary,
|
|
361
|
-
useAssistantController,
|
|
362
|
-
} from "lemma-sdk/react";
|
|
363
|
-
|
|
364
|
-
function CustomAssistantShell() {
|
|
365
|
-
const assistant = useAssistantController({
|
|
366
|
-
client,
|
|
367
|
-
podId: "pod_123",
|
|
368
|
-
assistantId: "uuid",
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
const rows = buildDisplayMessageRows(assistant.messages);
|
|
372
|
-
const plan = latestPlanSummary(assistant.messages);
|
|
373
|
-
const activeToolBanner = getActiveToolBanner(assistant.messages);
|
|
374
|
-
|
|
375
|
-
return (
|
|
376
|
-
<AssistantThemeScope theme="auto" style={{ height: 720 }}>
|
|
377
|
-
<AssistantShellLayout
|
|
378
|
-
main={(
|
|
379
|
-
<div className="flex min-h-0 flex-1 flex-col gap-3">
|
|
380
|
-
<AssistantHeader
|
|
381
|
-
title="Lemma Assistant"
|
|
382
|
-
subtitle="Ask anything"
|
|
383
|
-
/>
|
|
384
|
-
|
|
385
|
-
{plan ? <PlanSummaryStrip plan={plan} onHide={() => {}} /> : null}
|
|
386
|
-
{activeToolBanner ? <div>{activeToolBanner.summary}</div> : null}
|
|
387
|
-
|
|
388
|
-
<AssistantMessageViewport>
|
|
389
|
-
{assistant.messages.length === 0 ? (
|
|
390
|
-
<EmptyState
|
|
391
|
-
suggestions={[
|
|
392
|
-
{ text: "Summarize this for me" },
|
|
393
|
-
{ text: "Help me draft a reply" },
|
|
394
|
-
{ text: "Brainstorm next steps" },
|
|
395
|
-
]}
|
|
396
|
-
onSendMessage={(text) => {
|
|
397
|
-
void assistant.sendMessage(text);
|
|
398
|
-
}}
|
|
399
|
-
/>
|
|
400
|
-
) : null}
|
|
401
|
-
|
|
402
|
-
{rows.map((row, index) => (
|
|
403
|
-
<MessageGroup
|
|
404
|
-
key={row.id}
|
|
405
|
-
message={row.message}
|
|
406
|
-
conversationId={assistant.activeConversationId}
|
|
407
|
-
onWidgetSendPrompt={(text) => assistant.sendMessage(text)}
|
|
408
|
-
isStreaming={assistant.isActiveConversationRunning && row.sourceIndexes.includes(assistant.messages.length - 1)}
|
|
409
|
-
showAssistantHeader={index === 0 || rows[index - 1]?.message.role !== "assistant"}
|
|
410
|
-
renderMessageContent={({ message }) => <div>{message.content}</div>}
|
|
411
|
-
/>
|
|
412
|
-
))}
|
|
413
|
-
|
|
414
|
-
{assistant.isActiveConversationRunning ? <ThinkingIndicator /> : null}
|
|
415
|
-
</AssistantMessageViewport>
|
|
416
|
-
|
|
417
|
-
<AssistantComposer>
|
|
418
|
-
<textarea placeholder="Message Lemma Assistant" />
|
|
419
|
-
</AssistantComposer>
|
|
420
|
-
</div>
|
|
421
|
-
)}
|
|
422
|
-
/>
|
|
423
|
-
</AssistantThemeScope>
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
Useful primitives exported from `lemma-sdk/react`:
|
|
429
|
-
|
|
430
|
-
- `AssistantThemeScope`
|
|
431
|
-
- `AssistantHeader`
|
|
432
|
-
- `AssistantConversationList`
|
|
433
|
-
- `AssistantModelPicker`
|
|
434
|
-
- `AssistantShellLayout`
|
|
435
|
-
- `AssistantComposer`
|
|
436
|
-
- `AssistantMessageViewport`
|
|
437
|
-
- `AssistantAskOverlay`
|
|
438
|
-
- `AssistantPendingFileChip`
|
|
439
|
-
- `AssistantStatusPill`
|
|
440
|
-
- `MessageGroup`
|
|
441
|
-
- `PlanSummaryStrip`
|
|
442
|
-
- `ThinkingIndicator`
|
|
443
|
-
|
|
444
|
-
Guidance:
|
|
445
|
-
|
|
446
|
-
- prefer `AssistantEmbedded` over this path when the SDK layout is acceptable
|
|
447
|
-
- prefer `AssistantExperienceView` over this path when you only need controller ownership, theming control, or a few render overrides
|
|
448
|
-
- reach for primitives only when you are replacing the layout itself or deeply integrating the assistant into app-specific chrome
|
|
449
|
-
|
|
450
|
-
Default empty-state suggestions are intentionally generic so they work across support, internal tools, content, and general assistant use cases. Override them with `emptyStateSuggestions` when you want task-specific prompts.
|
|
451
|
-
|
|
452
|
-
#### Theming
|
|
453
|
-
|
|
454
|
-
Use `AssistantThemeScope` around custom assistant layouts:
|
|
101
|
+
```ts
|
|
102
|
+
await client.tables.create({ name: "todos" });
|
|
455
103
|
|
|
456
|
-
|
|
457
|
-
|
|
104
|
+
await client.records.create("todos", {
|
|
105
|
+
title: "Ship docs rewrite",
|
|
106
|
+
status: "todo",
|
|
107
|
+
});
|
|
458
108
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
109
|
+
const page = await client.records.list("todos", {
|
|
110
|
+
limit: 20,
|
|
111
|
+
sort: [{ field: "created_at", direction: "desc" }],
|
|
112
|
+
});
|
|
462
113
|
```
|
|
463
114
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
- `theme="auto"`: follows host dark-mode selectors and system color scheme
|
|
467
|
-
- `theme="light"`: forces the light SDK palette
|
|
468
|
-
- `theme="dark"`: forces the dark SDK palette
|
|
469
|
-
|
|
470
|
-
If you use `AssistantEmbedded`, pass `theme` directly on that component instead of wrapping it again.
|
|
471
|
-
|
|
472
|
-
#### What belongs in the SDK vs your app
|
|
473
|
-
|
|
474
|
-
The intended split is:
|
|
475
|
-
|
|
476
|
-
- SDK: `useAssistantController`, message/tool normalization, markdown rendering, plan parsing, tool rollups, and reusable assistant UI primitives
|
|
477
|
-
- App: modal shell, fullscreen/window controls, route navigation, workspace/file viewers, and product-specific renderers
|
|
478
|
-
|
|
479
|
-
### Assistant names (resource key)
|
|
480
|
-
|
|
481
|
-
Assistant CRUD is name-based:
|
|
115
|
+
### Files (Datastore)
|
|
482
116
|
|
|
483
117
|
```ts
|
|
484
|
-
await client.
|
|
485
|
-
await client.
|
|
486
|
-
|
|
118
|
+
await client.files.folder.create("reports", { directoryPath: "/" });
|
|
119
|
+
await client.files.upload(fileBlob, { directoryPath: "/reports", name: "q1.pdf" });
|
|
120
|
+
|
|
121
|
+
const listing = await client.files.list({ directoryPath: "/reports" });
|
|
122
|
+
const downloaded = await client.files.download("/reports/q1.pdf");
|
|
487
123
|
```
|
|
488
124
|
|
|
489
|
-
###
|
|
125
|
+
### Assistants + Conversations
|
|
490
126
|
|
|
491
127
|
```ts
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
128
|
+
await client.assistants.create({
|
|
129
|
+
name: "support_assistant",
|
|
130
|
+
instruction: "Help with support triage.",
|
|
495
131
|
});
|
|
496
132
|
|
|
497
133
|
const conversation = await client.conversations.createForAssistant("support_assistant", {
|
|
498
|
-
title: "Ticket
|
|
134
|
+
title: "Ticket review",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await client.conversations.messages.send(conversation.id, {
|
|
138
|
+
content: "Summarize unresolved issues from today.",
|
|
499
139
|
});
|
|
500
140
|
```
|
|
501
141
|
|
|
502
|
-
|
|
142
|
+
## Streaming (SSE)
|
|
143
|
+
|
|
144
|
+
Use `readSSE` + `parseSSEJson` for incremental events.
|
|
503
145
|
|
|
504
146
|
```ts
|
|
147
|
+
import { readSSE, parseSSEJson } from "lemma-sdk";
|
|
148
|
+
|
|
505
149
|
const stream = await client.conversations.sendMessageStream(conversationId, {
|
|
506
|
-
content: "
|
|
150
|
+
content: "Analyze recent incidents",
|
|
507
151
|
});
|
|
508
152
|
|
|
509
153
|
for await (const event of readSSE(stream)) {
|
|
510
154
|
const payload = parseSSEJson(event);
|
|
511
155
|
if (!payload) continue;
|
|
512
|
-
console.log(payload);
|
|
156
|
+
console.log(event.event, payload);
|
|
513
157
|
}
|
|
514
158
|
```
|
|
515
159
|
|
|
516
|
-
|
|
160
|
+
Task stream example:
|
|
517
161
|
|
|
518
162
|
```ts
|
|
519
163
|
const task = await client.tasks.create({
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
runtimeAccountIds: ["acc_123"],
|
|
164
|
+
agent_name: "triage_agent",
|
|
165
|
+
input_data: { ticketId: "TCK-1042" },
|
|
523
166
|
});
|
|
524
167
|
|
|
525
|
-
const
|
|
526
|
-
for await (const event of readSSE(stream)) {
|
|
527
|
-
const payload = parseSSEJson(event);
|
|
528
|
-
if (!payload) continue;
|
|
529
|
-
console.log(payload);
|
|
530
|
-
}
|
|
168
|
+
const taskStream = await client.tasks.stream(task.id);
|
|
531
169
|
```
|
|
532
170
|
|
|
533
|
-
##
|
|
534
|
-
|
|
535
|
-
Import from `lemma-sdk/react`:
|
|
536
|
-
|
|
537
|
-
- `useAuth(client)`
|
|
538
|
-
- `AuthGuard`
|
|
539
|
-
- `useAgentRunStream(...)`
|
|
540
|
-
- `useAssistantRun(...)`
|
|
541
|
-
- `useAssistantSession(...)`
|
|
542
|
-
- `useTaskSession(...)`
|
|
543
|
-
- `useFunctionSession(...)`
|
|
544
|
-
- `useFlowSession(...)`
|
|
171
|
+
## Access Grants (`accessible_tables`)
|
|
545
172
|
|
|
546
|
-
|
|
173
|
+
When creating agents/functions/assistants, `accessible_tables` must use object entries:
|
|
547
174
|
|
|
548
|
-
|
|
549
|
-
-
|
|
550
|
-
- `isTerminalFunctionStatus(...)`
|
|
551
|
-
- `isTerminalFlowStatus(...)`
|
|
552
|
-
- `parseTaskStreamEvent(...)`
|
|
553
|
-
- `upsertTaskMessage(...)`
|
|
554
|
-
- `parseAssistantStreamEvent(...)`
|
|
555
|
-
- `upsertConversationMessage(...)`
|
|
556
|
-
|
|
557
|
-
Example:
|
|
175
|
+
```ts
|
|
176
|
+
import { TableAccessMode } from "lemma-sdk";
|
|
558
177
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
client,
|
|
564
|
-
podId,
|
|
565
|
-
conversationId,
|
|
566
|
-
onEvent: (event, payload) => {
|
|
567
|
-
console.log(event.event, payload);
|
|
568
|
-
},
|
|
569
|
-
});
|
|
178
|
+
accessible_tables: [
|
|
179
|
+
{ table_name: "expenses", mode: TableAccessMode.READ },
|
|
180
|
+
{ table_name: "expense_notes", mode: TableAccessMode.WRITE },
|
|
181
|
+
];
|
|
570
182
|
```
|
|
571
183
|
|
|
572
|
-
|
|
184
|
+
`["expenses"]` is not valid.
|
|
185
|
+
|
|
186
|
+
## Auth
|
|
573
187
|
|
|
574
|
-
|
|
188
|
+
Default mode is cookie/session auth (`credentials: "include"`).
|
|
575
189
|
|
|
576
|
-
|
|
190
|
+
For local browser testing, token injection is supported via local storage key `lemma_token`:
|
|
577
191
|
|
|
578
192
|
```ts
|
|
579
|
-
|
|
580
|
-
|
|
193
|
+
import { setTestingToken, clearTestingToken } from "lemma-sdk";
|
|
194
|
+
|
|
195
|
+
setTestingToken("<access-token>");
|
|
196
|
+
clearTestingToken();
|
|
581
197
|
```
|
|
582
198
|
|
|
583
|
-
|
|
199
|
+
Auth helpers:
|
|
584
200
|
|
|
585
|
-
|
|
201
|
+
- `buildAuthUrl(...)`
|
|
202
|
+
- `buildFederatedLogoutUrl(...)`
|
|
203
|
+
- `resolveSafeRedirectUri(...)`
|
|
204
|
+
- `client.auth.redirectToAuth(...)`
|
|
205
|
+
- `client.auth.redirectToFederatedLogout(...)`
|
|
586
206
|
|
|
587
|
-
|
|
588
|
-
2. Move pod-scoped calls into namespaces (`tasks`, `assistants`, `conversations`, etc.).
|
|
589
|
-
3. Keep rare/unmodeled endpoints on `client.request(...)` temporarily.
|
|
590
|
-
4. Replace SSE parsing code with `readSSE` + `parseSSEJson`.
|
|
591
|
-
5. Gradually lift app-specific run/chat logic into reusable hooks in `lemma-sdk/react`.
|
|
207
|
+
## React Package (`lemma-sdk/react`)
|
|
592
208
|
|
|
593
|
-
|
|
209
|
+
Includes auth helpers, run hooks, and assistant UI primitives.
|
|
594
210
|
|
|
595
|
-
|
|
211
|
+
Install React peer dependency in your app if not already installed:
|
|
596
212
|
|
|
597
213
|
```bash
|
|
598
|
-
|
|
214
|
+
npm i react react-dom
|
|
599
215
|
```
|
|
600
216
|
|
|
601
|
-
|
|
217
|
+
Import stylesheet once:
|
|
602
218
|
|
|
603
|
-
```
|
|
604
|
-
|
|
219
|
+
```tsx
|
|
220
|
+
import "lemma-sdk/react/styles.css";
|
|
605
221
|
```
|
|
606
222
|
|
|
607
|
-
|
|
223
|
+
Fastest assistant integration:
|
|
608
224
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
225
|
+
```tsx
|
|
226
|
+
import { AssistantEmbedded } from "lemma-sdk/react";
|
|
227
|
+
|
|
228
|
+
<div style={{ height: 720, minHeight: 0 }}>
|
|
229
|
+
<AssistantEmbedded
|
|
230
|
+
client={client}
|
|
231
|
+
podId="<pod-id>"
|
|
232
|
+
assistantName="support_assistant"
|
|
233
|
+
title="Support Assistant"
|
|
234
|
+
placeholder="Message Support Assistant"
|
|
235
|
+
showConversationList
|
|
236
|
+
/>
|
|
237
|
+
</div>;
|
|
238
|
+
```
|
|
612
239
|
|
|
613
|
-
|
|
240
|
+
Auth guard example:
|
|
614
241
|
|
|
615
|
-
```
|
|
616
|
-
|
|
242
|
+
```tsx
|
|
243
|
+
import { AuthGuard } from "lemma-sdk/react";
|
|
244
|
+
|
|
245
|
+
<AuthGuard client={client}>
|
|
246
|
+
<App />
|
|
247
|
+
</AuthGuard>;
|
|
617
248
|
```
|
|
618
249
|
|
|
619
|
-
|
|
250
|
+
## Browser Bundle
|
|
620
251
|
|
|
621
|
-
|
|
622
|
-
|
|
252
|
+
The package also ships a standalone browser bundle:
|
|
253
|
+
|
|
254
|
+
- npm artifact path: `dist/browser/lemma-client.js`
|
|
255
|
+
- export path: `lemma-sdk/browser-bundle`
|
|
256
|
+
- global: `window.LemmaClient.LemmaClient`
|
|
257
|
+
|
|
258
|
+
Example:
|
|
259
|
+
|
|
260
|
+
```html
|
|
261
|
+
<script src="https://unpkg.com/lemma-sdk@latest/dist/browser/lemma-client.js"></script>
|
|
262
|
+
<script>
|
|
263
|
+
const client = new window.LemmaClient.LemmaClient({
|
|
264
|
+
apiUrl: "https://api.lemma.work",
|
|
265
|
+
authUrl: "https://auth.lemma.work/auth",
|
|
266
|
+
podId: "<pod-id>"
|
|
267
|
+
});
|
|
268
|
+
</script>
|
|
623
269
|
```
|
|
624
270
|
|
|
625
|
-
As of March 26, 2026, npm package name `lemma` is already taken. This package publishes as `lemma-sdk`.
|