Muse now forwards business context alongside each per-user token request, so new-api needs to ingest those tags for relay selection, billing audit, and log correlation without breaking its own internal request-id semantics. This adds a dedicated Muse request-context middleware, carries Muse tags into RelayInfo, and appends them into consume/error log payloads. Muse request ids are stored as separate business metadata instead of overwriting the internal request id used for pre-consume idempotency. Constraint: The relay layer must keep working for both /v1 and /v1beta request paths Constraint: Client-supplied Muse request ids cannot replace the internal request id used by new-api billing/idempotency logic Rejected: Overwriting common.RequestIdKey with X-Request-Id | mixes external business ids with internal idempotency ids Rejected: Adding new log table columns in phase 1 | this task only needs relay/log propagation, so other JSON is the minimal compatible path Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Keep Muse business identifiers in dedicated context keys and log other fields; do not reuse internal request-id slots for external correlation ids Tested: go test ./middleware ./relay/common ./controller -run MuseRequestContext -count=1 Tested: go test ./middleware ./relay/common ./controller -count=1 Not-tested: Live relay call from Muse into a running new-api instance
74 lines
2.7 KiB
Go
74 lines
2.7 KiB
Go
package common
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/constant"
|
|
"github.com/QuantumNous/new-api/dto"
|
|
"github.com/QuantumNous/new-api/types"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatPrefersExplicitFinal(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatOpenAI,
|
|
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
|
|
FinalRequestRelayFormat: types.RelayFormatOpenAIResponses,
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatOpenAIResponses), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToConversionChain(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatOpenAI,
|
|
RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatClaude},
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatClaude), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatFallsBackToRelayFormat(t *testing.T) {
|
|
info := &RelayInfo{
|
|
RelayFormat: types.RelayFormatGemini,
|
|
}
|
|
|
|
require.Equal(t, types.RelayFormat(types.RelayFormatGemini), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestRelayInfoGetFinalRequestRelayFormatNilReceiver(t *testing.T) {
|
|
var info *RelayInfo
|
|
require.Equal(t, types.RelayFormat(""), info.GetFinalRequestRelayFormat())
|
|
}
|
|
|
|
func TestMuseRequestContextGenRelayInfoCopiesMuseFields(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
recorder := httptest.NewRecorder()
|
|
ctx, _ := gin.CreateTestContext(recorder)
|
|
ctx.Request = httptest.NewRequest(http.MethodPost, "/v1/chat/completions", nil)
|
|
|
|
ctx.Set(common.RequestIdKey, "req-1")
|
|
common.SetContextKey(ctx, constant.ContextKeyUserId, 11)
|
|
common.SetContextKey(ctx, constant.ContextKeyUserGroup, "default")
|
|
common.SetContextKey(ctx, constant.ContextKeyUsingGroup, "default")
|
|
common.SetContextKey(ctx, constant.ContextKeyMuseUserID, "muse-user-1")
|
|
common.SetContextKey(ctx, constant.ContextKeyMuseWorkID, "42")
|
|
common.SetContextKey(ctx, constant.ContextKeyMuseRequestID, "muse-req-1")
|
|
common.SetContextKey(ctx, constant.ContextKeyMuseScene, "suggestion_generation")
|
|
common.SetContextKey(ctx, constant.ContextKeyMuseTraceID, "trace-1")
|
|
|
|
info := GenRelayInfoOpenAI(ctx, &dto.GeneralOpenAIRequest{})
|
|
|
|
require.NotNil(t, info)
|
|
require.Equal(t, "req-1", info.RequestId)
|
|
require.Equal(t, "muse-user-1", info.MuseUserID)
|
|
require.Equal(t, "42", info.MuseWorkID)
|
|
require.Equal(t, "muse-req-1", info.MuseRequestID)
|
|
require.Equal(t, "suggestion_generation", info.MuseScene)
|
|
require.Equal(t, "trace-1", info.MuseTraceID)
|
|
}
|