@brotu/ai 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,6 +55,10 @@ const ai = brotuClient({
55
55
  qwen: { apiKey: process.env.QWEN_API_KEY! },
56
56
  elevenlabs: { apiKey: process.env.ELEVENLABS_API_KEY! },
57
57
  },
58
+ webhook: {
59
+ url: "https://my.app/hooks/brotu",
60
+ secret: process.env.BROTU_WEBHOOK_SECRET,
61
+ },
58
62
  });
59
63
  ```
60
64
 
@@ -69,6 +73,7 @@ Every public call returns `{ data, error }`. `data` is unusable until you narrow
69
73
  | `ai.text` | `submit` / `generate` |
70
74
  | `ai.audio` | `submit` / `generate` |
71
75
  | `ai.jobs` | `poll` / `wait` |
76
+ | `ai.webhook` | `set` / `clear` / `get` |
72
77
  | `ai.estimateCost` | units, and USD when verified |
73
78
  | `ai.kling` | `motionControl`, `omniVideo`, `avatar`, `outpainting`, `imageOmni` |
74
79
  | `ai.google` | `omniVideo` (conversational refine) |
@@ -156,6 +161,41 @@ if (result === undefined && snapshot?.status === "pending") {
156
161
  }
157
162
  ```
158
163
 
164
+ ## Webhook
165
+
166
+ Register a URL on the client. When `generate`, `jobs.wait` or a terminal `jobs.poll` settles, the SDK POSTs the result there. A down hook never fails the generation.
167
+
168
+ ```ts
169
+ ai.webhook.set("https://my.app/hooks/brotu");
170
+ ai.webhook.clear();
171
+ ```
172
+
173
+ Per request, if one generation should go somewhere else:
174
+
175
+ ```ts
176
+ await ai.video.submit({
177
+ model: "kling/v2-6",
178
+ prompt: "a cat",
179
+ webhook: "https://my.app/hooks/this-one",
180
+ });
181
+ ```
182
+
183
+ The POST is JSON. Check `x-brotu-event` and, if you set a secret, `x-brotu-webhook-secret`.
184
+
185
+ ```json
186
+ {
187
+ "event": "generation.succeeded",
188
+ "jobId": "task-1",
189
+ "provider": "kling",
190
+ "model": "kling/v2-6",
191
+ "kind": "video",
192
+ "outputs": [{ "url": "https://…", "mimeType": "video/mp4" }],
193
+ "completedAt": "2026-08-16T12:00:00.000Z"
194
+ }
195
+ ```
196
+
197
+ `generation.failed` carries `error: { code, message }` and no outputs. Timeouts and routing errors do not fire the hook.
198
+
159
199
  Error codes: `unknown_model`, `missing_key`, `unsupported_provider`, `invalid_request`, `provider_error`, `timeout`.
160
200
 
161
201
  ## Motion control, avatars, omni
package/dist/client.d.ts CHANGED
@@ -2,6 +2,7 @@ import { getModel } from "./catalog";
2
2
  import type { AIModelConfig } from "./constants/model.types";
3
3
  import { type AIError, type Generation, type Result } from "./helpers/result";
4
4
  import { type Job, type JobSnapshot } from "./lib/jobs";
5
+ import { type WebhookConfig } from "./lib/webhook";
5
6
  import type { AudioGenerationParams, CostEstimate, GenerationOutput, GenerationParams, GenerationType, ImageGenerationParams, TextGenerationParams, VideoGenerationParams } from "./ports/content-generator.port";
6
7
  import { type AvatarInput, type ImageOmniInput, type MotionControlInput, type OmniVideoInput, type OutpaintingInput } from "./providers/kling.models";
7
8
  import type { BrotuAIOptions } from "./types";
@@ -35,6 +36,15 @@ export interface BrotuAI {
35
36
  timeoutMs?: number;
36
37
  }): Promise<Result<Generation>>;
37
38
  };
