Update tui and app to use the toolkit

This commit is contained in:
Yiorgis Gozadinos 2026-02-16 12:15:03 +02:00
parent 7ff2123806
commit 8c76897af2
No known key found for this signature in database
14 changed files with 150 additions and 81 deletions

View file

@ -8,6 +8,7 @@
- `create_document_toolset()` — document listing, retrieval, and summarization - `create_document_toolset()` — document listing, retrieval, and summarization
- `create_qa_toolset()` — question answering via research graph with prior answer recall - `create_qa_toolset()` — question answering via research graph with prior answer recall
- `create_analysis_toolset()` — computational analysis via RLM agent (Docker sandbox) - `create_analysis_toolset()` — computational analysis via RLM agent (Docker sandbox)
- **`Toolkit` and `build_toolkit()`**: High-level factory that bundles toolsets, prompt, and context creation for a given feature set. Reduces agent composition from ~15 lines to ~5. `build_chat_toolkit()` adds chat-specific defaults (background summarization callback)
- **`ToolContext`**: Namespace-based state container shared across toolsets. Toolsets register Pydantic models under string namespaces, enabling state accumulation (search results, citations, QA history) across invocations - **`ToolContext`**: Namespace-based state container shared across toolsets. Toolsets register Pydantic models under string namespaces, enabling state accumulation (search results, citations, QA history) across invocations
- **`ToolContextCache`**: In-memory TTL-based cache for `ToolContext` instances, keyed by external session/thread ID. Replaces module-level caches for embeddings and summaries - **`ToolContextCache`**: In-memory TTL-based cache for `ToolContext` instances, keyed by external session/thread ID. Replaces module-level caches for embeddings and summaries
- **`run_qa_core()`**: Extracted core QA function for direct programmatic use without an agent - **`run_qa_core()`**: Extracted core QA function for direct programmatic use without an agent
@ -17,6 +18,8 @@
### Changed ### Changed
- **Toolset factories decoupled from runtime dependencies**: `create_search_toolset()`, `create_qa_toolset()`, `create_document_toolset()`, `create_analysis_toolset()`, and `create_chat_agent()` no longer take `client` or `context` parameters. Instead, tool functions receive these via pydantic-ai's `RunContext.deps`. This enables toolset and agent creation at configuration time (cacheable, created once), with only lightweight deps created per-request. Deps must satisfy the `RAGDeps` protocol (`client: HaikuRAG`, `tool_context: ToolContext | None`) - **Toolset factories decoupled from runtime dependencies**: `create_search_toolset()`, `create_qa_toolset()`, `create_document_toolset()`, `create_analysis_toolset()`, and `create_chat_agent()` no longer take `client` or `context` parameters. Instead, tool functions receive these via pydantic-ai's `RunContext.deps`. This enables toolset and agent creation at configuration time (cacheable, created once), with only lightweight deps created per-request. Deps must satisfy the `RAGDeps` protocol (`client: HaikuRAG`, `tool_context: ToolContext | None`)
- **Toolset factory return types narrowed to `FunctionToolset[RAGDeps]`**: All four toolset factories now declare their return type as `FunctionToolset[RAGDeps]` instead of bare `FunctionToolset`
- **`create_chat_agent()` accepts optional `toolkit` parameter**: Pass a pre-built `Toolkit` to share toolsets between agent and context creation, avoiding duplicate construction
- **`ChatDeps` now includes `client`**: `ChatDeps(config=..., client=..., tool_context=...)` — the `client` field was added since it's no longer captured by the agent factory - **`ChatDeps` now includes `client`**: `ChatDeps(config=..., client=..., tool_context=...)` — the `client` field was added since it's no longer captured by the agent factory
- **`prepare_chat_context()` helper**: Extracted from `create_chat_agent()` for idempotent namespace registration, since the agent factory no longer has access to the context - **`prepare_chat_context()` helper**: Extracted from `create_chat_agent()` for idempotent namespace registration, since the agent factory no longer has access to the context
- **Chat agent architecture**: Rebuilt on composable toolsets instead of monolithic tool definitions. Chat agent is now a thin wrapper around `create_search_toolset`, `create_document_toolset`, `create_qa_toolset`, and `create_analysis_toolset` - **Chat agent architecture**: Rebuilt on composable toolsets instead of monolithic tool definitions. Chat agent is now a thin wrapper around `create_search_toolset`, `create_document_toolset`, `create_qa_toolset`, and `create_analysis_toolset`

