mirror of
https://github.com/deepset-ai/haystack.git
synced 2026-10-12 02:12:50 +00:00
chore: use ruff for formatting Python snippets in docs (#12245)
This commit is contained in:
+2
-11
@@ -17,24 +17,15 @@ repos:
|
||||
args: [--markdown-linebreak-ext=md, --markdown-linebreak-ext=mdx]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.2
|
||||
rev: v0.16.0
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [ --fix ]
|
||||
- id: ruff-format
|
||||
types_or: [python, pyi, jupyter, mdx]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: ruff-format-docs
|
||||
name: ruff-format-docs
|
||||
language: python
|
||||
entry: python scripts/ruff_format_docs.py --line-length=88
|
||||
files: ^docs-website/.*\.mdx$
|
||||
types: [text]
|
||||
additional_dependencies:
|
||||
- ruff
|
||||
- add-trailing-comma
|
||||
|
||||
- id: release-note-backticks
|
||||
name: release-note-backticks
|
||||
language: python
|
||||
|
||||
@@ -99,7 +99,9 @@ After adding `FileContent` to a `ChatMessage`, use the `file` and `files` proper
|
||||
from haystack.dataclasses import ChatMessage, FileContent
|
||||
|
||||
file_content = FileContent.from_file_path("data/invoice.pdf")
|
||||
message = ChatMessage.from_user(content_parts=[file_content, "Extract the invoice total."])
|
||||
message = ChatMessage.from_user(
|
||||
content_parts=[file_content, "Extract the invoice total."]
|
||||
)
|
||||
|
||||
print(message.file)
|
||||
print(message.files)
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -40,36 +40,40 @@ If your tracing backend isn't supported out of the box, you can connect it to Ha
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
|
||||
@@ -132,12 +132,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from haystack_integrations.tracing.opentelemetry import OpenTelemetryTracer
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
|
||||
@@ -57,10 +57,12 @@ document_store = ArcadeDBDocumentStore(
|
||||
recreate_type=True,
|
||||
)
|
||||
|
||||
document_store.write_documents([
|
||||
Document(content="This is first", embedding=[0.0] * 768),
|
||||
Document(content="This is second", embedding=[0.1, 0.2, 0.3] + [0.0] * 765),
|
||||
])
|
||||
document_store.write_documents(
|
||||
[
|
||||
Document(content="This is first", embedding=[0.0] * 768),
|
||||
Document(content="This is second", embedding=[0.1, 0.2, 0.3] + [0.0] * 765),
|
||||
]
|
||||
)
|
||||
print(document_store.count_documents())
|
||||
```
|
||||
|
||||
|
||||
@@ -102,7 +102,9 @@ store.add_memories(
|
||||
|
||||
# Write transient session context to the session cache.
|
||||
store.add_memories(
|
||||
messages=[ChatMessage.from_user("Alice is currently debugging a vector store issue.")],
|
||||
messages=[
|
||||
ChatMessage.from_user("Alice is currently debugging a vector store issue.")
|
||||
],
|
||||
session_id="alice_session_1",
|
||||
)
|
||||
|
||||
|
||||
@@ -79,7 +79,9 @@ Mem0 supports narrowing the scope of reads and writes with `user_id`, `run_id`,
|
||||
|
||||
```python
|
||||
store.add_memories(
|
||||
messages=[ChatMessage.from_user("Alice is working on a documentation search system.")],
|
||||
messages=[
|
||||
ChatMessage.from_user("Alice is working on a documentation search system.")
|
||||
],
|
||||
user_id="alice",
|
||||
run_id="docs-assistant-session-1",
|
||||
infer=True,
|
||||
|
||||
@@ -44,7 +44,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
@@ -42,7 +42,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
@@ -101,7 +101,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ document_store = InMemoryDocumentStore()
|
||||
|
||||
indexing_pipeline = Pipeline()
|
||||
indexing_pipeline.add_component("converter", TextFileToDocument())
|
||||
indexing_pipeline.add_component("embedder", EdenAIDocumentEmbedder(model="openai/text-embedding-3-small"))
|
||||
indexing_pipeline.add_component(
|
||||
"embedder", EdenAIDocumentEmbedder(model="openai/text-embedding-3-small")
|
||||
)
|
||||
indexing_pipeline.add_component("writer", DocumentWriter(document_store=document_store))
|
||||
|
||||
indexing_pipeline.connect("converter", "embedder")
|
||||
|
||||
@@ -93,8 +93,12 @@ documents_with_embeddings = document_embedder.run(documents)["documents"]
|
||||
document_store.write_documents(documents_with_embeddings)
|
||||
|
||||
query_pipeline = Pipeline()
|
||||
query_pipeline.add_component("text_embedder", EdenAITextEmbedder(model="openai/text-embedding-3-small"))
|
||||
query_pipeline.add_component("retriever", InMemoryEmbeddingRetriever(document_store=document_store))
|
||||
query_pipeline.add_component(
|
||||
"text_embedder", EdenAITextEmbedder(model="openai/text-embedding-3-small")
|
||||
)
|
||||
query_pipeline.add_component(
|
||||
"retriever", InMemoryEmbeddingRetriever(document_store=document_store)
|
||||
)
|
||||
query_pipeline.connect("text_embedder.embedding", "retriever.query_embedding")
|
||||
|
||||
result = query_pipeline.run({"text_embedder": {"text": "Who lives in Berlin?"}})
|
||||
|
||||
@@ -55,7 +55,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
@@ -111,18 +111,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
# and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
# and so on, either invoking component directly or in the pipeline
|
||||
|
||||
@@ -80,7 +80,9 @@ You can stream output as it's generated. Pass a callback to `streaming_callback`
|
||||
```python
|
||||
from haystack.components.generators.utils import print_streaming_chunk
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack_integrations.components.generators.anthropic import AnthropicFoundryChatGenerator
|
||||
from haystack_integrations.components.generators.anthropic import (
|
||||
AnthropicFoundryChatGenerator,
|
||||
)
|
||||
|
||||
generator = AnthropicFoundryChatGenerator(
|
||||
resource="my-resource",
|
||||
@@ -102,13 +104,17 @@ See [Streaming Support](guides-to-generators/choosing-the-right-generator.mdx#st
|
||||
```python
|
||||
import asyncio
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack_integrations.components.generators.anthropic import AnthropicFoundryChatGenerator
|
||||
from haystack_integrations.components.generators.anthropic import (
|
||||
AnthropicFoundryChatGenerator,
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
generator = AnthropicFoundryChatGenerator(resource="my-resource")
|
||||
result = await generator.run_async([ChatMessage.from_user("Hello!")])
|
||||
print(result["replies"][0].text)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
@@ -125,7 +131,9 @@ pip install anthropic-haystack
|
||||
```python
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.utils import Secret
|
||||
from haystack_integrations.components.generators.anthropic import AnthropicFoundryChatGenerator
|
||||
from haystack_integrations.components.generators.anthropic import (
|
||||
AnthropicFoundryChatGenerator,
|
||||
)
|
||||
|
||||
generator = AnthropicFoundryChatGenerator(
|
||||
model="claude-sonnet-4-5",
|
||||
@@ -143,7 +151,9 @@ print(response)
|
||||
from haystack import Pipeline
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack_integrations.components.generators.anthropic import AnthropicFoundryChatGenerator
|
||||
from haystack_integrations.components.generators.anthropic import (
|
||||
AnthropicFoundryChatGenerator,
|
||||
)
|
||||
|
||||
pipe = Pipeline()
|
||||
pipe.add_component("prompt_builder", ChatPromptBuilder())
|
||||
|
||||
@@ -118,7 +118,10 @@ question = "What is Eden AI?"
|
||||
result = rag_pipeline.run(
|
||||
{
|
||||
"fetcher": {"urls": ["https://www.edenai.co/"]},
|
||||
"prompt_builder": {"template_variables": {"query": question}, "template": messages},
|
||||
"prompt_builder": {
|
||||
"template_variables": {"query": question},
|
||||
"template": messages,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ pip install sentence-transformers-haystack
|
||||
```
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Document
|
||||
from haystack_integrations.components.embedders.sentence_transformers import (
|
||||
SentenceTransformersDocumentEmbedder,
|
||||
|
||||
@@ -112,7 +112,11 @@ result = splitter.run(
|
||||
)
|
||||
|
||||
for chunk in result["documents"]:
|
||||
print(chunk.meta["start_line"], chunk.meta["end_line"], chunk.meta.get("include_classes"))
|
||||
print(
|
||||
chunk.meta["start_line"],
|
||||
chunk.meta["end_line"],
|
||||
chunk.meta.get("include_classes"),
|
||||
)
|
||||
```
|
||||
|
||||
### With docstring stripping for RAG
|
||||
@@ -145,7 +149,9 @@ splitter = PythonCodeSplitter(
|
||||
max_effective_lines=100,
|
||||
strip_docstrings=True,
|
||||
)
|
||||
result = splitter.run(documents=[Document(content=source, meta={"file_name": "my_module.py"})])
|
||||
result = splitter.run(
|
||||
documents=[Document(content=source, meta={"file_name": "my_module.py"})]
|
||||
)
|
||||
for chunk in result["documents"]:
|
||||
print(chunk.content)
|
||||
print(chunk.meta.get("docstrings"))
|
||||
|
||||
@@ -81,7 +81,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -92,7 +92,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -74,7 +74,9 @@ from haystack_integrations.document_stores.elasticsearch import (
|
||||
)
|
||||
from haystack.document_stores.types import DuplicatePolicy
|
||||
|
||||
document_store = ElasticsearchDocumentStore(hosts="http://localhost:9200/", index="my_index")
|
||||
document_store = ElasticsearchDocumentStore(
|
||||
hosts="http://localhost:9200/", index="my_index"
|
||||
)
|
||||
|
||||
documents = [
|
||||
Document(content="There are over 7,000 languages spoken around the world today."),
|
||||
|
||||
@@ -74,7 +74,9 @@ from haystack_integrations.document_stores.opensearch import (
|
||||
)
|
||||
from haystack.document_stores.types import DuplicatePolicy
|
||||
|
||||
document_store = OpenSearchDocumentStore(hosts="http://localhost:9200", index="my_index")
|
||||
document_store = OpenSearchDocumentStore(
|
||||
hosts="http://localhost:9200", index="my_index"
|
||||
)
|
||||
|
||||
documents = [
|
||||
Document(content="There are over 7,000 languages spoken around the world today."),
|
||||
|
||||
@@ -63,7 +63,9 @@ pip install psycopg2-binary
|
||||
A self-contained example using an in-memory SQLite database seeded via `init_script`:
|
||||
|
||||
```python
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import SQLAlchemyTableRetriever
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import (
|
||||
SQLAlchemyTableRetriever,
|
||||
)
|
||||
|
||||
retriever = SQLAlchemyTableRetriever(
|
||||
drivername="sqlite",
|
||||
@@ -83,7 +85,9 @@ Connecting to a real backend looks the same — swap the driver and pass connect
|
||||
|
||||
```python
|
||||
from haystack.utils import Secret
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import SQLAlchemyTableRetriever
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import (
|
||||
SQLAlchemyTableRetriever,
|
||||
)
|
||||
|
||||
retriever = SQLAlchemyTableRetriever(
|
||||
drivername="postgresql+psycopg2",
|
||||
@@ -105,7 +109,9 @@ from haystack.utils import Secret
|
||||
from haystack.components.builders import ChatPromptBuilder
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import SQLAlchemyTableRetriever
|
||||
from haystack_integrations.components.retrievers.sqlalchemy import (
|
||||
SQLAlchemyTableRetriever,
|
||||
)
|
||||
|
||||
retriever = SQLAlchemyTableRetriever(
|
||||
drivername="postgresql+psycopg2",
|
||||
|
||||
@@ -83,7 +83,9 @@ To write to the session cache instead of the permanent graph, pass a `session_id
|
||||
```python
|
||||
session_writer = CogneeWriter(memory_store=store, session_id="alice_session_1")
|
||||
session_writer.run(
|
||||
messages=[ChatMessage.from_user("Alice is currently debugging a vector store issue.")],
|
||||
messages=[
|
||||
ChatMessage.from_user("Alice is currently debugging a vector store issue.")
|
||||
],
|
||||
user_id="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
)
|
||||
```
|
||||
|
||||
@@ -238,9 +238,7 @@ By default, a tool's return value is converted to a string using a default handl
|
||||
You can use `outputs_to_string` to customize this behavior using one of two formats:
|
||||
1. **Single output format**: Use `source`, `handler`, and/or `raw_result` at the root level.
|
||||
```python
|
||||
{
|
||||
"source": "docs", "handler": format_documents, "raw_result": False
|
||||
}
|
||||
{"source": "docs", "handler": format_documents, "raw_result": False}
|
||||
```
|
||||
- `source`: (Optional) Specifies the key to extract from the tool's output dictionary. If omitted, the entire result is passed to the handler.
|
||||
- `handler`: (Optional) A function that takes the output (or the extracted source value) and returns the final result.
|
||||
@@ -252,7 +250,7 @@ You can use `outputs_to_string` to customize this behavior using one of two form
|
||||
```python
|
||||
{
|
||||
"formatted_docs": {"source": "docs", "handler": format_documents},
|
||||
"summary": {"source": "summary_text", "handler": str.upper}
|
||||
"summary": {"source": "summary_text", "handler": str.upper},
|
||||
}
|
||||
```
|
||||
Each entry defines a `source` key and can optionally include a `handler`. The individual outputs are processed,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Ruff configuration for the docs website.
|
||||
#
|
||||
# It applies to the Python code blocks embedded in .mdx pages, and is picked up automatically by both `hatch run fmt`
|
||||
# and the ruff-format pre-commit hook, so the two always agree on what gets formatted.
|
||||
|
||||
extend = "../pyproject.toml"
|
||||
|
||||
extension = { "mdx" = "markdown" }
|
||||
|
||||
line-length = 88
|
||||
|
||||
extend-exclude = ["node_modules", "build", ".docusaurus", "scripts"]
|
||||
|
||||
[format]
|
||||
skip-magic-trailing-comma = false
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -156,36 +161,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -279,12 +288,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -39,7 +39,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -37,7 +37,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -96,7 +96,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -50,7 +50,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -78,18 +78,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
+3
-2
@@ -37,7 +37,9 @@ pip install google-vertex-haystack
|
||||
Basic usage:
|
||||
|
||||
````python
|
||||
from haystack_integrations.components.generators.google_vertex import VertexAICodeGenerator
|
||||
from haystack_integrations.components.generators.google_vertex import (
|
||||
VertexAICodeGenerator,
|
||||
)
|
||||
|
||||
generator = VertexAICodeGenerator()
|
||||
|
||||
@@ -45,7 +47,6 @@ result = generator.run(prefix="def to_json(data):")
|
||||
|
||||
for answer in result["replies"]:
|
||||
print(answer)
|
||||
|
||||
````
|
||||
|
||||
You can also set other parameters like the number of output tokens, temperature, stop sequences, and the number of candidates.
|
||||
|
||||
+3
-2
@@ -39,13 +39,14 @@ pip install google-vertex-haystack
|
||||
Basic usage:
|
||||
|
||||
````python
|
||||
from haystack_integrations.components.generators.google_vertex import VertexAITextGenerator
|
||||
from haystack_integrations.components.generators.google_vertex import (
|
||||
VertexAITextGenerator,
|
||||
)
|
||||
|
||||
generator = VertexAITextGenerator()
|
||||
res = generator.run("Tell me a good interview question for a software engineer.")
|
||||
|
||||
print(res["replies"][0])
|
||||
|
||||
````
|
||||
|
||||
You can also set other parameters like the number of answers generated, temperature to control the randomness, and stop sequences to stop generation. For a full list of possible parameters, see the documentation of [`TextGenerationModel.predict()`](https://cloud.google.com/python/docs/reference/aiplatform/latest/vertexai.language_models.TextGenerationModel#vertexai_language_models_TextGenerationModel_predict).
|
||||
|
||||
-1
@@ -76,7 +76,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -87,7 +87,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -156,36 +161,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -279,12 +288,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -43,7 +43,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -54,7 +54,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -111,18 +111,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
+3
-2
@@ -41,7 +41,9 @@ pip install google-vertex-haystack
|
||||
Basic usage:
|
||||
|
||||
````python
|
||||
from haystack_integrations.components.generators.google_vertex import VertexAICodeGenerator
|
||||
from haystack_integrations.components.generators.google_vertex import (
|
||||
VertexAICodeGenerator,
|
||||
)
|
||||
|
||||
generator = VertexAICodeGenerator()
|
||||
|
||||
@@ -49,7 +51,6 @@ result = generator.run(prefix="def to_json(data):")
|
||||
|
||||
for answer in result["replies"]:
|
||||
print(answer)
|
||||
|
||||
````
|
||||
|
||||
You can also set other parameters like the number of output tokens, temperature, stop sequences, and the number of candidates.
|
||||
|
||||
+3
-2
@@ -43,13 +43,14 @@ pip install google-vertex-haystack
|
||||
Basic usage:
|
||||
|
||||
````python
|
||||
from haystack_integrations.components.generators.google_vertex import VertexAITextGenerator
|
||||
from haystack_integrations.components.generators.google_vertex import (
|
||||
VertexAITextGenerator,
|
||||
)
|
||||
|
||||
generator = VertexAITextGenerator()
|
||||
res = generator.run("Tell me a good interview question for a software engineer.")
|
||||
|
||||
print(res["replies"][0])
|
||||
|
||||
````
|
||||
|
||||
You can also set other parameters like the number of answers generated, temperature to control the randomness, and stop sequences to stop generation. For a full list of possible parameters, see the documentation of [`TextGenerationModel.predict()`](https://cloud.google.com/python/docs/reference/aiplatform/latest/vertexai.language_models.TextGenerationModel#vertexai_language_models_TextGenerationModel_predict).
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -153,36 +158,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -275,12 +284,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -43,7 +43,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -54,7 +54,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -110,18 +110,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -153,36 +158,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -275,12 +284,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -43,7 +43,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -54,7 +54,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -110,18 +110,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -153,36 +158,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -275,12 +284,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -43,7 +43,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -54,7 +54,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -110,18 +110,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
-1
@@ -36,7 +36,6 @@ This component is inspired by [5 Levels of Text Splitting](https://github.com/Fu
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Document
|
||||
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
|
||||
from haystack.components.preprocessors import EmbeddingBasedDocumentSplitter
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -138,7 +138,6 @@ new_env_secret = Secret.from_dict(env_secret_dict)
|
||||
Both types of secrets can be resolved to their actual values using the `resolve_value` method. This method returns the token or the value of the environment variable.
|
||||
|
||||
```python
|
||||
|
||||
## Resolve the token-based secret
|
||||
token_value = api_key_secret.resolve_value()
|
||||
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -153,36 +158,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -275,12 +284,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
-1
@@ -43,7 +43,6 @@ Optionally, you can provide metadata using the `meta` parameter. This can be a s
|
||||
This component is primarily meant to be used in pipelines.
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToDocument
|
||||
|
||||
converter = ImageFileToDocument()
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ This component is often used in query pipelines just before a `ChatPromptBuilder
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.converters.image import ImageFileToImageContent
|
||||
|
||||
converter = ImageFileToImageContent(detail="high", size=(800, 600))
|
||||
|
||||
-1
@@ -100,7 +100,6 @@ assert result["output_adapter"]["output"] == "Haystack"
|
||||
You can also define your own custom filters, which can then be added to an `OutputAdapter` instance through its init method and used in templates. Here’s an example of this approach:
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Pipeline, component, Document
|
||||
from haystack.components.converters import OutputAdapter
|
||||
|
||||
|
||||
-1
@@ -54,7 +54,6 @@ Remember to set the`MISTRAL_API_KEY` as an environment variable first or pass it
|
||||
Here is how you can use the component on its own:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.embedders.mistral.text_embedder import (
|
||||
MistralTextEmbedder,
|
||||
)
|
||||
|
||||
+13
-4
@@ -110,18 +110,27 @@ from haystack.dataclasses import ChatMessage
|
||||
generation_kwargs = {"extra_headers": {"anthropic-beta": "prompt-caching-2024-07-31"}}
|
||||
|
||||
claude_llm = AnthropicChatGenerator(
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"), generation_kwargs=generation_kwargs
|
||||
api_key=Secret.from_env_var("ANTHROPIC_API_KEY"),
|
||||
generation_kwargs=generation_kwargs,
|
||||
)
|
||||
|
||||
system_message = ChatMessage.from_system("Replace with some long text documents, code or instructions")
|
||||
system_message = ChatMessage.from_system(
|
||||
"Replace with some long text documents, code or instructions"
|
||||
)
|
||||
system_message.meta["cache_control"] = {"type": "ephemeral"}
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("A query about the long text for example")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("A query about the long text for example"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and now invoke again with
|
||||
|
||||
messages = [system_message, ChatMessage.from_user("Another query about the long text etc")]
|
||||
messages = [
|
||||
system_message,
|
||||
ChatMessage.from_user("Another query about the long text etc"),
|
||||
]
|
||||
result = claude_llm.run(messages)
|
||||
|
||||
## and so on, either invoking component directly or in the pipeline
|
||||
|
||||
-1
@@ -141,7 +141,6 @@ Here are the components that support shared hosted models in Haystack:
|
||||
Here is an example using OctoAI and [`OpenAIChatGenerator`](../openaichatgenerator.mdx):
|
||||
|
||||
```python
|
||||
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.utils import Secret
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
-1
@@ -36,7 +36,6 @@ This component is inspired by [5 Levels of Text Splitting](https://github.com/Fu
|
||||
### On its own
|
||||
|
||||
```python
|
||||
|
||||
from haystack import Document
|
||||
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
|
||||
from haystack.components.preprocessors import EmbeddingBasedDocumentSplitter
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
The below example shows how to use the `AzureAISearchBM25Retriever` in a RAG pipeline. Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.azure_ai_search import (
|
||||
AzureAISearchBM25Retriever,
|
||||
)
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ retriever.run(query="How many languages are spoken around the world today?")
|
||||
Set your `OPENAI_API_KEY` as an environment variable and then run the following code:
|
||||
|
||||
```python
|
||||
|
||||
from haystack_integrations.components.retrievers.elasticsearch import (
|
||||
ElasticsearchBM25Retriever,
|
||||
)
|
||||
|
||||
@@ -131,9 +131,7 @@ By default, a tool's return value is converted to a string using a default handl
|
||||
You can use `outputs_to_string` to customize this behavior using one of two formats:
|
||||
1. **Single output format**: Use `source`, `handler`, and/or `raw_result` at the root level.
|
||||
```python
|
||||
{
|
||||
"source": "docs", "handler": format_documents, "raw_result": False
|
||||
}
|
||||
{"source": "docs", "handler": format_documents, "raw_result": False}
|
||||
```
|
||||
- `source`: (Optional) Specifies the key to extract from the tool's output dictionary. If omitted, the entire result is passed to the handler.
|
||||
- `handler`: (Optional) A function that takes the output (or the extracted source value) and returns the final result.
|
||||
@@ -145,7 +143,7 @@ You can use `outputs_to_string` to customize this behavior using one of two form
|
||||
```python
|
||||
{
|
||||
"formatted_docs": {"source": "docs", "handler": format_documents},
|
||||
"summary": {"source": "summary_text", "handler": str.upper}
|
||||
"summary": {"source": "summary_text", "handler": str.upper},
|
||||
}
|
||||
```
|
||||
Each entry defines a `source` key and can optionally include a `handler`. The individual outputs are processed,
|
||||
|
||||
@@ -211,14 +211,20 @@ If your pipeline seems stuck or runs longer than expected, here are common cause
|
||||
|
||||
```python
|
||||
# Problematic: user input goes directly to a component inside the loop
|
||||
result = pipe.run({
|
||||
"generator": {"prompt": query}, # This input persists and may retrigger the loop
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"generator": {
|
||||
"prompt": query
|
||||
}, # This input persists and may retrigger the loop
|
||||
}
|
||||
)
|
||||
|
||||
# Better: use an entry-point component outside the loop
|
||||
result = pipe.run({
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
})
|
||||
result = pipe.run(
|
||||
{
|
||||
"prompt_builder": {"query": query}, # Entry point feeds into the loop once
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
See [Greedy vs. Lazy Variadic Sockets](#greedy-vs-lazy-variadic-sockets-in-loops) for details on how inputs are consumed.
|
||||
|
||||
@@ -66,17 +66,22 @@ To use OpenTelemetry as your tracing backend, follow these steps:
|
||||
from opentelemetry.semconv.resource import ResourceAttributes
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
})
|
||||
resource = Resource(
|
||||
attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack" # Correct constant
|
||||
}
|
||||
)
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Tell Haystack to auto-detect the configured tracer
|
||||
import haystack.tracing
|
||||
|
||||
haystack.tracing.auto_enable_tracing()
|
||||
|
||||
# Explicitly tell Haystack to use your tracer
|
||||
@@ -153,36 +158,40 @@ To use your custom tracing backend with Haystack, follow these steps:
|
||||
from haystack.tracing import utils as tracing_utils
|
||||
import opentelemetry.trace
|
||||
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
class OpenTelemetrySpan(Span):
|
||||
def __init__(self, span: opentelemetry.trace.Span) -> None:
|
||||
self._span = span
|
||||
|
||||
def set_tag(self, key: str, value: Any) -> None:
|
||||
# Tracing backends usually don't support any tag value
|
||||
# `coerce_tag_value` forces the value to either be a Python
|
||||
# primitive (int, float, boolean, str) or tries to dump it as string.
|
||||
coerced_value = tracing_utils.coerce_tag_value(value)
|
||||
self._span.set_attribute(key, coerced_value)
|
||||
|
||||
|
||||
class OpenTelemetryTracer(Tracer):
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
def __init__(self, tracer: opentelemetry.trace.Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
@contextlib.contextmanager
|
||||
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
@contextlib.contextmanager
|
||||
def trace(
|
||||
self, operation_name: str, tags: Optional[Dict[str, Any]] = None
|
||||
) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(operation_name) as span:
|
||||
span = OpenTelemetrySpan(span)
|
||||
if tags:
|
||||
span.set_tags(tags)
|
||||
|
||||
yield span
|
||||
yield span
|
||||
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
def current_span(self) -> Optional[Span]:
|
||||
current_span = trace.get_current_span()
|
||||
if isinstance(current_span, NonRecordingSpan):
|
||||
return None
|
||||
|
||||
return OpenTelemetrySpan(current_span)
|
||||
return OpenTelemetrySpan(current_span)
|
||||
```
|
||||
|
||||
2. Tell Haystack to use your custom tracer:
|
||||
@@ -275,12 +284,12 @@ Use [Jaeger](https://www.jaegertracing.io/docs/1.6/getting-started/) as a lightw
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Service name is required for most backends
|
||||
resource = Resource(attributes={
|
||||
ResourceAttributes.SERVICE_NAME: "haystack"
|
||||
})
|
||||
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "haystack"})
|
||||
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
|
||||
processor = BatchSpanProcessor(
|
||||
OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
|
||||
)
|
||||
tracer_provider.add_span_processor(processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user