@alvera-ai/platform-sdk 0.10.0-rc.2 → 0.10.0-rc.21
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/.agent/AGENTS.md +440 -0
- package/.agent/account_management.md +455 -0
- package/.agent/action_status_updaters.md +262 -0
- package/.agent/ai_agents.md +423 -0
- package/.agent/ai_sandbox.md +265 -0
- package/.agent/async.md +111 -0
- package/.agent/connected_apps.md +407 -0
- package/.agent/cookbook/_fixtures/README.md +99 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_customer.liquid +32 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_mdm.liquid +20 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_generic_table.liquid +33 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +88 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_mdm.liquid +48 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +47 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +24 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +38 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_compliance_screening.liquid +59 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_mdm.liquid +36 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_mdm.liquid +30 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_payment_account.liquid +55 -0
- package/.agent/cookbook/_setup/accounts_receivable.md +282 -0
- package/.agent/cookbook/_setup/foundation.md +277 -0
- package/.agent/cookbook/_setup/healthcare.md +279 -0
- package/.agent/cookbook/_setup/payment_risk.md +283 -0
- package/.agent/cookbook/appointment-review-sms-workflow.md +761 -0
- package/.agent/cookbook/birthday-greeting-sms-trigger.md +656 -0
- package/.agent/cookbook/contact-us-triage-with-llm.md +603 -0
- package/.agent/cookbook/dunning-sms-for-delinquent.md +619 -0
- package/.agent/cookbook/kyc-notification-on-account-activation.md +619 -0
- package/.agent/cookbook/sanctions-screening-with-agent-review.md +711 -0
- package/.agent/cookbook/score-leads-with-llm-categorization.md +602 -0
- package/.agent/cookbook/welcome-sms-for-customers.md +607 -0
- package/.agent/data_activation_clients.md +557 -0
- package/.agent/data_sources.md +234 -0
- package/.agent/datalakes.md +712 -0
- package/.agent/debugging.md +137 -0
- package/.agent/errors.md +196 -0
- package/.agent/generic_tables.md +351 -0
- package/.agent/interoperability_contracts.md +351 -0
- package/.agent/mdm.md +293 -0
- package/.agent/mutations.md +152 -0
- package/.agent/templates.md +98 -0
- package/.agent/tool-call-configs.md +90 -0
- package/.agent/tools.md +546 -0
- package/.agent/type_naming.md +131 -0
- package/.agent/workflows.md +601 -0
- package/README.md +46 -0
- package/dist/bin/platform-sdk.d.mts +1 -0
- package/dist/bin/platform-sdk.mjs +106 -0
- package/dist/bin/platform-sdk.mjs.map +1 -0
- package/dist/index.d.mts +1200 -43201
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1859 -7319
- package/dist/index.mjs.map +1 -1
- package/package.json +19 -10
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Action status updaters
|
|
2
|
+
|
|
3
|
+
An **action status updater** is a scheduled job that polls an
|
|
4
|
+
external system (e.g. a cloud-provider log group, a delivery
|
|
5
|
+
provider's API) for status updates on actions the platform
|
|
6
|
+
previously fired, and writes those updates back onto the
|
|
7
|
+
matching message rows.
|
|
8
|
+
|
|
9
|
+
The typical use case: a workflow's SMS action fires through a
|
|
10
|
+
provider (a sender tool), and a status updater periodically
|
|
11
|
+
polls the provider's log group to mark each message as
|
|
12
|
+
`delivered` / `failed` / `bounced` on its tracked record.
|
|
13
|
+
|
|
14
|
+
SDK namespace: `api.actionStatusUpdaters`.
|
|
15
|
+
|
|
16
|
+
Action status updaters are **Datalake-DB-resident** — the
|
|
17
|
+
parent datalake must be `status: 'ready'` and every
|
|
18
|
+
referenced tool must exist before POST.
|
|
19
|
+
|
|
20
|
+
## 1. Wire shape
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import type {
|
|
24
|
+
ActionStatusUpdaterRequestWritable,
|
|
25
|
+
ActionStatusUpdaterResponse,
|
|
26
|
+
} from '@alvera-ai/platform-sdk'
|
|
27
|
+
|
|
28
|
+
const { data: created } = await api.actionStatusUpdaters.create(
|
|
29
|
+
tenantSlug,
|
|
30
|
+
datalakeSlug,
|
|
31
|
+
{
|
|
32
|
+
name: 'SMS Delivery Updater',
|
|
33
|
+
cron_expression: '*/30 * * * *', // every 30 minutes
|
|
34
|
+
updater_type: 'cloud_watch', // polling source family
|
|
35
|
+
updater_tool_id: cloudWatchToolId, // the tool that issues the poll
|
|
36
|
+
sender_tool_ids: [smsToolId], // tools whose sent messages this updater tracks
|
|
37
|
+
datalake_id: datalakeId,
|
|
38
|
+
|
|
39
|
+
// Polymorphic — discriminator: updater_body_type
|
|
40
|
+
updater_body: {
|
|
41
|
+
updater_body_type: 'cloud_watch_request',
|
|
42
|
+
log_group_name: 'sns/us-east-1/...',
|
|
43
|
+
start_time: '{{ now | minutes_ago: 45 }}', // Liquid window
|
|
44
|
+
end_time: '{{ now }}',
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Liquid template emitting JSON that maps poll events to message updates
|
|
48
|
+
message_config: {
|
|
49
|
+
type: 'custom',
|
|
50
|
+
body:
|
|
51
|
+
'{% for event in events %}' +
|
|
52
|
+
'{"external_id": "{{ event.message_id }}", "set_params": {"status": "{{ event.status }}"}}' +
|
|
53
|
+
'{% endfor %}',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 2. Rules the type cannot encode
|
|
60
|
+
|
|
61
|
+
### `updater_body` is polymorphic on `updater_body_type`
|
|
62
|
+
|
|
63
|
+
The body's discriminator selects the polling shape. Each
|
|
64
|
+
variant declares the fields the platform needs to construct
|
|
65
|
+
the poll request:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
updater_body_type: 'cloud_watch_request'
|
|
69
|
+
log_group_name string — the log group to scan
|
|
70
|
+
start_time string — Liquid template producing a timestamp
|
|
71
|
+
end_time string — Liquid template producing a timestamp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
(See the SDK type for the full set of body types — others
|
|
75
|
+
follow the same `<verb>_request` naming and carry the fields
|
|
76
|
+
their target API requires.)
|
|
77
|
+
|
|
78
|
+
`start_time` / `end_time` accept Liquid expressions using the
|
|
79
|
+
`now` variable plus relative-time filters (`minutes_ago: N`,
|
|
80
|
+
`hours_ago: N`, ...). A literal ISO 8601 string also works for
|
|
81
|
+
fixed windows.
|
|
82
|
+
|
|
83
|
+
### `message_config.body` emits a JSON event stream
|
|
84
|
+
|
|
85
|
+
The template runs against an `events` array — the parsed
|
|
86
|
+
response from the polling tool. Each iteration must render
|
|
87
|
+
one JSON object with two required fields:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
external_id string — the message identifier to update (typically
|
|
91
|
+
the provider's id, matching the recipient
|
|
92
|
+
record's external_id column)
|
|
93
|
+
set_params object — fields to update on the matching message row
|
|
94
|
+
(e.g. { status, delivered_at, error_code })
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The platform parses the rendered output, joins each event to a
|
|
98
|
+
message row by `external_id`, and applies `set_params` as the
|
|
99
|
+
update. Events that don't match any message are silently
|
|
100
|
+
discarded.
|
|
101
|
+
|
|
102
|
+
### `cron_expression` is a standard 5-field cron string
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
*/30 * * * * — every 30 minutes
|
|
106
|
+
0 * * * * — every hour on the minute
|
|
107
|
+
0 9 * * 1-5 — 9am Mon-Fri
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Sub-minute cadences are NOT supported. The minimum interval
|
|
111
|
+
the scheduler honors depends on platform-level configuration;
|
|
112
|
+
default minimum is 5 minutes.
|
|
113
|
+
|
|
114
|
+
### `updater_type` and `updater_body.updater_body_type` must correspond
|
|
115
|
+
|
|
116
|
+
The body carries TWO discriminator fields that name the same
|
|
117
|
+
choice from different angles:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
updater_type outer field on the request body
|
|
121
|
+
(top-level)
|
|
122
|
+
updater_body.updater_body_type inner field on the polymorphic
|
|
123
|
+
body embed
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
They must agree according to a fixed mapping:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
updater_type: "cloud_watch" ↔ updater_body_type: "cloud_watch_request"
|
|
130
|
+
updater_type: "restapi" ↔ updater_body_type: "restapi_request"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
A mismatched pair (e.g. `updater_type: "cloud_watch"` +
|
|
134
|
+
`updater_body_type: "restapi_request"`) returns 422 — typically
|
|
135
|
+
on `/updater_type` with a "type / body type mismatch" message.
|
|
136
|
+
The full mapping is exported by the SDK; the `updater_type` enum
|
|
137
|
+
and the `updater_body_type` enum are kept in lockstep by the
|
|
138
|
+
generated types.
|
|
139
|
+
|
|
140
|
+
### `updater_tool_id` and `sender_tool_ids` serve different roles
|
|
141
|
+
|
|
142
|
+
- `updater_tool_id` (single) — the tool the updater uses to
|
|
143
|
+
ISSUE the poll. Must be a tool whose `intent` is
|
|
144
|
+
`status_poller` (e.g. a `cloud_watch_log_group` body type).
|
|
145
|
+
- `sender_tool_ids` (array) — the tools whose sent messages
|
|
146
|
+
this updater is responsible for tracking. The match logic
|
|
147
|
+
joins each polled event back to a message row that was
|
|
148
|
+
sent through one of these tools.
|
|
149
|
+
|
|
150
|
+
A single status updater can track messages from multiple
|
|
151
|
+
sender tools (e.g. an SMS tool plus a voice tool routed
|
|
152
|
+
through the same provider).
|
|
153
|
+
|
|
154
|
+
## 3. Field ownership
|
|
155
|
+
|
|
156
|
+
**Server-derived (Response-only).** Universal set from
|
|
157
|
+
`type_naming.md`.
|
|
158
|
+
|
|
159
|
+
**Caller-supplied (round-trip).**
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
name required string
|
|
163
|
+
cron_expression required string — standard cron
|
|
164
|
+
updater_type required enum — e.g. 'cloud_watch'
|
|
165
|
+
updater_tool_id required UUID — the polling tool
|
|
166
|
+
sender_tool_ids required UUID[] — tools whose messages this updater tracks
|
|
167
|
+
datalake_id required UUID
|
|
168
|
+
updater_body required embed — { updater_body_type, ... }
|
|
169
|
+
message_config required embed — { type, body }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Write-only (Request-only).** None.
|
|
173
|
+
|
|
174
|
+
## 4. Error envelopes
|
|
175
|
+
|
|
176
|
+
Standard JSON:API envelopes per `errors.md`. Common rejections:
|
|
177
|
+
|
|
178
|
+
| `source.pointer` | Cause |
|
|
179
|
+
|-------------------------------------|----------------------------------------------------|
|
|
180
|
+
| `/cron_expression` | invalid cron syntax or below minimum interval |
|
|
181
|
+
| `/updater_tool_id` | tool's intent isn't `status_poller` |
|
|
182
|
+
| `/sender_tool_ids/0` | tool id doesn't exist or wrong datalake |
|
|
183
|
+
| `/updater_body/updater_body_type` | enum mismatch |
|
|
184
|
+
| `/updater_type` | mismatch with `updater_body.updater_body_type` |
|
|
185
|
+
| `/message_config/body` | empty when `type: 'custom'` |
|
|
186
|
+
| `/base` | `cron_management_failed` — body validated and saved but scheduler registration failed; the row is rolled back |
|
|
187
|
+
|
|
188
|
+
## 5. Lifecycle
|
|
189
|
+
|
|
190
|
+
### Create
|
|
191
|
+
|
|
192
|
+
Synchronous. A successful create registers a scheduled poll
|
|
193
|
+
under the supplied `cron_expression`; the first run fires at
|
|
194
|
+
the next cron slot at-or-after create time.
|
|
195
|
+
|
|
196
|
+
The cron registration is part of the create transaction — if
|
|
197
|
+
the body validates but scheduler registration fails, the
|
|
198
|
+
platform rolls the row back and returns 422 with
|
|
199
|
+
`cron_management_failed` on `/base` (see §4). Conversely, a
|
|
200
|
+
body that fails structural validation never reaches the
|
|
201
|
+
scheduler; cron-job registration only happens on the success
|
|
202
|
+
branch.
|
|
203
|
+
|
|
204
|
+
### Read shapes
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
.list(tenantSlug, datalakeSlug) Paged: { data, meta }
|
|
208
|
+
.get(tenantSlug, datalakeSlug, idOrSlug) One row
|
|
209
|
+
.metadata(tenantSlug, datalakeSlug) Markdown — catalog
|
|
210
|
+
.metadataDetails(tenantSlug, datalakeSlug, Markdown — one updater
|
|
211
|
+
idOrSlug)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Update
|
|
215
|
+
|
|
216
|
+
`PUT` replays the full body. The polymorphic `updater_body`
|
|
217
|
+
must keep the same `updater_body_type` on update — changing
|
|
218
|
+
the discriminator requires delete + recreate.
|
|
219
|
+
|
|
220
|
+
### Delete
|
|
221
|
+
|
|
222
|
+
`DELETE` removes the row and cancels future scheduled polls.
|
|
223
|
+
In-flight polls complete; their writebacks land normally.
|
|
224
|
+
|
|
225
|
+
The delete is **graceful** with respect to scheduler state: if
|
|
226
|
+
the row exists but its cron-job entry is somehow missing or
|
|
227
|
+
the scheduler raises during cancellation, the row is still
|
|
228
|
+
deleted and the DELETE returns 204. Consumers do not need to
|
|
229
|
+
check or retry for partial-delete states.
|
|
230
|
+
|
|
231
|
+
## 6. Gotchas
|
|
232
|
+
|
|
233
|
+
1. **The polling tool's `intent` must be `status_poller`.**
|
|
234
|
+
A tool with `intent: 'sms'` or any other non-poller intent
|
|
235
|
+
is rejected at create time. The poller intent gates the
|
|
236
|
+
tool's eligibility as an `updater_tool_id`.
|
|
237
|
+
|
|
238
|
+
2. **`message_config.body` must emit valid JSON.** The
|
|
239
|
+
template is rendered against the polling tool's parsed
|
|
240
|
+
response and the output is then JSON-parsed. A render that
|
|
241
|
+
produces malformed JSON (e.g. trailing comma, unquoted
|
|
242
|
+
string) makes the entire poll cycle silently no-op for
|
|
243
|
+
that updater — events stay unprocessed.
|
|
244
|
+
|
|
245
|
+
3. **`external_id` join is exact-match.** Events whose
|
|
246
|
+
`external_id` doesn't match a message row are discarded
|
|
247
|
+
silently. If your provider returns a prefixed id (e.g.
|
|
248
|
+
`provider-prefix/12345`) but your message rows carry
|
|
249
|
+
the bare id, transform inside the Liquid template (string
|
|
250
|
+
filters) so the rendered `external_id` matches.
|
|
251
|
+
|
|
252
|
+
4. **`set_params` keys must be writable columns on the
|
|
253
|
+
message row.** Unknown keys are silently dropped during
|
|
254
|
+
the update. Refer to the message dataset's schema to
|
|
255
|
+
confirm which columns are writable from updater output.
|
|
256
|
+
|
|
257
|
+
5. **Cron windows + polling-window overlap.** Choose
|
|
258
|
+
`start_time` / `end_time` to overlap consecutive cron
|
|
259
|
+
intervals (e.g. cron every 30 min, window of 45 min) so
|
|
260
|
+
transient delivery delays don't fall between two polls.
|
|
261
|
+
The defaults in the wire-shape example illustrate this
|
|
262
|
+
pattern: 30-minute cron, 45-minute window.
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# AI agents
|
|
2
|
+
|
|
3
|
+
An **AI agent** is a registered LLM caller — a configured pairing
|
|
4
|
+
of a chat-completion tool, a model identifier, a system prompt
|
|
5
|
+
template, and JSON Schemas declaring the agent's input shape and
|
|
6
|
+
the LLM's required output shape. Workflows attach agents at
|
|
7
|
+
their enrichment stage; the agent's structured output then feeds
|
|
8
|
+
the workflow's decision template.
|
|
9
|
+
|
|
10
|
+
SDK namespace: `api.aiAgents`.
|
|
11
|
+
|
|
12
|
+
AI agents are **Datalake-DB-resident** — the parent datalake
|
|
13
|
+
must be `status: 'ready'` and the bound tool must exist before
|
|
14
|
+
POST.
|
|
15
|
+
|
|
16
|
+
## 1. Wire shape
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import type {
|
|
20
|
+
AiAgentRequestWritable,
|
|
21
|
+
AiAgentResponse,
|
|
22
|
+
} from '@alvera-ai/platform-sdk'
|
|
23
|
+
|
|
24
|
+
const { data: created } = await api.aiAgents.create(
|
|
25
|
+
tenantSlug,
|
|
26
|
+
datalakeSlug,
|
|
27
|
+
{
|
|
28
|
+
name: 'Contact Us Triage Categorizer',
|
|
29
|
+
description: 'Triages inbound submissions into appointment / job / spam',
|
|
30
|
+
|
|
31
|
+
tool_id: chatCompletionToolId, // see §2 — must be a tool with intent: 'llm_enrichment'
|
|
32
|
+
|
|
33
|
+
model: 'llama3.2:3b', // provider-specific model identifier
|
|
34
|
+
data_access: 'unregulated', // 'unregulated' | 'regulated' (compliance gate)
|
|
35
|
+
temperature: 0.0,
|
|
36
|
+
max_tokens: 2048,
|
|
37
|
+
enabled: true,
|
|
38
|
+
|
|
39
|
+
input_schema: { // JSON Schema — fields the prompt receives
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
msg: { type: 'string' },
|
|
43
|
+
submission_id: { type: 'string' },
|
|
44
|
+
},
|
|
45
|
+
required: ['msg', 'submission_id'],
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
llm_response_schema: { // JSON Schema — what the LLM MUST return
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
category: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
enum: ['appointment_request', 'job_inquiry', 'flag_spam'],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ['category'],
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
prompt_config: {
|
|
61
|
+
type: 'custom',
|
|
62
|
+
body: `You are a triage assistant. Categorize this submission ...
|
|
63
|
+
|
|
64
|
+
Submission ID: {{ submission_id }}
|
|
65
|
+
Message: {{ msg }}
|
|
66
|
+
|
|
67
|
+
Respond with JSON: {"category": "..."}`,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
// created.id, created.slug — server-derived
|
|
72
|
+
// created.name === body.name
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 2. Rules the type cannot encode
|
|
76
|
+
|
|
77
|
+
### `tool_id` must point to a chat-completion tool
|
|
78
|
+
|
|
79
|
+
The bound tool's `intent` MUST be `llm_enrichment`. Tools with
|
|
80
|
+
any other intent (`sms`, `data_exchange`, `status_poller`, …)
|
|
81
|
+
are rejected at create time as a 422 on `/tool_id`.
|
|
82
|
+
|
|
83
|
+
The chat-completion tool is typically a `tool_body_type:
|
|
84
|
+
'rest_api'` configured against an OpenAI-compatible endpoint
|
|
85
|
+
(OpenAI itself, Ollama, vLLM, etc.). The platform appends
|
|
86
|
+
`/chat/completions` to the tool's `base_url`, so:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
tool.base_url = 'http://localhost:11434/v1'
|
|
90
|
+
→ actual request URL = 'http://localhost:11434/v1/chat/completions'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
A tool whose `base_url` already contains `/chat/completions`
|
|
94
|
+
will produce a doubled path and 404.
|
|
95
|
+
|
|
96
|
+
### `llm_response_schema` is REQUIRED and load-bearing
|
|
97
|
+
|
|
98
|
+
The schema is mandatory at create time — a body omitting it
|
|
99
|
+
returns a 422 on `/llm_response_schema`. The schema serves two
|
|
100
|
+
roles:
|
|
101
|
+
|
|
102
|
+
1. **Provider-side decoding constraint**: the platform forwards
|
|
103
|
+
the schema in the chat-completion request as
|
|
104
|
+
`response_format: json_schema`, instructing the provider to
|
|
105
|
+
constrain decoding so the LLM cannot return prose, markdown
|
|
106
|
+
code fences, or chat preamble around the JSON. Without this
|
|
107
|
+
constraint, small models often wrap output in fences and the
|
|
108
|
+
platform's JSON parse step fails with `json_decode_failed`.
|
|
109
|
+
2. **Output binding**: the parsed LLM output is exposed in the
|
|
110
|
+
workflow's `additional_context` under the agent's slug, with
|
|
111
|
+
property paths matching the schema (see §3 below).
|
|
112
|
+
|
|
113
|
+
### Prompt receives Liquid-rendered fields from `input_schema`
|
|
114
|
+
|
|
115
|
+
The agent's `prompt_config.body` is a Liquid template that
|
|
116
|
+
references the agent's `input_schema` fields via `{{ <field> }}`.
|
|
117
|
+
At workflow runtime, the workflow's `context_mapping_config`
|
|
118
|
+
populates these fields from the inbound dataset row (see §3).
|
|
119
|
+
|
|
120
|
+
The platform validates at create time that every `{{ <field> }}`
|
|
121
|
+
referenced by the prompt body resolves to a property declared in
|
|
122
|
+
`input_schema`. Drift between the two surfaces as a 422 on
|
|
123
|
+
`/prompt_config/body`.
|
|
124
|
+
|
|
125
|
+
### `data_access` is the compliance gate
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
data_access: 'unregulated' — the LLM call receives tokenized,
|
|
129
|
+
redacted data; safe for any LLM
|
|
130
|
+
(third-party, on-prem, …).
|
|
131
|
+
Default for triage / categorization
|
|
132
|
+
/ lead-scoring workflows.
|
|
133
|
+
data_access: 'regulated' — the LLM call receives raw values;
|
|
134
|
+
MUST be paired with an LLM the
|
|
135
|
+
operator trusts with this tier
|
|
136
|
+
(typically an on-prem deployment).
|
|
137
|
+
Use for compliance workflows that
|
|
138
|
+
need raw values — sanctions
|
|
139
|
+
screening, manual review, KYC.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The setting is **immutable post-create**. Flipping the access
|
|
143
|
+
tier requires recreating the agent (delete + create).
|
|
144
|
+
|
|
145
|
+
### `enabled` gates whether workflows can invoke the agent
|
|
146
|
+
|
|
147
|
+
A workflow that references a `disabled` agent fails at run time
|
|
148
|
+
with `error_code: 'ai_agent_disabled'` on the workflow execution
|
|
149
|
+
log. `enabled: false` is the operator's kill switch for an agent
|
|
150
|
+
without removing it from the workflow's `ai_agents` array.
|
|
151
|
+
|
|
152
|
+
### Numeric clamps on `temperature` and `max_tokens`
|
|
153
|
+
|
|
154
|
+
The platform validates the numeric ranges at create / update time
|
|
155
|
+
— BEFORE any chat-completion call to the bound tool. Out-of-range
|
|
156
|
+
values surface as field-level rejections, not as runtime
|
|
157
|
+
provider errors:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
temperature required number ∈ [0.0, 2.0] inclusive on both ends
|
|
161
|
+
max_tokens required integer ∈ [1, 100_000] inclusive on both ends
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
These bounds are the platform's, not the provider's. Picking a
|
|
165
|
+
value the provider doesn't support (e.g. `temperature: 1.5` on
|
|
166
|
+
a model that caps at 1.0) surfaces later as a provider error in
|
|
167
|
+
the workflow execution log, not at create.
|
|
168
|
+
|
|
169
|
+
### Name uniqueness is per-tenant, not per-datalake
|
|
170
|
+
|
|
171
|
+
The agent's `name` (and the server-derived `slug`) must be
|
|
172
|
+
unique within the tenant — collisions across **different
|
|
173
|
+
datalakes of the same tenant** are rejected with a 422 on
|
|
174
|
+
`/name` or `/slug`. This is unusual: most other resources scoped
|
|
175
|
+
under a datalake (tools, data sources, interoperability
|
|
176
|
+
contracts) carry per-datalake uniqueness. AI agents are the
|
|
177
|
+
exception because their `slug` is the workflow-template
|
|
178
|
+
reference key (`additional_context.<slug>`) and the platform
|
|
179
|
+
needs a stable cross-datalake namespace for it.
|
|
180
|
+
|
|
181
|
+
Practical consequence: if you want the same logical agent in
|
|
182
|
+
two datalakes (e.g. dev + prod inside one tenant), give them
|
|
183
|
+
distinct names. A naming convention like `"<purpose>-<env>"` or
|
|
184
|
+
including the datalake slug in the agent name keeps the
|
|
185
|
+
namespace clean.
|
|
186
|
+
|
|
187
|
+
## 3. Workflow attachment
|
|
188
|
+
|
|
189
|
+
A workflow attaches an AI agent at its **enrichment** stage via
|
|
190
|
+
the inline `ai_agents` array on the workflow body (see
|
|
191
|
+
`workflows.md` §3):
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
{
|
|
195
|
+
// ... other workflow fields ...
|
|
196
|
+
ai_agents: [
|
|
197
|
+
{
|
|
198
|
+
ai_agent_id: createdAgentId,
|
|
199
|
+
position: 0, // ordering when multiple agents fire
|
|
200
|
+
context_mapping_config: {
|
|
201
|
+
type: 'custom',
|
|
202
|
+
// Liquid renders to a JSON object whose keys match the
|
|
203
|
+
// agent's input_schema. The values reference the inbound
|
|
204
|
+
// dataset row via `event_dataset.<field>`.
|
|
205
|
+
body: JSON.stringify({
|
|
206
|
+
msg: '{{ event_dataset.message }}',
|
|
207
|
+
submission_id: '{{ event_dataset.submission_id }}',
|
|
208
|
+
}),
|
|
209
|
+
output_schema: '{"type":"object"}',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
decision_config: {
|
|
214
|
+
type: 'custom',
|
|
215
|
+
// The agent's parsed output is exposed at
|
|
216
|
+
// `additional_context.<agent_slug>.<field>`. Property paths
|
|
217
|
+
// match the agent's llm_response_schema.
|
|
218
|
+
body: '["{{ additional_context.contact-us-triage-categorizer.category }}"]',
|
|
219
|
+
output_schema: '{"type":"array","items":{"type":"string"}}',
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The flow at run time:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
inbound row
|
|
228
|
+
│
|
|
229
|
+
▼
|
|
230
|
+
workflow filter
|
|
231
|
+
│
|
|
232
|
+
▼
|
|
233
|
+
for each ai_agents[i] (ordered by position):
|
|
234
|
+
• render context_mapping_config.body with Liquid (event_dataset.*, etc.)
|
|
235
|
+
• parse rendered output as JSON
|
|
236
|
+
• check it conforms to agent.input_schema
|
|
237
|
+
• forward to the bound tool as a chat completion
|
|
238
|
+
• parse the LLM response per agent.llm_response_schema
|
|
239
|
+
• merge under additional_context[agent.slug]
|
|
240
|
+
│
|
|
241
|
+
▼
|
|
242
|
+
workflow decision (reads additional_context.<agent_slug>.<field>)
|
|
243
|
+
│
|
|
244
|
+
▼
|
|
245
|
+
workflow action(s)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 4. Field ownership
|
|
249
|
+
|
|
250
|
+
**Server-derived (Response-only).** Universal set from
|
|
251
|
+
`type_naming.md`. The `slug` is what workflow templates
|
|
252
|
+
reference via `additional_context.<slug>`.
|
|
253
|
+
|
|
254
|
+
**Caller-supplied (round-trip).**
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
name required string
|
|
258
|
+
description optional string
|
|
259
|
+
tool_id required UUID — must be intent: llm_enrichment
|
|
260
|
+
model required string — provider-specific identifier
|
|
261
|
+
data_access required enum — 'unregulated' | 'regulated'
|
|
262
|
+
temperature required number
|
|
263
|
+
max_tokens required number
|
|
264
|
+
enabled required bool
|
|
265
|
+
input_schema required JSON Schema object
|
|
266
|
+
llm_response_schema required JSON Schema object — see §2
|
|
267
|
+
prompt_config required embed — { type, body }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Write-only (Request-only).** None.
|
|
271
|
+
|
|
272
|
+
## 5. Error envelopes
|
|
273
|
+
|
|
274
|
+
Standard JSON:API envelopes per `errors.md`. Common rejections:
|
|
275
|
+
|
|
276
|
+
| `source.pointer` | Cause |
|
|
277
|
+
|---------------------------------|----------------------------------------------------|
|
|
278
|
+
| `/tool_id` | tool's intent isn't `llm_enrichment` |
|
|
279
|
+
| `/llm_response_schema` | missing or not a valid JSON Schema object |
|
|
280
|
+
| `/input_schema` | missing or not a valid JSON Schema object |
|
|
281
|
+
| `/prompt_config/body` | references a field absent from `input_schema` |
|
|
282
|
+
| `/data_access` | value not in enum |
|
|
283
|
+
| `/temperature` | outside the inclusive range [0.0, 2.0] |
|
|
284
|
+
| `/max_tokens` | outside the inclusive range [1, 100_000] |
|
|
285
|
+
| `/name` or `/slug` | uniqueness collision — scope is per-tenant |
|
|
286
|
+
|
|
287
|
+
## 6. Lifecycle
|
|
288
|
+
|
|
289
|
+
### Create
|
|
290
|
+
|
|
291
|
+
Synchronous. The agent is immediately invocable by workflows
|
|
292
|
+
once `enabled: true`.
|
|
293
|
+
|
|
294
|
+
### Read shapes
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
.list(tenantSlug, datalakeSlug) Paged: { data, meta }
|
|
298
|
+
.get(tenantSlug, datalakeSlug, id) One row, BY ID (UUID — not slug)
|
|
299
|
+
.metadata(tenantSlug, datalakeSlug) Markdown catalog
|
|
300
|
+
.metadataDetails(tenantSlug, datalakeSlug, Markdown for one agent;
|
|
301
|
+
id) section headings: Model,
|
|
302
|
+
Bound tool, Prompt config
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`.get` and `.metadataDetails` accept the agent's UUID `id` as
|
|
306
|
+
the path key — NOT the slug. The slug is only used as a
|
|
307
|
+
runtime reference key (`additional_context.<slug>` in workflow
|
|
308
|
+
templates).
|
|
309
|
+
|
|
310
|
+
### Update
|
|
311
|
+
|
|
312
|
+
`PUT` replays the full body — no PATCH. Replace-on-PUT means
|
|
313
|
+
all required fields must be re-supplied verbatim, including
|
|
314
|
+
`input_schema`, `llm_response_schema`, and `prompt_config`.
|
|
315
|
+
Omitting any returns a 422.
|
|
316
|
+
|
|
317
|
+
### Delete
|
|
318
|
+
|
|
319
|
+
`DELETE` removes the row. Workflows that still reference the
|
|
320
|
+
agent in their `ai_agents` array fail at run time with
|
|
321
|
+
`error_code: 'ai_agent_not_found'`; clean up workflow
|
|
322
|
+
references first.
|
|
323
|
+
|
|
324
|
+
## 7. Run-time failure modes
|
|
325
|
+
|
|
326
|
+
When a workflow run's enrichment stage fails, the per-execution
|
|
327
|
+
`error.json` artifact carries a structured failure record (see
|
|
328
|
+
`workflows.md` §6 for the artifact convention). The
|
|
329
|
+
agent-specific codes:
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
tool_execution_failed — the bound chat-completion tool's
|
|
333
|
+
HTTP call failed (404, refused,
|
|
334
|
+
timeout). error.json carries the
|
|
335
|
+
rich detail (URL, status); the
|
|
336
|
+
workflow execution log carries
|
|
337
|
+
a coarse error_message of the
|
|
338
|
+
form "AI enrichment failed: <agent_name>"
|
|
339
|
+
json_decode_failed — provider returned a response that
|
|
340
|
+
didn't parse as JSON (typically
|
|
341
|
+
when llm_response_schema wasn't
|
|
342
|
+
wired through to the provider's
|
|
343
|
+
structured-output mode)
|
|
344
|
+
context_mapping_failed — context_mapping_config.body
|
|
345
|
+
rendered output that didn't
|
|
346
|
+
validate against the agent's
|
|
347
|
+
input_schema
|
|
348
|
+
ai_agent_disabled — agent's enabled flag is false
|
|
349
|
+
ai_agent_not_found — agent was deleted but workflow
|
|
350
|
+
still references it
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The `error.json` shape:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
{
|
|
357
|
+
error_code: string — one of the codes above
|
|
358
|
+
stage: string — 'enrichment' for agent-related failures
|
|
359
|
+
ai_agent_slug: string — slug of the failing agent
|
|
360
|
+
error_message: string — coarse copy of the workflow execution log's message
|
|
361
|
+
detail: string — rich provider context (URL, HTTP status, etc.);
|
|
362
|
+
ONLY present in error.json, NOT mirrored to the
|
|
363
|
+
workflow execution log row
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The `enrichment.json` artifact (success branch) carries the
|
|
368
|
+
parsed agent outputs:
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
{
|
|
372
|
+
status: 'completed' | 'failed' | 'skipped',
|
|
373
|
+
<agent_slug_1>: {
|
|
374
|
+
status: 'completed',
|
|
375
|
+
output: { /* matches llm_response_schema */ },
|
|
376
|
+
},
|
|
377
|
+
<agent_slug_2>: { /* ... */ },
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
For workflows without any `ai_agents`, `enrichment.json` lands
|
|
382
|
+
with `{ status: 'skipped' }` and no per-agent entries.
|
|
383
|
+
|
|
384
|
+
## 8. Gotchas
|
|
385
|
+
|
|
386
|
+
1. **`llm_response_schema` is REQUIRED**, not optional. Omitting
|
|
387
|
+
it returns 422 on `/llm_response_schema`. The platform uses
|
|
388
|
+
it both as a provider-side decoding constraint and as the
|
|
389
|
+
output-binding shape for `additional_context.<slug>`. Missing
|
|
390
|
+
the schema means no structured output and downstream decision
|
|
391
|
+
templates can't reference the agent's fields.
|
|
392
|
+
|
|
393
|
+
2. **`tool_id` must reference an `intent: 'llm_enrichment'`
|
|
394
|
+
tool.** Other intents are rejected at create. The chat
|
|
395
|
+
completion tool is typically `tool_body_type: 'rest_api'`
|
|
396
|
+
against an OpenAI-compatible endpoint; the platform appends
|
|
397
|
+
`/chat/completions` to its `base_url`.
|
|
398
|
+
|
|
399
|
+
3. **`additional_context.<slug>` is the workflow accessor.**
|
|
400
|
+
The agent's `slug` (server-derived from `name`) is what
|
|
401
|
+
workflow decision and action templates reference. Capture it
|
|
402
|
+
from the create response — don't pre-compute.
|
|
403
|
+
|
|
404
|
+
4. **`.get` takes UUID `id`, not slug.** The SHOW route is
|
|
405
|
+
`/ai-agents/:id`. The slug is only a runtime template key.
|
|
406
|
+
|
|
407
|
+
5. **Replace-on-PUT.** Update bodies must re-supply
|
|
408
|
+
`input_schema`, `llm_response_schema`, `prompt_config`
|
|
409
|
+
verbatim. Partial PUT silently drops fields and returns
|
|
410
|
+
422 on the missing required field.
|
|
411
|
+
|
|
412
|
+
6. **`data_access` is immutable post-create.** Flipping the
|
|
413
|
+
compliance tier requires delete + recreate.
|
|
414
|
+
|
|
415
|
+
7. **`prompt_config.body` field references must resolve in
|
|
416
|
+
`input_schema`.** A `{{ field }}` that isn't declared in
|
|
417
|
+
`input_schema` returns 422 on `/prompt_config/body` at
|
|
418
|
+
create time.
|
|
419
|
+
|
|
420
|
+
8. **The chat-completion tool's `base_url` should NOT include
|
|
421
|
+
`/chat/completions`.** The platform appends the path; a
|
|
422
|
+
doubled path 404s. Use the API root (e.g. `/v1` for OpenAI
|
|
423
|
+
or Ollama).
|