Files
ds2api/internal/httpapi/openai/test_bridge_test.go

161 lines
4.6 KiB
Go

package openai
import (
"context"
"encoding/json"
"net/http"
"strings"
"testing"
"github.com/go-chi/chi/v5"
"ds2api/internal/auth"
"ds2api/internal/chathistory"
"ds2api/internal/httpapi/openai/chat"
"ds2api/internal/httpapi/openai/embeddings"
"ds2api/internal/httpapi/openai/files"
"ds2api/internal/httpapi/openai/history"
"ds2api/internal/httpapi/openai/responses"
"ds2api/internal/httpapi/openai/shared"
"ds2api/internal/promptcompat"
)
type openAITestSurface struct {
Store shared.ConfigReader
Auth shared.AuthResolver
DS shared.DeepSeekCaller
ChatHistory *chathistory.Store
chat *chat.Handler
responses *responses.Handler
files *files.Handler
embeddings *embeddings.Handler
models *shared.ModelsHandler
}
func (h *openAITestSurface) deps() shared.Deps {
if h == nil {
return shared.Deps{}
}
return shared.Deps{Store: h.Store, Auth: h.Auth, DS: h.DS, ChatHistory: h.ChatHistory}
}
func (h *openAITestSurface) chatHandler() *chat.Handler {
if h.chat == nil {
deps := h.deps()
h.chat = &chat.Handler{Store: deps.Store, Auth: deps.Auth, DS: deps.DS, ChatHistory: deps.ChatHistory}
}
return h.chat
}
func (h *openAITestSurface) responsesHandler() *responses.Handler {
if h.responses == nil {
deps := h.deps()
h.responses = &responses.Handler{Store: deps.Store, Auth: deps.Auth, DS: deps.DS, ChatHistory: deps.ChatHistory}
}
return h.responses
}
func (h *openAITestSurface) filesHandler() *files.Handler {
if h.files == nil {
deps := h.deps()
h.files = &files.Handler{Store: deps.Store, Auth: deps.Auth, DS: deps.DS, ChatHistory: deps.ChatHistory}
}
return h.files
}
func (h *openAITestSurface) embeddingsHandler() *embeddings.Handler {
if h.embeddings == nil {
deps := h.deps()
h.embeddings = &embeddings.Handler{Store: deps.Store, Auth: deps.Auth, DS: deps.DS, ChatHistory: deps.ChatHistory}
}
return h.embeddings
}
func (h *openAITestSurface) modelsHandler() *shared.ModelsHandler {
if h.models == nil {
h.models = &shared.ModelsHandler{Store: h.Store}
}
return h.models
}
func (h *openAITestSurface) ChatCompletions(w http.ResponseWriter, r *http.Request) {
h.chatHandler().ChatCompletions(w, r)
}
func (h *openAITestSurface) applyCurrentInputFile(ctx context.Context, a *auth.RequestAuth, stdReq promptcompat.StandardRequest) (promptcompat.StandardRequest, error) {
stdReq = shared.ApplyThinkingInjection(h.Store, stdReq)
svc := history.Service{Store: h.Store, DS: h.DS}
out, err := svc.ApplyCurrentInputFile(ctx, a, stdReq)
if err != nil || out.CurrentInputFileApplied {
return out, err
}
return out, nil
}
func (h *openAITestSurface) preprocessInlineFileInputs(ctx context.Context, a *auth.RequestAuth, req map[string]any) error {
return h.filesHandler().PreprocessInlineFileInputs(ctx, a, req)
}
func registerOpenAITestRoutes(r chi.Router, h *openAITestSurface) {
r.Get("/v1/models", h.modelsHandler().ListModels)
r.Get("/v1/models/{model_id}", h.modelsHandler().GetModel)
r.Post("/v1/chat/completions", h.chatHandler().ChatCompletions)
r.Post("/v1/responses", h.responsesHandler().Responses)
r.Get("/v1/responses/{response_id}", h.responsesHandler().GetResponseByID)
r.Post("/v1/files", h.filesHandler().UploadFile)
r.Get("/v1/files/{file_id}", h.filesHandler().RetrieveFile)
r.Post("/v1/embeddings", h.embeddingsHandler().Embeddings)
}
func buildOpenAICurrentInputContextTranscript(messages []any) string {
return promptcompat.BuildOpenAICurrentInputContextTranscript(messages)
}
func writeOpenAIError(w http.ResponseWriter, status int, message string) {
shared.WriteOpenAIError(w, status, message)
}
func replaceCitationMarkersWithLinks(text string, links map[int]string) string {
return shared.ReplaceCitationMarkersWithLinks(text, links)
}
func sanitizeLeakedOutput(text string) string {
return shared.CleanVisibleOutput(text, false)
}
func requestTraceID(r *http.Request) string {
return shared.RequestTraceID(r)
}
func asString(v any) string {
return shared.AsString(v)
}
func parseSSEDataFrames(t *testing.T, body string) ([]map[string]any, bool) {
t.Helper()
lines := strings.Split(body, "\n")
frames := make([]map[string]any, 0, len(lines))
done := false
for _, line := range lines {
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, "data:") {
continue
}
payload := strings.TrimSpace(strings.TrimPrefix(line, "data:"))
if payload == "" {
continue
}
if payload == "[DONE]" {
done = true
continue
}
var frame map[string]any
if err := json.Unmarshal([]byte(payload), &frame); err != nil {
t.Fatalf("decode sse frame failed: %v, payload=%s", err, payload)
}
frames = append(frames, frame)
}
return frames, done
}