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>
64 lines
2.7 KiB
Gherkin
64 lines
2.7 KiB
Gherkin
Feature: Chat with a video's stored transcript
|
|
As a reader whose summary made me want to dig deeper
|
|
I want to ask questions about the video without watching it
|
|
So that I can go further on the ones worth it, without leaving the reader
|
|
|
|
# ADR-027. The load-bearing constraint is safety-by-construction: chat runs
|
|
# ONLY against an already-stored transcript (ADR-021) and never fetches captions,
|
|
# never touches the rate gate, never reaches YouTube. Entry is from the summary
|
|
# view of one's OWN video; the conversation is ephemeral (no persisted history).
|
|
|
|
Background:
|
|
Given I have a summarized video with a stored transcript
|
|
|
|
Scenario: A summary view offers a deeper-dive into the video
|
|
When I view the summary
|
|
Then I see a "dig deeper" affordance that opens a chat about this video
|
|
|
|
Scenario: Ask a question answered from the stored transcript
|
|
When I ask a question in the chat
|
|
Then the answer is produced from the stored transcript
|
|
And no caption fetch and no YouTube call occurs
|
|
|
|
Scenario: Chat never fetches captions or reaches YouTube
|
|
When I ask a question in the chat
|
|
Then Tapir reads only the stored transcript
|
|
And the caption-fetch and video-fetch paths are never invoked
|
|
|
|
Scenario: A video with no stored transcript offers no chat
|
|
Given a video that has no stored transcript
|
|
When I open the chat for it
|
|
Then I am told chat is not available
|
|
And no fetch is attempted and no model is called
|
|
|
|
Scenario: The default model is the summary's model and is switchable
|
|
When I open the chat
|
|
Then the model defaults to the model that produced the summary
|
|
And I can switch among the offered chain models
|
|
|
|
Scenario: Switching models re-runs against the same transcript
|
|
When I ask a question with a different chain model selected
|
|
Then the chosen model answers
|
|
And it answers against the same stored transcript
|
|
|
|
Scenario: The cloud model is hidden when cloud is disabled
|
|
Given the cloud fallback model is disabled
|
|
Then the chat switcher offers only local models
|
|
|
|
Scenario: A long transcript is bounded and the chat says so
|
|
Given the stored transcript is longer than the model budget
|
|
When I ask a question
|
|
Then the answer is produced from a bounded portion
|
|
And the chat notes that it worked from a bounded portion
|
|
|
|
Scenario: A multi-turn conversation is ephemeral
|
|
When I ask a follow-up question
|
|
Then the prior turn is carried into the answer
|
|
And nothing about the conversation is written to the database
|
|
|
|
Scenario: Chat is reachable only from my own summary view
|
|
Given another user has a summarized video with a stored transcript
|
|
When I try to open the chat for their video
|
|
Then I get a not-found response
|
|
And no model is called
|