Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#51: Build routers & models based on provided config #53

Merged
merged 10 commits into from
Jan 1, 2024
8 changes: 8 additions & 0 deletions config.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ telemetry:
#api:
# http:
# ...

routers:
language:
- id: myrouter
models:
- id: openai
openai:
api_key: "${env:OPENAI_API_KEY}"
79 changes: 79 additions & 0 deletions pkg/providers/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package providers

import (
"errors"
"fmt"
"time"

"glide/pkg/providers/openai"
"glide/pkg/telemetry"
)

var ErrProviderNotFound = errors.New("provider not found")

type LangModelConfig struct {
ID string `yaml:"id"`
Enabled bool `yaml:"enabled"`
Timeout *time.Duration `yaml:"timeout,omitempty"`
OpenAI *openai.Config
// Add other providers like
// Cohere *cohere.Config
// Anthropic *anthropic.Config
}

func DefaultLangModelConfig() *LangModelConfig {
defaultTimeout := 10 * time.Second

return &LangModelConfig{
Enabled: true,
Timeout: &defaultTimeout,
}
}

func (c *LangModelConfig) ToModel(tel *telemetry.Telemetry) (LanguageModel, error) {
if c.OpenAI != nil {
client, err := openai.NewClient(c.OpenAI, tel)
if err != nil {
return nil, fmt.Errorf("error initing openai client: %v", err)
}

return client, nil
}

return nil, ErrProviderNotFound
}

func (c *LangModelConfig) validateOneProvider() error {
providersConfigured := 0

if c.OpenAI != nil {
providersConfigured++
}

// check other providers here
if providersConfigured == 0 {
return fmt.Errorf("exactly one provider must be cofigured for model \"%v\", none is configured", c.ID)
}

if providersConfigured > 1 {
return fmt.Errorf(
"exactly one provider must be cofigured for model \"%v\", %v are configured",
c.ID,
providersConfigured,
)
}

return nil
}

func (c *LangModelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = *DefaultLangModelConfig()

type plain LangModelConfig // to avoid recursion

if err := unmarshal((*plain)(c)); err != nil {
return err
}

return c.validateOneProvider()
}
5 changes: 5 additions & 0 deletions pkg/providers/errs/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package errs

import "errors"

var ErrProviderUnavailable = errors.New("provider is not available")
12 changes: 0 additions & 12 deletions pkg/providers/language.go

This file was deleted.

4 changes: 2 additions & 2 deletions pkg/providers/openai/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"io"
"net/http"

"glide/pkg/providers"
"glide/pkg/providers/errs"

"glide/pkg/api/schemas"
"go.uber.org/zap"
Expand Down Expand Up @@ -144,7 +144,7 @@ func (c *Client) doChatRequest(ctx context.Context, payload *ChatRequest) (*sche
zap.Any("headers", resp.Header),
)

return nil, providers.ErrProviderUnavailable
return nil, errs.ErrProviderUnavailable
}

// Parse response
Expand Down
11 changes: 9 additions & 2 deletions pkg/providers/provider.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package providers

import "errors"
import (
"context"

var ErrProviderUnavailable = errors.New("provider is not available")
"glide/pkg/api/schemas"
)

// ModelProvider defines an interface all model providers should support
type ModelProvider interface {
Provider() string
}

// LanguageModel defines the interface a provider should fulfill to be able to serve language chat requests
type LanguageModel interface {
Chat(ctx context.Context, request *schemas.UnifiedChatRequest) (*schemas.UnifiedChatResponse, error)
}
54 changes: 5 additions & 49 deletions pkg/routers/config.go
Original file line number Diff line number Diff line change
@@ -1,63 +1,19 @@
package routers

import (
"fmt"

"glide/pkg/providers/openai"
"glide/pkg/providers"
"glide/pkg/routers/strategy"
)

type Config struct {
LanguageRouters []LangRouterConfig `yaml:"language"`
}

