Skip to content

feat: public extension API and embeddable bootstrap#479

Merged
SantiagoDePolonia merged 5 commits into
mainfrom
feat/extension-api
Jul 4, 2026
Merged

feat: public extension API and embeddable bootstrap#479
SantiagoDePolonia merged 5 commits into
mainfrom
feat/extension-api

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What this does

Gives GoModel a small, feature-neutral extension surface so custom gateway binaries can be built on top of it — the open-source binary itself becomes a thin wrapper over the same API. Split out of #457 so the module-path rename can be decided independently; this PR does not touch the module name (434 rename-only files dropped from the original diff).

1. Public ext package + ingress wiring

A registry where custom builds register:

  • Request rewriters — receive the raw JSON body of POST /v1/chat/completions, /v1/messages, /v1/responses (subroutes excluded) after auth and before workflow resolution, so body rewrites (including model) affect routing, failover, guardrails, budgets, and caching. Fail-closed, errors rendered in each endpoint's native dialect.
  • Echo middleware (before gateway auth), extra routes, and auth-skip paths.

An empty registry adds zero request overhead — the middleware isn't even registered, so the default build is byte-for-byte unchanged in behavior.

2. Public run package

run.Run(ctx, run.Options) is the full importable gateway lifecycle (CLI, probes, dotenv, logging, config, providers, signals); Options.Setup fires only when the process actually serves, keeping --version and Docker healthchecks silent. cmd/gomodel keeps byte-identical CLI behavior.

3. Audit request-revision chain

request_body stays the original client request; a new request_revisions array records each ingress rewrite (rewriter, sizes, optional detail, rewritten body when body logging is on) — mirroring the provider-attempts pattern — and the dashboard renders them as "Rewritten" tabs.

The extension/embedding guide (docs/extensions.md) ships with #457, since the embedding recipe only becomes practical once the module is go-gettable. Until then the ext/run packages should be treated as experimental with no compatibility promise.

Testing

Full unit suite, e2e, 402 dashboard JS tests, and golangci-lint all green. New table-driven tests cover registry semantics, per-endpoint gating, rewriter chaining, fail-closed error envelopes per dialect, >64KB/>1MB bodies, audit-keeps-original for streaming and buffered paths, revision recording with/without body logging, and run CLI behavior.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added gateway extension APIs for registering request rewriters, extra middleware/routes, and auth-skip public paths.
    • Added request-rewrite audit revision tracking and new “Rewritten” panes in the audit dashboard.
    • Refined startup by consolidating lifecycle handling into the reusable runtime entry point.
  • Bug Fixes
    • Request-rewrite failures now fail closed with client-facing rejection responses and preserve the original request in audit logs.
  • Tests
    • Expanded unit and middleware/audit/UI tests, plus end-to-end CLI/runtime coverage.

SantiagoDePolonia and others added 2 commits July 5, 2026 00:47
Adds the ext package — a registry where custom gateway builds register
raw-JSON request rewriters, Echo middleware, extra routes, and
auth-skip paths — and wires it into the server: rewriters run on
POST /v1/chat/completions, /v1/messages, and /v1/responses after
authentication and before workflow resolution, fail-closed, with
audit logs keeping the original client body. Nothing is registered
when no extensions exist, so the default build pays nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Moves CLI parsing, logging setup, health/ready probes, provider
registration, and the start/shutdown lifecycle into the importable run
package: run.Run(ctx, run.Options) with a Setup hook that fires only
when the gateway actually serves (keeps --version and healthcheck
probes silent) and run.ExitCode for exit-code mapping. cmd/gomodel
becomes a thin wrapper with identical CLI behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mintlify

mintlify Bot commented Jul 4, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 4, 2026, 10:48 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac601fd1-f280-4387-aeef-567aff24688e

📥 Commits

Reviewing files that changed from the base of the PR and between 1b13d56 and 5bce952.

📒 Files selected for processing (7)
  • ext/ext.go
  • internal/app/app.go
  • internal/app/app_test.go
  • internal/server/request_rewrite.go
  • internal/server/request_rewrite_test.go
  • run/providers_test.go
  • run/run_test.go

📝 Walkthrough

Walkthrough

This PR moves gateway startup into run, adds a public extension registry for request rewriters, wires extensions through server construction, records rewrite revisions in audit logs, and shows those revisions in the admin dashboard.

