Skip to content

Commit

Permalink
Adds basic translator and api schemas (#27)
Browse files Browse the repository at this point in the history
This migrates the translation logic from the PoC code in poc-showcasing
branch. Nothing special in here - just the manual translation plus the 
barebone API definitions for each OpenAI and AWS Bedrock.

Just in case anyway curious about why not using OpenAPI generator for
the OpenAI part, here's the comment in the apischema/openai package:
```
// Package openai contains the followings are the OpenAI API schema definitions.
// Note that we intentionally do not use the code generation tools like OpenAPI Generator not only to keep the code simple
// but also because the OpenAI's OpenAPI definition is not compliant with the spec and the existing tools do not work well.
```

Especially, the OpenAPI schema is a bit tricky especially on the
streaming=true/false
conditionals. Given that only a few endpoints will be supported here
anyways,
it's not that problematic to have the manually written data structure.

---------

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
  • Loading branch information
mathetake authored Dec 9, 2024
1 parent 6bd91fc commit 683c3a2
Show file tree
Hide file tree
Showing 10 changed files with 1,253 additions and 0 deletions.
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module github.com/envoyproxy/ai-gateway
go 1.23.2

require (
github.com/aws/aws-sdk-go v1.55.5
github.com/envoyproxy/go-control-plane v0.13.1
github.com/stretchr/testify v1.10.0
k8s.io/apimachinery v0.31.3
sigs.k8s.io/controller-runtime v0.19.3
Expand All @@ -12,8 +14,10 @@ require (
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
Expand All @@ -36,6 +40,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
Expand All @@ -53,6 +58,8 @@ require (
golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.5.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
Expand Down Expand Up @@ -46,6 +54,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand All @@ -71,6 +81,8 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -143,6 +155,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
68 changes: 68 additions & 0 deletions internal/apischema/awsbedrock/awsbedrock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package awsbedrock

import (
"encoding/json"
"strings"
)

// ConverseRequest is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html#API_runtime_Converse_RequestBody
type ConverseRequest struct {
Messages []Message `json:"messages,omitempty"`
}

// Message is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Message.html#bedrock-Type-runtime_Message-content
type Message struct {
Role string `json:"role,omitempty"`
Content []ContentBlock `json:"content,omitempty"`
}

// ContentBlock is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ContentBlock.html
type ContentBlock struct {
Text string `json:"text,omitempty"`
}

// ConverseResponse is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html#API_runtime_Converse_ResponseElements
type ConverseResponse struct {
Output ConverseResponseOutput `json:"output,omitempty"`
Usage TokenUsage `json:"usage,omitempty"`
}

// ConverseResponseOutput is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseOutput.html
type ConverseResponseOutput struct {
Message Message `json:"message,omitempty"`
}

// TokenUsage is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_TokenUsage.html
type TokenUsage struct {
InputTokens int `json:"inputTokens,omitempty"`
OutputTokens int `json:"outputTokens,omitempty"`
TotalTokens int `json:"totalTokens,omitempty"`
}

// ConverseStreamEvent is the union of all possible event types in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html
type ConverseStreamEvent struct {
ContentBlockIndex int `json:"contentBlockIndex,omitempty"`
Delta *ConverseStreamEventContentBlockDelta `json:"delta,omitempty"`
Role *string `json:"role,omitempty"`
StopReason *string `json:"stopReason,omitempty"`
Usage *TokenUsage `json:"usage,omitempty"`
}

// String implements fmt.Stringer.
func (c ConverseStreamEvent) String() string {
buf, _ := json.Marshal(c)
return strings.ReplaceAll(string(buf), ",", ", ")
}

// ConverseStreamEventContentBlockDelta is defined in the AWS Bedrock API:
// https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ContentBlockDelta.html
type ConverseStreamEventContentBlockDelta struct {
Text string `json:"text,omitempty"`
}
108 changes: 108 additions & 0 deletions internal/apischema/openai/openai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Package openai contains the followings are the OpenAI API schema definitions.
// Note that we intentionally do not use the code generation tools like OpenAPI Generator not only to keep the code simple
// but also because the OpenAI's OpenAPI definition is not compliant with the spec and the existing tools do not work well.
package openai

import (
"encoding/json"
"strings"
)

// ChatCompletionRequest represents a request to /v1/chat/completions.
// https://platform.openai.com/docs/api-reference/chat/create
type ChatCompletionRequest struct {
// Model is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-model
Model string `json:"model"`

// Messages is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages
Messages []ChatCompletionRequestMessage `json:"messages"`

// Stream is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream
Stream bool `json:"stream,omitempty"`
}

// ChatCompletionRequestMessage represents a message in a ChatCompletionRequest.
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages
type ChatCompletionRequestMessage struct {
// Role is the role of the message. The role of the message (whether it represents the user or the AI).
Role string `json:"role,omitempty"`
// Content is the content of the message. Mainly this is a string, but it can be more complex.
Content any `json:"content,omitempty"`
}

// ChatCompletionResponse represents a response from /v1/chat/completions.
// https://platform.openai.com/docs/api-reference/chat/object
type ChatCompletionResponse struct {
// Choices are described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-choices
Choices []ChatCompletionResponseChoice `json:"choices,omitempty"`

// Object is always "chat.completion" for completions.
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-object
Object string `json:"object,omitempty"`

// Usage is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-usage
Usage ChatCompletionResponseUsage `json:"usage,omitempty"`
}

// ChatCompletionResponseChoice is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-choices
type ChatCompletionResponseChoice struct {
// Message is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-choices
Message ChatCompletionResponseChoiceMessage `json:"message,omitempty"`
}

// ChatCompletionResponseChoiceMessage is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-choices
type ChatCompletionResponseChoiceMessage struct {
Content *string `json:"content,omitempty"`
Role string `json:"role,omitempty"`
}

// ChatCompletionResponseUsage is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/object#chat/object-usage
type ChatCompletionResponseUsage struct {
CompletionTokens int `json:"completion_tokens,omitempty"`
PromptTokens int `json:"prompt_tokens,omitempty"`
TotalTokens int `json:"total_tokens,omitempty"`
}

// ChatCompletionResponseChunk is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/streaming#chat-create-messages
type ChatCompletionResponseChunk struct {
// Choices are described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-choices
Choices []ChatCompletionResponseChunkChoice `json:"choices,omitempty"`

// Object is always "chat.completion.chunk" for completions.
// https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-object
Object string `json:"object,omitempty"`

// Usage is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-usage
Usage *ChatCompletionResponseUsage `json:"usage,omitempty"`
}

// String implements fmt.Stringer.
func (c *ChatCompletionResponseChunk) String() string {
buf, _ := json.Marshal(c)
return strings.ReplaceAll(string(buf), ",", ", ")
}

// ChatCompletionResponseChunkChoice is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-choices
type ChatCompletionResponseChunkChoice struct {
Delta *ChatCompletionResponseChunkChoiceDelta `json:"delta,omitempty"`
}

// ChatCompletionResponseChunkChoiceDelta is described in the OpenAI API documentation:
// https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-choices
type ChatCompletionResponseChunkChoiceDelta struct {
Content *string `json:"content,omitempty"`
Role *string `json:"role,omitempty"`
}
Loading

0 comments on commit 683c3a2

Please sign in to comment.