Remove agents/chat/
This commit is contained in:
parent
ed89ff0fc9
commit
8176103eb0
29 changed files with 0 additions and 18458 deletions
|
|
@ -1,39 +0,0 @@
|
|||
from haiku.rag.agents.chat.agent import (
|
||||
DEFAULT_FEATURES,
|
||||
FEATURE_ANALYSIS,
|
||||
FEATURE_DOCUMENTS,
|
||||
FEATURE_QA,
|
||||
FEATURE_SEARCH,
|
||||
ChatDeps,
|
||||
build_chat_toolkit,
|
||||
create_chat_agent,
|
||||
prepare_chat_context,
|
||||
run_chat_agent,
|
||||
trigger_background_summarization,
|
||||
)
|
||||
from haiku.rag.agents.chat.prompts import build_chat_prompt
|
||||
from haiku.rag.agents.chat.state import (
|
||||
AGUI_STATE_KEY,
|
||||
ChatSessionState,
|
||||
_rebuild_models,
|
||||
)
|
||||
from haiku.rag.tools.qa import QAHistoryEntry
|
||||
|
||||
_rebuild_models(QAHistoryEntry)
|
||||
|
||||
__all__ = [
|
||||
"AGUI_STATE_KEY",
|
||||
"DEFAULT_FEATURES",
|
||||
"FEATURE_ANALYSIS",
|
||||
"FEATURE_DOCUMENTS",
|
||||
"FEATURE_QA",
|
||||
"FEATURE_SEARCH",
|
||||
"build_chat_prompt",
|
||||
"build_chat_toolkit",
|
||||
"create_chat_agent",
|
||||
"prepare_chat_context",
|
||||
"run_chat_agent",
|
||||
"trigger_background_summarization",
|
||||
"ChatDeps",
|
||||
"ChatSessionState",
|
||||
]
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
from pydantic_ai import Agent
|
||||
|
||||
from haiku.rag.agents.chat.context import (
|
||||
trigger_background_summarization as _trigger_summarization,
|
||||
)
|
||||
from haiku.rag.agents.chat.prompts import build_chat_prompt
|
||||
from haiku.rag.agents.chat.state import AGUI_STATE_KEY
|
||||
from haiku.rag.config.models import AppConfig
|
||||
from haiku.rag.tools.context import ToolContext
|
||||
from haiku.rag.tools.deps import AgentDeps
|
||||
from haiku.rag.tools.qa import QA_SESSION_NAMESPACE, QASessionState
|
||||
from haiku.rag.tools.session import SessionContext
|
||||
from haiku.rag.tools.toolkit import (
|
||||
FEATURE_ANALYSIS,
|
||||
FEATURE_DOCUMENTS,
|
||||
FEATURE_QA,
|
||||
FEATURE_SEARCH,
|
||||
Toolkit,
|
||||
build_toolkit,
|
||||
)
|
||||
from haiku.rag.utils import get_model
|
||||
|
||||
DEFAULT_FEATURES = [FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA]
|
||||
|
||||
|
||||
def _on_qa_complete(qa_session_state: QASessionState, config: AppConfig) -> None:
|
||||
_trigger_summarization(qa_session_state=qa_session_state, config=config)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChatDeps(AgentDeps):
|
||||
"""Dependencies for chat agent.
|
||||
|
||||
Extends AgentDeps with chat-specific config and state handling.
|
||||
"""
|
||||
|
||||
config: AppConfig = field(default_factory=AppConfig)
|
||||
|
||||
@AgentDeps.state.setter
|
||||
def state(self, value: dict[str, Any] | None) -> None:
|
||||
"""Set state from AG-UI protocol with chat-specific overrides."""
|
||||
if value is None:
|
||||
return
|
||||
|
||||
state_data = self._extract_state_data(value)
|
||||
|
||||
# Preserve server's session_context before restore overwrites it
|
||||
qa_session_state = self.tool_context.get(QA_SESSION_NAMESPACE, QASessionState)
|
||||
server_session_context = (
|
||||
qa_session_state.session_context if qa_session_state is not None else None
|
||||
)
|
||||
|
||||
self.tool_context.restore_state_snapshot(state_data)
|
||||
|
||||
# Chat-specific overrides after generic restore
|
||||
if qa_session_state is not None:
|
||||
# Prefer server's session_context (background summarizer may
|
||||
# have updated it since the client's last snapshot).
|
||||
if server_session_context is not None:
|
||||
qa_session_state.session_context = server_session_context
|
||||
|
||||
# Handle initial_context -> session_context for first message
|
||||
if qa_session_state.session_context is None:
|
||||
if "initial_context" in state_data:
|
||||
initial = state_data.get("initial_context")
|
||||
if initial:
|
||||
qa_session_state.session_context = SessionContext(
|
||||
summary=initial
|
||||
)
|
||||
|
||||
|
||||
def build_chat_toolkit(
|
||||
config: AppConfig,
|
||||
features: list[str] | None = None,
|
||||
) -> Toolkit:
|
||||
"""Build a Toolkit configured for the chat agent.
|
||||
|
||||
Includes the on_qa_complete callback that triggers background
|
||||
session summarization.
|
||||
|
||||
Args:
|
||||
config: Application configuration.
|
||||
features: List of features to enable. Defaults to DEFAULT_FEATURES.
|
||||
|
||||
Returns:
|
||||
A Toolkit ready for chat agent composition and context creation.
|
||||
"""
|
||||
if features is None:
|
||||
features = DEFAULT_FEATURES
|
||||
|
||||
return build_toolkit(config, features=features, on_qa_complete=_on_qa_complete)
|
||||
|
||||
|
||||
def prepare_chat_context(
|
||||
context: ToolContext,
|
||||
features: list[str] | None = None,
|
||||
) -> None:
|
||||
"""Register required namespaces in a ToolContext for chat agent use.
|
||||
|
||||
Idempotent — safe to call multiple times on the same context.
|
||||
|
||||
Args:
|
||||
context: ToolContext to prepare.
|
||||
features: List of enabled features. Defaults to DEFAULT_FEATURES.
|
||||
"""
|
||||
from haiku.rag.tools.context import prepare_context
|
||||
|
||||
if features is None:
|
||||
features = DEFAULT_FEATURES
|
||||
|
||||
prepare_context(context, features=features, state_key=AGUI_STATE_KEY)
|
||||
|
||||
|
||||
def create_chat_agent(
|
||||
config: AppConfig,
|
||||
features: list[str] | None = None,
|
||||
preamble: str | None = None,
|
||||
toolkit: Toolkit | None = None,
|
||||
) -> Agent[ChatDeps, str]:
|
||||
"""Create the chat agent with composed toolsets.
|
||||
|
||||
Args:
|
||||
config: Application configuration.
|
||||
features: List of features to enable. Defaults to DEFAULT_FEATURES
|
||||
(search, documents, qa). Available features: "search",
|
||||
"documents", "qa", "analysis".
|
||||
preamble: Optional custom identity/rules section for the system prompt.
|
||||
When provided, replaces the default identity prompt. Tool guidance,
|
||||
feature rules, and closing are still appended by the builder.
|
||||
toolkit: Optional pre-built Toolkit. When provided, its toolsets are
|
||||
used directly. When omitted, a toolkit is built from config and
|
||||
features.
|
||||
|
||||
Returns:
|
||||
The configured chat agent.
|
||||
|
||||
Example:
|
||||
async with HaikuRAG(db_path, create=True) as client:
|
||||
toolkit = build_chat_toolkit(config)
|
||||
context = toolkit.create_context(state_key=AGUI_STATE_KEY)
|
||||
agent = create_chat_agent(config, toolkit=toolkit)
|
||||
deps = ChatDeps(config=config, client=client, tool_context=context)
|
||||
result = await agent.run("Search for X", deps=deps)
|
||||
"""
|
||||
if features is None:
|
||||
features = DEFAULT_FEATURES
|
||||
|
||||
if toolkit is None:
|
||||
toolkit = build_chat_toolkit(config, features=features)
|
||||
|
||||
model = get_model(config.qa.model, config)
|
||||
|
||||
return Agent(
|
||||
model,
|
||||
deps_type=ChatDeps,
|
||||
output_type=str,
|
||||
instructions=build_chat_prompt(features, preamble=preamble),
|
||||
toolsets=toolkit.toolsets,
|
||||
retries=3,
|
||||
)
|
||||
|
||||
|
||||
def trigger_background_summarization(deps: ChatDeps) -> None:
|
||||
"""Trigger background session summarization if qa_history has entries.
|
||||
|
||||
Call this after agent.run() or agent.run_stream() completes to update
|
||||
the session context summary in the background.
|
||||
|
||||
Args:
|
||||
deps: Chat dependencies with tool_context containing QASessionState.
|
||||
"""
|
||||
qa_session_state = deps.tool_context.get(QA_SESSION_NAMESPACE, QASessionState)
|
||||
if qa_session_state is None or not qa_session_state.qa_history:
|
||||
return
|
||||
|
||||
_trigger_summarization(
|
||||
qa_session_state=qa_session_state,
|
||||
config=deps.config,
|
||||
)
|
||||
|
||||
|
||||
async def run_chat_agent(
|
||||
agent: Agent[ChatDeps, str],
|
||||
deps: ChatDeps,
|
||||
message: str,
|
||||
) -> str:
|
||||
"""Run the chat agent.
|
||||
|
||||
Args:
|
||||
agent: The chat agent.
|
||||
deps: Chat dependencies.
|
||||
message: User message.
|
||||
|
||||
Returns:
|
||||
Agent response.
|
||||
"""
|
||||
result = await agent.run(message, deps=deps)
|
||||
return result.output
|
||||
|
||||
|
||||
__all__ = [
|
||||
"build_chat_toolkit",
|
||||
"create_chat_agent",
|
||||
"prepare_chat_context",
|
||||
"run_chat_agent",
|
||||
"trigger_background_summarization",
|
||||
"ChatDeps",
|
||||
"AGUI_STATE_KEY",
|
||||
"FEATURE_SEARCH",
|
||||
"FEATURE_DOCUMENTS",
|
||||
"FEATURE_QA",
|
||||
"FEATURE_ANALYSIS",
|
||||
"DEFAULT_FEATURES",
|
||||
]
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic_ai import Agent
|
||||
|
||||
from haiku.rag.agents.chat.prompts import SESSION_SUMMARY_PROMPT
|
||||
from haiku.rag.config.models import AppConfig
|
||||
from haiku.rag.tools.session import SessionContext
|
||||
from haiku.rag.utils import get_model
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from haiku.rag.tools.qa import QAHistoryEntry, QASessionState
|
||||
|
||||
|
||||
# Track summarization tasks to allow cancellation
|
||||
_summarization_tasks: dict[int, asyncio.Task[None]] = {}
|
||||
|
||||
|
||||
async def summarize_session(
|
||||
qa_history: list["QAHistoryEntry"],
|
||||
config: AppConfig,
|
||||
current_context: str | None = None,
|
||||
) -> str:
|
||||
"""Summarize qa_history into compact context.
|
||||
|
||||
Args:
|
||||
qa_history: List of Q&A pairs from the conversation.
|
||||
config: AppConfig for model selection.
|
||||
current_context: Previous session_context.summary to incorporate.
|
||||
The summarizer will build upon this.
|
||||
|
||||
Returns:
|
||||
Markdown summary of the conversation history.
|
||||
"""
|
||||
if not qa_history:
|
||||
return ""
|
||||
|
||||
model = get_model(config.qa.model, config)
|
||||
agent: Agent[None, str] = Agent(
|
||||
model,
|
||||
output_type=str,
|
||||
instructions=SESSION_SUMMARY_PROMPT,
|
||||
retries=2,
|
||||
)
|
||||
|
||||
history_text = _format_qa_history(qa_history)
|
||||
if current_context:
|
||||
history_text = f"## Current Context\n{current_context}\n\n{history_text}"
|
||||
result = await agent.run(history_text)
|
||||
return result.output
|
||||
|
||||
|
||||
async def update_session_context(
|
||||
qa_history: list["QAHistoryEntry"],
|
||||
config: AppConfig,
|
||||
current_context: str | None = None,
|
||||
) -> SessionContext:
|
||||
"""Summarize qa_history and return the resulting session context.
|
||||
|
||||
Args:
|
||||
qa_history: List of Q&A pairs from the conversation.
|
||||
config: AppConfig for model selection.
|
||||
current_context: Previous summary to incorporate.
|
||||
|
||||
Returns:
|
||||
The new SessionContext with summary and timestamp.
|
||||
"""
|
||||
summary = await summarize_session(
|
||||
qa_history, config, current_context=current_context
|
||||
)
|
||||
return SessionContext(
|
||||
summary=summary,
|
||||
last_updated=datetime.now(),
|
||||
)
|
||||
|
||||
|
||||
def _format_qa_history(qa_history: list["QAHistoryEntry"]) -> str:
|
||||
"""Format qa_history for input to summarization."""
|
||||
lines: list[str] = []
|
||||
for i, qa in enumerate(qa_history, 1):
|
||||
lines.append(f"## Q{i}: {qa.question}")
|
||||
lines.append(f"**Answer** (confidence: {qa.confidence:.0%}):")
|
||||
lines.append(qa.answer)
|
||||
|
||||
if qa.sources:
|
||||
lines.append(f"**Sources:** {', '.join(qa.sources)}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
async def _update_context_background(
|
||||
qa_session_state: "QASessionState",
|
||||
config: AppConfig,
|
||||
) -> None:
|
||||
"""Background task to update session context after an ask."""
|
||||
try:
|
||||
current_summary = (
|
||||
qa_session_state.session_context.summary
|
||||
if qa_session_state.session_context is not None
|
||||
else None
|
||||
)
|
||||
result = await update_session_context(
|
||||
qa_history=list(qa_session_state.qa_history),
|
||||
config=config,
|
||||
current_context=current_summary,
|
||||
)
|
||||
|
||||
if result.summary:
|
||||
qa_session_state.session_context = result
|
||||
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception as e: # pragma: no cover
|
||||
import logging
|
||||
|
||||
logging.getLogger(__name__).exception(f"Background summarization failed: {e}")
|
||||
|
||||
|
||||
def trigger_background_summarization(
|
||||
qa_session_state: "QASessionState",
|
||||
config: AppConfig,
|
||||
) -> None:
|
||||
"""Trigger background session summarization if qa_history has entries.
|
||||
|
||||
Args:
|
||||
qa_session_state: QASessionState with qa_history to summarize.
|
||||
config: AppConfig for model selection.
|
||||
"""
|
||||
if not qa_session_state.qa_history:
|
||||
return
|
||||
|
||||
key = id(qa_session_state)
|
||||
|
||||
# Cancel any existing summarization task for this state
|
||||
if key in _summarization_tasks:
|
||||
_summarization_tasks[key].cancel()
|
||||
|
||||
# Spawn background task
|
||||
task = asyncio.create_task(
|
||||
_update_context_background(
|
||||
qa_session_state=qa_session_state,
|
||||
config=config,
|
||||
)
|
||||
)
|
||||
_summarization_tasks[key] = task
|
||||
task.add_done_callback(lambda _t, k=key: _summarization_tasks.pop(k, None))
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
from haiku.rag.tools.prompts import build_tools_prompt
|
||||
|
||||
_PROMPT_BASE = """You are a helpful research assistant powered by haiku.rag, a knowledge base system.
|
||||
|
||||
You have access to a knowledge base of documents. Use your tools to search and answer questions.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. For greetings or casual chat: respond directly WITHOUT using any tools
|
||||
2. NEVER call the same tool multiple times for a single user message
|
||||
3. NEVER make up information - always use tools to get facts from the knowledge base"""
|
||||
|
||||
_PROMPT_QA_RULES = """
|
||||
4. For questions: Use the "ask" tool EXACTLY ONCE - it automatically uses prior conversation context"""
|
||||
|
||||
_PROMPT_SEARCH_RULES = """
|
||||
5. For searches: Use the "search" tool EXACTLY ONCE - it handles multi-query expansion internally"""
|
||||
|
||||
_PROMPT_SEARCH_OUTPUT = """
|
||||
After calling search, copy the ENTIRE tool response to your output INCLUDING the content snippets. Do NOT shorten, summarize, or omit any part of the results."""
|
||||
|
||||
_PROMPT_CLOSING = """
|
||||
Be friendly and conversational."""
|
||||
|
||||
_PROMPT_QA_CLOSING = (
|
||||
""" When you use the "ask" tool, summarize the key findings for the user."""
|
||||
)
|
||||
|
||||
|
||||
def build_chat_prompt(
|
||||
features: list[str],
|
||||
preamble: str | None = None,
|
||||
) -> str:
|
||||
"""Build a chat system prompt from the given feature list.
|
||||
|
||||
Each feature adds its relevant tool guidance to the prompt.
|
||||
The base identity, critical rules, and closing are always included.
|
||||
|
||||
Args:
|
||||
features: List of feature names (e.g., ["search", "documents", "qa"]).
|
||||
preamble: Optional custom identity/rules section. When provided,
|
||||
replaces the default identity prompt. Tool guidance, feature
|
||||
rules, and closing are still appended.
|
||||
|
||||
Returns:
|
||||
The composed system prompt string.
|
||||
"""
|
||||
parts = [preamble if preamble is not None else _PROMPT_BASE]
|
||||
|
||||
# Add feature-specific critical rules
|
||||
if "qa" in features:
|
||||
parts.append(_PROMPT_QA_RULES)
|
||||
if "search" in features:
|
||||
parts.append(_PROMPT_SEARCH_RULES)
|
||||
|
||||
# Tool guidance (reusable across agents)
|
||||
tools_prompt = build_tools_prompt(features)
|
||||
if tools_prompt:
|
||||
parts.append(tools_prompt)
|
||||
|
||||
# Chat-specific search output rule
|
||||
if "search" in features:
|
||||
parts.append(_PROMPT_SEARCH_OUTPUT)
|
||||
|
||||
parts.append(_PROMPT_CLOSING)
|
||||
if "qa" in features:
|
||||
parts.append(_PROMPT_QA_CLOSING)
|
||||
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
CHAT_SYSTEM_PROMPT = build_chat_prompt(["search", "documents", "qa"])
|
||||
|
||||
SESSION_SUMMARY_PROMPT = """You are a session summarizer. Given a conversation history of Q&A pairs (and optionally existing context), produce a structured summary that captures key information for future context.
|
||||
|
||||
If a "Current Context" section is provided at the start of the input, incorporate that context into your summary. This might be initial background context from the user or a previous summary - build upon it rather than discard it.
|
||||
|
||||
Your summary should be concise (aim for 500-1500 tokens) and include:
|
||||
|
||||
1. **Key Facts Established** - Specific facts, data, or conclusions learned during the conversation
|
||||
2. **Documents Referenced** - Documents or sources that were cited, with brief notes on what they contain
|
||||
3. **Current Focus** - What topic or question thread the user is currently exploring
|
||||
|
||||
Rules:
|
||||
- Extract only high-signal information that would help answer follow-up questions
|
||||
- When building on existing context, merge new information with prior context
|
||||
- Omit small talk, greetings, or low-confidence answers
|
||||
- Use bullet points for clarity
|
||||
- Keep technical details but compress verbose explanations
|
||||
- Preserve document names/titles when mentioned in sources
|
||||
|
||||
Output the summary directly in markdown format. Do not include meta-commentary about the summary itself."""
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from haiku.rag.agents.research.models import Citation
|
||||
from haiku.rag.tools.session import SessionContext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from haiku.rag.tools.qa import QAHistoryEntry
|
||||
|
||||
AGUI_STATE_KEY = "haiku.rag.chat"
|
||||
|
||||
|
||||
class ChatSessionState(BaseModel):
|
||||
"""State shared between frontend and agent via AG-UI."""
|
||||
|
||||
initial_context: str | None = None
|
||||
citations: list[Citation] = []
|
||||
citations_history: list[list[Citation]] = []
|
||||
qa_history: list["QAHistoryEntry"] = []
|
||||
session_context: SessionContext | None = None
|
||||
document_filter: list[str] = []
|
||||
citation_registry: dict[str, int] = {}
|
||||
|
||||
|
||||
def _rebuild_models(qa_history_entry_cls: type) -> None:
|
||||
"""Resolve ChatSessionState forward reference to QAHistoryEntry.
|
||||
|
||||
Must be called after QAHistoryEntry is defined, passing the class.
|
||||
"""
|
||||
ChatSessionState.model_rebuild(
|
||||
_types_namespace={"QAHistoryEntry": qa_history_entry_cls}
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,336 +0,0 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from haiku.rag.agents.research.models import Citation
|
||||
from haiku.rag.config import Config
|
||||
from haiku.rag.tools.qa import QAHistoryEntry
|
||||
from haiku.rag.tools.session import SessionContext
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_cassette_dir():
|
||||
return str(Path(__file__).parent.parent.parent / "cassettes" / "test_chat_context")
|
||||
|
||||
|
||||
class TestSessionContext:
|
||||
"""Tests for SessionContext model."""
|
||||
|
||||
def test_session_context_creation_empty(self):
|
||||
"""Test SessionContext can be created with defaults."""
|
||||
ctx = SessionContext()
|
||||
assert ctx.summary == ""
|
||||
assert ctx.last_updated is None
|
||||
|
||||
def test_session_context_creation_with_values(self):
|
||||
"""Test SessionContext can be created with provided values."""
|
||||
now = datetime.now()
|
||||
ctx = SessionContext(
|
||||
summary="User discussed authentication patterns.",
|
||||
last_updated=now,
|
||||
)
|
||||
assert ctx.summary == "User discussed authentication patterns."
|
||||
assert ctx.last_updated == now
|
||||
|
||||
def test_session_context_serialization_roundtrip(self):
|
||||
"""Test SessionContext serializes and deserializes correctly."""
|
||||
now = datetime.now()
|
||||
original = SessionContext(
|
||||
summary="Test summary with facts.",
|
||||
last_updated=now,
|
||||
)
|
||||
# Serialize to dict
|
||||
data = original.model_dump()
|
||||
# Deserialize back
|
||||
restored = SessionContext(**data)
|
||||
|
||||
assert restored.summary == original.summary
|
||||
assert restored.last_updated == original.last_updated
|
||||
|
||||
|
||||
class TestSummarizeSession:
|
||||
"""Tests for summarize_session function."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_summarize_session_empty_history(self):
|
||||
"""Test summarize_session with empty qa_history returns empty string."""
|
||||
from haiku.rag.agents.chat.context import summarize_session
|
||||
|
||||
result = await summarize_session(qa_history=[], config=Config)
|
||||
assert result == ""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr()
|
||||
async def test_summarize_session_single_entry(
|
||||
self, allow_model_requests, temp_db_path
|
||||
):
|
||||
"""Test summarize_session with a single qa entry."""
|
||||
from haiku.rag.agents.chat.context import summarize_session
|
||||
|
||||
qa_history = [
|
||||
QAHistoryEntry(
|
||||
question="What is the authentication method?",
|
||||
answer="The API uses JWT tokens for authentication.",
|
||||
confidence=0.95,
|
||||
citations=[
|
||||
Citation(
|
||||
index=1,
|
||||
document_id="doc-1",
|
||||
chunk_id="chunk-1",
|
||||
document_uri="auth-guide.md",
|
||||
document_title="Auth Guide",
|
||||
content="JWT token details...",
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
result = await summarize_session(qa_history=qa_history, config=Config)
|
||||
|
||||
# Should produce a non-empty summary
|
||||
assert len(result) > 0
|
||||
# Summary should mention authentication or JWT
|
||||
assert "authentication" in result.lower() or "jwt" in result.lower()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr()
|
||||
async def test_summarize_session_multiple_entries(
|
||||
self, allow_model_requests, temp_db_path
|
||||
):
|
||||
"""Test summarize_session with multiple qa entries produces consolidated summary."""
|
||||
from haiku.rag.agents.chat.context import summarize_session
|
||||
|
||||
qa_history = [
|
||||
QAHistoryEntry(
|
||||
question="What is the authentication method?",
|
||||
answer="The API uses JWT tokens for authentication.",
|
||||
confidence=0.95,
|
||||
citations=[
|
||||
Citation(
|
||||
index=1,
|
||||
document_id="doc-1",
|
||||
chunk_id="chunk-1",
|
||||
document_uri="auth-guide.md",
|
||||
document_title="Auth Guide",
|
||||
content="JWT token details...",
|
||||
)
|
||||
],
|
||||
),
|
||||
QAHistoryEntry(
|
||||
question="What is the rate limit?",
|
||||
answer="Rate limiting is set to 100 requests per minute.",
|
||||
confidence=0.9,
|
||||
citations=[
|
||||
Citation(
|
||||
index=1,
|
||||
document_id="doc-2",
|
||||
chunk_id="chunk-2",
|
||||
document_uri="api-reference.md",
|
||||
document_title="API Reference",
|
||||
content="Rate limit config...",
|
||||
)
|
||||
],
|
||||
),
|
||||
QAHistoryEntry(
|
||||
question="How do I refresh tokens?",
|
||||
answer="Use the /refresh endpoint with your refresh token.",
|
||||
confidence=0.85,
|
||||
citations=[
|
||||
Citation(
|
||||
index=1,
|
||||
document_id="doc-1",
|
||||
chunk_id="chunk-3",
|
||||
document_uri="auth-guide.md",
|
||||
document_title="Auth Guide",
|
||||
content="Token refresh...",
|
||||
)
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
result = await summarize_session(qa_history=qa_history, config=Config)
|
||||
|
||||
# Should produce a non-empty summary
|
||||
assert len(result) > 0
|
||||
# Summary should contain structured sections
|
||||
result_lower = result.lower()
|
||||
assert "key facts" in result_lower or "established" in result_lower
|
||||
assert "documents" in result_lower or "sources" in result_lower
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr()
|
||||
async def test_summarize_session_with_current_context(self, allow_model_requests):
|
||||
"""Test summarize_session incorporates current_context into the summary."""
|
||||
from haiku.rag.agents.chat.context import summarize_session
|
||||
|
||||
qa_history = [
|
||||
QAHistoryEntry(
|
||||
question="What's the rate limit?",
|
||||
answer="100 requests per minute.",
|
||||
confidence=0.9,
|
||||
)
|
||||
]
|
||||
|
||||
# Provide current_context (e.g., previous summary)
|
||||
current_context = "Focus on Python APIs. User is building a web application."
|
||||
|
||||
result = await summarize_session(
|
||||
qa_history=qa_history,
|
||||
config=Config,
|
||||
current_context=current_context,
|
||||
)
|
||||
|
||||
# Summary should be non-empty and ideally incorporate context about Python/web
|
||||
assert len(result) > 0
|
||||
# The context about "Python" or "web application" should influence the summary
|
||||
result_lower = result.lower()
|
||||
assert (
|
||||
"rate" in result_lower or "limit" in result_lower or "100" in result_lower
|
||||
)
|
||||
|
||||
|
||||
class TestUpdateSessionContext:
|
||||
"""Tests for update_session_context function."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr()
|
||||
async def test_update_session_context_returns_context(
|
||||
self, allow_model_requests, temp_db_path
|
||||
):
|
||||
"""Test update_session_context returns a populated SessionContext."""
|
||||
from haiku.rag.agents.chat.context import update_session_context
|
||||
|
||||
qa_history = [
|
||||
QAHistoryEntry(
|
||||
question="What is the authentication method?",
|
||||
answer="The API uses JWT tokens.",
|
||||
confidence=0.95,
|
||||
)
|
||||
]
|
||||
|
||||
result = await update_session_context(
|
||||
qa_history=qa_history,
|
||||
config=Config,
|
||||
)
|
||||
|
||||
assert result.summary != ""
|
||||
assert result.last_updated is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_session_context_with_empty_history(self):
|
||||
"""Test update_session_context with empty history returns empty summary."""
|
||||
from haiku.rag.agents.chat.context import update_session_context
|
||||
|
||||
result = await update_session_context(
|
||||
qa_history=[],
|
||||
config=Config,
|
||||
)
|
||||
|
||||
assert result.summary == ""
|
||||
|
||||
|
||||
class TestTriggerBackgroundSummarization:
|
||||
"""Tests for trigger_background_summarization."""
|
||||
|
||||
def test_trigger_with_empty_qa_history(self):
|
||||
"""trigger_background_summarization returns early with empty qa_history."""
|
||||
from haiku.rag.agents.chat.context import (
|
||||
_summarization_tasks,
|
||||
trigger_background_summarization,
|
||||
)
|
||||
from haiku.rag.tools.qa import QASessionState
|
||||
|
||||
tasks_before = len(_summarization_tasks)
|
||||
|
||||
qa_session_state = QASessionState()
|
||||
assert len(qa_session_state.qa_history) == 0
|
||||
|
||||
trigger_background_summarization(qa_session_state, config=Config)
|
||||
|
||||
# No new task should have been created
|
||||
assert len(_summarization_tasks) == tasks_before
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_trigger_cancels_existing_task(self):
|
||||
"""Second trigger cancels the previous background task."""
|
||||
import asyncio
|
||||
from unittest.mock import patch
|
||||
|
||||
from haiku.rag.agents.chat.context import (
|
||||
_summarization_tasks,
|
||||
trigger_background_summarization,
|
||||
)
|
||||
from haiku.rag.tools.qa import QAHistoryEntry, QASessionState
|
||||
|
||||
_summarization_tasks.clear()
|
||||
|
||||
qa_session_state = QASessionState(
|
||||
qa_history=[QAHistoryEntry(question="Q1", answer="A1", confidence=0.9)]
|
||||
)
|
||||
|
||||
# Patch _update_context_background to be a slow coroutine
|
||||
async def slow_background(*args, **kwargs):
|
||||
await asyncio.sleep(10)
|
||||
|
||||
with patch(
|
||||
"haiku.rag.agents.chat.context._update_context_background",
|
||||
new=slow_background,
|
||||
):
|
||||
# First trigger creates a task
|
||||
trigger_background_summarization(qa_session_state, config=Config)
|
||||
key = id(qa_session_state)
|
||||
assert key in _summarization_tasks
|
||||
first_task = _summarization_tasks[key]
|
||||
|
||||
# Second trigger should cancel the first
|
||||
trigger_background_summarization(qa_session_state, config=Config)
|
||||
await asyncio.sleep(0) # Let cancellation propagate
|
||||
assert first_task.cancelled() or first_task.done()
|
||||
|
||||
# Cleanup
|
||||
if key in _summarization_tasks:
|
||||
_summarization_tasks[key].cancel()
|
||||
try:
|
||||
await _summarization_tasks[key]
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
_summarization_tasks.clear()
|
||||
|
||||
|
||||
class TestUpdateSessionContextPassesCurrentContext:
|
||||
"""Tests for update_session_context current_context forwarding."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_session_context_passes_current_context(self):
|
||||
"""Test update_session_context passes current_context to summarizer."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from haiku.rag.agents.chat.context import update_session_context
|
||||
|
||||
qa_history = [
|
||||
QAHistoryEntry(
|
||||
question="What is JWT?",
|
||||
answer="JSON Web Token for authentication.",
|
||||
confidence=0.95,
|
||||
)
|
||||
]
|
||||
|
||||
captured_current_context = []
|
||||
|
||||
async def mock_summarize(qa_history, config, current_context=None):
|
||||
captured_current_context.append(current_context)
|
||||
return "Mocked summary"
|
||||
|
||||
with patch(
|
||||
"haiku.rag.agents.chat.context.summarize_session",
|
||||
new=mock_summarize,
|
||||
):
|
||||
await update_session_context(
|
||||
qa_history=qa_history,
|
||||
config=Config,
|
||||
current_context="Previous session summary",
|
||||
)
|
||||
|
||||
assert len(captured_current_context) == 1
|
||||
assert captured_current_context[0] == "Previous session summary"
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
from pydantic_ai import FunctionToolset
|
||||
|
||||
from haiku.rag.agents.chat.agent import (
|
||||
DEFAULT_FEATURES,
|
||||
FEATURE_ANALYSIS,
|
||||
FEATURE_DOCUMENTS,
|
||||
FEATURE_QA,
|
||||
FEATURE_SEARCH,
|
||||
ChatDeps,
|
||||
create_chat_agent,
|
||||
prepare_chat_context,
|
||||
)
|
||||
from haiku.rag.agents.chat.prompts import build_chat_prompt
|
||||
from haiku.rag.client import HaikuRAG
|
||||
from haiku.rag.config import Config
|
||||
from haiku.rag.tools.context import ToolContext
|
||||
from haiku.rag.tools.qa import QA_SESSION_NAMESPACE, QASessionState
|
||||
from haiku.rag.tools.session import SESSION_NAMESPACE, SessionState
|
||||
|
||||
|
||||
def _count_function_toolsets(agent) -> int:
|
||||
"""Count FunctionToolset instances in an agent (excludes internal toolsets)."""
|
||||
return sum(1 for t in agent.toolsets if type(t) is FunctionToolset)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Feature Selection Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def test_default_features(temp_db_path):
|
||||
"""Default features create search + document + qa toolsets and register both states."""
|
||||
context = ToolContext()
|
||||
prepare_chat_context(context)
|
||||
agent = create_chat_agent(Config)
|
||||
|
||||
# Should have 3 toolsets (search, document, qa)
|
||||
assert _count_function_toolsets(agent) == 3
|
||||
|
||||
# Both SessionState and QASessionState should be registered
|
||||
assert context.get(SESSION_NAMESPACE, SessionState) is not None
|
||||
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is not None
|
||||
|
||||
|
||||
def test_search_only(temp_db_path):
|
||||
"""features=["search"] creates only search toolset, no QASessionState."""
|
||||
context = ToolContext()
|
||||
prepare_chat_context(context, features=[FEATURE_SEARCH])
|
||||
agent = create_chat_agent(Config, features=[FEATURE_SEARCH])
|
||||
|
||||
assert _count_function_toolsets(agent) == 1
|
||||
|
||||
# SessionState always registered, but QASessionState should NOT be
|
||||
assert context.get(SESSION_NAMESPACE, SessionState) is not None
|
||||
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
|
||||
|
||||
|
||||
def test_search_and_documents(temp_db_path):
|
||||
"""features=["search", "documents"] creates both toolsets, no QASessionState."""
|
||||
context = ToolContext()
|
||||
prepare_chat_context(context, features=[FEATURE_SEARCH, FEATURE_DOCUMENTS])
|
||||
agent = create_chat_agent(Config, features=[FEATURE_SEARCH, FEATURE_DOCUMENTS])
|
||||
|
||||
assert _count_function_toolsets(agent) == 2
|
||||
|
||||
assert context.get(SESSION_NAMESPACE, SessionState) is not None
|
||||
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
|
||||
|
||||
|
||||
def test_all_features(temp_db_path):
|
||||
"""All four features create four toolsets."""
|
||||
context = ToolContext()
|
||||
prepare_chat_context(
|
||||
context,
|
||||
features=[FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA, FEATURE_ANALYSIS],
|
||||
)
|
||||
agent = create_chat_agent(
|
||||
Config,
|
||||
features=[FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA, FEATURE_ANALYSIS],
|
||||
)
|
||||
|
||||
assert _count_function_toolsets(agent) == 4
|
||||
|
||||
assert context.get(SESSION_NAMESPACE, SessionState) is not None
|
||||
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is not None
|
||||
|
||||
|
||||
def test_no_qa_skips_qa_session_state(temp_db_path):
|
||||
"""Without QA feature, QASessionState is not registered."""
|
||||
context = ToolContext()
|
||||
prepare_chat_context(context, features=[FEATURE_SEARCH, FEATURE_DOCUMENTS])
|
||||
|
||||
assert context.get(QA_SESSION_NAMESPACE, QASessionState) is None
|
||||
|
||||
|
||||
def test_chat_deps_state_without_qa(temp_db_path):
|
||||
"""ChatDeps.state getter omits qa_history/session_context when QASessionState absent."""
|
||||
from haiku.rag.agents.chat.state import AGUI_STATE_KEY
|
||||
|
||||
client = HaikuRAG(temp_db_path, create=True)
|
||||
context = ToolContext()
|
||||
prepare_chat_context(context, features=[FEATURE_SEARCH])
|
||||
|
||||
deps = ChatDeps(config=Config, client=client, tool_context=context)
|
||||
state = deps.state
|
||||
|
||||
# State is wrapped under the AGUI state key
|
||||
assert AGUI_STATE_KEY in state
|
||||
inner = state[AGUI_STATE_KEY]
|
||||
|
||||
# SessionState fields should be present
|
||||
assert "document_filter" in inner
|
||||
assert "citation_registry" in inner
|
||||
assert "citations" in inner
|
||||
# QA fields should NOT be present
|
||||
assert "qa_history" not in inner
|
||||
assert "session_context" not in inner
|
||||
client.close()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Prompt Composition Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def test_build_chat_prompt_default():
|
||||
"""Default features produce prompt mentioning all standard tools."""
|
||||
prompt = build_chat_prompt(DEFAULT_FEATURES)
|
||||
|
||||
assert "list_documents" in prompt
|
||||
assert "get_document" in prompt
|
||||
assert "summarize_document" in prompt
|
||||
assert "ask" in prompt
|
||||
assert "search" in prompt
|
||||
assert "analyze" not in prompt
|
||||
|
||||
|
||||
def test_build_chat_prompt_search_only():
|
||||
"""Search-only prompt doesn't mention ask or document tools."""
|
||||
prompt = build_chat_prompt([FEATURE_SEARCH])
|
||||
|
||||
assert "search" in prompt
|
||||
assert '"ask"' not in prompt
|
||||
assert '"list_documents"' not in prompt
|
||||
assert '"get_document"' not in prompt
|
||||
assert '"summarize_document"' not in prompt
|
||||
|
||||
|
||||
def test_build_chat_prompt_includes_analysis():
|
||||
"""Analysis feature adds analyze guidance to prompt."""
|
||||
prompt = build_chat_prompt(
|
||||
[FEATURE_SEARCH, FEATURE_QA, FEATURE_DOCUMENTS, FEATURE_ANALYSIS]
|
||||
)
|
||||
|
||||
assert "analyze" in prompt
|
||||
assert "search" in prompt
|
||||
assert "ask" in prompt
|
||||
|
||||
|
||||
def test_build_chat_prompt_with_preamble():
|
||||
"""Custom preamble replaces the default identity section."""
|
||||
custom = "You are a custom assistant."
|
||||
prompt = build_chat_prompt(DEFAULT_FEATURES, preamble=custom)
|
||||
|
||||
assert prompt.startswith(custom)
|
||||
# Tool guidance should still be appended
|
||||
assert "search" in prompt
|
||||
assert "ask" in prompt
|
||||
# Default identity should NOT be present
|
||||
assert "haiku.rag" not in prompt
|
||||
|
||||
|
||||
def test_build_chat_prompt_without_preamble_uses_default():
|
||||
"""Without preamble, the default identity section is used."""
|
||||
prompt = build_chat_prompt(DEFAULT_FEATURES)
|
||||
|
||||
assert "haiku.rag" in prompt
|
||||
|
||||
|
||||
def test_create_chat_agent_with_preamble():
|
||||
"""create_chat_agent passes preamble through to build_chat_prompt."""
|
||||
custom = "You are a domain expert."
|
||||
agent = create_chat_agent(Config, preamble=custom)
|
||||
|
||||
assert agent is not None
|
||||
# _instructions is the internal list of instruction strings/callables
|
||||
assert any(
|
||||
custom in instr for instr in agent._instructions if isinstance(instr, str)
|
||||
)
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
from haiku.rag.agents.chat.state import ChatSessionState
|
||||
from haiku.rag.agents.research.models import Citation
|
||||
from haiku.rag.tools.session import SessionContext, SessionState
|
||||
|
||||
|
||||
def test_max_qa_history_constant():
|
||||
"""Test MAX_QA_HISTORY constant value."""
|
||||
from haiku.rag.tools.qa import MAX_QA_HISTORY
|
||||
|
||||
assert MAX_QA_HISTORY == 50
|
||||
|
||||
|
||||
def test_citation_registry_index_assignment():
|
||||
"""Test get_or_assign_index basic index assignment behavior.
|
||||
|
||||
Verifies:
|
||||
- First chunk gets index 1
|
||||
- Second unique chunk gets index 2
|
||||
- Same chunk_id always returns same index
|
||||
"""
|
||||
session_state = SessionState()
|
||||
|
||||
# First chunk gets index 1
|
||||
index1 = session_state.get_or_assign_index("chunk-abc")
|
||||
assert index1 == 1
|
||||
|
||||
# Second unique chunk gets index 2
|
||||
index2 = session_state.get_or_assign_index("chunk-def")
|
||||
assert index2 == 2
|
||||
|
||||
# Same chunk_id returns same index (not incremented)
|
||||
index1_again = session_state.get_or_assign_index("chunk-abc")
|
||||
assert index1_again == 1
|
||||
|
||||
|
||||
def test_citation_registry_stability():
|
||||
"""Test citation indices are stable across multiple calls in any order."""
|
||||
session_state = SessionState()
|
||||
|
||||
# First round assigns indices 1, 2, 3
|
||||
idx_a = session_state.get_or_assign_index("chunk-a")
|
||||
idx_b = session_state.get_or_assign_index("chunk-b")
|
||||
idx_c = session_state.get_or_assign_index("chunk-c")
|
||||
|
||||
# Second round - existing chunks keep their indices regardless of order
|
||||
assert session_state.get_or_assign_index("chunk-b") == idx_b
|
||||
assert session_state.get_or_assign_index("chunk-a") == idx_a
|
||||
assert session_state.get_or_assign_index("chunk-c") == idx_c
|
||||
|
||||
# New chunk gets next index
|
||||
idx_d = session_state.get_or_assign_index("chunk-d")
|
||||
assert idx_d == 4
|
||||
|
||||
|
||||
def test_citation_registry_serialization_roundtrip():
|
||||
"""Test citation_registry serializes and deserializes correctly for AG-UI state."""
|
||||
# Create state and assign indices
|
||||
original = ChatSessionState()
|
||||
original.citation_registry = {"chunk-a": 1, "chunk-b": 2}
|
||||
|
||||
# Serialize
|
||||
state_dict = original.model_dump()
|
||||
assert "citation_registry" in state_dict
|
||||
assert state_dict["citation_registry"] == {"chunk-a": 1, "chunk-b": 2}
|
||||
|
||||
# Deserialize (simulating AG-UI state restoration)
|
||||
restored = ChatSessionState.model_validate(state_dict)
|
||||
assert restored.citation_registry == {"chunk-a": 1, "chunk-b": 2}
|
||||
|
||||
|
||||
def test_chat_session_state_initial_context_default_none():
|
||||
"""Initial context should default to None."""
|
||||
state = ChatSessionState()
|
||||
assert state.initial_context is None
|
||||
|
||||
|
||||
def test_chat_session_state_initial_context_preserved():
|
||||
"""Explicit initial_context should be preserved."""
|
||||
state = ChatSessionState(initial_context="Background info about the project")
|
||||
assert state.initial_context == "Background info about the project"
|
||||
|
||||
|
||||
def test_chat_session_state_initial_context_serialization():
|
||||
"""initial_context should serialize and deserialize correctly."""
|
||||
state = ChatSessionState(
|
||||
initial_context="User is working on authentication",
|
||||
)
|
||||
state_dict = state.model_dump()
|
||||
assert state_dict["initial_context"] == "User is working on authentication"
|
||||
|
||||
restored = ChatSessionState.model_validate(state_dict)
|
||||
assert restored.initial_context == "User is working on authentication"
|
||||
|
||||
|
||||
def test_chat_session_state_model_dump_json_serializes_datetime():
|
||||
"""model_dump(mode='json') should serialize datetime to ISO string.
|
||||
|
||||
Agent tools use model_dump(mode='json') when creating StateSnapshotEvent
|
||||
to ensure datetime fields are JSON-serializable for external clients
|
||||
persisting AG-UI state to database JSON columns.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
session_state = ChatSessionState(
|
||||
session_context=SessionContext(
|
||||
summary="Test summary",
|
||||
last_updated=datetime(2025, 1, 27, 12, 0, 0),
|
||||
),
|
||||
)
|
||||
|
||||
# This is how agent.py creates snapshots for StateSnapshotEvent
|
||||
snapshot = session_state.model_dump(mode="json")
|
||||
|
||||
# datetime should be serialized as ISO string, not datetime object
|
||||
assert isinstance(snapshot["session_context"]["last_updated"], str)
|
||||
assert snapshot["session_context"]["last_updated"] == "2025-01-27T12:00:00"
|
||||
|
||||
|
||||
def test_chat_session_state_citations_history_default():
|
||||
"""citations_history defaults to empty list."""
|
||||
state = ChatSessionState()
|
||||
assert state.citations_history == []
|
||||
|
||||
|
||||
def test_chat_session_state_citations_history_roundtrip():
|
||||
"""citations_history serializes and deserializes correctly."""
|
||||
citation = Citation(
|
||||
index=1,
|
||||
document_id="d1",
|
||||
chunk_id="c1",
|
||||
document_uri="test://doc",
|
||||
document_title="Doc",
|
||||
page_numbers=[],
|
||||
headings=None,
|
||||
content="content",
|
||||
)
|
||||
state = ChatSessionState(citations_history=[[citation]])
|
||||
data = state.model_dump(mode="json")
|
||||
restored = ChatSessionState.model_validate(data)
|
||||
assert len(restored.citations_history) == 1
|
||||
assert restored.citations_history[0][0].chunk_id == "c1"
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,187 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '5211'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a helpful research assistant powered by haiku.rag, a knowledge base system.
|
||||
|
||||
You have access to a knowledge base of documents. Use your tools to search and answer questions.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. For greetings or casual chat: respond directly WITHOUT using any tools
|
||||
2. For questions: Use the "ask" tool EXACTLY ONCE - it automatically uses prior conversation context
|
||||
3. For searches: Use the "search" tool EXACTLY ONCE - it handles multi-query expansion internally
|
||||
4. NEVER call the same tool multiple times for a single user message
|
||||
5. NEVER make up information - always use tools to get facts from the knowledge base
|
||||
|
||||
How to decide which tool to use:
|
||||
- "list_documents" - Use when the user wants to browse or see what documents are available (e.g., "what documents are available?", "show me the documents", "list available docs").
|
||||
- "summarize_document" - Use when the user wants an overview or summary of a specific document (e.g., "summarize document X", "what does Y cover?", "give me an overview of Z").
|
||||
- "get_document" - Use when the user wants the FULL content of a specific document (e.g., "get the paper about Y", "fetch 2412.00566", "show me the full document").
|
||||
- "ask" - Use for questions about topics in the knowledge base. It automatically finds relevant prior answers from conversation history and searches across documents to return answers with citations.
|
||||
- "search" - Use when the user explicitly asks to search/find/explore documents. Call it ONCE. After calling search, copy the ENTIRE tool response to your output INCLUDING the content snippets. Do NOT shorten, summarize, or omit any part of the results.
|
||||
|
||||
IMPORTANT - When user mentions a document in search/ask:
|
||||
- If user says "search in <doc>", "find in <doc>", "answer from <doc>", or "<topic> in <doc>":
|
||||
- Extract the TOPIC as `query`/`question`
|
||||
- Extract the DOCUMENT NAME as `document_name`
|
||||
- Examples for search:
|
||||
- "search for embeddings in the ML paper" → query="embeddings", document_name="ML paper"
|
||||
- "find transformer architecture in 2412.00566" → query="transformer architecture", document_name="2412.00566"
|
||||
- Examples for ask:
|
||||
- "what does the ML paper say about embeddings?" → question="what are the embedding methods?", document_name="ML paper"
|
||||
- "answer from 2412.00566 about model training" → question="how is the model trained?", document_name="2412.00566"
|
||||
|
||||
Be friendly and conversational. When you use the "ask" tool, summarize the key findings for the user.
|
||||
role: system
|
||||
- content: Get me the nonexistent document
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
tool_choice: auto
|
||||
tools:
|
||||
- function:
|
||||
description: |-
|
||||
Search the knowledge base for relevant documents.
|
||||
|
||||
Use this when you need to find documents or explore the knowledge base.
|
||||
Results are displayed to the user - just list the titles found.
|
||||
name: search
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
document_name:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Optional document name/title to search within
|
||||
limit:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: 'Number of results to return (default: 5)'
|
||||
query:
|
||||
description: The search query (what to search for)
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Answer a specific question using the knowledge base.
|
||||
|
||||
Use this for direct questions that need a focused answer with citations.
|
||||
Uses a research graph for planning, searching, and synthesis.
|
||||
name: ask
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
document_name:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Optional document name/title to search within (e.g., "tbmed593", "army manual")
|
||||
question:
|
||||
description: The question to answer
|
||||
type: string
|
||||
required:
|
||||
- question
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
List available documents in the knowledge base.
|
||||
|
||||
Use this when the user wants to browse or see what documents are available.
|
||||
name: list_documents
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
page:
|
||||
default: 1
|
||||
description: 'Page number (default: 1, 50 documents per page)'
|
||||
type: integer
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Retrieve a specific document by title or URI.
|
||||
|
||||
Use this when the user wants to fetch/get/retrieve a specific document.
|
||||
name: get_document
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
query:
|
||||
description: The document title or URI to look up
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
strict: true
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Generate a summary of a specific document.
|
||||
|
||||
Use this when the user wants an overview or summary of a document's content.
|
||||
name: summarize_document
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
query:
|
||||
description: The document title or URI to summarize
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
strict: true
|
||||
type: function
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '539'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: I’m sorry, but that document isn’t available in the knowledge base. If there’s another topic or document
|
||||
you’d like help with, just let me know!
|
||||
reasoning: User asking for nonexistent document. Need to respond that none exists. No tool usage.
|
||||
role: assistant
|
||||
created: 1769793913
|
||||
id: chatcmpl-124
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 60
|
||||
prompt_tokens: 1025
|
||||
total_tokens: 1085
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,196 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '5368'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a helpful research assistant powered by haiku.rag, a knowledge base system.
|
||||
|
||||
You have access to a knowledge base of documents. Use your tools to search and answer questions.
|
||||
|
||||
CRITICAL RULES:
|
||||
1. For greetings or casual chat: respond directly WITHOUT using any tools
|
||||
2. For questions: Use the "ask" tool EXACTLY ONCE - it automatically uses prior conversation context
|
||||
3. For searches: Use the "search" tool EXACTLY ONCE - it handles multi-query expansion internally
|
||||
4. NEVER call the same tool multiple times for a single user message
|
||||
5. NEVER make up information - always use tools to get facts from the knowledge base
|
||||
|
||||
How to decide which tool to use:
|
||||
- "list_documents" - Use when the user wants to browse or see what documents are available (e.g., "what documents are available?", "show me the documents", "list available docs").
|
||||
- "summarize_document" - Use when the user wants an overview or summary of a specific document (e.g., "summarize document X", "what does Y cover?", "give me an overview of Z").
|
||||
- "get_document" - Use when the user wants the FULL content of a specific document (e.g., "get the paper about Y", "fetch 2412.00566", "show me the full document").
|
||||
- "ask" - Use for questions about topics in the knowledge base. It automatically finds relevant prior answers from conversation history and searches across documents to return answers with citations.
|
||||
- "search" - Use when the user explicitly asks to search/find/explore documents. Call it ONCE. After calling search, copy the ENTIRE tool response to your output INCLUDING the content snippets. Do NOT shorten, summarize, or omit any part of the results.
|
||||
|
||||
IMPORTANT - When user mentions a document in search/ask:
|
||||
- If user says "search in <doc>", "find in <doc>", "answer from <doc>", or "<topic> in <doc>":
|
||||
- Extract the TOPIC as `query`/`question`
|
||||
- Extract the DOCUMENT NAME as `document_name`
|
||||
- Examples for search:
|
||||
- "search for embeddings in the ML paper" → query="embeddings", document_name="ML paper"
|
||||
- "find transformer architecture in 2412.00566" → query="transformer architecture", document_name="2412.00566"
|
||||
- Examples for ask:
|
||||
- "what does the ML paper say about embeddings?" → question="what are the embedding methods?", document_name="ML paper"
|
||||
- "answer from 2412.00566 about model training" → question="how is the model trained?", document_name="2412.00566"
|
||||
|
||||
Be friendly and conversational. When you use the "ask" tool, summarize the key findings for the user.
|
||||
role: system
|
||||
- content: Summarize the nonexistent document
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
tool_choice: auto
|
||||
tools:
|
||||
- function:
|
||||
description: |-
|
||||
Search the knowledge base for relevant documents.
|
||||
|
||||
Use this when you need to find documents or explore the knowledge base.
|
||||
Results are displayed to the user - just list the titles found.
|
||||
name: search
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
document_name:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Optional document name/title to search within
|
||||
limit:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: 'Number of results to return (default: 5)'
|
||||
query:
|
||||
description: The search query (what to search for)
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Answer a specific question using the knowledge base.
|
||||
|
||||
Use this for direct questions that need a focused answer with citations.
|
||||
Uses a research graph for planning, searching, and synthesis.
|
||||
name: ask
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
document_name:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Optional document name/title to search within (e.g., "tbmed593", "army manual")
|
||||
question:
|
||||
description: The question to answer
|
||||
type: string
|
||||
required:
|
||||
- question
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
List available documents in the knowledge base.
|
||||
|
||||
Use this when the user wants to browse or see what documents are available.
|
||||
name: list_documents
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
limit:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Maximum number of documents to return
|
||||
offset:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: 'null'
|
||||
default: null
|
||||
description: Number of documents to skip (for pagination)
|
||||
type: object
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Retrieve a specific document by title or URI.
|
||||
|
||||
Use this when the user wants to fetch/get/retrieve a specific document.
|
||||
name: get_document
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
query:
|
||||
description: The document title or URI to look up
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
strict: true
|
||||
type: function
|
||||
- function:
|
||||
description: |-
|
||||
Generate a summary of a specific document.
|
||||
|
||||
Use this when the user wants an overview or summary of a document's content.
|
||||
name: summarize_document
|
||||
parameters:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
query:
|
||||
description: The document title or URI to summarize
|
||||
type: string
|
||||
required:
|
||||
- query
|
||||
type: object
|
||||
strict: true
|
||||
type: function
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '655'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: I’m sorry, but I couldn’t find a document with that name. If you have the exact title or a related keyword,
|
||||
let me know and I’ll try again.
|
||||
reasoning: User asks to summarize nonexistent document. According to rule, for summary use summarize_document tool,
|
||||
but if document doesn't exist? We must search? Likely we respond that document not found. No tool needed.
|
||||
role: assistant
|
||||
created: 1769523898
|
||||
id: chatcmpl-69
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 86
|
||||
prompt_tokens: 1039
|
||||
total_tokens: 1125
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1493'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a session summarizer. Given a conversation history of Q&A pairs, produce a structured summary that captures key information for future context.
|
||||
|
||||
Your summary should be concise (aim for 500-1500 tokens) and include:
|
||||
|
||||
1. **Key Facts Established** - Specific facts, data, or conclusions learned during the conversation
|
||||
2. **Documents Referenced** - Documents or sources that were cited, with brief notes on what they contain
|
||||
3. **Current Focus** - What topic or question thread the user is currently exploring
|
||||
|
||||
Rules:
|
||||
- Extract only high-signal information that would help answer follow-up questions
|
||||
- Omit small talk, greetings, or low-confidence answers
|
||||
- Use bullet points for clarity
|
||||
- Keep technical details but compress verbose explanations
|
||||
- Preserve document names/titles when mentioned in sources
|
||||
|
||||
Output the summary directly in markdown format. Do not include meta-commentary about the summary itself.
|
||||
role: system
|
||||
- content: |
|
||||
## Q1: What is the authentication method?
|
||||
**Answer** (confidence: 95%):
|
||||
The API uses JWT tokens for authentication.
|
||||
**Sources:** Auth Guide
|
||||
|
||||
## Q2: What is the rate limit?
|
||||
**Answer** (confidence: 90%):
|
||||
Rate limiting is set to 100 requests per minute.
|
||||
**Sources:** API Reference
|
||||
|
||||
## Q3: How do I refresh tokens?
|
||||
**Answer** (confidence: 85%):
|
||||
Use the /refresh endpoint with your refresh token.
|
||||
**Sources:** Auth Guide
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '865'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: |-
|
||||
## Key Facts Established
|
||||
- **Authentication method**: JWT tokens.
|
||||
- **Rate limit**: 100 requests per minute.
|
||||
- **Token refresh**: use `/refresh` endpoint with a refresh token.
|
||||
|
||||
## Documents Referenced
|
||||
- **Auth Guide** – contains details on JWT usage, token issuance, and refresh mechanism.
|
||||
- **API Reference** – includes rate limiting policy and endpoint descriptions.
|
||||
|
||||
## Current Focus
|
||||
The user is currently gathering foundational API usage details, specifically authentication methods, rate limits, and token refresh procedures.
|
||||
reasoning: We need to summarize.
|
||||
role: assistant
|
||||
created: 1769007514
|
||||
id: chatcmpl-760
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 121
|
||||
prompt_tokens: 365
|
||||
total_tokens: 486
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1207'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a session summarizer. Given a conversation history of Q&A pairs, produce a structured summary that captures key information for future context.
|
||||
|
||||
Your summary should be concise (aim for 500-1500 tokens) and include:
|
||||
|
||||
1. **Key Facts Established** - Specific facts, data, or conclusions learned during the conversation
|
||||
2. **Documents Referenced** - Documents or sources that were cited, with brief notes on what they contain
|
||||
3. **Current Focus** - What topic or question thread the user is currently exploring
|
||||
|
||||
Rules:
|
||||
- Extract only high-signal information that would help answer follow-up questions
|
||||
- Omit small talk, greetings, or low-confidence answers
|
||||
- Use bullet points for clarity
|
||||
- Keep technical details but compress verbose explanations
|
||||
- Preserve document names/titles when mentioned in sources
|
||||
|
||||
Output the summary directly in markdown format. Do not include meta-commentary about the summary itself.
|
||||
role: system
|
||||
- content: |
|
||||
## Q1: What is the authentication method?
|
||||
**Answer** (confidence: 95%):
|
||||
The API uses JWT tokens for authentication.
|
||||
**Sources:** Auth Guide
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '575'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: |-
|
||||
**Key Facts Established**
|
||||
- The API uses **JWT tokens** for authentication.
|
||||
|
||||
**Documents Referenced**
|
||||
- **Auth Guide** – Provides details on JWT usage for this API.
|
||||
|
||||
**Current Focus**
|
||||
- Understanding the authentication method employed by the API.
|
||||
reasoning: We need to summarize.
|
||||
role: assistant
|
||||
created: 1769007512
|
||||
id: chatcmpl-739
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 65
|
||||
prompt_tokens: 292
|
||||
total_tokens: 357
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1581'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a session summarizer. Given a conversation history of Q&A pairs (and optionally existing context), produce a structured summary that captures key information for future context.
|
||||
|
||||
If a "Current Context" section is provided at the start of the input, incorporate that context into your summary. This might be initial background context from the user or a previous summary - build upon it rather than discard it.
|
||||
|
||||
Your summary should be concise (aim for 500-1500 tokens) and include:
|
||||
|
||||
1. **Key Facts Established** - Specific facts, data, or conclusions learned during the conversation
|
||||
2. **Documents Referenced** - Documents or sources that were cited, with brief notes on what they contain
|
||||
3. **Current Focus** - What topic or question thread the user is currently exploring
|
||||
|
||||
Rules:
|
||||
- Extract only high-signal information that would help answer follow-up questions
|
||||
- When building on existing context, merge new information with prior context
|
||||
- Omit small talk, greetings, or low-confidence answers
|
||||
- Use bullet points for clarity
|
||||
- Keep technical details but compress verbose explanations
|
||||
- Preserve document names/titles when mentioned in sources
|
||||
|
||||
Output the summary directly in markdown format. Do not include meta-commentary about the summary itself.
|
||||
role: system
|
||||
- content: |
|
||||
## Current Context
|
||||
Focus on Python APIs. User is building a web application.
|
||||
|
||||
## Q1: What's the rate limit?
|
||||
**Answer** (confidence: 90%):
|
||||
100 requests per minute.
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '682'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: |-
|
||||
## Summary
|
||||
|
||||
- **Key Facts Established**
|
||||
- The user is building a web application and is focused on Python APIs.
|
||||
- The relevant rate limit is **100 requests per minute** (confidence 90%).
|
||||
|
||||
- **Documents Referenced**
|
||||
- None cited in this exchange.
|
||||
|
||||
- **Current Focus**
|
||||
- Understanding and managing API rate limits for the Python-based web application.
|
||||
reasoning: We need summary.
|
||||
role: assistant
|
||||
created: 1769164539
|
||||
id: chatcmpl-369
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 91
|
||||
prompt_tokens: 362
|
||||
total_tokens: 453
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
interactions:
|
||||
- request:
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate, zstd
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1163'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- localhost:11434
|
||||
method: POST
|
||||
parsed_body:
|
||||
messages:
|
||||
- content: |-
|
||||
You are a session summarizer. Given a conversation history of Q&A pairs, produce a structured summary that captures key information for future context.
|
||||
|
||||
Your summary should be concise (aim for 500-1500 tokens) and include:
|
||||
|
||||
1. **Key Facts Established** - Specific facts, data, or conclusions learned during the conversation
|
||||
2. **Documents Referenced** - Documents or sources that were cited, with brief notes on what they contain
|
||||
3. **Current Focus** - What topic or question thread the user is currently exploring
|
||||
|
||||
Rules:
|
||||
- Extract only high-signal information that would help answer follow-up questions
|
||||
- Omit small talk, greetings, or low-confidence answers
|
||||
- Use bullet points for clarity
|
||||
- Keep technical details but compress verbose explanations
|
||||
- Preserve document names/titles when mentioned in sources
|
||||
|
||||
Output the summary directly in markdown format. Do not include meta-commentary about the summary itself.
|
||||
role: system
|
||||
- content: |
|
||||
## Q1: What is the authentication method?
|
||||
**Answer** (confidence: 95%):
|
||||
The API uses JWT tokens.
|
||||
role: user
|
||||
model: gpt-oss
|
||||
reasoning_effort: low
|
||||
stream: false
|
||||
uri: http://localhost:11434/v1/chat/completions
|
||||
response:
|
||||
headers:
|
||||
content-length:
|
||||
- '559'
|
||||
content-type:
|
||||
- application/json
|
||||
parsed_body:
|
||||
choices:
|
||||
- finish_reason: stop
|
||||
index: 0
|
||||
message:
|
||||
content: |-
|
||||
## Key Facts Established
|
||||
- The API authentication method is **JWT tokens** (high confidence 95%).
|
||||
|
||||
## Documents Referenced
|
||||
- *None provided*.
|
||||
|
||||
## Current Focus
|
||||
- The user is exploring details related to **API authentication mechanisms**.
|
||||
reasoning: We need summary.
|
||||
role: assistant
|
||||
created: 1769007530
|
||||
id: chatcmpl-975
|
||||
model: gpt-oss
|
||||
object: chat.completion
|
||||
system_fingerprint: fp_ollama
|
||||
usage:
|
||||
completion_tokens: 64
|
||||
prompt_tokens: 284
|
||||
total_tokens: 348
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
Loading…
Reference in a new issue