Files
new-api/relaykit/relayconvert/reasoning/intent_test.go
T
Calcium-Ion 0ed497f066 feat(relay): hosted-tool conversion fidelity, reasoning normalization, and billing usage integrity (#7137)
* feat(relaykit): preserve hosted tools across conversions

- add protocol-neutral hosted-tool DTOs, conversion metadata, and loss policies
- bridge citations, grounding metadata, and hosted-tool stream lifecycles
- document the public conversion behavior and channel policy controls

* refactor(relaykit): normalize reasoning and thinking intent

- centralize provider-neutral reasoning intent, effort, and budget mappings
- parse model suffixes at the host entry boundary while preserving provider-owned tails
- keep adaptive Claude thinking and explicit zero-token compatibility consistent

* fix(billing): preserve authoritative usage across relay hops

- carry native BillingUsage sidecars through direct and streamed protocol bridges
- merge partial and terminal usage monotonically with safe fallback settlement
- retain cache metadata, penultimate usage, and per-call Gemini tool surcharges

* feat(relay): bridge Responses with Claude and Gemini protocols

- add direct request, response, and stream converters across supported relay formats
- expose Claude count_tokens and Chat-to-Responses compatibility endpoints
- carry conversion diagnostics through the host while retaining the curated public goldens

* fix(relay): wire relaykit conversions into host channels

- connect handlers, adaptors, and channel settings to the standalone conversion layer
- keep model mapping, pricing identity, retries, and provider-specific suffix behavior aligned
- ignore local audit artifacts and retain focused public regression coverage
2026-09-01 21:53:35 +08:00

126 lines
3.4 KiB
Go

package reasoning
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMergeExplicitAndSuffix(t *testing.T) {
t.Parallel()
budget1024 := 1024
budget2048 := 2048
tests := []struct {
name string
explicit Intent
suffix Intent
wantErr bool
wantMode Mode
wantEffort Effort
wantBudget *int
wantThoughts *bool
}{
{
name: "enabled plus matching effort merges",
explicit: Intent{Mode: ModeEnabled, Effort: EffortHigh},
suffix: Intent{Mode: ModeEnabled, Effort: EffortHigh, Source: SourceSuffix},
wantMode: ModeEnabled,
wantEffort: EffortHigh,
},
{
name: "enabled vs disabled conflict",
explicit: Intent{Mode: ModeEnabled, Effort: EffortHigh},
suffix: Intent{Mode: ModeDisabled, Effort: EffortNone, Source: SourceSuffix},
wantErr: true,
},
{
name: "different efforts conflict",
explicit: Intent{Mode: ModeEnabled, Effort: EffortLow},
suffix: Intent{Mode: ModeEnabled, Effort: EffortHigh, Source: SourceSuffix},
wantErr: true,
},
{
name: "different budgets conflict",
explicit: Intent{BudgetTokens: &budget1024},
suffix: Intent{BudgetTokens: &budget2048, Source: SourceSuffix, BudgetSource: SourceSuffix},
wantErr: true,
},
{
name: "effort versus exact suffix budget conflict",
explicit: Intent{Mode: ModeEnabled, Effort: EffortHigh},
suffix: Intent{BudgetTokens: &budget1024, Source: SourceSuffix, BudgetSource: SourceSuffix},
wantErr: true,
},
{
name: "suffix only is adopted",
suffix: Intent{Mode: ModeEnabled, Effort: EffortMedium, Source: SourceSuffix},
wantMode: ModeEnabled,
wantEffort: EffortMedium,
wantThoughts: nil,
},
{
name: "explicit include thoughts overlays empty suffix strength",
explicit: Intent{IncludeThoughts: boolPtr(false)},
suffix: Intent{Mode: ModeEnabled, Effort: EffortLow, Source: SourceSuffix},
wantMode: ModeEnabled,
wantEffort: EffortLow,
wantThoughts: boolPtr(false),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := MergeExplicitAndSuffix(tt.explicit, tt.suffix, "claude-opus-4-8")
if tt.wantErr {
require.Error(t, err)
assert.ErrorIs(t, err, ErrEffortConflict)
return
}
require.NoError(t, err)
assert.Equal(t, tt.wantMode, got.Mode)
assert.Equal(t, tt.wantEffort, got.Effort)
if tt.wantBudget != nil {
require.NotNil(t, got.BudgetTokens)
assert.Equal(t, *tt.wantBudget, *got.BudgetTokens)
}
if tt.wantThoughts != nil {
require.NotNil(t, got.IncludeThoughts)
assert.Equal(t, *tt.wantThoughts, *got.IncludeThoughts)
}
})
}
}
func TestIntentStateRoundTrip(t *testing.T) {
t.Parallel()
budget := 4096
include := true
intent := Intent{
Mode: ModeEnabled,
Effort: EffortHigh,
BudgetTokens: &budget,
IncludeThoughts: &include,
}
state := StateFromIntent(intent)
require.NotNil(t, state)
got := IntentFromState(state)
assert.Equal(t, intent.Mode, got.Mode)
assert.Equal(t, intent.Effort, got.Effort)
require.NotNil(t, got.BudgetTokens)
assert.Equal(t, budget, *got.BudgetTokens)
require.NotNil(t, got.IncludeThoughts)
assert.True(t, *got.IncludeThoughts)
assert.True(t, IntentFromState(nil).IsEmpty())
assert.Nil(t, StateFromIntent(Intent{}))
}
func boolPtr(v bool) *bool {
return &v
}