@softr/sdk 0.0.79-alpha.0 → 0.0.79-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/dist/mcp.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatasourceAction } from "./action.js";
2
- import * as _tanstack_react_start5 from "@tanstack/react-start";
2
+ import * as _tanstack_react_start0 from "@tanstack/react-start";
3
3
 
4
4
  //#region src/mcp.d.ts
5
5
 
@@ -31,6 +31,6 @@ type SoftrMcpOptions = {
31
31
  * ],
32
32
  * }))
33
33
  */
34
- declare function softrMcpMiddleware(options?: SoftrMcpOptions): _tanstack_react_start5.RequestMiddlewareAfterServer<{}, undefined, undefined>;
34
+ declare function softrMcpMiddleware(options?: SoftrMcpOptions): _tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>;
35
35
  //#endregion
36
36
  export { softrMcpMiddleware };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.0.79-alpha.0";
2
+ var version = "0.0.79-alpha.1";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softr/sdk",
3
- "version": "0.0.79-alpha.0",
3
+ "version": "0.0.79-alpha.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
@@ -84,7 +84,7 @@ Asking an AI model for anything: a summary, fields out of a document, a classifi
84
84
  Calling a connected third-party API (Slack, Stripe, Gmail, Google Calendar, …)?
85
85
  load softr-sdk/integrations
86
86
 
87
- Running a model on the user's OWN provider account, because they asked for that?
87
+ Running a model on the user's OWN provider account, because they chose that for the feature?
88
88
  load softr-sdk/integrations: its "Calling an AI model" section
89
89
 
90
90
  Opening the app to an AI assistant over MCP?
@@ -8,7 +8,8 @@ description: >-
8
8
  optional JSON schema for an answer that is data, and optional files;
9
9
  ai.stream runs it for an answer the user watches being written. Load this
10
10
  for a summary, an extraction, a classification, a draft, or a chat the app
11
- asks a model for.
11
+ asks a model for, and before you decide whether a feature runs on Softr AI
12
+ or on the user's own account.
12
13
  metadata:
13
14
  type: sub-skill
14
15
  library: softr-sdk
@@ -19,23 +20,34 @@ metadata:
19
20
 
20
21
  # AI
21
22
 
22
- An app asks a model through an AI action. Softr runs the call on its own provider keys and bills the workspace's AI credits, so there is no key to ask the user for and nothing to connect. This is the path for every AI feature of the app's own — a summary of a record, fields extracted from a document, a classification, a draft, a chat — unless the user explicitly asked to use their own provider account, which is the `integrations` sub-skill's path.
23
+ An app asks a model through an AI action. Softr runs the call on its own provider keys and bills the workspace's AI credits, so there is no key to ask the user for and nothing to connect. This is the default for every AI feature of the app's own — a summary of a record, fields extracted from a document, a classification, a draft, a chat. The other path is the user's own provider account, through a connected integration: the `integrations` sub-skill. An app can hold both: each feature carries its own choice in its code.
23
24
 
24
- ## Choose the model first
25
+ ## Decide who runs it first
26
+
27
+ Never choose silently between Softr AI and the user's own account. Decide per feature, before you write it, in this order:
28
+
29
+ 1. The user named Softr AI or their own account in the prompt: follow it.
30
+ 2. The app already has AI features: follow the choice they made, and say so in one line.
31
+ 3. `integration_list` shows no `OPEN_AI`, `ANTHROPIC_AI` or `GEMINI_AI` integration: use Softr AI, and say so in one line. Call the tool; do not assume the answer.
32
+ 4. Otherwise ask with one `requestinput` of type `aiProvider`, naming the feature. The answer is Softr AI, or the integration with its id and type; a declined request means Softr AI.
33
+
34
+ An own-account answer leaves this skill: the call goes through `defineIntegration`, as the `integrations` sub-skill describes.
35
+
36
+ ## Choose the model
25
37
 
26
38
  The model is chosen when the app is built and frozen in its code. Before you declare an action, read Softr's catalog with the `ai_list_models` tool. Each entry carries the `id` the action names, the `provider`, and `bestFor`, what to pick it for:
27
39
 
28
- - a summary, a briefing, a draft, reasoning over many facts: the strongest model listed;
40
+ - a chat, an assistant, a summary, a briefing, a draft, reasoning over many facts: the strongest model listed;
29
41
  - an extraction or a classification over one document: the cheapest model that reads that document's kind — a PDF needs a model whose `bestFor` says so.
30
42
 
31
- Copy the `id` exactly. An id outside the catalog is refused when the action is saved, and a model spelled from memory never is in it.
43
+ Copy the `id` exactly from the tool's answer. An id outside the catalog is refused when the action is saved, and a model spelled from memory, or taken from an example, never is in it.
32
44
 
33
45
  ## Declare the action
34
46
 
35
47
  ```ts
36
48
  const summarizeDeal = defineAiAction({
37
49
  id: 'summarizeDeal',
38
- model: 'anthropic-claude-sonnet-4-6',
50
+ model: '<an id from ai_list_models>',
39
51
  permissions: { userGroupTypes: ['LOGGED_IN_USERS'] },
40
52
  })
41
53
  ```
@@ -105,29 +117,9 @@ const extractInvoice = createServerFn({ method: 'POST' })
105
117
 
106
118
  The file's URL comes from the app's own upload: load the Softr SDK `file-uploads` guidance for that half. `readInvoice` names a model whose `bestFor` reads PDF documents.
107
119
 
108
- ## A conversation
109
-
110
- A chat sends its earlier turns in `messages` and the new one as `prompt`. The app keeps the turns; the model holds nothing between calls.
111
-
112
- ```ts
113
- const reply = createServerFn({ method: 'POST' })
114
- .validator(z.object({ turns: z.array(z.object({ role: z.enum(['user', 'assistant']), content: z.string() })), prompt: z.string() }))
115
- .handler(async ({ data }) => {
116
- const answer = await ai.generate(supportChat, {
117
- system: 'You answer questions about the order the user is looking at.',
118
- messages: data.turns,
119
- prompt: data.prompt,
120
- temperature: 0.5,
121
- })
122
- return answer.text
123
- })
124
- ```
125
-
126
- A chat the user watches being written streams instead, with the same `messages`: see the next section.
127
-
128
120
  ## Streaming: an answer the user watches being written
129
121
 
130
- A chat, an assistant, a long draft shown as it is typed — never buffer these: the user would watch a blank panel until the last token. `ai.stream(action, init)` takes the same `init` and answers with a `Response` whose body is a stream of Server-Sent Events. Hand it to the browser from the server function, unchanged:
122
+ A chat, an assistant, a long draft shown as it is typed — never buffer these: the user would watch a blank panel until the last token. A chat box the user types into is always this case, never `ai.generate`. `ai.stream(action, init)` takes the same `init` and answers with a `Response` whose body is a stream of Server-Sent Events. Hand it to the browser from the server function, unchanged:
131
123
 
132
124
  ```ts
133
125
  const draftReply = createServerFn({ method: 'POST' })
@@ -144,7 +136,7 @@ Each event is one JSON object on a `data:` line:
144
136
  - `{ "done": true, "usage": { "inputTokens", "outputTokens" }, "model": "..." }` — the answer is complete.
145
137
  - `{ "error": "..." }` — in place of `done`, when the call ended before its answer: show the message.
146
138
 
147
- On the client, call the server function's URL with `fetch`, read `response.body` with a stream reader, split on blank lines, and parse the JSON after `data:`.
139
+ On the client, call the server function's URL with `fetch`, read `response.body` with a stream reader, split on blank lines, and parse the JSON after `data:`. A conversation sends its earlier turns in `messages`; the app keeps them, the model holds nothing between calls.
148
140
 
149
141
  ## Failure modes
150
142
 
@@ -156,6 +148,8 @@ On the client, call the server function's URL with `fetch`, read `response.body`
156
148
 
157
149
  **Trusting `json` without checking it.** A model may stray from the schema; `json` is then `null`, or a shape close to the one asked. Validate it with `zod` before writing it to a record, and keep `text` as the fallback the user can read.
158
150
 
151
+ **Buffering a chat.** A chat built on `ai.generate` shows nothing until the whole reply is done, and the user reads "replies appear all at once" in your closing message. A chat streams.
152
+
159
153
  **Reading a streamed body with `res.json()`.** The body of `ai.stream` is an event stream, not one JSON document. Return `res.body` in a `Response` or read it with a stream reader.
160
154
 
161
- **Asking for the user's API key.** An AI feature of the app's own needs none. Only when the user asks to run the app on their own provider account does the call go through a connected integration — load the `integrations` sub-skill then.
155
+ **Asking for the user's API key.** An AI feature of the app's own needs none. Only when the user chose their own provider account for the feature does the call go through a connected integration — load the `integrations` sub-skill then.
@@ -126,7 +126,7 @@ Do not add an `Authorization` key to `headers` — Softr injects the integration
126
126
 
127
127
  ## Calling an AI model
128
128
 
129
- **This is the path for the user's own provider account only.** An AI feature of the app's own — a summary, an extraction, a chat — runs on Softr's keys through an AI action, with nothing to connect and no key to ask for: load the `ai` sub-skill for it. Call a model through an integration only when the user asked to use their own OpenAI, Anthropic or Gemini account, and had it connected.
129
+ **This is the path for the user's own provider account only.** An AI feature of the app's own — a summary, an extraction, a chat — runs on Softr's keys through an AI action, with nothing to connect and no key to ask for: load the `ai` sub-skill for it. Call a model through an integration only when the user chose their own OpenAI, Anthropic or Gemini account for the feature, in the prompt or when asked, and had it connected.
130
130
 
131
131
  An AI integration is called like any other. For `ANTHROPIC_AI`, the action is `POST` on
132
132
  `https://api.anthropic.com/v1/messages`, the request carries the `anthropic-version` header and a body of