Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions haystack/components/connectors/openapi_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ def patch_request(
:param data: The request body to send.
:param parameters: The parameters used to create the path.
:param raw_response: If true, return the raw response instead of validating
and exterpolating it.
and extrapolating it.
:param security: The security scheme to use, and the values it needs to
process successfully.
:param session: A persistent request session.
:param verify: If we should do an ssl verification on the request or not.
:param verify: If we should do an SSL verification on the request or not.
In case str was provided, will use that as the CA.
:return: The response data, either raw or processed depending on raw_response flag.
"""
Expand Down
1 change: 1 addition & 0 deletions haystack/components/converters/csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CSVToDocument:

```python
from haystack.components.converters.csv import CSVToDocument
from datetime import datetime
converter = CSVToDocument()
results = converter.run(sources=["sample.csv"], meta={"date_added": datetime.now().isoformat()})
documents = results["documents"]
Expand Down
3 changes: 2 additions & 1 deletion haystack/components/converters/xlsx.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ class XLSXToDocument:

```python
from haystack.components.converters.xlsx import XLSXToDocument
from datetime import datetime

converter = XLSXToDocument()
results = converter.run(sources=["sample.xlsx"], meta={"date_added": datetime.now().isoformat()})
documents = results["documents"]
print(documents[0].content)
# ",A,B\n1,col_a,col_b\n2,1.5,test\n"
# ",A,B\\n1,col_a,col_b\\n2,1.5,test\\n"
```
"""

Expand Down
2 changes: 1 addition & 1 deletion haystack/components/evaluators/faithfulness.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class FaithfulnessEvaluator(LLMEvaluator):
# 0.5
print(result["results"])
# [{'statements': ['Python is a high-level general-purpose programming language.',
'Python was created by George Lucas.'], 'statement_scores': [1, 0], 'score': 0.5}]
# 'Python was created by George Lucas.'], 'statement_scores': [1, 0], 'score': 0.5}]
```
"""

Expand Down
1 change: 1 addition & 0 deletions haystack/components/generators/chat/hugging_face_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class HuggingFaceAPIChatGenerator:

result = generator.run(messages)
print(result)
```

#### With self-hosted text generation inference

Expand Down
10 changes: 5 additions & 5 deletions haystack/components/generators/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ class OpenAIGenerator:
response = client.run("What's Natural Language Processing? Be brief.")
print(response)

>> {'replies': ['Natural Language Processing (NLP) is a branch of artificial intelligence that focuses on
>> the interaction between computers and human language. It involves enabling computers to understand, interpret,
>> and respond to natural human language in a way that is both meaningful and useful.'], 'meta': [{'model':
>> 'gpt-5-mini', 'index': 0, 'finish_reason': 'stop', 'usage': {'prompt_tokens': 16,
>> 'completion_tokens': 49, 'total_tokens': 65}}]}
# >> {'replies': ['Natural Language Processing (NLP) is a branch of artificial intelligence that focuses on
# >> the interaction between computers and human language. It involves enabling computers to understand, interpret,
# >> and respond to natural human language in a way that is both meaningful and useful.'], 'meta': [{'model':
# >> 'gpt-5-mini', 'index': 0, 'finish_reason': 'stop', 'usage': {'prompt_tokens': 16,
# >> 'completion_tokens': 49, 'total_tokens': 65}}]}
```
"""

Expand Down
4 changes: 2 additions & 2 deletions haystack/components/joiners/branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ class BranchJoiner:
print(json.loads(result["validator"]["validated"][0].text))


>> {'first_name': 'Peter', 'last_name': 'Parker', 'nationality': 'American', 'name': 'Spider-Man', 'occupation':
>> 'Superhero', 'age': 23, 'location': 'New York City'}
# >> {'first_name': 'Peter', 'last_name': 'Parker', 'nationality': 'American', 'name': 'Spider-Man', 'occupation':
# >> 'Superhero', 'age': 23, 'location': 'New York City'}
```

Note that `BranchJoiner` can manage only one data type at a time. In this case, `BranchJoiner` is created for
Expand Down
2 changes: 1 addition & 1 deletion haystack/components/joiners/string_joiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class StringJoiner:

print(pipeline.run(data={"prompt_builder_1": {"query": string_1}, "prompt_builder_2": {"query": string_2}}))

>> {"string_joiner": {"strings": ["Builder 1: What's Natural Language Processing?", "Builder 2: What is life?"]}}
# >> {"string_joiner": {"strings": ["Builder 1: What's Natural Language Processing?", "Builder 2: What is life?"]}}
```
"""

Expand Down
12 changes: 6 additions & 6 deletions haystack/components/preprocessors/recursive_splitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ class RecursiveDocumentSplitter:
doc = Document(content=text)
doc_chunks = chunker.run([doc])
print(doc_chunks["documents"])
>[
>Document(id=..., content: 'Artificial intelligence (AI) - Introduction\\n\\n', meta: {'original_id': '...', 'split_id': 0, 'split_idx_start': 0, '_split_overlap': []})
>Document(id=..., content: 'AI, in its broadest sense, is intelligence exhibited by machines, particularly computer systems.\\n', meta: {'original_id': '...', 'split_id': 1, 'split_idx_start': 45, '_split_overlap': []})
>Document(id=..., content: 'AI technology is widely used throughout industry, government, and science.', meta: {'original_id': '...', 'split_id': 2, 'split_idx_start': 142, '_split_overlap': []})
>Document(id=..., content: ' Some high-profile applications include advanced web search engines; recommendation systems; interac...', meta: {'original_id': '...', 'split_id': 3, 'split_idx_start': 216, '_split_overlap': []})
>]
# [
# Document(id=..., content: 'Artificial intelligence (AI) - Introduction\\n\\n', meta: {'original_id': '...', 'split_id': 0, 'split_idx_start': 0, '_split_overlap': []})
# Document(id=..., content: 'AI, in its broadest sense, is intelligence exhibited by machines, particularly computer systems.\\n', meta: {'original_id': '...', 'split_id': 1, 'split_idx_start': 45, '_split_overlap': []})
# Document(id=..., content: 'AI technology is widely used throughout industry, government, and science.', meta: {'original_id': '...', 'split_id': 2, 'split_idx_start': 142, '_split_overlap': []})
# Document(id=..., content: ' Some high-profile applications include advanced web search engines; recommendation systems; interac...', meta: {'original_id': '...', 'split_id': 3, 'split_idx_start': 216, '_split_overlap': []})
# ]
```
""" # noqa: E501

Expand Down
2 changes: 1 addition & 1 deletion haystack/components/routers/llm_messages_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class LLMMessagesRouter:
print(router.run([ChatMessage.from_user("How to rob a bank?")]))

# {
# 'chat_generator_text': 'unsafe\nS2',
# 'chat_generator_text': 'unsafe\\nS2',
# 'unsafe': [
# ChatMessage(
# _role=<ChatRole.USER: 'user'>,
Expand Down
1 change: 1 addition & 0 deletions haystack/core/pipeline/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def run( # noqa: PLR0915, PLR0912, C901

retriever = InMemoryBM25Retriever(document_store=document_store)
prompt_builder = PromptBuilder(template=prompt_template)
api_key = "your-openai-api-key"
llm = OpenAIGenerator(api_key=Secret.from_token(api_key))

rag_pipeline = Pipeline()
Expand Down
6 changes: 4 additions & 2 deletions haystack/tools/searchable_toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ class SearchableToolset(Toolset):

# Create a catalog of tools
catalog = [
Tool(name="get_weather", description="Get weather for a city", ...),
Tool(name="search_web", description="Search the web", ...),
Tool(name="get_weather", description="Get weather for a city",
parameters={}, function=lambda: None),
Tool(name="search_web", description="Search the web",
parameters={}, function=lambda: None),
# ... 100s more tools
]
toolset = SearchableToolset(catalog=catalog)
Expand Down
206 changes: 103 additions & 103 deletions haystack/tools/toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,115 +22,115 @@ class Toolset:
to manage and use them as a unit in Haystack pipelines.

Example:
```python
from haystack.tools import Tool, Toolset
from haystack.components.tools import ToolInvoker

# Define math functions
def add_numbers(a: int, b: int) -> int:
return a + b

def subtract_numbers(a: int, b: int) -> int:
return a - b

# Create tools with proper schemas
add_tool = Tool(
name="add",
description="Add two numbers",
parameters={
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"required": ["a", "b"]
},
function=add_numbers
)

subtract_tool = Tool(
name="subtract",
description="Subtract b from a",
parameters={
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"required": ["a", "b"]
},
function=subtract_numbers
)

# Create a toolset with the math tools
math_toolset = Toolset([add_tool, subtract_tool])

# Use the toolset with a ToolInvoker or ChatGenerator component
invoker = ToolInvoker(tools=math_toolset)
```
```python
from haystack.tools import Tool, Toolset
from haystack.components.tools import ToolInvoker

# Define math functions
def add_numbers(a: int, b: int) -> int:
return a + b

def subtract_numbers(a: int, b: int) -> int:
return a - b

# Create tools with proper schemas
add_tool = Tool(
name="add",
description="Add two numbers",
parameters={
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"required": ["a", "b"]
},
function=add_numbers
)

subtract_tool = Tool(
name="subtract",
description="Subtract b from a",
parameters={
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"required": ["a", "b"]
},
function=subtract_numbers
)

# Create a toolset with the math tools
math_toolset = Toolset([add_tool, subtract_tool])

# Use the toolset with a ToolInvoker or ChatGenerator component
invoker = ToolInvoker(tools=math_toolset)
```

2. Base class for dynamic tool loading:
By subclassing Toolset, you can create implementations that dynamically load tools
from external sources like OpenAPI URLs, MCP servers, or other resources.

Example:
```python
from haystack.core.serialization import generate_qualified_class_name
from haystack.tools import Tool, Toolset
from haystack.components.tools import ToolInvoker

class CalculatorToolset(Toolset):
'''A toolset for calculator operations.'''

def __init__(self) -> None:
tools = self._create_tools()
super().__init__(tools)

def _create_tools(self):
# These Tool instances are obviously defined statically and for illustration purposes only.
# In a real-world scenario, you would dynamically load tools from an external source here.
tools = []
add_tool = Tool(
name="add",
description="Add two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=lambda a, b: a + b,
)

multiply_tool = Tool(
name="multiply",
description="Multiply two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=lambda a, b: a * b,
)

tools.append(add_tool)
tools.append(multiply_tool)

return tools

def to_dict(self):
return {
"type": generate_qualified_class_name(type(self)),
"data": {}, # no data to serialize as we define the tools dynamically
}

@classmethod
def from_dict(cls, data):
return cls() # Recreate the tools dynamically during deserialization

# Create the dynamic toolset and use it with ToolInvoker
calculator_toolset = CalculatorToolset()
invoker = ToolInvoker(tools=calculator_toolset)
```
```python
from haystack.core.serialization import generate_qualified_class_name
from haystack.tools import Tool, Toolset
from haystack.components.tools import ToolInvoker

class CalculatorToolset(Toolset):
'''A toolset for calculator operations.'''

def __init__(self) -> None:
tools = self._create_tools()
super().__init__(tools)

def _create_tools(self):
# These Tool instances are obviously defined statically and for illustration purposes only.
# In a real-world scenario, you would dynamically load tools from an external source here.
tools = []
add_tool = Tool(
name="add",
description="Add two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=lambda a, b: a + b,
)

multiply_tool = Tool(
name="multiply",
description="Multiply two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=lambda a, b: a * b,
)

tools.append(add_tool)
tools.append(multiply_tool)

return tools

def to_dict(self):
return {
"type": generate_qualified_class_name(type(self)),
"data": {}, # no data to serialize as we define the tools dynamically
}

@classmethod
def from_dict(cls, data):
return cls() # Recreate the tools dynamically during deserialization

# Create the dynamic toolset and use it with ToolInvoker
calculator_toolset = CalculatorToolset()
invoker = ToolInvoker(tools=calculator_toolset)
```

Toolset implements the collection interface (__iter__, __contains__, __len__, __getitem__),
making it behave like a list of Tools. This makes it compatible with components that expect
Expand Down
Loading