Skip to content

feat(ai-step): new small composite action for structured AI output - #131

Merged
Piotr1215 merged 6 commits into
mainfrom
devops-834/ai-transform-action
Apr 21, 2026
Merged

feat(ai-step): new small composite action for structured AI output#131
Piotr1215 merged 6 commits into
mainfrom
devops-834/ai-transform-action

Conversation

@Piotr1215

Copy link
Copy Markdown
Contributor

Summary

  • New .github/actions/ai-step composite: prompt + input + JSON Schema → schema-conforming JSON on steps.<id>.outputs.result. Caller parses via fromJSON(...) and branches on typed fields.
  • Two providers, same contract: Anthropic (Messages API + output_config.format.schema) and OpenAI (Chat Completions + response_format.json_schema.schema). No wrappers, no bun, no PR-review machinery.
  • Auto-enforces additionalProperties: false on object nodes (both providers require it in strict mode).
  • Never hard-fails: API errors, empty responses, non-JSON content → conclusion=failed with upstream body in the CI log, caller decides.

Why this, not generalize ai-pr-review

ai-pr-review is PR-shaped — owns checkout, commenting, sticky summaries, provenance footer. ai-step is the primitive for any classify/extract/route flow that doesn't need PR context. Generalizing the review action would have coupled unrelated concerns.

An earlier draft wrapped claude-code-action / codex-action. Live testing showed ~90s cold-start plus a bun directory-mismatch hang unrelated to schema binding — both rooted in the wrappers' PR-review machinery. Direct SDK calls run end-to-end in ~15s and sidestep that entirely.

Live-tested

Both providers ran the same schema + prompt in the devops-scratchpad smoke workflow:

Anthropic haiku-4-5 OpenAI gpt-5.4-mini
sentiment positive positive
confidence 0.95 0.98
duration 16s 16s

Test plan

  • make test-ai-step — 14/14 bats tests pass for resolve-config.sh (happy path per provider × effort, invalid inputs, missing envs, empty schema).
  • Live smoke against Anthropic (haiku-4-5) — loft-sh/devops-scratchpad run 24732067259: schema conformance verified, fromJSON() branching exercised.
  • Live smoke against OpenAI (gpt-5.4-mini) — loft-sh/devops-scratchpad run 24732244920: same schema, same contract.
  • Reviewer: spot-check docs/workflows/ai-step.md for the design rationale (why direct-SDK won over the wrapper approach).
  • Reviewer: try the snippet in .github/actions/ai-step/README.md against your own schema before cutting the ai-step/v1 tag.

Closes DEVOPS-834

Callers want an ai-in / JSON-out primitive: pass a prompt + input + JSON
Schema, get back schema-conforming JSON on steps.*.outputs.result that
downstream steps can branch on via fromJSON(). ai-pr-review owns the
PR-review preset (comments, sticky summary, provenance) — the wrong
shape for "classify this diff," "pick a reviewer," or "extract fields
from a changelog." Generalizing ai-pr-review would bloat its input
surface and couple unrelated concerns.

ai-step is the small building block: wraps claude-code-action with
--json-schema and codex-action with output-schema, unifies the outputs,
never hard-fails. The caller decides how to react to empty or
unexpected JSON — structured output is the contract, caller owns the
response policy.

Closes DEVOPS-834
GitHub Actions parses any ${{ ... }} it sees inside action.yml —
including prose in a description field — as an expression. The
placeholder example in the 'input' description triggered:
  Unrecognized named-value: 'steps'
on every caller, breaking composite-action loading before it ran.
Rewrite the description in plain prose; the usage examples in the
README keep the templated form.
claude-code-action tries to exchange an OIDC token for a GitHub App
token on startup unless github_token is explicitly provided. That fails
with 'Could not fetch an OIDC token' in any caller workflow that
doesn't set id-token: write — a surprising requirement for what is
meant to be a pure text-to-JSON primitive with no PR interaction.
Passing github.token (always available to composite actions) makes
id-token: write unnecessary for ai-step callers.
The claude-code-action wrapper was the wrong dependency for a generic
text-to-JSON primitive: it ships a full PR-review pipeline (bun
install, sandboxing, review-mode cleanup) that we don't need, which
cost ~90s cold-start per call and exposed bugs unrelated to schema
binding (tsconfig directory-mismatch; --json-schema hangs when combined
with default output-format; OIDC dance even for pure API calls).

Both providers ship first-class structured-output support on their
chat APIs — Anthropic via output_config.format.schema on the Messages
API, OpenAI via response_format.json_schema.schema on Chat Completions.
Using the official python SDKs gives us native schema binding, proper
error types, and a ~3-5s end-to-end call in place of ~90s, with none
of the wrapper-specific failure modes. Losing tool-use/MCP support in
v1 is intentional; callers who need it can reach for claude-code-action
directly.
Anthropic structured-output (and OpenAI strict mode) reject schemas
where an object type doesn't explicitly set additionalProperties=false.
First smoke call failed with 'For object type, additionalProperties
must be explicitly set to false' — annoying for every caller to
repeat on every nested object. Walk the parsed schema once before
sending and set the flag where missing, leaving explicit values alone.
…hema fields

Previous docs described a claude-code-action / codex-action wrapper and
listed tool/mcp-config inputs we removed when we moved to direct SDK
calls. Also relax the in-repo smoke schema to drop numeric min/max so
the same schema works on Anthropic strict mode.
@Piotr1215
Piotr1215 merged commit 5d1e3a4 into main Apr 21, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant