diff --git a/internal/httpapi/openai/file_inline_upload_test.go b/internal/httpapi/openai/file_inline_upload_test.go index 4ea2445..fa399b8 100644 --- a/internal/httpapi/openai/file_inline_upload_test.go +++ b/internal/httpapi/openai/file_inline_upload_test.go @@ -216,6 +216,45 @@ func TestChatCompletionsInlineUploadFailureReturnsBadRequest(t *testing.T) { } } +func TestChatCompletionsInlineUploadLimitReturnsBadRequest(t *testing.T) { + ds := &inlineUploadDSStub{} + h := &openAITestSurface{Store: mockOpenAIConfig{wideInput: true}, Auth: streamStatusAuthStub{}, DS: ds} + content := []any{map[string]any{"type": "input_text", "text": "hi"}} + for i := 0; i < 51; i++ { + content = append(content, map[string]any{ + "type": "image_url", + "image_url": map[string]any{"url": "data:image/png;base64,QUJDRA=="}, + }) + } + body, err := json.Marshal(map[string]any{ + "model": "deepseek-v4-flash", + "messages": []any{map[string]any{ + "role": "user", + "content": content, + }}, + "stream": false, + }) + if err != nil { + t.Fatalf("marshal request: %v", err) + } + req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(string(body))) + req.Header.Set("Authorization", "Bearer direct-token") + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + h.ChatCompletions(rec, req) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d body=%s", rec.Code, rec.Body.String()) + } + if !strings.Contains(rec.Body.String(), "exceeded maximum of 50 inline files per request") { + t.Fatalf("expected inline file limit error, got body=%s", rec.Body.String()) + } + if ds.completionReq != nil { + t.Fatalf("did not expect completion call after inline file limit error") + } +} + func TestResponsesInlineUploadFailureReturnsInternalServerError(t *testing.T) { ds := &inlineUploadDSStub{uploadErr: errors.New("boom")} h := &openAITestSurface{Store: mockOpenAIConfig{wideInput: true}, Auth: streamStatusAuthStub{}, DS: ds} diff --git a/internal/httpapi/openai/files/file_inline_upload.go b/internal/httpapi/openai/files/file_inline_upload.go index c8d59a9..e12267c 100644 --- a/internal/httpapi/openai/files/file_inline_upload.go +++ b/internal/httpapi/openai/files/file_inline_upload.go @@ -135,7 +135,8 @@ func (s *inlineUploadState) tryUploadBlock(block map[string]any) (map[string]any return nil, false, nil } if s.uploadCount >= maxInlineFilesPerRequest { - return nil, true, fmt.Errorf("exceeded maximum of %d inline files per request", maxInlineFilesPerRequest) + err := fmt.Errorf("exceeded maximum of %d inline files per request", maxInlineFilesPerRequest) + return nil, true, &inlineFileUploadError{status: http.StatusBadRequest, message: err.Error(), err: err} } fileID, err := s.uploadInlineFile(decoded) if err != nil {