Skip to content

fix: add AI Gateway runtime steps - #2326

Open
franklincg wants to merge 1 commit into
KeeperHub:stagingfrom
franklincg:fix/ai-gateway-runtime-steps
Open

fix: add AI Gateway runtime steps#2326
franklincg wants to merge 1 commit into
KeeperHub:stagingfrom
franklincg:fix/ai-gateway-runtime-steps

Conversation

@franklincg

Copy link
Copy Markdown

Summary

Fixes #2316 by adding the missing runtime step implementations for the AI Gateway generate-text and generate-image actions.

  • adds generate-text / generate-image step files and core logic
  • uses fetchCredentials for the existing BYOK apiKey integration field
  • routes outbound AI Gateway traffic through safeFetch({ plugin: "ai-gateway" })
  • supports text and schema-backed object output
  • returns base64 image output in the shape already declared by the plugin
  • registers ai-gateway in the generated plugin index/allowlist
  • adds focused mocked unit coverage without making external AI calls

Validation

  • vitest run tests/unit/ai-gateway-steps.test.ts tests/unit/ai-gateway-registry.test.ts12/12 PASS
  • tsc --noEmitPASS
  • git diff --checkPASS

discover-plugins on this Windows checkout reaches AI Gateway discovery successfully, then hits the repository's pre-existing Windows ESM absolute-path issue while importing protocols/*.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.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

This pull request is waiting on issue triage

A referenced issue is filed but not yet marked 'accepted'.

  • #2316 is not marked 'accepted' (state: open; labels: bug, confirmed, needs-discussion).

Nothing to do on your side. The issue is filed and is waiting for a maintainer to triage it and apply the accepted label; that step is ours, not yours. Once accepted lands this check re-runs on its own and turns green; there is nothing to re-trigger by hand.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

About the build check on this pull request

This pull request comes from a fork, so GitHub does not pass it the credentials build normally uses for our image registry cache and staging build configuration. The build still runs and still compiles the image, so a red build here is real; it just takes longer than on team branches.

Every workflow run on a pull request from a fork also waits for a maintainer to approve it, so checks can sit at "awaiting approval" for a while after each push. Nothing is needed from you for either of these.

@suisuss suisuss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-link fails because #2316 is not labelled accepted. 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 because ai-gateway is 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: b83e96e0 deleted this plugin as "unneeded" in December, a71eb405 brought index.ts back 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, 627716bf and 1fb8e2dc both 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 declares dependencies: { ai, openai, "@google/genai", zod }, and openai and @google/genai are not in the root package.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 gets openai@^6.8.0 and @google/genai@^1.28.0 in its generated package.json, pulled from npm, for code that imports neither. -> Delete the dependencies block. 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 :30 and :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 - parseSchemaFields returns undefined on a JSON.parse failure, and schemaForFields folds 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:199 and generate-image-core.ts:55 - (input.aiModel ?? DEFAULT).trim() has no typeof === "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 a TypeError out of the step instead of returning a typed USER failure. Mirror the prompt's guard.

  • generate-image-core.ts:55 uses imageModel raw while generate-text-core.ts:213 runs aiModel through normaliseModel. 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 and docs/plugins/_meta.ts a 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.ts on 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 stubs fetchCredentials with a key; safeFetch throwing (ai-gateway-core.ts:90-97); a non-JSON response body (:43-49); both bare-name branches of normaliseModel (: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-gateway should 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 that docs/api/integrations.md:22 advertises 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.

@suisuss suisuss added changes-requested Triage: reviewed, changes needed from the contributor decision-needed Blocked on a maintainer decision, not on the contributor labels Sep 7, 2026
@Dami904

Dami904 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Resolved on #2316 -- deferring to this PR, I'll step back from "taking" the issue. Also flagged there that #2318 (a follow-on I filed) depends on ai-gateway surviving, as input for the build-vs-delete call still pending with the core team.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes-requested Triage: reviewed, changes needed from the contributor decision-needed Blocked on a maintainer decision, not on the contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: AI Gateway generate-text/generate-image nodes 404 at execution (no runtime step file)

3 participants