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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ VECTOR_INDEX_NAME_PREFIX=Vector_index

# Weaviate configuration
WEAVIATE_ENDPOINT=http://localhost:8080
WEAVIATE_GRPC_ENDPOINT=grpc://localhost:50051
WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih
WEAVIATE_BATCH_SIZE=100
WEAVIATE_TOKENIZATION=word
Expand Down
2 changes: 1 addition & 1 deletion api/providers/vdb/vdb-weaviate/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "dify-vdb-weaviate"
version = "0.0.1"

dependencies = [
"weaviate-client==4.20.5",
"weaviate-client==4.22.0",
]
description = "Dify vector store backend (dify-vdb-weaviate)."

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import atexit
import datetime
import importlib.metadata
import json
import logging
import threading
Expand Down Expand Up @@ -37,6 +38,48 @@
_weaviate_client: weaviate.WeaviateClient | None = None
_weaviate_client_lock = threading.Lock()

# Identifies Dify to Weaviate's server-side telemetry via the X-Weaviate-Client-Integration
# header, matching the identifier emitted by other official Weaviate integrations.
_INTEGRATION_NAME = "dify"


def _integration_version() -> str:
"""Best-effort Dify version reported in the integration telemetry header."""
version = (dify_config.project.version or "").strip()
if version:
return version
try:
return importlib.metadata.version("dify-api")
except Exception:
return "unknown"


def _register_integration(client: weaviate.WeaviateClient) -> None:
"""
Registers the ``X-Weaviate-Client-Integration`` telemetry header on the client.

The header value is ``dify/<version>`` and is sent on both HTTP and gRPC requests, letting
Weaviate's server attribute traffic to Dify in its telemetry (alongside the built-in
``X-Weaviate-Client`` header). Registration is best-effort: any failure — including older
weaviate-client releases that lack the ``integrations`` API — is swallowed so telemetry never
breaks client initialization.
"""
try:
from pydantic import Field
from weaviate.connect.integrations import _IntegrationConfig

value = f"{_INTEGRATION_NAME}/{_integration_version()}"

class _DifyIntegration(_IntegrationConfig):
integration: str = Field(
default=value,
serialization_alias="X-Weaviate-Client-Integration",
)

client.integrations.configure(_DifyIntegration())
except Exception:
logger.debug("Could not register Dify Weaviate integration header", exc_info=True)


def _shutdown_weaviate_client() -> None:
"""
Expand Down Expand Up @@ -159,6 +202,9 @@ def _init_client(self, config: WeaviateConfig) -> weaviate.WeaviateClient:
skip_init_checks=True, # Skip PyPI version check to avoid unnecessary HTTP requests
)

# Tag outgoing requests so Weaviate can attribute traffic to Dify in its telemetry.
_register_integration(client)

if not client.is_ready():
raise ConnectionError("Vector database is not ready")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Unit tests for the Weaviate client integration telemetry header.

Verifies that Dify registers the ``X-Weaviate-Client-Integration`` header on the Weaviate
client so server-side telemetry can attribute traffic to Dify, mirroring the behavior of other
official Weaviate integrations.
"""

import unittest
from typing import override
from unittest.mock import MagicMock, patch

from dify_vdb_weaviate import weaviate_vector as weaviate_vector_module
from dify_vdb_weaviate.weaviate_vector import (
_INTEGRATION_NAME,
WeaviateConfig,
WeaviateVector,
_integration_version,
_register_integration,
)


class TestIntegrationVersion(unittest.TestCase):
"""Tests for resolving the version reported in the integration header."""

def test_uses_dify_project_version_when_present(self):
with patch.object(weaviate_vector_module.dify_config.project, "version", "1.15.0"):
assert _integration_version() == "1.15.0"

def test_strips_whitespace_from_version(self):
with patch.object(weaviate_vector_module.dify_config.project, "version", " 1.15.0 "):
assert _integration_version() == "1.15.0"

