Skip to content

Commit a94c9d4

Browse files
committed
Update README ; applying reviewers suggestions
Signed-off-by: Michele Pangrazzi <[email protected]>
1 parent c1dd447 commit a94c9d4

File tree

7 files changed

+89
-43
lines changed

7 files changed

+89
-43
lines changed

examples/basic/frameworks/haystack_deep_research_agent/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ You can customize the workflow by:
195195
196196
1. **Modifying the system prompt** in `config.yml` to change the agent's behavior
197197
2. **Adding more document types** by extending the RAG tool to support other file formats
198-
3. **Changing the LLM model** by updating the NVIDIA model names in the configuration (`agent_model` and `rag_model` in `config.yml`). See Haystack's NvidiaChatGenerator docs: [NvidiaChatGenerator](https://docs.haystack.deepset.ai/docs/nvidiachatgenerator)
198+
3. **Changing the LLM model** by updating the top-level `llms` section in `config.yml`. This example defines `agent_llm` and `rag_llm` using the `nim` provider so they can leverage common parameters like `temperature`, `top_p`, and `max_tokens`. The workflow references them via the builder. See Haystack's NvidiaChatGenerator docs: [NvidiaChatGenerator](https://docs.haystack.deepset.ai/docs/nvidiachatgenerator)
199199
4. **Adjusting search parameters** to optimize for your use case
200200
201201
## Troubleshooting
@@ -213,9 +213,9 @@ You can customize the workflow by:
213213
214214
The workflow demonstrates several key NeMo-Agent-Toolkit patterns:
215215
216-
- **Function Registration**: Each tool is registered as a function with its own configuration
217-
- **Builder Pattern**: The NeMo-Agent-Toolkit Builder is used to create and manage tools and LLMs
218-
- **Component Integration**: Haystack components are wrapped as NeMo-Agent-Toolkit functions
216+
- **Workflow Registration**: The agent is exposed as a workflow function with a pydantic config
217+
- **Builder LLM Integration**: LLMs are defined under top-level `llms:` and accessed via `builder.get_llm_config(...)`
218+
- **Component Integration**: Haystack components are composed into tools within the workflow
219219
- **Error Handling**: Robust error handling with fallback behaviors
220220
- **Async Operations**: All operations are asynchronous for better performance
221221

examples/basic/frameworks/haystack_deep_research_agent/src/aiq_haystack_deep_research_agent/configs/config.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,23 @@
1616
general:
1717
use_uvloop: true
1818

19+
llms:
20+
rag_llm:
21+
_type: nim
22+
model: nvidia/llama-3.3-nemotron-super-49b-v1
23+
base_url: https://integrate.api.nvidia.com/v1
24+
api_key: ${NVIDIA_API_KEY}
25+
agent_llm:
26+
_type: nim
27+
model: nvidia/llama-3.3-nemotron-super-49b-v1
28+
base_url: https://integrate.api.nvidia.com/v1
29+
api_key: ${NVIDIA_API_KEY}
30+
1931
workflow:
2032
_type: haystack_deep_research_agent
21-
agent_model: nvidia/llama-3.3-nemotron-super-49b-v1
22-
rag_model: nvidia/llama-3.3-nemotron-super-49b-v1
23-
nvidia_api_url: https://integrate.api.nvidia.com/v1
2433
max_agent_steps: 20
34+
search_top_k: 10
35+
rag_top_k: 15
2536
opensearch_url: http://localhost:9200
2637
index_on_startup: true
2738
data_dir: /data

examples/basic/frameworks/haystack_deep_research_agent/src/aiq_haystack_deep_research_agent/pipelines/indexing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
from pathlib import Path
5-
from typing import Tuple
65

76
from haystack.core.pipeline import Pipeline
87
from haystack.components.converters.pypdf import PyPDFToDocument
@@ -12,7 +11,7 @@
1211
from haystack.document_stores.types import DuplicatePolicy
1312

1413

15-
def _gather_sources(base_dir: Path) -> Tuple[list[Path], list[Path]]:
14+
def _gather_sources(base_dir: Path) -> tuple[list[Path], list[Path]]:
1615
pdfs = list(base_dir.glob("**/*.pdf"))
1716
texts = list(base_dir.glob("**/*.txt")) + list(base_dir.glob("**/*.md"))
1817
return pdfs, texts

