@anyformat/sdk 0.2.1 → 0.2.3

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/README.md CHANGED
@@ -24,6 +24,54 @@ const result = await af
24
24
  result.field("vendor")?.value; // -> "Acme Corp"
25
25
  ```
26
26
 
27
+ ## Quick parse: one call → markdown
28
+
29
+ `af.parse()` runs the platform's atomic `/parse` operation (a fast lite parse)
30
+ and returns an opaque `jobId`. Poll `getParseMarkdown(jobId)` for the markdown —
31
+ no workflow to author or manage.
32
+
33
+ ```ts
34
+ const jobId = await af.parse(file); // File | Blob
35
+ let markdown = await af.getParseMarkdown(jobId);
36
+ while (markdown === null) {
37
+ await new Promise((r) => setTimeout(r, 3000));
38
+ markdown = await af.getParseMarkdown(jobId); // null until done
39
+ }
40
+ console.log(markdown);
41
+ ```
42
+
43
+ ## Managing workflows
44
+
45
+ ```ts
46
+ const workflows = await af.listWorkflows({ pageSize: 50 }); // -> Workflow[]
47
+ for (const wf of workflows) console.log(wf.id, wf.name);
48
+
49
+ await af.deleteWorkflow(workflows[0].id); // -> the deleted id
50
+ ```
51
+
52
+ ## Smart lookup
53
+
54
+ Resolve a field by matching the document against a reference file (CSV/catalog) instead of
55
+ reading it off the page — flag the field with `{ lookup: true }` and pass the file via
56
+ `lookupFiles`:
57
+
58
+ ```ts
59
+ await af
60
+ .workflow("Invoice + vendor lookup")
61
+ .parse()
62
+ .extract(
63
+ [
64
+ Schema.string("vendor_name", "Vendor as printed on the invoice."),
65
+ Schema.string("vendor_id", "Canonical vendor code from the catalog, joined on vendor_name.", { lookup: true }),
66
+ ],
67
+ { lookupFiles: ["vendor_catalog.csv"], lookupSuggestion: "Join vendor_name → vendor_id." },
68
+ )
69
+ .run(file)
70
+ .wait();
71
+ ```
72
+
73
+ Full runnable example: [`examples/smart-lookup.mjs`](examples/smart-lookup.mjs).
74
+
27
75
  ## How it's built
28
76
 
29
77
  The wire types and the low-level HTTP client are **generated** from the API's
@@ -34,8 +82,8 @@ ergonomic layer (`Schema`, `WorkflowBuilder`, `Result`, `Anyformat`) wraps it.
34
82
  ## Regenerating (and the drift guard)
35
83
 
36
84
  ```bash
