@alvera-ai/platform-sdk 0.17.0 → 0.18.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.
Files changed (83) hide show
  1. package/.agent/AGENTS.md +82 -144
  2. package/.agent/account_management.md +2 -2
  3. package/.agent/action_logs.md +4 -4
  4. package/.agent/ai_agents.md +28 -21
  5. package/.agent/ai_sandbox.md +49 -39
  6. package/.agent/connected_apps.md +3 -3
  7. package/.agent/cookbook/_fixtures/README.md +1 -1
  8. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_generic_table.liquid +1 -1
  9. package/.agent/cookbook/_fixtures/organic-marketing/_lead_submissions_foundation_legal_entity.liquid +80 -0
  10. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_mdm.liquid +2 -1
  11. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_generic_table.liquid +57 -0
  12. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_legal_entity.liquid +30 -0
  13. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_mdm.liquid +44 -0
  14. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_generic_table.liquid +57 -0
  15. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_legal_entity.liquid +36 -0
  16. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_mdm.liquid +41 -0
  17. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_generic_table.liquid +70 -0
  18. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_legal_entity.liquid +52 -0
  19. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_mdm.liquid +42 -0
  20. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_generic_table.liquid +38 -0
  21. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_legal_entity.liquid +48 -0
  22. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_mdm.liquid +49 -0
  23. package/.agent/cookbook/organic-marketing.md +2801 -0
  24. package/.agent/cookbook/payments-compliance.md +2180 -0
  25. package/.agent/cookbook/primary-care.md +2175 -0
  26. package/.agent/cookbook/subscription-saas.md +2403 -0
  27. package/.agent/data_activation_clients.md +65 -52
  28. package/.agent/datalakes.md +338 -171
  29. package/.agent/errors.md +3 -3
  30. package/.agent/generic_tables.md +151 -62
  31. package/.agent/interoperability_contracts.md +57 -22
  32. package/.agent/mdm.md +136 -153
  33. package/.agent/messages.md +36 -34
  34. package/.agent/mock-services.md +1 -1
  35. package/.agent/mutations.md +2 -2
  36. package/.agent/templates.md +14 -13
  37. package/.agent/tools.md +63 -21
  38. package/.agent/type_naming.md +13 -13
  39. package/.agent/workflows.md +99 -53
  40. package/README.md +2 -2
  41. package/dist/bin/platform-sdk.mjs +33 -47
  42. package/dist/bin/platform-sdk.mjs.map +1 -1
  43. package/dist/index.d.mts +565 -379
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs +494 -59
  46. package/dist/index.mjs.map +1 -1
  47. package/package.json +4 -3
  48. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +0 -88
  49. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +0 -47
  50. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +0 -24
  51. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +0 -38
  52. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_compliance_screening.liquid +0 -59
  53. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_mdm.liquid +0 -36
  54. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_mdm.liquid +0 -30
  55. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_payment_account.liquid +0 -55
  56. package/.agent/cookbook/_fixtures/subscription/_customers_subscription_mdm.liquid +0 -20
  57. package/.agent/cookbook/_setup/foundation.md +0 -359
  58. package/.agent/cookbook/_setup/healthcare.md +0 -361
  59. package/.agent/cookbook/_setup/payments.md +0 -365
  60. package/.agent/cookbook/_setup/subscription.md +0 -364
  61. package/.agent/cookbook/action-status-updaters.md +0 -278
  62. package/.agent/cookbook/ai-agent-invoke.md +0 -279
  63. package/.agent/cookbook/appointment-review-sms-workflow.md +0 -801
  64. package/.agent/cookbook/birthday-greeting-sms-trigger.md +0 -696
  65. package/.agent/cookbook/bulk-ingest.md +0 -302
  66. package/.agent/cookbook/contact-us-triage-with-llm.md +0 -663
  67. package/.agent/cookbook/dunning-sms-for-delinquent.md +0 -659
  68. package/.agent/cookbook/generic-tables.md +0 -244
  69. package/.agent/cookbook/invite-team.md +0 -200
  70. package/.agent/cookbook/kyc-notification-on-account-activation.md +0 -661
  71. package/.agent/cookbook/marketing-campaign-send.md +0 -1044
  72. package/.agent/cookbook/paginated-restapi-poller.md +0 -383
  73. package/.agent/cookbook/rest-fetch.md +0 -273
  74. package/.agent/cookbook/sanctions-screening-with-agent-review.md +0 -773
  75. package/.agent/cookbook/score-leads-with-llm-categorization.md +0 -665
  76. package/.agent/cookbook/system-templates.md +0 -165
  77. package/.agent/cookbook/talk-to-data.md +0 -178
  78. package/.agent/cookbook/triage-prospects-by-priority.md +0 -571
  79. package/.agent/cookbook/welcome-sms-for-customers.md +0 -647
  80. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/memorandum-of-association-01.png +0 -0
  81. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/sample_two_page.pdf +0 -0
  82. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/_customers_subscription_customer.liquid +0 -0
  83. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/stripe_customers_batch1.csv +0 -0
