Remove background_context & simplify

This commit is contained in:
Yiorgis Gozadinos 2026-01-23 17:27:54 +02:00
parent 9d71ccb213
commit 5aabae4d06
No known key found for this signature in database
19 changed files with 31 additions and 554 deletions

View file

@ -82,7 +82,6 @@ async def stream_chat(request: Request) -> Response:
# Restore session state from incoming AG-UI state (look under namespaced key)
initial_qa_history: list[QAResponse] = []
background_context: str | None = None
session_id: str | None = None
state = getattr(run_input, "state", None)
if state:
@ -91,7 +90,6 @@ async def stream_chat(request: Request) -> Response:
initial_qa_history = [
QAResponse(**qa) for qa in chat_state.get("qa_history", [])
]
background_context = chat_state.get("background_context")
session_id = chat_state.get("session_id")
# Determine session_id: prefer state, fall back to thread_id, generate UUID if neither
@ -105,8 +103,6 @@ async def stream_chat(request: Request) -> Response:
session_state=ChatSessionState(
session_id=session_id,
qa_history=initial_qa_history,
background_context=background_context,
# session_context intentionally NOT set - agent will fetch from cache
),
state_key=AGUI_STATE_KEY,
)

View file

@ -7,12 +7,11 @@ import {
useCopilotAction,
} from "@copilotkit/react-core";
import { CopilotChat } from "@copilotkit/react-ui";
import { useCallback, useEffect, useState } from "react";
import { useState } from "react";
import "@copilotkit/react-ui/styles.css";
import CitationBlock from "./CitationBlock";
import ContextPanel from "./ContextPanel";
import DbInfo from "./DbInfo";
import SettingsPanel, { STORAGE_KEY } from "./SettingsPanel";
// Must match AGUI_STATE_KEY from haiku.rag.agents.chat
const AGUI_STATE_KEY = "haiku.rag.chat";
@ -44,7 +43,6 @@ interface ChatSessionState {
session_id: string;
citations: Citation[];
qa_history: QAResponse[];
background_context: string | null;
session_context: SessionContext | null;
}
@ -141,24 +139,6 @@ function FileIcon() {
);
}
function SettingsIcon() {
return (
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
<circle cx="12" cy="12" r="3" />
</svg>
);
}
function BrainIcon() {
return (
<svg
@ -389,28 +369,9 @@ function ToolCallIndicator({
);
}
function ChatContentInner({
backgroundContext,
setBackgroundContext,
}: {
backgroundContext: string;
setBackgroundContext: (value: string) => void;
}) {
const [settingsOpen, setSettingsOpen] = useState(false);
function ChatContentInner() {
const [contextOpen, setContextOpen] = useState(false);
const handleSaveContext = useCallback(
(value: string) => {
setBackgroundContext(value);
if (value) {
localStorage.setItem(STORAGE_KEY, value);
} else {
localStorage.removeItem(STORAGE_KEY);
}
},
[setBackgroundContext],
);
const { state: agentState } = useCoAgent<AgentState>({
name: "chat_agent",
initialState: {
@ -418,7 +379,6 @@ function ChatContentInner({
session_id: "",
citations: [],
qa_history: [],
background_context: backgroundContext || null,
session_context: null,
},
},
@ -566,19 +526,6 @@ function ChatContentInner({
<BrainIcon />
Memory
</button>
<button
type="button"
className={`header-btn ${backgroundContext ? "has-content" : ""}`}
onClick={() => setSettingsOpen(true)}
title={
backgroundContext
? "Background context is set"
: "Set background context"
}
>
<SettingsIcon />
Settings
</button>
</div>
<div className="chat-content">
<CopilotChat
@ -592,12 +539,6 @@ function ChatContentInner({
<DbInfo />
</div>
</div>
<SettingsPanel
isOpen={settingsOpen}
onClose={() => setSettingsOpen(false)}
onSave={handleSaveContext}
currentValue={backgroundContext}
/>
<ContextPanel
isOpen={contextOpen}
onClose={() => setContextOpen(false)}
@ -608,25 +549,7 @@ function ChatContentInner({
}
function ChatContent() {
const [backgroundContext, setBackgroundContext] = useState<string | null>(
null,
);
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
setBackgroundContext(stored || "");
}, []);
if (backgroundContext === null) {
return null;
}
return (
<ChatContentInner
backgroundContext={backgroundContext}
setBackgroundContext={setBackgroundContext}
/>
);
return <ChatContentInner />;
}
export default function Chat() {

View file

@ -1,188 +0,0 @@
"use client";
import { useCallback, useEffect, useId, useState } from "react";
const STORAGE_KEY = "haiku.rag.settings.background_context";
interface SettingsPanelProps {
isOpen: boolean;
onClose: () => void;
onSave: (backgroundContext: string) => void;
currentValue: string;
}
export default function SettingsPanel({
isOpen,
onClose,
onSave,
currentValue,
}: SettingsPanelProps) {
const [value, setValue] = useState(currentValue);
const titleId = useId();
useEffect(() => {
if (isOpen) {
setValue(currentValue);
}
}, [isOpen, currentValue]);
const handleSave = useCallback(() => {
onSave(value);
onClose();
}, [value, onSave, onClose]);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Escape") {
onClose();
}
},
[onClose],
);
if (!isOpen) {
return null;
}
return (
<>
<style>{`
.settings-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.settings-modal {
background: white;
border-radius: 12px;
padding: 1.5rem;
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow: auto;
position: relative;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.settings-modal-title {
margin: 0 0 0.5rem 0;
font-size: 1.25rem;
font-weight: 600;
color: #1e293b;
}
.settings-modal-description {
margin: 0 0 1rem 0;
font-size: 0.875rem;
color: #64748b;
line-height: 1.5;
}
.settings-textarea {
width: 100%;
min-height: 150px;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 0.875rem;
font-family: inherit;
resize: vertical;
color: #334155;
line-height: 1.5;
}
.settings-textarea:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.settings-textarea::placeholder {
color: #94a3b8;
}
.settings-modal-actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
margin-top: 1.25rem;
}
.settings-btn {
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
}
.settings-btn-cancel {
background: white;
color: #475569;
border: 1px solid #e2e8f0;
}
.settings-btn-cancel:hover {
background: #f8fafc;
border-color: #cbd5e1;
}
.settings-btn-save {
background: #3b82f6;
color: white;
border: none;
}
.settings-btn-save:hover {
background: #2563eb;
}
`}</style>
<div
className="settings-modal-overlay"
onClick={onClose}
onKeyDown={handleKeyDown}
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
>
{/* biome-ignore lint/a11y/noStaticElementInteractions: modal content wrapper */}
<div
className="settings-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<h2 id={titleId} className="settings-modal-title">
Background Context
</h2>
<p className="settings-modal-description">
Provide background information that will be used throughout your
conversation. This helps the assistant understand domain-specific
context, terminology, or any relevant details about your questions.
</p>
<textarea
className="settings-textarea"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="e.g., Focus on Python programming concepts and best practices..."
autoFocus
/>
<div className="settings-modal-actions">
<button
type="button"
className="settings-btn settings-btn-cancel"
onClick={onClose}
>
Cancel
</button>
<button
type="button"
className="settings-btn settings-btn-save"
onClick={handleSave}
>
Save
</button>
</div>
</div>
</div>
</>
);
}
export { STORAGE_KEY };

View file

@ -103,8 +103,7 @@ The `ChatSessionState` maintains:
- `session_id` — Unique identifier for the session
- `qa_history` — List of previous Q/A pairs (FIFO, max 50)
- `background_context` — Optional background context for the conversation
- `embedding_cache` — Cached embeddings for semantic ranking
- `session_context` — Automatically maintained session context summary
Q/A history is used to:
@ -112,19 +111,6 @@ Q/A history is used to:
2. Avoid repeating previous answers
3. Enable semantic ranking of relevant past answers
### Background Context
You can provide background context that persists throughout the conversation:
```python
session = ChatSessionState(
background_context="Focus on Python programming concepts and best practices."
)
deps = ChatDeps(client=client, config=config, session_state=session)
```
The context is included in the agent's system prompt and passed to the research graph when answering questions.
### AG-UI Integration
When using the chat agent with AG-UI streaming, state is emitted under a namespaced key to avoid conflicts with other agents:
@ -230,18 +216,6 @@ async with HaikuRAG(path_to_db) as client:
print(report.executive_summary)
```
**With background context:**
```python
context = ResearchContext(
original_question="What are the safety protocols?",
background_context="Industrial manufacturing and workplace safety domain."
)
state = ResearchState.from_config(context=context, config=Config)
```
The `background_context` provides domain background that helps the planning and synthesis agents understand the context of the research question.
**With custom config:**
```python

View file

@ -57,13 +57,6 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
retries=3,
)
@agent.system_prompt
async def add_background_context(ctx: RunContext[ChatDeps]) -> str:
"""Add background_context to system prompt when available."""
if ctx.deps.session_state and ctx.deps.session_state.background_context:
return f"\nBACKGROUND CONTEXT:\n{ctx.deps.session_state.background_context}"
return ""
@agent.tool
async def search(
ctx: RunContext[ChatDeps],
@ -117,11 +110,6 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
qa_history=(
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
),
background_context=(
ctx.deps.session_state.background_context
if ctx.deps.session_state
else None
),
session_context=get_cached_session_context(session_id)
if session_id
else None,
@ -177,27 +165,19 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
# Build and run the conversational research graph
graph = build_conversational_graph(config=ctx.deps.config)
# Determine context strategy:
# 1. Read from server cache (ignoring client state)
# 2. Fall back to explicit background_context (first request)
background_context: str | None = None
session_id = ctx.deps.session_state.session_id if ctx.deps.session_state else ""
if ctx.deps.session_state:
cached_context = (
get_cached_session_context(session_id) if session_id else None
)
if cached_context and cached_context.summary:
# Use cached SessionContext from previous summarization
background_context = cached_context.render_markdown()
elif ctx.deps.session_state.background_context:
# Fall back to explicit background_context (first request)
background_context = ctx.deps.session_state.background_context
# Get session context from server cache for planning
cached_context = get_cached_session_context(session_id) if session_id else None
session_context = (
cached_context.render_markdown()
if cached_context and cached_context.summary
else None
)
context = ResearchContext(
original_question=question,
background_context=background_context,
session_context=session_context,
)
state = ResearchState(
context=context,
@ -264,11 +244,6 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
qa_history=(
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
),
background_context=(
ctx.deps.session_state.background_context
if ctx.deps.session_state
else None
),
session_context=get_cached_session_context(session_id)
if session_id
else None,

View file

@ -53,8 +53,8 @@ async def summarize_session(
Args:
qa_history: List of Q&A pairs from the conversation.
config: AppConfig for model selection.
current_context: Previous context to incorporate (background_context or
previous session_context.summary). The summarizer will build upon this.
current_context: Previous session_context.summary to incorporate.
The summarizer will build upon this.
Returns:
Markdown summary of the conversation history.
@ -89,14 +89,10 @@ async def update_session_context(
config: AppConfig for model selection.
session_state: The session state to update.
"""
# Determine current context to incorporate:
# 1. If session_context already exists, use its summary
# 2. Otherwise, use background_context (if available)
# Use existing session_context summary if available
current_context: str | None = None
if session_state.session_context and session_state.session_context.summary:
current_context = session_state.session_context.summary
elif session_state.background_context:
current_context = session_state.background_context
summary = await summarize_session(
qa_history, config, current_context=current_context

View file

@ -47,7 +47,6 @@ class ChatSessionState(BaseModel):
session_id: str = ""
citations: list[Citation] = []
qa_history: list[QAResponse] = []
background_context: str | None = None
session_context: SessionContext | None = None
@ -97,10 +96,6 @@ class ChatDeps:
Citation(**c) if isinstance(c, dict) else c
for c in state_data.get("citations", [])
]
if "background_context" in state_data:
self.session_state.background_context = state_data.get(
"background_context"
)
if state_data.get("session_id"):
self.session_state.session_id = state_data["session_id"]
# NOTE: session_context intentionally NOT updated from client

View file

@ -19,9 +19,9 @@ class ResearchContext(BaseModel):
qa_responses: list[Any] = Field(
default_factory=list, description="Structured QA pairs used during research"
)
background_context: str | None = Field(
session_context: str | None = Field(
default=None,
description="Optional background context provided at session start",
description="Session context from previous Q&A summarization",
)
def add_qa_response(self, qa: "SearchAnswer") -> None:

View file

@ -42,8 +42,8 @@ def format_context_for_prompt(
"""
context_data: dict[str, object] = {}
if context.background_context:
context_data["background"] = context.background_context
if context.session_context:
context_data["background"] = context.session_context
context_data["question"] = context.original_question
@ -78,9 +78,9 @@ async def _plan_step_logic(
"""Shared logic for the plan step."""
model_config = config.research.model
# Use context-aware prompt if we have existing qa_responses
# Use context-aware prompt if we have existing qa_responses or session_context
has_prior_answers = bool(state.context.qa_responses)
has_background = bool(state.context.background_context)
has_session_context = bool(state.context.session_context)
effective_plan_prompt = (
build_prompt(PLAN_PROMPT_WITH_CONTEXT, config)
if has_prior_answers
@ -118,7 +118,7 @@ async def _plan_step_logic(
f"{context_xml}\n\n"
f"Main question: {state.context.original_question}"
)
elif has_background:
elif has_session_context:
context_xml = format_context_for_prompt(state.context)
prompt = (
f"Plan a focused approach for the main question.\n\n"

View file

@ -376,7 +376,6 @@ class HaikuRAGApp:
cite: bool = False,
deep: bool = False,
filter: str | None = None,
background_context: str | None = None,
):
"""Ask a question using the RAG system.
@ -385,7 +384,6 @@ class HaikuRAGApp:
cite: Include citations in the answer
deep: Use deep QA mode (multi-step reasoning)
filter: SQL WHERE clause to filter documents
background_context: Optional background context for the question
"""
async with HaikuRAG(
db_path=self.db_path,
@ -396,9 +394,7 @@ class HaikuRAGApp:
citations = []
if deep:
graph = build_research_graph(config=self.config)
context = ResearchContext(
original_question=question, background_context=background_context
)
context = ResearchContext(original_question=question)
state = ResearchState.from_config(
context=context,
config=self.config,
@ -427,14 +423,7 @@ class HaikuRAGApp:
else:
self.console.print("[yellow]No answer generated.[/yellow]")
else:
system_prompt = (
f"BACKGROUND CONTEXT:\n{background_context}"
if background_context
else None
)
answer, citations = await self.client.ask(
question, system_prompt=system_prompt, filter=filter
)
answer, citations = await self.client.ask(question, filter=filter)
self.console.print(f"[bold blue]Question:[/bold blue] {question}")
self.console.print()
@ -448,14 +437,12 @@ class HaikuRAGApp:
self,
question: str,
filter: str | None = None,
background_context: str | None = None,
):
"""Run research via the pydantic-graph pipeline.
Args:
question: The research question
filter: SQL WHERE clause to filter documents
background_context: Optional background context for the research
"""
async with HaikuRAG(
db_path=self.db_path,
@ -468,9 +455,7 @@ class HaikuRAGApp:
self.console.print()
graph = build_research_graph(config=self.config)
context = ResearchContext(
original_question=question, background_context=background_context
)
context = ResearchContext(original_question=question)
state = ResearchState.from_config(context=context, config=self.config)
state.search_filter = filter
deps = ResearchDeps(client=client)

View file

@ -6,7 +6,6 @@ def run_chat(
db_path: Path | None = None,
read_only: bool = False,
before: datetime | None = None,
background_context: str | None = None,
) -> None:
"""Run the chat TUI.
@ -14,7 +13,6 @@ def run_chat(
db_path: Path to the LanceDB database. If None, uses default from config.
read_only: Whether to open the database in read-only mode.
before: Query database as it existed before this datetime.
background_context: Optional background context for the conversation.
"""
try:
from haiku.rag.chat.app import ChatApp
@ -33,6 +31,5 @@ def run_chat(
db_path,
read_only=read_only,
before=before,
background_context=background_context,
)
app.run()

View file

@ -21,7 +21,6 @@ from haiku.rag.agents.chat.state import (
AGUI_STATE_KEY,
ChatDeps,
ChatSessionState,
SessionContext,
)
from haiku.rag.agents.research.models import Citation
from haiku.rag.client import HaikuRAG
@ -92,13 +91,11 @@ class ChatApp(App):
db_path: Path,
read_only: bool = False,
before: datetime | None = None,
background_context: str | None = None,
) -> None:
super().__init__()
self.db_path = db_path
self.read_only = read_only
self.before = before
self.background_context = background_context
self.client: HaikuRAG | None = None
self.config = get_config()
self.agent: Agent[ChatDeps, str] | None = None
@ -129,15 +126,8 @@ class ChatApp(App):
# Create agent and session state
self.agent = create_chat_agent(self.config)
initial_context = (
SessionContext(summary=self.background_context, last_updated=datetime.now())
if self.background_context
else None
)
self.session_state = ChatSessionState(
session_id=str(uuid.uuid4()),
background_context=self.background_context,
session_context=initial_context,
)
# Focus the input field
@ -283,16 +273,9 @@ class ChatApp(App):
self._last_citations.clear()
self._selected_citation_idx = None
self._message_history.clear()
# Reset session state for fresh conversation (preserve background_context)
initial_context = (
SessionContext(summary=self.background_context, last_updated=datetime.now())
if self.background_context
else None
)
# Reset session state for fresh conversation
self.session_state = ChatSessionState(
session_id=str(uuid.uuid4()),
background_context=self.background_context,
session_context=initial_context,
)
def action_focus_input(self) -> None:

View file

@ -352,24 +352,7 @@ def ask(
"-f",
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
),
context: str | None = typer.Option(
None,
"--context",
help="Background context for the question",
),
context_file: Path | None = typer.Option(
None,
"--context-file",
help="Path to a file containing background context",
),
):
# Resolve initial context from flag or file
background_context: str | None = None
if context_file:
background_context = context_file.read_text()
elif context:
background_context = context
app = create_app(db)
asyncio.run(
app.ask(
@ -377,7 +360,6 @@ def ask(
cite=cite,
deep=deep,
filter=filter,
background_context=background_context,
)
)
@ -396,30 +378,9 @@ def research(
"-f",
help="SQL WHERE clause to filter documents (e.g., \"uri LIKE '%arxiv%'\")",
),
context: str | None = typer.Option(
None,
"--context",
help="Background context for the research",
),
context_file: Path | None = typer.Option(
None,
"--context-file",
help="Path to a file containing background context",
),
):
# Resolve initial context from flag or file
background_context: str | None = None
if context_file:
background_context = context_file.read_text()
elif context:
background_context = context
app = create_app(db)
asyncio.run(
app.research(
question=question, filter=filter, background_context=background_context
)
)
asyncio.run(app.research(question=question, filter=filter))
@_cli.command("settings", help="Display current configuration settings")
@ -632,34 +593,16 @@ def chat(
"--db",
help="Path to the LanceDB database file",
),
context: str | None = typer.Option(
None,
"--context",
help="Initial context/background information for the conversation",
),
context_file: Path | None = typer.Option(
None,
"--context-file",
help="Path to a file containing initial context",
),
):
"""Launch the chat TUI for conversational RAG."""
from haiku.rag.chat import run_chat
db_path = db if db else get_config().storage.data_dir / "haiku.rag.lancedb"
# Resolve initial context from flag or file
background_context: str | None = None
if context_file:
background_context = context_file.read_text()
elif context:
background_context = context
run_chat(
db_path,
read_only=_read_only,
before=_before,
background_context=background_context,
)

View file

@ -76,18 +76,6 @@ def test_chat_session_state():
assert state.qa_history == []
def test_chat_agent_has_dynamic_system_prompt():
"""Test that chat agent registers a dynamic system prompt for background_context."""
agent = create_chat_agent(Config)
# The agent should have at least one system prompt function registered
# (the add_background_context function)
system_prompt_functions = getattr(agent, "_system_prompt_functions")
assert len(system_prompt_functions) >= 1
# Verify it's the add_background_context function
func_names = [r.function.__name__ for r in system_prompt_functions]
assert "add_background_context" in func_names
def test_citation():
"""Test Citation model."""
citation = Citation(

View file

@ -186,7 +186,7 @@ class TestSummarizeSession:
)
]
# Provide current_context (e.g., background_context or previous summary)
# Provide current_context (e.g., previous summary)
current_context = "Focus on Python APIs. User is building a web application."
result = await summarize_session(
@ -362,6 +362,7 @@ class TestSessionContextCache:
cached = get_cached_session_context("cache-test-session")
assert cached is not None
assert cached.summary == "Mocked summary"
assert session_state.session_context is not None
assert cached.summary == session_state.session_context.summary
@pytest.mark.asyncio

View file

@ -34,25 +34,6 @@ def test_max_qa_history_constant():
assert MAX_QA_HISTORY == 50
def test_chat_session_state_background_context():
"""Test ChatSessionState accepts background_context."""
from haiku.rag.agents.chat.state import ChatSessionState
state = ChatSessionState(
session_id="test-session",
background_context="This is background knowledge about the topic.",
)
assert state.background_context == "This is background knowledge about the topic."
def test_chat_session_state_background_context_defaults_to_none():
"""Test ChatSessionState background_context defaults to None."""
from haiku.rag.agents.chat.state import ChatSessionState
state = ChatSessionState(session_id="test-session")
assert state.background_context is None
def test_chat_deps_state_getter_returns_namespaced_state():
"""Test ChatDeps.state getter returns state under namespaced key."""
from unittest.mock import MagicMock
@ -67,7 +48,6 @@ def test_chat_deps_state_getter_returns_namespaced_state():
qa_history=[
QAResponse(question="Q1", answer="A1", confidence=0.9),
],
background_context="Background info",
)
deps = ChatDeps(
@ -83,7 +63,6 @@ def test_chat_deps_state_getter_returns_namespaced_state():
assert state[AGUI_STATE_KEY]["session_id"] == "test-123"
assert len(state[AGUI_STATE_KEY]["qa_history"]) == 1
assert state[AGUI_STATE_KEY]["qa_history"][0]["question"] == "Q1"
assert state[AGUI_STATE_KEY]["background_context"] == "Background info"
def test_chat_deps_state_getter_without_namespace():
@ -152,7 +131,6 @@ def test_chat_deps_state_setter_updates_from_namespaced_state():
{"question": "Q1", "answer": "A1", "confidence": 0.9, "citations": []}
],
"citations": [],
"background_context": "New context",
}
}
@ -162,7 +140,6 @@ def test_chat_deps_state_setter_updates_from_namespaced_state():
assert deps.session_state.session_id == "updated-123"
assert len(deps.session_state.qa_history) == 1
assert deps.session_state.qa_history[0].question == "Q1"
assert deps.session_state.background_context == "New context"
def test_chat_deps_state_setter_handles_none():
@ -242,7 +219,6 @@ def test_chat_deps_state_setter_with_citation_dicts():
"content": "Test content",
}
],
"background_context": None,
}
}

View file

@ -46,65 +46,6 @@ async def test_graph_end_to_end(allow_model_requests, temp_db_path, qa_corpus):
client.close()
def test_research_context_background_context():
"""Test ResearchContext accepts background_context."""
context = ResearchContext(
original_question="What is X?",
background_context="Background: X is a concept in domain Y.",
)
assert context.background_context == "Background: X is a concept in domain Y."
def test_research_context_background_context_defaults_to_none():
"""Test ResearchContext background_context defaults to None."""
context = ResearchContext(original_question="What is X?")
assert context.background_context is None
def test_format_context_for_prompt_includes_background():
"""Test format_context_for_prompt includes background in output."""
from haiku.rag.agents.research.graph import format_context_for_prompt
context = ResearchContext(
original_question="What is X?",
background_context="X is a concept in domain Y.",
)
result = format_context_for_prompt(context)
assert "X is a concept in domain Y." in result
assert "<background>" in result
def test_format_context_for_prompt_excludes_background_when_none():
"""Test format_context_for_prompt excludes background when None."""
from haiku.rag.agents.research.graph import format_context_for_prompt
context = ResearchContext(original_question="What is X?")
result = format_context_for_prompt(context)
assert "<background>" not in result
def test_format_context_for_prompt_without_pending_includes_background():
"""Test format_context_for_prompt with include_pending_questions=False includes background."""
from haiku.rag.agents.research.graph import format_context_for_prompt
context = ResearchContext(
original_question="What is X?",
background_context="X is a concept in domain Y.",
)
result = format_context_for_prompt(context, include_pending_questions=False)
assert "X is a concept in domain Y." in result
assert "<background>" in result
def test_format_context_for_prompt_without_pending_excludes_background_when_none():
"""Test format_context_for_prompt with include_pending_questions=False excludes background when None."""
from haiku.rag.agents.research.graph import format_context_for_prompt
context = ResearchContext(original_question="What is X?")
result = format_context_for_prompt(context, include_pending_questions=False)
assert "<background>" not in result
def test_research_plan_allows_empty_sub_questions():
"""Test ResearchPlan accepts empty sub_questions when context is sufficient."""
from haiku.rag.agents.research.models import ResearchPlan

View file

@ -304,9 +304,7 @@ async def test_ask_without_cite(app: HaikuRAGApp, monkeypatch):
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
await app.ask("test question")
mock_client.ask.assert_called_once_with(
"test question", system_prompt=None, filter=None
)
mock_client.ask.assert_called_once_with("test question", filter=None)
@pytest.mark.asyncio
@ -335,9 +333,7 @@ async def test_ask_with_cite(app: HaikuRAGApp, monkeypatch):
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
await app.ask("test question", cite=True)
mock_client.ask.assert_called_once_with(
"test question", system_prompt=None, filter=None
)
mock_client.ask.assert_called_once_with("test question", filter=None)
# Verify print was called (once for answer, once for citations)
assert mock_print.call_count >= 1

View file

@ -285,7 +285,6 @@ def test_ask():
cite=False,
deep=False,
filter=None,
background_context=None,
)
@ -303,7 +302,6 @@ def test_ask_with_cite():
cite=True,
deep=False,
filter=None,
background_context=None,
)
@ -321,7 +319,6 @@ def test_ask_with_deep():
cite=False,
deep=True,
filter=None,
background_context=None,
)
@ -339,7 +336,6 @@ def test_ask_with_deep_and_cite():
cite=True,
deep=True,
filter=None,
background_context=None,
)