37
- npm run gen # dump the v2 OpenAPI offline + regenerate src/sdk
38
- npm run gen:check # regenerate and fail if the committed output drifted
85
+ bun run gen # dump the v2 OpenAPI offline + regenerate src/sdk
86
+ bun run gen:check # regenerate and fail if the committed output drifted
39
87
  ```
40
88
 
41
89
  `gen:check` runs in CI and as a pre-commit hook (scoped to changes under
@@ -46,10 +94,30 @@ caught as a type error, not just a diff.
46
94
 
47
95
  ## Scripts
48
96
 
97
+ Install deps with `bun install --frozen-lockfile` (matches CI + the frontend
98
+ toolchain). Then:
99
+
49
100
  | script | what |
50
101
  | --- | --- |
51
- | `npm run gen` | dump spec + regenerate `src/sdk` |
52
- | `npm run gen:check` | regenerate + assert no drift |
53
- | `npm run build` | bundle to `dist/` (ESM + CJS + types) via tsup |
54
- | `npm run typecheck` | `tsc --noEmit` |
55
- | `npm test` | vitest |
102
+ | `bun run gen` | dump spec + regenerate `src/sdk` |
103
+ | `bun run gen:check` | regenerate + assert no drift |
104
+ | `bun run build` | bundle to `dist/` (ESM + CJS + types) via tsup |
105
+ | `bun run typecheck` | `tsc --noEmit` |
106
+ | `bun run test` | vitest |
107
+
108
+ ## Releasing
109
+
110
+ Cut on demand — a maintainer bumps the version in a PR, then pushes a `sdk-js-vX.Y.Z` tag on the release commit. Full flow + one-time setup: [`../RELEASING.md`](../RELEASING.md). Break-glass: `./publish.sh [--build-only|--dry-run]`.
111
+
112
+ ## Node configuration knobs
113
+
114
+ Every knob is an option on `.parse({...})` / `.extract(fields, {...})`. `parse` options are a
115
+ `mode`-discriminated union, so a wrong-mode knob is a compile-time error.
116
+
117
+ **`.parse({...})`** — `mode` (`"standard"`/`"agentic"`/`"lite"`), `promptHint`, `figureEnhancement`,
118
+ `cache`; **agentic-only** `effort` (`"low"`/`"mid"`/`"accurate"`); **lite-only** `ocrEffort`
119
+ (`"medium"`/`"high"`), `region` (`"eu"`/`"global"`), `skipRoutingReview`, `figures`,
120
+ `textFormatting`, `routingEffort`.
121
+
122
+ **`.extract(fields, {...})`** — `mode` (`"standard"`/`"agentic"`/`"max"`/`"lite"`), `useImages`,
123
+ `lookupSuggestion`, `lookupReasoningEffort` (`"minimal"`/`"low"`/`"medium"`/`"high"`), `lookupFiles`.
package/dist/index.cjs CHANGED
@@ -24,44 +24,144 @@ __export(index_exports, {
24
24
  Anyformat: () => Anyformat,
25
25
  AnyformatError: () => AnyformatError,
26
26
  BadRequest: () => BadRequest,
27
+ BadRequestError: () => BadRequestError,
27
28
  Forbidden: () => Forbidden,
29
+ ForbiddenError: () => ForbiddenError,
28
30
  NotFound: () => NotFound,
31
+ NotFoundError: () => NotFoundError,
32
+ ParseView: () => ParseView,
29
33
  RateLimited: () => RateLimited,
34
+ RateLimitedError: () => RateLimitedError,
30
35
  Result: () => Result,
31
36
  Run: () => Run,
32
37
  SDKTimeout: () => SDKTimeout,
38
+ SDKTimeoutError: () => SDKTimeoutError,
33
39
  Schema: () => Schema,
34
40
  ServerError: () => ServerError,
35
41
  Unauthorized: () => Unauthorized,
42
+ UnauthorizedError: () => UnauthorizedError,
43
+ Upload: () => Upload,
44
+ Workflow: () => Workflow,
36
45
  WorkflowBuilder: () => WorkflowBuilder,
37
46
  WorkflowBuilderError: () => WorkflowBuilderError
38
47
  });
39
48
  module.exports = __toCommonJS(index_exports);
40
49
 
50
+ // package.json
51
+ var package_default = {
52
+ name: "@anyformat/sdk",
53
+ version: "0.2.3",
54
+ description: "TypeScript SDK for the AnyFormat API",
55
+ license: "MIT",
56
+ author: "Anyformat <info@anyformat.ai>",
57
+ type: "module",
58
+ main: "./dist/index.cjs",
59
+ module: "./dist/index.js",
60
+ types: "./dist/index.d.ts",
61
+ exports: {
62
+ ".": {
63
+ types: "./dist/index.d.ts",
64
+ import: "./dist/index.js",
65
+ require: "./dist/index.cjs"
66
+ }
67
+ },
68
+ files: [
69
+ "dist"
70
+ ],
71
+ engines: {
72
+ node: ">=18"
73
+ },
74
+ scripts: {
75
+ "gen:spec": 'ROOT=$(unset GIT_DIR && git rev-parse --show-toplevel) && cd "$ROOT/anyformat/services/api" && uv run python "$ROOT/anyformat/libs/public/sdk-js/scripts/dump_openapi.py" "$ROOT/anyformat/libs/public/sdk-js/openapi/v2.json"',
76
+ "gen:client": "openapi-ts",
77
+ gen: "bun run gen:spec && bun run gen:client",
78
+ "gen:check": "bun run gen && git diff --exit-code -- openapi/v2.json src/sdk",
79
+ build: "tsup",
80
+ typecheck: "tsc --noEmit",
81
+ test: "vitest run",
82
+ prepublishOnly: "bun run build"
83
+ },
84
+ devDependencies: {
85
+ "@hey-api/openapi-ts": "^0.99.0",
86
+ "@types/node": "^25.9.1",
87
+ tsup: "^8.3.0",
88
+ typescript: "^5.6.0",
89
+ vitest: "^4.1.7"
90
+ }
91
+ };
92
+
41
93
  // src/builder.ts
42
94
  var import_node_fs = require("fs");
43
95
  var import_node_path = require("path");
44
96
 
97
+ // src/parse-view.ts
98
+ var ParseView = class {
99
+ /** @internal */
100
+ constructor(parse) {
101
+ this.parse = parse;
102
+ }
103
+ parse;
104
+ get markdown() {
105
+ return this.parse.markdown ?? null;
106
+ }
107
+ get text() {
108
+ return this.parse.text ?? null;
109
+ }
110
+ get blocks() {
111
+ return this.parse.blocks ?? [];
112
+ }
113
+ /**
114
+ * Document-level confidence (0-100) in the parsed content. May be `null` for
115
+ * some parse modes — fall back to `layoutConfidence`.
116
+ */
117
+ get parseConfidence() {
118
+ return this.parse.parse_confidence ?? null;
119
+ }
120
+ /**
121
+ * Document-level confidence (0-100) in the page layout (block positions).
122
+ * `null` when not available.
123
+ */
124
+ get layoutConfidence() {
125
+ return this.parse.layout_confidence ?? null;
126
+ }
127
+ };
128
+
45
129
  // src/result.ts
46
130
  var Result = class {
47
131
  constructor(raw) {
48
132
  this.raw = raw;
49
133
  }
50
134
  raw;
135
+ _parse;
51
136
  get collectionId() {
52
137
  return this.raw.collection_id;
53
138
  }
54
139
  get extractions() {
55
140
  return this.raw.extractions ?? [];
56
141
  }
142
+ /**
143
+ * Typed view over the parse node's output, or `null` when the workflow has
144
+ * no parse node. Cached so repeated access doesn't re-wrap.
145
+ */
146
+ get parse() {
147
+ if (this._parse === void 0) {
148
+ const raw = this.raw.parse;
149
+ this._parse = raw ? new ParseView(raw) : null;
150
+ }
151
+ return this._parse;
152
+ }
57
153
  /**
58
154
  * Convenience accessor for a top-level scalar field on the first
59
155
  * extraction (linear workflows). Returns `undefined` for missing fields
60
156
  * or list-valued fields — read `extractions` directly for those.
157
+ *
158
+ * Parameterise with the declared schema type to type `.value`:
159
+ * `result.field<number>("total").value` is `number | null`.
61
160
  */
62
161
  field(name) {
63
162
  const value = this.extractions[0]?.fields?.[name];
64
- return value !== void 0 && !Array.isArray(value) ? value : void 0;
163
+ if (value === void 0 || Array.isArray(value)) return void 0;
164
+ return value;
65
165
  }
66
166
  };
67
167
 
@@ -78,35 +178,41 @@ var APIError = class extends AnyformatError {
78
178
  status;
79
179
  body;
80
180
  };
81
- var BadRequest = class extends APIError {
181
+ var BadRequestError = class extends APIError {
82
182
  };
83
- var Unauthorized = class extends APIError {
183
+ var UnauthorizedError = class extends APIError {
84
184
  };
85
- var Forbidden = class extends APIError {
185
+ var ForbiddenError = class extends APIError {
86
186
  };
87
- var NotFound = class extends APIError {
187
+ var NotFoundError = class extends APIError {
88
188
  };
89
- var RateLimited = class extends APIError {
189
+ var RateLimitedError = class extends APIError {
90
190
  };
91
191
  var ServerError = class extends APIError {
92
192
  };
93
- var SDKTimeout = class extends AnyformatError {
193
+ var BadRequest = BadRequestError;
194
+ var Unauthorized = UnauthorizedError;
195
+ var Forbidden = ForbiddenError;
196
+ var NotFound = NotFoundError;
197
+ var RateLimited = RateLimitedError;
198
+ var SDKTimeoutError = class extends AnyformatError {
94
199
  };
200
+ var SDKTimeout = SDKTimeoutError;
95
201
  var WorkflowBuilderError = class extends AnyformatError {
96
202
  };
97
203
  function toApiError(status, body) {
98
204
  const message = `AnyFormat API error (${status})`;
99
205
  switch (status) {
100
206
  case 400:
101
- return new BadRequest(status, message, body);
207
+ return new BadRequestError(status, message, body);
102
208
  case 401:
103
- return new Unauthorized(status, message, body);
209
+ return new UnauthorizedError(status, message, body);
104
210
  case 403:
105
- return new Forbidden(status, message, body);
211
+ return new ForbiddenError(status, message, body);
106
212
  case 404:
107
- return new NotFound(status, message, body);
213
+ return new NotFoundError(status, message, body);
108
214
  case 429:
109
- return new RateLimited(status, message, body);
215
+ return new RateLimitedError(status, message, body);
110
216
  default:
111
217
  return status >= 500 ? new ServerError(status, message, body) : new APIError(status, message, body);
112
218
  }
@@ -923,9 +1029,29 @@ var createClient = (config = {}) => {
923
1029
  };
924
1030
 
925
1031
  // src/sdk/client.gen.ts
926
- var client = createClient(createConfig({ baseUrl: "https://api-dev.anyformat.ai" }));
1032
+ var client = createClient(createConfig({ baseUrl: "https://api.anyformat.dev" }));
927
1033
 
928
1034
  // src/sdk/sdk.gen.ts
1035
+ var v2Parse = (options) => (options.client ?? client).post({
1036
+ ...formDataBodySerializer,
1037
+ security: [{ scheme: "bearer", type: "http" }],
1038
+ url: "/v2/operations/parse",
1039
+ ...options,
1040
+ headers: {
1041
+ "Content-Type": null,
1042
+ ...options.headers
1043
+ }
1044
+ });
1045
+ var v2ParseResult = (options) => (options.client ?? client).get({
1046
+ security: [{ scheme: "bearer", type: "http" }],
1047
+ url: "/v2/operations/parse/{job_id}",
1048
+ ...options
1049
+ });
1050
+ var v2ListWorkflows = (options) => (options?.client ?? client).get({
1051
+ security: [{ scheme: "bearer", type: "http" }],
1052
+ url: "/v2/workflows/",
1053
+ ...options
1054
+ });
929
1055
  var v2CreateWorkflow = (options) => (options.client ?? client).post({
930
1056
  security: [{ scheme: "bearer", type: "http" }],
931
1057
  url: "/v2/workflows/",
@@ -935,11 +1061,30 @@ var v2CreateWorkflow = (options) => (options.client ?? client).post({
935
1061
  ...options.headers
936
1062
  }
937
1063
  });
1064
+ var v2DeleteWorkflow = (options) => (options.client ?? client).delete({
1065
+ security: [{ scheme: "bearer", type: "http" }],
1066
+ url: "/v2/workflows/{workflow_id}/",
1067
+ ...options
1068
+ });
1069
+ var v2UpdateWorkflow = (options) => (options.client ?? client).put({
1070
+ security: [{ scheme: "bearer", type: "http" }],
1071
+ url: "/v2/workflows/{workflow_id}/",
1072
+ ...options,
1073
+ headers: {
1074
+ "Content-Type": "application/json",
1075
+ ...options.headers
1076
+ }
1077
+ });
938
1078
  var v2GetFileResults = (options) => (options.client ?? client).get({
939
1079
  security: [{ scheme: "bearer", type: "http" }],
940
1080
  url: "/v2/workflows/{workflow_id}/files/{collection_id}/results/",
941
1081
  ...options
942
1082
  });
1083
+ var v2RunFile = (options) => (options.client ?? client).post({
1084
+ security: [{ scheme: "bearer", type: "http" }],
1085
+ url: "/v2/workflows/{workflow_id}/files/{file_id}/run/",
1086
+ ...options
1087
+ });
943
1088
  var v2RunWorkflow = (options) => (options.client ?? client).post({
944
1089
  ...formDataBodySerializer,
945
1090
  security: [{ scheme: "bearer", type: "http" }],
@@ -950,48 +1095,36 @@ var v2RunWorkflow = (options) => (options.client ?? client).post({
950
1095
  ...options.headers
951
1096
  }
952
1097
  });
1098
+ var v2UploadFile = (options) => (options.client ?? client).post({
1099
+ ...formDataBodySerializer,
1100
+ security: [{ scheme: "bearer", type: "http" }],
1101
+ url: "/v2/workflows/{workflow_id}/upload/",
1102
+ ...options,
1103
+ headers: {
1104
+ "Content-Type": null,
1105
+ ...options.headers
1106
+ }
1107
+ });
953
1108
 
954
1109
  // src/run.ts
955
1110
  var Run = class {
956
- constructor(client2, request, file, text) {
1111
+ /** @internal */
1112
+ constructor(client2, workflowId, id) {
957
1113
  this.client = client2;
958
- this.request = request;
959
- this.file = file;
960
- this.text = text;
1114
+ this.workflowId = workflowId;
1115
+ this.id = id;
961
1116
  }
962
1117
  client;
963
- request;
964
- file;
965
- text;
966
- workflowId = null;
967
- collectionId = null;
968
- /**
969
- * Create the workflow and submit the document — exactly once per `Run`.
970
- * Memoized so that retrying `wait()` after an `SDKTimeout` resumes polling
971
- * the in-flight run instead of creating a duplicate workflow + submission.
972
- */
973
- async submit() {
974
- if (this.collectionId !== null) return;
975
- const created = await v2CreateWorkflow({ client: this.client, body: this.request });
976
- if (created.error) throw toApiError(created.response?.status ?? 500, created.error);
977
- this.workflowId = created.data.id;
978
- const submitted = await v2RunWorkflow({
979
- client: this.client,
980
- path: { workflow_id: this.workflowId },
981
- body: { file: this.file, text: this.text }
982
- });
983
- if (submitted.error) throw toApiError(submitted.response?.status ?? 500, submitted.error);
984
- this.collectionId = submitted.data.id;
985
- }
1118
+ workflowId;
1119
+ id;
986
1120
  async wait(opts = {}) {
987
- const timeoutMs = opts.timeoutMs ?? 12e4;
988
- const pollMs = opts.pollMs ?? 2e3;
989
- await this.submit();
1121
+ const timeoutMs = opts.timeoutMs ?? 3e5;
1122
+ const pollMs = opts.pollMs ?? 3e3;
990
1123
  const deadline = Date.now() + timeoutMs;
991
1124
  while (Date.now() < deadline) {
992
1125
  const results = await v2GetFileResults({
993
1126
  client: this.client,
994
- path: { workflow_id: this.workflowId, collection_id: this.collectionId }
1127
+ path: { workflow_id: this.workflowId, collection_id: this.id }
995
1128
  });
996
1129
  if (!results.error) return new Result(results.data);
997
1130
  if (results.response?.status !== 412) {
@@ -999,7 +1132,89 @@ var Run = class {
999
1132
  }
1000
1133
  await new Promise((resolve) => setTimeout(resolve, pollMs));
1001
1134
  }
1002
- throw new SDKTimeout(`Extraction did not complete within ${timeoutMs}ms`);
1135
+ throw new SDKTimeoutError(`Extraction did not complete within ${timeoutMs}ms`);
1136
+ }
1137
+ };
1138
+
1139
+ // src/upload.ts
1140
+ var Upload = class {
1141
+ /** @internal */
1142
+ constructor(client2, workflowId, collectionId, fileId) {
1143
+ this.client = client2;
1144
+ this.workflowId = workflowId;
1145
+ this.collectionId = collectionId;
1146
+ this.fileId = fileId;
1147
+ }
1148
+ client;
1149
+ workflowId;
1150
+ collectionId;
1151
+ fileId;
1152
+ /** Trigger extraction on the staged file and return a pending `Run`. */
1153
+ async run() {
1154
+ if (!this.fileId) {
1155
+ throw new WorkflowBuilderError("This upload has no file_id \u2014 cannot run it");
1156
+ }
1157
+ const res = await v2RunFile({
1158
+ client: this.client,
1159
+ path: { workflow_id: this.workflowId, file_id: this.fileId }
1160
+ });
1161
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1162
+ return new Run(this.client, this.workflowId, res.data.id ?? this.collectionId);
1163
+ }
1164
+ };
1165
+
1166
+ // src/workflow.ts
1167
+ function validateDocument(file, op) {
1168
+ if (file === null) {
1169
+ throw new WorkflowBuilderError(`${op}() requires a named file`);
1170
+ }
1171
+ const filename = file.name;
1172
+ if (typeof filename !== "string" || filename.length === 0) {
1173
+ throw new WorkflowBuilderError(
1174
+ `${op}() needs a named file so the server can detect its type \u2014 pass a File, e.g. new File([bytes], "invoice.pdf"), not a bare Blob`
1175
+ );
1176
+ }
1177
+ }
1178
+ var Workflow = class {
1179
+ /** @internal */
1180
+ constructor(client2, id, name) {
1181
+ this.client = client2;
1182
+ this.id = id;
1183
+ this.name = name;
1184
+ }
1185
+ client;
1186
+ id;
1187
+ name;
1188
+ /** Submit a document against this workflow and return a pending `Run`. */
1189
+ async run(file = null) {
1190
+ validateDocument(file, "run");
1191
+ const submitted = await v2RunWorkflow({
1192
+ client: this.client,
1193
+ path: { workflow_id: this.id },
1194
+ body: { file }
1195
+ });
1196
+ if (submitted.error) throw toApiError(submitted.response?.status ?? 500, submitted.error);
1197
+ return new Run(this.client, this.id, submitted.data.id);
1198
+ }
1199
+ /**
1200
+ * Stage a document in this workflow WITHOUT running it (no credits spent).
1201
+ * Returns an `Upload`; call `.run()` on it to start extraction.
1202
+ */
1203
+ async upload(file = null) {
1204
+ validateDocument(file, "upload");
1205
+ const staged = await v2UploadFile({
1206
+ client: this.client,
1207
+ path: { workflow_id: this.id },
1208
+ body: { file }
1209
+ });
1210
+ if (staged.error) throw toApiError(staged.response?.status ?? 500, staged.error);
1211
+ const collectionId = staged.data.file_collection_id;
1212
+ if (!collectionId) {
1213
+ throw new WorkflowBuilderError(
1214
+ "Upload succeeded but the server did not return a file_collection_id \u2014 cannot build an Upload handle"
1215
+ );
1216
+ }
1217
+ return new Upload(this.client, this.id, collectionId, staged.data.file_id ?? null);
1003
1218
  }
1004
1219
  };
1005
1220
 
@@ -1029,12 +1244,21 @@ var WorkflowBuilder = class {
1029
1244
  }
1030
1245
  parse(opts = {}) {
1031
1246
  this.require(this.parseNode === null, "parse() can only be called once");
1247
+ const o = opts;
1032
1248
  this.parseNode = {
1033
1249
  type: "parse",
1034
- id: opts.id ?? this.nextId("parse"),
1035
- mode: opts.mode ?? "standard",
1036
- ...opts.promptHint !== void 0 ? { prompt_hint: opts.promptHint } : {},
1037
- figure_enhancement: opts.figureEnhancement ?? false
1250
+ id: o.id ?? this.nextId("parse"),
1251
+ mode: o.mode ?? "standard",
1252
+ ...o.promptHint !== void 0 ? { prompt_hint: o.promptHint } : {},
1253
+ figure_enhancement: o.figureEnhancement ?? false,
1254
+ cache: o.cache ?? true,
1255
+ ...o.effort !== void 0 ? { effort: o.effort } : {},
1256
+ ...o.ocrEffort !== void 0 ? { ocr_effort: o.ocrEffort } : {},
1257
+ ...o.region !== void 0 ? { region: o.region } : {},
1258
+ ...o.skipRoutingReview !== void 0 ? { skip_routing_review: o.skipRoutingReview } : {},
1259
+ ...o.figures !== void 0 ? { figures: o.figures } : {},
1260
+ ...o.textFormatting !== void 0 ? { text_formatting: o.textFormatting } : {},
1261
+ ...o.routingEffort !== void 0 ? { routing_effort: o.routingEffort } : {}
1038
1262
  };
1039
1263
  return this;
1040
1264
  }
@@ -1081,9 +1305,12 @@ var WorkflowBuilder = class {
1081
1305
  const node = {
1082
1306
  type: "extract",
1083
1307
  id: opts.id ?? this.nextId("extract"),
1308
+ ...opts.mode !== void 0 ? { mode: opts.mode } : {},
1084
1309
  extraction_schema: { fields },
1085
1310
  ...lookupFileUploads.length > 0 ? { lookup_file_uploads: lookupFileUploads } : {},
1086
- ...opts.lookupSuggestion !== void 0 ? { lookup_suggestion: opts.lookupSuggestion } : {}
1311
+ ...opts.lookupSuggestion !== void 0 ? { lookup_suggestion: opts.lookupSuggestion } : {},
1312
+ ...opts.lookupReasoningEffort !== void 0 ? { lookup_reasoning_effort: opts.lookupReasoningEffort } : {},
1313
+ ...opts.useImages !== void 0 ? { use_images: opts.useImages } : {}
1087
1314
  };
1088
1315
  this.extracts.push([node, branchId]);
1089
1316
  return this;
@@ -1165,30 +1392,22 @@ var WorkflowBuilder = class {
1165
1392
  }
1166
1393
  return { name: this.name, description: this.description, nodes, edges };
1167
1394
  }
1168
- /** Persist the workflow and return its id. */
1395
+ /** Persist the workflow and return a handle you can `.run(file)` on. */
1169
1396
  async create() {
1170
1397
  const res = await v2CreateWorkflow({ client: this.client, body: this.build() });
1171
1398
  if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1172
- return res.data.id;
1399
+ return new Workflow(this.client, res.data.id, res.data.name);
1173
1400
  }
1174
- /** Create the workflow, submit a document, and return a pending `Run`. */
1175
- run(file = null, opts = {}) {
1176
- this.require(
1177
- !(file === null && opts.text === void 0),
1178
- "run() requires a document \u2014 pass a file or opts.text"
1179
- );
1180
- if (file !== null) {
1181
- const filename = file.name;
1182
- this.require(
1183
- typeof filename === "string" && filename.length > 0,
1184
- 'run() needs a named file so the server can detect its type \u2014 pass a File, e.g. new File([bytes], "invoice.pdf"), not a bare Blob'
1185
- );
1186
- }
1187
- return new Run(this.client, this.build(), file, opts.text ?? null);
1401
+ /** Replace an existing workflow's definition and return a handle you can `.run(file)` on. */
1402
+ async update(workflowId) {
1403
+ const res = await v2UpdateWorkflow({ client: this.client, path: { workflow_id: workflowId }, body: this.build() });
1404
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1405
+ return new Workflow(this.client, res.data.id, res.data.name);
1188
1406
  }
1189
1407
  };
1190
1408
 
1191
1409
  // src/client.ts
1410
+ var SDK_USER_AGENT = `anyformat-typescript-sdk-${package_default.version}`;
1192
1411
  var Anyformat = class {
1193
1412
  /** @internal */
1194
1413
  _client;
@@ -1196,13 +1415,72 @@ var Anyformat = class {
1196
1415
  this._client = createClient(
1197
1416
  createConfig({
1198
1417
  baseUrl: opts.baseUrl ?? "https://api.anyformat.ai",
1199
- auth: opts.apiKey
1418
+ auth: opts.apiKey,
1419
+ // Identifies the caller in our logs (Datadog facet). Plain HTTP callers
1420
+ // send no such header and are logged as origin "api". User-Agent is
1421
+ // ignored by browsers (forbidden header) but used by Node fetch.
1422
+ headers: {
1423
+ "x-anyformat-origin": "js-sdk",
1424
+ "User-Agent": SDK_USER_AGENT
1425
+ }
1200
1426
  })
1201
1427
  );
1202
1428
  }
1203
1429
  workflow(name, description) {
1204
1430
  return new WorkflowBuilder(this._client, name, description);
1205
1431
  }
1432
+ async listWorkflows(opts) {
1433
+ const res = await v2ListWorkflows({
1434
+ client: this._client,
1435
+ query: { page: opts?.page, page_size: opts?.pageSize, status: opts?.status }
1436
+ });
1437
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1438
+ return res.data.results.map((r) => new Workflow(this._client, r.id, r.name ?? ""));
1439
+ }
1440
+ async deleteWorkflow(workflowId) {
1441
+ const res = await v2DeleteWorkflow({
1442
+ client: this._client,
1443
+ path: { workflow_id: workflowId }
1444
+ });
1445
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1446
+ return workflowId;
1447
+ }
1448
+ /** Stage a document in a workflow without running it. Returns an `Upload`. */
1449
+ upload(workflowId, file = null) {
1450
+ return new Workflow(this._client, workflowId, "").upload(file);
1451
+ }
1452
+ /** Trigger extraction on already-uploaded files. Returns a pending `Run` per file_id. */
1453
+ async runWorkflow(workflowId, fileIds) {
1454
+ const runs = [];
1455
+ for (const fileId of fileIds) {
1456
+ const res = await v2RunFile({
1457
+ client: this._client,
1458
+ path: { workflow_id: workflowId, file_id: fileId }
1459
+ });
1460
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1461
+ runs.push(new Run(this._client, workflowId, res.data.id ?? fileId));
1462
+ }
1463
+ return runs;
1464
+ }
1465
+ /**
1466
+ * Parse a document with the platform's atomic `/parse` operation (fast lite
1467
+ * parse). Returns an opaque `jobId`; poll `getParseMarkdown(jobId)` for the
1468
+ * parsed markdown.
1469
+ */
1470
+ async parse(file) {
1471
+ const res = await v2Parse({ client: this._client, body: { file } });
1472
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1473
+ return res.data.job_id;
1474
+ }
1475
+ /**
1476
+ * Poll a parse job. Returns the parsed markdown once the job is done, or
1477
+ * `null` while it is still processing.
1478
+ */
1479
+ async getParseMarkdown(jobId) {
1480
+ const res = await v2ParseResult({ client: this._client, path: { job_id: jobId } });
1481
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1482
+ return res.data.status === "done" ? res.data.markdown ?? "" : null;
1483
+ }
1206
1484
  };
1207
1485
 
1208
1486
  // src/schema.ts
@@ -1228,15 +1506,24 @@ var Schema = {
1228
1506
  Anyformat,
1229
1507
  AnyformatError,
1230
1508
  BadRequest,
1509
+ BadRequestError,
1231
1510
  Forbidden,
1511
+ ForbiddenError,
1232
1512
  NotFound,
1513
+ NotFoundError,
1514
+ ParseView,
1233
1515
  RateLimited,
1516
+ RateLimitedError,
1234
1517
  Result,
1235
1518
  Run,
1236
1519
  SDKTimeout,
1520
+ SDKTimeoutError,
1237
1521
  Schema,
1238
1522
  ServerError,
1239
1523
  Unauthorized,
1524
+ UnauthorizedError,
1525
+ Upload,
1526
+ Workflow,
1240
1527
  WorkflowBuilder,
1241
1528
  WorkflowBuilderError
1242
1529
  });