Skip to content

Conversation

@ssncferreira
Copy link
Contributor

@ssncferreira ssncferreira commented Dec 19, 2025

Description

Implements request routing for the AI Bridge Proxy. After MITM decryption, requests to known AI providers (Anthropic, OpenAI) are rewritten to the corresponding aibridged endpoint, while requests to unknown hosts are passed through to their original destination.

Changes

  • Add CoderAccessURL configuration option for specifying the Coder deployment URL
  • Add handleRequest to route decrypted requests based on target host
  • Route known AI providers (Anthropic and OpenAI) to AI Bridge specific endpoint.
  • Passthrough requests to unknown hosts directly to their original destination
  • Inject Coder session token (from feat: add proxy authorization to aibridgeproxyd #21342) as Authorization: Bearer header for aibridged
  • Add tests for routing and passthrough behavior

Depends on: #21342
Closes: coder/internal#1181

Copy link
Contributor Author

ssncferreira commented Dec 19, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@ssncferreira ssncferreira force-pushed the ssncferreira/feat-aiproxy-routing branch from 4d87f7d to 9b2593b Compare December 19, 2025 21:17
@ssncferreira ssncferreira marked this pull request as ready for review December 19, 2025 22:40

// canonicalHost strips the port from a host:port string and lowercases it.
func canonicalHost(host string) string {
if i := strings.IndexByte(host, ':'); i != -1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you aware of net.SplitHostPort but chose this approach for some reason?

func providerFromHost(host string) string {
switch canonicalHost(host) {
case "api.anthropic.com":
return "anthropic"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the provider constants here pls.

e.g. https://github.com/coder/aibridge/blob/main/provider_anthropic.go#L29

// Check if this request is for a supported AI provider.
provider := providerFromHost(req.Host)
if provider == "" {
s.logger.Debug(s.ctx, "passthrough request to unknown host",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't really happen, right? Since we only MITM allowlisted domains?

Comment on lines +297 to +301
originalPath := req.URL.Path
aibridgePath := "/" + provider + originalPath

newURL := *s.coderAccessURL
newURL.Path = "/api/v2/aibridge" + aibridgePath
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please prefer https://pkg.go.dev/net/url#JoinPath instead; covers a bunch of edge-cases.

originalPath := req.URL.Path
aibridgePath := "/" + provider + originalPath

newURL := *s.coderAccessURL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's technically possible that this could be nil here, so let's avoid the nil-deref panic pls.

Comment on lines +338 to +339
targetPath: "/v1/messages",
expectedPath: "/api/v2/aibridge/anthropic/v1/messages",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also please include a case for known a provider whose route is neither bridged nor passthru?

w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("hello from aibridged"))
}))
defer aibridgedServer.Close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: t.Cleanup

// - For AI providers, use their real hostnames to trigger routing.
var targetURL string
if tt.passthrough {
targetURL = targetServer.URL + tt.targetPath
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto about url.JoinPath

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.

3 participants