@alvera-ai/platform-sdk 0.10.0-rc.8 → 0.11.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 (66) hide show
  1. package/.agent/AGENTS.md +499 -0
  2. package/.agent/account_management.md +456 -0
  3. package/.agent/action_status_updaters.md +264 -0
  4. package/.agent/ai_agents.md +462 -0
  5. package/.agent/ai_sandbox.md +265 -0
  6. package/.agent/async.md +112 -0
  7. package/.agent/connected_apps.md +408 -0
  8. package/.agent/cookbook/_fixtures/README.md +106 -0
  9. package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_customer.liquid +32 -0
  10. package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_mdm.liquid +20 -0
  11. package/.agent/cookbook/_fixtures/accounts_receivable/stripe_customers_batch1.csv +5 -0
  12. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_generic_table.liquid +33 -0
  13. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +88 -0
  14. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_mdm.liquid +48 -0
  15. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +47 -0
  16. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +24 -0
  17. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +38 -0
  18. package/.agent/cookbook/_fixtures/healthcare/memorandum-of-association-01.png +0 -0
  19. package/.agent/cookbook/_fixtures/healthcare/sample_two_page.pdf +43 -0
  20. package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_compliance_screening.liquid +59 -0
  21. package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_mdm.liquid +36 -0
  22. package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_mdm.liquid +30 -0
  23. package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_payment_account.liquid +55 -0
  24. package/.agent/cookbook/_setup/accounts_receivable.md +282 -0
  25. package/.agent/cookbook/_setup/foundation.md +277 -0
  26. package/.agent/cookbook/_setup/healthcare.md +279 -0
  27. package/.agent/cookbook/_setup/payment_risk.md +283 -0
  28. package/.agent/cookbook/action-status-updaters.md +212 -0
  29. package/.agent/cookbook/ai-agent-invoke.md +243 -0
  30. package/.agent/cookbook/appointment-review-sms-workflow.md +761 -0
  31. package/.agent/cookbook/birthday-greeting-sms-trigger.md +656 -0
  32. package/.agent/cookbook/bulk-ingest.md +254 -0
  33. package/.agent/cookbook/contact-us-triage-with-llm.md +622 -0
  34. package/.agent/cookbook/custom-tables.md +201 -0
  35. package/.agent/cookbook/dunning-sms-for-delinquent.md +619 -0
  36. package/.agent/cookbook/invite-team.md +194 -0
  37. package/.agent/cookbook/kyc-notification-on-account-activation.md +619 -0
  38. package/.agent/cookbook/rest-fetch.md +246 -0
  39. package/.agent/cookbook/sanctions-screening-with-agent-review.md +733 -0
  40. package/.agent/cookbook/score-leads-with-llm-categorization.md +624 -0
  41. package/.agent/cookbook/system-templates.md +129 -0
  42. package/.agent/cookbook/triage-prospects-by-priority.md +533 -0
  43. package/.agent/cookbook/welcome-sms-for-customers.md +607 -0
  44. package/.agent/data_activation_clients.md +559 -0
  45. package/.agent/data_sources.md +235 -0
  46. package/.agent/datalakes.md +714 -0
  47. package/.agent/debugging.md +137 -0
  48. package/.agent/errors.md +190 -0
  49. package/.agent/generic_tables.md +351 -0
  50. package/.agent/interoperability_contracts.md +417 -0
  51. package/.agent/mdm.md +293 -0
  52. package/.agent/mutations.md +126 -0
  53. package/.agent/templates.md +98 -0
  54. package/.agent/tool-call-configs.md +90 -0
  55. package/.agent/tools.md +547 -0
  56. package/.agent/type_naming.md +129 -0
  57. package/.agent/workflows.md +617 -0
  58. package/README.md +178 -0
  59. package/dist/bin/platform-sdk.d.mts +1 -0
  60. package/dist/bin/platform-sdk.mjs +106 -0
  61. package/dist/bin/platform-sdk.mjs.map +1 -0
  62. package/dist/index.d.mts +1310 -44116
  63. package/dist/index.d.mts.map +1 -1
  64. package/dist/index.mjs +1194 -7344
  65. package/dist/index.mjs.map +1 -1
  66. package/package.json +19 -10
@@ -0,0 +1,254 @@
1
+ ---
2
+ title: "Capability: load a whole file of records in one upload"
3
+ summary: A capability walk for bulk file ingestion. Stand up an ingestion chain (data source → manual-upload tool → interop contract → DAC), mint a presigned upload URL (`api.datalakes.createUploadLink`), PUT a CSV to it, enqueue the file (`api.dataActivationClients.ingestFile`), discover the batch the worker allocates by diffing the logs, and verify every row landed (`api.datasets.search`). For backfills and exports — not row-at-a-time.
4
+ industry: accounts_receivable
5
+ slug: bulk-ingest
6
+ vitest_source:
7
+ - integration-tests/tests/accounts_receivable/run-dac-bulk.test.ts
8
+ - integration-tests/tests/accounts_receivable/create-dac.test.ts
9
+ - integration-tests/tests/accounts_receivable/interoperability-contracts.test.ts
10
+ - integration-tests/tests/accounts_receivable/tools.test.ts
11
+ - integration-tests/tests/accounts_receivable/data-sources.test.ts
12
+ - integration-tests/tests/accounts_receivable/bootstrap.test.ts
13
+ status: green
14
+ ---
15
+
16
+ # Capability
17
+
18
+ **What you get:** a whole file of records — a CSV export, a backfill — turned
19
+ into dataset rows in one upload, instead of one `.ingest` call per row.
20
+
21
+ The bulk path is three calls plus a discovery step:
22
+
23
+ 1. `datalakes.createUploadLink(...)` → a presigned PUT URL + a storage `key`.
24
+ 2. Raw `fetch` PUT of the file bytes to that URL (**plain HTTP, not the SDK**).
25
+ 3. `dataActivationClients.ingestFile(..., { key })` → enqueues a background job.
26
+ 4. The worker reads the file, allocates a fresh `batch_id`, and fans out
27
+ per-row jobs. The `batch_id` is **not returned** — you discover it by diffing
28
+ the DAC's logs before vs. after.
29
+
30
+ It runs through the same data-activation chain as inline `.ingest`; only the
31
+ entry point differs. This is **global** to every datalake. See
32
+ `data_activation_clients.md` §6.2 for the reference.
33
+
34
+ # Walkthrough
35
+
36
+ The `_setup/accounts_receivable.md` bootstrap left `api`, `tenantSlug`,
37
+ `datalakeSlug`, and `ctx.datalakeId` populated. Steps 001–004 build the same
38
+ ingestion chain any AR cookbook uses; 005–008 are the bulk-specific part.
39
+
40
+ ## 001 — create the data source
41
+
42
+ The chain's first link registers where the rows originate. Its `uri` flows into
43
+ each ingested row's `source_uri`.
44
+
45
+ ```typescript
46
+ const { data: ds } = await api.dataSources.create(tenantSlug, datalakeSlug, {
47
+ name: `Bulk Stripe Source ${runSuffix}`,
48
+ uri: 'stripe.example.com',
49
+ description: 'Stripe billing export — origin of the bulk customer CSV.',
50
+ status: 'active',
51
+ is_default: false,
52
+ })
53
+ dataSourceId = ds.id!
54
+ ```
55
+
56
+ ## 002 — create the manual-upload tool
57
+
58
+ A `manual_upload` tool backs both the inline and the file-upload ingest paths —
59
+ the rows arrive in the upload, not by the tool fetching them.
60
+
61
+ ```typescript
62
+ const { data: tool } = await api.tools.create(tenantSlug, datalakeSlug, {
63
+ name: `Bulk Manual Upload Tool ${runSuffix}`,
64
+ description: 'Manual-upload data-exchange tool backing the bulk-ingest DAC.',
65
+ intent: 'data_exchange',
66
+ status: 'active',
67
+ datalake_id: ctx.datalakeId,
68
+ data_source_id: dataSourceId,
69
+ body: { tool_body_type: 'manual_upload' },
70
+ })
71
+ ctx.manualUploadToolId = tool.id!
72
+ ```
73
+
74
+ ## 003 — create the customer interoperability contract
75
+
76
+ The contract maps each inbound CSV row into a `Customer` upsert. Load the
77
+ production Stripe customer + MDM Liquid templates from the vendored fixtures
78
+ rather than inlining them.
79
+
80
+ ```typescript
81
+ const { readFileSync } = await import('node:fs')
82
+ const { join } = await import('node:path')
83
+ const customerTemplate = readFileSync(
84
+ join(process.env.COOKBOOK_FIXTURES_DIR!, 'accounts_receivable/_customers_accounts_receivable_customer.liquid'),
85
+ 'utf8',
86
+ )
87
+ const mdmTemplate = readFileSync(
88
+ join(process.env.COOKBOOK_FIXTURES_DIR!, 'accounts_receivable/_customers_accounts_receivable_mdm.liquid'),
89
+ 'utf8',
90
+ )
91
+
92
+ const { data: contract } = await api.interoperabilityContracts.create(tenantSlug, datalakeSlug, {
93
+ name: `Bulk Stripe Customer Contract ${runSuffix}`,
94
+ description: 'Stripe customers → AccountsReceivable Customer (custom Liquid + MDM input).',
95
+ resource_type: 'customer',
96
+ template_config: { type: 'custom', body: customerTemplate },
97
+ mdm_input_config: { type: 'custom', body: mdmTemplate },
98
+ generic_table_id: null,
99
+ })
100
+ interopContractId = contract.id!
101
+ ```
102
+
103
+ ## 004 — create the manual-upload DAC
104
+
105
+ The Data Activation Client binds tool + data source + contract into one
106
+ ingestion endpoint. Its server-derived `slug` is the handle the file is ingested
107
+ against.
108
+
109
+ ```typescript
110
+ const { data: dac } = await api.dataActivationClients.create(tenantSlug, datalakeSlug, {
111
+ name: `Bulk Stripe Customer DAC ${runSuffix}`,
112
+ description: 'Manual-upload DAC — ingests the bulk Stripe customer CSV.',
113
+ tool_id: ctx.manualUploadToolId,
114
+ data_source_id: dataSourceId,
115
+ tool_call: { tool_call_type: 'manual_upload' },
116
+ interoperability_contract_ids: [interopContractId],
117
+ })
118
+ dacId = dac.id!
119
+ ctx.dacSlug = dac.slug!
120
+ ```
121
+
122
+ ## 005 — mint a presigned upload URL and PUT the CSV
123
+
124
+ `createUploadLink` returns a presigned PUT `url` and the storage `key` the
125
+ platform will reference. Upload the file body to the URL with a **raw `fetch`** —
126
+ this PUT goes straight to object storage, **not** through the SDK. The CSV is the
127
+ vendored 4-row Stripe customer export.
128
+
129
+ ```typescript
130
+ const { readFileSync } = await import('node:fs')
131
+ const { join } = await import('node:path')
132
+ const csvBody = readFileSync(
133
+ join(process.env.COOKBOOK_FIXTURES_DIR!, 'accounts_receivable/stripe_customers_batch1.csv'),
134
+ 'utf8',
135
+ )
136
+
137
+ const { data: link } = await api.datalakes.createUploadLink(tenantSlug, datalakeSlug, {
138
+ content_type: 'text/csv',
139
+ filename: 'stripe_customers_batch1.csv',
140
+ })
141
+ ctx.uploadKey = link.key!
142
+
143
+ const put = await fetch(link.url!, {
144
+ method: 'PUT',
145
+ headers: { 'Content-Type': 'text/csv' },
146
+ body: csvBody,
147
+ })
148
+ if (put.status !== 200) {
149
+ throw new Error(`presigned PUT failed: ${put.status}`)
150
+ }
151
+ ```
152
+
153
+ ## 006 — snapshot the logs, then enqueue the file
154
+
155
+ `ingestFile` is fully async — it enqueues a background job and returns a
156
+ `job_id`, but **no `batch_id`** (the worker allocates that later). Snapshot the
157
+ existing log batch ids first, so step 007 can spot the new one the worker
158
+ creates.
159
+
160
+ ```typescript
161
+ const { data: pre } = await api.dataActivationClients.logs.list(tenantSlug, datalakeSlug, ctx.dacSlug)
162
+ ctx.preBatchIds = (pre.data ?? [])
163
+ .map((r) => (r as { batch_id?: string }).batch_id)
164
+ .filter((b): b is string => typeof b === 'string')
165
+
166
+ const { data: job } = await api.dataActivationClients.ingestFile(tenantSlug, datalakeSlug, ctx.dacSlug, {
167
+ key: ctx.uploadKey,
168
+ })
169
+ if (!job.job_id) {
170
+ throw new Error('ingestFile did not return a job_id')
171
+ }
172
+ ```
173
+
174
+ ## 007 — discover the batch the worker allocated
175
+
176
+ Poll the logs until a **new** batch id (one not in the pre-snapshot) shows a
177
+ fully-merged customer ingest: `rows_ingested >= 4` (the four CSV rows) and a
178
+ non-empty `output_files` array (the merged data landed in storage).
179
+
180
+ ```typescript
181
+ const pre = new Set<string>(ctx.preBatchIds)
182
+ const deadline = Date.now() + 60_000
183
+ let batchId: string | null = null
184
+ while (Date.now() < deadline && !batchId) {
185
+ const { data } = await api.dataActivationClients.logs.list(tenantSlug, datalakeSlug, ctx.dacSlug)
186
+ for (const row of (data.data ?? []) as Array<Record<string, unknown>>) {
187
+ const b = row.batch_id
188
+ if (typeof b !== 'string' || pre.has(b)) continue
189
+ if (row.dataset_table !== 'customers') continue
190
+ if (typeof row.rows_ingested !== 'number' || row.rows_ingested < 4) continue
191
+ if (!Array.isArray(row.output_files) || row.output_files.length === 0) continue
192
+ batchId = b
193
+ break
194
+ }
195
+ if (!batchId) await new Promise((r) => setTimeout(r, 1_000))
196
+ }
197
+ if (!batchId) {
198
+ throw new Error('bulk ingest did not produce a fully-merged batch within 60s')
199
+ }
200
+ ctx.batchId = batchId
201
+ ```
202
+
203
+ ## 008 — verify every row landed
204
+
205
+ Search the `customer` dataset scoped to that batch. `createUserSearch` compiles
206
+ the query (`ra` is the customer dataset alias); `search` returns the rows once
207
+ they materialize — poll until all four appear.
208
+
209
+ ```typescript
210
+ const { data: userSearch } = await api.datasets.createUserSearch(tenantSlug, datalakeSlug, 'customer', {
211
+ search_query: `ra.batch_id = '${ctx.batchId}'`,
212
+ })
213
+ if (userSearch.status !== 'completed') {
214
+ throw new Error(`customer user-search did not compile: ${userSearch.status}`)
215
+ }
216
+
217
+ const deadline = Date.now() + 90_000
218
+ let rows: Array<Record<string, unknown>> = []
219
+ while (Date.now() < deadline && rows.length < 4) {
220
+ const { data: page } = await api.datasets.search(tenantSlug, datalakeSlug, 'customer', {
221
+ userSearchId: userSearch.id!,
222
+ dataAccessMode: 'unregulated',
223
+ })
224
+ rows = (page.data ?? []) as Array<Record<string, unknown>>
225
+ if (rows.length < 4) await new Promise((r) => setTimeout(r, 1_000))
226
+ }
227
+ if (rows.length < 4) {
228
+ throw new Error(`expected 4 ingested customers, found ${rows.length} within 90s`)
229
+ }
230
+ ```
231
+
232
+ # Gotchas
233
+
234
+ - **The file PUT is raw HTTP, not the SDK.** `createUploadLink` and `ingestFile`
235
+ are SDK calls; the upload itself is a plain `fetch` PUT to the presigned URL.
236
+ Don't try to route the bytes through an `api.*` method.
237
+ - **`ingestFile` does NOT return the `batch_id`.** It returns only a `job_id`.
238
+ The worker allocates the `batch_id` when it reads the file — discover it by
239
+ diffing the DAC logs before vs. after (the snapshot in 006).
240
+ - **Wait for merge, not just for rows.** A log row with `rows_ingested > 0` but
241
+ `output_files: []` is mid-merge — gate on `output_files.length > 0` before
242
+ trusting the batch.
243
+ - **Search is a two-call, poll-until-ready pattern.** `createUserSearch` compiles
244
+ the query and returns `status: 'completed'`; the rows still take a moment to
245
+ materialize, so poll `search` until they appear.
246
+
247
+ # See also
248
+
249
+ - `data_activation_clients.md` §6.2 — `.ingestFile` reference + the bulk flow
250
+ - `datalakes.md` — `createUploadLink` (presigned upload) reference
251
+ - `_setup/accounts_receivable.md` — the bootstrap this walk starts from
252
+ - `_fixtures/accounts_receivable/` — the vendored Stripe customer CSV + Liquid
253
+ - `integration-tests/tests/accounts_receivable/run-dac-bulk.test.ts` — the green
254
+ test these calls are lifted from