type LangModel struct {
ID string `yaml:"id"`
TimeoutMs *int `yaml:"timeout_ms,omitempty"` // TODO: try to use Duration to bring more flexibility
OpenAI *openai.Config
// Add other providers like
// Cohere *cohere.Config
// Anthropic *anthropic.Config
}

func (m *LangModel) validateOneProvider() error {
providersConfigured := 0

if m.OpenAI != nil {
providersConfigured++
}

// check other providers here
if providersConfigured == 0 {
return fmt.Errorf("exactly one provider must be cofigured for model \"%v\", none is configured", m.ID)
}

if providersConfigured > 1 {
return fmt.Errorf(
"exactly one provider must be cofigured for model \"%v\", %v are configured",
m.ID,
providersConfigured,
)
}

return nil
}

func (m *LangModel) UnmarshalYAML(unmarshal func(interface{}) error) error {
type plain LangModel // to avoid recursion

if err := unmarshal((*plain)(m)); err != nil {
return err
}

return m.validateOneProvider()
}

type LangRouterConfig struct {
ID string `yaml:"id"`
Enabled bool `yaml:"enabled"`
RoutingStrategy strategy.RoutingStrategy `yaml:"strategy"`
Models []LangModel `yaml:"models"`
ID string `yaml:"id"`
Enabled bool `yaml:"enabled"`
RoutingStrategy strategy.RoutingStrategy `yaml:"strategy"`
Models []providers.LangModelConfig `yaml:"models"`
}

func DefaultLangRouterConfig() LangRouterConfig {
Expand Down
72 changes: 55 additions & 17 deletions pkg/routers/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,76 @@ package routers
import (
"errors"

"go.uber.org/multierr"
"go.uber.org/zap"

"glide/pkg/telemetry"
)

var ErrRouterNotFound = errors.New("no router found with given ID")

type RouterManager struct {
config *Config
telemetry *telemetry.Telemetry
myRouter *LangRouter // TODO: replace by list of routers
config *Config
telemetry *telemetry.Telemetry
langRouterMap *map[string]*LangRouter
langRouters []*LangRouter
}

// NewManager creates a new instance of Router Manager that creates, holds and returns all routers
func NewManager(cfg *Config, tel *telemetry.Telemetry) (*RouterManager, error) {
// TODO: init routers by config
router, err := NewLangRouter(tel)
if err != nil {
return nil, err
}

return &RouterManager{
manager := RouterManager{
config: cfg,
telemetry: tel,
myRouter: router,
}, nil
}

err := manager.BuildRouters(cfg.LanguageRouters)

return &manager, err
}

func (r *RouterManager) BuildRouters(routerConfigs []LangRouterConfig) error {
routerMap := make(map[string]*LangRouter, len(routerConfigs))
routers := make([]*LangRouter, 0, len(routerConfigs))

var errs error

for idx, routerConfig := range routerConfigs {
if !routerConfig.Enabled {
r.telemetry.Logger.Info("router is disabled, skipping", zap.String("routerID", routerConfig.ID))
continue
}

r.telemetry.Logger.Debug("init router", zap.String("routerID", routerConfig.ID))

router, err := NewLangRouter(&routerConfigs[idx], r.telemetry)
if err != nil {
errs = multierr.Append(errs, err)
continue
}

routerMap[routerConfig.ID] = router
routers = append(routers, router)
}

if errs != nil {
return errs
}

r.langRouterMap = &routerMap
r.langRouters = routers

return nil
}

func (r *RouterManager) GetLangRouters() []*LangRouter {
return r.langRouters
}

// Get returns a router by type and ID
// GetLangRouter returns a router by type and ID
func (r *RouterManager) GetLangRouter(routerID string) (*LangRouter, error) {
// TODO: implement actual logic
if routerID != "myrouter" {
return nil, ErrRouterNotFound
if router, found := (*r.langRouterMap)[routerID]; found {
return router, nil
}

return r.myRouter, nil
return nil, ErrRouterNotFound
}
Loading
Loading