* fix: stabilize multimodal image compatibility across OpenCode flows Advertise vision-capable metadata in /v1/models and make model matching deterministic so OpenCode does not downgrade image support or route 4.6 models incorrectly. Expand request translation to accept OpenCode/OpenAI attachment shapes, sanitize [Image N] placeholders safely, keep image-only follow-up turns non-empty, and improve token accounting so base64 image bytes no longer inflate prompt token usage and trigger premature compaction. * fix: deduplicate thinking streams and trim injected prompt noise * fix: align /v1/messages thinking blocks and message_start usage * fix: reduce repetitive thinking across tool turns Select a single reasoning stream source, prevent chunk replay, and preserve structured tool-loop context so the model keeps continuity instead of re-planning each turn. * fix: unify token counting on existing API endpoints Compute usage deterministically on /v1/messages and /v1/chat/completions even when upstream omits tokenUsage. - remove roo-only token path and keep behavior on existing endpoints - add proxy/token_estimator.go with shared Claude/OpenAI estimators (input/system/messages/tools + output/thinking/tool calls) - wire stream/non-stream handlers to use estimator-derived input/output usage - update /v1/messages/count_tokens to reuse the same estimator - keep robust upstream usage parsing/normalization in proxy/kiro.go while dropping parser-level estimate fallback Why: direct upstream tests show metering/context events frequently arrive without tokenUsage in this environment; this made usage zero or inconsistent. Local deterministic accounting keeps reported usage stable and explicit.
38 lines
1008 B
Go
38 lines
1008 B
Go
package proxy
|
|
|
|
import "testing"
|
|
|
|
func TestNormalizeChunkBasicProgression(t *testing.T) {
|
|
prev := ""
|
|
|
|
if got := normalizeChunk("abc", &prev); got != "abc" {
|
|
t.Fatalf("expected first chunk to pass through, got %q", got)
|
|
}
|
|
if got := normalizeChunk("abcde", &prev); got != "de" {
|
|
t.Fatalf("expected appended delta, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeChunkPrefixRewindDoesNotReplay(t *testing.T) {
|
|
prev := ""
|
|
|
|
_ = normalizeChunk("abcde", &prev)
|
|
if got := normalizeChunk("abc", &prev); got != "" {
|
|
t.Fatalf("expected rewind chunk to be ignored, got %q", got)
|
|
}
|
|
if prev != "abcde" {
|
|
t.Fatalf("expected previous snapshot to remain longest version, got %q", prev)
|
|
}
|
|
if got := normalizeChunk("abcdef", &prev); got != "f" {
|
|
t.Fatalf("expected only unseen suffix after rewind, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeChunkOverlapDelta(t *testing.T) {
|
|
prev := "hello world"
|
|
|
|
if got := normalizeChunk("world!!!", &prev); got != "!!!" {
|
|
t.Fatalf("expected overlap suffix delta, got %q", got)
|
|
}
|
|
}
|