Skip to content

feat(ci): add OpenAI API spec conformance tracking#2197

Open
m-misiura wants to merge 3 commits into
NVIDIA-NeMo:developfrom
m-misiura:oasdiff
Open

feat(ci): add OpenAI API spec conformance tracking#2197
m-misiura wants to merge 3 commits into
NVIDIA-NeMo:developfrom
m-misiura:oasdiff

Conversation

@m-misiura

@m-misiura m-misiura commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds oasdiff to check for breaking API changes and regression against a committed baseline via:

  1. pre-commit:
  • oasdiff breaking should catch breaking changes in fern/openapi.yml vs HEAD
  • oasdiff changelog should count conformance gaps vs vendored OpenAI spec; fails if count increases

Note that we use --fail-on ERR, so only error-level breaking changes fail the hook. Warning-level changes (like removing an optional request property) should pass through.

  1. weekly gh workflow
  • fetches the latest OpenAI spec from upstream
  • compares our API spec (fern/openapi.yml) against the vendored OpenAI spec (schemas/openai-spec.yml) and lists all per-endpoint differences
  • if the vendored spec is outdated, also compares against the latest upstream OpenAI spec so you can see what a vendor bump would change

Related Issue(s)

This PR deals with the following issue: GH Issue #2061

Verification

  • pre-commit no changes:
OpenAI spec conformance..................................................Passed
- hook id: api-conformance
- duration: 2.32s

[1/2] Breaking changes (HEAD vs working tree): none
[2/2] Conformance (OpenAI v2.3.0 vs fern/openapi.yml): 95 changes (unchanged)
  • pre-commit with a breaking change (new required property)
OpenAI spec conformance..................................................Failed
- hook id: api-conformance
- duration: 0.19s
- exit code: 1

[1/2] Breaking changes (HEAD vs working tree): 1 changes: 1 error, 0 warning, 0 info
error	[new-required-request-property] at fern/openapi.yml
	in API POST /v1/chat/completions
		added the new required request property `organization_id`

AI Assistance

  • No AI tools were used.
  • AI tools were used; a human reviewed and can explain every change (tool: Claude Code).

Checklist

  • I've read the CONTRIBUTING guidelines.
  • This PR links to a triaged issue assigned to me.
  • My PR title follows the project commit convention.
  • I've updated the documentation if applicable.
  • I've added tests if applicable.
  • I've noted any verification beyond CI and any checks I couldn't run.
  • I did not update generated changelog files manually.
  • I addressed all CodeRabbit, Greptile, and other review comments, or replied with why no change is needed.
  • @mentions of the person or team responsible for reviewing proposed changes.

cc @Pouyanpi @tgasser-nv

@github-actions github-actions Bot added size: XL status: needs triage New issues that have not yet been reviewed or categorized. labels Jul 21, 2026
@Pouyanpi

Copy link
Copy Markdown
Collaborator

Thanks @m-misiura. few things worth reconsidering before this lands:

  1. target spec (out of scope, just noting): the check diffs fern/openapi.yml, the hand-written spec that feeds Fern docs, not the running server. the actual contract is FastAPI's app.openapi() (pydantic models in nemoguardrails/server/api.py), and nothing keeps the two in sync. so this guards docs conformance, not implementation conformance, which is fine for this PR. worth a follow-up: an equivalence test that app.openapi() stays in sync with fern/openapi.yml would close the gap without switching the docs pipeline to generation. just worth dropping any "guards the implementation" framing so it's not read as more than it is.
  2. vendored spec. schemas/openai-spec.yml is 81k lines but the script only diffs /chat/completions. that's a large third-party blob in history/clones for one path. pinning an upstream ref and fetching at check time (the weekly job already curls it) gives a reproducible baseline without vendoring (a pruned subset is a middle option but I don't think it is desired).
  3. another question is whether to add /v1/models to the conformance side
  4. pre-commit hook: language: golang pulls a Go toolchain + oasdiff onto anyone who edits fern/openapi.yml, and --update mutate a tracked file (baseline.json) during commit. both are surprising for a spec only chane. a PR-gated CI job (paths: fern/openapi.yml) keeps this off contributor's machines and makes baseline updates an explicit, reviewed diff.

@m-misiura

m-misiura commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @m-misiura. few things worth reconsidering before this lands:

  1. target spec (out of scope, just noting): the check diffs fern/openapi.yml, the hand-written spec that feeds Fern docs, not the running server. the actual contract is FastAPI's app.openapi() (pydantic models in nemoguardrails/server/api.py), and nothing keeps the two in sync. so this guards docs conformance, not implementation conformance, which is fine for this PR. worth a follow-up: an equivalence test that app.openapi() stays in sync with fern/openapi.yml would close the gap without switching the docs pipeline to generation. just worth dropping any "guards the implementation" framing so it's not read as more than it is.
  2. vendored spec. schemas/openai-spec.yml is 81k lines but the script only diffs /chat/completions. that's a large third-party blob in history/clones for one path. pinning an upstream ref and fetching at check time (the weekly job already curls it) gives a reproducible baseline without vendoring (a pruned subset is a middle option but I don't think it is desired).
  3. another question is whether to add /v1/models to the conformance side
  4. pre-commit hook: language: golang pulls a Go toolchain + oasdiff onto anyone who edits fern/openapi.yml, and --update mutate a tracked file (baseline.json) during commit. both are surprising for a spec only chane. a PR-gated CI job (paths: fern/openapi.yml) keeps this off contributor's machines and makes baseline updates an explicit, reviewed diff.

Hi @Pouyanpi

As always, many thanks for the very useful feedback. Correspondingly:

  1. splitting the single workflow into two might be worthwhile. Those two workflows would be the one that diffs app.openapi() against the upstream spec and another one that diffs fern/openapi.yml against the upstream spec. I like the app.openapi() -> fern/openapi.yml test and if you think this valuable, I can create a GH issue and follow with another PR for this
  2. removing vendored spec could be prudent; the script can fetch the upstream spec at runtime
  3. adding v1/models also makes a ton sense since this is of course another openai compatible endpoint
  4. removing pre-commit hook and keeping things to CI jobs only would make things easier

I will work on those changes accordingly.

@Pouyanpi

Copy link
Copy Markdown
Collaborator

Thank you @m-misiura . it sounds good, plan works for me. I think we should keep the upstream ref pinned for the gating check (let the weekly job track master and flag drift) 👍🏻

Comment thread scripts/openai_coverage.py Outdated


def _fetch_openai_spec(url: str = OPENAI_SPEC_URL) -> Path:
tmp = Path(tempfile.mktemp(suffix=".yml"))
Comment thread scripts/openai_coverage.py Outdated
from nemoguardrails.server.api import app

spec = app.openapi()
tmp = Path(tempfile.mktemp(suffix=".yml"))
@m-misiura

Copy link
Copy Markdown
Contributor Author

Thank you @m-misiura . it sounds good, plan works for me. I think we should keep the upstream ref pinned for the gating check (let the weekly job track master and flag drift) 👍🏻

Ack, thanks @Pouyanpi. Let me know what you think about the introduced changes

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

Labels

size: L status: needs triage New issues that have not yet been reviewed or categorized.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants