Skip to content

Commit

Permalink
Merge pull request #32 from bricks-cloud/v1.4.2
Browse files Browse the repository at this point in the history
[V1.5.0] Support monthly spend limit on API keys
  • Loading branch information
spikelu2016 committed Dec 14, 2023
2 parents 2bd4e93 + 448f6f1 commit d140b69
Show file tree
Hide file tree
Showing 9 changed files with 798 additions and 36 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 1.5.0 - 2023-12-14
### Added
- Added support for OpenAI audio endpoints
- Added support for OpenAI image endpoints

### Fixed
- Removed deperacated fields when updating keys
- Fixed issues with inconsistent rate limit cache expiration date

## 1.4.1 - 2023-12-07
### Added
- Added path access control at the API key level
Expand Down
55 changes: 50 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ PathConfig
> | settingId | required | `string` | 98daa3ae-961d-4253-bf6a-322a32fdca3d | API key |
> | costLimitInUsd | optional | `float64` | `5.5` | Total spend limit of the API key.
> | costLimitInUsdOverTime | optional | `float64` | `2` | Total spend within period of time. This field is required if costLimitInUsdUnit is specified. |
> | costLimitInUsdUnit | optional | `enum` | d | Time unit for costLimitInUsdOverTime. Possible values are [`h`, `d`]. |
> | costLimitInUsdUnit | optional | `enum` | d | Time unit for costLimitInUsdOverTime. Possible values are [`h`, `d`, `mo`]. |
> | rateLimitOverTime | optional | `int` | 2 | rate limit over period of time. This field is required if rateLimitUnit is specified. |
> | rateLimitUnit | optional | `enum` | m | Time unit for rateLimitOverTime. Possible values are [`h`, `m`, `s`, `d`] |
> | ttl | optional | `string` | 2d | time to live. Available units are [`s`, `m`, `h`] |
Expand Down Expand Up @@ -279,10 +279,6 @@ PathConfig
> | tags | optional | `[]string` | `["org-tag-12345"]` | Identifiers associated with the key. |
> | revoked | optional | `boolean` | `true` | Indicator for whether the key is revoked. |
> | revokedReason| optional | `string` | The key has expired | Reason for why the key is revoked. |
> | costLimitInUsdOverTime | optional | `float64` | `2` | Total spend within period of time. This field is required if costLimitInUsdUnit is specified. |
> | costLimitInUsdUnit | optional | `enum` | d | Time unit for costLimitInUsdOverTime. Possible values are [`h`, `d`]. |
> | rateLimitOverTime | optional | `int` | `2` | rate limit over period of time. This field is required if rateLimitUnit is specified. |
> | rateLimitUnit | optional | `enum` | m | Time unit for rateLimitOverTime. Possible values are [`h`, `m`, `s`, `d`] |
> | allowedPaths | optional | `[]PathConfig` | 2d | Pathes allowed for access. |
##### Error Response
Expand Down Expand Up @@ -791,6 +787,55 @@ This endpoint is set up for retrieving an OpenAI file content. Documentation for

</details>

### Images
<details>
<summary>Generate images: <code>POST</code> <code><b>/api/providers/openai/v1/images/generations</b></code></summary>

##### Description
This endpoint is set up for generating OpenAI images. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/images/create).

</details>

<details>
<summary>Edit images: <code>POST</code> <code><b>/api/providers/openai/v1/images/edits</b></code></summary>

##### Description
This endpoint is set up for editting OpenAI generated images. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/images/createEdit).

</details>

<details>
<summary>Create image variations: <code>POST</code> <code><b>/api/providers/openai/v1/images/variations</b></code></summary>

##### Description
This endpoint is set up for creating OpenAI image variations. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/images/createVariation).

</details>

### Voices
<details>
<summary>Create speech: <code>POST</code> <code><b>/api/providers/openai/v1/audio/speech</b></code></summary>

##### Description
This endpoint is set up for creating speeches. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/audio/createSpeech).

</details>

<details>
<summary>Create transcriptions: <code>POST</code> <code><b>/api/providers/openai/v1/audio/transcriptions</b></code></summary>

##### Description
This endpoint is set up for editting generated images. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/audio/createTranscription).

</details>

<details>
<summary>Create translations: <code>POST</code> <code><b>/api/providers/openai/v1/audios/translations</b></code></summary>

