mirror of
https://github.com/coder/coder.git
synced 2026-06-04 05:28:20 +00:00
8f3bb0b0d1
Adds GitHub Copilot as a supported AI provider in aibridge. Depends on: https://github.com/coder/aibridge/pull/137 Closes: https://github.com/coder/internal/issues/1235
90 lines
3.2 KiB
Go
90 lines
3.2 KiB
Go
//go:build !slim
|
|
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/aibridge"
|
|
"github.com/coder/aibridge/config"
|
|
"github.com/coder/coder/v2/coderd/tracing"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
"github.com/coder/coder/v2/enterprise/aibridged"
|
|
"github.com/coder/coder/v2/enterprise/coderd"
|
|
)
|
|
|
|
func newAIBridgeDaemon(coderAPI *coderd.API) (*aibridged.Server, error) {
|
|
ctx := context.Background()
|
|
coderAPI.Logger.Debug(ctx, "starting in-memory aibridge daemon")
|
|
|
|
logger := coderAPI.Logger.Named("aibridged")
|
|
cfg := coderAPI.DeploymentValues.AI.BridgeConfig
|
|
|
|
// Build circuit breaker config if enabled.
|
|
var cbConfig *config.CircuitBreaker
|
|
if cfg.CircuitBreakerEnabled.Value() {
|
|
cbConfig = &config.CircuitBreaker{
|
|
FailureThreshold: uint32(cfg.CircuitBreakerFailureThreshold.Value()), //nolint:gosec // Validated by serpent.Validate in deployment options.
|
|
Interval: cfg.CircuitBreakerInterval.Value(),
|
|
Timeout: cfg.CircuitBreakerTimeout.Value(),
|
|
MaxRequests: uint32(cfg.CircuitBreakerMaxRequests.Value()), //nolint:gosec // Validated by serpent.Validate in deployment options.
|
|
}
|
|
}
|
|
|
|
// Setup supported providers with circuit breaker config.
|
|
providers := []aibridge.Provider{
|
|
aibridge.NewOpenAIProvider(aibridge.OpenAIConfig{
|
|
BaseURL: cfg.OpenAI.BaseURL.String(),
|
|
Key: cfg.OpenAI.Key.String(),
|
|
CircuitBreaker: cbConfig,
|
|
SendActorHeaders: cfg.SendActorHeaders.Value(),
|
|
}),
|
|
aibridge.NewAnthropicProvider(aibridge.AnthropicConfig{
|
|
BaseURL: cfg.Anthropic.BaseURL.String(),
|
|
Key: cfg.Anthropic.Key.String(),
|
|
CircuitBreaker: cbConfig,
|
|
SendActorHeaders: cfg.SendActorHeaders.Value(),
|
|
}, getBedrockConfig(cfg.Bedrock)),
|
|
aibridge.NewCopilotProvider(aibridge.CopilotConfig{
|
|
CircuitBreaker: cbConfig,
|
|
}),
|
|
}
|
|
|
|
reg := prometheus.WrapRegistererWithPrefix("coder_aibridged_", coderAPI.PrometheusRegistry)
|
|
metrics := aibridge.NewMetrics(reg)
|
|
tracer := coderAPI.TracerProvider.Tracer(tracing.TracerName)
|
|
|
|
// Create pool for reusable stateful [aibridge.RequestBridge] instances (one per user).
|
|
pool, err := aibridged.NewCachedBridgePool(aibridged.DefaultPoolOptions, providers, logger.Named("pool"), metrics, tracer) // TODO: configurable size.
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("create request pool: %w", err)
|
|
}
|
|
|
|
// Create daemon.
|
|
srv, err := aibridged.New(ctx, pool, func(dialCtx context.Context) (aibridged.DRPCClient, error) {
|
|
return coderAPI.CreateInMemoryAIBridgeServer(dialCtx)
|
|
}, logger, tracer)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("start in-memory aibridge daemon: %w", err)
|
|
}
|
|
return srv, nil
|
|
}
|
|
|
|
func getBedrockConfig(cfg codersdk.AIBridgeBedrockConfig) *aibridge.AWSBedrockConfig {
|
|
if cfg.Region.String() == "" && cfg.BaseURL.String() == "" && cfg.AccessKey.String() == "" && cfg.AccessKeySecret.String() == "" {
|
|
return nil
|
|
}
|
|
|
|
return &aibridge.AWSBedrockConfig{
|
|
BaseURL: cfg.BaseURL.String(),
|
|
Region: cfg.Region.String(),
|
|
AccessKey: cfg.AccessKey.String(),
|
|
AccessKeySecret: cfg.AccessKeySecret.String(),
|
|
Model: cfg.Model.String(),
|
|
SmallFastModel: cfg.SmallFastModel.String(),
|
|
}
|
|
}
|