fix: add AI Gateway runtime steps - #2326
Conversation
This pull request is waiting on issue triageA referenced issue is filed but not yet marked 'accepted'.
Nothing to do on your side. The issue is filed and is waiting for a maintainer to triage it and apply the |
About the
|
suisuss
left a comment
There was a problem hiding this comment.
What this changes
Adds plugins/ai-gateway/credentials.ts, a shared transport at steps/ai-gateway-core.ts posting to https://ai-gateway.vercel.sh/v1 through safeFetch, portable cores for text and image, and the two app entry points with "use step", fetchCredentials outside runPluginStep, maxRetries = 0 and _integrationType. Then it adds "ai-gateway" to plugins/plugin-allowlist.json:5 and the matching import "./ai-gateway" in plugins/index.ts - which is generator output, not a hand edit, since scripts/discover-plugins.ts:331-332 filters by the allowlist before writing that file. Two test files, 9 cases.
The plugin's index.ts already exists on staging with both actions declared. This is the flip that makes it registered and reachable: AI Gateway appears in the builder palette, gets a Connections entry, and its two actions become dispatchable.
Does it match the description
Matches. The safeFetch claim holds - .github/workflows/pr-checks.yml:142-148 greps plugins/ for fetch(, axios and http.request, and no new step file imports ai, @ai-sdk/* or openai. I also checked the two invariant tests the allowlist entry newly brings this plugin into scope of, and both pass: credential-map-coverage collects AI_GATEWAY_API_KEY from the generated map, and features-egress-invariant is satisfied by egress: "fixed-host" with no type: "url" field.
Blocking
-
The issue gate is red and it is mine, not yours.
check-issue-linkfails because #2316 is not labelledaccepted. You referenced it correctly and the reference is right; nothing about that is on you, and refiling would not help.I have to be straight with you about why it is not accepted, because it affects whether this diff is the right diff. I triaged #2316 today and put it in
needs-discussion. The actions do not 404 because the step files are missing - they 404 becauseai-gatewayis not in the allowlist, so the plugin never registers. Your PR is the first thing to identify that correctly, and adding the allowlist entry is exactly right for the direction it assumes. What is unsettled is the direction:b83e96e0deleted this plugin as "unneeded" in December,a71eb405broughtindex.tsback as a merge artefact the day after the allowlist was created, and it has never appeared in any of the allowlist's nine revisions. So the open question is whether to build the steps or delete the stale declaration, and the second answer would make this diff moot. Against it,627716bfand1fb8e2dcboth deliberately maintained this file rather than removing it.I am settling that with the core team now and will come back on #2316 shortly. I would not put more work into this until I do - not because the work is wrong, but because I do not want you polishing something I might ask to delete.
One more thing you should know now rather than at merge: @Dami904 filed #2316 and commented that they were taking it. Neither of you did anything wrong, and I would rather surface the collision than let one of you find out at review. Sort it between you, and whichever way it goes, this diff is the better-informed of the two starting points.
-
plugins/ai-gateway/index.ts:38-43- the plugin declaresdependencies: { ai, openai, "@google/genai", zod }, andopenaiand@google/genaiare not in the rootpackage.json.getAllDependencies()(plugins/registry.ts:525-532) aggregates that field across registered integrations, which this PR makes ai-gateway for the first time. -> Every exported standalone workflow project getsopenai@^6.8.0and@google/genai@^1.28.0in its generatedpackage.json, pulled from npm, for code that imports neither. -> Delete thedependenciesblock. The safeFetch rewrite is what makes it removable, so it belongs in this PR.
Mechanical - actionable as-is
-
plugins/ai-gateway/steps/ai-gateway-core.ts:38-40- the nested provider-message branch has no.trim()guard, unlike:30and:34. A body of{"error":{"message":""}}returns"", which is not nullish, so the?? \... HTTP ${status}`fallback at:72does not fire and the user sees a blank error with no status code. Add the same&& data.error.message.trim()` guard. -
generate-text-core.ts:72-79-parseSchemaFieldsreturnsundefinedon aJSON.parsefailure, andschemaForFieldsfolds that into the same branch as an empty array, so a malformed schema string is reported as "Schema must contain at least one field." Give the parse failure its own message. -
generate-text-core.ts:199andgenerate-image-core.ts:55-(input.aiModel ?? DEFAULT).trim()has notypeof === "string"guard, while the prompt two lines above does. These cross a JSON config boundary, so the TS type is not a runtime guarantee, and a non-string throws aTypeErrorout of the step instead of returning a typedUSERfailure. Mirror the prompt's guard. -
generate-image-core.ts:55usesimageModelraw whilegenerate-text-core.ts:213runsaiModelthroughnormaliseModel. Harmless for the five current options, which all carry a slash, but the two paths will drift. -
docs/plugins/has a page for every allowlisted plugin anddocs/plugins/_meta.tsa nav entry for each. This adds neither, so ai-gateway would ship as the only enabled plugin with no docs. Nothing enforces it in CI; it should still land before the plugin is enabled. -
plugins/ai-gateway/test.tson staging returns{status: "success"}unconditionally, so the Connections "Test Connection" button will report success for a garbage key. Not your code, but this PR is what makes it user-visible - worth fixing in the same change. -
Untested branches worth covering, all reachable: the missing-API-key path (
generate-text-core.ts:186-191,generate-image-core.ts:42-47) - every test stubsfetchCredentialswith a key;safeFetchthrowing (ai-gateway-core.ts:90-97); a non-JSON response body (:43-49); both bare-name branches ofnormaliseModel(:55-63), since both tests pass fully-qualified models; and nested-object, array-of-object, duplicate-name and unsupported-type schema recursion (generate-text-core.ts:97,109-135), where only a single flat string field is covered.
With the team
- Whether
ai-gatewayshould be enabled at all, or its declaration deleted. I am weighing the allowlist history and the December removal against two later commits that deliberately kept the file, plus the fact thatdocs/api/integrations.md:22advertises the type. The cost of guessing wrong either way is a wasted round for you. I am discussing it with the core team now and will come back on #2316 shortly. Nothing here is blocked on you.
Verdict
Changes requested - the stale dependencies block ships two uninstalled packages into every exported project, and the direction question above needs settling before more work goes in.
The step code itself is the right shape: "use step", fetchCredentials resolved outside runPluginStep, _integrationType exported, the core-file split, and a closed union for the request path so nothing user-controlled reaches the destination host. fixed-host is the correct classification for what this actually does.
Splitting this would be wrong and I want to say so explicitly rather than leave it open: plugins/ai-gateway/index.ts declares both actions with their stepImportPath, and generateStepRegistry emits an import for every action of every registered plugin, so shipping the allowlist entry with only one step file produces a registry importing a module that does not exist. The allowlist entry, plugins/index.ts and both step files are one atomic unit.
tests/unit/ai-gateway-steps.test.ts earns its place - it asserts the real URL, headers and serialized body, the generated JSON Schema structurally, and the error mapping through the real requestAiGateway. ai-gateway-registry.test.ts executes none of the new logic, which is fine for what it is: it proves the registry-to-file wiring holds.
Summary
Fixes #2316 by adding the missing runtime step implementations for the AI Gateway
generate-textandgenerate-imageactions.generate-text/generate-imagestep files and core logicfetchCredentialsfor the existing BYOKapiKeyintegration fieldsafeFetch({ plugin: "ai-gateway" })ai-gatewayin the generated plugin index/allowlistValidation
vitest run tests/unit/ai-gateway-steps.test.ts tests/unit/ai-gateway-registry.test.ts— 12/12 PASStsc --noEmit— PASSgit diff --check— PASSdiscover-pluginson this Windows checkout reaches AI Gateway discovery successfully, then hits the repository's pre-existing Windows ESM absolute-path issue while importingprotocols/*.ts(ERR_UNSUPPORTED_ESM_URL_SCHEME, drive-letter path). That failure is outside these AI Gateway files; the targeted registry tests prove both new runtime modules resolve and register correctly.No API keys or paid external calls were used during validation.