examples/basic/frameworks/haystack_deep_research_agent/src/aiq_haystack_deep_research_agent/pipelines/rag.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4-
from typing import Tuple
5-
64
from haystack.components.builders import ChatPromptBuilder
75
from haystack.core.pipeline import Pipeline
86
from haystack.core.super_component import SuperComponent
@@ -16,18 +14,29 @@
1614

1715
def create_rag_tool(
1816
document_store,
19-
rag_model: str,
20-
nvidia_api_url: str,
21-
secret_provider,
2217
*,
2318
top_k: int = 15,
24-
) -> Tuple[ComponentTool, Pipeline]:
19+
generator: NvidiaChatGenerator | None = None,
20+
) -> tuple[ComponentTool, Pipeline]:
21+
"""
22+
Build a RAG tool composed of OpenSearch retriever and NvidiaChatGenerator.
23+
24+
Args:
25+
document_store: OpenSearch document store instance.
26+
top_k: Number of documents to retrieve for RAG.
27+
generator: Pre-configured NvidiaChatGenerator created from builder LLM config.
28+
29+
Returns:
30+
(ComponentTool, Pipeline): The tool and underlying pipeline.
31+
32+
Raises:
33+
ValueError: If a generator is not provided.
34+
"""
2535
retriever = OpenSearchBM25Retriever(document_store=document_store, top_k=top_k)
26-
generator = NvidiaChatGenerator(
27-
model=rag_model,
28-
api_base_url=nvidia_api_url,
29-
api_key=secret_provider,
30-
)
36+
if generator is None:
37+
raise ValueError(
38+
"NvidiaChatGenerator instance must be provided via builder-configured LLM."
39+
)
3140

3241
template = """
3342
{% for document in documents %}

examples/basic/frameworks/haystack_deep_research_agent/src/aiq_haystack_deep_research_agent/pipelines/search.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@
1010
from haystack.tools import ComponentTool
1111

1212

13-
def create_search_tool() -> ComponentTool:
13+
def create_search_tool(top_k: int = 10) -> ComponentTool:
14+
"""
15+
Build a Haystack web search tool pipeline.
16+
17+
Args:
18+
top_k: Number of search results to retrieve from Serper.
19+
20+
Returns:
21+
ComponentTool: A Haystack tool that executes web search and returns formatted text.
22+
"""
1423
search_pipeline = Pipeline()
15-
search_pipeline.add_component("search", SerperDevWebSearch(top_k=10))
24+
search_pipeline.add_component("search", SerperDevWebSearch(top_k=top_k))
1625
search_pipeline.add_component(
1726
"fetcher",
1827
LinkContentFetcher(timeout=3, raise_on_failure=False, retry_attempts=2),

examples/basic/frameworks/haystack_deep_research_agent/src/aiq_haystack_deep_research_agent/register.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
3+
# pyright: reportMissingTypeStubs=false, reportMissingImports=false
34
#
45
# Licensed under the Apache License, Version 2.0 (the "License");
56
# you may not use this file except in compliance with the License.
@@ -18,6 +19,7 @@
1819
from aiq.builder.builder import Builder
1920
from aiq.cli.register_workflow import register_function
2021
from aiq.data_models.function import FunctionBaseConfig
22+
from aiq.llm.nim_llm import NIMModelConfig
2123

2224
logger = logging.getLogger(__name__)
2325

@@ -37,11 +39,10 @@ class HaystackDeepResearchWorkflowConfig(
3739
When you use information from the document database, cite the text used from the source document.
3840
It is important that you cite accurately.
3941
"""
40-
agent_model: str = "meta/llama-3.1-8b-instruct"
41-
rag_model: str = "meta/llama-3.1-8b-instruct"
4242
max_agent_steps: int = 20
43+
search_top_k: int = 10
44+
rag_top_k: int = 15
4345
opensearch_url: str = "http://localhost:9200"
44-
nvidia_api_url: str = "https://integrate.api.nvidia.com/v1"
4546
# Indexing configuration
4647
index_on_startup: bool = True
4748
# Default to "/data" so users can mount a volume or place files at repo_root/data.
@@ -54,7 +55,10 @@ async def haystack_deep_research_agent_workflow(
5455
config: HaystackDeepResearchWorkflowConfig, builder: Builder
5556
):
5657
"""
57-
Main workflow that creates and returns the deep research agent
58+
Main workflow that creates and returns the deep research agent.
59+
60+
Uses top-level `llms` configuration via builder to instantiate Haystack NvidiaChatGenerator
61+
for both the agent and RAG tool, per review suggestions.
5862
"""
5963
from haystack.components.agents import Agent
6064
from haystack.utils import Secret
@@ -71,7 +75,7 @@ async def haystack_deep_research_agent_workflow(
7175
logger.info(f"Starting Haystack Deep Research Agent workflow with config: {config}")
7276

7377
# Create search tool
74-
search_tool = create_search_tool()
78+
search_tool = create_search_tool(top_k=config.search_top_k)
7579

7680
# Create document store
7781
document_store = OpenSearchDocumentStore(
@@ -85,22 +89,34 @@ async def haystack_deep_research_agent_workflow(
8589
document_store=document_store, data_dir=config.data_dir, logger=logger
8690
)
8791

88-
# Create RAG tool
92+
def _nim_to_haystack_generator(cfg: NIMModelConfig) -> NvidiaChatGenerator:
93+
return NvidiaChatGenerator(
94+
model=cfg.model_name,
95+
api_base_url=cfg.base_url,
96+
api_key=Secret.from_env_var("NVIDIA_API_KEY"),
97+
)
98+
99+
# Instantiate LLMs via builder configs (expecting NIM)
100+
rag_llm_cfg = builder.get_llm_config("rag_llm")
101+
agent_llm_cfg = builder.get_llm_config("agent_llm")
102+
103+
if not isinstance(rag_llm_cfg, NIMModelConfig):
104+
raise TypeError("llms.rag_llm must be of type 'nim'.")
105+
if not isinstance(agent_llm_cfg, NIMModelConfig):
106+
raise TypeError("llms.agent_llm must be of type 'nim'.")
107+
108+
rag_generator = _nim_to_haystack_generator(rag_llm_cfg)
89109
rag_tool, _ = create_rag_tool(
90110
document_store=document_store,
91-
rag_model=config.rag_model,
92-
nvidia_api_url=config.nvidia_api_url,
93-
secret_provider=Secret.from_env_var("NVIDIA_API_KEY"),
94-
top_k=15,
111+
top_k=config.rag_top_k,
112+
generator=rag_generator,
95113
)
96114

97115
# Create the agent
116+
agent_generator = _nim_to_haystack_generator(agent_llm_cfg)
117+
98118
agent = Agent(
99-
chat_generator=NvidiaChatGenerator(
100-
model=config.agent_model,
101-
api_base_url=config.nvidia_api_url,
102-
api_key=Secret.from_env_var("NVIDIA_API_KEY"),
103-
),
119+
chat_generator=agent_generator,
104120
tools=Toolset(tools=[search_tool, rag_tool]),
105121
system_prompt=config.system_prompt,
106122
exit_conditions=["text"],
@@ -113,14 +129,13 @@ async def haystack_deep_research_agent_workflow(
113129

114130
async def _response_fn(input_message: str) -> str:
115131
"""
116-
Process the input message and generate a research response
117-
Implements the exact logic from the notebook
132+
Process the input message and generate a research response.
118133
119134
Args:
120135
input_message: The user's research question
121136
122137
Returns:
123-
Comprehensive research report
138+
Comprehensive research report.
124139
"""
125140
try:
126141
logger.info(f"Processing research query: {input_message}")

examples/basic/frameworks/haystack_deep_research_agent/tests/test_haystack_deep_research_agent.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,13 @@ def test_config_yaml_loads_and_has_keys() -> None:
8282
assert "_type: haystack_deep_research_agent" in text
8383
# key fields expected
8484
for key in [
85-
"agent_model:",
86-
"rag_model:",
87-
"nvidia_api_url:",
85+
"llms:",
86+
"rag_llm:",
87+
"agent_llm:",
88+
"workflow:",
8889
"max_agent_steps:",
90+
"search_top_k:",
91+
"rag_top_k:",
8992
"opensearch_url:",
9093
"index_on_startup:",
9194
"data_dir:",

0 commit comments

Comments
 (0)