A deeper-dive chat entered from the summary view: ask questions about a video against its already-stored transcript (ADR-021), no caption fetch, ever. Safety by construction — the load-bearing property. The chat handlers reach the chat.Service only after reading the SHARED stored transcript via Store.GetTranscript (a pure DB read); the service holds no VideoSource. So an enabled chat cannot trigger a caption fetch, touch the rate gate, or reach YouTube. A video with no stored transcript gets an honest "not available" — no fetch, no model call. The key web test wires the summarize/fetch collaborators as tripwires that fail the test if chat ever routes into them, and asserts the model answered from the stored text. Model defaults to the summary's own model and is switchable among the ADR-022 chain (phi4-mini → gemma4-26b → mistral-small); switching re-runs against the same transcript — deliberate model-comparison instrumentation. The cloud model is absent from the switcher when TAPIR_CLOUD_FALLBACK_MODEL="" (the local-first / NDA lever), honoured the same way the summarizer honours it. Reuses the existing LiteLLM gateway client (a chat is a different call, not a new integration) and the TAPIR_MAX_TRANSCRIPT_CHARS truncation, surfacing an honest bounded-context note when a long transcript is cut. Ephemeral v1: the multi-turn conversation rides in hidden request fields; no table, no migration, nothing persisted. Entry is RLS-scoped through GetSummaryByVideo, so chat is reachable only from the user's own summary view. Show-source verification and on-demand fetch are deferred (ADR-027). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
2.2 KiB
Go
65 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.d-ma.be/mathias/tapir/internal/config"
|
|
)
|
|
|
|
// TestChatModelsReuseTheChainLocalFirst: the switcher offers the ADR-022 chain in
|
|
// order, deduped — primary, local fallback, cloud.
|
|
func TestChatModelsReuseTheChainLocalFirst(t *testing.T) {
|
|
got := chatModels(config.Config{
|
|
SummarizerModel: "koala/phi4-mini",
|
|
FallbackModel: "iguana/gemma4-26b",
|
|
CloudFallbackModel: "berget/mistral-small",
|
|
})
|
|
want := []string{"koala/phi4-mini", "iguana/gemma4-26b", "berget/mistral-small"}
|
|
if strings.Join(got, ",") != strings.Join(want, ",") {
|
|
t.Fatalf("chatModels = %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
// TestChatCloudModelAbsentWhenDisabled: the local-first / NDA lever — with the
|
|
// cloud fallback empty (TAPIR_CLOUD_FALLBACK_MODEL=""), no external model is
|
|
// offered in the switcher, so chat content never leaves the local stack (ADR-027,
|
|
// honouring ADR-022's "content stays local" guarantee).
|
|
func TestChatCloudModelAbsentWhenDisabled(t *testing.T) {
|
|
got := chatModels(config.Config{
|
|
SummarizerModel: "koala/phi4-mini",
|
|
FallbackModel: "iguana/gemma4-26b",
|
|
CloudFallbackModel: "",
|
|
})
|
|
for _, m := range got {
|
|
if strings.HasPrefix(m, "berget/") || strings.Contains(m, "mistral") {
|
|
t.Fatalf("cloud model %q offered though the cloud fallback is disabled", m)
|
|
}
|
|
}
|
|
want := []string{"koala/phi4-mini", "iguana/gemma4-26b"}
|
|
if strings.Join(got, ",") != strings.Join(want, ",") {
|
|
t.Fatalf("chatModels = %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
// TestChatModelsDedup: a config that reuses one alias across slots collapses to a
|
|
// single switcher entry (no duplicate options).
|
|
func TestChatModelsDedup(t *testing.T) {
|
|
got := chatModels(config.Config{
|
|
SummarizerModel: "koala/phi4-mini",
|
|
FallbackModel: "koala/phi4-mini",
|
|
CloudFallbackModel: "",
|
|
})
|
|
if len(got) != 1 || got[0] != "koala/phi4-mini" {
|
|
t.Fatalf("chatModels = %v, want a single deduped entry", got)
|
|
}
|
|
}
|
|
|
|
// TestBuildChatNilWithoutGateway: no gateway → no chat backend (routes unmounted,
|
|
// read path unaffected).
|
|
func TestBuildChatNilWithoutGateway(t *testing.T) {
|
|
if c := buildChat(config.Config{GatewayURL: ""}); c != nil {
|
|
t.Fatal("buildChat must return nil without a gateway URL")
|
|
}
|
|
}
|