def test_falls_back_to_package_metadata_when_version_empty(self):
with (
patch.object(weaviate_vector_module.dify_config.project, "version", ""),
patch("dify_vdb_weaviate.weaviate_vector.importlib.metadata.version", return_value="9.9.9"),
):
assert _integration_version() == "9.9.9"

def test_falls_back_to_unknown_when_everything_fails(self):
with (
patch.object(weaviate_vector_module.dify_config.project, "version", ""),
patch(
"dify_vdb_weaviate.weaviate_vector.importlib.metadata.version",
side_effect=Exception("no metadata"),
),
):
assert _integration_version() == "unknown"


class TestRegisterIntegration(unittest.TestCase):
"""Tests for registering the integration header on the client."""

def test_configures_header_with_dify_name_and_version(self):
client = MagicMock()
with patch.object(weaviate_vector_module.dify_config.project, "version", "1.15.0"):
_register_integration(client)

client.integrations.configure.assert_called_once()
config = client.integrations.configure.call_args.args[0]
header = config._to_header()
assert header == {"X-Weaviate-Client-Integration": "dify/1.15.0"}
assert _INTEGRATION_NAME == "dify"

def test_swallows_errors_so_init_never_breaks(self):
client = MagicMock()
client.integrations.configure.side_effect = Exception("old client without integrations API")

# Must not raise.
with patch.object(weaviate_vector_module.dify_config.project, "version", "1.15.0"):
_register_integration(client)


class TestInitClientRegistersIntegration(unittest.TestCase):
"""Ensures the integration header is registered during client initialization."""

@override
def setUp(self):
weaviate_vector_module._weaviate_client = None
self.config = WeaviateConfig(endpoint="http://localhost:8080", api_key="test-key")

@override
def tearDown(self):
weaviate_vector_module._weaviate_client = None

@patch("dify_vdb_weaviate.weaviate_vector.weaviate")
def test_init_client_registers_integration(self, mock_weaviate_module):
mock_client = MagicMock()
mock_client.is_ready.return_value = True
mock_weaviate_module.connect_to_custom.return_value = mock_client

with patch("dify_vdb_weaviate.weaviate_vector._register_integration") as mock_register:
WeaviateVector(
collection_name="Test_Collection_Node",
config=self.config,
attributes=["doc_id"],
)

mock_register.assert_called_once_with(mock_client)


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions api/tests/integration_tests/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ CONSOLE_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
VECTOR_STORE=weaviate
# Weaviate configuration
WEAVIATE_ENDPOINT=http://localhost:8080
WEAVIATE_GRPC_ENDPOINT=grpc://localhost:50051
WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih
WEAVIATE_BATCH_SIZE=100
WEAVIATE_TOKENIZATION=word
Expand Down
9 changes: 5 additions & 4 deletions api/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docker/docker-compose-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ services:

# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.27.0
image: cr.weaviate.io/semitechnologies/weaviate:1.38.1
profiles:
- weaviate
restart: always
Expand Down
2 changes: 1 addition & 1 deletion docker/docker-compose.middleware.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ services:

# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.27.0
image: cr.weaviate.io/semitechnologies/weaviate:1.38.1
profiles:
- ""
- weaviate
Expand Down
2 changes: 1 addition & 1 deletion docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ services:

# The Weaviate vector store.
weaviate:
image: semitechnologies/weaviate:1.27.0
image: cr.weaviate.io/semitechnologies/weaviate:1.38.1
profiles:
- weaviate
restart: always
Expand Down
1 change: 1 addition & 0 deletions docker/envs/middleware.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ EXPOSE_REDIS_PORT=6379
EXPOSE_SANDBOX_PORT=8194
EXPOSE_SSRF_PROXY_PORT=3128
EXPOSE_WEAVIATE_PORT=8080
EXPOSE_WEAVIATE_GRPC_PORT=50051

# ------------------------------
# Plugin Daemon Configuration
Expand Down