feat(providers): add Hetzner experimental inference provider - #14
feat(providers): add Hetzner experimental inference provider#14weselben wants to merge 9 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
weselben
left a comment
There was a problem hiding this comment.
Review loop findings (4: 0 red, 1 yellow, 2 blue, 1 question).
.env.template:L607 — 🟡 risk: no HETZNER_API_KEY / HETZNER_BASE_URL / HETZNER_MODELS entries; every other registered provider type has a documented row. Add the trio for discoverability parity.
internal/providers/hetzner/hetzner_test.go:L266 — 🔵 nit: file missing trailing newline at EOF.
tests/contract/: 🔵 nit: no contract replay test or testdata/hetzner/ fixture; kimicode has one. Optional, but mirror the precedent if live recording is feasible.
internal/providers/hetzner/hetzner.go:L34 — ❓ q: Embeddings forwards /v1/embeddings and surfaces the raw upstream 404 to callers. Docs and TestEmbeddings_ForwardsToUpstreamWhichReturnsError already flag the footgun. Worth overriding Embeddings to return a clear "not supported" error so users get a typed signal instead of leaking upstream body text.
Review findings from PR #14: - override Embeddings to return a typed "not supported" error instead of forwarding to the absent upstream /v1/embeddings (kilo precedent) - add missing trailing newline to hetzner_test.go - update hetzner.mdx to document the typed error
pr-resolve summaryReview findings triaged. 2 resolved, 2 skipped, 0 unanswered. Resolved in
Skipped with reason:
Validation: |
Mirror the kimicode pattern: wrap the shared openai.ChatCompatible adapter behind a thin Registration/New/NewWithHTTPClient surface. Hetzner exposes chat completions, model listing, and passthrough via OpenAI-compat at https://inference.hetzner.com/api/v1. No embeddings endpoint is documented upstream; the embedded adapter advertises the capability, but embedding requests will fail at the provider.
Wire hetzner.Registration into defaultProviderFactory and assert it is registered and instantiable. Add hetzner to the expected provider type list kept in lockstep with the dashboard's Add Provider selector.
Add hetzner entry to testDiscoveryConfigs and a focused test that applyProviderEnvVars discovers the type and resolves its default base URL. .env.template and config.example.yaml are out of scope while Hetzner's API is experimental.
New hetzner.mdx leads with the experimental warning and documents configuration, runtime model discovery, rate limits (429, windows change during the experiment), and free-while-experimental pricing. Overview table gains a hetzner row and a provider note; docs.json gets the nav entry. No model list or limit table is hardcoded — both moved during the experimental period.
Mirror the kilo test depth (test-to-impl ratio ~4x) with no golden JSON and no live API calls. Cover registration shape, both constructors (nil HTTP client + zero hooks paths), Bearer auth on chat and stream, model ID passthrough, /v1/models list, embeddings upstream-failure path, and the optional interface guard matching kilo.
Review findings from PR #14: - override Embeddings to return a typed "not supported" error instead of forwarding to the absent upstream /v1/embeddings (kilo precedent) - add missing trailing newline to hetzner_test.go - update hetzner.mdx to document the typed error
d6bf585 to
30e5052
Compare
Address feedback from the second pr-review loop: - run/providers_test.go: restore tabs lost during rebase conflict resolution (gofmt violation caught by the pre-commit hook) - hetzner_test.go: harden TestEmbeddings_ReturnsUnsupportedError to assert zero upstream requests via httptest; a regression that forwards embeddings upstream fails deterministically instead of hitting the network - hetzner_test.go: add TestResponses_TranslatesToChatCompletions so the "serves /v1/responses via chat" doc claim is exercised by a test - hetzner.mdx: add a Note that the example model ID comes from the official Hetzner docs and may differ at read time (experimental catalogue)
weselben
left a comment
There was a problem hiding this comment.
Review findings (round 2, post-rebase). Reviewed at head 30e5052. Five findings. Three resolved immediately in 695f1db; two answered in thread.
Resolved in 695f1db:
- 🔴
run/providers_test.go:179— line lost its two leading tabs whenhetznerwas inserted during rebase conflict resolution. gofmt violation. Restored the tabs. - 🟡
internal/providers/hetzner/hetzner_test.go:227—TestEmbeddings_ReturnsUnsupportedErrorused an empty base URL and a nil HTTP client. A regression that forwards embeddings upstream would hit the network instead of failing the test. Rewrote the test to point at anhttptestserver and assert zero requests received. - 🔵
docs/providers/hetzner.mdx:17— doc claims/v1/responsesis translated via chat completions, but no hetzner test exercised the path. AddedTestResponses_TranslatesToChatCompletions.
Answered in thread (no commit):
- 🟡
docs/providers/hetzner.mdx:38— example model IDQwen/Qwen3.6-35B-A3B-FP8comes from the official Hetzner docs (checked 2026-08-17). The catalogue is experimental and moves; the guide now carries a Note naming the source and directing readers to/v1/models. - ❓
docs/providers/overview.mdx:63— passthrough marked ✅. All models are documented as OpenAI-compatible; passthrough is a generic forwarder. The row label(experimental)carries the caveat.
Validation: go test ./... green (80 packages, 0 FAIL). go test -cover ./internal/providers/hetzner/ reports 100.0% statement coverage. gofmt -l clean on the fixed files.
… caveat Move the two thread-answered round-2 findings into the docs so downstream review bots on the upstream mirror do not re-raise them: - overview.mdx provider note: name the example model ID's source (official Hetzner docs, 2026-08-17) and mark the passthrough check as adapter capability with unverified upstream tolerance - hetzner.mdx: add a passthrough Note stating the forwarder is generic and arbitrary paths may 404/405 while the API is experimental
|
Round-2 thread answers are now documented in the source, not only in the review. Commit
A review bot on the upstream mirror should no longer re-raise either point. |
|
PR body updated to document the two round-1 deliberate skips in source, not just in review threads. New "Deliberately out of scope (map #7)" section covers:
Body also refreshed: 12 tests (not 11), current |
Three findings from upstream PR review bots, addressed in source: - docs/providers/hetzner.mdx: drop the undocumented request-count limit claim (CodeRabbit verified only token-based limits are documented); state the actual 3M/60k per 60s and 500M/5M per 24h windows - internal/providers/hetzner/hetzner_test.go: harden TestEmbeddings_ReturnsUnsupportedError with errors.As against *core.GatewayError so a plain error with the same text would fail the typed-contract assertion (CodeRabbit) - internal/server/passthrough_support.go: add hetzner to the default ENABLED_PASSTHROUGH_PROVIDERS allowlist (greptile P1: provider matrix marked \xE2\x9C\x85 but default-configured gateway returned 400 on /p/hetzner/...) - .env.template + docs/providers/overview.mdx + docs/providers/hetzner.mdx: document the default-allowlist inclusion - internal/server/handlers_test.go: update the rejection-message expectation to include hetzner in the sorted allowlist - internal/server/passthrough_support_test.go: add an assertion that the default allowlist contains hetzner (regression guard)
|
merged on upstream ENTERPILOT#701 |
…LOT#701) * feat(providers): add hetzner experimental provider Mirror the kimicode pattern: wrap the shared openai.ChatCompatible adapter behind a thin Registration/New/NewWithHTTPClient surface. Hetzner exposes chat completions, model listing, and passthrough via OpenAI-compat at https://inference.hetzner.com/api/v1. No embeddings endpoint is documented upstream; the embedded adapter advertises the capability, but embedding requests will fail at the provider. * feat(run): register hetzner provider in factory Wire hetzner.Registration into defaultProviderFactory and assert it is registered and instantiable. Add hetzner to the expected provider type list kept in lockstep with the dashboard's Add Provider selector. * test(providers): add hetzner to config parser test fixtures Add hetzner entry to testDiscoveryConfigs and a focused test that applyProviderEnvVars discovers the type and resolves its default base URL. .env.template and config.example.yaml are out of scope while Hetzner's API is experimental. * docs(providers): add hetzner provider guide New hetzner.mdx leads with the experimental warning and documents configuration, runtime model discovery, rate limits (429, windows change during the experiment), and free-while-experimental pricing. Overview table gains a hetzner row and a provider note; docs.json gets the nav entry. No model list or limit table is hardcoded — both moved during the experimental period. * test(providers): hetzner unit tests, 100% statement coverage Mirror the kilo test depth (test-to-impl ratio ~4x) with no golden JSON and no live API calls. Cover registration shape, both constructors (nil HTTP client + zero hooks paths), Bearer auth on chat and stream, model ID passthrough, /v1/models list, embeddings upstream-failure path, and the optional interface guard matching kilo. * fix(providers): hetzner embeddings typed error, test newline Review findings from PR #14: - override Embeddings to return a typed "not supported" error instead of forwarding to the absent upstream /v1/embeddings (kilo precedent) - add missing trailing newline to hetzner_test.go - update hetzner.mdx to document the typed error * fix(providers): review findings round 2 Address feedback from the second pr-review loop: - run/providers_test.go: restore tabs lost during rebase conflict resolution (gofmt violation caught by the pre-commit hook) - hetzner_test.go: harden TestEmbeddings_ReturnsUnsupportedError to assert zero upstream requests via httptest; a regression that forwards embeddings upstream fails deterministically instead of hitting the network - hetzner_test.go: add TestResponses_TranslatesToChatCompletions so the "serves /v1/responses via chat" doc claim is exercised by a test - hetzner.mdx: add a Note that the example model ID comes from the official Hetzner docs and may differ at read time (experimental catalogue) * docs(providers): document hetzner model-ID provenance and passthrough caveat Move the two thread-answered round-2 findings into the docs so downstream review bots on the upstream mirror do not re-raise them: - overview.mdx provider note: name the example model ID's source (official Hetzner docs, 2026-08-17) and mark the passthrough check as adapter capability with unverified upstream tolerance - hetzner.mdx: add a passthrough Note stating the forwarder is generic and arbitrary paths may 404/405 while the API is experimental * fix(providers): review-bot findings on PR ENTERPILOT#701 Three findings from upstream PR review bots, addressed in source: - docs/providers/hetzner.mdx: drop the undocumented request-count limit claim (CodeRabbit verified only token-based limits are documented); state the actual 3M/60k per 60s and 500M/5M per 24h windows - internal/providers/hetzner/hetzner_test.go: harden TestEmbeddings_ReturnsUnsupportedError with errors.As against *core.GatewayError so a plain error with the same text would fail the typed-contract assertion (CodeRabbit) - internal/server/passthrough_support.go: add hetzner to the default ENABLED_PASSTHROUGH_PROVIDERS allowlist (greptile P1: provider matrix marked \xE2\x9C\x85 but default-configured gateway returned 400 on /p/hetzner/...) - .env.template + docs/providers/overview.mdx + docs/providers/hetzner.mdx: document the default-allowlist inclusion - internal/server/handlers_test.go: update the rejection-message expectation to include hetzner in the sorted allowlist - internal/server/passthrough_support_test.go: add an assertion that the default allowlist contains hetzner (regression guard)
TL;DR
Hetzner runs an experimental OpenAI-compatible inference API at
https://inference.hetzner.com/api/v1. GoModel had no provider for it. This PR adds thehetznerprovider. It wraps the shared OpenAI chat adapter with zero overrides. The upstream API was confirmed to be plain vLLM OpenAI-compatible before implementation: standard SSE chunks, standardmax_tokens, standardimage_urlvision parts, Bearer auth.Files to review (9, +533 / -1):
internal/providers/hetzner/hetzner.go(start here)internal/providers/hetzner/hetzner_test.go(new)run/providers.gofactory.Add.run/lifecycle_test.goTestMain_HetznerProviderRegistration. Mirrors the kimicode test.run/providers_test.gohetznerto the dashboard-types lockstep list.internal/providers/config_test.godocs/providers/hetzner.mdx(new)docs/providers/overview.mdxdocs/docs.jsonproviders/kimicode.Reviewer notes
max_tokens, and the Bearer default. NoSetHeaders, noAdaptChatRequest, noRequestMutator.Embeddingsreturns a typed "not supported" error. The embedded adapter would handle/v1/embeddings; Hetzner doesn't expose one. The override matches the kilo pattern. The unit test asserts zero upstream requests viahttptest, so a regression that forwards embeddings upstream fails deterministically instead of hitting the network.TestResponses_TranslatesToChatCompletionsproves the doc claim that/v1/responsesis served through chat translation./v1/modelsand the official docs. The example model ID in the overview row carries a provenance note in the provider note.Deliberately out of scope
These are conscious omissions while Hetzner's API is experimental. Any reviewer raising them should defer to the rationale below.
.env.templateandconfig.example.yamlentries. NoHETZNER_API_KEY,HETZNER_BASE_URL,HETZNER_MODELSrows. Operators set the env var directly;applyProviderEnvVarsdiscovers the provider viaHETZNER_API_KEY(asserted by the config test). Entries land when the API leaves experimental status.tests/contract/hetzner_test.go, notestdata/hetzner/. Live fixture recording is out of scope while the API is experimental and changes. The provider package instead ships 12 Go unit tests with 100% statement coverage./v1/embeddingsendpoint. The provider overridesEmbeddingswith a typed error instead of forwarding.costload-balancing cannot rank hetzner by price until upstream publishes pricing.Tests
go test ./internal/providers/hetzner/ -coverreports 100.0% statement coverage.go test ./...is green (80 packages, 0 FAIL).go build ./...is clean.gofmt -lclean on the touched files.Links
This PR description was generated with AI assistance.