feat: Add support for custom headers (#1162)

* feat: Add support for custom headers

Signed-off-by: Anush008 <mail@anush.sh>

* add warnings when user provides user-agent or api-key in headers or options

* fix: fix is none condition

---------

Signed-off-by: Anush008 <mail@anush.sh>
Co-authored-by: George Panchuk <george.panchuk@qdrant.tech>
This commit is contained in:
Anush
2026-03-13 20:34:39 +05:30
committed by George Panchuk
parent a410b9d602
commit 1699d30899
4 changed files with 59 additions and 1 deletions

View File

@@ -77,6 +77,7 @@ class AsyncQdrantClient(AsyncQdrantFastembedMixin):
local_inference_batch_size: inference batch size used by fastembed when using local inference with `models.Document` and other models.
pool_size: connection pool size, Default: None. Default value for gRPC connection pool is 3, rest default is
inherited from `httpx` (default: 100)
headers: Custom headers to send with every request.
**kwargs: Additional arguments passed directly into REST client initialization
"""
@@ -100,6 +101,7 @@ class AsyncQdrantClient(AsyncQdrantFastembedMixin):
local_inference_batch_size: int | None = None,
check_compatibility: bool = True,
pool_size: int | None = None,
headers: dict[str, str] | None = None,
**kwargs: Any,
):
self._init_options = {
@@ -138,6 +140,7 @@ class AsyncQdrantClient(AsyncQdrantFastembedMixin):
auth_token_provider=auth_token_provider,
check_compatibility=check_compatibility,
pool_size=pool_size,
headers=headers,
**kwargs,
)
if isinstance(self._client, AsyncQdrantLocal) and cloud_inference:

View File

@@ -59,6 +59,7 @@ class AsyncQdrantRemote(AsyncQdrantBase):
auth_token_provider: Callable[[], str] | Callable[[], Awaitable[str]] | None = None,
check_compatibility: bool = True,
pool_size: int | None = None,
headers: dict[str, str] | None = None,
**kwargs: Any,
):
super().__init__(**kwargs)
@@ -116,6 +117,10 @@ class AsyncQdrantRemote(AsyncQdrantBase):
http2 = kwargs.pop("http2", False)
self._grpc_headers = []
self._rest_headers = {k: v for (k, v) in kwargs.pop("metadata", {}).items()}
if headers:
for key, value in headers.items():
self._rest_headers[key] = value
self._grpc_headers.append((key, value))
if api_key is not None:
if self._scheme == "http":
show_warning(
@@ -123,11 +128,30 @@ class AsyncQdrantRemote(AsyncQdrantBase):
category=UserWarning,
stacklevel=4,
)
if (
any([header_name == "api-key" for (header_name, _) in self._grpc_headers])
or "api-key" in self._rest_headers
):
show_warning_once(
message="`api-key` has been passed in `headers`, but it will be overridden with `api_key` parameter value",
category=UserWarning,
stacklevel=4,
)
self._rest_headers["api-key"] = api_key
self._grpc_headers.append(("api-key", api_key))
client_version = importlib.metadata.version("qdrant-client")
python_version = platform.python_version()
user_agent = f"python-client/{client_version} python/{python_version}"
if "User-Agent" in self._rest_headers:
show_warning_once(
f"`User-Agent` has been passed in `headers`, but it will be overridden with the builtin value: `{user_agent}`."
)
if grpc_options is not None and "grpc.primary_user_agent" in grpc_options:
show_warning_once(
message=f"`grpc.primary_user_agent will be overridden with the builtin value: {user_agent}`.",
category=UserWarning,
stacklevel=4,
)
self._rest_headers["User-Agent"] = user_agent
self._grpc_options["grpc.primary_user_agent"] = user_agent
grpc_compression: Compression | None = kwargs.pop("grpc_compression", None)

View File

@@ -76,6 +76,7 @@ class QdrantClient(QdrantFastembedMixin):
local_inference_batch_size: inference batch size used by fastembed when using local inference with `models.Document` and other models.
pool_size: connection pool size, Default: None. Default value for gRPC connection pool is 3, rest default is
inherited from `httpx` (default: 100)
headers: Custom headers to send with every request.
**kwargs: Additional arguments passed directly into REST client initialization
"""
@@ -99,6 +100,7 @@ class QdrantClient(QdrantFastembedMixin):
local_inference_batch_size: int | None = None,
check_compatibility: bool = True,
pool_size: int | None = None,
headers: dict[str, str] | None = None,
**kwargs: Any,
):
# Saving the init options to facilitate building AsyncQdrantClient from QdrantClient and vice versa.
@@ -143,6 +145,7 @@ class QdrantClient(QdrantFastembedMixin):
auth_token_provider=auth_token_provider,
check_compatibility=check_compatibility,
pool_size=pool_size,
headers=headers,
**kwargs,
)

View File

@@ -61,6 +61,7 @@ class QdrantRemote(QdrantBase):
auth_token_provider: Callable[[], str] | Callable[[], Awaitable[str]] | None = None,
check_compatibility: bool = True,
pool_size: int | None = None,
headers: dict[str, str] | None = None,
**kwargs: Any,
):
super().__init__(**kwargs)
@@ -144,6 +145,12 @@ class QdrantRemote(QdrantBase):
http2 = kwargs.pop("http2", False)
self._grpc_headers = []
self._rest_headers = {k: v for k, v in kwargs.pop("metadata", {}).items()}
if headers:
for key, value in headers.items():
self._rest_headers[key] = value
self._grpc_headers.append((key, value))
if api_key is not None:
if self._scheme == "http":
show_warning(
@@ -153,13 +160,34 @@ class QdrantRemote(QdrantBase):
)
# http2 = True
if (
any([header_name == "api-key" for header_name, _ in self._grpc_headers])
or "api-key" in self._rest_headers
):
show_warning_once(
message="`api-key` has been passed in `headers`, but it will be overridden with `api_key` "
"parameter value",
category=UserWarning,
stacklevel=4,
)
self._rest_headers["api-key"] = api_key
self._grpc_headers.append(("api-key", api_key))
client_version = importlib.metadata.version("qdrant-client")
python_version = platform.python_version()
user_agent = f"python-client/{client_version} python/{python_version}"
if "User-Agent" in self._rest_headers:
show_warning_once(
"`User-Agent` has been passed in `headers`, but "
f"it will be overridden with the builtin value: `{user_agent}`."
)
if grpc_options is not None and "grpc.primary_user_agent" in grpc_options:
show_warning_once(
message=f"`grpc.primary_user_agent will be overridden with the builtin value: {user_agent}`.",
category=UserWarning,
stacklevel=4,
)
self._rest_headers["User-Agent"] = user_agent
self._grpc_options["grpc.primary_user_agent"] = user_agent