39
+ /**
40
+ * URL the client POSTs when a generation settles. Register at construction
41
+ * (`webhook:`) or here later. A down hook never fails the generation.
42
+ */
43
+ webhook: {
44
+ set(value: string | WebhookConfig): void;
45
+ clear(): void;
46
+ get(): WebhookConfig | undefined;
47
+ };
38
48
  /**
39
49
  * Google capabilities with no portable equivalent. Present only when a google
40
50
  * key is configured.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAMA,OAAO,EAEN,QAAQ,EAGR,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACN,KAAK,OAAO,EAGZ,KAAK,UAAU,EAEf,KAAK,MAAM,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEN,KAAK,GAAG,EACR,KAAK,WAAW,EAEhB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EACX,qBAAqB,EAErB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAEhB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,YAC5B,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,MAAM,CACG,CAAC;AAOX,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE;QACN,sEAAsE;QACtE,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,4EAA4E;QAC5E,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,KAAK,EAAE;QACN,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,IAAI,EAAE;QACL,MAAM,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACpE,CAAC;IACF,uDAAuD;IACvD,KAAK,EAAE;QACN,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,IAAI,EAAE;QACL,uDAAuD;QACvD,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7C,wDAAwD;QACxD,IAAI,CACH,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAC9B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KAC/B,CAAC;IACF;;;OAGG;IACH,MAAM,CAAC,EAAE;QACR;;;;;WAKG;QACH,SAAS,CACR,KAAK,EAAE,qBAAqB,GAAG;YAAE,qBAAqB,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,MAAM,CAAC;YAAE,aAAa,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,gBAAgB,EAAE,CAAA;SAAE,CAAC,CAAC,CAAC;KAC5E,CAAC;IACF;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACP,wDAAwD;QACxD,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,8EAA8E;QAC9E,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,6DAA6D;QAC7D,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,iDAAiD;QACjD,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,mDAAmD;QACnD,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;KACvD,CAAC;IACF,0DAA0D;IAC1D,MAAM,IAAI,aAAa,EAAE,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CACX,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;CACjC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAqX5D;AAED,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAMA,OAAO,EAEN,QAAQ,EAGR,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACN,KAAK,OAAO,EAGZ,KAAK,UAAU,EAEf,KAAK,MAAM,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEN,KAAK,GAAG,EACR,KAAK,WAAW,EAEhB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGN,KAAK,aAAa,EAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACX,qBAAqB,EAErB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAEhB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,YAC5B,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,MAAM,CACG,CAAC;AAOX,MAAM,WAAW,OAAO;IACvB,KAAK,EAAE;QACN,sEAAsE;QACtE,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,4EAA4E;QAC5E,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,KAAK,EAAE;QACN,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,IAAI,EAAE;QACL,MAAM,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACpE,CAAC;IACF,uDAAuD;IACvD,KAAK,EAAE;QACN,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACrE,CAAC;IACF,IAAI,EAAE;QACL,uDAAuD;QACvD,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QAC7C,wDAAwD;QACxD,IAAI,CACH,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAC9B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KAC/B,CAAC;IACF;;;OAGG;IACH,OAAO,EAAE;QACR,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;QACzC,KAAK,IAAI,IAAI,CAAC;QACd,GAAG,IAAI,aAAa,GAAG,SAAS,CAAC;KACjC,CAAC;IACF;;;OAGG;IACH,MAAM,CAAC,EAAE;QACR;;;;;WAKG;QACH,SAAS,CACR,KAAK,EAAE,qBAAqB,GAAG;YAAE,qBAAqB,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,MAAM,CAAC;YAAE,aAAa,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,gBAAgB,EAAE,CAAA;SAAE,CAAC,CAAC,CAAC;KAC5E,CAAC;IACF;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACP,wDAAwD;QACxD,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,8EAA8E;QAC9E,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,6DAA6D;QAC7D,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,iDAAiD;QACjD,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,mDAAmD;QACnD,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;KACvD,CAAC;IACF,0DAA0D;IAC1D,MAAM,IAAI,aAAa,EAAE,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CACX,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,gBAAgB,GACtB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;CACjC;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAgiB5D;AAED,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC"}
package/dist/index.cjs CHANGED
@@ -61,6 +61,7 @@ __export(index_exports, {
61
61
  QwenAdapter: () => QwenAdapter,
62
62
  brotuClient: () => brotuClient,
63
63
  createS3Storage: () => createS3Storage,
64
+ deliverWebhook: () => deliverWebhook,
64
65
  fail: () => fail,
65
66
  getAvailableModels: () => getAvailableModels,
66
67
  getModel: () => getModel,
@@ -72,7 +73,8 @@ __export(index_exports, {
72
73
  persistOutputs: () => persistOutputs,
73
74
  registerModels: () => registerModels,
74
75
  resetCatalog: () => resetCatalog,
75
- resolveProvider: () => resolveProvider
76
+ resolveProvider: () => resolveProvider,
77
+ resolveWebhook: () => resolveWebhook
76
78
  });
77
79
  module.exports = __toCommonJS(index_exports);
78
80
 
@@ -3534,6 +3536,45 @@ async function persistOutputs(storage, outputs) {
3534
3536
  );
3535
3537
  }
3536
3538
 
3539
+ // src/lib/webhook.ts
3540
+ function resolveWebhook(value) {
3541
+ if (!value) return void 0;
3542
+ if (typeof value === "string") {
3543
+ return isHttpUrl(value) ? { url: value } : void 0;
3544
+ }
3545
+ if (!isHttpUrl(value.url)) return void 0;
3546
+ return {
3547
+ url: value.url,
3548
+ secret: value.secret,
3549
+ headers: value.headers
3550
+ };
3551
+ }
3552
+ function isHttpUrl(value) {
3553
+ try {
3554
+ const parsed = new URL(value);
3555
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
3556
+ } catch {
3557
+ return false;
3558
+ }
3559
+ }
3560
+ async function deliverWebhook(config, payload) {
3561
+ try {
3562
+ await fetch(config.url, {
3563
+ method: "POST",
3564
+ headers: {
3565
+ "content-type": "application/json",
3566
+ "user-agent": "@brotu/ai",
3567
+ "x-brotu-event": payload.event,
3568
+ ...config.secret ? { "x-brotu-webhook-secret": config.secret } : {},
3569
+ ...config.headers
3570
+ },
3571
+ body: JSON.stringify(payload),
3572
+ signal: AbortSignal.timeout(1e4)
3573
+ });
3574
+ } catch {
3575
+ }
3576
+ }
3577
+
3537
3578
  // src/client.ts
3538
3579
  var NATIVE_PROVIDERS = [
3539
3580
  "byteplus",
@@ -3548,6 +3589,8 @@ function brotuClient(options) {
3548
3589
  throw new Error("brotuClient requires at least one provider API key.");
3549
3590
  }
3550
3591
  registerModels(options.models);
3592
+ let registeredWebhook = resolveWebhook(options.webhook);
3593
+ const notifiedJobIds = /* @__PURE__ */ new Set();
3551
3594
  const storage = options.storage ? createS3Storage(options.storage) : void 0;