View file

@ -13,9 +13,10 @@ from starlette.responses import JSONResponse, Response, StreamingResponse
from starlette.routing import Route from starlette.routing import Route
from haiku.rag.agents.chat import ( from haiku.rag.agents.chat import (
AGUI_STATE_KEY,
ChatDeps, ChatDeps,
build_chat_toolkit,
create_chat_agent, create_chat_agent,
prepare_chat_context,
) )
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import load_yaml_config from haiku.rag.config import load_yaml_config
@ -68,8 +69,9 @@ def get_client() -> HaikuRAG:
return _client return _client
# Agent is created once at module level (no runtime deps needed) # Toolkit and agent are created once at module level
agent = create_chat_agent(Config) chat_toolkit = build_chat_toolkit(Config)
agent = create_chat_agent(Config, toolkit=chat_toolkit)
async def stream_chat(request: Request) -> Response: async def stream_chat(request: Request) -> Response:
@ -85,7 +87,7 @@ async def stream_chat(request: Request) -> Response:
thread_id = getattr(run_input, "thread_id", None) or "default" thread_id = getattr(run_input, "thread_id", None) or "default"
context, is_new = context_cache.get_or_create(thread_id) context, is_new = context_cache.get_or_create(thread_id)
if is_new: if is_new:
prepare_chat_context(context) chat_toolkit.prepare(context, state_key=AGUI_STATE_KEY)
deps = ChatDeps( deps = ChatDeps(
config=Config, config=Config,

View file

@ -35,17 +35,16 @@ haiku-rag ask "What are the main features of haiku.rag?" --deep
```python ```python
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config.models import ModelConfig
from haiku.rag.agents.qa.agent import QuestionAnswerAgent from haiku.rag.agents.qa.agent import QuestionAnswerAgent
async with HaikuRAG(path_to_db) as client: async with HaikuRAG(path_to_db) as client:
agent = QuestionAnswerAgent( agent = QuestionAnswerAgent(
client=client, client=client,
provider="openai", model_config=ModelConfig(provider="openai", name="gpt-4o-mini"),
model="gpt-4o-mini",
use_citations=False,
) )
answer = await agent.answer("What is climate change?") answer, citations = await agent.answer("What is climate change?")
print(answer) print(answer)
``` ```
@ -256,15 +255,14 @@ async with HaikuRAG(path_to_db) as client:
```python ```python
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config.models import AppConfig, ResearchConfig from haiku.rag.config.models import AppConfig, ModelConfig, ResearchConfig
from haiku.rag.agents.research.dependencies import ResearchContext from haiku.rag.agents.research.dependencies import ResearchContext
from haiku.rag.agents.research.graph import build_research_graph from haiku.rag.agents.research.graph import build_research_graph
from haiku.rag.agents.research.state import ResearchDeps, ResearchState from haiku.rag.agents.research.state import ResearchDeps, ResearchState
custom_config = AppConfig( custom_config = AppConfig(
research=ResearchConfig( research=ResearchConfig(
provider="openai", model=ModelConfig(provider="openai", name="gpt-4o-mini"),
model="gpt-4o-mini",
max_iterations=5, max_iterations=5,
max_concurrency=3, max_concurrency=3,
) )
@ -282,6 +280,7 @@ async with HaikuRAG(path_to_db) as client:
**Conversational mode with prior answers:** **Conversational mode with prior answers:**
```python ```python
from haiku.rag.config import Config
from haiku.rag.agents.research.dependencies import ResearchContext from haiku.rag.agents.research.dependencies import ResearchContext
from haiku.rag.agents.research.graph import build_research_graph from haiku.rag.agents.research.graph import build_research_graph
from haiku.rag.agents.research.models import SearchAnswer from haiku.rag.agents.research.models import SearchAnswer

View file

@ -433,24 +433,19 @@ haiku.rag provides composable toolset factories that can be mixed into any pydan
```python ```python
from pydantic_ai import Agent from pydantic_ai import Agent
from haiku.rag.tools import ( from haiku.rag.tools import AgentDeps, build_toolkit
AgentDeps, ToolContext, prepare_context,
create_search_toolset, create_qa_toolset,
)
search = create_search_toolset(config) toolkit = build_toolkit(config, features=["search", "qa"])
qa = create_qa_toolset(config)
agent = Agent( agent = Agent(
"openai:gpt-4o", "openai:gpt-4o",
deps_type=AgentDeps, deps_type=AgentDeps,
instructions="You are a helpful assistant.", instructions=f"You are a helpful assistant.\n{toolkit.prompt}",
toolsets=[search, qa], toolsets=toolkit.toolsets,
) )
async with HaikuRAG("path/to/db.lancedb") as client: async with HaikuRAG("path/to/db.lancedb") as client:
context = ToolContext() context = toolkit.create_context()
prepare_context(context, features=["search", "qa"])
deps = AgentDeps(client=client, tool_context=context) deps = AgentDeps(client=client, tool_context=context)
result = await agent.run("What are the main findings?", deps=deps) result = await agent.run("What are the main findings?", deps=deps)
``` ```

View file

@ -227,7 +227,59 @@ Available features: `"search"`, `"qa"`, `"documents"`, `"analysis"`.
## Composing Custom Agents ## Composing Custom Agents
Toolsets are designed to be composed into custom pydantic-ai agents. Use `AgentDeps`, `prepare_context`, and `build_tools_prompt` for minimal boilerplate: ### Using `build_toolkit` (recommended)
`build_toolkit()` bundles toolsets, prompt, and context creation for a given feature set:
```python
from pydantic_ai import Agent
from haiku.rag.client import HaikuRAG
from haiku.rag.tools import AgentDeps, build_toolkit
toolkit = build_toolkit(config, features=["search", "documents", "qa"])
agent = Agent(
"openai:gpt-4o",
deps_type=AgentDeps,
instructions=f"You are a helpful research assistant.\n{toolkit.prompt}",
toolsets=toolkit.toolsets,
)
async with HaikuRAG("path/to/db.lancedb") as client:
context = toolkit.create_context()
deps = AgentDeps(client=client, tool_context=context)
result = await agent.run("What documents do we have about climate?", deps=deps)
print(result.output)
# Access accumulated state
from haiku.rag.tools.search import SearchState, SEARCH_NAMESPACE
search_state = context.get(SEARCH_NAMESPACE, SearchState)
if search_state:
print(f"Total search results: {len(search_state.results)}")
```
**Parameters:**
| Parameter | Default | Description |
|-----------|---------|-------------|
| `config` | required | AppConfig |
| `features` | `["search", "documents"]` | Features to enable |
| `base_filter` | `None` | SQL WHERE clause applied to all toolsets |
| `expand_context` | `True` | Expand search results with surrounding chunks |
| `on_qa_complete` | `None` | Callback invoked after each QA cycle |
**`Toolkit` properties:**
- `toolsets` — list of `FunctionToolset` instances to pass to the Agent
- `prompt` — tool guidance text for the system prompt
- `features` — the feature list this toolkit was built from
- `create_context(state_key=None)` — create a prepared `ToolContext` matching these features
- `prepare(context, state_key=None)` — register namespaces on an existing `ToolContext`
### Using individual factories
For full control, create toolsets individually with `create_*_toolset()`, `build_tools_prompt()`, and `prepare_context()`:
```python ```python
from pydantic_ai import Agent from pydantic_ai import Agent
@ -242,7 +294,6 @@ from haiku.rag.tools import (
create_document_toolset, create_document_toolset,
) )
# Toolsets are created once at configuration time
search = create_search_toolset(config) search = create_search_toolset(config)
qa = create_qa_toolset(config) qa = create_qa_toolset(config)
docs = create_document_toolset(config) docs = create_document_toolset(config)
@ -264,19 +315,12 @@ async with HaikuRAG("path/to/db.lancedb") as client:
result = await agent.run("What documents do we have about climate?", deps=deps) result = await agent.run("What documents do we have about climate?", deps=deps)
print(result.output) print(result.output)
# Access accumulated state
from haiku.rag.tools.search import SearchState, SEARCH_NAMESPACE
search_state = context.get(SEARCH_NAMESPACE, SearchState)
if search_state:
print(f"Total search results: {len(search_state.results)}")
``` ```
`AgentDeps` satisfies the `RAGDeps` protocol and implements the AG-UI state protocol (`state` getter/setter). For AG-UI streaming, set `state_key` on the `ToolContext` (via `prepare_context`): `AgentDeps` satisfies the `RAGDeps` protocol and implements the AG-UI state protocol (`state` getter/setter). For AG-UI streaming, set `state_key` on the `ToolContext` (via `prepare_context` or `toolkit.create_context`):
```python ```python
context = ToolContext() context = toolkit.create_context(state_key="my_app")
prepare_context(context, features=["search", "qa"], state_key="my_app")
deps = AgentDeps(client=client, tool_context=context) deps = AgentDeps(client=client, tool_context=context)
``` ```
@ -300,20 +344,22 @@ prepare_context(context, features=["search", "qa"], state_key="my_app")
deps = AgentDeps(client=client, tool_context=context) deps = AgentDeps(client=client, tool_context=context)
``` ```
**Chat agent** uses `ChatDeps` + `prepare_chat_context` (adds chat-specific overrides like background summarization and initial context handling): **Chat agent** uses `ChatDeps` + `build_chat_toolkit` (adds chat-specific defaults like background summarization):
```python ```python
from haiku.rag.agents.chat import ( from haiku.rag.agents.chat import (
ChatDeps, create_chat_agent, prepare_chat_context, AGUI_STATE_KEY, ChatDeps, build_chat_toolkit, create_chat_agent,
) )
from haiku.rag.tools import ToolContext, ToolContextCache from haiku.rag.tools import ToolContextCache
agent = create_chat_agent(config) chat_toolkit = build_chat_toolkit(config)
agent = create_chat_agent(config, toolkit=chat_toolkit)
# For multi-session apps, cache ToolContext per thread # For multi-session apps, cache ToolContext per thread
cache = ToolContextCache() cache = ToolContextCache()
context, _is_new = cache.get_or_create(thread_id) context, is_new = cache.get_or_create(thread_id)
prepare_chat_context(context) # idempotent; sets state_key="haiku.rag.chat" if is_new:
chat_toolkit.prepare(context, state_key=AGUI_STATE_KEY)
deps = ChatDeps( deps = ChatDeps(
config=config, config=config,

View file

@ -5,6 +5,7 @@ from haiku.rag.agents.chat.agent import (
FEATURE_QA, FEATURE_QA,
FEATURE_SEARCH, FEATURE_SEARCH,
ChatDeps, ChatDeps,
build_chat_toolkit,
create_chat_agent, create_chat_agent,
prepare_chat_context, prepare_chat_context,
run_chat_agent, run_chat_agent,
@ -28,6 +29,7 @@ __all__ = [
"FEATURE_QA", "FEATURE_QA",
"FEATURE_SEARCH", "FEATURE_SEARCH",
"build_chat_prompt", "build_chat_prompt",
"build_chat_toolkit",
"create_chat_agent", "create_chat_agent",
"prepare_chat_context", "prepare_chat_context",
"run_chat_agent", "run_chat_agent",

View file

@ -9,19 +9,20 @@ from haiku.rag.agents.chat.context import (
from haiku.rag.agents.chat.prompts import build_chat_prompt from haiku.rag.agents.chat.prompts import build_chat_prompt
from haiku.rag.agents.chat.state import AGUI_STATE_KEY from haiku.rag.agents.chat.state import AGUI_STATE_KEY
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.tools.context import ToolContext, prepare_context from haiku.rag.tools.context import ToolContext
from haiku.rag.tools.deps import AgentDeps from haiku.rag.tools.deps import AgentDeps
from haiku.rag.tools.document import create_document_toolset from haiku.rag.tools.qa import QA_SESSION_NAMESPACE, QASessionState
from haiku.rag.tools.qa import QA_SESSION_NAMESPACE, QASessionState, create_qa_toolset
from haiku.rag.tools.search import create_search_toolset
from haiku.rag.tools.session import SessionContext 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 from haiku.rag.utils import get_model
FEATURE_SEARCH = "search"
FEATURE_DOCUMENTS = "documents"
FEATURE_QA = "qa"
FEATURE_ANALYSIS = "analysis"
DEFAULT_FEATURES = [FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA] DEFAULT_FEATURES = [FEATURE_SEARCH, FEATURE_DOCUMENTS, FEATURE_QA]
@ -71,6 +72,28 @@ class ChatDeps(AgentDeps):
) )
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( def prepare_chat_context(
context: ToolContext, context: ToolContext,
features: list[str] | None = None, features: list[str] | None = None,
@ -83,6 +106,8 @@ def prepare_chat_context(
context: ToolContext to prepare. context: ToolContext to prepare.
features: List of enabled features. Defaults to DEFAULT_FEATURES. features: List of enabled features. Defaults to DEFAULT_FEATURES.
""" """
from haiku.rag.tools.context import prepare_context
if features is None: if features is None:
features = DEFAULT_FEATURES features = DEFAULT_FEATURES
@ -93,6 +118,7 @@ def create_chat_agent(
config: AppConfig, config: AppConfig,
features: list[str] | None = None, features: list[str] | None = None,
preamble: str | None = None, preamble: str | None = None,
toolkit: Toolkit | None = None,
) -> Agent[ChatDeps, str]: ) -> Agent[ChatDeps, str]:
"""Create the chat agent with composed toolsets. """Create the chat agent with composed toolsets.
@ -104,32 +130,26 @@ def create_chat_agent(
preamble: Optional custom identity/rules section for the system prompt. preamble: Optional custom identity/rules section for the system prompt.
When provided, replaces the default identity prompt. Tool guidance, When provided, replaces the default identity prompt. Tool guidance,
feature rules, and closing are still appended by the builder. 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: Returns:
The configured chat agent. The configured chat agent.
Example: Example:
async with HaikuRAG(db_path, create=True) as client: async with HaikuRAG(db_path, create=True) as client:
context = ToolContext() toolkit = build_chat_toolkit(config)
prepare_chat_context(context) context = toolkit.create_context(state_key=AGUI_STATE_KEY)
agent = create_chat_agent(config) agent = create_chat_agent(config, toolkit=toolkit)
deps = ChatDeps(config=config, client=client, tool_context=context) deps = ChatDeps(config=config, client=client, tool_context=context)
result = await agent.run("Search for X", deps=deps) result = await agent.run("Search for X", deps=deps)
""" """
if features is None: if features is None:
features = DEFAULT_FEATURES features = DEFAULT_FEATURES
toolsets = [] if toolkit is None:
if FEATURE_SEARCH in features: toolkit = build_chat_toolkit(config, features=features)
toolsets.append(create_search_toolset(config))
if FEATURE_DOCUMENTS in features:
toolsets.append(create_document_toolset(config))
if FEATURE_QA in features:
toolsets.append(create_qa_toolset(config, on_ask_complete=_on_qa_complete))
if FEATURE_ANALYSIS in features:
from haiku.rag.tools.analysis import create_analysis_toolset
toolsets.append(create_analysis_toolset(config))
model = get_model(config.qa.model, config) model = get_model(config.qa.model, config)
@ -138,7 +158,7 @@ def create_chat_agent(
deps_type=ChatDeps, deps_type=ChatDeps,
output_type=str, output_type=str,
instructions=build_chat_prompt(features, preamble=preamble), instructions=build_chat_prompt(features, preamble=preamble),
toolsets=toolsets, toolsets=toolkit.toolsets,
retries=3, retries=3,
) )
@ -182,6 +202,7 @@ async def run_chat_agent(
__all__ = [ __all__ = [
"build_chat_toolkit",
"create_chat_agent", "create_chat_agent",
"prepare_chat_context", "prepare_chat_context",
"run_chat_agent", "run_chat_agent",

View file

@ -56,18 +56,18 @@ class QuestionAnswerAgent:
# Agent created per-call: toolset varies with filter, and Agent # Agent created per-call: toolset varies with filter, and Agent
# construction is pure Python (no IO). # construction is pure Python (no IO).
agent = Agent( agent: Agent[_QARunDeps, RawSearchAnswer] = Agent( # ty: ignore[invalid-assignment]
model=get_model(self._model_config, self._config), model=get_model(self._model_config, self._config),
deps_type=_QARunDeps, deps_type=_QARunDeps,
output_type=RawSearchAnswer, output_type=RawSearchAnswer,
output_retries=3, output_retries=3,
instructions=self._system_prompt, instructions=self._system_prompt,
toolsets=[search_toolset], # ty: ignore[invalid-argument-type] toolsets=[search_toolset],
retries=3, retries=3,
) )
deps = _QARunDeps(client=self._client, tool_context=context) deps = _QARunDeps(client=self._client, tool_context=context)
result = await agent.run(question, deps=deps) # ty: ignore[invalid-argument-type] result = await agent.run(question, deps=deps)
output = result.output output = result.output
# Get search results from context for citation resolution # Get search results from context for citation resolution

View file

@ -17,13 +17,13 @@ from pydantic_ai.messages import ModelMessage
from haiku.rag.agents.chat.agent import ( from haiku.rag.agents.chat.agent import (
ChatDeps, ChatDeps,
build_chat_toolkit,
create_chat_agent, create_chat_agent,
prepare_chat_context,
trigger_background_summarization, trigger_background_summarization,
) )
from haiku.rag.agents.chat.state import AGUI_STATE_KEY
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import get_config from haiku.rag.config import get_config
from haiku.rag.tools.context import ToolContext
from haiku.rag.tools.session import SESSION_NAMESPACE, SessionState from haiku.rag.tools.session import SESSION_NAMESPACE, SessionState
if TYPE_CHECKING: if TYPE_CHECKING:
@ -151,10 +151,10 @@ class ChatApp(App):
) )
await self.client.__aenter__() await self.client.__aenter__()
# Create tool context and agent # Create toolkit, context, and agent
self.tool_context = ToolContext() self.toolkit = build_chat_toolkit(self.config)
prepare_chat_context(self.tool_context) self.tool_context = self.toolkit.create_context(state_key=AGUI_STATE_KEY)
self.agent = create_chat_agent(self.config) self.agent = create_chat_agent(self.config, toolkit=self.toolkit)
# Sync document filter to tool context # Sync document filter to tool context
session_state = self.tool_context.get(SESSION_NAMESPACE, SessionState) session_state = self.tool_context.get(SESSION_NAMESPACE, SessionState)

View file

@ -17,7 +17,7 @@ def create_analysis_toolset(
config: AppConfig, config: AppConfig,
base_filter: str | None = None, base_filter: str | None = None,
tool_name: str = "analyze", tool_name: str = "analyze",
) -> FunctionToolset: ) -> FunctionToolset[RAGDeps]:
"""Create a toolset with code analysis capabilities via RLM agent. """Create a toolset with code analysis capabilities via RLM agent.
Args: Args:
@ -77,6 +77,6 @@ def create_analysis_toolset(
code_executed=bool(program), code_executed=bool(program),
) )
toolset = FunctionToolset() toolset: FunctionToolset[RAGDeps] = FunctionToolset()
toolset.add_function(analyze, name=tool_name) toolset.add_function(analyze, name=tool_name)
return toolset return toolset

View file

@ -68,7 +68,7 @@ async def find_document(client: HaikuRAG, query: str):
def create_document_toolset( def create_document_toolset(
config: AppConfig, config: AppConfig,
base_filter: str | None = None, base_filter: str | None = None,
) -> FunctionToolset: ) -> FunctionToolset[RAGDeps]:
"""Create a toolset with document management capabilities. """Create a toolset with document management capabilities.
Args: Args:
@ -169,7 +169,7 @@ def create_document_toolset(
return f"**Summary of {doc.title or doc.uri}:**\n\n{result.output}" return f"**Summary of {doc.title or doc.uri}:**\n\n{result.output}"
toolset = FunctionToolset() toolset: FunctionToolset[RAGDeps] = FunctionToolset()
toolset.add_function(list_documents) toolset.add_function(list_documents)
toolset.add_function(get_document) toolset.add_function(get_document)
toolset.add_function(summarize_document) toolset.add_function(summarize_document)

View file

@ -210,7 +210,7 @@ def create_qa_toolset(
base_filter: str | None = None, base_filter: str | None = None,
tool_name: str = "ask", tool_name: str = "ask",
on_ask_complete: Callable[[QASessionState, AppConfig], None] | None = None, on_ask_complete: Callable[[QASessionState, AppConfig], None] | None = None,
) -> FunctionToolset: ) -> FunctionToolset[RAGDeps]:
"""Create a toolset with Q&A capabilities using research graph. """Create a toolset with Q&A capabilities using research graph.
Args: Args:
@ -277,6 +277,6 @@ def create_qa_toolset(
return qa_result return qa_result
toolset = FunctionToolset() toolset: FunctionToolset[RAGDeps] = FunctionToolset()
toolset.add_function(ask, name=tool_name) toolset.add_function(ask, name=tool_name)
return toolset return toolset

View file

@ -25,7 +25,7 @@ def create_search_toolset(
expand_context: bool = True, expand_context: bool = True,
base_filter: str | None = None, base_filter: str | None = None,
tool_name: str = "search", tool_name: str = "search",
) -> FunctionToolset: ) -> FunctionToolset[RAGDeps]:
"""Create a toolset with search capabilities. """Create a toolset with search capabilities.
Args: Args:
@ -150,6 +150,6 @@ def create_search_toolset(
] ]
return "\n\n".join(formatted) return "\n\n".join(formatted)
toolset = FunctionToolset() toolset: FunctionToolset[RAGDeps] = FunctionToolset()
toolset.add_function(search, name=tool_name) toolset.add_function(search, name=tool_name)
return toolset return toolset

View file

@ -1,5 +1,6 @@
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any
from pydantic_ai import FunctionToolset from pydantic_ai import FunctionToolset
@ -21,7 +22,7 @@ class Toolkit:
an agent with haiku.rag toolsets and create matching ToolContexts. an agent with haiku.rag toolsets and create matching ToolContexts.
""" """
toolsets: list[FunctionToolset] = field(default_factory=list) toolsets: list[FunctionToolset[Any]] = field(default_factory=list)
prompt: str = "" prompt: str = ""
features: list[str] = field(default_factory=list) features: list[str] = field(default_factory=list)
@ -72,7 +73,7 @@ def build_toolkit(
if features is None: if features is None:
features = [FEATURE_SEARCH, FEATURE_DOCUMENTS] features = [FEATURE_SEARCH, FEATURE_DOCUMENTS]
toolsets: list[FunctionToolset] = [] toolsets: list[FunctionToolset[Any]] = []
if FEATURE_SEARCH in features: if FEATURE_SEARCH in features:
from haiku.rag.tools.search import create_search_toolset from haiku.rag.tools.search import create_search_toolset