Changes

Run package extraction and CLI lifecycle

Layer / File(s) Summary
run package repackaging
run/flags.go, run/flags_test.go, run/health.go, run/health_test.go, run/lifecycle_test.go, run/logging.go, run/logging_test.go, run/ready_test.go
Existing CLI, health, logging, and lifecycle code moves to package run; parseCLI now accepts a product name and the old exit-code helper is removed.
Default provider factory
run/providers.go, run/providers_test.go
Adds defaultProviderFactory and verifies the registered provider set.
Run entrypoint and exit codes
run/run.go, run/run_test.go
Implements Options, Run(ctx, opts), and ExitCode, including CLI handling, probes, shutdown, and tests.
main.go delegation
cmd/gomodel/main.go
Reduces main() to a run.Run call plus run.ExitCode.

Extension system, request rewriting, and audit UI

Layer / File(s) Summary
ext contracts and registry
ext/ext.go, ext/registry.go, ext/registry_test.go
Defines the request-rewriter API, rejection error type, and concurrency-safe registry with tests.
Extension wiring into app and server
internal/app/app.go, internal/app/app_test.go, internal/server/http.go
Adds extension registry support to app config and server config, then applies registered middleware, routes, rewriters, and auth-skip paths.
Request rewrite middleware and audit capture
internal/server/request_rewrite.go, internal/server/request_rewrite_test.go, internal/auditlog/auditlog.go, internal/auditlog/middleware.go
Implements request rewriting, audit revision recording, body pinning, and error mapping.
Audit revision tabs in dashboard
internal/admin/dashboard/static/js/modules/audit-list.js, internal/admin/dashboard/static/js/modules/audit-list.test.cjs, internal/admin/dashboard/templates/audit-pane.html
Adds request-revision panes and dynamic headers title rendering in the audit UI.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Main as cmd/gomodel/main.go
  participant Run as run.Run
  participant Config as Config Loader
  participant Factory as defaultProviderFactory
  participant App as Application

  Main->>Run: Run(ctx, Options)
  Run->>Run: parseCLI / handle --version / --help
  Run->>Config: load config
  Run->>Factory: defaultProviderFactory(cfg)
  Run->>App: app.New(cfg, factory, Extensions)
  Run->>App: Start()
  Run->>App: Shutdown() on SIGINT / SIGTERM
  Run-->>Main: error
  Main->>Main: ExitCode(err)
Loading
sequenceDiagram
  participant Client
  participant AuthMW as Auth Middleware
  participant RewriteMW as RequestRewriteMiddleware
  participant Rewriter as ext.RequestRewriter
  participant Audit as AuditLog
  participant Provider

  Client->>AuthMW: POST /v1/chat/completions
  AuthMW->>RewriteMW: authenticated request
  RewriteMW->>RewriteMW: check rewriteEndpoint
  RewriteMW->>Rewriter: Rewrite(ctx, Input)
  Rewriter-->>RewriteMW: Result{Body, ResponseHeader, Detail}
  RewriteMW->>Audit: pinOriginalAuditRequestBody + recordRequestRevision
  RewriteMW->>RewriteMW: applyRewrittenBody
  RewriteMW->>Provider: forward rewritten request
  Provider-->>Client: response
Loading

Possibly related PRs

  • ENTERPILOT/GoModel#404: Shares the same CLI entrypoint and health-probe area, including --health dispatch and startup flow.
  • ENTERPILOT/GoModel#444: Touches the same dashboard audit pane flow that now renders request-revision tabs.

Poem

A rabbit hopped through startup code,
Then split the path where rewrites flowed 🐇
It pinned each trail, then hopped along,
To audit panes where tabs belong,
And left the gateway neat and bold.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: a public extension API and an embeddable bootstrap entrypoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/extension-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jul 4, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 77.03704% with 31 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/server/request_rewrite.go 85.29% 9 Missing and 6 partials ⚠️
internal/auditlog/middleware.go 0.00% 10 Missing ⚠️
cmd/gomodel/main.go 0.00% 5 Missing ⚠️
internal/app/app.go 87.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge with low risk.