@@ -1,279 +0,0 @@
1
- ---
2
- title: "Capability: read an uploaded document and pull structured fields out of it"
3
- summary: A capability walk for direct AI-agent invocation with file vision. Upload a PDF + image, register an LLM-enrichment tool (the provider adapter) and an AI agent constrained to an output schema, then invoke the agent with the files (`api.aiAgents.invoke`) to get structured JSON back. This is the correct path for reading an uploaded file — the file is uploaded to storage and the agent reads it; the agent does NOT ingest the file as a dataset row.
4
- industry: healthcare
5
- slug: ai-agent-invoke
6
- vitest_source:
7
- - integration-tests/tests/healthcare/ai-agent-invoke.test.ts
8
- - integration-tests/tests/healthcare/bootstrap.test.ts
9
- status: green
10
- ---
11
-
12
- # Capability
13
-
14
- **What you get:** structured JSON pulled out of an uploaded document or image —
15
- a scanned form, a PDF, an ID — by an AI agent, in one call.
16
-
17
- The shape:
18
-
19
- 1. **Upload** the file(s) to storage (`datalakes.createUploadLink` + a raw PUT)
20
- and keep the returned `key`s.
21
- 2. Register an **LLM-enrichment tool** — the provider adapter. Its `base_body`
22
- authors the provider's multimodal request and its `response_extractor` maps
23
- the provider's envelope back to a canonical `{ output_json, … }`.
24
- 3. Register an **AI agent** that points at the tool, constrained by an
25
- `llm_response_schema` (the JSON shape you want back) and an `input_schema`.
26
- 4. `aiAgents.invoke(..., { input, files: [{ key, content_type }] })` → the
27
- parsed JSON, an explanation, and token usage.
28
-
29
- > **File-ingestion note (read this).** This is how you make an agent *read* a
30
- > file. It is **not** how you turn a file into dataset rows — that is bulk
31
- > ingestion (`dataActivationClients.ingestFile`; see `bulk-ingest.md`). An AI
32
- > agent does **enrichment / extraction** on content handed to it; it does not
33
- > ingest files. When a contract needs per-row vision enrichment during
34
- > ingestion, nest the agent on the contract (`interoperability_contracts.md`
35
- > §6) — the row references the uploaded file's key and the agent reads it as
36
- > enrichment. The file→row step is always the DAC, never the agent.
37
-
38
- This is **global** to every datalake. See `ai_agents.md` for the agent
39
- reference. The walk uses a WireMock-stubbed provider so it runs deterministically
40
- on the mock stack; swap the tool's `base_url` + adapter for a real provider
41
- unchanged.
42
-
43
- # Walkthrough
44
-
45
- The `_setup/healthcare.md` bootstrap left `api`, `tenantSlug`, `datalakeSlug`,
46
- and `ctx.datalakeId` populated.
47
-
48
- ## 001 — upload the document and image
49
-
50
- Mint a presigned URL per file and PUT the bytes with a **raw `fetch`** (the
51
- upload goes to object storage, not through the SDK). Keep each returned `key` —
52
- that is how you hand the file to the agent. The fixtures are a 2-page PDF and a
53
- document image.
54
-
55
- ```typescript
56
- const { readFileSync } = await import('node:fs')
57
- const { join } = await import('node:path')
58
-
59
- async function uploadFixture(filename: string, contentType: string): Promise<string> {
60
- const bytes = readFileSync(join(process.env.COOKBOOK_FIXTURES_DIR!, 'healthcare', filename))
61
- const { data: link } = await api.datalakes.createUploadLink(tenantSlug, datalakeSlug, {
62
- filename,
63
- content_type: contentType,
64
- })
65
- const put = await fetch(link.url!, { method: 'PUT', headers: { 'Content-Type': contentType }, body: bytes })
66
- if (put.status !== 200) throw new Error(`upload of ${filename} failed: ${put.status}`)
67
- return link.key!
68
- }
69
-
70
- ctx.pdfKey = await uploadFixture('sample_two_page.pdf', 'application/pdf')
71
- ctx.imageKey = await uploadFixture('memorandum-of-association-01.png', 'image/png')
72
- ```
73
-
74
- ## 002 — register the LLM-enrichment tool (the provider adapter)
75
-
76
- The tool is the complete provider adapter. `base_body` authors the provider's
77
- multimodal request (here the OpenAI chat-completions shape, with the agent's
78
- `llm_response_schema` injected as `{{ schema }}` and uploaded images appended as
79
- `image_url` parts). `response_extractor` reads the provider's envelope back into
80
- the canonical `{ output_json, explanation, …_tokens }`; its `output_schema` is
81
- required for an `llm_enrichment` tool.
82
-
83
- ```typescript
84
- const ENRICHMENT_OUTPUT_SCHEMA = {
85
- type: 'object',
86
- properties: {
87
- output_json: {},
88
- input_tokens: { type: ['integer', 'null'] },
89
- output_tokens: { type: ['integer', 'null'] },
90
- total_tokens: { type: ['integer', 'null'] },
91
- explanation: { type: ['string', 'null'] },
92
- },
93
- required: ['output_json'],
94
- }
95
-
96
- const OPENAI_BASE_BODY =
97
- '{"model": "{{ model }}", "messages": [{"role": "user", "content": [{"type": "text", "text": "{{ rendered_prompt | json_escape }}"}{% for img in images %}, {"type": "image_url", "image_url": {"url": "data:{{ img.content_type }};base64,{{ img.data }}"}}{% endfor %}]}], "response_format": {"type": "json_schema", "json_schema": {"name": "output", "strict": true, "schema": {{ schema | to_json }}}}}'
98
-
99
- const OPENAI_EXTRACTOR =
100
- '{"output_json": "{{ msg.choices[0].message.content | json_escape }}", "explanation": "{{ msg.choices[0].message.reasoning_content | json_escape }}", "input_tokens": {{ msg.usage.prompt_tokens | default: 0 }}, "output_tokens": {{ msg.usage.completion_tokens | default: 0 }}, "total_tokens": {{ msg.usage.total_tokens | default: 0 }}}'
101
-
102
- const { data: tool } = await api.tools.create(tenantSlug, datalakeSlug, {
103
- name: `Legal-Entity Extraction Tool ${runSuffix}`,
104
- description: 'Multimodal LLM-enrichment adapter for legal_entity extraction.',
105
- intent: 'llm_enrichment',
106
- status: 'active',
107
- datalake_id: ctx.datalakeId,
108
- response_extractor: { type: 'custom', body: OPENAI_EXTRACTOR, output_schema: ENRICHMENT_OUTPUT_SCHEMA },
109
- body: {
110
- tool_body_type: 'rest_api',
111
- base_url: 'http://localhost:8080/openai',
112
- base_path: { type: 'custom', body: '/v1/chat/completions' },
113
- auth_method: 'api_key',
114
- api_key: 'stub-key',
115
- api_key_name: 'Authorization',
116
- api_key_location: 'header',
117
- request_type: 'json',
118
- response_type: 'json',
119
- timeout_ms: 300_000,
120
- base_body: { type: 'custom', body: OPENAI_BASE_BODY },
121
- },
122
- })
123
- toolId = tool.id!
124
- ```
125
-
126
- ## 003 — register the extraction agent
127
-
128
- The agent points at the tool and is constrained by two schemas: `input_schema`
129
- (what `invoke` must pass — here `instructions`) and `llm_response_schema` (the
130
- shape the model must return — load-bearing, the platform constrains the model to
131
- it). `data_access: 'unregulated'` is the compliance gate for what the agent may
132
- read.
133
-
134
- ```typescript
135
- const LEGAL_ENTITY_RESPONSE_SCHEMA = {
136
- type: 'object',
137
- properties: {
138
- company_name: { type: 'string', nullable: true, maxLength: 200 },
139
- registered_address: { type: 'string', nullable: true, maxLength: 300 },
140
- date_of_formation: { type: 'string', nullable: true, maxLength: 40 },
141
- capital_amount: { type: 'number', nullable: true },
142
- capital_currency: { type: 'string', nullable: true, maxLength: 20 },
143
- },
144
- required: ['company_name'],
145
- additionalProperties: false,
146
- }
147
-
148
- const { data: agent } = await api.aiAgents.create(tenantSlug, datalakeSlug, {
149
- name: `Legal Entity Extraction Agent ${runSuffix}`,
150
- description: 'Extracts legal_entity-shaped fields from corporate documents.',
151
- tool_id: toolId,
152
- model: 'gpt-4o',
153
- data_access: 'unregulated',
154
- temperature: 0.0,
155
- max_tokens: 8_192,
156
- enabled: true,
157
- input_schema: { type: 'object', properties: { instructions: { type: 'string' } }, required: ['instructions'] },
158
- llm_response_schema: LEGAL_ENTITY_RESPONSE_SCHEMA,
159
- prompt_config: {
160
- type: 'custom',
161
- body: 'You are a legal-entity data extraction specialist.\nExtract structured corporate information from the provided content and return a JSON object aligned to the legal_entity schema.\n\n{{ instructions }}',
162
- },
163
- })
164
- aiAgentId = agent.id!
165
- ```
166
-
167
- ## 004 — invoke the agent with the files
168
-
169
- `invoke` runs the agent against the uploaded files: `input` satisfies the
170
- `input_schema`, and `files` lists the storage keys + content types to attach. The
171
- result is the parsed JSON (`output`, matching `llm_response_schema`), an
172
- `explanation`, and token `usage`.
173
-
174
- ```typescript
175
- const { data: result } = await api.aiAgents.invoke(tenantSlug, datalakeSlug, aiAgentId, {
176
- input: { instructions: 'Extract the company name and any visible registration details from the attached documents.' },
177
- files: [
178
- { key: ctx.pdfKey, content_type: 'application/pdf' },
179
- { key: ctx.imageKey, content_type: 'image/png' },
180
- ],
181
- })
182
-
183
- if (!result.output || typeof result.output !== 'object') {
184
- throw new Error('invoke did not return a structured output object')
185
- }
186
- if (!(result.output as Record<string, unknown>).company_name) {
187
- throw new Error('expected a company_name in the extracted output')
188
- }
189
- if (!result.usage || (result.usage as { input_tokens?: number }).input_tokens === undefined) {
190
- throw new Error('expected token usage on the invoke result')
191
- }
192
- ```
193
-
194
- ## 005 — a malformed input is rejected
195
-
196
- `invoke` validates `input` against the agent's `input_schema` before calling the
197
- model. Omitting the required `instructions` returns a 422 — caught here so the
198
- walk proves the gate without aborting.
199
-
200
- ```typescript
201
- let rejected = false
202
- try {
203
- await api.aiAgents.invoke(tenantSlug, datalakeSlug, aiAgentId, {
204
- input: { wrong_field: 'no instructions here' },
205
- files: [],
206
- })
207
- } catch (err) {
208
- const status = (err as { _httpStatus?: number })._httpStatus
209
- if (status !== 422) throw err
210
- rejected = true
211
- }
212
- if (!rejected) {
213
- throw new Error('expected a 422 for input that violates the agent input_schema')
214
- }
215
- ```
216
-
217
- ## 006 — write the integration test
218
-
219
- End the build with a test you keep: re-read the agent and probe the one
220
- behaviour that is provable deterministically — the input gate. A real
221
- extraction (§004) needs a live provider behind the tool; the schema gate
222
- does not, which is exactly what makes it the durable test. This block
223
- runs live under `make validate-cookbook`.
224
-
225
- ```typescript
226
- // Re-GET — the agent must be enabled, or every invoke will refuse.
227
- const { data: agentRow } = await api.aiAgents.get(tenantSlug, datalakeSlug, aiAgentId)
228
- if (agentRow.enabled !== true) {
229
- throw new Error('expected enabled=true on agent read-back')
230
- }
231
- // Behavioural probe — the input_schema gate fires BEFORE the model:
232
- // a malformed input must 422 even with no working provider.
233
- let gateFired = false
234
- try {
235
- await api.aiAgents.invoke(tenantSlug, datalakeSlug, aiAgentId, {
236
- input: { wrong_field: 'test — no instructions here' },
237
- files: [],
238
- })
239
- } catch (err) {
240
- const status = (err as { _httpStatus?: number })._httpStatus
241
- if (status !== 422) throw err
242
- gateFired = true
243
- }
244
- if (!gateFired) {
245
- throw new Error('agent accepted input that violates its input_schema (expected 422)')
246
- }
247
- ```
248
-
249
- If the gate stops firing in production, escalate with the
250
- accepted-payload evidence — don't loosen the `input_schema` to make the
251
- error go away.
252
-
253
- # Gotchas
254
-
255
- - **Uploading a file is not ingesting it.** The upload + `invoke` here makes the
256
- agent *read* the file. To turn a file into dataset rows, use
257
- `dataActivationClients.ingestFile` (`bulk-ingest.md`). Don't reach for an agent
258
- to "ingest" a document — agents enrich, the DAC ingests.
259
- - **`llm_response_schema` is load-bearing.** It is what constrains the model's
260
- output; the tool's `response_extractor.output_schema` is required too (a
261
- missing one is a 422 at `/response_extractor/output_schema`).
262
- - **The model lives on the agent, not the tool.** The tool is the provider
263
- adapter (URL + request/response shape); the agent sets the `model`. Swapping
264
- providers means a new tool adapter + the agent's `model`.
265
- - **The file PUT is raw HTTP.** `createUploadLink` is an SDK call; the upload is a
266
- plain `fetch` PUT to the presigned URL, exactly like bulk ingest.
267
- - **`data_access` gates what the agent may read.** `unregulated` here; a stricter
268
- setting changes what content the agent is allowed to see.
269
-
270
- # See also
271
-
272
- - `ai_agents.md` — agent wire shape, `input_schema` / `llm_response_schema`, invoke
273
- - `interoperability_contracts.md` §6 — nesting an agent on a contract for per-row
274
- enrichment during ingestion (the file→row path stays the DAC's)
275
- - `bulk-ingest.md` — the correct path for turning a file into dataset rows
276
- - `_setup/healthcare.md` — the bootstrap this walk starts from
277
- - `_fixtures/healthcare/` — the vendored PDF + image
278
- - `integration-tests/tests/healthcare/ai-agent-invoke.test.ts` — the green test
279
- these calls are lifted from