File size: 4,727 Bytes
8d3471e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 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
}
|