##### Description
This endpoint is set up for creating translations. Documentation for this endpoint can be found [here](https://platform.openai.com/docs/api-reference/audio/createTranslation).
</details>

### Assistants
<details>
<summary>Create assistant: <code>POST</code> <code><b>/api/providers/openai/v1/assistants</b></code></summary>
Expand Down
3 changes: 2 additions & 1 deletion internal/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (rk *RequestKey) Validate() error {
return internal_errors.NewValidationError("cost limit unit can not be empty if cost limit over time is specified")
}

if rk.CostLimitInUsdUnit != DayTimeUnit && rk.CostLimitInUsdUnit != HourTimeUnit {
if rk.CostLimitInUsdUnit != DayTimeUnit && rk.CostLimitInUsdUnit != HourTimeUnit && rk.CostLimitInUsdUnit != MonthTimeUnit {
return internal_errors.NewValidationError("cost limit unit can not be identified")
}
}
Expand All @@ -184,6 +184,7 @@ const (
MinuteTimeUnit TimeUnit = "m"
SecondTimeUnit TimeUnit = "s"
DayTimeUnit TimeUnit = "d"
MonthTimeUnit TimeUnit = "mo"
)

type ResponseKey struct {
Expand Down
84 changes: 84 additions & 0 deletions internal/server/web/proxy/audio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package proxy

import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

type SpeechRequest struct {
Model string `json:"model"`
Input string `json:"input"`
Voice string `json:"voice"`
ResponseFormat string `json:"response_format"`
Speed float64 `json:"speed"`
}

func logCreateSpeechRequest(log *zap.Logger, sr *SpeechRequest, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
zap.String("model", sr.Model),
zap.String("voice", sr.Voice),
}

if !private {
fields = append(fields, zap.String("input", sr.Input))
}

if len(sr.ResponseFormat) != 0 {
fields = append(fields, zap.String("response_format", sr.ResponseFormat))
}

if sr.Speed != 0 {
fields = append(fields, zap.Float64("speed", sr.Speed))
}

log.Info("openai create speech request", fields...)
}
}

func logCreateTranscriptionRequest(log *zap.Logger, model, language, prompt, responseFormat string, temperature float64, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
zap.String("model", model),
}

if !private && len(prompt) != 0 {
fields = append(fields, zap.String("prompt", prompt))
}

if len(language) != 0 {
fields = append(fields, zap.String("language", language))
}

if len(responseFormat) != 0 {
fields = append(fields, zap.String("response_format", responseFormat))
}

if temperature != 0 {
fields = append(fields, zap.Float64("temperature", temperature))
}

log.Info("openai create transcription request", fields...)
}
}

func logCreateTranslationRequest(log *zap.Logger, model, prompt, responseFormat string, temperature float64, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
zap.String("model", model),
}

if !private && len(prompt) == 0 {
fields = append(fields, zap.String("prompt", prompt))
}

if len(responseFormat) != 0 {
fields = append(fields, zap.String("response_format", responseFormat))
}

log.Info("openai create translation request", fields...)
}
}
2 changes: 1 addition & 1 deletion internal/server/web/proxy/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func logRetrieveFileContentResponse(log *zap.Logger, data []byte, prod bool, cid
}
}

func logUploadFileRequest(log *zap.Logger, data []byte, prod bool, cid, purpose string) {
func logUploadFileRequest(log *zap.Logger, prod bool, cid, purpose string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
Expand Down
116 changes: 116 additions & 0 deletions internal/server/web/proxy/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package proxy

import (
"encoding/json"

goopenai "github.com/sashabaranov/go-openai"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

func logCreateImageRequest(log *zap.Logger, ir *goopenai.ImageRequest, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
zap.String("model", ir.Model),
zap.Int("n", ir.N),
zap.String("quality", ir.Quality),
zap.String("size", ir.Size),
zap.String("style", ir.Style),
zap.String("response_format", ir.ResponseFormat),
zap.String("user", ir.User),
}

if !private && len(ir.Prompt) != 0 {
fields = append(fields, zap.String("prompt", ir.Prompt))
}

log.Info("openai create image request", fields...)
}
}

func logEditImageRequest(log *zap.Logger, prompt, model string, n int, size, responseFormat, user string, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
}

if !private && len(prompt) != 0 {
fields = append(fields, zap.String("prompt", prompt))
}

if len(model) != 0 {
fields = append(fields, zap.String("prompt", model))
}

if n != 0 {
fields = append(fields, zap.Int("n", n))
}

if len(size) != 0 {
fields = append(fields, zap.String("size", size))
}

if len(responseFormat) != 0 {
fields = append(fields, zap.String("response_format", responseFormat))
}

if len(user) != 0 {
fields = append(fields, zap.String("user", user))
}

log.Info("openai edit image request", fields...)
}
}

func logImageVariationsRequest(log *zap.Logger, model string, n int, size, responseFormat, user string, prod, private bool, cid string) {
if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
}

if len(model) != 0 {
fields = append(fields, zap.String("model", model))
}

if n != 0 {
fields = append(fields, zap.Int("n", n))
}

if len(size) != 0 {
fields = append(fields, zap.String("size", size))
}

if len(responseFormat) != 0 {
fields = append(fields, zap.String("response_format", user))
}

if len(user) != 0 {
fields = append(fields, zap.String("user", user))
}

log.Info("openai image variations request", fields...)
}
}

func logImageResponse(log *zap.Logger, data []byte, prod, private bool, cid string) {
ir := &goopenai.ImageResponse{}
err := json.Unmarshal(data, ir)
if err != nil {
logError(log, "error when unmarshalling image response", prod, cid, err)
return
}

if prod {
fields := []zapcore.Field{
zap.String(correlationId, cid),
zap.Int64("created", ir.Created),
}

if !private && len(ir.Data) != 0 {
fields = append(fields, zap.Any("data", ir.Data))
}

log.Info("openai image response", fields...)
}
}
Loading

0 comments on commit d140b69

Please sign in to comment.