The change is scoped around a new extension surface and preserves the default path by only registering rewrite middleware when extensions are configured. The updated paths include tests for registry semantics, CLI behavior, endpoint gating, rewrite chaining, audit capture, and dashboard rendering. No blocking correctness or security issues were identified in the reviewed changed code.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The pre-run audit log was saved, capturing the command and CWD header and showing a final exit code of 0, establishing a clean setup for the test run.
  • The post-run audit log shows the focused changed tests passing, including TestRequestRewriteMiddlewareRewritesChatCompletions, TestRequestRewriteMiddlewareRewritesMessages, TestRequestRewriteMiddlewareEndpointGating, TestRequestRewriteMiddlewareChainsInRegistrationOrder, TestRequestRewriteMiddlewareRejectionError/openai_dialect, TestRequestRewriteMiddlewareRejectionError/anthropic_dialect, TestRequestRewriteMiddlewareInternalErrorFailsClosed, TestRequestRewriteMiddlewareLargeBody, TestRequestRewriteMiddlewareAuditKeepsOriginalBody, and TestRequestRewriteMiddlewareRecordsRevisions/{with_body_logging,without_body_logging}, with final EXIT_CODE: 0.
  • Two artifacts were saved around the audit— the before-log and the after-log—allowing reviewers to inspect both the setup and the test outcomes.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant Echo as Echo middleware stack
participant Auth as Gateway auth
participant RW as ext.RequestRewriter chain
participant Audit as Audit log
participant Workflow as Workflow/model resolution
participant Handler as Inference handler
participant Provider

Client->>Echo: POST /v1/chat/completions, /v1/messages, or /v1/responses
Echo->>Echo: body limit, request id, request snapshot
Echo->>Audit: create live audit entry
Echo->>Auth: authenticate request / set user path
Auth-->>Echo: authenticated context
Echo->>RW: raw JSON body + headers + user path
loop registered rewriters
    RW->>RW: Rewrite(ctx, Input)
    alt result body changed
        RW->>Audit: append request_revisions entry
        RW->>RW: pass rewritten body to next rewriter
    else rejection/error
        RW-->>Client: endpoint-native error response
    end
end
RW->>Audit: pin original request body when changed
RW->>Echo: install final rewritten body snapshot
Echo->>Workflow: resolve workflow/model using rewritten body
Workflow->>Handler: parsed rewritten request
Handler->>Provider: routed provider request
Provider-->>Client: normalized response
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant Echo as Echo middleware stack
participant Auth as Gateway auth
participant RW as ext.RequestRewriter chain
participant Audit as Audit log
participant Workflow as Workflow/model resolution
participant Handler as Inference handler
participant Provider

Client->>Echo: POST /v1/chat/completions, /v1/messages, or /v1/responses
Echo->>Echo: body limit, request id, request snapshot
Echo->>Audit: create live audit entry
Echo->>Auth: authenticate request / set user path
Auth-->>Echo: authenticated context
Echo->>RW: raw JSON body + headers + user path
loop registered rewriters
    RW->>RW: Rewrite(ctx, Input)
    alt result body changed
        RW->>Audit: append request_revisions entry
        RW->>RW: pass rewritten body to next rewriter
    else rejection/error
        RW-->>Client: endpoint-native error response
    end
end
RW->>Audit: pin original request body when changed
RW->>Echo: install final rewritten body snapshot
Echo->>Workflow: resolve workflow/model using rewritten body
Workflow->>Handler: parsed rewritten request
Handler->>Provider: routed provider request
Provider-->>Client: normalized response
Loading

Reviews (1): Last reviewed commit: "feat(auditlog): request-revision chain w..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/extensions.md`:
- Around line 8-15: The markdown in extensions.md triggers MD028 because there
is a blank line inside a blockquote, causing two quoted sections to merge.
Update the blockquote around the GoModel module path note and the API stability
note so they are separated without an empty quoted line, keeping the content in
distinct quote blocks. Use the existing quoted paragraphs in the docs section as
the reference points.

In `@ext/ext.go`:
- Around line 27-55: Redact credential-bearing headers before rewriters see them
and before any revision details are persisted; the current flow passes the full
cloned header map through Input.Header and then stores Result.Detail via
recordRequestRevision without sanitization. Update the request rewrite path so
Input.Header excludes or masks sensitive values like Authorization, Cookie,
Proxy-Authorization, and X-Api-Key, and ensure any Detail produced by a rewriter
is filtered before it is recorded. Keep the fix centered around Input, Result,
and recordRequestRevision so the audit trail never captures secrets.

