Add additional configuration options

- This includes temperature, top_p, frequency_penalty and
  presence_penalty
This commit is contained in:
kardolus
2023-10-29 12:45:21 -04:00
parent c5c942b418
commit 158b25acd1
8 changed files with 501 additions and 108 deletions

View File

@@ -164,18 +164,22 @@ values, the `config.yaml` file, and environment variables, in that respective or
Configuration variables:
| Variable | Description | Default |
|--------------------|-----------------------------------------------------------------------------------|--------------------------------|
| `name` | The prefix for environment variable overrides. | 'openai' |
| `api_key` | Your OpenAI API key. | (none for security) |
| `model` | The GPT model used by the application. | 'gpt-3.5-turbo' |
| `max_tokens` | The maximum number of tokens that can be used in a single API call. | 4096 |
| `role` | The system role | 'You are a helpful assistant.' |
| `thread` | The name of the current chat thread. Each unique thread name has its own context. | 'default' |
| `omit_history` | If true, the chat history will not be used to provide context for the GPT model. | false |
| `url` | The base URL for the OpenAI API. | 'https://api.openai.com' |
| `completions_path` | The API endpoint for completions. | '/v1/chat/completions' |
| `models_path` | The API endpoint for accessing model information. | '/v1/models' |
| Variable | Description | Default |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
| `name` | The prefix for environment variable overrides. | 'openai' |
| `api_key` | Your OpenAI API key. | (none for security) |
| `model` | The GPT model used by the application. | 'gpt-3.5-turbo' |
| `max_tokens` | The maximum number of tokens that can be used in a single API call. | 4096 |
| `role` | The system role | 'You are a helpful assistant.' |
| `temperature` | What sampling temperature to use, between 0 and 2. Higher values make the output more random; lower values make it more focused and deterministic. | 1.0 |
| `frequency_penalty` | Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far. | 0.0 |
| `top_p` | An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. | 1.0 |
| `presence_penalty` | Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far. | 0.0 |
| `thread` | The name of the current chat thread. Each unique thread name has its own context. | 'default' |
| `omit_history` | If true, the chat history will not be used to provide context for the GPT model. | false |
| `url` | The base URL for the OpenAI API. | 'https://api.openai.com' |
| `completions_path` | The API endpoint for completions. | '/v1/chat/completions' |
| `models_path` | The API endpoint for accessing model information. | '/v1/models' |
The defaults can be overridden by providing your own values in the user configuration file,
named `.chatgpt-cli/config.yaml`, located in your home directory.

View File

