From c95b345cdee09ae3b42db8a336f48c7ff14596ed Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 30 Jul 2024 10:43:00 -0700
Subject: [PATCH 01/18] [community]: Render documents to graphviz
- **Description:** Adds a helper that renders documents with the
GraphVectorStore metadata fields to Graphviz for visualization.
This is helpful for understanding and debugging.
---
.../graph_vectorstores/visualize.py | 76 +++++++++++++++++++
.../graph_vectorstores/test_visualize.py | 64 ++++++++++++++++
2 files changed, 140 insertions(+)
create mode 100644 libs/community/langchain_community/graph_vectorstores/visualize.py
create mode 100644 libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
new file mode 100644
index 0000000000000..acfee9640d5d7
--- /dev/null
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -0,0 +1,76 @@
+from typing import Dict, Iterable, Optional, Set
+from langchain_core.documents import Document
+from langchain_core.graph_vectorstores.links import get_links
+
+def _escape_id(id: str) -> str:
+ return id.replace(':', '_')
+
+_EDGE_DIRECTION = {
+ "in": "back",
+ "out": "forward",
+ "bidir": "both",
+}
+
+def render_graphviz(
+ documents: Iterable[Document],
+ node_color: str = "white",
+ node_colors: Optional[Dict[str, str]] = {},
+) -> "graphviz.Digraph":
+ """Render a collection of GraphVectorStore documents to GraphViz format.
+
+ Args:
+ documents: The documents to render.
+ node_color: General node color. Defaults to `white`.
+ node_colors: Dictionary specifying colors of specific nodes. Useful for
+ emphasizing nodes that were selected by MMR, or differ from other
+ results.
+
+ Returns:
+ The "graphviz.Digraph" representing the nodes. May be printed to source,
+ or rendered using `dot`.
+
+ Note:
+ To render the generated DOT source code, you also need to install Graphviz_
+ (`download page `_,
+ `archived versions `_,
+ `installation procedure for Windows `_).
+ """
+ if node_colors is None:
+ node_colors = {}
+ try:
+ import graphviz
+ except (ImportError, ModuleNotFoundError):
+ raise ImportError(
+ "Could not import graphviz python package. "
+ "Please install it with `pip install graphviz`."
+ )
+
+ try:
+ graphviz.version()
+ except graphviz.ExecutableNotFound:
+ raise ImportError(
+ "Could not execute `dot`. "
+ "Make sure graphviz executable is installed (see https://www.graphviz.org/download/)."
+ )
+
+ tags = set()
+
+ graph = graphviz.Digraph()
+ graph.attr("node", style="filled")
+ for document in documents:
+ id = document.id
+ if id is None:
+ raise ValueError(f"Illegal graph document without ID: {document}")
+ escaped_id = _escape_id(id)
+ color = node_colors.get(id) or node_color
+ graph.node(escaped_id, label = f"{id}", shape="note", fillcolor=color)
+
+ for link in get_links(document):
+ tag = f"{link.kind}_{link.tag}"
+ if tag not in tags:
+ graph.node(tag, label=f"{link.kind}:{link.tag}")
+ tags.add(tag)
+
+ graph.edge(escaped_id, tag, dir=_EDGE_DIRECTION[link.direction])
+ return graph
+
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
new file mode 100644
index 0000000000000..61889ccd68fd4
--- /dev/null
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -0,0 +1,64 @@
+from langchain_core.graph_vectorstores.links import METADATA_LINKS_KEY, Link
+from langchain_core.documents import Document
+from langchain_community.graph_vectorstores.visualize import render_graphviz
+
+def test_visualize_simple_graph():
+ doc1 = Document(
+ id = "a",
+ page_content = "some content",
+ metadata = {
+ METADATA_LINKS_KEY: [
+ Link.incoming("href", "a"),
+ Link.bidir("kw", "foo"),
+ ]
+ },
+ )
+ doc2 = Document(
+ id = "b",
+ page_content = "some more content",
+ metadata = {
+ METADATA_LINKS_KEY: [
+ Link.incoming("href", "b"),
+ Link.outgoing("href", "a"),
+ Link.bidir("kw", "foo"),
+ Link.bidir("kw", "bar"),
+ ]
+ },
+ )
+
+ assert render_graphviz([doc1, doc2]).source == (
+ 'digraph {\n'
+ '\tnode [style=filled]\n'
+ '\ta [label=a fillcolor=white shape=note]\n'
+ '\thref_a [label="href:a"]\n'
+ '\ta -> href_a [dir=back]\n'
+ '\tkw_foo [label="kw:foo"]\n'
+ '\ta -> kw_foo [dir=both]\n'
+ '\tb [label=b fillcolor=white shape=note]\n'
+ '\thref_b [label="href:b"]\n'
+ '\tb -> href_b [dir=back]\n'
+ '\tb -> href_a [dir=forward]\n'
+ '\tb -> kw_foo [dir=both]\n'
+ '\tkw_bar [label="kw:bar"]\n'
+ '\tb -> kw_bar [dir=both]\n'
+ '}\n'
+ )
+
+ assert render_graphviz([doc1, doc2],
+ node_colors = { "a": "gold"}).source == (
+ 'digraph {\n'
+ '\tnode [style=filled]\n'
+ '\ta [label=a fillcolor=gold shape=note]\n'
+ '\thref_a [label="href:a"]\n'
+ '\ta -> href_a [dir=back]\n'
+ '\tkw_foo [label="kw:foo"]\n'
+ '\ta -> kw_foo [dir=both]\n'
+ '\tb [label=b fillcolor=white shape=note]\n'
+ '\thref_b [label="href:b"]\n'
+ '\tb -> href_b [dir=back]\n'
+ '\tb -> href_a [dir=forward]\n'
+ '\tb -> kw_foo [dir=both]\n'
+ '\tkw_bar [label="kw:bar"]\n'
+ '\tb -> kw_bar [dir=both]\n'
+ '}\n'
+ )
\ No newline at end of file
From 59767f4a8cc819f24f2dd6849fe7df3c4e546db3 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:02:45 -0700
Subject: [PATCH 02/18] add page content as tooltip
---
libs/community/extended_testing_deps.txt | 1 +
.../graph_vectorstores/visualize.py | 26 +++++--
.../graph_vectorstores/test_visualize.py | 67 ++++++++++---------
3 files changed, 55 insertions(+), 39 deletions(-)
diff --git a/libs/community/extended_testing_deps.txt b/libs/community/extended_testing_deps.txt
index d9879fd6aa07c..be09a81eed33e 100644
--- a/libs/community/extended_testing_deps.txt
+++ b/libs/community/extended_testing_deps.txt
@@ -29,6 +29,7 @@ gliner>=0.2.7
google-cloud-documentai>=2.20.1,<3
gql>=3.4.1,<4
gradientai>=1.4.0,<2
+graphviz>=0.20.3,<0.21
hdbcli>=2.19.21,<3
hologres-vector==0.0.6
html2text>=2020.1.16
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index acfee9640d5d7..9679288914bcf 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -1,9 +1,15 @@
-from typing import Dict, Iterable, Optional, Set
+from typing import TYPE_CHECKING, Dict, Iterable, Optional
+
from langchain_core.documents import Document
from langchain_core.graph_vectorstores.links import get_links
+if TYPE_CHECKING:
+ import graphviz
+
+
def _escape_id(id: str) -> str:
- return id.replace(':', '_')
+ return id.replace(":", "_")
+
_EDGE_DIRECTION = {
"in": "back",
@@ -11,10 +17,11 @@ def _escape_id(id: str) -> str:
"bidir": "both",
}
+
def render_graphviz(
- documents: Iterable[Document],
- node_color: str = "white",
- node_colors: Optional[Dict[str, str]] = {},
+ documents: Iterable[Document],
+ node_color: str = "white",
+ node_colors: Optional[Dict[str, str]] = {},
) -> "graphviz.Digraph":
"""Render a collection of GraphVectorStore documents to GraphViz format.
@@ -63,7 +70,13 @@ def render_graphviz(
raise ValueError(f"Illegal graph document without ID: {document}")
escaped_id = _escape_id(id)
color = node_colors.get(id) or node_color
- graph.node(escaped_id, label = f"{id}", shape="note", fillcolor=color)
+ graph.node(
+ escaped_id,
+ label=f"{id}",
+ shape="note",
+ fillcolor=color,
+ tooltip=document.page_content,
+ )
for link in get_links(document):
tag = f"{link.kind}_{link.tag}"
@@ -73,4 +86,3 @@ def render_graphviz(
graph.edge(escaped_id, tag, dir=_EDGE_DIRECTION[link.direction])
return graph
-
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 61889ccd68fd4..abef5be8f6e90 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -1,12 +1,16 @@
-from langchain_core.graph_vectorstores.links import METADATA_LINKS_KEY, Link
+import pytest
from langchain_core.documents import Document
+from langchain_core.graph_vectorstores.links import METADATA_LINKS_KEY, Link
+
from langchain_community.graph_vectorstores.visualize import render_graphviz
+
+@pytest.mark.requires("graphviz")
def test_visualize_simple_graph():
doc1 = Document(
- id = "a",
- page_content = "some content",
- metadata = {
+ id="a",
+ page_content="some content",
+ metadata={
METADATA_LINKS_KEY: [
Link.incoming("href", "a"),
Link.bidir("kw", "foo"),
@@ -14,9 +18,9 @@ def test_visualize_simple_graph():
},
)
doc2 = Document(
- id = "b",
- page_content = "some more content",
- metadata = {
+ id="b",
+ page_content="some more content",
+ metadata={
METADATA_LINKS_KEY: [
Link.incoming("href", "b"),
Link.outgoing("href", "a"),
@@ -27,38 +31,37 @@ def test_visualize_simple_graph():
)
assert render_graphviz([doc1, doc2]).source == (
- 'digraph {\n'
- '\tnode [style=filled]\n'
- '\ta [label=a fillcolor=white shape=note]\n'
+ "digraph {\n"
+ "\tnode [style=filled]\n"
+ "\ta [label=a fillcolor=white shape=note]\n"
'\thref_a [label="href:a"]\n'
- '\ta -> href_a [dir=back]\n'
+ "\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
- '\ta -> kw_foo [dir=both]\n'
- '\tb [label=b fillcolor=white shape=note]\n'
+ "\ta -> kw_foo [dir=both]\n"
+ "\tb [label=b fillcolor=white shape=note]\n"
'\thref_b [label="href:b"]\n'
- '\tb -> href_b [dir=back]\n'
- '\tb -> href_a [dir=forward]\n'
- '\tb -> kw_foo [dir=both]\n'
+ "\tb -> href_b [dir=back]\n"
+ "\tb -> href_a [dir=forward]\n"
+ "\tb -> kw_foo [dir=both]\n"
'\tkw_bar [label="kw:bar"]\n'
- '\tb -> kw_bar [dir=both]\n'
- '}\n'
+ "\tb -> kw_bar [dir=both]\n"
+ "}\n"
)
- assert render_graphviz([doc1, doc2],
- node_colors = { "a": "gold"}).source == (
- 'digraph {\n'
- '\tnode [style=filled]\n'
- '\ta [label=a fillcolor=gold shape=note]\n'
+ assert render_graphviz([doc1, doc2], node_colors={"a": "gold"}).source == (
+ "digraph {\n"
+ "\tnode [style=filled]\n"
+ "\ta [label=a fillcolor=gold shape=note]\n"
'\thref_a [label="href:a"]\n'
- '\ta -> href_a [dir=back]\n'
+ "\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
- '\ta -> kw_foo [dir=both]\n'
- '\tb [label=b fillcolor=white shape=note]\n'
+ "\ta -> kw_foo [dir=both]\n"
+ "\tb [label=b fillcolor=white shape=note]\n"
'\thref_b [label="href:b"]\n'
- '\tb -> href_b [dir=back]\n'
- '\tb -> href_a [dir=forward]\n'
- '\tb -> kw_foo [dir=both]\n'
+ "\tb -> href_b [dir=back]\n"
+ "\tb -> href_a [dir=forward]\n"
+ "\tb -> kw_foo [dir=both]\n"
'\tkw_bar [label="kw:bar"]\n'
- '\tb -> kw_bar [dir=both]\n'
- '}\n'
- )
\ No newline at end of file
+ "\tb -> kw_bar [dir=both]\n"
+ "}\n"
+ )
From 9598ea425bf055dc8f5aa3feb9461e3f88105b82 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:03:42 -0700
Subject: [PATCH 03/18] escape
---
.../langchain_community/graph_vectorstores/visualize.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 9679288914bcf..2f9d52e548a26 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -72,16 +72,16 @@ def render_graphviz(
color = node_colors.get(id) or node_color
graph.node(
escaped_id,
- label=f"{id}",
+ label=graphviz.escape(id),
shape="note",
fillcolor=color,
- tooltip=document.page_content,
+ tooltip=graphviz.escape(document.page_content),
)
for link in get_links(document):
tag = f"{link.kind}_{link.tag}"
if tag not in tags:
- graph.node(tag, label=f"{link.kind}:{link.tag}")
+ graph.node(tag, label=graphviz.escape(f"{link.kind}:{link.tag}"))
tags.add(tag)
graph.edge(escaped_id, tag, dir=_EDGE_DIRECTION[link.direction])
From 2f88fa897fe64bffccc726de299c6be5a7b726d3 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:06:12 -0700
Subject: [PATCH 04/18] update tests
---
.../unit_tests/graph_vectorstores/test_visualize.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index abef5be8f6e90..a6663dc36d5b4 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -19,7 +19,7 @@ def test_visualize_simple_graph():
)
doc2 = Document(
id="b",
- page_content="some more content",
+ page_content="",
metadata={
METADATA_LINKS_KEY: [
Link.incoming("href", "b"),
@@ -33,12 +33,12 @@ def test_visualize_simple_graph():
assert render_graphviz([doc1, doc2]).source == (
"digraph {\n"
"\tnode [style=filled]\n"
- "\ta [label=a fillcolor=white shape=note]\n"
+ '\ta [label=a fillcolor=white shape=note tooltip="some content"]\n'
'\thref_a [label="href:a"]\n'
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- "\tb [label=b fillcolor=white shape=note]\n"
+ '\tb [label=b fillcolor=white shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
@@ -51,12 +51,12 @@ def test_visualize_simple_graph():
assert render_graphviz([doc1, doc2], node_colors={"a": "gold"}).source == (
"digraph {\n"
"\tnode [style=filled]\n"
- "\ta [label=a fillcolor=gold shape=note]\n"
+ '\ta [label=a fillcolor=gold shape=note tooltip="some content"]\n'
'\thref_a [label="href:a"]\n'
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- "\tb [label=b fillcolor=white shape=note]\n"
+ '\tb [label=b fillcolor=white shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
From 6d88b202b1bd1d9f41dccb181f26139fc9cf5a30 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:45:46 -0700
Subject: [PATCH 05/18] include page content prefix
---
.../graph_vectorstores/visualize.py | 21 ++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 2f9d52e548a26..db3c873cb7a88 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -1,4 +1,5 @@
from typing import TYPE_CHECKING, Dict, Iterable, Optional
+import re
from langchain_core.documents import Document
from langchain_core.graph_vectorstores.links import get_links
@@ -17,6 +18,21 @@ def _escape_id(id: str) -> str:
"bidir": "both",
}
+_WORD_RE = re.compile('\s*\S+')
+
+def _split_prefix(s: str, max_chars: int = 50) -> str:
+ words = _WORD_RE.finditer(s)
+
+ split = min(len(s), max_chars)
+ for word in words:
+ if word.end(0) > max_chars:
+ break
+ split = word.end(0)
+
+ if split == len(s):
+ return s
+ else:
+ return f"{s[0:split]}..."
def render_graphviz(
documents: Iterable[Document],
@@ -70,9 +86,12 @@ def render_graphviz(
raise ValueError(f"Illegal graph document without ID: {document}")
escaped_id = _escape_id(id)
color = node_colors.get(id) or node_color
+
+
+ node_label=f"{graphviz.escape(id)}\n{graphviz.escape(_split_prefix(document.page_content))}"
graph.node(
escaped_id,
- label=graphviz.escape(id),
+ label=node_label,
shape="note",
fillcolor=color,
tooltip=graphviz.escape(document.page_content),
From b87d802e885be47067b57c86c0626d6028cb6341 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Wed, 31 Jul 2024 08:04:03 -0700
Subject: [PATCH 06/18] allow none colors
---
.../graph_vectorstores/visualize.py | 8 ++---
.../graph_vectorstores/test_visualize.py | 29 ++++++++++++++++---
2 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index db3c873cb7a88..7e11e51ecd1e8 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -36,8 +36,8 @@ def _split_prefix(s: str, max_chars: int = 50) -> str:
def render_graphviz(
documents: Iterable[Document],
- node_color: str = "white",
- node_colors: Optional[Dict[str, str]] = {},
+ node_color: Optional[str] = None,
+ node_colors: Optional[Dict[str, Optional[str]]] = {},
) -> "graphviz.Digraph":
"""Render a collection of GraphVectorStore documents to GraphViz format.
@@ -79,14 +79,14 @@ def render_graphviz(
tags = set()
graph = graphviz.Digraph()
+ graph.attr(rankdir="LR")
graph.attr("node", style="filled")
for document in documents:
id = document.id
if id is None:
raise ValueError(f"Illegal graph document without ID: {document}")
escaped_id = _escape_id(id)
- color = node_colors.get(id) or node_color
-
+ color = node_colors[id] if id in node_colors else node_color
node_label=f"{graphviz.escape(id)}\n{graphviz.escape(_split_prefix(document.page_content))}"
graph.node(
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index a6663dc36d5b4..6638df8c6b4b8 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -32,13 +32,14 @@ def test_visualize_simple_graph():
assert render_graphviz([doc1, doc2]).source == (
"digraph {\n"
+ "\trankdir=LR\n"
"\tnode [style=filled]\n"
- '\ta [label=a fillcolor=white shape=note tooltip="some content"]\n'
+ '\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
'\thref_a [label="href:a"]\n'
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- '\tb [label=b fillcolor=white shape=note tooltip=""]\n'
+ '\tb [label="b\n" shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
@@ -50,13 +51,33 @@ def test_visualize_simple_graph():
assert render_graphviz([doc1, doc2], node_colors={"a": "gold"}).source == (
"digraph {\n"
+ "\trankdir=LR\n"
"\tnode [style=filled]\n"
- '\ta [label=a fillcolor=gold shape=note tooltip="some content"]\n'
+ '\ta [label="a\nsome content" fillcolor=gold shape=note tooltip="some content"]\n'
'\thref_a [label="href:a"]\n'
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- '\tb [label=b fillcolor=white shape=note tooltip=""]\n'
+ '\tb [label="b\n" shape=note tooltip=""]\n'
+ '\thref_b [label="href:b"]\n'
+ "\tb -> href_b [dir=back]\n"
+ "\tb -> href_a [dir=forward]\n"
+ "\tb -> kw_foo [dir=both]\n"
+ '\tkw_bar [label="kw:bar"]\n'
+ "\tb -> kw_bar [dir=both]\n"
+ "}\n"
+ )
+
+ assert render_graphviz([doc1, doc2], node_color = "gold", node_colors={"a": None}).source == (
+ "digraph {\n"
+ "\trankdir=LR\n"
+ "\tnode [style=filled]\n"
+ '\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
+ '\thref_a [label="href:a"]\n'
+ "\ta -> href_a [dir=back]\n"
+ '\tkw_foo [label="kw:foo"]\n'
+ "\ta -> kw_foo [dir=both]\n"
+ '\tb [label="b\n" fillcolor=gold shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
From 3ee648f7a4c872554952233c0906ab52d54e2b93 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Wed, 31 Jul 2024 08:14:07 -0700
Subject: [PATCH 07/18] allow setting the engine
---
.../langchain_community/graph_vectorstores/visualize.py | 4 +++-
.../tests/unit_tests/graph_vectorstores/test_visualize.py | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 7e11e51ecd1e8..ccc7621e082d5 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -36,6 +36,7 @@ def _split_prefix(s: str, max_chars: int = 50) -> str:
def render_graphviz(
documents: Iterable[Document],
+ engine: Optional[str] = None,
node_color: Optional[str] = None,
node_colors: Optional[Dict[str, Optional[str]]] = {},
) -> "graphviz.Digraph":
@@ -43,6 +44,7 @@ def render_graphviz(
Args:
documents: The documents to render.
+ engine: GraphViz layout engine to use. `None` uses the default.
node_color: General node color. Defaults to `white`.
node_colors: Dictionary specifying colors of specific nodes. Useful for
emphasizing nodes that were selected by MMR, or differ from other
@@ -78,7 +80,7 @@ def render_graphviz(
tags = set()
- graph = graphviz.Digraph()
+ graph = graphviz.Digraph(engine=engine)
graph.attr(rankdir="LR")
graph.attr("node", style="filled")
for document in documents:
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 6638df8c6b4b8..f5be9c20c0cf4 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -49,6 +49,8 @@ def test_visualize_simple_graph():
"}\n"
)
+ assert render_graphviz([doc1, doc2], engine="fdp").engine == "fdp"
+
assert render_graphviz([doc1, doc2], node_colors={"a": "gold"}).source == (
"digraph {\n"
"\trankdir=LR\n"
From 3c3966e16c5fb022d4b020df1a6ce11ba17a40e8 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Wed, 31 Jul 2024 08:23:51 -0700
Subject: [PATCH 08/18] fix lint
---
.../graph_vectorstores/visualize.py | 13 +++++++++----
.../graph_vectorstores/test_visualize.py | 16 +++++++++++-----
2 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index ccc7621e082d5..5c6df76284bf7 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -1,5 +1,5 @@
-from typing import TYPE_CHECKING, Dict, Iterable, Optional
import re
+from typing import TYPE_CHECKING, Dict, Iterable, Optional
from langchain_core.documents import Document
from langchain_core.graph_vectorstores.links import get_links
@@ -18,7 +18,8 @@ def _escape_id(id: str) -> str:
"bidir": "both",
}
-_WORD_RE = re.compile('\s*\S+')
+_WORD_RE = re.compile("\s*\S+")
+
def _split_prefix(s: str, max_chars: int = 50) -> str:
words = _WORD_RE.finditer(s)
@@ -29,11 +30,12 @@ def _split_prefix(s: str, max_chars: int = 50) -> str:
break
split = word.end(0)
- if split == len(s):
+ if split == len(s):
return s
else:
return f"{s[0:split]}..."
+
def render_graphviz(
documents: Iterable[Document],
engine: Optional[str] = None,
@@ -90,7 +92,10 @@ def render_graphviz(
escaped_id = _escape_id(id)
color = node_colors[id] if id in node_colors else node_color
- node_label=f"{graphviz.escape(id)}\n{graphviz.escape(_split_prefix(document.page_content))}"
+ node_label = "\n".join([
+ graphviz.escape(id),
+ graphviz.escape(_split_prefix(document.page_content)),
+ ])
graph.node(
escaped_id,
label=node_label,
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index f5be9c20c0cf4..d6fdfee94de1a 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -39,7 +39,8 @@ def test_visualize_simple_graph():
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- '\tb [label="b\n" shape=note tooltip=""]\n'
+ '\tb [label="b\n" '
+ 'shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
@@ -55,12 +56,14 @@ def test_visualize_simple_graph():
"digraph {\n"
"\trankdir=LR\n"
"\tnode [style=filled]\n"
- '\ta [label="a\nsome content" fillcolor=gold shape=note tooltip="some content"]\n'
+ '\ta [label="a\nsome content" fillcolor=gold '
+ 'shape=note tooltip="some content"]\n'
'\thref_a [label="href:a"]\n'
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- '\tb [label="b\n" shape=note tooltip=""]\n'
+ '\tb [label="b\n" '
+ 'shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
@@ -70,7 +73,9 @@ def test_visualize_simple_graph():
"}\n"
)
- assert render_graphviz([doc1, doc2], node_color = "gold", node_colors={"a": None}).source == (
+ assert render_graphviz(
+ [doc1, doc2], node_color="gold", node_colors={"a": None}
+ ).source == (
"digraph {\n"
"\trankdir=LR\n"
"\tnode [style=filled]\n"
@@ -79,7 +84,8 @@ def test_visualize_simple_graph():
"\ta -> href_a [dir=back]\n"
'\tkw_foo [label="kw:foo"]\n'
"\ta -> kw_foo [dir=both]\n"
- '\tb [label="b\n" fillcolor=gold shape=note tooltip=""]\n'
+ '\tb [label="b\n" fillcolor=gold '
+ 'shape=note tooltip=""]\n'
'\thref_b [label="href:b"]\n'
"\tb -> href_b [dir=back]\n"
"\tb -> href_a [dir=forward]\n"
From c417e8f6ecae7b71e7085a5162f27e4374e9d676 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:08:04 -0700
Subject: [PATCH 09/18] fix issue with tag IDs; allow skipping tags
---
.../graph_vectorstores/visualize.py | 33 +++++++++++++------
1 file changed, 23 insertions(+), 10 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 5c6df76284bf7..fbcc81c3e0f87 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -1,5 +1,5 @@
import re
-from typing import TYPE_CHECKING, Dict, Iterable, Optional
+from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple
from langchain_core.documents import Document
from langchain_core.graph_vectorstores.links import get_links
@@ -40,17 +40,20 @@ def render_graphviz(
documents: Iterable[Document],
engine: Optional[str] = None,
node_color: Optional[str] = None,
- node_colors: Optional[Dict[str, Optional[str]]] = {},
+ node_colors: Optional[Dict[str, Optional[str]]] = None,
+ skip_tags: Iterable[Tuple[str, str]] = (),
) -> "graphviz.Digraph":
"""Render a collection of GraphVectorStore documents to GraphViz format.
Args:
documents: The documents to render.
engine: GraphViz layout engine to use. `None` uses the default.
- node_color: General node color. Defaults to `white`.
+ node_color: Default node color. Defaults to `white`.
node_colors: Dictionary specifying colors of specific nodes. Useful for
emphasizing nodes that were selected by MMR, or differ from other
results.
+ skip_tags: Set of tags to skip when rendering the graph. Specified as
+ tuples containing the kind and tag.
Returns:
The "graphviz.Digraph" representing the nodes. May be printed to source,
@@ -64,6 +67,9 @@ def render_graphviz(
"""
if node_colors is None:
node_colors = {}
+ if node_color is None:
+ node_color = "white"
+
try:
import graphviz
except (ImportError, ModuleNotFoundError):
@@ -80,11 +86,13 @@ def render_graphviz(
"Make sure graphviz executable is installed (see https://www.graphviz.org/download/)."
)
- tags = set()
-
graph = graphviz.Digraph(engine=engine)
graph.attr(rankdir="LR")
graph.attr("node", style="filled")
+
+ skip_tags = set(skip_tags)
+ tags = { }
+
for document in documents:
id = document.id
if id is None:
@@ -105,10 +113,15 @@ def render_graphviz(
)
for link in get_links(document):
- tag = f"{link.kind}_{link.tag}"
- if tag not in tags:
- graph.node(tag, label=graphviz.escape(f"{link.kind}:{link.tag}"))
- tags.add(tag)
+ tag_key = (link.kind, link.tag)
+ if tag_key in skip_tags:
+ continue
+
+ tag_id = tags.get(tag_key)
+ if tag_id is None:
+ tag_id = f"tag_{len(tags)}"
+ tags[tag_key] = tag_id
+ graph.node(tag_id, label=graphviz.escape(f"{link.kind}:{link.tag}"))
- graph.edge(escaped_id, tag, dir=_EDGE_DIRECTION[link.direction])
+ graph.edge(escaped_id, tag_id, dir=_EDGE_DIRECTION[link.direction])
return graph
From 21e1a9f2ca6c7f975034fb5cf8b33e01719c479d Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:14:57 -0700
Subject: [PATCH 10/18] lint
---
.../graph_vectorstores/visualize.py | 15 +++++++++------
.../graph_vectorstores/test_visualize.py | 2 +-
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index fbcc81c3e0f87..6810f91c3b03a 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -2,7 +2,8 @@
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple
from langchain_core.documents import Document
-from langchain_core.graph_vectorstores.links import get_links
+
+from langchain_community.graph_vectorstores.links import get_links
if TYPE_CHECKING:
import graphviz
@@ -91,7 +92,7 @@ def render_graphviz(
graph.attr("node", style="filled")
skip_tags = set(skip_tags)
- tags = { }
+ tags: dict[Tuple[str, str], str] = {}
for document in documents:
id = document.id
@@ -100,10 +101,12 @@ def render_graphviz(
escaped_id = _escape_id(id)
color = node_colors[id] if id in node_colors else node_color
- node_label = "\n".join([
- graphviz.escape(id),
- graphviz.escape(_split_prefix(document.page_content)),
- ])
+ node_label = "\n".join(
+ [
+ graphviz.escape(id),
+ graphviz.escape(_split_prefix(document.page_content)),
+ ]
+ )
graph.node(
escaped_id,
label=node_label,
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index d6fdfee94de1a..6e446af768ae2 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -6,7 +6,7 @@
@pytest.mark.requires("graphviz")
-def test_visualize_simple_graph():
+def test_visualize_simple_graph() -> None:
doc1 = Document(
id="a",
page_content="some content",
From e112ae7156eb684aca1b93ed3791a8fffe254de2 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:20:09 -0700
Subject: [PATCH 11/18] fix
---
.../tests/unit_tests/graph_vectorstores/test_visualize.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 6e446af768ae2..10601956a79a2 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -1,7 +1,7 @@
import pytest
from langchain_core.documents import Document
-from langchain_core.graph_vectorstores.links import METADATA_LINKS_KEY, Link
+from langchain_community.graph_vectorstores.links import METADATA_LINKS_KEY, Link
from langchain_community.graph_vectorstores.visualize import render_graphviz
From f547250556f72c4be060e8b7fe060a2322a5a9c7 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:25:40 -0700
Subject: [PATCH 12/18] fix test
---
.../langchain_community/graph_vectorstores/visualize.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 6810f91c3b03a..33bee819109de 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -79,14 +79,6 @@ def render_graphviz(
"Please install it with `pip install graphviz`."
)
- try:
- graphviz.version()
- except graphviz.ExecutableNotFound:
- raise ImportError(
- "Could not execute `dot`. "
- "Make sure graphviz executable is installed (see https://www.graphviz.org/download/)."
- )
-
graph = graphviz.Digraph(engine=engine)
graph.attr(rankdir="LR")
graph.attr("node", style="filled")
From 33dd684b7ae6611a5b0cae031d5e67d6595e8e0f Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:40:13 -0700
Subject: [PATCH 13/18] update tests
---
.../graph_vectorstores/test_visualize.py | 60 +++++++++----------
1 file changed, 30 insertions(+), 30 deletions(-)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 10601956a79a2..23518b833561c 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -35,18 +35,18 @@ def test_visualize_simple_graph() -> None:
"\trankdir=LR\n"
"\tnode [style=filled]\n"
'\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
- '\thref_a [label="href:a"]\n'
- "\ta -> href_a [dir=back]\n"
- '\tkw_foo [label="kw:foo"]\n'
- "\ta -> kw_foo [dir=both]\n"
+ '\tag_0 [label="href:a"]\n'
+ "\ta -> tag_0 [dir=back]\n"
+ '\ttag_1 [label="kw:foo"]\n'
+ "\ta -> tag_1 [dir=both]\n"
'\tb [label="b\n" '
'shape=note tooltip=""]\n'
- '\thref_b [label="href:b"]\n'
- "\tb -> href_b [dir=back]\n"
- "\tb -> href_a [dir=forward]\n"
- "\tb -> kw_foo [dir=both]\n"
- '\tkw_bar [label="kw:bar"]\n'
- "\tb -> kw_bar [dir=both]\n"
+ '\ttag_2 [label="href:b"]\n'
+ "\tb -> tag_2 [dir=back]\n"
+ "\tb -> tag_0 [dir=forward]\n"
+ "\tb -> tag_1 [dir=both]\n"
+ '\ttag_3 [label="kw:bar"]\n'
+ "\tb -> tag_3 [dir=both]\n"
"}\n"
)
@@ -58,18 +58,18 @@ def test_visualize_simple_graph() -> None:
"\tnode [style=filled]\n"
'\ta [label="a\nsome content" fillcolor=gold '
'shape=note tooltip="some content"]\n'
- '\thref_a [label="href:a"]\n'
- "\ta -> href_a [dir=back]\n"
- '\tkw_foo [label="kw:foo"]\n'
- "\ta -> kw_foo [dir=both]\n"
+ '\tag_0 [label="href:a"]\n'
+ "\ta -> tag_0 [dir=back]\n"
+ '\ttag_1 [label="kw:foo"]\n'
+ "\ta -> tag_1 [dir=both]\n"
'\tb [label="b\n" '
'shape=note tooltip=""]\n'
- '\thref_b [label="href:b"]\n'
- "\tb -> href_b [dir=back]\n"
- "\tb -> href_a [dir=forward]\n"
- "\tb -> kw_foo [dir=both]\n"
- '\tkw_bar [label="kw:bar"]\n'
- "\tb -> kw_bar [dir=both]\n"
+ '\ttag_2 [label="href:b"]\n'
+ "\tb -> tag_2 [dir=back]\n"
+ "\tb -> tag_0 [dir=forward]\n"
+ "\tb -> tag_1 [dir=both]\n"
+ '\ttag_3 [label="kw:bar"]\n'
+ "\tb -> tag_3 [dir=both]\n"
"}\n"
)
@@ -80,17 +80,17 @@ def test_visualize_simple_graph() -> None:
"\trankdir=LR\n"
"\tnode [style=filled]\n"
'\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
- '\thref_a [label="href:a"]\n'
- "\ta -> href_a [dir=back]\n"
- '\tkw_foo [label="kw:foo"]\n'
- "\ta -> kw_foo [dir=both]\n"
+ '\ttag_0 [label="href:a"]\n'
+ "\ta -> tag_0 [dir=back]\n"
+ '\ttag_1 [label="kw:foo"]\n'
+ "\ta -> tag_1 [dir=both]\n"
'\tb [label="b\n" fillcolor=gold '
'shape=note tooltip=""]\n'
- '\thref_b [label="href:b"]\n'
- "\tb -> href_b [dir=back]\n"
- "\tb -> href_a [dir=forward]\n"
- "\tb -> kw_foo [dir=both]\n"
- '\tkw_bar [label="kw:bar"]\n'
- "\tb -> kw_bar [dir=both]\n"
+ '\ttag_2 [label="href:b"]\n'
+ "\tb -> tag_2 [dir=back]\n"
+ "\tb -> tag_0 [dir=forward]\n"
+ "\tb -> tag_1 [dir=both]\n"
+ '\ttag_3 [label="kw:bar"]\n'
+ "\tb -> tag_3 [dir=both]\n"
"}\n"
)
From 5aaefcbbb339a7f207a6a1f1a70d522390f537c6 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:42:43 -0700
Subject: [PATCH 14/18] fix
---
.../tests/unit_tests/graph_vectorstores/test_visualize.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 23518b833561c..a20ac1e510f06 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -35,7 +35,7 @@ def test_visualize_simple_graph() -> None:
"\trankdir=LR\n"
"\tnode [style=filled]\n"
'\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
- '\tag_0 [label="href:a"]\n'
+ '\ttag_0 [label="href:a"]\n'
"\ta -> tag_0 [dir=back]\n"
'\ttag_1 [label="kw:foo"]\n'
"\ta -> tag_1 [dir=both]\n"
@@ -58,7 +58,7 @@ def test_visualize_simple_graph() -> None:
"\tnode [style=filled]\n"
'\ta [label="a\nsome content" fillcolor=gold '
'shape=note tooltip="some content"]\n'
- '\tag_0 [label="href:a"]\n'
+ '\ttag_0 [label="href:a"]\n'
"\ta -> tag_0 [dir=back]\n"
'\ttag_1 [label="kw:foo"]\n'
"\ta -> tag_1 [dir=both]\n"
From c027c0e5b9300825b34aeb0dc4f7a3459e03b589 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:54:47 -0700
Subject: [PATCH 15/18] fix
---
.../langchain_community/graph_vectorstores/visualize.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index 33bee819109de..e9ba1a5f0a908 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -49,7 +49,7 @@ def render_graphviz(
Args:
documents: The documents to render.
engine: GraphViz layout engine to use. `None` uses the default.
- node_color: Default node color. Defaults to `white`.
+ node_color: Default node color.
node_colors: Dictionary specifying colors of specific nodes. Useful for
emphasizing nodes that were selected by MMR, or differ from other
results.
@@ -68,8 +68,6 @@ def render_graphviz(
"""
if node_colors is None:
node_colors = {}
- if node_color is None:
- node_color = "white"
try:
import graphviz
From ffa3e0dc44802412be5dc340bf681402ec8e1af1 Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 15:01:56 -0700
Subject: [PATCH 16/18] add test for skip_tags
---
.../graph_vectorstores/test_visualize.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index a20ac1e510f06..219e2cc81a83f 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -94,3 +94,22 @@ def test_visualize_simple_graph() -> None:
"\tb -> tag_3 [dir=both]\n"
"}\n"
)
+
+ assert render_graphviz(
+ [doc1, doc2], skip_tags=[("kw", "foo")]
+ ).source == (
+ "digraph {\n"
+ "\trankdir=LR\n"
+ "\tnode [style=filled]\n"
+ '\ta [label="a\nsome content" shape=note tooltip="some content"]\n'
+ '\ttag_0 [label="href:a"]\n'
+ "\ta -> tag_0 [dir=back]\n"
+ '\tb [label="b\n" '
+ 'shape=note tooltip=""]\n'
+ '\ttag_1 [label="href:b"]\n'
+ "\tb -> tag_1 [dir=back]\n"
+ "\tb -> tag_0 [dir=forward]\n"
+ '\ttag_2 [label="kw:bar"]\n'
+ "\tb -> tag_2 [dir=both]\n"
+ "}\n"
+ )
\ No newline at end of file
From 11341c18b3a72cb37dbc4af6700261522970b12a Mon Sep 17 00:00:00 2001
From: Ben Chambers <35960+bjchambers@users.noreply.github.com>
Date: Tue, 24 Sep 2024 15:17:55 -0700
Subject: [PATCH 17/18] lint
---
.../tests/unit_tests/graph_vectorstores/test_visualize.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
index 219e2cc81a83f..89615c0bfe6b9 100644
--- a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
+++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py
@@ -95,9 +95,7 @@ def test_visualize_simple_graph() -> None:
"}\n"
)
- assert render_graphviz(
- [doc1, doc2], skip_tags=[("kw", "foo")]
- ).source == (
+ assert render_graphviz([doc1, doc2], skip_tags=[("kw", "foo")]).source == (
"digraph {\n"
"\trankdir=LR\n"
"\tnode [style=filled]\n"
@@ -112,4 +110,4 @@ def test_visualize_simple_graph() -> None:
'\ttag_2 [label="kw:bar"]\n'
"\tb -> tag_2 [dir=both]\n"
"}\n"
- )
\ No newline at end of file
+ )
From d65c905fe798a16884ac78d9533f6aa5df9ed644 Mon Sep 17 00:00:00 2001
From: Erick Friis
Date: Fri, 13 Dec 2024 17:59:34 -0800
Subject: [PATCH 18/18] x
---
.../langchain_community/graph_vectorstores/visualize.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py
index e9ba1a5f0a908..7dd0f1dcfcf20 100644
--- a/libs/community/langchain_community/graph_vectorstores/visualize.py
+++ b/libs/community/langchain_community/graph_vectorstores/visualize.py
@@ -1,6 +1,7 @@
import re
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple
+from langchain_core._api import beta
from langchain_core.documents import Document
from langchain_community.graph_vectorstores.links import get_links
@@ -37,6 +38,7 @@ def _split_prefix(s: str, max_chars: int = 50) -> str:
return f"{s[0:split]}..."
+@beta()
def render_graphviz(
documents: Iterable[Document],
engine: Optional[str] = None,