From b3e49f991bb8e64f8b6581587a0727c4bd3cdb34 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 08:26:35 +0100 Subject: [PATCH 01/11] chore: first commit --- run.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 run.go diff --git a/run.go b/run.go new file mode 100644 index 00000000..b4e2621d --- /dev/null +++ b/run.go @@ -0,0 +1,91 @@ +package openai + +import ( + "context" + "fmt" + "net/http" +) + +type Run struct { + ID string `json:"id"` + Object string `json:"object"` + CreatedAt int64 `json:"created_at"` + ThreadID string `json:"thread_id"` + AssistantID string `json:"assistant_id"` + Status RunStatus `json:"status"` + RequiredAction *RunRequiredAction `json:"required_action,omitempty"` + LastError *RunLastError `json:"last_error,omitempty"` + ExpiresAt int64 `json:"expires_at"` + StartedAt *int64 `json:"started_at,omitempty"` + CancelledAt *int64 `json:"cancelled_at,omitempty"` + FailedAt *int64 `json:"failed_at,omitempty"` + CompletedAt *int64 `json:"completed_at,omitempty"` + Model string `json:"model"` + Instructions string `json:"instructions,omitempty"` + Tools []Tool `json:"tools"` + FileIDS []string `json:"file_ids"` + Metadata map[string]any `json:"metadata"` + + httpHeader +} + +type RunStatus string + +const ( + RunStatusQueued RunStatus = "queued" + RunStatusInProgress RunStatus = "in_progress" + RunStatusRequiresAction RunStatus = "requires_action" + RunStatusCancelling RunStatus = "cancelling" + RunStatusFailed RunStatus = "failed" + RunStatusCompleted RunStatus = "completed" + RunStatusExpired RunStatus = "expired" +) + +type RunRequiredAction struct { + Type RequiredActionType `json:"type"` + SubmitToolOutputs *RunSubmitToolOutputs `json:"submit_tool_outputs,omitempty"` +} + +type RequiredActionType string + +const ( + RequiredActionTypeSubmitToolOutputs RequiredActionType = "submit_tool_outputs" +) + +type RunSubmitToolOutputs struct { + ToolCalls []ToolCall `json:"tool_calls"` +} + +type RunLastError struct { + Code RunError `json:"code"` + Message string `json:"message"` +} + +type RunError string + +const ( + RunErrorServerError RunError = "server_error" + RunErrorRateLimitExceeded RunError = "rate_limit_exceeded" +) + +type RunRequest struct { + ThreadID string `json:"-"` + AssistantID string `json:"assistant_id"` + Model *string `json:"model,omitempty"` + Instructions *string `json:"instructions,omitempty"` + Tools []Tool `json:"tools,omitempty"` + Metadata map[string]any +} + +// CreateRun creates a new run. +func (c *Client) CreateRun(ctx context.Context, request RunRequest) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/run", request.ThreadID) + req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} From 7d2270b3622831be5b3997fbb323074c1f9362ad Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 08:33:11 +0100 Subject: [PATCH 02/11] add apis --- run.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/run.go b/run.go index b4e2621d..bd764c71 100644 --- a/run.go +++ b/run.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "net/url" ) type Run struct { @@ -69,7 +70,6 @@ const ( ) type RunRequest struct { - ThreadID string `json:"-"` AssistantID string `json:"assistant_id"` Model *string `json:"model,omitempty"` Instructions *string `json:"instructions,omitempty"` @@ -77,9 +77,24 @@ type RunRequest struct { Metadata map[string]any } +type RunModifyRequest struct { + Metadata map[string]any `json:"metadata,omitempty"` +} + +// RunList is a list of assistants. +type RunList struct { + Runs []Run `json:"data"` + + httpHeader +} + // CreateRun creates a new run. -func (c *Client) CreateRun(ctx context.Context, request RunRequest) (response Run, err error) { - urlSuffix := fmt.Sprintf("/threads/%s/run", request.ThreadID) +func (c *Client) CreateRun( + ctx context.Context, + threadID string, + request RunRequest, +) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/run", threadID) req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), withBetaAssistantV1()) if err != nil { @@ -89,3 +104,77 @@ func (c *Client) CreateRun(ctx context.Context, request RunRequest) (response Ru err = c.sendRequest(req, &response) return } + +// RetrieveRun retrieves a run. +func (c *Client) RetrieveRun( + ctx context.Context, + threadID string, + runID string, +) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/run/%s", threadID, runID) + req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} + +// ModifyRun modifies a run. +func (c *Client) ModifyRun( + ctx context.Context, + threadID string, + runID string, + request RunModifyRequest, +) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/run/%s", threadID, runID) + req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} + +// ListRuns lists runs. +func (c *Client) ListRuns( + ctx context.Context, + threadID string, + limit *int, + order *string, + after *string, + before *string, +) (response RunList, err error) { + urlValues := url.Values{} + if limit != nil { + urlValues.Add("limit", fmt.Sprintf("%d", *limit)) + } + if order != nil { + urlValues.Add("order", *order) + } + if after != nil { + urlValues.Add("after", *after) + } + if before != nil { + urlValues.Add("before", *before) + } + + encodedValues := "" + if len(urlValues) > 0 { + encodedValues = "?" + urlValues.Encode() + } + + urlSuffix := fmt.Sprintf("/threads/%s/run%s", threadID, encodedValues) + req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} From b322f4641eed2daab7da3700504acf75356b7479 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 08:51:22 +0100 Subject: [PATCH 03/11] chore: add tests --- client_test.go | 12 ++++++ run.go | 8 ++-- run_test.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 run_test.go diff --git a/client_test.go b/client_test.go index bff2597c..9c536db5 100644 --- a/client_test.go +++ b/client_test.go @@ -301,6 +301,18 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) { {"DeleteAssistantFile", func() (any, error) { return nil, client.DeleteAssistantFile(ctx, "", "") }}, + {"CreateRun", func() (any, error) { + return client.CreateRun(ctx, "", RunRequest{}) + }}, + {"RetrieveRun", func() (any, error) { + return client.RetrieveRun(ctx, "", "") + }}, + {"ModifyRun", func() (any, error) { + return client.ModifyRun(ctx, "", "", RunModifyRequest{}) + }}, + {"ListRuns", func() (any, error) { + return client.ListRuns(ctx, "", nil, nil, nil, nil) + }}, } for _, testCase := range testCases { diff --git a/run.go b/run.go index bd764c71..cfb392d1 100644 --- a/run.go +++ b/run.go @@ -94,7 +94,7 @@ func (c *Client) CreateRun( threadID string, request RunRequest, ) (response Run, err error) { - urlSuffix := fmt.Sprintf("/threads/%s/run", threadID) + urlSuffix := fmt.Sprintf("/threads/%s/runs", threadID) req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), withBetaAssistantV1()) if err != nil { @@ -111,7 +111,7 @@ func (c *Client) RetrieveRun( threadID string, runID string, ) (response Run, err error) { - urlSuffix := fmt.Sprintf("/threads/%s/run/%s", threadID, runID) + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s", threadID, runID) req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), withBetaAssistantV1()) if err != nil { @@ -129,7 +129,7 @@ func (c *Client) ModifyRun( runID string, request RunModifyRequest, ) (response Run, err error) { - urlSuffix := fmt.Sprintf("/threads/%s/run/%s", threadID, runID) + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s", threadID, runID) req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), withBetaAssistantV1()) if err != nil { @@ -168,7 +168,7 @@ func (c *Client) ListRuns( encodedValues = "?" + urlValues.Encode() } - urlSuffix := fmt.Sprintf("/threads/%s/run%s", threadID, encodedValues) + urlSuffix := fmt.Sprintf("/threads/%s/runs%s", threadID, encodedValues) req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), withBetaAssistantV1()) if err != nil { diff --git a/run_test.go b/run_test.go new file mode 100644 index 00000000..bddf573f --- /dev/null +++ b/run_test.go @@ -0,0 +1,111 @@ +package openai_test + +import ( + "context" + + openai "github.com/sashabaranov/go-openai" + "github.com/sashabaranov/go-openai/internal/test/checks" + + "encoding/json" + "fmt" + "net/http" + "testing" +) + +// TestAssistant Tests the assistant endpoint of the API using the mocked server. +func TestRun(t *testing.T) { + assistantID := "asst_abc123" + threadID := "thread_abc123" + runID := "run_abc123" + // assistantName := "Ambrogio" + // assistantDescription := "Ambrogio is a friendly assistant." + // assitantInstructions := `You are a personal math tutor. + // When asked a question, write and run Python code to answer the question.` + // assistantFileID := "file-wB6RM6wHdA49HfS2DJ9fEyrH" + limit := 20 + order := "desc" + after := "asst_abc122" + before := "asst_abc124" + + client, server, teardown := setupOpenAITestServer() + defer teardown() + + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs/"+runID, + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusQueued, + }) + fmt.Fprintln(w, string(resBytes)) + } else if r.Method == http.MethodPost { + var request openai.RunModifyRequest + err := json.NewDecoder(r.Body).Decode(&request) + checks.NoError(t, err, "Decode error") + + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusQueued, + Metadata: request.Metadata, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs", + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + var request openai.RunRequest + err := json.NewDecoder(r.Body).Decode(&request) + checks.NoError(t, err, "Decode error") + + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusQueued, + }) + fmt.Fprintln(w, string(resBytes)) + } else if r.Method == http.MethodGet { + resBytes, _ := json.Marshal(openai.RunList{ + Runs: []openai.Run{ + { + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusQueued, + }, + }, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + + ctx := context.Background() + + _, err := client.CreateRun(ctx, threadID, openai.RunRequest{ + AssistantID: assistantID, + }) + checks.NoError(t, err, "CreateRun error") + + _, err = client.RetrieveRun(ctx, threadID, runID) + checks.NoError(t, err, "RetrieveRun error") + + _, err = client.ModifyRun(ctx, threadID, runID, openai.RunModifyRequest{ + Metadata: map[string]any{ + "key": "value", + }, + }) + checks.NoError(t, err, "ModifyRun error") + + _, err = client.ListRuns(ctx, threadID, &limit, &order, &after, &before) + checks.NoError(t, err, "ListRuns error") +} From 74de9eb4c200a79d440911f8d7c6a55a02805354 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:08:37 +0100 Subject: [PATCH 04/11] feat add apis --- run.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++--- run_test.go | 5 ---- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/run.go b/run.go index cfb392d1..33380f9c 100644 --- a/run.go +++ b/run.go @@ -43,8 +43,8 @@ const ( ) type RunRequiredAction struct { - Type RequiredActionType `json:"type"` - SubmitToolOutputs *RunSubmitToolOutputs `json:"submit_tool_outputs,omitempty"` + Type RequiredActionType `json:"type"` + SubmitToolOutputs *SubmitToolOutputs `json:"submit_tool_outputs,omitempty"` } type RequiredActionType string @@ -53,7 +53,7 @@ const ( RequiredActionTypeSubmitToolOutputs RequiredActionType = "submit_tool_outputs" ) -type RunSubmitToolOutputs struct { +type SubmitToolOutputs struct { ToolCalls []ToolCall `json:"tool_calls"` } @@ -88,6 +88,20 @@ type RunList struct { httpHeader } +type SubmitToolOutputsRequest struct { + ToolOutputs []ToolOutput `json:"tool_outputs"` +} + +type ToolOutput struct { + ToolCallID string `json:"tool_call_id"` + Output any `json:"output"` +} + +type CreateThreadAndRunRequest struct { + RunRequest + // Thread *ThreadRequest `json:"thread,omitempty"` uncomment when thread is implemented +} + // CreateRun creates a new run. func (c *Client) CreateRun( ctx context.Context, @@ -178,3 +192,52 @@ func (c *Client) ListRuns( err = c.sendRequest(req, &response) return } + +// SubmitToolOutputs submits tool outputs. +func (c *Client) SubmitToolOutputs( + ctx context.Context, + threadID string, + runID string, + request SubmitToolOutputsRequest) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/submit_tool_outputs", threadID, runID) + req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} + +// CancelRun cancels a run. +func (c *Client) CancelRun( + ctx context.Context, + threadID string, + runID string, + request SubmitToolOutputsRequest) (response Run, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/cancel", threadID, runID) + req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} + +// CreateThreadAndRun submits tool outputs. +func (c *Client) CreateThreadAndRun( + ctx context.Context, + request CreateThreadAndRunRequest) (response Run, err error) { + urlSuffix := "/threads/runs" + req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} diff --git a/run_test.go b/run_test.go index bddf573f..ce81c58d 100644 --- a/run_test.go +++ b/run_test.go @@ -17,11 +17,6 @@ func TestRun(t *testing.T) { assistantID := "asst_abc123" threadID := "thread_abc123" runID := "run_abc123" - // assistantName := "Ambrogio" - // assistantDescription := "Ambrogio is a friendly assistant." - // assitantInstructions := `You are a personal math tutor. - // When asked a question, write and run Python code to answer the question.` - // assistantFileID := "file-wB6RM6wHdA49HfS2DJ9fEyrH" limit := 20 order := "desc" after := "asst_abc122" From 2f7db50a537df5659f807495e2a9f40723da4597 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:28:42 +0100 Subject: [PATCH 05/11] chore: add api and tests --- client_test.go | 9 +++++++ run.go | 54 +++++++++++++++++++++++++++++++++++--- run_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/client_test.go b/client_test.go index a3e6b061..9622e78c 100644 --- a/client_test.go +++ b/client_test.go @@ -325,6 +325,15 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) { {"ListRuns", func() (any, error) { return client.ListRuns(ctx, "", nil, nil, nil, nil) }}, + {"SubmitToolOutputs", func() (any, error) { + return client.SubmitToolOutputs(ctx, "", "", SubmitToolOutputsRequest{}) + }}, + {"CancelRun", func() (any, error) { + return client.CancelRun(ctx, "", "") + }}, + {"CreateThreadAndRun", func() (any, error) { + return client.CreateThreadAndRun(ctx, CreateThreadAndRunRequest{}) + }}, } for _, testCase := range testCases { diff --git a/run.go b/run.go index 33380f9c..dc54684e 100644 --- a/run.go +++ b/run.go @@ -99,7 +99,56 @@ type ToolOutput struct { type CreateThreadAndRunRequest struct { RunRequest - // Thread *ThreadRequest `json:"thread,omitempty"` uncomment when thread is implemented + Thread ThreadRequest `json:"thread"` +} + +type RunStep struct { + ID string `json:"id"` + Object string `json:"object"` + CreatedAt int64 `json:"created_at"` + AssistantID string `json:"assistant_id"` + ThreadID string `json:"thread_id"` + RunID string `json:"run_id"` + Type RunStepType `json:"type"` + Status RunStepStatus `json:"status"` + StepDetails StepDetails `json:"step_details"` + LastError *RunLastError `json:"last_error,omitempty"` + ExpiredAt *int64 `json:"expired_at,omitempty"` + CancelledAt *int64 `json:"cancelled_at,omitempty"` + FailedAt *int64 `json:"failed_at,omitempty"` + CompletedAt *int64 `json:"completed_at,omitempty"` + Metadata map[string]any `json:"metadata"` +} + +type RunStepStatus string + +const ( + RunStepStatusInProgress RunStatus = "in_progress" + RunStepStatusCancelling RunStatus = "cancelled" + RunStepStatusFailed RunStatus = "failed" + RunStepStatusCompleted RunStatus = "completed" + RunStepStatusExpired RunStatus = "expired" +) + +type RunStepType string + +const ( + RunStepTypeMessageCreation RunStepType = "message_creation" + RunStepTypeToolCalls RunStepType = "tool_calls" +) + +type StepDetails struct { + Type RunStepType `json:"type"` + MessageCreation *StepDetailsMessageCreation `json:"message_creation,omitempty"` + ToolCalls *StepDetailsToolCalls `json:"tool_calls,omitempty"` +} + +type StepDetailsMessageCreation struct { + MessageID string `json:"message_id"` +} + +type StepDetailsToolCalls struct { + ToolCalls []ToolCall `json:"tool_calls"` } // CreateRun creates a new run. @@ -214,8 +263,7 @@ func (c *Client) SubmitToolOutputs( func (c *Client) CancelRun( ctx context.Context, threadID string, - runID string, - request SubmitToolOutputsRequest) (response Run, err error) { + runID string) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/cancel", threadID, runID) req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBetaAssistantV1()) diff --git a/run_test.go b/run_test.go index ce81c58d..b9288698 100644 --- a/run_test.go +++ b/run_test.go @@ -25,6 +25,36 @@ func TestRun(t *testing.T) { client, server, teardown := setupOpenAITestServer() defer teardown() + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs/"+runID+"/cancel", + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusCancelling, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs/"+runID+"/submit_tool_outputs", + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusCancelling, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + server.RegisterHandler( "/v1/threads/"+threadID+"/runs/"+runID, func(w http.ResponseWriter, r *http.Request) { @@ -84,6 +114,25 @@ func TestRun(t *testing.T) { }, ) + server.RegisterHandler( + "/v1/threads/runs", + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + var request openai.CreateThreadAndRunRequest + err := json.NewDecoder(r.Body).Decode(&request) + checks.NoError(t, err, "Decode error") + + resBytes, _ := json.Marshal(openai.Run{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStatusQueued, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + ctx := context.Background() _, err := client.CreateRun(ctx, threadID, openai.RunRequest{ @@ -103,4 +152,26 @@ func TestRun(t *testing.T) { _, err = client.ListRuns(ctx, threadID, &limit, &order, &after, &before) checks.NoError(t, err, "ListRuns error") + + _, err = client.SubmitToolOutputs(ctx, threadID, runID, + openai.SubmitToolOutputsRequest{}) + checks.NoError(t, err, "SubmitToolOutputs error") + + _, err = client.CancelRun(ctx, threadID, runID) + checks.NoError(t, err, "CancelRun error") + + _, err = client.CreateThreadAndRun(ctx, openai.CreateThreadAndRunRequest{ + RunRequest: openai.RunRequest{ + AssistantID: assistantID, + }, + Thread: openai.ThreadRequest{ + Messages: []openai.ThreadMessage{ + { + Role: openai.ThreadMessageRoleUser, + Content: "Hello, World!", + }, + }, + }, + }) + checks.NoError(t, err, "CreateThreadAndRun error") } From 2470008a7b708a3c603ab68e93926e858d93f9bd Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:36:16 +0100 Subject: [PATCH 06/11] chore: add tests --- run.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++++---- run_test.go | 38 ++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 6 deletions(-) diff --git a/run.go b/run.go index dc54684e..1120c16f 100644 --- a/run.go +++ b/run.go @@ -81,7 +81,7 @@ type RunModifyRequest struct { Metadata map[string]any `json:"metadata,omitempty"` } -// RunList is a list of assistants. +// RunList is a list of runs. type RunList struct { Runs []Run `json:"data"` @@ -118,16 +118,18 @@ type RunStep struct { FailedAt *int64 `json:"failed_at,omitempty"` CompletedAt *int64 `json:"completed_at,omitempty"` Metadata map[string]any `json:"metadata"` + + httpHeader } type RunStepStatus string const ( - RunStepStatusInProgress RunStatus = "in_progress" - RunStepStatusCancelling RunStatus = "cancelled" - RunStepStatusFailed RunStatus = "failed" - RunStepStatusCompleted RunStatus = "completed" - RunStepStatusExpired RunStatus = "expired" + RunStepStatusInProgress RunStepStatus = "in_progress" + RunStepStatusCancelling RunStepStatus = "cancelled" + RunStepStatusFailed RunStepStatus = "failed" + RunStepStatusCompleted RunStepStatus = "completed" + RunStepStatusExpired RunStepStatus = "expired" ) type RunStepType string @@ -151,6 +153,13 @@ type StepDetailsToolCalls struct { ToolCalls []ToolCall `json:"tool_calls"` } +// RunStepList is a list of steps. +type RunStepList struct { + RunSteps []RunStep `json:"data"` + + httpHeader +} + // CreateRun creates a new run. func (c *Client) CreateRun( ctx context.Context, @@ -289,3 +298,61 @@ func (c *Client) CreateThreadAndRun( err = c.sendRequest(req, &response) return } + +// RetrieveRunStep retrieves a run step. +func (c *Client) RetrieveRunStep( + ctx context.Context, + threadID string, + runID string, + stepID string, +) (response RunStep, err error) { + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/steps/%s", threadID, runID, stepID) + req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} + +// ListRunSteps lists run steps. +func (c *Client) ListRunSteps( + ctx context.Context, + threadID string, + runID string, + limit *int, + order *string, + after *string, + before *string, +) (response RunStepList, err error) { + urlValues := url.Values{} + if limit != nil { + urlValues.Add("limit", fmt.Sprintf("%d", *limit)) + } + if order != nil { + urlValues.Add("order", *order) + } + if after != nil { + urlValues.Add("after", *after) + } + if before != nil { + urlValues.Add("before", *before) + } + + encodedValues := "" + if len(urlValues) > 0 { + encodedValues = "?" + urlValues.Encode() + } + + urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/steps%s", threadID, runID, encodedValues) + req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), + withBetaAssistantV1()) + if err != nil { + return + } + + err = c.sendRequest(req, &response) + return +} diff --git a/run_test.go b/run_test.go index b9288698..34748db7 100644 --- a/run_test.go +++ b/run_test.go @@ -17,6 +17,7 @@ func TestRun(t *testing.T) { assistantID := "asst_abc123" threadID := "thread_abc123" runID := "run_abc123" + stepID := "step_abc123" limit := 20 order := "desc" after := "asst_abc122" @@ -25,6 +26,40 @@ func TestRun(t *testing.T) { client, server, teardown := setupOpenAITestServer() defer teardown() + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs/"+runID+"/steps/"+stepID, + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + resBytes, _ := json.Marshal(openai.RunStep{ + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStepStatusCompleted, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + + server.RegisterHandler( + "/v1/threads/"+threadID+"/runs/"+runID+"/steps/", + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + resBytes, _ := json.Marshal(openai.RunStepList{ + RunSteps: []openai.RunStep{ + { + ID: runID, + Object: "run", + CreatedAt: 1234567890, + Status: openai.RunStepStatusCompleted, + }, + }, + }) + fmt.Fprintln(w, string(resBytes)) + } + }, + ) + server.RegisterHandler( "/v1/threads/"+threadID+"/runs/"+runID+"/cancel", func(w http.ResponseWriter, r *http.Request) { @@ -174,4 +209,7 @@ func TestRun(t *testing.T) { }, }) checks.NoError(t, err, "CreateThreadAndRun error") + + _, err = client.RetrieveRunStep(ctx, threadID, runID, stepID) + checks.NoError(t, err, "CreateThreadAndRun error") } From 05d5ea2e3cbe3cad572bb8a79f77f0d11c77668b Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:40:13 +0100 Subject: [PATCH 07/11] fix --- client_test.go | 6 ++++++ run_test.go | 3 +++ 2 files changed, 9 insertions(+) diff --git a/client_test.go b/client_test.go index 9622e78c..16d69f5a 100644 --- a/client_test.go +++ b/client_test.go @@ -334,6 +334,12 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) { {"CreateThreadAndRun", func() (any, error) { return client.CreateThreadAndRun(ctx, CreateThreadAndRunRequest{}) }}, + {"RetrieveRunStep", func() (any, error) { + return client.RetrieveRunStep(ctx, "", "", "") + }}, + {"ListRunSteps", func() (any, error) { + return client.ListRunSteps(ctx, "", "", nil, nil, nil, nil) + }}, } for _, testCase := range testCases { diff --git a/run_test.go b/run_test.go index 34748db7..47d00980 100644 --- a/run_test.go +++ b/run_test.go @@ -212,4 +212,7 @@ func TestRun(t *testing.T) { _, err = client.RetrieveRunStep(ctx, threadID, runID, stepID) checks.NoError(t, err, "CreateThreadAndRun error") + + _, err = client.ListRunSteps(ctx, threadID, runID, &limit, &order, &after, &before) + checks.NoError(t, err, "ListRunSteps error") } From 7d41dad230eb85335f670e998eb5aff7a391afb0 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:44:09 +0100 Subject: [PATCH 08/11] trigger build From aa276afe41eb07a090ff0b5b6e59a9d0d87a4727 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 09:50:46 +0100 Subject: [PATCH 09/11] fix --- run_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_test.go b/run_test.go index 47d00980..36c71947 100644 --- a/run_test.go +++ b/run_test.go @@ -42,7 +42,7 @@ func TestRun(t *testing.T) { ) server.RegisterHandler( - "/v1/threads/"+threadID+"/runs/"+runID+"/steps/", + "/v1/threads/"+threadID+"/runs/"+runID+"/steps", func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { resBytes, _ := json.Marshal(openai.RunStepList{ @@ -211,7 +211,7 @@ func TestRun(t *testing.T) { checks.NoError(t, err, "CreateThreadAndRun error") _, err = client.RetrieveRunStep(ctx, threadID, runID, stepID) - checks.NoError(t, err, "CreateThreadAndRun error") + checks.NoError(t, err, "RetrieveRunStep error") _, err = client.ListRunSteps(ctx, threadID, runID, &limit, &order, &after, &before) checks.NoError(t, err, "ListRunSteps error") From 413fea3a482c1db8234af3bc3745ecb83f84b4a0 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 18:00:01 +0100 Subject: [PATCH 10/11] chore: formatting code --- run.go | 76 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/run.go b/run.go index 1120c16f..1ae9c465 100644 --- a/run.go +++ b/run.go @@ -167,8 +167,13 @@ func (c *Client) CreateRun( request RunRequest, ) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs", threadID) - req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodPost, + c.fullURL(urlSuffix), + withBody(request), + withBetaAssistantV1(), + ) if err != nil { return } @@ -184,8 +189,12 @@ func (c *Client) RetrieveRun( runID string, ) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s", threadID, runID) - req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodGet, + c.fullURL(urlSuffix), + withBetaAssistantV1(), + ) if err != nil { return } @@ -202,8 +211,13 @@ func (c *Client) ModifyRun( request RunModifyRequest, ) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s", threadID, runID) - req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodPost, + c.fullURL(urlSuffix), + withBody(request), + withBetaAssistantV1(), + ) if err != nil { return } @@ -241,8 +255,12 @@ func (c *Client) ListRuns( } urlSuffix := fmt.Sprintf("/threads/%s/runs%s", threadID, encodedValues) - req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodGet, + c.fullURL(urlSuffix), + withBetaAssistantV1(), + ) if err != nil { return } @@ -258,8 +276,13 @@ func (c *Client) SubmitToolOutputs( runID string, request SubmitToolOutputsRequest) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/submit_tool_outputs", threadID, runID) - req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodPost, + c.fullURL(urlSuffix), + withBody(request), + withBetaAssistantV1(), + ) if err != nil { return } @@ -274,8 +297,12 @@ func (c *Client) CancelRun( threadID string, runID string) (response Run, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/cancel", threadID, runID) - req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodPost, + c.fullURL(urlSuffix), + withBetaAssistantV1(), + ) if err != nil { return } @@ -289,8 +316,13 @@ func (c *Client) CreateThreadAndRun( ctx context.Context, request CreateThreadAndRunRequest) (response Run, err error) { urlSuffix := "/threads/runs" - req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodPost, + c.fullURL(urlSuffix), + withBody(request), + withBetaAssistantV1(), + ) if err != nil { return } @@ -307,8 +339,12 @@ func (c *Client) RetrieveRunStep( stepID string, ) (response RunStep, err error) { urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/steps/%s", threadID, runID, stepID) - req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodGet, + c.fullURL(urlSuffix), + withBetaAssistantV1(), + ) if err != nil { return } @@ -347,8 +383,12 @@ func (c *Client) ListRunSteps( } urlSuffix := fmt.Sprintf("/threads/%s/runs/%s/steps%s", threadID, runID, encodedValues) - req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix), - withBetaAssistantV1()) + req, err := c.newRequest( + ctx, + http.MethodGet, + c.fullURL(urlSuffix), + withBetaAssistantV1(), + ) if err != nil { return } From e5a29cd6d8d6ec565dc1a8bc9487da2885636f04 Mon Sep 17 00:00:00 2001 From: Simone Vellei Date: Thu, 9 Nov 2023 18:00:32 +0100 Subject: [PATCH 11/11] chore: add pagination type --- client_test.go | 4 ++-- run.go | 49 +++++++++++++++++++++++++------------------------ run_test.go | 23 +++++++++++++++++++++-- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/client_test.go b/client_test.go index 16d69f5a..d5d3e264 100644 --- a/client_test.go +++ b/client_test.go @@ -323,7 +323,7 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) { return client.ModifyRun(ctx, "", "", RunModifyRequest{}) }}, {"ListRuns", func() (any, error) { - return client.ListRuns(ctx, "", nil, nil, nil, nil) + return client.ListRuns(ctx, "", Pagination{}) }}, {"SubmitToolOutputs", func() (any, error) { return client.SubmitToolOutputs(ctx, "", "", SubmitToolOutputsRequest{}) @@ -338,7 +338,7 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) { return client.RetrieveRunStep(ctx, "", "", "") }}, {"ListRunSteps", func() (any, error) { - return client.ListRunSteps(ctx, "", "", nil, nil, nil, nil) + return client.ListRunSteps(ctx, "", "", Pagination{}) }}, } diff --git a/run.go b/run.go index 1ae9c465..5d6ea58d 100644 --- a/run.go +++ b/run.go @@ -160,6 +160,13 @@ type RunStepList struct { httpHeader } +type Pagination struct { + Limit *int + Order *string + After *string + Before *string +} + // CreateRun creates a new run. func (c *Client) CreateRun( ctx context.Context, @@ -230,23 +237,20 @@ func (c *Client) ModifyRun( func (c *Client) ListRuns( ctx context.Context, threadID string, - limit *int, - order *string, - after *string, - before *string, + pagination Pagination, ) (response RunList, err error) { urlValues := url.Values{} - if limit != nil { - urlValues.Add("limit", fmt.Sprintf("%d", *limit)) + if pagination.Limit != nil { + urlValues.Add("limit", fmt.Sprintf("%d", *pagination.Limit)) } - if order != nil { - urlValues.Add("order", *order) + if pagination.Order != nil { + urlValues.Add("order", *pagination.Order) } - if after != nil { - urlValues.Add("after", *after) + if pagination.After != nil { + urlValues.Add("after", *pagination.After) } - if before != nil { - urlValues.Add("before", *before) + if pagination.Before != nil { + urlValues.Add("before", *pagination.Before) } encodedValues := "" @@ -358,23 +362,20 @@ func (c *Client) ListRunSteps( ctx context.Context, threadID string, runID string, - limit *int, - order *string, - after *string, - before *string, + pagination Pagination, ) (response RunStepList, err error) { urlValues := url.Values{} - if limit != nil { - urlValues.Add("limit", fmt.Sprintf("%d", *limit)) + if pagination.Limit != nil { + urlValues.Add("limit", fmt.Sprintf("%d", *pagination.Limit)) } - if order != nil { - urlValues.Add("order", *order) + if pagination.Order != nil { + urlValues.Add("order", *pagination.Order) } - if after != nil { - urlValues.Add("after", *after) + if pagination.After != nil { + urlValues.Add("after", *pagination.After) } - if before != nil { - urlValues.Add("before", *before) + if pagination.Before != nil { + urlValues.Add("before", *pagination.Before) } encodedValues := "" diff --git a/run_test.go b/run_test.go index 36c71947..cdf99db0 100644 --- a/run_test.go +++ b/run_test.go @@ -185,7 +185,16 @@ func TestRun(t *testing.T) { }) checks.NoError(t, err, "ModifyRun error") - _, err = client.ListRuns(ctx, threadID, &limit, &order, &after, &before) + _, err = client.ListRuns( + ctx, + threadID, + openai.Pagination{ + Limit: &limit, + Order: &order, + After: &after, + Before: &before, + }, + ) checks.NoError(t, err, "ListRuns error") _, err = client.SubmitToolOutputs(ctx, threadID, runID, @@ -213,6 +222,16 @@ func TestRun(t *testing.T) { _, err = client.RetrieveRunStep(ctx, threadID, runID, stepID) checks.NoError(t, err, "RetrieveRunStep error") - _, err = client.ListRunSteps(ctx, threadID, runID, &limit, &order, &after, &before) + _, err = client.ListRunSteps( + ctx, + threadID, + runID, + openai.Pagination{ + Limit: &limit, + Order: &order, + After: &after, + Before: &before, + }, + ) checks.NoError(t, err, "ListRunSteps error") }