@@ -160,9 +160,13 @@ func (c *Client) Stream(input string) error {
func (c *Client) createBody(stream bool) ([]byte, error) {
body := types.CompletionsRequest{
Messages: c.History,
Model: c.Config.Model,
Stream: stream,
Messages: c.History,
Model: c.Config.Model,
Temperature: c.Config.Temperature,
TopP: c.Config.TopP,
FrequencyPenalty: c.Config.FrequencyPenalty,
PresencePenalty: c.Config.PresencePenalty,
Stream: stream,
}
return json.Marshal(body)

View File

@@ -22,15 +22,19 @@ import (
//go:generate mockgen -destination=configmocks_test.go -package=client_test github.com/kardolus/chatgpt-cli/config ConfigStore
const (
defaultMaxTokens = 4096
defaultURL = "https://default.openai.com"
defaultName = "default-name"
defaultModel = "gpt-3.5-turbo"
defaultCompletionsPath = "/default/completions"
defaultModelsPath = "/default/models"
defaultThread = "default-thread"
defaultRole = "You are a great default-role"
envApiKey = "api-key"
defaultMaxTokens = 4096
defaultURL = "https://default.openai.com"
defaultName = "default-name"
defaultModel = "gpt-3.5-turbo"
defaultCompletionsPath = "/default/completions"
defaultModelsPath = "/default/models"
defaultThread = "default-thread"
defaultRole = "You are a great default-role"
defaultTemperature = 1.1
defaultTopP = 2.2
defaultFrequencyPenalty = 3.3
defaultPresencePenalty = 4.4
envApiKey = "api-key"
)
var (
@@ -140,7 +144,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
subject := factory.buildClientWithoutConfig()
messages = createMessages(nil, query)
body, err = createBody(messages, subject.Config.Model, false)
body, err = createBody(messages, false)
Expect(err).NotTo(HaveOccurred())
respBytes, err := tt.setupPostReturn()
@@ -192,17 +196,26 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(answer))
}
it("uses the model specified by the configuration instead of the default model", func() {
const model = "overwritten"
it("uses the values specified by the configuration instead of the default values", func() {
const (
model = "overwritten"
temperature = 100.1
topP = 200.2
frequencyPenalty = 300.3
presencePenalty = 400.4
)
messages = createMessages(nil, query)
factory.withoutHistory()
subject := factory.buildClientWithConfig(types.Config{
Model: model,
Model: model,
Temperature: temperature,
TopP: topP,
FrequencyPenalty: frequencyPenalty,
PresencePenalty: presencePenalty,
})
body, err = createBody(messages, model, false)
body, err = createBodyWithConfig(messages, false, model, temperature, topP, frequencyPenalty, presencePenalty)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, nil, body, false)
})
@@ -225,7 +238,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
factory.withHistory(history)
subject := factory.buildClientWithoutConfig()
body, err = createBody(messages, subject.Config.Model, false)
body, err = createBody(messages, false)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, history, body, false)
@@ -244,7 +257,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
messages = createMessages(nil, query)
body, err = createBody(messages, subject.Config.Model, false)
body, err = createBody(messages, false)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, nil, body, true)
@@ -289,7 +302,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
// messages get truncated. Index 1+2 are cut out
messages = append(messages[:1], messages[3:]...)
body, err = createBody(messages, subject.Config.Model, false)
body, err = createBody(messages, false)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, history, body, false)
@@ -308,7 +321,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
subject := factory.buildClientWithoutConfig()
messages = createMessages(nil, query)
body, err = createBody(messages, subject.Config.Model, true)
body, err = createBody(messages, true)
Expect(err).NotTo(HaveOccurred())
errorMsg := "error message"
@@ -323,7 +336,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
testValidHTTPResponse := func(subject *client.Client, history []types.Message, expectedBody []byte) {
messages = createMessages(nil, query)
body, err = createBody(messages, subject.Config.Model, true)
body, err = createBody(messages, true)
Expect(err).NotTo(HaveOccurred())
mockCaller.EXPECT().Post(subject.Config.URL+subject.Config.CompletionsPath, expectedBody, true).Return([]byte(answer), nil)
@@ -344,7 +357,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
subject := factory.buildClientWithoutConfig()
messages = createMessages(nil, query)
body, err = createBody(messages, subject.Config.Model, true)
body, err = createBody(messages, true)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, nil, body)
@@ -368,7 +381,7 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
subject := factory.buildClientWithoutConfig()
messages = createMessages(history, query)
body, err = createBody(messages, subject.Config.Model, true)
body, err = createBody(messages, true)
Expect(err).NotTo(HaveOccurred())
testValidHTTPResponse(subject, history, body)
@@ -443,11 +456,29 @@ func testClient(t *testing.T, when spec.G, it spec.S) {
})
}
func createBody(messages []types.Message, model string, stream bool) ([]byte, error) {
func createBody(messages []types.Message, stream bool) ([]byte, error) {
req := types.CompletionsRequest{
Model: model,
Messages: messages,
Stream: stream,
Model: defaultModel,
Messages: messages,
Stream: stream,
Temperature: defaultTemperature,
TopP: defaultTopP,
FrequencyPenalty: defaultFrequencyPenalty,
PresencePenalty: defaultPresencePenalty,
}
return json.Marshal(req)
}
func createBodyWithConfig(messages []types.Message, stream bool, model string, temperature float64, topP float64, frequencyPenalty float64, presencePenalty float64) ([]byte, error) {
req := types.CompletionsRequest{
Model: model,
Messages: messages,
Stream: stream,
Temperature: temperature,
TopP: topP,
FrequencyPenalty: frequencyPenalty,
PresencePenalty: presencePenalty,
}
return json.Marshal(req)
@@ -481,14 +512,18 @@ type clientFactory struct {
func newClientFactory(mc *MockCaller, mcs *MockConfigStore, mhs *MockHistoryStore) *clientFactory {
mockConfigStore.EXPECT().ReadDefaults().Return(types.Config{
Name: defaultName,
Model: defaultModel,
MaxTokens: defaultMaxTokens,
URL: defaultURL,
CompletionsPath: defaultCompletionsPath,
ModelsPath: defaultModelsPath,
Role: defaultRole,
Thread: defaultThread,
Name: defaultName,
Model: defaultModel,
MaxTokens: defaultMaxTokens,
URL: defaultURL,
CompletionsPath: defaultCompletionsPath,
ModelsPath: defaultModelsPath,
Role: defaultRole,
Thread: defaultThread,
Temperature: defaultTemperature,
PresencePenalty: defaultPresencePenalty,
TopP: defaultTopP,
FrequencyPenalty: defaultFrequencyPenalty,
}).Times(1)
return &clientFactory{

View File

@@ -9,14 +9,18 @@ import (
)
const (
openAIName = "openai"
openAIModel = "gpt-3.5-turbo"
openAIModelMaxTokens = 4096
openAIURL = "https://api.openai.com"
openAICompletionsPath = "/v1/chat/completions"
openAIModelsPath = "/v1/models"
openAIRole = "You are a helpful assistant."
openAIThread = "default"
openAIName = "openai"
openAIModel = "gpt-3.5-turbo"
openAIModelMaxTokens = 4096
openAIURL = "https://api.openai.com"
openAICompletionsPath = "/v1/chat/completions"
openAIModelsPath = "/v1/models"
openAIRole = "You are a helpful assistant."
openAIThread = "default"
openAITemperature = 1.0
openAITopP = 1.0
openAIFrequencyPenalty = 0.0
openAIPresencePenalty = 0.0
)
type ConfigStore interface {
@@ -50,14 +54,18 @@ func (f *FileIO) Read() (types.Config, error) {
func (f *FileIO) ReadDefaults() types.Config {
return types.Config{
Name: openAIName,
Model: openAIModel,
Role: openAIRole,
MaxTokens: openAIModelMaxTokens,
URL: openAIURL,
CompletionsPath: openAICompletionsPath,
ModelsPath: openAIModelsPath,
Thread: openAIThread,
Name: openAIName,
Model: openAIModel,
Role: openAIRole,
MaxTokens: openAIModelMaxTokens,
URL: openAIURL,
CompletionsPath: openAICompletionsPath,
ModelsPath: openAIModelsPath,
Thread: openAIThread,
Temperature: openAITemperature,
TopP: openAITopP,
FrequencyPenalty: openAIFrequencyPenalty,
PresencePenalty: openAIPresencePenalty,
}
}

View File

@@ -78,6 +78,10 @@ func replaceByConfigFile(defaultConfig, userConfig types.Config) types.Config {
if userBool := userField.Bool(); &userBool != nil {
defaultField.SetBool(userBool)
}
case reflect.Float64:
if userFloat := userField.Float(); userFloat != 0.0 {
defaultField.SetFloat(userFloat)
}
}
}
@@ -107,6 +111,9 @@ func replaceByEnvironment(configuration types.Config) types.Config {
case reflect.Bool:
boolValue, _ := strconv.ParseBool(value)
field.SetBool(boolValue)
case reflect.Float64:
floatValue, _ := strconv.ParseFloat(value, 64)
field.SetFloat(floatValue)
}
}
}

View File

@@ -23,16 +23,20 @@ func TestUnitConfigManager(t *testing.T) {
func testConfig(t *testing.T, when spec.G, it spec.S) {
const (
defaultMaxTokens = 10
defaultName = "default-name"
defaultURL = "default-url"
defaultModel = "default-model"
defaultRole = "default-role"
defaultApiKey = "default-api-key"
defaultThread = "default-thread"
defaultCompletionsPath = "default-completions-path"
defaultModelsPath = "default-models-path"
defaultOmitHistory = false
defaultMaxTokens = 10
defaultName = "default-name"
defaultURL = "default-url"
defaultModel = "default-model"
defaultRole = "default-role"
defaultApiKey = "default-api-key"
defaultThread = "default-thread"
defaultCompletionsPath = "default-completions-path"
defaultModelsPath = "default-models-path"
defaultOmitHistory = false
defaultTemperature = 1.1
defaultTopP = 2.2
defaultFrequencyPenalty = 3.3
defaultPresencePenalty = 4.4
)
var (
@@ -48,16 +52,20 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
mockConfigStore = NewMockConfigStore(mockCtrl)
defaultConfig = types.Config{
Name: defaultName,
APIKey: defaultApiKey,
Model: defaultModel,
MaxTokens: defaultMaxTokens,
URL: defaultURL,
CompletionsPath: defaultCompletionsPath,
ModelsPath: defaultModelsPath,
OmitHistory: defaultOmitHistory,
Role: defaultRole,
Thread: defaultThread,
Name: defaultName,
APIKey: defaultApiKey,
Model: defaultModel,
MaxTokens: defaultMaxTokens,
URL: defaultURL,
CompletionsPath: defaultCompletionsPath,
ModelsPath: defaultModelsPath,
OmitHistory: defaultOmitHistory,
Role: defaultRole,
Thread: defaultThread,
Temperature: defaultTemperature,
TopP: defaultTopP,
FrequencyPenalty: defaultFrequencyPenalty,
PresencePenalty: defaultPresencePenalty,
}
envPrefix = strings.ToUpper(defaultConfig.Name) + "_"
@@ -91,6 +99,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided model", func() {
userModel := "the-model"
@@ -110,6 +122,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided name", func() {
userName := "the-name"
@@ -129,6 +145,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided max-tokens", func() {
userMaxTokens := 42
@@ -148,7 +168,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided URL", func() {
userURL := "the-user-url"
@@ -187,6 +210,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided models-path", func() {
modelsPath := "the-models-path"
@@ -206,6 +233,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided api-key", func() {
apiKey := "new-api-key"
@@ -225,6 +256,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided omit-history", func() {
omitHistory := true
@@ -244,6 +279,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.OmitHistory).To(Equal(omitHistory))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided thread", func() {
userThread := "user-thread"
@@ -263,6 +302,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(userThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided role", func() {
userRole := "user-role"
@@ -282,8 +325,104 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(userRole))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the OMIT_HISTORY environment variable", func() {
it("gives precedence to the user provided temperature", func() {
userTemperature := 100.1
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{Temperature: userTemperature}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Temperature).To(Equal(userTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided top_p", func() {
userTopP := 200.2
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{TopP: userTopP}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(userTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided frequency_penalty", func() {
userFrequencyPenalty := 300.3
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{FrequencyPenalty: userFrequencyPenalty}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(userFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the user provided presence_penalty", func() {
userPresencePenalty := 400.4
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{PresencePenalty: userPresencePenalty}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(userPresencePenalty))
})
it("gives precedence to the OMIT_HISTORY environment variable when environment is true", func() {
var (
environmentValue = true
configValue = false
@@ -306,6 +445,38 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.OmitHistory).To(Equal(environmentValue))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the OMIT_HISTORY environment variable when environment is false", func() {
var (
environmentValue = false
configValue = true
)
Expect(os.Setenv(envPrefix+"OMIT_HISTORY", strconv.FormatBool(environmentValue))).To(Succeed())
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{OmitHistory: configValue}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.OmitHistory).To(Equal(environmentValue))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the THREAD environment variable", func() {
var (
@@ -330,6 +501,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(environmentValue))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the ROLE environment variable", func() {
var (
@@ -354,6 +529,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Role).To(Equal(environmentValue))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the API_KEY environment variable", func() {
var (
@@ -378,6 +557,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the MODEL environment variable", func() {
var (
@@ -402,6 +585,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the MAX_TOKENS environment variable", func() {
var (
@@ -426,6 +613,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the URL environment variable", func() {
var (
@@ -450,6 +641,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the COMPLETIONS_PATH environment variable", func() {
var (
@@ -474,6 +669,10 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the MODELS_PATH environment variable", func() {
var (
@@ -498,6 +697,122 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the TEMPERATURE environment variable", func() {
var (
envTemperature = 5.5
confTemperature = 6.6
)
Expect(os.Setenv(envPrefix+"TEMPERATURE", fmt.Sprintf("%f", envTemperature))).To(Succeed())
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{Temperature: confTemperature}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(envTemperature))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the TOP_P environment variable", func() {
var (
envTopP = 7.7
confTopP = 8.8
)
Expect(os.Setenv(envPrefix+"TOP_P", fmt.Sprintf("%f", envTopP))).To(Succeed())
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{TopP: confTopP}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.TopP).To(Equal(envTopP))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the FREQUENCY_PENALTY environment variable", func() {
var (
envFrequencyPenalty = 5.5
confFrequencyPenalty = 6.6
)
Expect(os.Setenv(envPrefix+"FREQUENCY_PENALTY", fmt.Sprintf("%f", envFrequencyPenalty))).To(Succeed())
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{FrequencyPenalty: confFrequencyPenalty}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.FrequencyPenalty).To(Equal(envFrequencyPenalty))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
Expect(subject.Config.PresencePenalty).To(Equal(defaultPresencePenalty))
})
it("gives precedence to the PRESENCE_PENALTY environment variable", func() {
var (
envPresencePenalty = 5.5
confPresencePenalty = 6.6
)
Expect(os.Setenv(envPrefix+"PRESENCE_PENALTY", fmt.Sprintf("%f", envPresencePenalty))).To(Succeed())
mockConfigStore.EXPECT().ReadDefaults().Return(defaultConfig).Times(1)
mockConfigStore.EXPECT().Read().Return(types.Config{PresencePenalty: confPresencePenalty}, nil).Times(1)
subject := configmanager.New(mockConfigStore).WithEnvironment()
Expect(subject.Config.Name).To(Equal(defaultName))
Expect(subject.Config.Model).To(Equal(defaultModel))
Expect(subject.Config.MaxTokens).To(Equal(defaultMaxTokens))
Expect(subject.Config.URL).To(Equal(defaultURL))
Expect(subject.Config.CompletionsPath).To(Equal(defaultCompletionsPath))
Expect(subject.Config.ModelsPath).To(Equal(defaultModelsPath))
Expect(subject.Config.APIKey).To(Equal(defaultApiKey))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(subject.Config.Temperature).To(Equal(defaultTemperature))
Expect(subject.Config.FrequencyPenalty).To(Equal(defaultFrequencyPenalty))
Expect(subject.Config.PresencePenalty).To(Equal(envPresencePenalty))
Expect(subject.Config.TopP).To(Equal(defaultTopP))
})
})
@@ -517,9 +832,13 @@ func testConfig(t *testing.T, when spec.G, it spec.S) {
Expect(result).To(ContainSubstring(defaultCompletionsPath))
Expect(result).To(ContainSubstring(defaultModelsPath))
Expect(result).To(ContainSubstring(fmt.Sprintf("%d", defaultMaxTokens)))
Expect(subject.Config.OmitHistory).To(Equal(defaultOmitHistory))
Expect(subject.Config.Role).To(Equal(defaultRole))
Expect(subject.Config.Thread).To(Equal(defaultThread))
Expect(result).To(ContainSubstring(fmt.Sprintf("%t", defaultOmitHistory)))
Expect(result).To(ContainSubstring(defaultRole))
Expect(result).To(ContainSubstring(defaultThread))
Expect(result).To(ContainSubstring(fmt.Sprintf("%.1f", defaultTemperature)))
Expect(result).To(ContainSubstring(fmt.Sprintf("%.1f", defaultTopP)))
Expect(result).To(ContainSubstring(fmt.Sprintf("%.1f", defaultFrequencyPenalty)))
Expect(result).To(ContainSubstring(fmt.Sprintf("%.1f", defaultPresencePenalty)))
})
})
@@ -564,4 +883,8 @@ func cleanEnv(envPrefix string) {
Expect(os.Unsetenv(envPrefix + "OMIT_HISTORY")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "THREAD")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "ROLE")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "TEMPERATURE")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "TOP_P")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "FREQUENCY_PENALTY")).To(Succeed())
Expect(os.Unsetenv(envPrefix + "PRESENCE_PENALTY")).To(Succeed())
}

