first commit
This commit is contained in:
220
tests/async/fixtures_test.go
Normal file
220
tests/async/fixtures_test.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package async
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// endpointCase describes a single async endpoint and the request payload to send.
|
||||
type endpointCase struct {
|
||||
name string
|
||||
submitPath string // POST target, e.g. /v1/async/chat/completions
|
||||
pollBase string // GET base; job ID is appended as /{job_id}
|
||||
body map[string]any
|
||||
multipart *multipartCase
|
||||
}
|
||||
|
||||
// multipartCase holds fields and named files for a multipart/form-data submission.
|
||||
type multipartCase struct {
|
||||
fields map[string]string
|
||||
files map[string]fileFixture
|
||||
}
|
||||
|
||||
type fileFixture struct {
|
||||
filename string
|
||||
data []byte
|
||||
}
|
||||
|
||||
// defaultModels maps each ASYNC_*_MODEL env key to its default model string.
|
||||
var defaultModels = map[string]string{
|
||||
"ASYNC_TEXT_COMPLETION_MODEL": "openai/gpt-3.5-turbo-instruct",
|
||||
"ASYNC_CHAT_COMPLETION_MODEL": "openai/gpt-4o-mini",
|
||||
"ASYNC_RESPONSES_MODEL": "openai/gpt-4o-mini",
|
||||
"ASYNC_EMBEDDINGS_MODEL": "openai/text-embedding-3-small",
|
||||
"ASYNC_SPEECH_MODEL": "openai/tts-1",
|
||||
"ASYNC_TRANSCRIPTION_MODEL": "openai/whisper-1",
|
||||
"ASYNC_IMAGE_GEN_MODEL": "openai/dall-e-3",
|
||||
"ASYNC_IMAGE_EDIT_MODEL": "openai/dall-e-2",
|
||||
"ASYNC_IMAGE_VARIATION_MODEL": "openai/dall-e-2",
|
||||
"ASYNC_RERANK_MODEL": "cohere/rerank-english-v3.0",
|
||||
"ASYNC_OCR_MODEL": "mistral/mistral-ocr-latest",
|
||||
}
|
||||
|
||||
// modelFor returns the env-var override for envKey, falling back to the default in defaultModels.
|
||||
func modelFor(envKey string) string {
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultModels[envKey]
|
||||
}
|
||||
|
||||
// endpointCases returns the full set of async endpoint fixtures, one per supported endpoint.
|
||||
// Override any model via the corresponding ASYNC_*_MODEL environment variable.
|
||||
func endpointCases() []endpointCase {
|
||||
return []endpointCase{
|
||||
{
|
||||
name: "text_completions",
|
||||
submitPath: "/v1/async/completions",
|
||||
pollBase: "/v1/async/completions",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_TEXT_COMPLETION_MODEL"),
|
||||
"prompt": "Say hello in one word.",
|
||||
"max_tokens": 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "chat_completions",
|
||||
submitPath: "/v1/async/chat/completions",
|
||||
pollBase: "/v1/async/chat/completions",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_CHAT_COMPLETION_MODEL"),
|
||||
"messages": []map[string]any{
|
||||
{"role": "user", "content": "Say hello in one word."},
|
||||
},
|
||||
"max_tokens": 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "responses",
|
||||
submitPath: "/v1/async/responses",
|
||||
pollBase: "/v1/async/responses",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_RESPONSES_MODEL"),
|
||||
"input": "Say hello in one word.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "embeddings",
|
||||
submitPath: "/v1/async/embeddings",
|
||||
pollBase: "/v1/async/embeddings",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_EMBEDDINGS_MODEL"),
|
||||
"input": "Hello world",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "speech",
|
||||
submitPath: "/v1/async/audio/speech",
|
||||
pollBase: "/v1/async/audio/speech",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_SPEECH_MODEL"),
|
||||
"input": "Hello",
|
||||
"voice": "alloy",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "transcriptions",
|
||||
submitPath: "/v1/async/audio/transcriptions",
|
||||
pollBase: "/v1/async/audio/transcriptions",
|
||||
multipart: &multipartCase{
|
||||
fields: map[string]string{
|
||||
"model": modelFor("ASYNC_TRANSCRIPTION_MODEL"),
|
||||
},
|
||||
files: map[string]fileFixture{
|
||||
"file": {filename: "sample.mp3", data: sampleAudio()},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image_generations",
|
||||
submitPath: "/v1/async/images/generations",
|
||||
pollBase: "/v1/async/images/generations",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_IMAGE_GEN_MODEL"),
|
||||
"prompt": "A simple red circle on a white background",
|
||||
"n": 1,
|
||||
"size": "1024x1024",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image_edits",
|
||||
submitPath: "/v1/async/images/edits",
|
||||
pollBase: "/v1/async/images/edits",
|
||||
multipart: &multipartCase{
|
||||
fields: map[string]string{
|
||||
"model": modelFor("ASYNC_IMAGE_EDIT_MODEL"),
|
||||
"prompt": "Make it blue",
|
||||
"n": "1",
|
||||
"size": "256x256",
|
||||
},
|
||||
files: map[string]fileFixture{
|
||||
"image": {filename: "image.png", data: samplePNG()},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "image_variations",
|
||||
submitPath: "/v1/async/images/variations",
|
||||
pollBase: "/v1/async/images/variations",
|
||||
multipart: &multipartCase{
|
||||
fields: map[string]string{
|
||||
"model": modelFor("ASYNC_IMAGE_VARIATION_MODEL"),
|
||||
"n": "1",
|
||||
"size": "256x256",
|
||||
},
|
||||
files: map[string]fileFixture{
|
||||
"image": {filename: "image.png", data: samplePNG()},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rerank",
|
||||
submitPath: "/v1/async/rerank",
|
||||
pollBase: "/v1/async/rerank",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_RERANK_MODEL"),
|
||||
"query": "What is the capital of France?",
|
||||
"documents": []map[string]any{
|
||||
{"text": "Paris is the capital of France."},
|
||||
{"text": "London is the capital of the United Kingdom."},
|
||||
{"text": "Berlin is the capital of Germany."},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ocr",
|
||||
submitPath: "/v1/async/ocr",
|
||||
pollBase: "/v1/async/ocr",
|
||||
body: map[string]any{
|
||||
"model": modelFor("ASYNC_OCR_MODEL"),
|
||||
"document": map[string]any{
|
||||
"type": "image_url",
|
||||
"image_url": envOr("ASYNC_OCR_IMAGE_URL", "https://pestworldcdn-dcf2a8gbggazaghf.z01.azurefd.net/media/561791/carpenter-ant4.jpg"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// sampleAudio reads core/internal/llmtests/scenarios/media/sample.mp3.
|
||||
// go test sets the working directory to the package source directory, so the
|
||||
// relative path is stable without runtime.Caller (which breaks under -trimpath).
|
||||
func sampleAudio() []byte {
|
||||
mediaPath := filepath.Join("..", "..", "core", "internal", "llmtests", "scenarios", "media", "sample.mp3")
|
||||
data, err := os.ReadFile(mediaPath)
|
||||
if err != nil {
|
||||
panic("sampleAudio: cannot read " + mediaPath + ": " + err.Error())
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// samplePNG generates a 256x256 white RGBA PNG for image edit / variation fixtures.
|
||||
// DALL-E 2 requires images with an alpha channel (RGBA PNG).
|
||||
func samplePNG() []byte {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 256, 256))
|
||||
white := color.RGBA{R: 255, G: 255, B: 255, A: 255}
|
||||
for y := range 256 {
|
||||
for x := range 256 {
|
||||
img.Set(x, y, white)
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
panic("samplePNG: encode failed: " + err.Error())
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
Reference in New Issue
Block a user