feat: optimize types & add full comments

This commit is contained in:
WqyJh
2024-10-16 16:15:52 +08:00
parent 72b8f69c00
commit b31d3a3e53
14 changed files with 508 additions and 143 deletions
+4
View File
@@ -64,24 +64,28 @@ type connectOption struct {
type ConnectOption func(*connectOption)
// WithModel sets the model to use for the connection.
func WithModel(model string) ConnectOption {
return func(opts *connectOption) {
opts.model = model
}
}
// WithDialOptions sets the dial options for the connection.
func WithDialOptions(dialOptions *websocket.DialOptions) ConnectOption {
return func(opts *connectOption) {
opts.dialOptions = dialOptions
}
}
// WithReadLimit sets the read limit for the connection.
func WithReadLimit(limit int64) ConnectOption {
return func(opts *connectOption) {
opts.readLimit = limit
}
}
// Connect connects to the OpenAI Realtime API.
func (c *Client) Connect(ctx context.Context, opts ...ConnectOption) (*Conn, error) {
connectOpts := connectOption{
model: GPT4oRealtimePreview,
+83 -24
View File
@@ -2,6 +2,7 @@ package openairt
import "encoding/json"
// ClientEventType is the type of client event. See https://platform.openai.com/docs/guides/realtime/client-events
type ClientEventType string
const (
@@ -16,30 +17,48 @@ const (
ClientEventTypeResponseCancel ClientEventType = "response.cancel"
)
// ClientEvent is the interface for client event.
type ClientEvent interface {
ClientEventType() ClientEventType
}
// EventBase is the base struct for all client events.
type EventBase struct {
// Optional client-generated ID used to identify this event.
EventID string `json:"event_id,omitempty"`
}
type ClientSession struct {
Modalities []Modality `json:"modalities,omitempty"`
Instructions string `json:"instructions,omitempty"`
Voice Voice `json:"voice,omitempty"`
InputAudioFormat AudioFormat `json:"input_audio_format,omitempty"`
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
// The set of modalities the model can respond with. To disable audio, set this to ["text"].
Modalities []Modality `json:"modalities,omitempty"`
// The default system instructions prepended to model calls.
Instructions string `json:"instructions,omitempty"`
// The voice the model uses to respond - one of alloy, echo, or shimmer. Cannot be changed once the model has responded with audio at least once.
Voice Voice `json:"voice,omitempty"`
// The format of input audio. Options are "pcm16", "g711_ulaw", or "g711_alaw".
InputAudioFormat AudioFormat `json:"input_audio_format,omitempty"`
// The format of output audio. Options are "pcm16", "g711_ulaw", or "g711_alaw".
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
// Configuration for input audio transcription. Can be set to `nil` to turn off.
InputAudioTranscription *InputAudioTranscription `json:"input_audio_transcription,omitempty"`
TurnDetection *TurnDetection `json:"turn_detection,omitempty"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice ToolChoiceInterface `json:"tool_choice,omitempty"`
Temperature *float32 `json:"temperature,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
// Configuration for turn detection. Can be set to `nil` to turn off.
TurnDetection *TurnDetection `json:"turn_detection,omitempty"`
// Tools (functions) available to the model.
Tools []Tool `json:"tools,omitempty"`
// How the model chooses tools. Options are "auto", "none", "required", or specify a function.
ToolChoice ToolChoiceInterface `json:"tool_choice,omitempty"`
// Sampling temperature for the model.
Temperature *float32 `json:"temperature,omitempty"`
// Maximum number of output tokens for a single assistant response, inclusive of tool calls. Provide an integer between 1 and 4096 to limit output tokens, or "inf" for the maximum available tokens for a given model. Defaults to "inf".
MaxOutputTokens IntOrInf `json:"max_output_tokens,omitempty"`
}
// SessionUpdateEvent is the event for session update.
// Send this event to update the session’s default configuration.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/session/update
type SessionUpdateEvent struct {
EventBase
// Session configuration to update.
Session ClientSession `json:"session"`
}
@@ -59,9 +78,12 @@ func (m SessionUpdateEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// InputAudioBufferAppendEvent is the event for input audio buffer append.
// Send this event to append audio bytes to the input audio buffer.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/input_audio_buffer/append
type InputAudioBufferAppendEvent struct {
EventBase
Audio string `json:"audio"`
Audio string `json:"audio"` // Base64-encoded audio bytes.
}
func (m InputAudioBufferAppendEvent) ClientEventType() ClientEventType {
@@ -80,6 +102,9 @@ func (m InputAudioBufferAppendEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// InputAudioBufferCommitEvent is the event for input audio buffer commit.
// Send this event to commit audio bytes to a user message.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/input_audio_buffer/commit
type InputAudioBufferCommitEvent struct {
EventBase
}
@@ -100,6 +125,9 @@ func (m InputAudioBufferCommitEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// InputAudioBufferClearEvent is the event for input audio buffer clear.
// Send this event to clear the audio bytes in the buffer.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/input_audio_buffer/clear
type InputAudioBufferClearEvent struct {
EventBase
}
@@ -120,10 +148,15 @@ func (m InputAudioBufferClearEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// ConversationItemCreateEvent is the event for conversation item create.
// Send this event when adding an item to the conversation.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/conversation/item/create
type ConversationItemCreateEvent struct {
EventBase
PreviousItemID string `json:"previous_item_id,omitempty"`
Item MessageItem `json:"item"`
// The ID of the preceding item after which the new item will be inserted.
PreviousItemID string `json:"previous_item_id,omitempty"`
// The item to add to the conversation.
Item MessageItem `json:"item"`
}
func (m ConversationItemCreateEvent) ClientEventType() ClientEventType {
@@ -142,11 +175,17 @@ func (m ConversationItemCreateEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// ConversationItemTruncateEvent is the event for conversation item truncate.
// Send this event when you want to truncate a previous assistant message’s audio.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/conversation/item/truncate
type ConversationItemTruncateEvent struct {
EventBase
ItemID string `json:"item_id"`
ContentIndex int `json:"content_index"`
AudioEndMs int `json:"audio_end_ms"`
// The ID of the assistant message item to truncate.
ItemID string `json:"item_id"`
// The index of the content part to truncate.
ContentIndex int `json:"content_index"`
// Inclusive duration up to which audio is truncated, in milliseconds.
AudioEndMs int `json:"audio_end_ms"`
}
func (m ConversationItemTruncateEvent) ClientEventType() ClientEventType {
@@ -165,8 +204,12 @@ func (m ConversationItemTruncateEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// ConversationItemDeleteEvent is the event for conversation item delete.
// Send this event when you want to remove any item from the conversation history.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/conversation/item/delete
type ConversationItemDeleteEvent struct {
EventBase
// The ID of the item to delete.
ItemID string `json:"item_id"`
}
@@ -187,18 +230,30 @@ func (m ConversationItemDeleteEvent) MarshalJSON() ([]byte, error) {
}
type ResponseCreateParams struct {
Modalities []Modality `json:"modalities,omitempty"`
Instructions string `json:"instructions,omitempty"`
Voice Voice `json:"voice,omitempty"`
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice ToolChoiceInterface `json:"tool_choice,omitempty"`
Temperature *float32 `json:"temperature,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
// The modalities for the response.
Modalities []Modality `json:"modalities,omitempty"`
// Instructions for the model.
Instructions string `json:"instructions,omitempty"`
// The voice the model uses to respond - one of alloy, echo, or shimmer.
Voice Voice `json:"voice,omitempty"`
// The format of output audio.
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
// Tools (functions) available to the model.
Tools []Tool `json:"tools,omitempty"`
// How the model chooses tools.
ToolChoice ToolChoiceInterface `json:"tool_choice,omitempty"`
// Sampling temperature.
Temperature *float32 `json:"temperature,omitempty"`
// Maximum number of output tokens for a single assistant response, inclusive of tool calls. Provide an integer between 1 and 4096 to limit output tokens, or "inf" for the maximum available tokens for a given model. Defaults to "inf".
MaxOutputTokens IntOrInf `json:"max_output_tokens,omitempty"`
}
// ResponseCreateEvent is the event for response create.
// Send this event to trigger a response generation.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/response/create
type ResponseCreateEvent struct {
EventBase
// Configuration for the response.
Response ResponseCreateParams `json:"response"`
}
@@ -218,6 +273,9 @@ func (m ResponseCreateEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// ResponseCancelEvent is the event for response cancel.
// Send this event to cancel an in-progress response.
// See https://platform.openai.com/docs/api-reference/realtime-client-events/response/cancel
type ResponseCancelEvent struct {
EventBase
}
@@ -238,6 +296,7 @@ func (m ResponseCancelEvent) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}
// MarshalClientEvent marshals the client event to JSON.
func MarshalClientEvent(event ClientEvent) ([]byte, error) {
return json.Marshal(event)
}
+5 -4
View File
@@ -54,7 +54,7 @@ func TestSessionUpdateEvent(t *testing.T) {
},
},
Temperature: &temperature,
MaxOutputTokens: 100,
MaxOutputTokens: openairt.Int(100),
},
}
@@ -128,7 +128,7 @@ func TestSessionUpdateEventSimple(t *testing.T) {
Tools: nil,
ToolChoice: nil,
Temperature: &temperature,
MaxOutputTokens: 100,
MaxOutputTokens: openairt.Int(100),
},
}
@@ -313,7 +313,7 @@ func TestResponseCreateEvent(t *testing.T) {
Tools: nil,
ToolChoice: openairt.ToolChoiceAuto,
Temperature: nil,
MaxOutputTokens: 100,
MaxOutputTokens: openairt.Int(100),
},
}
data, err := json.MarshalIndent(message, "", "\t")
@@ -335,9 +335,10 @@ func TestResponseCreateEvent(t *testing.T) {
assert.JSONEq(t, expected, string(data))
message.EventBase.EventID = "test-id"
message.Response.MaxOutputTokens = openairt.Inf()
data, err = json.Marshal(message)
assert.NoError(t, err)
expected = `{"event_id":"test-id","response":{"modalities":["text","audio"],"instructions":"test-instructions","voice":"alloy","output_audio_format":"g711-ulaw","tool_choice":"auto","max_output_tokens":100},"type":"response.create"}`
expected = `{"event_id":"test-id","response":{"modalities":["text","audio"],"instructions":"test-instructions","voice":"alloy","output_audio_format":"g711-ulaw","tool_choice":"auto","max_output_tokens":"inf"},"type":"response.create"}`
assert.JSONEq(t, expected, string(data))
}
+16 -5
View File
@@ -1,28 +1,36 @@
package openairt
// APIType is the type of API.
type APIType string
const (
// APITypeOpenAI is the type of API for OpenAI.
APITypeOpenAI APIType = "OPEN_AI"
APITypeAzure APIType = "AZURE"
// APITypeAzure is the type of API for Azure.
APITypeAzure APIType = "AZURE"
)
const (
// openaiAPIURLv1 is the base URL for the OpenAI Realtime API.
openaiAPIURLv1 = "wss://api.openai.com/v1/realtime"
)
const (
// azureAPIVersion20241001Preview is the API version for Azure.
azureAPIVersion20241001Preview = "2024-10-01-preview"
)
// ClientConfig is the configuration for the client.
type ClientConfig struct {
authToken string
BaseURL string
APIType APIType
APIVersion string // required when APIType is APITypeAzure
BaseURL string // Base URL for the API. Defaults to "wss://api.openai.com/v1/realtime"
APIType APIType // API type. Defaults to APITypeOpenAI
APIVersion string // required when APIType is APITypeAzure
}
// DefaultConfig creates a new ClientConfig with the given auth token.
// Defaults to using the OpenAI Realtime API.
func DefaultConfig(authToken string) ClientConfig {
return ClientConfig{
authToken: authToken,
@@ -31,6 +39,8 @@ func DefaultConfig(authToken string) ClientConfig {
}
}
// DefaultAzureConfig creates a new ClientConfig with the given auth token and base URL.
// Defaults to using the Azure Realtime API.
func DefaultAzureConfig(apiKey, baseURL string) ClientConfig {
return ClientConfig{
authToken: apiKey,
@@ -40,6 +50,7 @@ func DefaultAzureConfig(apiKey, baseURL string) ClientConfig {
}
}
func (ClientConfig) String() string {
// String returns a string representation of the ClientConfig.
func (c ClientConfig) String() string {
return "<OpenAI Realtime API ClientConfig>"
}
+13 -6
View File
@@ -11,14 +11,17 @@ import (
type ServerEventHandler func(ctx context.Context, event ServerEvent)
// Conn is a connection to the OpenAI Realtime API.
type Conn struct {
conn *websocket.Conn
}
// Close closes the connection.
func (c *Conn) Close() error {
return c.conn.Close(websocket.StatusNormalClosure, "")
}
// SendMessage sends a client event to the server.
func (c *Conn) SendMessage(ctx context.Context, msg ClientEvent) error {
data, err := MarshalClientEvent(msg)
if err != nil {
@@ -27,6 +30,7 @@ func (c *Conn) SendMessage(ctx context.Context, msg ClientEvent) error {
return c.conn.Write(ctx, websocket.MessageText, data)
}
// ReadMessage reads a server event from the server.
func (c *Conn) ReadMessage(ctx context.Context) (ServerEvent, error) {
messageType, data, err := c.conn.Read(ctx)
if err != nil {
@@ -42,15 +46,20 @@ func (c *Conn) ReadMessage(ctx context.Context) (ServerEvent, error) {
return event, nil
}
// ConnHandler is a handler for a connection to the OpenAI Realtime API.
// It reads messages from the server in a standalone goroutine and calls the registered handlers.
// It is the responsibility of the caller to call Start and Stop.
// The handlers are called in the order they are registered.
// Users should not call ReadMessage directly when using ConnHandler.
type ConnHandler struct {
ctx context.Context
conn *Conn
handlers []ServerEventHandler
cancel context.CancelFunc
wg sync.WaitGroup
err error
}
// NewConnHandler creates a new ConnHandler with the given context and connection.
func NewConnHandler(ctx context.Context, conn *Conn, handlers ...ServerEventHandler) *ConnHandler {
ctx, cancel := context.WithCancel(ctx)
return &ConnHandler{
@@ -61,11 +70,12 @@ func NewConnHandler(ctx context.Context, conn *Conn, handlers ...ServerEventHand
}
}
// Start starts the ConnHandler.
func (c *ConnHandler) Start() {
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.err = c.run()
c.run()
}()
}
@@ -88,11 +98,8 @@ func (c *ConnHandler) run() error {
}
}
// Stop stops the ConnHandler.
func (c *ConnHandler) Stop() {
c.cancel()
c.wg.Wait()
}
func (c *ConnHandler) Err() error {
return c.err
}
+2 -7
View File
@@ -52,12 +52,7 @@ func main() {
connHandler := openairt.NewConnHandler(ctx, conn, responseDeltaHandler, responseHandler)
connHandler.Start()
defer func() {
connHandler.Stop()
if err := connHandler.Err(); err != nil {
log.Printf("connHandler.Err(): %v", err)
}
}()
defer connHandler.Stop()
err = conn.SendMessage(ctx, &openairt.SessionUpdateEvent{
Session: openairt.ClientSession{
@@ -93,7 +88,7 @@ func main() {
conn.SendMessage(ctx, &openairt.ResponseCreateEvent{
Response: openairt.ResponseCreateParams{
Modalities: []openairt.Modality{openairt.ModalityText},
MaxOutputTokens: 100,
MaxOutputTokens: openairt.Int(100),
},
})
}
+2 -7
View File
@@ -121,12 +121,7 @@ func main() {
connHandler := openairt.NewConnHandler(ctx, conn, allHandler, responseDeltaHandler, responseHandler, audioResponseHandler)
connHandler.Start()
defer func() {
connHandler.Stop()
if err := connHandler.Err(); err != nil {
log.Printf("connHandler.Err(): %v", err)
}
}()
defer connHandler.Stop()
err = conn.SendMessage(ctx, &openairt.SessionUpdateEvent{
Session: openairt.ClientSession{
@@ -164,7 +159,7 @@ func main() {
conn.SendMessage(ctx, &openairt.ResponseCreateEvent{
Response: openairt.ResponseCreateParams{
Modalities: []openairt.Modality{openairt.ModalityText, openairt.ModalityAudio},
MaxOutputTokens: 4000,
MaxOutputTokens: openairt.Int(4000),
},
})
}
+55
View File
@@ -0,0 +1,55 @@
package openairt
import (
"encoding/json"
)
// IntOrInf is a type that can be either an int or "inf".
type IntOrInf struct {
value int
inf bool
}
// Inf returns an IntOrInf with the value "inf".
func Inf() IntOrInf {
return IntOrInf{
inf: true,
}
}
// Int returns an IntOrInf with the given value.
func Int(value int) IntOrInf {
return IntOrInf{
value: value,
}
}
// IsInf returns true if the value is "inf".
func (m IntOrInf) IsInf() bool {
return m.inf
}
// Value returns the value as an int.
func (m IntOrInf) Value() int {
return m.value
}
// MarshalJSON marshals the IntOrInf to JSON.
func (m IntOrInf) MarshalJSON() ([]byte, error) {
if m.inf {
return []byte("\"inf\""), nil
}
return json.Marshal(m.value)
}
// UnmarshalJSON unmarshals the IntOrInf from JSON.
func (m *IntOrInf) UnmarshalJSON(data []byte) error {
if string(data) == "\"inf\"" {
m.inf = true
return nil
}
if len(data) == 0 {
return nil
}
return json.Unmarshal(data, &m.value)
}
+28
View File
@@ -0,0 +1,28 @@
package openairt_test
import (
"encoding/json"
"testing"
openairt "github.com/WqyJh/go-openai-realtime"
"github.com/stretchr/testify/assert"
)
func TestIntOrInfMarshalJSON(t *testing.T) {
v := openairt.Int(10)
assert.Equal(t, 10, v.Value())
assert.False(t, v.IsInf())
b, err := json.Marshal(v)
assert.NoError(t, err)
assert.Equal(t, []byte("10"), b)
v = openairt.Inf()
assert.Equal(t, 0, v.Value())
assert.True(t, v.IsInf())
v = openairt.Inf()
b, err = json.Marshal(v)
assert.NoError(t, err)
assert.Equal(t, []byte("\"inf\""), b)
}
+174 -48
View File
@@ -38,59 +38,97 @@ const (
ServerEventTypeRateLimitsUpdated ServerEventType = "rate_limits.updated"
)
// ServerEvent is the interface for server events.
type ServerEvent interface {
ServerEventType() ServerEventType
}
// ServerEventBase is the base struct for all server events.
type ServerEventBase struct {
EventID string `json:"event_id,omitempty"`
Type ServerEventType `json:"type"`
// The unique ID of the server event.
EventID string `json:"event_id,omitempty"`
// The type of the server event.
Type ServerEventType `json:"type"`
}
func (m ServerEventBase) ServerEventType() ServerEventType {
return m.Type
}
// ErrorEvent is the event for error.
// Returned when an error occurs.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/error
type ErrorEvent struct {
ServerEventBase
// Details of the error.
Error Error `json:"error"`
}
// SessionCreatedEvent is the event for session created.
// Returned when a session is created. Emitted automatically when a new connection is established.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/session/created
type SessionCreatedEvent struct {
ServerEventBase
// The session resource.
Session ServerSession `json:"session"`
}
// SessionUpdatedEvent is the event for session updated.
// Returned when a session is updated.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/session/updated
type SessionUpdatedEvent struct {
ServerEventBase
// The updated session resource.
Session ServerSession `json:"session"`
}
// ConversationCreatedEvent is the event for conversation created.
// Returned when a conversation is created. Emitted right after session creation.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/conversation/created
type ConversationCreatedEvent struct {
ServerEventBase
// The conversation resource.
Conversation Conversation `json:"conversation"`
}
// InputAudioBufferCommittedEvent is the event for input audio buffer committed.
// Returned when an input audio buffer is committed, either by the client or automatically in server VAD mode.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/input_audio_buffer/committed
type InputAudioBufferCommittedEvent struct {
ServerEventBase
// The ID of the preceding item after which the new item will be inserted.
PreviousItemID string `json:"previous_item_id,omitempty"`
ItemID string `json:"item_id"`
// The ID of the user message item that will be created.
ItemID string `json:"item_id"`
}
// InputAudioBufferClearedEvent is the event for input audio buffer cleared.
// Returned when the input audio buffer is cleared by the client.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/input_audio_buffer/cleared
type InputAudioBufferClearedEvent struct {
ServerEventBase
}
// InputAudioBufferSpeechStartedEvent is the event for input audio buffer speech started.
// Returned in server turn detection mode when speech is detected.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/input_audio_buffer/speech_started
type InputAudioBufferSpeechStartedEvent struct {
ServerEventBase
AudioStartMs int64 `json:"audio_start_ms"` // Milliseconds since the session started when speech was detected.
ItemID string `json:"item_id"`
// Milliseconds since the session started when speech was detected.
AudioStartMs int64 `json:"audio_start_ms"`
// The ID of the user message item that will be created when speech stops.
ItemID string `json:"item_id"`
}
// InputAudioBufferSpeechStoppedEvent is the event for input audio buffer speech stopped.
// Returned in server turn detection mode when speech stops.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/input_audio_buffer/speech_stopped
type InputAudioBufferSpeechStoppedEvent struct {
ServerEventBase
AudioEndMs int64 `json:"audio_end_ms"` // Milliseconds since the session started when speech stopped.
ItemID string `json:"item_id"`
// Milliseconds since the session started when speech stopped.
AudioEndMs int64 `json:"audio_end_ms"`
// The ID of the user message item that will be created.
ItemID string `json:"item_id"`
}
type ConversationItemCreatedEvent struct {
@@ -125,30 +163,53 @@ type ConversationItemDeletedEvent struct {
ItemID string `json:"item_id"` // The ID of the item that was deleted.
}
// ResponseCreatedEvent is the event for response created.
// Returned when a new Response is created. The first event of response creation, where the response is in an initial state of "in_progress".
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/created
type ResponseCreatedEvent struct {
ServerEventBase
// The response resource.
Response Response `json:"response"`
}
// ResponseDoneEvent is the event for response done.
// Returned when a Response is done streaming. Always emitted, no matter the final state.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/done
type ResponseDoneEvent struct {
ServerEventBase
// The response resource.
Response Response `json:"response"`
}
// ResponseOutputItemAddedEvent is the event for response output item added.
// Returned when a new Item is created during response generation.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/output_item/added
type ResponseOutputItemAddedEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
OutputIndex int `json:"output_index"`
Item ResponseMessageItem `json:"item"`
// The ID of the response to which the item belongs.
ResponseID string `json:"response_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The item that was added.
Item ResponseMessageItem `json:"item"`
}
// ResponseOutputItemDoneEvent is the event for response output item done.
// Returned when an Item is done streaming. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/output_item/done
type ResponseOutputItemDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
OutputIndex int `json:"output_index"`
Item ResponseMessageItem `json:"item"`
// The ID of the response to which the item belongs.
ResponseID string `json:"response_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The completed item.
Item ResponseMessageItem `json:"item"`
}
// ResponseContentPartAddedEvent is the event for response content part added.
// Returned when a new content part is added to an assistant message item during response generation.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/content_part/added
type ResponseContentPartAddedEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
@@ -158,15 +219,26 @@ type ResponseContentPartAddedEvent struct {
Part MessageContentPart `json:"part"`
}
// ResponseContentPartDoneEvent is the event for response content part done.
// Returned when a content part is done streaming in an assistant message item. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/content_part/done
type ResponseContentPartDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
ContentIndex int `json:"content_index"`
Part MessageContentPart `json:"part"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item to which the content part was added.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The index of the content part in the item's content array.
ContentIndex int `json:"content_index"`
// The content part that was added.
Part MessageContentPart `json:"part"`
}
// ResponseTextDeltaEvent is the event for response text delta.
// Returned when the text value of a "text" content part is updated.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/text/delta
type ResponseTextDeltaEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
@@ -176,6 +248,9 @@ type ResponseTextDeltaEvent struct {
Delta string `json:"delta"`
}
// ResponseTextDoneEvent is the event for response text done.
// Returned when the text value of a "text" content part is done streaming. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/text/done
type ResponseTextDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
@@ -185,65 +260,116 @@ type ResponseTextDoneEvent struct {
Text string `json:"text"`
}
// ResponseAudioTranscriptDeltaEvent is the event for response audio transcript delta.
// Returned when the model-generated transcription of audio output is updated.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/audio_transcript/delta
type ResponseAudioTranscriptDeltaEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
ContentIndex int `json:"content_index"`
Delta string `json:"delta"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The index of the content part in the item's content array.
ContentIndex int `json:"content_index"`
// The transcript delta.
Delta string `json:"delta"`
}
// ResponseAudioTranscriptDoneEvent is the event for response audio transcript done.
// Returned when the model-generated transcription of audio output is done streaming. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/audio_transcript/done
type ResponseAudioTranscriptDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
ContentIndex int `json:"content_index"`
Transcript string `json:"transcript"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The index of the content part in the item's content array.
ContentIndex int `json:"content_index"`
// The final transcript of the audio.
Transcript string `json:"transcript"`
}
// ResponseAudioDeltaEvent is the event for response audio delta.
// Returned when the model-generated audio is updated.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/audio/delta
type ResponseAudioDeltaEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
ContentIndex int `json:"content_index"`
Delta string `json:"delta"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The index of the content part in the item's content array.
ContentIndex int `json:"content_index"`
// Base64-encoded audio data delta.
Delta string `json:"delta"`
}
// ResponseAudioDoneEvent is the event for response audio done.
// Returned when the model-generated audio is done. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/audio/done
type ResponseAudioDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
ContentIndex int `json:"content_index"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The index of the content part in the item's content array.
ContentIndex int `json:"content_index"`
}
// ResponseFunctionCallArgumentsDeltaEvent is the event for response function call arguments delta.
// Returned when the model-generated function call arguments are updated.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/function_call_arguments/delta
type ResponseFunctionCallArgumentsDeltaEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
CallID string `json:"call_id"`
Delta string `json:"delta"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The ID of the function call.
CallID string `json:"call_id"`
// The arguments delta as a JSON string.
Delta string `json:"delta"`
}
// ResponseFunctionCallArgumentsDoneEvent is the event for response function call arguments done.
// Returned when the model-generated function call arguments are done streaming. Also emitted when a Response is interrupted, incomplete, or cancelled.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/response/function_call_arguments/done
type ResponseFunctionCallArgumentsDoneEvent struct {
ServerEventBase
ResponseID string `json:"response_id"`
ItemID string `json:"item_id"`
OutputIndex int `json:"output_index"`
CallID string `json:"call_id"`
Name string `json:"name"`
Arguments string `json:"arguments"`
// The ID of the response.
ResponseID string `json:"response_id"`
// The ID of the item.
ItemID string `json:"item_id"`
// The index of the output item in the response.
OutputIndex int `json:"output_index"`
// The ID of the function call.
CallID string `json:"call_id"`
// The final arguments as a JSON string.
Arguments string `json:"arguments"`
}
// RateLimitsUpdatedEvent is the event for rate limits updated.
// Emitted after every "response.done" event to indicate the updated rate limits.
// See https://platform.openai.com/docs/api-reference/realtime-server-events/rate_limits/updated
type RateLimitsUpdatedEvent struct {
ServerEventBase
// List of rate limit information.
RateLimits []RateLimit `json:"rate_limits"`
}
// UnmarshalServerEvent unmarshals the server event from the given JSON data.
func UnmarshalServerEvent(data []byte) (ServerEvent, error) {
var eventType struct {
Type ServerEventType `json:"type"`
+33 -2
View File
@@ -130,7 +130,7 @@ func TestSessionCreatedEvent(t *testing.T) {
Tools: []openairt.Tool{},
ToolChoice: openairt.ServerToolChoice{String: openairt.ToolChoiceAuto},
Temperature: &temperature,
MaxOutputTokens: 0,
MaxOutputTokens: openairt.Int(0),
},
}
actual, err := openairt.UnmarshalServerEvent([]byte(data))
@@ -190,13 +190,44 @@ func TestSessionUpdatedEvent(t *testing.T) {
Tools: []openairt.Tool{},
ToolChoice: openairt.ServerToolChoice{String: openairt.ToolChoiceNone},
Temperature: &temperature,
MaxOutputTokens: 200,
MaxOutputTokens: openairt.Int(200),
},
}
actual, err := openairt.UnmarshalServerEvent([]byte(data))
assert.NoError(t, err)
assert.Equal(t, openairt.ServerEventTypeSessionUpdated, actual.ServerEventType())
assert.Equal(t, expected, actual.(openairt.SessionUpdatedEvent))
data = `{
"event_id": "event_5678",
"type": "session.updated",
"session": {
"id": "sess_001",
"object": "realtime.session",
"model": "gpt-4o-realtime-preview-2024-10-01",
"modalities": ["text"],
"instructions": "New instructions",
"voice": "alloy",
"input_audio_format": "pcm16",
"output_audio_format": "pcm16",
"input_audio_transcription": {
"enabled": true,
"model": "whisper-1"
},
"turn_detection": {
"type": "none"
},
"tools": [],
"tool_choice": "none",
"temperature": 0.7,
"max_output_tokens": "inf"
}
}`
expected.Session.MaxOutputTokens = openairt.Inf()
actual, err = openairt.UnmarshalServerEvent([]byte(data))
assert.NoError(t, err)
assert.Equal(t, openairt.ServerEventTypeSessionUpdated, actual.ServerEventType())
assert.Equal(t, expected, actual.(openairt.SessionUpdatedEvent))
}
func TestConversationCreatedEvent(t *testing.T) {
+87 -40
View File
@@ -37,10 +37,14 @@ const (
)
type TurnDetection struct {
Type TurnDetectionType `json:"type"`
Threshold float64 `json:"threshold,omitempty"`
PrefixPaddingMs int `json:"prefix_padding_ms,omitempty"`
SilenceDurationMs int `json:"silence_duration_ms,omitempty"`
// The type of turn detection ("server_vad" or "none").
Type TurnDetectionType `json:"type"`
// Activation threshold for VAD.
Threshold float64 `json:"threshold,omitempty"`
// Audio included before speech starts (in milliseconds).
PrefixPaddingMs int `json:"prefix_padding_ms,omitempty"`
// Duration of silence to detect speech stop (in milliseconds).
SilenceDurationMs int `json:"silence_duration_ms,omitempty"`
}
type ToolType string
@@ -83,8 +87,10 @@ const (
)
type InputAudioTranscription struct {
Enabled bool `json:"enabled"`
Model string `json:"model"`
// Whether input audio transcription is enabled.
Enabled bool `json:"enabled"`
// The model used for transcription.
Model string `json:"model"`
}
type Tool struct {
@@ -113,30 +119,45 @@ const (
)
type MessageContentPart struct {
Type MessageContentType `json:"type"`
Text string `json:"text,omitempty"`
Audio string `json:"audio,omitempty"`
Transcript string `json:"transcript,omitempty"`
// The content type.
Type MessageContentType `json:"type"`
// The text content. Validated if type is text.
Text string `json:"text,omitempty"`
// Base64-encoded audio data. Validated if type is audio.
Audio string `json:"audio,omitempty"`
// The transcript of the audio. Validated if type is transcript.
Transcript string `json:"transcript,omitempty"`
}
type MessageItem struct {
ID string `json:"id"`
Type MessageItemType `json:"type"`
Status ItemStatus `json:"status"`
Role MessageRole `json:"role"`
// The unique ID of the item.
ID string `json:"id"`
// The type of the item ("message", "function_call", "function_call_output").
Type MessageItemType `json:"type"`
// The final status of the item.
Status ItemStatus `json:"status"`
// The role associated with the item.
Role MessageRole `json:"role"`
// The content of the item.
Content []MessageContentPart `json:"content"`
}
type ResponseMessageItem struct {
MessageItem
// The object type, must be "realtime.item".
Object string `json:"object,omitempty"`
}
type Error struct {
// The type of error (e.g., "invalid_request_error", "server_error").
Message string `json:"message,omitempty"`
Type string `json:"type,omitempty"`
Code string `json:"code,omitempty"`
Param string `json:"param,omitempty"`
// Error code, if any.
Type string `json:"type,omitempty"`
// A human-readable error message.
Code string `json:"code,omitempty"`
// Parameter related to the error, if any.
Param string `json:"param,omitempty"`
// The event_id of the client event that caused the error, if applicable.
EventID string `json:"event_id,omitempty"`
}
@@ -177,20 +198,34 @@ func (m ServerToolChoice) Get() ToolChoiceInterface {
}
type ServerSession struct {
ID string `json:"id"`
Object string `json:"object"`
Model string `json:"model"`
Modalities []Modality `json:"modalities,omitempty"`
Instructions string `json:"instructions,omitempty"`
Voice Voice `json:"voice,omitempty"`
InputAudioFormat AudioFormat `json:"input_audio_format,omitempty"`
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
// The unique ID of the session.
ID string `json:"id"`
// The object type, must be "realtime.session".
Object string `json:"object"`
// The default model used for this session.
Model string `json:"model"`
// The set of modalities the model can respond with.
Modalities []Modality `json:"modalities,omitempty"`
// The default system instructions.
Instructions string `json:"instructions,omitempty"`
// The voice the model uses to respond - one of alloy, echo, or shimmer.
Voice Voice `json:"voice,omitempty"`
// The format of input audio.
InputAudioFormat AudioFormat `json:"input_audio_format,omitempty"`
// The format of output audio.
OutputAudioFormat AudioFormat `json:"output_audio_format,omitempty"`
// Configuration for input audio transcription.
InputAudioTranscription *InputAudioTranscription `json:"input_audio_transcription,omitempty"`
TurnDetection *TurnDetection `json:"turn_detection,omitempty"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice ServerToolChoice `json:"tool_choice,omitempty"`
Temperature *float32 `json:"temperature,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
// Configuration for turn detection.
TurnDetection *TurnDetection `json:"turn_detection,omitempty"`
// Tools (functions) available to the model.
Tools []Tool `json:"tools,omitempty"`
// How the model chooses tools.
ToolChoice ServerToolChoice `json:"tool_choice,omitempty"`
// Sampling temperature.
Temperature *float32 `json:"temperature,omitempty"`
// Maximum number of output tokens.
MaxOutputTokens IntOrInf `json:"max_output_tokens,omitempty"`
}
type ItemStatus string
@@ -202,7 +237,9 @@ const (
)
type Conversation struct {
ID string `json:"id"`
// The unique ID of the conversation.
ID string `json:"id"`
// The object type, must be "realtime.conversation".
Object string `json:"object"`
}
@@ -223,17 +260,27 @@ type Usage struct {
}
type Response struct {
ID string `json:"id"`
Object string `json:"object"`
Status ResponseStatus `json:"status"`
StatusDetails any `json:"status_details,omitempty"`
Output []ResponseMessageItem `json:"output"`
Usage *Usage `json:"usage,omitempty"`
// The unique ID of the response.
ID string `json:"id"`
// The object type, must be "realtime.response".
Object string `json:"object"`
// The status of the response.
Status ResponseStatus `json:"status"`
// Additional details about the status.
StatusDetails any `json:"status_details,omitempty"`
// The list of output items generated by the response.
Output []ResponseMessageItem `json:"output"`
// Usage statistics for the response.
Usage *Usage `json:"usage,omitempty"`
}
type RateLimit struct {
Name string `json:"name"`
Limit int `json:"limit"`
Remaining int `json:"remaining"`
// The name of the rate limit ("requests", "tokens", "input_tokens", "output_tokens").
Name string `json:"name"`
// The maximum allowed value for the rate limit.
Limit int `json:"limit"`
// The remaining value before the limit is reached.
Remaining int `json:"remaining"`
// Seconds until the rate limit resets.
ResetSeconds float64 `json:"reset_seconds"`
}
+2
View File
@@ -5,6 +5,8 @@ import (
"math/big"
)
// GenerateId generates a random ID with a prefix and a specified length.
// The length of the returned ID is equal to the length parameter, therefore the prefix must be shorter than the length.
func GenerateId(prefix string, length int) string {
const chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
if length <= len(prefix) {
+4
View File
@@ -18,4 +18,8 @@ func TestGenerateId(t *testing.T) {
t.Logf("generated: %s", generated)
assert.True(t, strings.HasPrefix(generated, "test_"))
assert.Equal(t, 20, len(generated))
generated = openairt.GenerateId("test_", 5)
t.Logf("generated: %s", generated)
assert.Equal(t, "test_", generated)
}