View File

@@ -1,9 +1,13 @@
package types
type CompletionsRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Stream bool `json:"stream"`
Model string `json:"model"`
Temperature float64 `json:"temperature"`
TopP float64 `json:"top_p"`
FrequencyPenalty float64 `json:"frequency_penalty"`
PresencePenalty float64 `json:"presence_penalty"`
Messages []Message `json:"messages"`
Stream bool `json:"stream"`
}
type Message struct {
@@ -31,11 +35,15 @@ type Choice struct {
}
type Data struct {
ID string `json:"id"`
Object string `json:"object"`
Created int `json:"created"`
Model string `json:"model"`
Choices []struct {
ID string `json:"id"`
Object string `json:"object"`
Created int `json:"created"`
Model string `json:"model"`
Temperature float64 `json:"temperature"`
TopP float64 `json:"top_p"`
FrequencyPenalty float64 `json:"frequency_penalty"`
PresencePenalty float64 `json:"presence_penalty"`
Choices []struct {
Delta map[string]string `json:"delta"`
Index int `json:"index"`
FinishReason string `json:"finish_reason"`

View File

@@ -1,14 +1,18 @@
package types
type Config struct {
Name string `yaml:"name"`
APIKey string `yaml:"api_key"`
Model string `yaml:"model"`
MaxTokens int `yaml:"max_tokens"`
Role string `yaml:"role"`
Thread string `yaml:"thread"`
OmitHistory bool `yaml:"omit_history"`
URL string `yaml:"url"`
CompletionsPath string `yaml:"completions_path"`
ModelsPath string `yaml:"models_path"`
Name string `yaml:"name"`
APIKey string `yaml:"api_key"`
Model string `yaml:"model"`
MaxTokens int `yaml:"max_tokens"`
Role string `yaml:"role"`
Temperature float64 `yaml:"temperature"`
TopP float64 `yaml:"top_p"`
FrequencyPenalty float64 `yaml:"frequency_penalty"`
PresencePenalty float64 `yaml:"presence_penalty"`
Thread string `yaml:"thread"`
OmitHistory bool `yaml:"omit_history"`
URL string `yaml:"url"`
CompletionsPath string `yaml:"completions_path"`
ModelsPath string `yaml:"models_path"`
}