3552
3595
  const shouldPersist = storage && options.storage?.persistOutputs !== false;
3553
3596
  const adapters = /* @__PURE__ */ new Map();
@@ -3637,6 +3680,31 @@ function brotuClient(options) {
3637
3680
  return adapter.generateAudio(params);
3638
3681
  return adapter.generateText(params);
3639
3682
  }
3683
+ function webhookFor(params) {
3684
+ return resolveWebhook(params?.webhook) ?? registeredWebhook;
3685
+ }
3686
+ async function notifySettled(input) {
3687
+ const config = webhookFor(input.params);
3688
+ if (!config) return;
3689
+ const jobId = input.job?.id;
3690
+ if (jobId) {
3691
+ if (notifiedJobIds.has(jobId)) return;
3692
+ notifiedJobIds.add(jobId);
3693
+ }
3694
+ const payload = {
3695
+ event: input.event,
3696
+ jobId,
3697
+ provider: input.provider ?? input.job?.provider,
3698
+ model: input.model ?? input.job?.model,
3699
+ kind: input.kind ?? input.job?.kind,
3700
+ outputs: input.outputs,
3701
+ error: input.error ? { code: input.error.code, message: input.error.message } : void 0,
3702
+ metadata: input.metadata ?? input.job?.metadata,
3703
+ processingTimeMs: input.processingTimeMs,
3704
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
3705
+ };
3706
+ await deliverWebhook(config, payload);
3707
+ }
3640
3708
  async function toGeneration(raw, metadata) {
3641
3709
  if (!raw.success) {
3642
3710
  return fail({
@@ -3659,15 +3727,50 @@ function brotuClient(options) {
3659
3727
  const routed = route(params.model);
3660
3728
  if (routed.error) return fail(routed.error);
3661
3729
  try {
3662
- return await toGeneration(
3730
+ const result = await toGeneration(
3663
3731
  await generateWith(routed.data.adapter, kind, params),
3664
3732
  params.metadata
3665
3733
  );
3734
+ if (result.error) {
3735
+ await notifySettled({
3736
+ event: "generation.failed",
3737
+ params,
3738
+ kind,
3739
+ provider: routed.data.provider,
3740
+ model: routed.data.model,
3741
+ error: result.error,
3742
+ metadata: params.metadata
3743
+ });
3744
+ return result;
3745
+ }
3746
+ await notifySettled({
3747
+ event: "generation.succeeded",
3748
+ params,
3749
+ kind,
3750
+ provider: result.data.provider,
3751
+ model: result.data.model,
3752
+ outputs: result.data.outputs,
3753
+ metadata: result.data.metadata,
3754
+ processingTimeMs: result.data.processingTimeMs
3755
+ });
3756
+ return result;
3666
3757
  } catch (error) {
3667
- return failFrom("provider_error", error, {
3758
+ const failed = failFrom("provider_error", error, {
3668
3759
  provider: routed.data.provider,
3669
3760
  model: routed.data.model
3670
3761
  });
3762
+ if (failed.error) {
3763
+ await notifySettled({
3764
+ event: "generation.failed",
3765
+ params,
3766
+ kind,
3767
+ provider: routed.data.provider,
3768
+ model: routed.data.model,
3769
+ error: failed.error,
3770
+ metadata: params.metadata
3771
+ });
3772
+ }
3773
+ return failed;
3671
3774
  }
3672
3775
  }
3673
3776
  async function submit(kind, params) {
@@ -3686,18 +3789,40 @@ function brotuClient(options) {
3686
3789
  () => generateWith(routed.data.adapter, kind, params)
3687
3790
  );
3688
3791
  if (!raw.success) {
3689
- return fail({
3792
+ const error = {
3690
3793
  code: "provider_error",
3691
3794
  message: raw.error ?? "The provider rejected the request.",
3692
3795
  provider: raw.provider,
3693
3796
  model: raw.model
3797
+ };
3798
+ await notifySettled({
3799
+ event: "generation.failed",
3800
+ params,
3801
+ kind,
3802
+ provider: raw.provider,
3803
+ model: raw.model,
3804
+ error,
3805
+ metadata: params.metadata
3694
3806
  });
3807
+ return fail(error);
3695
3808
  }
3696
- return ok({
3809
+ const job = {
3697
3810
  ...base,
3698
3811
  id: `inline-${base.model}-${base.submittedAt}`,
3699
3812
  result: raw
3813
+ };
3814
+ await notifySettled({
3815
+ event: "generation.succeeded",
3816
+ params,
3817
+ job,
3818
+ kind,
3819
+ provider: raw.provider,
3820
+ model: raw.model,
3821
+ outputs: raw.outputs,
3822
+ metadata: params.metadata,
3823
+ processingTimeMs: raw.processingTimeMs
3700
3824
  });
3825
+ return ok(job);
3701
3826
  } catch (error) {
3702
3827
  if (!isPendingJob(error)) {
3703
3828
  return failFrom("provider_error", error, {
@@ -3712,11 +3837,54 @@ function brotuClient(options) {
3712
3837
  });
3713
3838
  }
3714
3839
  }
3840
+ async function notifyFromSnapshot(job, snapshot) {
3841
+ if (snapshot.status === "succeeded" && snapshot.result) {
3842
+ await notifySettled({
3843
+ event: "generation.succeeded",
3844
+ params: job.params,
3845
+ job,
3846
+ outputs: snapshot.result.outputs,
3847
+ provider: snapshot.result.provider,
3848
+ model: snapshot.result.model,
3849
+ processingTimeMs: snapshot.result.processingTimeMs,
3850
+ metadata: job.metadata
3851
+ });
3852
+ return;
3853
+ }
3854
+ if (snapshot.status === "failed") {
3855
+ await notifySettled({
3856
+ event: "generation.failed",
3857
+ params: job.params,
3858
+ job,
3859
+ error: {
3860
+ code: "provider_error",
3861
+ message: snapshot.error ?? "The job failed.",
3862
+ provider: job.provider,
3863
+ model: job.model
3864
+ },
3865
+ metadata: job.metadata
3866
+ });
3867
+ }
3868
+ }
3869
+ async function finalizeSnapshot(job, snapshot) {
3870
+ if (snapshot.status === "succeeded" && snapshot.result) {
3871
+ const persisted = await toGeneration(snapshot.result, job.metadata);
3872
+ if (!persisted.error) {
3873
+ snapshot = {
3874
+ status: "succeeded",
3875
+ result: {
3876
+ ...snapshot.result,
3877
+ outputs: persisted.data.outputs
3878
+ }
3879
+ };
3880
+ }
3881
+ }
3882
+ await notifyFromSnapshot(job, snapshot);
3883
+ return snapshot;
3884
+ }
3715
3885
  async function pollJob(job) {
3716
3886
  if (job.result) {
3717
- return ok(
3718
- job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }
3719
- );
3887
+ return ok(await finalizeSnapshot(job, job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }));
3720
3888
  }
3721
3889
  const routed = route(job.model);
3722
3890
  if (routed.error) return fail(routed.error);
@@ -3729,7 +3897,7 @@ function brotuClient(options) {
3729
3897
  });
3730
3898
  }
3731
3899
  try {
3732
- return ok(await adapter.completeJob(job));
3900
+ return ok(await finalizeSnapshot(job, await adapter.completeJob(job)));
3733
3901
  } catch (error) {
3734
3902
  return failFrom("provider_error", error, {
3735
3903
  provider: job.provider,
@@ -3798,6 +3966,17 @@ function brotuClient(options) {
3798
3966
  submit: (params) => submit("audio", params),
3799
3967
  generate: (params) => generate("audio", params)
3800
3968
  },
3969
+ webhook: {
3970
+ set(value) {
3971
+ registeredWebhook = resolveWebhook(value);
3972
+ },
3973
+ clear() {
3974
+ registeredWebhook = void 0;
3975
+ },
3976
+ get() {
3977
+ return registeredWebhook;
3978
+ }
3979
+ },
3801
3980
  jobs: {
3802
3981
  poll: pollJob,
3803
3982
  wait: async (job, waitOptions) => {
@@ -3807,7 +3986,13 @@ function brotuClient(options) {
3807
3986
  if (polled.error) return fail(polled.error);
3808
3987
  const snapshot = polled.data;
3809
3988
  if (snapshot.status === "succeeded" && snapshot.result) {
3810
- return toGeneration(snapshot.result, job.metadata);
3989
+ return ok({
3990
+ outputs: snapshot.result.outputs,
3991
+ provider: snapshot.result.provider,
3992
+ model: snapshot.result.model,
3993
+ processingTimeMs: snapshot.result.processingTimeMs,
3994
+ metadata: job.metadata
3995
+ });
3811
3996
  }
3812
3997
  if (snapshot.status === "failed") {
3813
3998
  return fail({
@@ -3877,6 +4062,7 @@ function brotuClient(options) {
3877
4062
  QwenAdapter,
3878
4063
  brotuClient,
3879
4064
  createS3Storage,
4065
+ deliverWebhook,
3880
4066
  fail,
3881
4067
  getAvailableModels,
3882
4068
  getModel,
@@ -3888,5 +4074,6 @@ function brotuClient(options) {
3888
4074
  persistOutputs,
3889
4075
  registerModels,
3890
4076
  resetCatalog,
3891
- resolveProvider
4077
+ resolveProvider,
4078
+ resolveWebhook
3892
4079
  });
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export type * from "./constants/model.types";
17
17
  export { type AIError, type AIErrorCode, fail, type Generation, ok, type Result, } from "./helpers/result";
18
18
  export { isPendingJob, type Job, type JobSnapshot, type JobStatus, PendingJob, } from "./lib/jobs";
19
19
  export { createS3Storage, persistOutputs, type S3StorageConfig, type Storage, } from "./lib/storage";
20
+ export { deliverWebhook, resolveWebhook, type WebhookConfig, type WebhookEvent, type WebhookEventName, } from "./lib/webhook";
20
21
  export type * from "./ports/content-generator.port";
21
22
  export { BYTEPLUS_CATALOG, BYTEPLUS_MODELS, type BytePlusModelBinding, } from "./providers/byteplus.models";
22
23
  export { ELEVENLABS_CATALOG, ELEVENLABS_MODELS, ELEVENLABS_OUTPUT_FORMATS, } from "./providers/elevenlabs.models";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,YAAY,EACX,wBAAwB,EACxB,eAAe,GACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EACN,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,IAAI,EACJ,KAAK,UAAU,EACf,EAAE,EACF,KAAK,MAAM,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,YAAY,EACZ,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,eAAe,EACf,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,OAAO,GACZ,MAAM,eAAe,CAAC;AACvB,mBAAmB,gCAAgC,CAAC;AACpD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GACnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACrB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,YAAY,EACX,wBAAwB,EACxB,eAAe,GACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EACN,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,eAAe,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,mBAAmB,yBAAyB,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,IAAI,EACJ,KAAK,UAAU,EACf,EAAE,EACF,KAAK,MAAM,GACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,YAAY,EACZ,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,eAAe,EACf,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,OAAO,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,cAAc,EACd,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACrB,MAAM,eAAe,CAAC;AACvB,mBAAmB,gCAAgC,CAAC;AACpD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACN,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GACnB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACrB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -2256,6 +2256,45 @@ async function persistOutputs(storage, outputs) {
2256
2256
  );
2257
2257
  }
2258
2258
 
2259
+ // src/lib/webhook.ts
2260
+ function resolveWebhook(value) {
2261
+ if (!value) return void 0;
2262
+ if (typeof value === "string") {
2263
+ return isHttpUrl(value) ? { url: value } : void 0;
2264
+ }
2265
+ if (!isHttpUrl(value.url)) return void 0;
2266
+ return {
2267
+ url: value.url,
2268
+ secret: value.secret,
2269
+ headers: value.headers
2270
+ };
2271
+ }
2272
+ function isHttpUrl(value) {
2273
+ try {
2274
+ const parsed = new URL(value);
2275
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
2276
+ } catch {
2277
+ return false;
2278
+ }
2279
+ }
2280
+ async function deliverWebhook(config, payload) {
2281
+ try {
2282
+ await fetch(config.url, {
2283
+ method: "POST",
2284
+ headers: {
2285
+ "content-type": "application/json",
2286
+ "user-agent": "@brotu/ai",
2287
+ "x-brotu-event": payload.event,
2288
+ ...config.secret ? { "x-brotu-webhook-secret": config.secret } : {},
2289
+ ...config.headers
2290
+ },
2291
+ body: JSON.stringify(payload),
2292
+ signal: AbortSignal.timeout(1e4)
2293
+ });
2294
+ } catch {
2295
+ }
2296
+ }
2297
+
2259
2298
  // src/client.ts
2260
2299
  var NATIVE_PROVIDERS = [
2261
2300
  "byteplus",
@@ -2270,6 +2309,8 @@ function brotuClient(options) {
2270
2309
  throw new Error("brotuClient requires at least one provider API key.");
2271
2310
  }
2272
2311
  registerModels(options.models);
2312
+ let registeredWebhook = resolveWebhook(options.webhook);
2313
+ const notifiedJobIds = /* @__PURE__ */ new Set();
2273
2314
  const storage = options.storage ? createS3Storage(options.storage) : void 0;
2274
2315
  const shouldPersist = storage && options.storage?.persistOutputs !== false;
2275
2316
  const adapters = /* @__PURE__ */ new Map();
@@ -2359,6 +2400,31 @@ function brotuClient(options) {
2359
2400
  return adapter.generateAudio(params);
2360
2401
  return adapter.generateText(params);
2361
2402
  }
2403
+ function webhookFor(params) {
2404
+ return resolveWebhook(params?.webhook) ?? registeredWebhook;
2405
+ }
2406
+ async function notifySettled(input) {
2407
+ const config = webhookFor(input.params);
2408
+ if (!config) return;
2409
+ const jobId = input.job?.id;
2410
+ if (jobId) {
2411
+ if (notifiedJobIds.has(jobId)) return;
2412
+ notifiedJobIds.add(jobId);
2413
+ }
2414
+ const payload = {
2415
+ event: input.event,
2416
+ jobId,
2417
+ provider: input.provider ?? input.job?.provider,
2418
+ model: input.model ?? input.job?.model,
2419
+ kind: input.kind ?? input.job?.kind,
2420
+ outputs: input.outputs,
2421
+ error: input.error ? { code: input.error.code, message: input.error.message } : void 0,
2422
+ metadata: input.metadata ?? input.job?.metadata,
2423
+ processingTimeMs: input.processingTimeMs,
2424
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
2425
+ };
2426
+ await deliverWebhook(config, payload);
2427
+ }
2362
2428
  async function toGeneration(raw, metadata) {
2363
2429
  if (!raw.success) {
2364
2430
  return fail({
@@ -2381,15 +2447,50 @@ function brotuClient(options) {
2381
2447
  const routed = route(params.model);
2382
2448
  if (routed.error) return fail(routed.error);
2383
2449
  try {
2384
- return await toGeneration(
2450
+ const result = await toGeneration(
2385
2451
  await generateWith(routed.data.adapter, kind, params),
2386
2452
  params.metadata
2387
2453
  );
2454
+ if (result.error) {
2455
+ await notifySettled({
2456
+ event: "generation.failed",
2457
+ params,
2458
+ kind,
2459
+ provider: routed.data.provider,
2460
+ model: routed.data.model,
2461
+ error: result.error,
2462
+ metadata: params.metadata
2463
+ });
2464
+ return result;
2465
+ }
2466
+ await notifySettled({
2467
+ event: "generation.succeeded",
2468
+ params,
2469
+ kind,
2470
+ provider: result.data.provider,
2471
+ model: result.data.model,
2472
+ outputs: result.data.outputs,
2473
+ metadata: result.data.metadata,
2474
+ processingTimeMs: result.data.processingTimeMs
2475
+ });
2476
+ return result;
2388
2477
  } catch (error) {
2389
- return failFrom("provider_error", error, {
2478
+ const failed = failFrom("provider_error", error, {
2390
2479
  provider: routed.data.provider,
2391
2480
  model: routed.data.model
2392
2481
  });
2482
+ if (failed.error) {
2483
+ await notifySettled({
2484
+ event: "generation.failed",
2485
+ params,
2486
+ kind,
2487
+ provider: routed.data.provider,
2488
+ model: routed.data.model,
2489
+ error: failed.error,
2490
+ metadata: params.metadata
2491
+ });
2492
+ }
2493
+ return failed;
2393
2494
  }
2394
2495
  }
2395
2496
  async function submit(kind, params) {
@@ -2408,18 +2509,40 @@ function brotuClient(options) {
2408
2509
  () => generateWith(routed.data.adapter, kind, params)
2409
2510
  );
2410
2511
  if (!raw.success) {
2411
- return fail({
2512
+ const error = {
2412
2513
  code: "provider_error",
2413
2514
  message: raw.error ?? "The provider rejected the request.",
2414
2515
  provider: raw.provider,
2415
2516
  model: raw.model
2517
+ };
2518
+ await notifySettled({
2519
+ event: "generation.failed",
2520
+ params,
2521
+ kind,
2522
+ provider: raw.provider,
2523
+ model: raw.model,
2524
+ error,
2525
+ metadata: params.metadata
2416
2526
  });
2527
+ return fail(error);
2417
2528
  }
2418
- return ok({
2529
+ const job = {
2419
2530
  ...base,
2420
2531
  id: `inline-${base.model}-${base.submittedAt}`,
2421
2532
  result: raw
2533
+ };
2534
+ await notifySettled({
2535
+ event: "generation.succeeded",
2536
+ params,
2537
+ job,
2538
+ kind,
2539
+ provider: raw.provider,
2540
+ model: raw.model,
2541
+ outputs: raw.outputs,
2542
+ metadata: params.metadata,
2543
+ processingTimeMs: raw.processingTimeMs
2422
2544
  });
2545
+ return ok(job);
2423
2546
  } catch (error) {
2424
2547
  if (!isPendingJob(error)) {
2425
2548
  return failFrom("provider_error", error, {
@@ -2434,11 +2557,54 @@ function brotuClient(options) {
2434
2557
  });
2435
2558
  }
2436
2559
  }
2560
+ async function notifyFromSnapshot(job, snapshot) {
2561
+ if (snapshot.status === "succeeded" && snapshot.result) {
2562
+ await notifySettled({
2563
+ event: "generation.succeeded",
2564
+ params: job.params,
2565
+ job,
2566
+ outputs: snapshot.result.outputs,
2567
+ provider: snapshot.result.provider,
2568
+ model: snapshot.result.model,
2569
+ processingTimeMs: snapshot.result.processingTimeMs,
2570
+ metadata: job.metadata
2571
+ });
2572
+ return;
2573
+ }
2574
+ if (snapshot.status === "failed") {
2575
+ await notifySettled({
2576
+ event: "generation.failed",
2577
+ params: job.params,
2578
+ job,
2579
+ error: {
2580
+ code: "provider_error",
2581
+ message: snapshot.error ?? "The job failed.",
2582
+ provider: job.provider,
2583
+ model: job.model
2584
+ },
2585
+ metadata: job.metadata
2586
+ });
2587
+ }
2588
+ }
2589
+ async function finalizeSnapshot(job, snapshot) {
2590
+ if (snapshot.status === "succeeded" && snapshot.result) {
2591
+ const persisted = await toGeneration(snapshot.result, job.metadata);
2592
+ if (!persisted.error) {
2593
+ snapshot = {
2594
+ status: "succeeded",
2595
+ result: {
2596
+ ...snapshot.result,
2597
+ outputs: persisted.data.outputs
2598
+ }
2599
+ };
2600
+ }
2601
+ }
2602
+ await notifyFromSnapshot(job, snapshot);
2603
+ return snapshot;
2604
+ }
2437
2605
  async function pollJob(job) {
2438
2606
  if (job.result) {
2439
- return ok(
2440
- job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }
2441
- );
2607
+ return ok(await finalizeSnapshot(job, job.result.success ? { status: "succeeded", result: job.result } : { status: "failed", error: job.result.error }));
2442
2608
  }
2443
2609
  const routed = route(job.model);
2444
2610
  if (routed.error) return fail(routed.error);
@@ -2451,7 +2617,7 @@ function brotuClient(options) {
2451
2617
  });
2452
2618
  }
2453
2619
  try {
2454
- return ok(await adapter.completeJob(job));
2620
+ return ok(await finalizeSnapshot(job, await adapter.completeJob(job)));
2455
2621
  } catch (error) {
2456
2622
  return failFrom("provider_error", error, {
2457
2623
  provider: job.provider,
@@ -2520,6 +2686,17 @@ function brotuClient(options) {
2520
2686
  submit: (params) => submit("audio", params),
2521
2687
  generate: (params) => generate("audio", params)
2522
2688
  },
2689
+ webhook: {
2690
+ set(value) {
2691
+ registeredWebhook = resolveWebhook(value);
2692
+ },
2693
+ clear() {
2694
+ registeredWebhook = void 0;
2695
+ },
2696
+ get() {
2697
+ return registeredWebhook;
2698
+ }
2699
+ },
2523
2700
  jobs: {
2524
2701
  poll: pollJob,
2525
2702
  wait: async (job, waitOptions) => {
@@ -2529,7 +2706,13 @@ function brotuClient(options) {
2529
2706
  if (polled.error) return fail(polled.error);
2530
2707
  const snapshot = polled.data;
2531
2708
  if (snapshot.status === "succeeded" && snapshot.result) {
2532
- return toGeneration(snapshot.result, job.metadata);
2709
+ return ok({
2710
+ outputs: snapshot.result.outputs,
2711
+ provider: snapshot.result.provider,
2712
+ model: snapshot.result.model,
2713
+ processingTimeMs: snapshot.result.processingTimeMs,
2714
+ metadata: job.metadata
2715
+ });
2533
2716
  }
2534
2717
  if (snapshot.status === "failed") {
2535
2718
  return fail({
@@ -2598,6 +2781,7 @@ export {
2598
2781
  QwenAdapter,
2599
2782
  brotuClient,
2600
2783
  createS3Storage,
2784
+ deliverWebhook,
2601
2785
  fail,
2602
2786
  getAvailableModels,
2603
2787
  getModel,
@@ -2609,5 +2793,6 @@ export {
2609
2793
  persistOutputs,
2610
2794
  registerModels,
2611
2795
  resetCatalog,
2612
- resolveProvider
2796
+ resolveProvider,
2797
+ resolveWebhook
2613
2798
  };
@@ -0,0 +1,28 @@
1
+ import type { AIError } from "../helpers/result";
2
+ import type { GenerationOutput, GenerationType } from "../ports/content-generator.port";
3
+ export type WebhookEventName = "generation.succeeded" | "generation.failed";
4
+ export interface WebhookConfig {
5
+ url: string;
6
+ /** Sent as `x-brotu-webhook-secret` so your endpoint can reject strangers. */
7
+ secret?: string;
8
+ headers?: Record<string, string>;
9
+ }
10
+ export interface WebhookEvent {
11
+ event: WebhookEventName;
12
+ jobId?: string;
13
+ provider?: string;
14
+ model?: string;
15
+ kind?: GenerationType;
16
+ outputs?: GenerationOutput[];
17
+ error?: Pick<AIError, "code" | "message">;
18
+ metadata?: Record<string, string>;
19
+ processingTimeMs?: number;
20
+ completedAt: string;
21
+ }
22
+ export declare function resolveWebhook(value: string | WebhookConfig | undefined): WebhookConfig | undefined;
23
+ /**
24
+ * POST the settled generation at the registered URL. Failures are swallowed:
25
+ * a down hook must not fail the call the user is waiting on.
26
+ */
27
+ export declare function deliverWebhook(config: WebhookConfig, payload: WebhookEvent): Promise<void>;
28
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/lib/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EACX,gBAAgB,EAChB,cAAc,EACd,MAAM,iCAAiC,CAAC;AAEzC,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAC7B,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,GACvC,aAAa,GAAG,SAAS,CAW3B;AAWD;;;GAGG;AACH,wBAAsB,cAAc,CACnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -1,4 +1,5 @@
1
1
  import type { BillingUnit } from "../constants/model.types";
2
+ import type { WebhookConfig } from "../lib/webhook";
2
3
  /** Known values, kept open because providers disagree on spelling and units. */
3
4
  export type VideoResolution = "480p" | "580p" | "720p" | "768p" | "1080p" | "4k" | "480P" | "720P" | "1080P" | (string & {});
4
5
  export type ImageResolution = "1K" | "2K" | "4K" | (string & {});
@@ -23,6 +24,11 @@ export interface CommonGenerationParams {
23
24
  * overrides what the adapter would otherwise send.
24
25
  */
25
26
  providerOptions?: Record<string, Record<string, unknown>>;
27
+ /**
28
+ * Override the client-level webhook for this request. Same shape: a URL
29
+ * string or `{ url, secret }`. Not sent to the provider.
30
+ */
31
+ webhook?: string | WebhookConfig;
26
32
  }
27
33
  /**
28
34
  * Shared image parameters.
@@ -1 +1 @@
1
- {"version":3,"file":"content-generator.port.d.ts","sourceRoot":"","sources":["../../src/ports/content-generator.port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-generator.port.d.ts","sourceRoot":"","sources":["../../src/ports/content-generator.port.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AIModelConfig } from "./constants/model.types";
2
2
  import type { S3StorageConfig } from "./lib/storage";
3
+ import type { WebhookConfig } from "./lib/webhook";
3
4
  export interface ProviderConfig {
4
5
  apiKey: string;
5
6
  /** Defaults to the known base URL for the provider, when there is one. */
@@ -45,6 +46,12 @@ export interface BrotuAIOptions {
45
46
  elevenLabsVoiceId?: string;
46
47
  /** Extra models, or overrides of built-in ones, merged by id. */
47
48
  models?: AIModelConfig[];
49
+ /**
50
+ * URL (or `{ url, secret }`) the client POSTs when a generation settles.
51
+ * Fires from `generate`, `jobs.wait`, and a terminal `jobs.poll`. Change it
52
+ * later with `ai.webhook.set`. A per-request `webhook` on the params wins.
53
+ */
54
+ webhook?: string | WebhookConfig;
48
55
  }
49
56
  /** Resolved per-request routing: which host and key serve this model. */
50
57
  export interface ResolvedProvider {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC1D;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,CACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC1D;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,CACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;CACjC;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brotu/ai",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "exports": {