In `@internal/app/app.go`:
- Around line 439-444: Add app-level test coverage for the cfg.Extensions wiring
in app.New: create a test in internal/app/app_test.go that passes a non-nil
Extensions registry and verifies the resulting serverCfg gets RequestRewriters,
ExtraMiddleware, ExtraRoutes, and ExtraAuthSkipPaths populated from
cfg.Extensions. Use the app.New entrypoint and the Extensions-related methods
Rewriters, Middleware, Routes, and PublicPaths to exercise the translation path
that server.Config tests do not cover.

In `@internal/server/request_rewrite.go`:
- Around line 26-37: RequestRewriteMiddleware always buffers the request body
even when there are no rewriters, which adds unnecessary overhead on the ingest
path. Add an early exit in RequestRewriteMiddleware, before calling
requestBodyBytes(c), so that when the rewriters slice is empty the middleware
immediately delegates to next(c) after rewriteEndpoint(c.Request()) succeeds;
keep the existing rewrite/audit flow only for the non-empty rewriters case.

In `@run/providers.go`:
- Around line 30-58: Add test coverage for defaultProviderFactory to prevent
regressions when provider registrations change. Create tests around
defaultProviderFactory that verify the cfg.Metrics.Enabled branch sets
observability.NewPrometheusHooks on the ProviderFactory and that, with metrics
disabled, no hooks are added. Also assert that all expected provider
registrations are present after the calls to factory.Add in
defaultProviderFactory, using provider type names or factory inspection helpers
so the tests remain stable if line numbers change.

In `@run/run_test.go`:
- Around line 1-61: Add Run()-level test coverage for the --health and --ready
dispatch paths in Run, since the existing tests only cover --version, usage, and
help. Extend this test file to exercise the cliOpts.Health and cliOpts.Ready
branches in run.go, asserting the correct short-circuit behavior and that errors
from the underlying probe/ready handling are surfaced through Run. Use the Run
and ExitCode symbols to locate the dispatch logic and verify the flag routing
end-to-end.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d096a235-d027-4cef-8e1c-0b955f8baa39

📥 Commits

Reviewing files that changed from the base of the PR and between e864064 and b28fbe3.

📒 Files selected for processing (25)
  • cmd/gomodel/main.go
  • docs/extensions.md
  • ext/ext.go
  • ext/registry.go
  • ext/registry_test.go
  • internal/admin/dashboard/static/js/modules/audit-list.js
  • internal/admin/dashboard/static/js/modules/audit-list.test.cjs
  • internal/admin/dashboard/templates/audit-pane.html
  • internal/app/app.go
  • internal/auditlog/auditlog.go
  • internal/auditlog/middleware.go
  • internal/server/http.go
  • internal/server/request_rewrite.go
  • internal/server/request_rewrite_test.go
  • run/flags.go
  • run/flags_test.go
  • run/health.go
  • run/health_test.go
  • run/lifecycle_test.go
  • run/logging.go
  • run/logging_test.go
  • run/providers.go
  • run/ready_test.go
  • run/run.go
  • run/run_test.go

