chore: Merge main into v3 (#11573)

Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: NIK-TIGER-BILL <[email protected]>
Co-authored-by: Sebastian Husch Lee <[email protected]>
Co-authored-by: Pragnyan Ramtha <[email protected]>
Co-authored-by: Julian Risch <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Aarkin Karnik <[email protected]>
Co-authored-by: Haystack Bot <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: davidsbatista <[email protected]>
Co-authored-by: bogdankostic <[email protected]>
Co-authored-by: bogdankostic <[email protected]>
Co-authored-by: Sachin Yadav <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Abdulhamid Onawole <[email protected]>
Co-authored-by: David S. Batista <[email protected]>
Co-authored-by: Aditya Raut <[email protected]>
Co-authored-by: Nhat Huy Vu <[email protected]>
Co-authored-by: julian-risch <[email protected]>
Co-authored-by: kota <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: devteamaegis <[email protected]>
Co-authored-by: Ritwij Aryan Parmar <[email protected]>
Co-authored-by: Srivatsa Kamballa <[email protected]>
Co-authored-by: vidigoat <[email protected]>
Co-authored-by: Syed Shahmeer Ali <[email protected]>
Co-authored-by: Mohamed Arbi <[email protected]>
Co-authored-by: Zamuldinov Nikita <[email protected]>
Co-authored-by: Bilge Yücel <[email protected]>
Co-authored-by: jdoughty04 <[email protected]>
Co-authored-by: Govindh Kishore <[email protected]>
This commit is contained in:
Stefano Fiorucci
2026-06-10 14:30:34 +02:00
committed by GitHub
co-authored by Sebastian Husch Lee Pragnyan Ramtha Julian Risch Claude Sonnet 4.6 Aarkin Karnik Haystack Bot dependabot[bot] davidsbatista bogdankostic bogdankostic Sachin Yadav Copilot Autofix powered by AI Abdulhamid Onawole David S. Batista Aditya Raut Nhat Huy Vu julian-risch kota Copilot Autofix powered by AI devteamaegis Ritwij Aryan Parmar Srivatsa Kamballa vidigoat Syed Shahmeer Ali Mohamed Arbi Zamuldinov Nikita Bilge Yücel jdoughty04 Govindh Kishore
parent 1771a300f3
commit f4c02eb523
38 changed files with 1174 additions and 55 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@fbda2eb1bdc90d319b8d853f5deb53bca199a7c1 # v1.0.140
uses: anthropics/claude-code-action@593d7a5c4e0073569f74772c2b7b64c30ec14707 # v1.0.141
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_PR_REVIEWS_PUBLIC_REPOS }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@fbda2eb1bdc90d319b8d853f5deb53bca199a7c1 # v1.0.140
uses: anthropics/claude-code-action@593d7a5c4e0073569f74772c2b7b64c30ec14707 # v1.0.141
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_PR_REVIEWS_PUBLIC_REPOS }}
additional_permissions: |
+7
View File
@@ -35,6 +35,13 @@ repos:
- ruff
- add-trailing-comma
- id: release-note-backticks
name: release-note-backticks
language: python
entry: python scripts/release_note_backticks.py
files: ^releasenotes/notes/.*\.yaml$
types: [text]
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
@@ -17,9 +17,9 @@ Classifies the documents based on the provided labels and adds them to their met
| **Mandatory init variables** | `model`: The name or path of a Hugging Face model for zero shot document classification <br /> <br />`labels`: The set of possible class labels to classify each document into, for example, [`positive`, `negative`]. The labels depend on the selected model. |
| **Mandatory run variables** | `documents`: A list of documents to classify |
| **Output variables** | `documents`: A list of processed documents with an added `classification` metadata field |
| **API reference** | [Classifiers](/reference/classifiers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/classifiers/zero_shot_document_classifier.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -42,11 +42,19 @@ Available models for the task of zero-shot-classification are:
## Usage
Install the `transformers-haystack` package to use the `TransformersZeroShotDocumentClassifier`:
```shell
pip install transformers-haystack
```
### On its own
```python
from haystack import Document
from haystack.components.classifiers import TransformersZeroShotDocumentClassifier
from haystack_integrations.components.classifiers.transformers import (
TransformersZeroShotDocumentClassifier,
)
documents = [
Document(id="0", content="Cats don't get teeth cavities."),
@@ -71,7 +79,9 @@ from haystack import Document
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.core.pipeline import Pipeline
from haystack.components.classifiers import TransformersZeroShotDocumentClassifier
from haystack_integrations.components.classifiers.transformers import (
TransformersZeroShotDocumentClassifier,
)
documents = [
Document(id="0", content="Today was a nice day!"),
@@ -88,8 +98,8 @@ document_classifier = TransformersZeroShotDocumentClassifier(
document_store.write_documents(documents)
pipeline = Pipeline()
pipeline.add_component(retriever, name="retriever")
pipeline.add_component(document_classifier, name="document_classifier")
pipeline.add_component(name="retriever", instance=retriever)
pipeline.add_component(name="document_classifier", instance=document_classifier)
pipeline.connect("retriever", "document_classifier")
queries = ["How was your day today?", "How was your day yesterday?"]
@@ -9,6 +9,12 @@ description: "This component extracts predefined entities out of a piece of text
This component extracts predefined entities out of a piece of text and writes them into documents’ meta field.
:::warning[Deprecated]
`NamedEntityExtractor` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersNamedEntityExtractor`. See [TransformersNamedEntityExtractor](transformersnamedentityextractor.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,102 @@
---
title: "TransformersNamedEntityExtractor"
id: transformersnamedentityextractor
slug: "/transformersnamedentityextractor"
description: "This component extracts predefined entities out of a piece of text and writes them into documents’ meta field."
---
# TransformersNamedEntityExtractor
This component extracts predefined entities out of a piece of text and writes them into documents’ meta field.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | After the [PreProcessor](../preprocessors.mdx) in an indexing pipeline or after a [Retriever](../retrievers.mdx) in a query pipeline |
| **Mandatory init variables** | `model`: Name or path of the model to use |
| **Mandatory run variables** | `documents`: A list of documents |
| **Output variables** | `documents`: A list of documents |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
`TransformersNamedEntityExtractor` looks for entities, which are spans in the text. The extractor automatically recognizes and groups them depending on their class, such as people's names, organizations, locations, and other types. The exact classes are determined by the model that you initialize the component with.
`TransformersNamedEntityExtractor` takes a list of documents as input and returns a list of the same documents with their `meta` data enriched with `NamedEntityAnnotations`. A `NamedEntityAnnotation` consists of the type of the entity, the start and end of the span, and a score calculated by the model, for example: `NamedEntityAnnotation(entity='PER', start=11, end=16, score=0.9)`.
When the `TransformersNamedEntityExtractor` is initialized, you need to set a `model`. Optionally, you can set `pipeline_kwargs`, which are then passed on to the Hugging Face pipeline. You can additionally set the `device` that is used to run the component.
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable.
## Usage
Install the `transformers-haystack` package to use the `TransformersNamedEntityExtractor`:
```shell
pip install transformers-haystack
```
The component works with any Hugging Face model that supports token classification or NER.
`TransformersNamedEntityExtractor` accepts a list of `Documents` as its input. The extractor annotates the raw text in the documents and stores the annotations in the document's `meta` dictionary under the `named_entities` key.
```python
from haystack.dataclasses import Document
from haystack_integrations.components.extractors.transformers import (
TransformersNamedEntityExtractor,
)
extractor = TransformersNamedEntityExtractor(model="dslim/bert-base-NER")
documents = [
Document(content="My name is Clara and I live in Berkeley, California."),
Document(content="I'm Merlin, the happy pig!"),
Document(content="New York State is home to the Empire State Building."),
]
extractor.run(documents)
print(documents)
```
Here is the example result:
```python
[Document(id=aec840d1b6c85609f4f16c3e222a5a25fd8c4c53bd981a40c1268ab9c72cee10, content: 'My name is Clara and I live in Berkeley, California.', meta: {'named_entities': [NamedEntityAnnotation(entity='PER', start=11, end=16, score=0.99641764), NamedEntityAnnotation(entity='LOC', start=31, end=39, score=0.996198), NamedEntityAnnotation(entity='LOC', start=41, end=51, score=0.9990196)]}),
Document(id=98f1dc5d0ccd9d9950cd191d1076db0f7af40c401dd7608f11c90cb3fc38c0c2, content: 'I'm Merlin, the happy pig!', meta: {'named_entities': [NamedEntityAnnotation(entity='PER', start=4, end=10, score=0.99054915)]}),
Document(id=44948ea0eec018b33aceaaedde4616eb9e93ce075e0090ec1613fc145f84b4a9, content: 'New York State is home to the Empire State Building.', meta: {'named_entities': [NamedEntityAnnotation(entity='LOC', start=0, end=14, score=0.9989541), NamedEntityAnnotation(entity='LOC', start=30, end=51, score=0.95746297)]})]
```
### Get stored annotations
This component includes the `get_stored_annotations` helper class method that allows you to retrieve the annotations stored in a `Document` transparently:
```python
from haystack.dataclasses import Document
from haystack_integrations.components.extractors.transformers import (
TransformersNamedEntityExtractor,
)
extractor = TransformersNamedEntityExtractor(model="dslim/bert-base-NER")
documents = [
Document(content="My name is Clara and I live in Berkeley, California."),
Document(content="I'm Merlin, the happy pig!"),
Document(content="New York State is home to the Empire State Building."),
]
extractor.run(documents)
annotations = [
TransformersNamedEntityExtractor.get_stored_annotations(doc) for doc in documents
]
print(annotations)
# If a Document doesn't contain any annotations, this returns None.
new_doc = Document(content="In one of many possible worlds...")
assert TransformersNamedEntityExtractor.get_stored_annotations(new_doc) is None
```
@@ -43,6 +43,18 @@ client = AzureOpenAIChatGenerator(
We recommend using environment variables instead of initialization parameters.
:::
To switch `azure_endpoint` and `api_version` between environments without editing your pipeline, pass a Secret that resolves them from environment variables at runtime:
```python
from haystack.components.generators.chat import AzureOpenAIChatGenerator
from haystack.utils import Secret
client = AzureOpenAIChatGenerator(
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
```
Then, the component needs a list of `ChatMessage` objects to operate. `ChatMessage` is a data class that contains a message, a role (who generated the message, such as `user`, `assistant`, `system`, `function`), and optional metadata. If a string is passed, it is converted into a list containing a single `ChatMessage` with the `user` role. See the [usage](#usage) section for an example.
You can pass any chat completion parameters that are valid for the `openai.ChatCompletion.create` method directly to `AzureOpenAIChatGenerator` using the `generation_kwargs` parameter, both at initialization and to `run()` method. For more details on the supported parameters, refer to the [Azure documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference).
@@ -9,6 +9,12 @@ description: "Provides an interface for chat completion using a Hugging Face mod
Provides an interface for chat completion using a Hugging Face model that runs locally.
:::warning[Deprecated]
`HuggingFaceLocalChatGenerator` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersChatGenerator`. See [TransformersChatGenerator](transformerschatgenerator.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,101 @@
---
title: "TransformersChatGenerator"
id: transformerschatgenerator
slug: "/transformerschatgenerator"
description: "Provides an interface for chat completion using a Hugging Face model that runs locally."
---
# TransformersChatGenerator
Provides an interface for chat completion using a Hugging Face model that runs locally.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | After a [ChatPromptBuilder](../builders/chatpromptbuilder.mdx) |
| **Mandatory init variables** | None |
| **Mandatory run variables** | `messages`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects representing the chat or a plain string |
| **Output variables** | `replies`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects generated by the LLM |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
Keep in mind that if LLMs run locally, you may need a powerful machine to run them. This depends strongly on the model you select and its parameter count.
If a string is passed to `messages`, it is converted into a list containing a single `ChatMessage` with the `user` role.
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable:
```python
generator = TransformersChatGenerator(
token=Secret.from_token("<your-api-key>"),
)
```
### Streaming
This Generator supports [streaming](guides-to-generators/choosing-the-right-generator.mdx#streaming-support) the tokens from the LLM directly in output. To do so, pass a function to the `streaming_callback` init parameter.
## Usage
Install the `transformers-haystack` package to use the `TransformersChatGenerator`:
```shell
pip install transformers-haystack
```
### On its own
```python
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
generator = TransformersChatGenerator(model="Qwen/Qwen3-0.6B")
messages = [ChatMessage.from_user("What's Natural Language Processing? Be brief.")]
print(generator.run(messages))
```
### In a Pipeline
```python
from haystack import Pipeline
from haystack.components.builders.prompt_builder import ChatPromptBuilder
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
from haystack.utils import Secret
prompt_builder = ChatPromptBuilder()
llm = TransformersChatGenerator(
model="Qwen/Qwen3-0.6B",
token=Secret.from_env_var("HF_API_TOKEN"),
)
pipe = Pipeline()
pipe.add_component("prompt_builder", prompt_builder)
pipe.add_component("llm", llm)
pipe.connect("prompt_builder.prompt", "llm.messages")
location = "Berlin"
messages = [
ChatMessage.from_system(
"Always respond in German even if some input data is in other languages.",
),
ChatMessage.from_user("Tell me about {{location}}"),
]
pipe.run(
data={
"prompt_builder": {
"template_variables": {"location": location},
"template": messages,
},
},
)
```
@@ -9,6 +9,12 @@ description: "Use this component in extractive question answering pipelines base
Use this component in extractive question answering pipelines based on a query and a list of documents.
:::warning[Deprecated]
`ExtractiveReader` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersExtractiveReader`. See [TransformersExtractiveReader](transformersextractivereader.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,119 @@
---
title: "TransformersExtractiveReader"
id: transformersextractivereader
slug: "/transformersextractivereader"
description: "Use this component in extractive question answering pipelines based on a query and a list of documents."
---
# TransformersExtractiveReader
Use this component in extractive question answering pipelines based on a query and a list of documents.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | In query pipelines, after a component that returns a list of documents, such as a [Retriever](../retrievers.mdx) |
| **Mandatory init variables** | None |
| **Mandatory run variables** | `documents`: A list of documents <br /> <br />`query`: A query string |
| **Output variables** | `answers`: A list of [`ExtractedAnswer`](../../concepts/data-classes.mdx#extractedanswer) objects |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
`TransformersExtractiveReader` locates and extracts answers to a given query from the document text. It's used in extractive QA systems where you want to know exactly where the answer is located within the document. It's usually coupled with a Retriever that precedes it, but you can also use it with other components that fetch documents.
Readers assign a _probability_ to answers. This score ranges from 0 to 1, indicating how well the results the Reader returned match the query. Probability closest to 1 means the model has high confidence in the answer's relevance. The Reader sorts the answers based on their probability scores, with higher probability listed first. You can limit the number of answers the Reader returns in the optional `top_k` parameter.
You can use the probability to set the quality expectations for your system. To do that, use the `confidence_score` parameter of the Reader to set a minimum probability threshold for answers. For example, setting `confidence_threshold` to `0.7` means only answers with a probability higher than 0.7 will be returned.
By default, the Reader includes a scenario where no answer to the query is found in the document text (`no_answer=True`). In this case, it returns an additional `ExtractedAnswer` with no text and the probability that none of the `top_k` answers are correct. For example, if `top_k=4` the system will return four answers and an additional empty one. Each answer has a probability assigned. If the empty answer has a probability of 0.5, it means that's the probability that none of the returned answers is correct. To receive only the actual top_k answers, set the `no_answer` parameter to `False` when initializing the component.
### Models
Here are the models that we recommend for using with `TransformersExtractiveReader`:
| | | |
| --- | --- | --- |
| Model URL | Description | Language |
| [deepset/roberta-base-squad2-distilled](https://huggingface.co/deepset/roberta-base-squad2-distilled) (default) | A distilled model, relatively fast and with good performance. | English |
| [deepset/roberta-large-squad2](https://huggingface.co/deepset/roberta-large-squad2) | A large model with good performance. Slower than the distilled one. | English |
| [deepset/tinyroberta-squad2](https://huggingface.co/deepset/tinyroberta-squad2) | A distilled version of roberta-large-squad2 model, very fast. | English |
| [deepset/xlm-roberta-base-squad2](https://huggingface.co/deepset/xlm-roberta-base-squad2) | A base multilingual model with good speed and performance. | Multilingual |
You can also view other question answering models on [Hugging Face](https://huggingface.co/models?pipeline_tag=question-answering).
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable.
## Usage
Install the `transformers-haystack` package to use the `TransformersExtractiveReader`:
```shell
pip install transformers-haystack
```
### On its own
Below is an example that uses the `TransformersExtractiveReader` outside of a pipeline. The Reader gets the query and the documents at runtime. It should return two answers and an additional third answer with no text and the probability that the `top_k` answers are incorrect.
```python
from haystack import Document
from haystack_integrations.components.readers.transformers import (
TransformersExtractiveReader,
)
docs = [
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
]
reader = TransformersExtractiveReader()
reader.run(query="What is the capital of France?", documents=docs, top_k=2)
```
### In a pipeline
Below is an example of a pipeline that retrieves a document from an `InMemoryDocumentStore` based on keyword search (using `InMemoryBM25Retriever`). It then uses the `TransformersExtractiveReader` to extract the answer to our query from the top retrieved documents.
With the TransformersExtractiveReader’s `top_k` set to 2, an additional, third answer with no text and the probability that the other `top_k` answers are incorrect is also returned.
```python
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack_integrations.components.readers.transformers import (
TransformersExtractiveReader,
)
docs = [
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
Document(content="Rome is the capital of Italy."),
Document(content="Madrid is the capital of Spain."),
]
document_store = InMemoryDocumentStore()
document_store.write_documents(docs)
retriever = InMemoryBM25Retriever(document_store=document_store)
reader = TransformersExtractiveReader()
extractive_qa_pipeline = Pipeline()
extractive_qa_pipeline.add_component(instance=retriever, name="retriever")
extractive_qa_pipeline.add_component(instance=reader, name="reader")
extractive_qa_pipeline.connect("retriever.documents", "reader.documents")
query = "What is the capital of France?"
extractive_qa_pipeline.run(
data={
"retriever": {"query": query, "top_k": 3},
"reader": {"query": query, "top_k": 2},
},
)
```
@@ -17,9 +17,9 @@ Use this component to route text input to various output connections based on a
| **Mandatory init variables** | `model`: The name or path of a Hugging Face model for text classification <br /> <br />`token`: The Hugging Face API token. Can be set with `HF_API_TOKEN` or `HF_TOKEN` env var. |
| **Mandatory run variables** | `text`: The text to be routed to one of the specified outputs based on which label it has been categorized into |
| **Output variables** | `documents`: A dictionary with the label as key and the text as value |
| **API reference** | [Routers](/reference/routers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/transformers_text_router.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -31,10 +31,16 @@ First, you need to set a selected model with a `model` parameter when initializi
You can additionally provide the `labels` parameter – a list of strings of possible class labels to classify each sequence into. If not provided, the component fetches the labels from the model configuration file hosted on the HuggingFace Hub using `transformers.AutoConfig.from_pretrained`.
To see the full list of parameters, check out our [API reference](/reference/routers-api#transformerstextrouter).
To see the full list of parameters, check out our [API reference](/reference/integrations-transformers#transformerstextrouter).
## Usage
Install the `transformers-haystack` package to use the `TransformersTextRouter`:
```shell
pip install transformers-haystack
```
### On its own
The `TransformersTextRouter` isn’t very effective on its own, as its main strength lies in working within a pipeline. The component's true potential is unlocked when it is integrated into a pipeline, where it can efficiently route text to the most appropriate components. Please see the following section for a complete example of usage.
@@ -45,9 +51,11 @@ Below is an example of a simple pipeline that routes English queries to a Text G
```python
from haystack import Pipeline
from haystack.components.routers import TransformersTextRouter
from haystack_integrations.components.routers.transformers import TransformersTextRouter
from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
from haystack.components.generators.chat import HuggingFaceLocalChatGenerator
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
p = Pipeline()
@@ -73,13 +81,13 @@ p.add_component(
name="german_prompt_builder",
)
p.add_component(
instance=HuggingFaceLocalChatGenerator(
instance=TransformersChatGenerator(
model="DiscoResearch/Llama3-DiscoLeo-Instruct-8B-v0.1",
),
name="german_llm",
)
p.add_component(
instance=HuggingFaceLocalChatGenerator(model="microsoft/Phi-3-mini-4k-instruct"),
instance=TransformersChatGenerator(model="microsoft/Phi-3-mini-4k-instruct"),
name="english_llm",
)
@@ -17,9 +17,9 @@ Use this component to route text input to various output connections based on it
| **Mandatory init variables** | `labels`: A list of labels for classification <br /> <br />`token`: The Hugging Face API token. Can be set with `HF_API_TOKEN` or `HF_TOKEN` env var. |
| **Mandatory run variables** | `text`: The text to be routed to one of the specified outputs based on which label it has been categorized into |
| **Output variables** | `documents`: A dictionary with the label as key and the text as value |
| **API reference** | [Routers](/reference/routers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/zero_shot_text_router.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -31,10 +31,16 @@ Use this component to route text input to various output connections based on it
To use `TransformersZeroShotTextRouter`, you need to provide the mandatory `labels` parameter – a list of strings of possible class labels to classify each sequence into.
To see the full list of parameters, check out our [API reference](/reference/routers-api#transformerszeroshottextrouter).
To see the full list of parameters, check out our [API reference](/reference/integrations-transformers#transformerszeroshottextrouter).
## Usage
Install the `transformers-haystack` package to use the `TransformersZeroShotTextRouter`:
```shell
pip install transformers-haystack
```
### On its own
The `TransformersZeroShotTextRouter` isn’t very effective on its own, as its main strength lies in working within a pipeline. The component's true potential is unlocked when it is integrated into a pipeline, where it can efficiently route text to the most appropriate components. Please see the following section for a complete example of usage.
@@ -53,7 +59,7 @@ Finally, the pipeline is executed with a sample text: "What is the capital of Ge
from haystack import Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.core.pipeline import Pipeline
from haystack.components.routers import TransformersZeroShotTextRouter
from haystack_integrations.components.routers.transformers import TransformersZeroShotTextRouter
from haystack.components.embedders import SentenceTransformersTextEmbedder, SentenceTransformersDocumentEmbedder
from haystack.components.retrievers import InMemoryEmbeddingRetriever
@@ -246,8 +246,8 @@ for the full list.
```python
__init__(
azure_endpoint: str | None = None,
api_version: str | None = "2024-12-01-preview",
azure_endpoint: str | Secret | None = None,
api_version: str | Secret | None = "2024-12-01-preview",
azure_deployment: str | None = "gpt-4.1-mini",
api_key: Secret | None = Secret.from_env_var(
"AZURE_OPENAI_API_KEY", strict=False
@@ -275,8 +275,15 @@ Initialize the Azure OpenAI Chat Generator component.
**Parameters:**
- **azure_endpoint** (<code>str | None</code>) – The endpoint of the deployed model, for example `"https://example-resource.azure.openai.com/"`.
- **api_version** (<code>str | None</code>) – The version of the API to use. Defaults to 2024-12-01-preview.
- **azure_endpoint** (<code>str | Secret | None</code>) – The endpoint of the deployed model, for example `"https://example-resource.azure.openai.com/"`.
Can also be a [Secret](https://docs.haystack.deepset.ai/docs/secret-management), for example
`Secret.from_env_var("AZURE_OPENAI_ENDPOINT")`, to resolve the value from an environment variable at
runtime. This is useful to switch endpoints between environments (e.g. dev and prod) without changing the
serialized pipeline.
- **api_version** (<code>str | Secret | None</code>) – The version of the API to use. Defaults to 2024-12-01-preview.
Can also be a [Secret](https://docs.haystack.deepset.ai/docs/secret-management), for example
`Secret.from_env_var("AZURE_OPENAI_API_VERSION")`, to resolve the value from an environment variable at
runtime.
- **azure_deployment** (<code>str | None</code>) – The deployment of the model, usually the model name.
- **api_key** (<code>Secret | None</code>) – The API key to use for authentication.
- **azure_ad_token** (<code>Secret | None</code>) – [Azure Active Directory token](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id).
+3
View File
@@ -367,6 +367,7 @@ export default {
'pipeline-components/extractors/namedentityextractor',
'pipeline-components/extractors/presidioentityextractor',
'pipeline-components/extractors/regextextextractor',
'pipeline-components/extractors/transformersnamedentityextractor',
],
},
{
@@ -437,6 +438,7 @@ export default {
'pipeline-components/generators/stackitchatgenerator',
'pipeline-components/generators/togetheraichatgenerator',
'pipeline-components/generators/togetheraigenerator',
'pipeline-components/generators/transformerschatgenerator',
'pipeline-components/generators/vertexaicodegenerator',
'pipeline-components/generators/vertexaigeminichatgenerator',
'pipeline-components/generators/vertexaigeminigenerator',
@@ -536,6 +538,7 @@ export default {
},
items: [
'pipeline-components/readers/extractivereader',
'pipeline-components/readers/transformersextractivereader',
],
},
{
@@ -17,9 +17,9 @@ Classifies the documents based on the provided labels and adds them to their met
| **Mandatory init variables** | `model`: The name or path of a Hugging Face model for zero shot document classification <br /> <br />`labels`: The set of possible class labels to classify each document into, for example, [`positive`, `negative`]. The labels depend on the selected model. |
| **Mandatory run variables** | `documents`: A list of documents to classify |
| **Output variables** | `documents`: A list of processed documents with an added `classification` metadata field |
| **API reference** | [Classifiers](/reference/classifiers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/classifiers/zero_shot_document_classifier.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -42,11 +42,19 @@ Available models for the task of zero-shot-classification are:
## Usage
Install the `transformers-haystack` package to use the `TransformersZeroShotDocumentClassifier`:
```shell
pip install transformers-haystack
```
### On its own
```python
from haystack import Document
from haystack.components.classifiers import TransformersZeroShotDocumentClassifier
from haystack_integrations.components.classifiers.transformers import (
TransformersZeroShotDocumentClassifier,
)
documents = [
Document(id="0", content="Cats don't get teeth cavities."),
@@ -71,7 +79,9 @@ from haystack import Document
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.core.pipeline import Pipeline
from haystack.components.classifiers import TransformersZeroShotDocumentClassifier
from haystack_integrations.components.classifiers.transformers import (
TransformersZeroShotDocumentClassifier,
)
documents = [
Document(id="0", content="Today was a nice day!"),
@@ -88,8 +98,8 @@ document_classifier = TransformersZeroShotDocumentClassifier(
document_store.write_documents(documents)
pipeline = Pipeline()
pipeline.add_component(retriever, name="retriever")
pipeline.add_component(document_classifier, name="document_classifier")
pipeline.add_component(name="retriever", instance=retriever)
pipeline.add_component(name="document_classifier", instance=document_classifier)
pipeline.connect("retriever", "document_classifier")
queries = ["How was your day today?", "How was your day yesterday?"]
@@ -9,6 +9,12 @@ description: "This component extracts predefined entities out of a piece of text
This component extracts predefined entities out of a piece of text and writes them into documents’ meta field.
:::warning[Deprecated]
`NamedEntityExtractor` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersNamedEntityExtractor`. See [TransformersNamedEntityExtractor](transformersnamedentityextractor.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,102 @@
---
title: "TransformersNamedEntityExtractor"
id: transformersnamedentityextractor
slug: "/transformersnamedentityextractor"
description: "This component extracts predefined entities out of a piece of text and writes them into documents’ meta field."
---
# TransformersNamedEntityExtractor
This component extracts predefined entities out of a piece of text and writes them into documents’ meta field.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | After the [PreProcessor](../preprocessors.mdx) in an indexing pipeline or after a [Retriever](../retrievers.mdx) in a query pipeline |
| **Mandatory init variables** | `model`: Name or path of the model to use |
| **Mandatory run variables** | `documents`: A list of documents |
| **Output variables** | `documents`: A list of documents |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
`TransformersNamedEntityExtractor` looks for entities, which are spans in the text. The extractor automatically recognizes and groups them depending on their class, such as people's names, organizations, locations, and other types. The exact classes are determined by the model that you initialize the component with.
`TransformersNamedEntityExtractor` takes a list of documents as input and returns a list of the same documents with their `meta` data enriched with `NamedEntityAnnotations`. A `NamedEntityAnnotation` consists of the type of the entity, the start and end of the span, and a score calculated by the model, for example: `NamedEntityAnnotation(entity='PER', start=11, end=16, score=0.9)`.
When the `TransformersNamedEntityExtractor` is initialized, you need to set a `model`. Optionally, you can set `pipeline_kwargs`, which are then passed on to the Hugging Face pipeline. You can additionally set the `device` that is used to run the component.
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable.
## Usage
Install the `transformers-haystack` package to use the `TransformersNamedEntityExtractor`:
```shell
pip install transformers-haystack
```
The component works with any Hugging Face model that supports token classification or NER.
`TransformersNamedEntityExtractor` accepts a list of `Documents` as its input. The extractor annotates the raw text in the documents and stores the annotations in the document's `meta` dictionary under the `named_entities` key.
```python
from haystack.dataclasses import Document
from haystack_integrations.components.extractors.transformers import (
TransformersNamedEntityExtractor,
)
extractor = TransformersNamedEntityExtractor(model="dslim/bert-base-NER")
documents = [
Document(content="My name is Clara and I live in Berkeley, California."),
Document(content="I'm Merlin, the happy pig!"),
Document(content="New York State is home to the Empire State Building."),
]
extractor.run(documents)
print(documents)
```
Here is the example result:
```python
[Document(id=aec840d1b6c85609f4f16c3e222a5a25fd8c4c53bd981a40c1268ab9c72cee10, content: 'My name is Clara and I live in Berkeley, California.', meta: {'named_entities': [NamedEntityAnnotation(entity='PER', start=11, end=16, score=0.99641764), NamedEntityAnnotation(entity='LOC', start=31, end=39, score=0.996198), NamedEntityAnnotation(entity='LOC', start=41, end=51, score=0.9990196)]}),
Document(id=98f1dc5d0ccd9d9950cd191d1076db0f7af40c401dd7608f11c90cb3fc38c0c2, content: 'I'm Merlin, the happy pig!', meta: {'named_entities': [NamedEntityAnnotation(entity='PER', start=4, end=10, score=0.99054915)]}),
Document(id=44948ea0eec018b33aceaaedde4616eb9e93ce075e0090ec1613fc145f84b4a9, content: 'New York State is home to the Empire State Building.', meta: {'named_entities': [NamedEntityAnnotation(entity='LOC', start=0, end=14, score=0.9989541), NamedEntityAnnotation(entity='LOC', start=30, end=51, score=0.95746297)]})]
```
### Get stored annotations
This component includes the `get_stored_annotations` helper class method that allows you to retrieve the annotations stored in a `Document` transparently:
```python
from haystack.dataclasses import Document
from haystack_integrations.components.extractors.transformers import (
TransformersNamedEntityExtractor,
)
extractor = TransformersNamedEntityExtractor(model="dslim/bert-base-NER")
documents = [
Document(content="My name is Clara and I live in Berkeley, California."),
Document(content="I'm Merlin, the happy pig!"),
Document(content="New York State is home to the Empire State Building."),
]
extractor.run(documents)
annotations = [
TransformersNamedEntityExtractor.get_stored_annotations(doc) for doc in documents
]
print(annotations)
# If a Document doesn't contain any annotations, this returns None.
new_doc = Document(content="In one of many possible worlds...")
assert TransformersNamedEntityExtractor.get_stored_annotations(new_doc) is None
```
@@ -43,6 +43,18 @@ client = AzureOpenAIChatGenerator(
We recommend using environment variables instead of initialization parameters.
:::
To switch `azure_endpoint` and `api_version` between environments without editing your pipeline, pass a Secret that resolves them from environment variables at runtime:
```python
from haystack.components.generators.chat import AzureOpenAIChatGenerator
from haystack.utils import Secret
client = AzureOpenAIChatGenerator(
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
```
Then, the component needs a list of `ChatMessage` objects to operate. `ChatMessage` is a data class that contains a message, a role (who generated the message, such as `user`, `assistant`, `system`, `function`), and optional metadata. If a string is passed, it is converted into a list containing a single `ChatMessage` with the `user` role. See the [usage](#usage) section for an example.
You can pass any chat completion parameters that are valid for the `openai.ChatCompletion.create` method directly to `AzureOpenAIChatGenerator` using the `generation_kwargs` parameter, both at initialization and to `run()` method. For more details on the supported parameters, refer to the [Azure documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference).
@@ -9,6 +9,12 @@ description: "Provides an interface for chat completion using a Hugging Face mod
Provides an interface for chat completion using a Hugging Face model that runs locally.
:::warning[Deprecated]
`HuggingFaceLocalChatGenerator` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersChatGenerator`. See [TransformersChatGenerator](transformerschatgenerator.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,101 @@
---
title: "TransformersChatGenerator"
id: transformerschatgenerator
slug: "/transformerschatgenerator"
description: "Provides an interface for chat completion using a Hugging Face model that runs locally."
---
# TransformersChatGenerator
Provides an interface for chat completion using a Hugging Face model that runs locally.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | After a [ChatPromptBuilder](../builders/chatpromptbuilder.mdx) |
| **Mandatory init variables** | None |
| **Mandatory run variables** | `messages`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects representing the chat or a plain string |
| **Output variables** | `replies`: A list of [`ChatMessage`](../../concepts/data-classes/chatmessage.mdx) objects generated by the LLM |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
Keep in mind that if LLMs run locally, you may need a powerful machine to run them. This depends strongly on the model you select and its parameter count.
If a string is passed to `messages`, it is converted into a list containing a single `ChatMessage` with the `user` role.
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable:
```python
generator = TransformersChatGenerator(
token=Secret.from_token("<your-api-key>"),
)
```
### Streaming
This Generator supports [streaming](guides-to-generators/choosing-the-right-generator.mdx#streaming-support) the tokens from the LLM directly in output. To do so, pass a function to the `streaming_callback` init parameter.
## Usage
Install the `transformers-haystack` package to use the `TransformersChatGenerator`:
```shell
pip install transformers-haystack
```
### On its own
```python
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
generator = TransformersChatGenerator(model="Qwen/Qwen3-0.6B")
messages = [ChatMessage.from_user("What's Natural Language Processing? Be brief.")]
print(generator.run(messages))
```
### In a Pipeline
```python
from haystack import Pipeline
from haystack.components.builders.prompt_builder import ChatPromptBuilder
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
from haystack.utils import Secret
prompt_builder = ChatPromptBuilder()
llm = TransformersChatGenerator(
model="Qwen/Qwen3-0.6B",
token=Secret.from_env_var("HF_API_TOKEN"),
)
pipe = Pipeline()
pipe.add_component("prompt_builder", prompt_builder)
pipe.add_component("llm", llm)
pipe.connect("prompt_builder.prompt", "llm.messages")
location = "Berlin"
messages = [
ChatMessage.from_system(
"Always respond in German even if some input data is in other languages.",
),
ChatMessage.from_user("Tell me about {{location}}"),
]
pipe.run(
data={
"prompt_builder": {
"template_variables": {"location": location},
"template": messages,
},
},
)
```
@@ -9,6 +9,12 @@ description: "Use this component in extractive question answering pipelines base
Use this component in extractive question answering pipelines based on a query and a list of documents.
:::warning[Deprecated]
`ExtractiveReader` is deprecated and will be removed in Haystack 3.0. It has moved to the `transformers-haystack` package and was renamed to `TransformersExtractiveReader`. See [TransformersExtractiveReader](transformersextractivereader.mdx) for the updated documentation.
:::
<div className="key-value-table">
| | |
@@ -0,0 +1,119 @@
---
title: "TransformersExtractiveReader"
id: transformersextractivereader
slug: "/transformersextractivereader"
description: "Use this component in extractive question answering pipelines based on a query and a list of documents."
---
# TransformersExtractiveReader
Use this component in extractive question answering pipelines based on a query and a list of documents.
<div className="key-value-table">
| | |
| --- | --- |
| **Most common position in a pipeline** | In query pipelines, after a component that returns a list of documents, such as a [Retriever](../retrievers.mdx) |
| **Mandatory init variables** | None |
| **Mandatory run variables** | `documents`: A list of documents <br /> <br />`query`: A query string |
| **Output variables** | `answers`: A list of [`ExtractedAnswer`](../../concepts/data-classes.mdx#extractedanswer) objects |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
## Overview
`TransformersExtractiveReader` locates and extracts answers to a given query from the document text. It's used in extractive QA systems where you want to know exactly where the answer is located within the document. It's usually coupled with a Retriever that precedes it, but you can also use it with other components that fetch documents.
Readers assign a _probability_ to answers. This score ranges from 0 to 1, indicating how well the results the Reader returned match the query. Probability closest to 1 means the model has high confidence in the answer's relevance. The Reader sorts the answers based on their probability scores, with higher probability listed first. You can limit the number of answers the Reader returns in the optional `top_k` parameter.
You can use the probability to set the quality expectations for your system. To do that, use the `confidence_score` parameter of the Reader to set a minimum probability threshold for answers. For example, setting `confidence_threshold` to `0.7` means only answers with a probability higher than 0.7 will be returned.
By default, the Reader includes a scenario where no answer to the query is found in the document text (`no_answer=True`). In this case, it returns an additional `ExtractedAnswer` with no text and the probability that none of the `top_k` answers are correct. For example, if `top_k=4` the system will return four answers and an additional empty one. Each answer has a probability assigned. If the empty answer has a probability of 0.5, it means that's the probability that none of the returned answers is correct. To receive only the actual top_k answers, set the `no_answer` parameter to `False` when initializing the component.
### Models
Here are the models that we recommend for using with `TransformersExtractiveReader`:
| | | |
| --- | --- | --- |
| Model URL | Description | Language |
| [deepset/roberta-base-squad2-distilled](https://huggingface.co/deepset/roberta-base-squad2-distilled) (default) | A distilled model, relatively fast and with good performance. | English |
| [deepset/roberta-large-squad2](https://huggingface.co/deepset/roberta-large-squad2) | A large model with good performance. Slower than the distilled one. | English |
| [deepset/tinyroberta-squad2](https://huggingface.co/deepset/tinyroberta-squad2) | A distilled version of roberta-large-squad2 model, very fast. | English |
| [deepset/xlm-roberta-base-squad2](https://huggingface.co/deepset/xlm-roberta-base-squad2) | A base multilingual model with good speed and performance. | Multilingual |
You can also view other question answering models on [Hugging Face](https://huggingface.co/models?pipeline_tag=question-answering).
Authentication with a Hugging Face API token is only required to access private or gated models. You can pass the token at initialization with `token`, or set the `HF_API_TOKEN` or `HF_TOKEN` environment variable.
## Usage
Install the `transformers-haystack` package to use the `TransformersExtractiveReader`:
```shell
pip install transformers-haystack
```
### On its own
Below is an example that uses the `TransformersExtractiveReader` outside of a pipeline. The Reader gets the query and the documents at runtime. It should return two answers and an additional third answer with no text and the probability that the `top_k` answers are incorrect.
```python
from haystack import Document
from haystack_integrations.components.readers.transformers import (
TransformersExtractiveReader,
)
docs = [
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
]
reader = TransformersExtractiveReader()
reader.run(query="What is the capital of France?", documents=docs, top_k=2)
```
### In a pipeline
Below is an example of a pipeline that retrieves a document from an `InMemoryDocumentStore` based on keyword search (using `InMemoryBM25Retriever`). It then uses the `TransformersExtractiveReader` to extract the answer to our query from the top retrieved documents.
With the TransformersExtractiveReader’s `top_k` set to 2, an additional, third answer with no text and the probability that the other `top_k` answers are incorrect is also returned.
```python
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack_integrations.components.readers.transformers import (
TransformersExtractiveReader,
)
docs = [
Document(content="Paris is the capital of France."),
Document(content="Berlin is the capital of Germany."),
Document(content="Rome is the capital of Italy."),
Document(content="Madrid is the capital of Spain."),
]
document_store = InMemoryDocumentStore()
document_store.write_documents(docs)
retriever = InMemoryBM25Retriever(document_store=document_store)
reader = TransformersExtractiveReader()
extractive_qa_pipeline = Pipeline()
extractive_qa_pipeline.add_component(instance=retriever, name="retriever")
extractive_qa_pipeline.add_component(instance=reader, name="reader")
extractive_qa_pipeline.connect("retriever.documents", "reader.documents")
query = "What is the capital of France?"
extractive_qa_pipeline.run(
data={
"retriever": {"query": query, "top_k": 3},
"reader": {"query": query, "top_k": 2},
},
)
```
@@ -17,9 +17,9 @@ Use this component to route text input to various output connections based on a
| **Mandatory init variables** | `model`: The name or path of a Hugging Face model for text classification <br /> <br />`token`: The Hugging Face API token. Can be set with `HF_API_TOKEN` or `HF_TOKEN` env var. |
| **Mandatory run variables** | `text`: The text to be routed to one of the specified outputs based on which label it has been categorized into |
| **Output variables** | `documents`: A dictionary with the label as key and the text as value |
| **API reference** | [Routers](/reference/routers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/transformers_text_router.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -31,10 +31,16 @@ First, you need to set a selected model with a `model` parameter when initializi
You can additionally provide the `labels` parameter – a list of strings of possible class labels to classify each sequence into. If not provided, the component fetches the labels from the model configuration file hosted on the HuggingFace Hub using `transformers.AutoConfig.from_pretrained`.
To see the full list of parameters, check out our [API reference](/reference/routers-api#transformerstextrouter).
To see the full list of parameters, check out our [API reference](/reference/integrations-transformers#transformerstextrouter).
## Usage
Install the `transformers-haystack` package to use the `TransformersTextRouter`:
```shell
pip install transformers-haystack
```
### On its own
The `TransformersTextRouter` isn’t very effective on its own, as its main strength lies in working within a pipeline. The component's true potential is unlocked when it is integrated into a pipeline, where it can efficiently route text to the most appropriate components. Please see the following section for a complete example of usage.
@@ -45,9 +51,11 @@ Below is an example of a simple pipeline that routes English queries to a Text G
```python
from haystack import Pipeline
from haystack.components.routers import TransformersTextRouter
from haystack_integrations.components.routers.transformers import TransformersTextRouter
from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
from haystack.components.generators.chat import HuggingFaceLocalChatGenerator
from haystack_integrations.components.generators.transformers import (
TransformersChatGenerator,
)
from haystack.dataclasses import ChatMessage
p = Pipeline()
@@ -73,13 +81,13 @@ p.add_component(
name="german_prompt_builder",
)
p.add_component(
instance=HuggingFaceLocalChatGenerator(
instance=TransformersChatGenerator(
model="DiscoResearch/Llama3-DiscoLeo-Instruct-8B-v0.1",
),
name="german_llm",
)
p.add_component(
instance=HuggingFaceLocalChatGenerator(model="microsoft/Phi-3-mini-4k-instruct"),
instance=TransformersChatGenerator(model="microsoft/Phi-3-mini-4k-instruct"),
name="english_llm",
)
@@ -17,9 +17,9 @@ Use this component to route text input to various output connections based on it
| **Mandatory init variables** | `labels`: A list of labels for classification <br /> <br />`token`: The Hugging Face API token. Can be set with `HF_API_TOKEN` or `HF_TOKEN` env var. |
| **Mandatory run variables** | `text`: The text to be routed to one of the specified outputs based on which label it has been categorized into |
| **Output variables** | `documents`: A dictionary with the label as key and the text as value |
| **API reference** | [Routers](/reference/routers-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/zero_shot_text_router.py |
| **Package name** | `haystack-ai` |
| **API reference** | [Transformers](/reference/integrations-transformers) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/transformers |
| **Package name** | `transformers-haystack` |
</div>
@@ -31,10 +31,16 @@ Use this component to route text input to various output connections based on it
To use `TransformersZeroShotTextRouter`, you need to provide the mandatory `labels` parameter – a list of strings of possible class labels to classify each sequence into.
To see the full list of parameters, check out our [API reference](/reference/routers-api#transformerszeroshottextrouter).
To see the full list of parameters, check out our [API reference](/reference/integrations-transformers#transformerszeroshottextrouter).
## Usage
Install the `transformers-haystack` package to use the `TransformersZeroShotTextRouter`:
```shell
pip install transformers-haystack
```
### On its own
The `TransformersZeroShotTextRouter` isn’t very effective on its own, as its main strength lies in working within a pipeline. The component's true potential is unlocked when it is integrated into a pipeline, where it can efficiently route text to the most appropriate components. Please see the following section for a complete example of usage.
@@ -53,7 +59,7 @@ Finally, the pipeline is executed with a sample text: "What is the capital of Ge
from haystack import Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.core.pipeline import Pipeline
from haystack.components.routers import TransformersZeroShotTextRouter
from haystack_integrations.components.routers.transformers import TransformersZeroShotTextRouter
from haystack.components.embedders import SentenceTransformersTextEmbedder, SentenceTransformersDocumentEmbedder
from haystack.components.retrievers import InMemoryEmbeddingRetriever
@@ -362,7 +362,8 @@
"pipeline-components/extractors/llmmetadataextractor",
"pipeline-components/extractors/namedentityextractor",
"pipeline-components/extractors/presidioentityextractor",
"pipeline-components/extractors/regextextextractor"
"pipeline-components/extractors/regextextextractor",
"pipeline-components/extractors/transformersnamedentityextractor"
]
},
{
@@ -433,6 +434,7 @@
"pipeline-components/generators/stackitchatgenerator",
"pipeline-components/generators/togetheraichatgenerator",
"pipeline-components/generators/togetheraigenerator",
"pipeline-components/generators/transformerschatgenerator",
"pipeline-components/generators/vertexaicodegenerator",
"pipeline-components/generators/vertexaigeminichatgenerator",
"pipeline-components/generators/vertexaigeminigenerator",
@@ -531,7 +533,8 @@
"id": "pipeline-components/readers"
},
"items": [
"pipeline-components/readers/extractivereader"
"pipeline-components/readers/extractivereader",
"pipeline-components/readers/transformersextractivereader"
]
},
{
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
import warnings
from dataclasses import replace
from typing import Any
@@ -112,6 +113,15 @@ class TransformersZeroShotDocumentClassifier:
Dictionary containing keyword arguments used to initialize the
Hugging Face pipeline for text classification.
"""
warnings.warn(
"`TransformersZeroShotDocumentClassifier` will be removed from Haystack in version 3.0, as it is moving "
"to the `transformers-haystack` package. To continue using it, install that package with "
"`pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.classifiers.transformers import "
"TransformersZeroShotDocumentClassifier`.",
FutureWarning,
stacklevel=2,
)
torch_and_transformers_import.check()
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
import warnings
from abc import ABC, abstractmethod
from collections.abc import Iterator
from contextlib import contextmanager
@@ -136,6 +137,15 @@ class NamedEntityExtractor:
:param token:
The API token to download private models from Hugging Face.
"""
warnings.warn(
"`NamedEntityExtractor` will be removed from Haystack in version 3.0, as it is moving to the "
"`transformers-haystack` package and being renamed to `TransformersNamedEntityExtractor`. "
"To continue using it, install that package with "
"`pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.extractors.transformers import TransformersNamedEntityExtractor`.",
FutureWarning,
stacklevel=2,
)
if isinstance(backend, str):
backend = NamedEntityExtractorBackend.from_str(backend)
+22 -7
View File
@@ -111,8 +111,8 @@ class AzureOpenAIChatGenerator(OpenAIChatGenerator):
# ruff: noqa: PLR0913
def __init__(
self,
azure_endpoint: str | None = None,
api_version: str | None = "2024-12-01-preview",
azure_endpoint: str | Secret | None = None,
api_version: str | Secret | None = "2024-12-01-preview",
azure_deployment: str | None = "gpt-4.1-mini",
api_key: Secret | None = Secret.from_env_var("AZURE_OPENAI_API_KEY", strict=False),
azure_ad_token: Secret | None = Secret.from_env_var("AZURE_OPENAI_AD_TOKEN", strict=False),
@@ -132,7 +132,14 @@ class AzureOpenAIChatGenerator(OpenAIChatGenerator):
Initialize the Azure OpenAI Chat Generator component.
:param azure_endpoint: The endpoint of the deployed model, for example `"https://example-resource.azure.openai.com/"`.
Can also be a [Secret](https://docs.haystack.deepset.ai/docs/secret-management), for example
`Secret.from_env_var("AZURE_OPENAI_ENDPOINT")`, to resolve the value from an environment variable at
runtime. This is useful to switch endpoints between environments (e.g. dev and prod) without changing the
serialized pipeline.
:param api_version: The version of the API to use. Defaults to 2024-12-01-preview.
Can also be a [Secret](https://docs.haystack.deepset.ai/docs/secret-management), for example
`Secret.from_env_var("AZURE_OPENAI_API_VERSION")`, to resolve the value from an environment variable at
runtime.
:param azure_deployment: The deployment of the model, usually the model name.
:param api_key: The API key to use for authentication.
:param azure_ad_token: [Azure Active Directory token](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id).
@@ -194,8 +201,14 @@ class AzureOpenAIChatGenerator(OpenAIChatGenerator):
# None init parameters. This way we accommodate the use case where env var AZURE_OPENAI_ENDPOINT is set instead
# of passing it as a parameter.
azure_endpoint = azure_endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT")
if not azure_endpoint:
# `azure_endpoint` and `api_version` accept either a plain string or a `Secret`. We keep the original value
# on the instance for serialization and resolve it to a string only when building the client.
resolved_azure_endpoint = (
azure_endpoint.resolve_value() if isinstance(azure_endpoint, Secret) else azure_endpoint
)
if not resolved_azure_endpoint:
raise ValueError("Please provide an Azure endpoint or set the environment variable AZURE_OPENAI_ENDPOINT.")
resolved_api_version = api_version.resolve_value() if isinstance(api_version, Secret) else api_version
if api_key is None and azure_ad_token is None:
raise ValueError("Please provide an API key or an Azure Active Directory token.")
@@ -221,8 +234,8 @@ class AzureOpenAIChatGenerator(OpenAIChatGenerator):
self.tools_strict = tools_strict
client_args: dict[str, Any] = {
"api_version": api_version,
"azure_endpoint": azure_endpoint,
"api_version": resolved_api_version,
"azure_endpoint": resolved_azure_endpoint,
"azure_deployment": azure_deployment,
"api_key": api_key.resolve_value() if api_key is not None else None,
"azure_ad_token": azure_ad_token.resolve_value() if azure_ad_token is not None else None,
@@ -279,10 +292,12 @@ class AzureOpenAIChatGenerator(OpenAIChatGenerator):
generation_kwargs["response_format"] = json_schema
return default_to_dict(
self,
azure_endpoint=self.azure_endpoint,
azure_endpoint=self.azure_endpoint.to_dict()
if isinstance(self.azure_endpoint, Secret)
else self.azure_endpoint,
azure_deployment=self.azure_deployment,
organization=self.organization,
api_version=self.api_version,
api_version=self.api_version.to_dict() if isinstance(self.api_version, Secret) else self.api_version,
streaming_callback=callback_name,
generation_kwargs=generation_kwargs,
timeout=self.timeout,
@@ -6,6 +6,7 @@ import asyncio
import json
import re
import sys
import warnings
from collections.abc import AsyncIterator, Callable
from concurrent.futures import ThreadPoolExecutor
from contextlib import asynccontextmanager, suppress
@@ -190,6 +191,15 @@ class HuggingFaceLocalChatGenerator:
Whether to enable thinking mode in the chat template for thinking-capable models.
When enabled, the model generates intermediate reasoning before the final response. Defaults to False.
"""
warnings.warn(
"`HuggingFaceLocalChatGenerator` will be removed from Haystack in version 3.0, as it is moving to the "
"`transformers-haystack` package and being renamed to `TransformersChatGenerator`. To continue using it, "
"install that package with `pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.generators.transformers import TransformersChatGenerator`.",
FutureWarning,
stacklevel=2,
)
torch_and_transformers_import.check()
if tools and streaming_callback is not None:
+10
View File
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
import math
import warnings
from dataclasses import replace
from pathlib import Path
from typing import Any
@@ -107,6 +108,15 @@ class ExtractiveReader:
when loading the model specified in `model`. For details on what kwargs you can pass,
see the model's documentation.
"""
warnings.warn(
"`ExtractiveReader` will be removed from Haystack in version 3.0, as it is moving to the "
"`transformers-haystack` package and being renamed to `TransformersExtractiveReader`. To continue using "
"it, install that package with `pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.readers.transformers import TransformersExtractiveReader`.",
FutureWarning,
stacklevel=2,
)
torch_and_transformers_import.check()
self.model_name_or_path = str(model)
self.model = None
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
import warnings
from typing import Any
from haystack import component, default_from_dict, default_to_dict
@@ -89,6 +90,15 @@ class TransformersTextRouter:
:param huggingface_pipeline_kwargs: A dictionary of keyword arguments for initializing the Hugging Face
text classification pipeline.
"""
warnings.warn(
"`TransformersTextRouter` will be removed from Haystack in version 3.0, as it is moving to the "
"`transformers-haystack` package. To continue using it, install that package with "
"`pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.routers.transformers import TransformersTextRouter`.",
FutureWarning,
stacklevel=2,
)
torch_and_transformers_import.check()
self.token = token
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
import warnings
from typing import Any
from haystack import component, default_from_dict, default_to_dict
@@ -116,6 +117,15 @@ class TransformersZeroShotTextRouter:
:param huggingface_pipeline_kwargs: A dictionary of keyword arguments for initializing the Hugging Face
zero shot text classification.
"""
warnings.warn(
"`TransformersZeroShotTextRouter` will be removed from Haystack in version 3.0, as it is moving to the "
"`transformers-haystack` package. To continue using it, install that package with "
"`pip install transformers-haystack` and update your import to "
"`from haystack_integrations.components.routers.transformers import TransformersZeroShotTextRouter`.",
FutureWarning,
stacklevel=2,
)
torch_and_transformers_import.check()
self.token = token
@@ -0,0 +1,8 @@
---
enhancements:
- |
``AzureOpenAIChatGenerator`` now accepts a ``Secret`` for the ``azure_endpoint`` and ``api_version`` parameters in
addition to a plain string. This makes it possible to resolve these values from environment variables at
runtime, for example with ``Secret.from_env_var("AZURE_OPENAI_ENDPOINT")``, so the same serialized pipeline can
switch between environments (e.g. dev and prod) by changing environment variables instead of the pipeline
definition.
@@ -0,0 +1,20 @@
---
deprecations:
- |
``TransformersZeroShotDocumentClassifier``, ``TransformersTextRouter``, ``TransformersZeroShotTextRouter``,
``HuggingFaceLocalChatGenerator``, ``NamedEntityExtractor``, and ``ExtractiveReader`` are deprecated and will be
removed from Haystack in version 3.0. They are moving to the ``transformers-haystack`` package. Some of them are
also being renamed. To continue using them, install the package with ``pip install transformers-haystack`` and
update your imports as follows:
.. code-block:: python
from haystack_integrations.components.classifiers.transformers import TransformersZeroShotDocumentClassifier
from haystack_integrations.components.routers.transformers import TransformersTextRouter
from haystack_integrations.components.routers.transformers import TransformersZeroShotTextRouter
# HuggingFaceLocalChatGenerator was renamed to TransformersChatGenerator
from haystack_integrations.components.generators.transformers import TransformersChatGenerator
# NamedEntityExtractor (Hugging Face backend) was renamed to TransformersNamedEntityExtractor
from haystack_integrations.components.extractors.transformers import TransformersNamedEntityExtractor
# ExtractiveReader was renamed to TransformersExtractiveReader
from haystack_integrations.components.readers.transformers import TransformersExtractiveReader
+60
View File
@@ -0,0 +1,60 @@
# SPDX-FileCopyrightText: 2022-present deepset GmbH <[email protected]>
#
# SPDX-License-Identifier: Apache-2.0
"""
Pre-commit hook that enforces reStructuredText inline-code syntax in reno release notes.
Release notes under ``releasenotes/notes/*.yaml`` are rendered as reStructuredText, where inline
code must use double backticks (``like_this``). A common mistake is to use Markdown-style single
backticks (`like_this`), which RST renders as italic "interpreted text" instead of code.
By default the hook rewrites single backticks to double backticks and exits non-zero when it
changes a file, so the rewrite can be reviewed before re-staging; ``--check`` only reports.
It leaves existing double backticks ``code`` untouched and skips RST roles (:func:`x`) and
hyperlink references (`text <url>`_).
"""
import argparse
import re
import sys
# A single-backtick inline-code span that should use double backticks. The look-arounds skip
# valid RST: ``...`` pairs (the backtick-adjacent guards), roles like :func:`x` (no ':' before
# the opening backtick), and hyperlink refs like `text <url>`_ (no '_' after the closing one).
SINGLE_BACKTICK_RE = re.compile(r"(?<![`:])`(?!`)([^`\n]+?)`(?![`_])")
def fix_text(text: str) -> str:
"""Return ``text`` with single-backtick inline code rewritten to double backticks."""
return SINGLE_BACKTICK_RE.sub(r"``\1``", text)
def main() -> int:
"""Entry point for the pre-commit hook."""
parser = argparse.ArgumentParser(description="Enforce double backticks in reno release notes.")
parser.add_argument("--check", action="store_true", help="Report problems without modifying files.")
parser.add_argument("files", nargs="*")
args = parser.parse_args()
ret = 0
for path in args.files:
with open(path, encoding="utf-8") as f:
original = f.read()
fixed = fix_text(original)
if fixed == original:
continue
ret = 1
print(f"{'single backtick in' if args.check else 'fixed'}: {path}", file=sys.stderr)
if not args.check:
with open(path, "w", encoding="utf-8") as f:
f.write(fixed)
if ret:
hint = "Run without --check to fix automatically." if args.check else "Review and re-stage the changes."
print(f"\nrelease notes need double backticks (``like_this``) for inline code. {hint}", file=sys.stderr)
return ret
if __name__ == "__main__":
raise SystemExit(main())
@@ -137,6 +137,100 @@ class TestAzureOpenAIChatGenerator:
assert component.azure_ad_token_provider is not None
assert component.max_retries == 0
def test_init_with_secret_azure_endpoint_and_api_version(self, monkeypatch):
"""`azure_endpoint` and `api_version` accept a Secret that is resolved from an environment variable."""
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
monkeypatch.setenv("AZURE_OPENAI_ENDPOINT", "https://test-resource.azure.openai.com/")
monkeypatch.setenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
component = AzureOpenAIChatGenerator(
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
# The Secret objects are kept on the instance so they can be serialized
assert component.azure_endpoint == Secret.from_env_var("AZURE_OPENAI_ENDPOINT")
assert component.api_version == Secret.from_env_var("AZURE_OPENAI_API_VERSION")
# The clients receive the resolved string values
assert str(component.client._azure_endpoint) == "https://test-resource.azure.openai.com/"
assert component.client._api_version == "2024-08-01-preview"
assert str(component.async_client._azure_endpoint) == "https://test-resource.azure.openai.com/"
assert component.async_client._api_version == "2024-08-01-preview"
def test_init_fail_with_unset_secret_azure_endpoint(self, monkeypatch):
"""A Secret azure_endpoint that resolves to nothing raises the same error as a missing endpoint."""
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
monkeypatch.delenv("AZURE_OPENAI_ENDPOINT", raising=False)
with pytest.raises(ValueError, match="Azure endpoint"):
AzureOpenAIChatGenerator(azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT", strict=False))
def test_to_dict_with_secret_azure_endpoint_and_api_version(self, monkeypatch):
"""Secret `azure_endpoint` and `api_version` are serialized as Secret dictionaries."""
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
monkeypatch.setenv("AZURE_OPENAI_ENDPOINT", "https://test-resource.azure.openai.com/")
monkeypatch.setenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
component = AzureOpenAIChatGenerator(
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
init_params = component.to_dict()["init_parameters"]
assert init_params["azure_endpoint"] == {
"type": "env_var",
"env_vars": ["AZURE_OPENAI_ENDPOINT"],
"strict": True,
}
assert init_params["api_version"] == {
"type": "env_var",
"env_vars": ["AZURE_OPENAI_API_VERSION"],
"strict": True,
}
def test_secret_azure_endpoint_and_api_version_roundtrip(self, monkeypatch):
"""Serializing and deserializing a component with Secret endpoint/version restores the Secrets."""
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
monkeypatch.setenv("AZURE_OPENAI_ENDPOINT", "https://test-resource.azure.openai.com/")
monkeypatch.setenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
component = AzureOpenAIChatGenerator(
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
deserialized = AzureOpenAIChatGenerator.from_dict(component.to_dict())
assert deserialized.azure_endpoint == Secret.from_env_var("AZURE_OPENAI_ENDPOINT")
assert deserialized.api_version == Secret.from_env_var("AZURE_OPENAI_API_VERSION")
assert str(deserialized.client._azure_endpoint) == "https://test-resource.azure.openai.com/"
assert deserialized.client._api_version == "2024-08-01-preview"
def test_from_dict_with_secret_azure_endpoint_and_api_version(self, monkeypatch):
"""from_dict deserializes Secret azure_endpoint/api_version dicts and resolves them for the client."""
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
monkeypatch.setenv("AZURE_OPENAI_ENDPOINT", "https://test-resource.azure.openai.com/")
monkeypatch.setenv("AZURE_OPENAI_API_VERSION", "2024-08-01-preview")
data = {
"type": "haystack.components.generators.chat.azure.AzureOpenAIChatGenerator",
"init_parameters": {
"api_key": {"env_vars": ["AZURE_OPENAI_API_KEY"], "strict": False, "type": "env_var"},
"azure_ad_token": {"env_vars": ["AZURE_OPENAI_AD_TOKEN"], "strict": False, "type": "env_var"},
"azure_endpoint": {"env_vars": ["AZURE_OPENAI_ENDPOINT"], "strict": True, "type": "env_var"},
"api_version": {"env_vars": ["AZURE_OPENAI_API_VERSION"], "strict": True, "type": "env_var"},
"azure_deployment": "gpt-4.1-mini",
"organization": None,
"streaming_callback": None,
"generation_kwargs": {},
"timeout": 30.0,
"max_retries": 5,
"default_headers": {},
"tools": None,
"tools_strict": False,
"azure_ad_token_provider": None,
"http_client_kwargs": None,
},
}
generator = AzureOpenAIChatGenerator.from_dict(data)
# The Secret dicts are deserialized back into Secret objects
assert generator.azure_endpoint == Secret.from_env_var("AZURE_OPENAI_ENDPOINT")
assert generator.api_version == Secret.from_env_var("AZURE_OPENAI_API_VERSION")
# And they are resolved to the string values the client expects
assert str(generator.client._azure_endpoint) == "https://test-resource.azure.openai.com/"
assert generator.client._api_version == "2024-08-01-preview"
def test_to_dict_default(self, monkeypatch):
monkeypatch.setenv("AZURE_OPENAI_API_KEY", "test-api-key")
component = AzureOpenAIChatGenerator(azure_endpoint="some-non-existing-endpoint")
+90
View File
@@ -0,0 +1,90 @@
# SPDX-FileCopyrightText: 2022-present deepset GmbH <[email protected]>
#
# SPDX-License-Identifier: Apache-2.0
import importlib.util
import subprocess
import sys
from pathlib import Path
import pytest
_SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "release_note_backticks.py"
_spec = importlib.util.spec_from_file_location("release_note_backticks", _SCRIPT)
_module = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_module)
fix_text = _module.fix_text
class TestFixText:
@pytest.mark.parametrize(
"text, expected",
[
("Use `OpenAIChatGenerator` now.", "Use ``OpenAIChatGenerator`` now."),
("Set `api_key` and `azure_endpoint`.", "Set ``api_key`` and ``azure_endpoint``."),
('Call `Secret.from_env_var("X")`.', 'Call ``Secret.from_env_var("X")``.'),
("Leading `code` token.", "Leading ``code`` token."),
],
)
def test_converts_single_to_double(self, text, expected):
assert fix_text(text) == expected
@pytest.mark.parametrize(
"text",
[
"Already correct: ``OpenAIChatGenerator``.",
"Two literals ``Secret`` and ``api_key``.",
"No inline code at all here.",
"RST role :func:`do_thing` must stay single.",
"Link `Haystack <https://haystack.deepset.ai>`_ must stay single.",
],
)
def test_leaves_valid_rst_untouched(self, text):
assert fix_text(text) == text
def test_only_single_backticks_in_mixed_text_are_converted(self):
text = "Use ``Secret`` for `api_key` and `azure_endpoint`."
expected = "Use ``Secret`` for ``api_key`` and ``azure_endpoint``."
assert fix_text(text) == expected
def test_is_idempotent(self):
once = fix_text("Set `x` and `y`.")
assert fix_text(once) == once
def test_unbalanced_single_backtick_is_left_untouched(self):
# A stray, unpaired backtick is ambiguous, so we never rewrite it.
text = "An unbalanced `backtick stays as is.\n"
assert fix_text(text) == text
class TestCli:
def test_fix_rewrites_file_and_is_idempotent(self, tmp_path):
note = tmp_path / "note.yaml"
note.write_text("enhancements:\n - |\n Use `Foo` and `Bar` now.\n", encoding="utf-8")
first = subprocess.run([sys.executable, str(_SCRIPT), str(note)], capture_output=True, text=True, check=False)
assert first.returncode == 1
assert "``Foo``" in note.read_text(encoding="utf-8")
assert "``Bar``" in note.read_text(encoding="utf-8")
# Running again on the now-fixed file is a no-op and succeeds.
second = subprocess.run([sys.executable, str(_SCRIPT), str(note)], capture_output=True, text=True, check=False)
assert second.returncode == 0
def test_check_mode_reports_without_modifying(self, tmp_path):
note = tmp_path / "note.yaml"
content = "enhancements:\n - |\n Use `Foo` now.\n"
note.write_text(content, encoding="utf-8")
result = subprocess.run(
[sys.executable, str(_SCRIPT), "--check", str(note)], capture_output=True, text=True, check=False
)
assert result.returncode == 1
assert note.read_text(encoding="utf-8") == content
def test_clean_file_passes(self, tmp_path):
note = tmp_path / "note.yaml"
note.write_text("enhancements:\n - |\n Use ``Foo`` now.\n", encoding="utf-8")
result = subprocess.run([sys.executable, str(_SCRIPT), str(note)], capture_output=True, text=True, check=False)
assert result.returncode == 0