You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat: add gcs_uri multipart RequestTransform plugin
First concrete consumer of the request body-transform framework (#256,
building on #257). Multi-modal endpoints (Whisper transcription, OCR)
expect multipart/form-data with a `url` field pointing at a signed object
URL, not the OpenAI-style JSON the pipeline produces. Producers can't put
raw media on the broker, so the queued payload carries a signed URL in a
`gcs_uri` field; this plugin rewrites the body at dispatch time.
The gcs_uri_multipart plugin (pkg/asyncworker/transform/gcsmultipart):
- Activates only when metadata.provider matches a configured provider AND
the payload has a non-empty gcs_uri; otherwise the default JSON path is
preserved unchanged.
- Transform: writes gcs_uri as a `url` form field (a plain field, not a
file upload), passes remaining fields through, drops gcs_uri, and
rejects a non-empty file_base64 as fatal/non-retryable.
- Validate: parses the signed URL expiry (V4 X-Goog-Date/X-Goog-Expires
or V2 Expires) and fails fatally before dispatch if it expires at or
before the request deadline, so the broker doesn't retry a doomed
request. Expiry parsing is best-effort and needs no new dependency.
Registered via init()/MustRegister and a blank import in cmd/main.go.
Configured under requestTransforms with a `providers` parameter. Adds a
README section documenting the flag, config shape, and plugin behavior.
Signed-off-by: Shimi Bandiel <shimib@google.com>
* feat(chart): wire request body-transform config
Address review feedback on #260: the chart had no way to configure the new
body-transform plugins. Add ap.transformConfig — rendered to transform-config.json
in the config ConfigMap and passed via --transform-config-file, mirroring how
worker-pools.json and pubsub-topics.json are mounted. Empty (the default) leaves
behavior unchanged.
Document the Helm path in the README and add helm-unittest coverage for the
set and empty cases.
Signed-off-by: Shimi Bandiel <shimib@google.com>
---------
Signed-off-by: Shimi Bandiel <shimib@google.com>
@@ -347,6 +349,46 @@ This per-pool topology provides complete backpressure and queue-level isolation:
347
349
348
350
Currently the only policy supported is `Random Robin Policy` which randomly picks messages from all queues configured for a given pool.
349
351
352
+
## Request Body Transforms
353
+
354
+
By default the worker dispatches the OpenAI-style JSON marshalled from a request's `payload`. Some providers need a different body shape at dispatch time — for example multi-modal endpoints (Whisper transcription, OCR) that expect `multipart/form-data` with a `url` field rather than JSON. Request body-transform plugins handle this without special-casing the worker: they rewrite the outgoing body and `Content-Type` based on per-message `metadata`, and the default JSON path is preserved byte-for-byte when no plugin applies.
355
+
356
+
Transforms are configured with `--transform-config-file`, pointing at a JSON object that groups plugins by direction:
357
+
358
+
```json
359
+
{
360
+
"requestTransforms": [
361
+
{
362
+
"name": "whisper-multipart",
363
+
"type": "gcs_uri_multipart",
364
+
"parameters": { "providers": ["whisper"] }
365
+
}
366
+
]
367
+
}
368
+
```
369
+
370
+
Each entry has a unique `name`, a registered plugin `type`, and opaque `parameters`. Unknown top-level fields are rejected. When the flag is empty, no transforms are loaded and behavior is unchanged.
371
+
372
+
With the Helm chart, set `ap.transformConfig` to this same object; the chart renders it to a config file and wires `--transform-config-file` automatically:
373
+
374
+
```yaml
375
+
ap:
376
+
transformConfig:
377
+
requestTransforms:
378
+
- name: "whisper-multipart"
379
+
type: "gcs_uri_multipart"
380
+
parameters:
381
+
providers: ["whisper"]
382
+
```
383
+
384
+
### `gcs_uri_multipart` plugin
385
+
386
+
Rewrites a JSON body into `multipart/form-data` for endpoints that take a signed object URL. Because producers can't put raw media bytes on the broker, the queued `payload` carries a signed URL (e.g. a GCS V4 signed URL) in a `gcs_uri` field.
387
+
388
+
- **Activation:** the message's `metadata.provider` must match one of the configured `providers`, and the `payload` must contain a non-empty `gcs_uri`. Otherwise the default JSON path is used unchanged.
389
+
- **Transform:** writes the `gcs_uri` value as a `url` form field (a plain field, not a file upload), passes the remaining payload fields through as form fields, and drops `gcs_uri`. A non-empty `file_base64` is rejected as a fatal, non-retryable error (inline media is not supported on this path).
390
+
- **Preflight:** parses the signed URL's expiry (V4 `X-Goog-Date` + `X-Goog-Expires`, or V2 `Expires`);if it expires at or before the message deadline, the request fails fatally before dispatch so the broker doesn't retry a request that cannot succeed.
391
+
350
392
## Retries
351
393
352
394
When a message processing has failed, either shedded or due to a server-side error, it will be scheduled for a retry (assuming the deadline has not passed).
0 commit comments