Comment thread docs/extensions.md Outdated
Comment on lines +8 to +15
> **Consuming from another module:** GoModel's module path is `gomodel`,
> which Go cannot fetch remotely, so external builds currently use a local
> checkout with a `replace gomodel => ../GoModel` directive (or vendoring).
> Renaming the module to a canonical fetchable path is proposed separately
> in [#457](https://github.com/ENTERPILOT/GoModel/pull/457).

> **API stability:** the `ext` and `run` packages are experimental — no
> compatibility promise until they are declared stable.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix MD028 lint warning: blank line inside blockquote.

markdownlint flags the blank line between the two > blockquotes; some renderers will merge them into one quote.

📝 Proposed fix
> **Consuming from another module:** GoModel's module path is `gomodel`,
> which Go cannot fetch remotely, so external builds currently use a local
> checkout with a `replace gomodel => ../GoModel` directive (or vendoring).
> Renaming the module to a canonical fetchable path is proposed separately
> in [`#457`](https://github.com/ENTERPILOT/GoModel/pull/457).

+<!-- -->
+
> **API stability:** the `ext` and `run` packages are experimental — no
> compatibility promise until they are declared stable.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> **Consuming from another module:** GoModel's module path is `gomodel`,
> which Go cannot fetch remotely, so external builds currently use a local
> checkout with a `replace gomodel => ../GoModel` directive (or vendoring).
> Renaming the module to a canonical fetchable path is proposed separately
> in [#457](https://github.com/ENTERPILOT/GoModel/pull/457).
> **API stability:** the `ext` and `run` packages are experimental — no
> compatibility promise until they are declared stable.
> **Consuming from another module:** GoModel's module path is `gomodel`,
> which Go cannot fetch remotely, so external builds currently use a local
> checkout with a `replace gomodel => ../GoModel` directive (or vendoring).
> Renaming the module to a canonical fetchable path is proposed separately
> in [`#457`](https://github.com/ENTERPILOT/GoModel/pull/457).
<!-- -->
> **API stability:** the `ext` and `run` packages are experimental — no
> compatibility promise until they are declared stable.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 13-13: Blank line inside blockquote

(MD028, no-blanks-blockquote)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/extensions.md` around lines 8 - 15, The markdown in extensions.md
triggers MD028 because there is a blank line inside a blockquote, causing two
quoted sections to merge. Update the blockquote around the GoModel module path
note and the API stability note so they are separated without an empty quoted
line, keeping the content in distinct quote blocks. Use the existing quoted
paragraphs in the docs section as the reference points.

Source: Linters/SAST tools

Comment thread ext/ext.go
Comment thread internal/app/app.go Outdated
Comment on lines +439 to +444
if cfg.Extensions != nil {
serverCfg.RequestRewriters = cfg.Extensions.Rewriters()
serverCfg.ExtraMiddleware = cfg.Extensions.Middleware()
serverCfg.ExtraRoutes = cfg.Extensions.Routes()
serverCfg.ExtraAuthSkipPaths = cfg.Extensions.PublicPaths()
}

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for existing app-level test coverage of Extensions wiring
rg -n 'Extensions' internal/app/app_test.go

Repository: ENTERPILOT/GoModel

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== app.go mapping =="
nl -ba internal/app/app.go | sed -n '430,455p'

echo
echo "== app_test.go references =="
rg -n 'app\.New|New\(|Extensions|RequestRewriters|ExtraMiddleware|ExtraRoutes|ExtraAuthSkipPaths' internal/app/app_test.go internal/app -g '*_test.go' -g '*.go'

echo
echo "== repo-wide test coverage for serverCfg mapping fields =="
rg -n 'RequestRewriters|ExtraMiddleware|ExtraRoutes|ExtraAuthSkipPaths|PublicPaths\(\)|Rewriters\(\)|Middleware\(\)|Routes\(\)' internal -g '*_test.go' -g '*.go'

Repository: ENTERPILOT/GoModel

Length of output: 217


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

path = Path('internal/app/app.go')
start, end = 430, 455
for i, line in enumerate(path.read_text().splitlines(), 1):
    if start <= i <= end:
        print(f"{i:4d}: {line}")
PY

echo
echo "== app_test.go references =="
rg -n 'app\.New|New\(|Extensions|RequestRewriters|ExtraMiddleware|ExtraRoutes|ExtraAuthSkipPaths' internal/app/app_test.go internal/app -g '*_test.go' -g '*.go' || true

echo
echo "== repo-wide test coverage for mapping fields =="
rg -n 'RequestRewriters|ExtraMiddleware|ExtraRoutes|ExtraAuthSkipPaths|PublicPaths\(\)|Rewriters\(\)|Middleware\(\)|Routes\(\)' internal -g '*_test.go' -g '*.go' || true

Repository: ENTERPILOT/GoModel

Length of output: 6829


Add app-level coverage for cfg.Extensions wiring Add a test in internal/app/app_test.go that calls app.New() with a non-nil Extensions registry and asserts RequestRewriters, ExtraMiddleware, ExtraRoutes, and ExtraAuthSkipPaths are copied onto serverCfg. The server tests cover server.Config directly, but not this translation path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/app/app.go` around lines 439 - 444, Add app-level test coverage for
the cfg.Extensions wiring in app.New: create a test in internal/app/app_test.go
that passes a non-nil Extensions registry and verifies the resulting serverCfg
gets RequestRewriters, ExtraMiddleware, ExtraRoutes, and ExtraAuthSkipPaths
populated from cfg.Extensions. Use the app.New entrypoint and the
Extensions-related methods Rewriters, Middleware, Routes, and PublicPaths to
exercise the translation path that server.Config tests do not cover.

Source: Path instructions

Comment on lines +26 to +37
func RequestRewriteMiddleware(rewriters []ext.RequestRewriter, auditLogger auditlog.LoggerInterface) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
endpoint, ok := rewriteEndpoint(c.Request())
if !ok {
return next(c)
}

body, err := requestBodyBytes(c)
if err != nil {
return handleError(c, core.NewInvalidRequestError("failed to read request body", err))
}

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.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip body buffering entirely when no rewriters are registered.

requestBodyBytes(c) is always invoked for any eligible POST endpoint, even when rewriters is empty (e.g. the default OSS binary with no custom ext build). This buffers the full request body into memory for no benefit on the hot ingest path.

⚡ Proposed early-exit
 func RequestRewriteMiddleware(rewriters []ext.RequestRewriter, auditLogger auditlog.LoggerInterface) echo.MiddlewareFunc {
 	return func(next echo.HandlerFunc) echo.HandlerFunc {
 		return func(c *echo.Context) error {
+			if len(rewriters) == 0 {
+				return next(c)
+			}
 			endpoint, ok := rewriteEndpoint(c.Request())
 			if !ok {
 				return next(c)
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func RequestRewriteMiddleware(rewriters []ext.RequestRewriter, auditLogger auditlog.LoggerInterface) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
endpoint, ok := rewriteEndpoint(c.Request())
if !ok {
return next(c)
}
body, err := requestBodyBytes(c)
if err != nil {
return handleError(c, core.NewInvalidRequestError("failed to read request body", err))
}
func RequestRewriteMiddleware(rewriters []ext.RequestRewriter, auditLogger auditlog.LoggerInterface) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
if len(rewriters) == 0 {
return next(c)
}
endpoint, ok := rewriteEndpoint(c.Request())
if !ok {
return next(c)
}
body, err := requestBodyBytes(c)
if err != nil {
return handleError(c, core.NewInvalidRequestError("failed to read request body", err))
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/server/request_rewrite.go` around lines 26 - 37,
RequestRewriteMiddleware always buffers the request body even when there are no
rewriters, which adds unnecessary overhead on the ingest path. Add an early exit
in RequestRewriteMiddleware, before calling requestBodyBytes(c), so that when
the rewriters slice is empty the middleware immediately delegates to next(c)
after rewriteEndpoint(c.Request()) succeeds; keep the existing rewrite/audit
flow only for the non-empty rewriters case.

Comment thread run/providers.go
Comment on lines +30 to +58
func defaultProviderFactory(cfg *config.Config) *providers.ProviderFactory {
factory := providers.NewProviderFactory()

if cfg.Metrics.Enabled {
factory.SetHooks(observability.NewPrometheusHooks())
}

factory.Add(openai.Registration)
factory.Add(openrouter.Registration)
factory.Add(azure.Registration)
factory.Add(bailian.Registration)
factory.Add(oracle.Registration)
factory.Add(anthropic.Registration)
factory.Add(bedrock.Registration)
factory.Add(deepseek.Registration)
factory.Add(fireworks.Registration)
factory.Add(gemini.Registration)
factory.Add(vertex.Registration)
factory.Add(groq.Registration)
factory.Add(minimax.Registration)
factory.Add(ollama.Registration)
factory.Add(opencodego.Registration)
factory.Add(vllm.Registration)
factory.Add(xai.Registration)
factory.Add(xiaomi.Registration)
factory.Add(zai.Registration)

return factory
}

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for defaultProviderFactory.

No test file accompanies this new function. A small table/behavior test verifying the metrics-hook conditional (cfg.Metrics.Enabled) and that all expected provider types are registered would guard against silent regressions when providers are added/removed.

✅ Example test sketch
func TestDefaultProviderFactory_RegistersMetricsHooksWhenEnabled(t *testing.T) {
	cfg := &config.Config{Metrics: config.MetricsConfig{Enabled: true}}
	factory := defaultProviderFactory(cfg)
	// assert factory has hooks set, e.g. via an exported accessor or behavior check
}

func TestDefaultProviderFactory_RegistersAllProviders(t *testing.T) {
	cfg := &config.Config{}
	factory := defaultProviderFactory(cfg)
	for _, typ := range []string{"openai", "openrouter", "azure", "bailian", "oracle", "anthropic", "bedrock", "deepseek", "fireworks", "gemini", "vertex", "groq", "minimax", "ollama", "opencodego", "vllm", "xai", "xiaomi", "zai"} {
		// assert factory has a registration for typ
	}
}

As per coding guidelines, **/*_test.go: "Add or update tests for behavior changes, with coverage for request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@run/providers.go` around lines 30 - 58, Add test coverage for
defaultProviderFactory to prevent regressions when provider registrations
change. Create tests around defaultProviderFactory that verify the
cfg.Metrics.Enabled branch sets observability.NewPrometheusHooks on the
ProviderFactory and that, with metrics disabled, no hooks are added. Also assert
that all expected provider registrations are present after the calls to
factory.Add in defaultProviderFactory, using provider type names or factory
inspection helpers so the tests remain stable if line numbers change.

Source: Coding guidelines

Comment thread run/run_test.go
Audit entries record one request_revisions entry per rewriter that
changed the body (name, sizes, optional detail, rewritten body when
body logging is on) while request_body stays the original client
request. The dashboard audit detail renders revisions as 'Rewritten'
tabs between Request and Response.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SantiagoDePolonia and others added 2 commits July 5, 2026 01:13
Rewriters run post-auth and get UserPath for identity, so they never
need raw credentials; masking Authorization/cookie/API-key values in
Input.Header keeps secrets out of anything a rewriter might echo into
its audit-trail detail. The Detail contract now states it must never
contain secrets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds coverage review flagged as missing: applyExtensions snapshotting
the registry into server.Config (extracted as a helper), default
provider factory registering all 19 provider types with metrics on and
off, and Run's --health/--ready dispatch against a live and an
unreachable gateway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SantiagoDePolonia

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit review — disposition of each finding:

Fixed:

  • Credential headers in ext.Input.Header — now redacted (Authorization, cookies, API keys via core.IsCredentialHeader) before rewriters see them; rewriters run post-auth and get UserPath for identity, so raw credentials are never needed. The Result.Detail contract now states it must never contain secrets. Core cannot meaningfully filter arbitrary rewriter-provided JSON beyond that — rewriters are compiled-in code at the same trust level as core — so redacting the input is the effective guarantee: secrets can't be echoed into audit details if the rewriter never receives them. Covered by a new assertion in the rewrite test.
  • app.New Extensions wiring untested — extracted the four-field copy into applyExtensions(serverCfg, registry) and added a table test covering population and the nil-registry no-op (a full app.New bootstrap test would require provider/storage scaffolding disproportionate to four field copies).
  • defaultProviderFactory untested — added a test asserting all 19 provider types are registered (via RegisteredTypes()) with metrics enabled and disabled. The Prometheus-hooks branch itself isn't directly assertable (hooks is unexported in providers); the test pins the registration list, which is the regression that matters when providers are added.
  • --health/--ready dispatch untested — added TestRunHealthAndReadyDispatch: both flags probed end-to-end against a live stub gateway (nil error) and an unreachable port (error surfaced, exit code 1).
  • MD028 blockquote in docs/extensions.md — fixed, but note the file moved to build: rename module to canonical github.com/ENTERPILOT/GoModel path #457 (the embedding guide lands with the canonical module path); the two quote blocks are now a single blockquote with a > separator.

Skipped:

  • Early exit in RequestRewriteMiddleware when rewriters are empty — not reachable: the middleware is only registered when len(cfg.RequestRewriters) > 0 (internal/server/http.go), so an empty-slice guard inside it would be dead code; the zero-rewriter build pays nothing today.

Full suite re-verified: unit, e2e, dashboard tests, golangci-lint 0 issues.

🤖 Generated with Claude Code

@SantiagoDePolonia

Copy link
Copy Markdown
Contributor Author

@coderabbitai continue

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@SantiagoDePolonia Thanks for the detailed breakdown — the redaction approach for ext.Input.Header, the applyExtensions extraction with table tests, the defaultProviderFactory registration test, and the --health/--ready dispatch test all sound like solid resolutions. The rationale for skipping the empty-rewriters guard in RequestRewriteMiddleware also makes sense given it's gated at the call site in internal/server/http.go — agreed that's dead code otherwise.

I'll kick off a fresh review pass now to verify everything against the latest commits.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@SantiagoDePolonia SantiagoDePolonia merged commit 9e21e80 into main Jul 4, 2026
20 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.

2 participants