@fias/create-fias-plugin 1.13.0 → 1.14.0
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/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- fias-sdk-guide-version: 2.
|
|
1
|
+
<!-- fias-sdk-guide-version: 2.22.0 -->
|
|
2
2
|
|
|
3
3
|
# FIAS Plugin Development Guide
|
|
4
4
|
|
|
@@ -465,6 +465,85 @@ The text capabilities:
|
|
|
465
465
|
|
|
466
466
|
Browse specific models with `npx fias-dev entities`.
|
|
467
467
|
|
|
468
|
+
#### Multi-turn conversations — `history`
|
|
469
|
+
|
|
470
|
+
`input` is the NEW user message. Send the earlier turns, oldest first, in `history`:
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
import { fitHistoryToLimits } from '@fias/arche-sdk';
|
|
474
|
+
|
|
475
|
+
const request = {
|
|
476
|
+
entityId: model.entityId,
|
|
477
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
478
|
+
input: newMessage,
|
|
479
|
+
};
|
|
480
|
+
await invoke({
|
|
481
|
+
...request,
|
|
482
|
+
// [{ role: 'user' | 'assistant', content }] — pass `request` so the history
|
|
483
|
+
// gets only the bytes the rest of the call leaves.
|
|
484
|
+
history: fitHistoryToLimits(messages, { maxTurns: 20, request }),
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Every turn is billed as input tokens on EVERY call, so send a window, not the whole transcript. Limits: 40 turns, 100,000 characters per turn, 150,000 in total (`ENTITY_INVOCATION_HISTORY_LIMITS`); a call over them, or a turn with a role other than `user` / `assistant`, is rejected with `INVALID_PARAMS`. The whole call must ALSO fit the bridge: 256 KB serialized for a text call, 1.5 MB with images (`ENTITY_INVOCATION_REQUEST_LIMITS`). A character can take up to 4 bytes (CJK 3, emoji 4, a newline or quote 2), so a history within the character limits can still be too large. `fitHistoryToLimits(turns, { request })` keeps the newest turns that fit both; `invoke()` refuses an oversized call with `REQUEST_TOO_LARGE` before sending it.
|
|
489
|
+
|
|
490
|
+
#### Reasoning, thinking, and several streams at once
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
const controller = new AbortController();
|
|
494
|
+
const result = await invoke(
|
|
495
|
+
{
|
|
496
|
+
entityId: model.entityId,
|
|
497
|
+
input,
|
|
498
|
+
history,
|
|
499
|
+
// Only for models with capabilities.reasoning; ignored otherwise.
|
|
500
|
+
parameters: { reasoning: { effort: 'medium', includeThinking: true } }, // effort: 'off' | 'low' | 'medium' | 'high'
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
onToken: (text) => appendAnswer(text), // this call's answer chunks
|
|
504
|
+
onThinking: (text) => appendThinking(text), // this call's reasoning text
|
|
505
|
+
signal: controller.signal, // controller.abort() → rejects with code 'ABORTED'
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
const cost = result.metadata?.usage?.costCredits; // what this reply cost the user, for a cost label
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
- **Per-call callbacks** (`onToken` / `onThinking`) are scoped to that one call, so you can run several streams side by side — e.g. the same prompt against three models. A call that passes `onToken` leaves the hook's shared `streamingText` / `result` / `error` alone — its failure arrives only as the rejected promise.
|
|
512
|
+
- **Reasoning** uses more output tokens (billed) but always fits inside the model's output ceiling, so it never raises the credit hold. Set `includeThinking` only if you show the thinking — on some providers it raises the cost of a `'low'` request.
|
|
513
|
+
- **Aborting stops your UI, not the charge.** The platform finishes and bills the generation.
|
|
514
|
+
- A stream fails with `TIMEOUT` only after 2 minutes with no sign of life (the host sends keepalives during silent reasoning) or 15 minutes in total.
|
|
515
|
+
- `result.metadata.usage` has `inputTokens`, `outputTokens`, and `costCredits` when the platform reported them. Display only — the charge is already settled.
|
|
516
|
+
- A failed call rejects with a `FiasBridgeError`: branch on `err.code` (e.g. `INSUFFICIENT_CREDITS`, `PROVIDER_OVERLOADED`), and show `err.requestId` as a support reference when present.
|
|
517
|
+
|
|
518
|
+
### `useTextModels()` — Let the user pick a text model
|
|
519
|
+
|
|
520
|
+
**Permission:** `entities:invoke` · **Returns:** `FiasTextModel[]`
|
|
521
|
+
|
|
522
|
+
For an app whose USER chooses the model (a chat app, a model comparison), read the platform's live catalog instead of hardcoding ids. Each entry's `entityId` goes straight into `invoke`. The list is `[]` until the host delivers it (usually immediately), and retired models drop out, so fall back to a `recommended` entry when a saved choice disappears.
|
|
523
|
+
|
|
524
|
+
```tsx
|
|
525
|
+
import { useTextModels } from '@fias/arche-sdk';
|
|
526
|
+
|
|
527
|
+
function ModelPicker({ value, onChange }: { value: string; onChange: (id: string) => void }) {
|
|
528
|
+
const models = useTextModels();
|
|
529
|
+
const selected = models.find((m) => m.entityId === value) ?? models.find((m) => m.recommended);
|
|
530
|
+
return (
|
|
531
|
+
<select value={selected?.entityId ?? ''} onChange={(e) => onChange(e.target.value)}>
|
|
532
|
+
{models.map((m) => (
|
|
533
|
+
<option key={m.entityId} value={m.entityId}>
|
|
534
|
+
{m.providerDisplayName} · {m.displayName}
|
|
535
|
+
{m.pricing
|
|
536
|
+
? ` — ${m.pricing.inputCreditsPerMillionTokens}/${m.pricing.outputCreditsPerMillionTokens} cr per 1M tok`
|
|
537
|
+
: ''}
|
|
538
|
+
</option>
|
|
539
|
+
))}
|
|
540
|
+
</select>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
`capabilities.vision` says the model accepts `images`; `capabilities.reasoning` says it honours `parameters.reasoning`. `pricing` is what the user pays per 1M tokens in credits (1 credit = 1¢), markup included — for display only.
|
|
546
|
+
|
|
468
547
|
### `useImageGeneration()` — Generate images via AI models
|
|
469
548
|
|
|
470
549
|
**Permission:** `entities:image_generate`
|
|
@@ -884,6 +963,17 @@ const { bytes } = await userDocs.getBytes(id); // raw bytes for PROCESSING binar
|
|
|
884
963
|
- **Workspaces.** The picker offers the files of the workspace the user is acting in — the workspace switcher in Fias: their Personal vault, or an org workspace they belong to — and `list()` returns the grants made in that workspace, so switching workspace changes what `list()` shows. A document you already hold a grant on stays readable by id (`get`, `getBytes`, `getDownloadUrl`, `saveContent`) whichever workspace the user is in: a grant belongs to the workspace the document lives in. A plugin never names a workspace itself. A member's role caps what they can share and read: a viewer can only share standard-sensitivity documents, and a document above a member's tier reads as `DOCUMENT_NOT_FOUND` even if another member granted it.
|
|
885
964
|
- **Testable in the dev harness (mock mode).** `pick()` grants a small fixture set — a text file, a real PDF, and one deliberately over the 15 MB cap — and `list` / `get` / `getDownloadUrl` / `getBytes` all resolve against it, including the refusals (`BINARY_DOCUMENT` on a text read of a PDF, `CONSENTED_DOCUMENT_TOO_LARGE`, `DOCUMENT_NOT_FOUND`). Run the harness with `FIAS_HARNESS_VAULT_PICK=canceled` to exercise the cancel branch. Not available in the builder preview — `pick()` resolves `{ canceled: true }` there.
|
|
886
965
|
|
|
966
|
+
**Workspace-approved reads** — `userDocs.workspace.*`, permission `vault:workspace-documents:read`, declared TOGETHER WITH `vault:user-documents:read` (escalates to human review). When the user is acting in an **org workspace** they belong to, whose admin (or its Company) has **approved your arche**, you can read that workspace's documents without a per-document pick — every document the acting member can see in Documents and Arche Saves, at their role, never proprietary:
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
const docs = await userDocs.workspace.list({ search: 'invoice' }); // the acting workspace's documents
|
|
970
|
+
const { document, content } = await userDocs.workspace.get(id, { includeContent: true });
|
|
971
|
+
const { url } = await userDocs.workspace.getDownloadUrl(id);
|
|
972
|
+
const { bytes } = await userDocs.workspace.getBytes(id);
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
Two refusals to handle: `WORKSPACE_REQUIRED` (the user is in their Personal vault, or not a member of the workspace — offer `pick()` instead) and `ARCHE_NOT_APPROVED` (this workspace has not approved your arche, or approved it before it asked for this access — say so; a workspace or Company admin approves apps under Workspace Controls). **An approval covers exactly what the admin was shown:** if you add this permission, or start reading a new kind of workspace data, after being approved, those reads are refused with `ARCHE_NOT_APPROVED` until an admin approves again — plan releases that widen access with that in mind. The workspace is fixed when your arche opens (the user reopens it to switch), approval is revocable at once, and every read is logged where the admin can see it. Not available in the builder preview. In the dev harness, `mockWorkspace` in `fias-dev.config.json` (`"approved"` default, `"unapproved"`, `"personal"`) lets you exercise both refusals.
|
|
976
|
+
|
|
887
977
|
**Editing the user's files** — permission `vault:user-documents:edit`, declared TOGETHER WITH `vault:user-documents:read` (escalates to human review). `:edit` does not include `:read`: the grants you save under are created and listed through the read surface (`pick`, `list`, `get`), so a manifest with `:edit` alone can ask for nothing and open nothing.
|
|
888
978
|
|
|
889
979
|
```tsx
|
|
@@ -1564,9 +1654,9 @@ The SDK also exports the following for advanced use cases. Most plugins don't ne
|
|
|
1564
1654
|
|
|
1565
1655
|
Other optional sub-fields: `currency: "usd"` (only USD supported today), `platformFeePercent` (override the default platform cut — non-standard).
|
|
1566
1656
|
|
|
1567
|
-
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `data:store`, `data:search`, `entities:invoke`, `entities:client_invoke`, `entities:image_generate`, `entities:image_edit`, `entities:audio_generate`, `entities:web_search`, `store:purchase`, `vault:documents:read`, `vault:documents:write`, `assets:read`, `navigation:open_arche`, `sandbox:vendored-libraries`
|
|
1657
|
+
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `data:store`, `data:search`, `data:workspace`, `entities:invoke`, `entities:client_invoke`, `entities:image_generate`, `entities:image_edit`, `entities:audio_generate`, `entities:web_search`, `store:purchase`, `vault:documents:read`, `vault:documents:write`, `vault:user-documents:read`, `vault:user-documents:write`, `vault:user-documents:edit`, `vault:workspace-documents:read`, `assets:read`, `assets:community:read`, `assets:community:publish`, `navigation:open_arche`, `sandbox:vendored-libraries`, `ai:actions`
|
|
1568
1658
|
|
|
1569
|
-
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
1659
|
+
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID (or a `{ capability }` selector, or an entry from `useTextModels()`) and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
1570
1660
|
|
|
1571
1661
|
**Defining IAP products (for `useFiasStore`):**
|
|
1572
1662
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- fias-sdk-guide-version: 2.
|
|
1
|
+
<!-- fias-sdk-guide-version: 2.22.0 -->
|
|
2
2
|
|
|
3
3
|
# FIAS Plugin Development Guide
|
|
4
4
|
|
|
@@ -465,6 +465,85 @@ The text capabilities:
|
|
|
465
465
|
|
|
466
466
|
Browse specific models with `npx fias-dev entities`.
|
|
467
467
|
|
|
468
|
+
#### Multi-turn conversations — `history`
|
|
469
|
+
|
|
470
|
+
`input` is the NEW user message. Send the earlier turns, oldest first, in `history`:
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
import { fitHistoryToLimits } from '@fias/arche-sdk';
|
|
474
|
+
|
|
475
|
+
const request = {
|
|
476
|
+
entityId: model.entityId,
|
|
477
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
478
|
+
input: newMessage,
|
|
479
|
+
};
|
|
480
|
+
await invoke({
|
|
481
|
+
...request,
|
|
482
|
+
// [{ role: 'user' | 'assistant', content }] — pass `request` so the history
|
|
483
|
+
// gets only the bytes the rest of the call leaves.
|
|
484
|
+
history: fitHistoryToLimits(messages, { maxTurns: 20, request }),
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Every turn is billed as input tokens on EVERY call, so send a window, not the whole transcript. Limits: 40 turns, 100,000 characters per turn, 150,000 in total (`ENTITY_INVOCATION_HISTORY_LIMITS`); a call over them, or a turn with a role other than `user` / `assistant`, is rejected with `INVALID_PARAMS`. The whole call must ALSO fit the bridge: 256 KB serialized for a text call, 1.5 MB with images (`ENTITY_INVOCATION_REQUEST_LIMITS`). A character can take up to 4 bytes (CJK 3, emoji 4, a newline or quote 2), so a history within the character limits can still be too large. `fitHistoryToLimits(turns, { request })` keeps the newest turns that fit both; `invoke()` refuses an oversized call with `REQUEST_TOO_LARGE` before sending it.
|
|
489
|
+
|
|
490
|
+
#### Reasoning, thinking, and several streams at once
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
const controller = new AbortController();
|
|
494
|
+
const result = await invoke(
|
|
495
|
+
{
|
|
496
|
+
entityId: model.entityId,
|
|
497
|
+
input,
|
|
498
|
+
history,
|
|
499
|
+
// Only for models with capabilities.reasoning; ignored otherwise.
|
|
500
|
+
parameters: { reasoning: { effort: 'medium', includeThinking: true } }, // effort: 'off' | 'low' | 'medium' | 'high'
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
onToken: (text) => appendAnswer(text), // this call's answer chunks
|
|
504
|
+
onThinking: (text) => appendThinking(text), // this call's reasoning text
|
|
505
|
+
signal: controller.signal, // controller.abort() → rejects with code 'ABORTED'
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
const cost = result.metadata?.usage?.costCredits; // what this reply cost the user, for a cost label
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
- **Per-call callbacks** (`onToken` / `onThinking`) are scoped to that one call, so you can run several streams side by side — e.g. the same prompt against three models. A call that passes `onToken` leaves the hook's shared `streamingText` / `result` / `error` alone — its failure arrives only as the rejected promise.
|
|
512
|
+
- **Reasoning** uses more output tokens (billed) but always fits inside the model's output ceiling, so it never raises the credit hold. Set `includeThinking` only if you show the thinking — on some providers it raises the cost of a `'low'` request.
|
|
513
|
+
- **Aborting stops your UI, not the charge.** The platform finishes and bills the generation.
|
|
514
|
+
- A stream fails with `TIMEOUT` only after 2 minutes with no sign of life (the host sends keepalives during silent reasoning) or 15 minutes in total.
|
|
515
|
+
- `result.metadata.usage` has `inputTokens`, `outputTokens`, and `costCredits` when the platform reported them. Display only — the charge is already settled.
|
|
516
|
+
- A failed call rejects with a `FiasBridgeError`: branch on `err.code` (e.g. `INSUFFICIENT_CREDITS`, `PROVIDER_OVERLOADED`), and show `err.requestId` as a support reference when present.
|
|
517
|
+
|
|
518
|
+
### `useTextModels()` — Let the user pick a text model
|
|
519
|
+
|
|
520
|
+
**Permission:** `entities:invoke` · **Returns:** `FiasTextModel[]`
|
|
521
|
+
|
|
522
|
+
For an app whose USER chooses the model (a chat app, a model comparison), read the platform's live catalog instead of hardcoding ids. Each entry's `entityId` goes straight into `invoke`. The list is `[]` until the host delivers it (usually immediately), and retired models drop out, so fall back to a `recommended` entry when a saved choice disappears.
|
|
523
|
+
|
|
524
|
+
```tsx
|
|
525
|
+
import { useTextModels } from '@fias/arche-sdk';
|
|
526
|
+
|
|
527
|
+
function ModelPicker({ value, onChange }: { value: string; onChange: (id: string) => void }) {
|
|
528
|
+
const models = useTextModels();
|
|
529
|
+
const selected = models.find((m) => m.entityId === value) ?? models.find((m) => m.recommended);
|
|
530
|
+
return (
|
|
531
|
+
<select value={selected?.entityId ?? ''} onChange={(e) => onChange(e.target.value)}>
|
|
532
|
+
{models.map((m) => (
|
|
533
|
+
<option key={m.entityId} value={m.entityId}>
|
|
534
|
+
{m.providerDisplayName} · {m.displayName}
|
|
535
|
+
{m.pricing
|
|
536
|
+
? ` — ${m.pricing.inputCreditsPerMillionTokens}/${m.pricing.outputCreditsPerMillionTokens} cr per 1M tok`
|
|
537
|
+
: ''}
|
|
538
|
+
</option>
|
|
539
|
+
))}
|
|
540
|
+
</select>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
`capabilities.vision` says the model accepts `images`; `capabilities.reasoning` says it honours `parameters.reasoning`. `pricing` is what the user pays per 1M tokens in credits (1 credit = 1¢), markup included — for display only.
|
|
546
|
+
|
|
468
547
|
### `useImageGeneration()` — Generate images via AI models
|
|
469
548
|
|
|
470
549
|
**Permission:** `entities:image_generate`
|
|
@@ -884,6 +963,17 @@ const { bytes } = await userDocs.getBytes(id); // raw bytes for PROCESSING binar
|
|
|
884
963
|
- **Workspaces.** The picker offers the files of the workspace the user is acting in — the workspace switcher in Fias: their Personal vault, or an org workspace they belong to — and `list()` returns the grants made in that workspace, so switching workspace changes what `list()` shows. A document you already hold a grant on stays readable by id (`get`, `getBytes`, `getDownloadUrl`, `saveContent`) whichever workspace the user is in: a grant belongs to the workspace the document lives in. A plugin never names a workspace itself. A member's role caps what they can share and read: a viewer can only share standard-sensitivity documents, and a document above a member's tier reads as `DOCUMENT_NOT_FOUND` even if another member granted it.
|
|
885
964
|
- **Testable in the dev harness (mock mode).** `pick()` grants a small fixture set — a text file, a real PDF, and one deliberately over the 15 MB cap — and `list` / `get` / `getDownloadUrl` / `getBytes` all resolve against it, including the refusals (`BINARY_DOCUMENT` on a text read of a PDF, `CONSENTED_DOCUMENT_TOO_LARGE`, `DOCUMENT_NOT_FOUND`). Run the harness with `FIAS_HARNESS_VAULT_PICK=canceled` to exercise the cancel branch. Not available in the builder preview — `pick()` resolves `{ canceled: true }` there.
|
|
886
965
|
|
|
966
|
+
**Workspace-approved reads** — `userDocs.workspace.*`, permission `vault:workspace-documents:read`, declared TOGETHER WITH `vault:user-documents:read` (escalates to human review). When the user is acting in an **org workspace** they belong to, whose admin (or its Company) has **approved your arche**, you can read that workspace's documents without a per-document pick — every document the acting member can see in Documents and Arche Saves, at their role, never proprietary:
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
const docs = await userDocs.workspace.list({ search: 'invoice' }); // the acting workspace's documents
|
|
970
|
+
const { document, content } = await userDocs.workspace.get(id, { includeContent: true });
|
|
971
|
+
const { url } = await userDocs.workspace.getDownloadUrl(id);
|
|
972
|
+
const { bytes } = await userDocs.workspace.getBytes(id);
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
Two refusals to handle: `WORKSPACE_REQUIRED` (the user is in their Personal vault, or not a member of the workspace — offer `pick()` instead) and `ARCHE_NOT_APPROVED` (this workspace has not approved your arche, or approved it before it asked for this access — say so; a workspace or Company admin approves apps under Workspace Controls). **An approval covers exactly what the admin was shown:** if you add this permission, or start reading a new kind of workspace data, after being approved, those reads are refused with `ARCHE_NOT_APPROVED` until an admin approves again — plan releases that widen access with that in mind. The workspace is fixed when your arche opens (the user reopens it to switch), approval is revocable at once, and every read is logged where the admin can see it. Not available in the builder preview. In the dev harness, `mockWorkspace` in `fias-dev.config.json` (`"approved"` default, `"unapproved"`, `"personal"`) lets you exercise both refusals.
|
|
976
|
+
|
|
887
977
|
**Editing the user's files** — permission `vault:user-documents:edit`, declared TOGETHER WITH `vault:user-documents:read` (escalates to human review). `:edit` does not include `:read`: the grants you save under are created and listed through the read surface (`pick`, `list`, `get`), so a manifest with `:edit` alone can ask for nothing and open nothing.
|
|
888
978
|
|
|
889
979
|
```tsx
|
|
@@ -1564,9 +1654,9 @@ The SDK also exports the following for advanced use cases. Most plugins don't ne
|
|
|
1564
1654
|
|
|
1565
1655
|
Other optional sub-fields: `currency: "usd"` (only USD supported today), `platformFeePercent` (override the default platform cut — non-standard).
|
|
1566
1656
|
|
|
1567
|
-
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `data:store`, `data:search`, `entities:invoke`, `entities:client_invoke`, `entities:image_generate`, `entities:image_edit`, `entities:audio_generate`, `entities:web_search`, `store:purchase`, `vault:documents:read`, `vault:documents:write`, `assets:read`, `navigation:open_arche`, `sandbox:vendored-libraries`
|
|
1657
|
+
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `data:store`, `data:search`, `data:workspace`, `entities:invoke`, `entities:client_invoke`, `entities:image_generate`, `entities:image_edit`, `entities:audio_generate`, `entities:web_search`, `store:purchase`, `vault:documents:read`, `vault:documents:write`, `vault:user-documents:read`, `vault:user-documents:write`, `vault:user-documents:edit`, `vault:workspace-documents:read`, `assets:read`, `assets:community:read`, `assets:community:publish`, `navigation:open_arche`, `sandbox:vendored-libraries`, `ai:actions`
|
|
1568
1658
|
|
|
1569
|
-
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
1659
|
+
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID (or a `{ capability }` selector, or an entry from `useTextModels()`) and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
1570
1660
|
|
|
1571
1661
|
**Defining IAP products (for `useFiasStore`):**
|
|
1572
1662
|
|