Files

195 lines
7.1 KiB
Go

package jsplugin
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"runtime"
"strings"
"sync/atomic"
"testing"
"github.com/QuantumNous/new-api/common"
pluginruntime "github.com/QuantumNous/new-api/pkg/jsplugin"
"github.com/QuantumNous/new-api/plugins"
relaycommon "github.com/QuantumNous/new-api/relay/common"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func benchmarkAlibaba(b *testing.B) (*pluginruntime.LoadedPlugin, *atomic.Int64) {
b.Helper()
source, err := plugins.Source("alibaba")
require.NoError(b, err)
initializations := &atomic.Int64{}
plugin, err := pluginruntime.CompilePlugin(source+"\n"+`console.log("benchmark_runtime_initialized");`, pluginruntime.Options{
Log: func(message string) {
if strings.HasSuffix(message, "benchmark_runtime_initialized") {
initializations.Add(1)
}
},
})
require.NoError(b, err)
return plugin, initializations
}
func BenchmarkTaskPluginRuntime(b *testing.B) {
for _, name := range []string{"HasExport", "MissingExport", "HookCall", "ParallelHookCall"} {
b.Run(name, func(b *testing.B) {
plugin, initializations := benchmarkAlibaba(b)
ctx := context.Background()
input := map[string]any{"model": "wan2.5-i2v-preview", "upstreamModel": "wan2.5-i2v-preview", "requestBody": map[string]any{
"prompt": "animate the landscape", "image": "https://cdn.example/image.png", "duration": 5, "size": "720P",
}}
before := initializations.Load()
b.ReportAllocs()
if name == "ParallelHookCall" {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if _, err := plugin.Engine.Call(ctx, "extractUsage", input); err != nil {
b.Error(err)
return
}
}
})
} else {
for b.Loop() {
var err error
switch name {
case "HasExport":
if !plugin.Engine.HasExport("extractUsage") {
b.Fatal("extractUsage not exported")
}
case "MissingExport":
if plugin.Engine.HasExport("absentHook") {
b.Fatal("absentHook exported")
}
default:
_, err = plugin.Engine.Call(ctx, "extractUsage", input)
}
if err != nil {
b.Fatal(err)
}
}
}
b.ReportMetric(float64(initializations.Load()-before)/float64(b.N), "runtime_inits/op")
})
}
}
// Run with -benchtime=50x: GC is excluded so this measures the first hook after
// reclamation, not the runtime.GC operation or an artificial throughput workload.
func BenchmarkTaskPluginAfterGC(b *testing.B) {
plugin, initializations := benchmarkAlibaba(b)
input := map[string]any{"model": "wan2.5-i2v-preview", "upstreamModel": "wan2.5-i2v-preview", "requestBody": map[string]any{
"prompt": "animate the landscape", "image": "https://cdn.example/image.png", "duration": 5, "size": "720P",
}}
before := initializations.Load()
b.ReportAllocs()
b.ResetTimer()
for range b.N {
b.StopTimer()
runtime.GC()
runtime.GC()
b.StartTimer()
if _, err := plugin.Engine.Call(b.Context(), "extractUsage", input); err != nil {
b.Fatal(err)
}
}
b.ReportMetric(float64(initializations.Load()-before)/float64(b.N), "runtime_inits/op")
}
func BenchmarkTaskPluginSubmit(b *testing.B) {
for _, size := range []int{4 << 10, 1 << 20, 8 << 20} {
b.Run(fmt.Sprintf("%dKiB", size>>10), func(b *testing.B) {
plugin, initializations := benchmarkAlibaba(b)
const modelName = "wan2.5-i2v-preview"
wire, err := common.Marshal(map[string]any{
"model": modelName, "duration": 5, "size": "720P",
"input": []any{map[string]any{"type": "input_text", "text": "animate the landscape"}, map[string]any{"type": "input_image", "image_url": "data:image/png;base64," + strings.Repeat("A", size)}},
})
require.NoError(b, err)
before := initializations.Load()
b.SetBytes(int64(len(wire)))
b.ReportAllocs()
for b.Loop() {
var body any
if err := common.Unmarshal(wire, &body); err != nil {
b.Fatal(err)
}
protocol := pluginruntime.ProtocolRequestContext{
RouteRequestContext: pluginruntime.RouteRequestContext{Path: "/v1/responses", Method: http.MethodPost, Body: map[string]any{"kind": "json", "value": body}},
Protocol: "openai_responses", Operation: "create", Model: modelName,
}
// The middleware decoder runs before channel selection; the adaptor
// decodes again after the executing provider has been pinned.
decoded, err := plugin.Engine.CallPath(b.Context(), "protocols", []string{"openai_responses", "decodeRequest"}, protocol.JSValue())
if err != nil {
b.Fatal(err)
}
c, _ := gin.CreateTestContext(httptest.NewRecorder())
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", nil)
c.Set("task_request", decoded.(map[string]any)["requestBody"])
c.Set(pluginruntime.ContextKeyRouteRequest, protocol.RouteRequestContext)
c.Set(pluginruntime.ContextKeyProtocolRequest, protocol)
c.Set(pluginruntime.ContextKeyPinnedEndpoint, pluginruntime.PinnedEndpoint{Plugin: plugin, Protocol: protocol.Protocol, Model: modelName})
info := &relaycommon.RelayInfo{OriginModelName: modelName, ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: modelName, ChannelBaseUrl: "https://provider.example"}, TaskRelayInfo: &relaycommon.TaskRelayInfo{PublicTaskID: "benchmark-task"}}
adaptor := New(plugin)
adaptor.Init(info)
if taskErr := adaptor.ValidateRequestAndSetAction(c, info); taskErr != nil {
b.Fatal(taskErr)
}
if _, err := adaptor.ExtractUsageFactsValidated(c, info); err != nil {
b.Fatal(err)
}
upstream, err := adaptor.BuildRequestBody(c, info)
if err != nil {
b.Fatal(err)
}
if _, err := io.Copy(io.Discard, upstream); err != nil {
b.Fatal(err)
}
}
b.ReportMetric(float64(initializations.Load()-before)/float64(b.N), "runtime_inits/op")
})
}
}
func BenchmarkTaskPluginSSE(b *testing.B) {
for _, tc := range []struct {
name string
events, textBytes int
}{{"small_state", 32, 48}, {"growing_state", 64, 8 << 10}} {
b.Run(tc.name, func(b *testing.B) {
plugin, initializations := benchmarkAlibaba(b)
var stream strings.Builder
for index := range tc.events {
choice := map[string]any{"message": map[string]any{"role": "assistant", "content": []any{map[string]any{"text": strings.Repeat("a", tc.textBytes)}}}}
if index == tc.events-1 {
choice["finish_reason"] = "stop"
}
frame, err := common.Marshal(map[string]any{"request_id": "benchmark-sse", "output": map[string]any{"choices": []any{choice}}, "usage": map[string]any{"image_count": 1}})
require.NoError(b, err)
stream.WriteString("data: ")
stream.Write(frame)
stream.WriteString("\n\n")
}
wire := stream.String()
input := map[string]any{"model": "wan2.6-image"}
before := initializations.Load()
b.SetBytes(int64(len(wire)))
b.ReportAllocs()
for b.Loop() {
response := &http.Response{Header: http.Header{"Content-Type": {"text/event-stream"}}, Body: io.NopCloser(strings.NewReader(wire))}
if _, err := New(plugin).readSubmitEvents(b.Context(), response, input); err != nil {
b.Fatal(err)
}
}
b.ReportMetric(float64(initializations.Load()-before)/float64(b.N), "runtime_inits/op")
})
}
}