Skip to content

feat(teamloader): add WithModelOptions passthrough for provider-agnostic HTTP transport wrapping#3549

Merged
dgageot merged 1 commit into
docker:mainfrom
simonferquel-clanker:apt-524-teamloader-transport-wrapper
Jul 9, 2026
Merged

feat(teamloader): add WithModelOptions passthrough for provider-agnostic HTTP transport wrapping#3549
dgageot merged 1 commit into
docker:mainfrom
simonferquel-clanker:apt-524-teamloader-transport-wrapper

Conversation

@simonferquel-clanker

Copy link
Copy Markdown
Contributor

🤖 Automated implementer agentthis comment was posted by the implementer bot from Docker Agentic Platform, not by a human developer

Problem

teamloader.Load/LoadWithConfig has no way for a caller to inject a custom options.Opt into the model clients it constructs internally. In particular, options.WithHTTPTransportWrapper (#3089) already lets a caller wrap the HTTP transport used by anthropic/openai/gemini provider clients — e.g. to inject a bearer token provider-agnostically, decoupled from environment.IsTrustedDockerURL — but that only works today for callers who build a provider client directly (e.g. agentic-platform's internal/gordon). Any caller going through teamloader.Load (which resolves providers dynamically from a cagent.yml config, so it can't build clients directly) has no equivalent hook.

This surfaced while investigating a credential-injection gap in agentic-platform's autonomous sandbox-runner sessions (tracked there as APT-632): the runner uses teamloader.Load, so it can't currently apply a transport wrapper for gateway auth across arbitrary configured providers.

Change

  • Adds WithModelOptions(opts ...options.Opt) Opt, appended after teamloader's own built-in opts (options.WithGateway, options.WithStructuredOutput, etc.) at all 4 internal provider-construction sites (getModelsForAgent, getFallbackModelsForAgent, getTitleModelForAgent, getCompactionModelForAgent), so caller-supplied opts take precedence for anything both sides set.
  • Threads it into loadExternalAgent so external (OCI/URL-referenced) sub-agents inherit it too.
  • Adds an end-to-end regression test (TestWithModelOptions_TransportWrapperReachesAgentModel): loads a team from an inline cagent config via WithModelOptions(options.WithHTTPTransportWrapper(...)), drives the resulting agent's model through a real httptest streaming call, and asserts the wrapper's RoundTrip was invoked — proving the opt reaches the actual HTTP client, not just that it compiles. (Confirmed by reverting only teamloader.go: the test then fails to compile with undefined: WithModelOptions.)

Testing

  • go build ./pkg/teamloader/..., go vet ./pkg/teamloader/..., go test ./pkg/teamloader/... — all pass.
  • go mod tidy --diff — empty.
  • go run ./lint . — no offenses (1491 files).
  • golangci-lint v2.12.2 run ./pkg/teamloader/... — 0 issues.
  • Independently reviewed by a sub-agent; verdict: approve. One non-blocking note: the new test only exercises the primary-model site end-to-end (the other 3 sites apply the identical one-line append, verified by code reading); happy to extend coverage if maintainers want it before merge.

…tic HTTP transport wrapping

Load/LoadWithConfig had no way to thread options.WithHTTPTransportWrapper
(docker#3089) into the model clients teamloader constructs internally (primary,
fallback, title, and compaction models, plus models loaded for external
OCI/URL-referenced sub-agents). WithHTTPTransportWrapper already lets an
embedder inject auth into anthropic/openai/gemini clients built directly
(internal/gordon in agentic-platform does this), but any caller going
through teamloader.Load — which resolves providers from a cagent.yml,
so the caller can't build clients directly — had no equivalent hook.

Adds WithModelOptions(...options.Opt), appended after teamloader's own
built-in opts at all 4 provider-construction call sites and threaded
into loadExternalAgent so external sub-agents inherit it too.

Adds an end-to-end regression test: loads a team from an inline cagent
config with WithModelOptions(options.WithHTTPTransportWrapper(...)),
drives the resulting agent's model through a real (httptest) streaming
call, and asserts the wrapper's RoundTrip was invoked — proving the opt
reaches the actual HTTP client, not just that it compiles.

@docker-agent docker-agent 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.

Assessment: 🟢 APPROVE

The WithModelOptions passthrough is mechanically correct. The new modelOpts []options.Opt field is safely threaded through all four provider-construction sites (getModelsForAgent, getFallbackModelsForAgent, getTitleModelForAgent, getCompactionModelForAgent) and into loadExternalAgent. Option ordering (caller opts appended after built-ins) is intentional and correctly documented. Nil/empty-slice edge cases are safe — variadic append(opts, modelOpts...) on an empty/nil slice is a no-op in Go.

The regression test (TestWithModelOptions_TransportWrapperReachesAgentModel) correctly validates end-to-end that the wrapper reaches the actual HTTP client: the countingTransport closure is invoked once at provider construction time (the documented semantics of WithHTTPTransportWrapper), so counter.base is always populated before any RoundTrip call. The httptest-driven streaming path and assertion on counter.calls.Load() are sound.

No bugs were found in the changes introduced by this PR.

@simonferquel simonferquel marked this pull request as ready for review July 9, 2026 10:14
@simonferquel simonferquel requested a review from a team as a code owner July 9, 2026 10:14

@docker-agent docker-agent 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.

Assessment: 🟢 APPROVE

The WithModelOptions passthrough is correctly implemented: caller-supplied opts are appended after teamloader's built-in opts at all four provider-construction sites (getModelsForAgent, getFallbackModelsForAgent, getTitleModelForAgent, getCompactionModelForAgent) and correctly threaded through loadExternalAgent for external sub-agents. The append semantics are sound — no aliasing hazards. The end-to-end regression test exercises the full Load → agent → provider → HTTP path and correctly asserts that the transport wrapper is invoked. No bugs introduced by this PR were confirmed.

@aheritier aheritier added area/models LLM model integrations and model providers kind/feat PR adds a new feature (maps to feat:). Use on PRs only. labels Jul 9, 2026
@dgageot dgageot merged commit 56c4dfa into docker:main Jul 9, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/models LLM model integrations and model providers kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants