agentvis 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. agentvis-0.1.0/PKG-INFO +103 -0
  2. agentvis-0.1.0/README.md +90 -0
  3. agentvis-0.1.0/agentvis/__init__.py +10 -0
  4. agentvis-0.1.0/agentvis/core/__init__.py +1 -0
  5. agentvis-0.1.0/agentvis/core/connection_creation_strategy/__init__.py +3 -0
  6. agentvis-0.1.0/agentvis/core/connection_creation_strategy/base.py +14 -0
  7. agentvis-0.1.0/agentvis/core/connection_creation_strategy/context.py +15 -0
  8. agentvis-0.1.0/agentvis/core/connection_creation_strategy/helper.py +18 -0
  9. agentvis-0.1.0/agentvis/core/connection_creation_strategy/strategy_tool_to_tool.py +55 -0
  10. agentvis-0.1.0/agentvis/core/export/__init__.py +1 -0
  11. agentvis-0.1.0/agentvis/core/export/base.py +10 -0
  12. agentvis-0.1.0/agentvis/core/export/json.py +13 -0
  13. agentvis-0.1.0/agentvis/core/export/link.py +25 -0
  14. agentvis-0.1.0/agentvis/core/export/main.py +15 -0
  15. agentvis-0.1.0/agentvis/core/main.py +51 -0
  16. agentvis-0.1.0/agentvis/core/models.py +55 -0
  17. agentvis-0.1.0/agentvis/core/retriever_strategy/__init__.py +2 -0
  18. agentvis-0.1.0/agentvis/core/retriever_strategy/base.py +15 -0
  19. agentvis-0.1.0/agentvis/core/retriever_strategy/bm25.py +24 -0
  20. agentvis-0.1.0/agentvis/core/retriever_strategy/context.py +15 -0
  21. agentvis-0.1.0/agentvis/core/retriever_strategy/models.py +5 -0
  22. agentvis-0.1.0/agentvis/framework/__init__.py +0 -0
  23. agentvis-0.1.0/agentvis/framework/base.py +10 -0
  24. agentvis-0.1.0/agentvis/framework/langchain/__init__.py +1 -0
  25. agentvis-0.1.0/agentvis/framework/langchain/main.py +36 -0
  26. agentvis-0.1.0/agentvis.egg-info/PKG-INFO +103 -0
  27. agentvis-0.1.0/agentvis.egg-info/SOURCES.txt +30 -0
  28. agentvis-0.1.0/agentvis.egg-info/dependency_links.txt +1 -0
  29. agentvis-0.1.0/agentvis.egg-info/requires.txt +5 -0
  30. agentvis-0.1.0/agentvis.egg-info/top_level.txt +1 -0
  31. agentvis-0.1.0/pyproject.toml +27 -0
  32. agentvis-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentvis
3
+ Version: 0.1.0
4
+ Summary: Framework-agnostic reasoning trace visualization tool for AI agents.
5
+ Author: Nitesh Kumar
6
+ License: Apache License 2.0
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: bm25s==0.3.0
10
+ Requires-Dist: pydantic>=2.0.0
11
+ Provides-Extra: langchain
12
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
13
+
14
+ # agentvis
15
+
16
+ <p align="center">
17
+ <picture align="center">
18
+ <img align="center" alt="agentvis logo" src="https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/968caea6e04577583d7d16f7015b9925362d5f53/packages/ui/public/agentvis_logo.svg" width="560">
19
+ </picture>
20
+ </p>
21
+
22
+
23
+ *agentvis* visualizes an agent’s reasoning trace, giving you clear insight into *why* the agent chose a particular path or triggered a specific tool call.
24
+
25
+ By exposing the influence flow behind each decision, it transforms opaque agent behavior into something understandable and actionable — so you can confidently refine and improve your prompts, which is often the hardest part of building reliable agents. 🧠✨
26
+
27
+ ## *agentvis* reasoning visualization example
28
+
29
+ ![flow_diagram](https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/refs/heads/main/packages/py/images/vis.png)
30
+
31
+ ## Installation
32
+
33
+ Install core:
34
+
35
+ ```bash
36
+ pip install agentvis
37
+ ```
38
+
39
+ With LangChain support:
40
+
41
+ ```bash
42
+ pip install agentvis[langchain]
43
+ ```
44
+
45
+
46
+ ## How to generate an agent reasoning graph
47
+
48
+ ### 1. Define your LangChain / LangGraph agent
49
+
50
+ ```python
51
+ from langchain_tavily import TavilySearch
52
+ from langchain_google_genai import ChatGoogleGenerativeAI
53
+ from langchain.agents import create_agent
54
+ from langchain.messages import HumanMessage
55
+ import os
56
+
57
+ os.environ["TAVILY_API_KEY"] = "<your-tavily-api-key>"
58
+
59
+ tavily_search = TavilySearch(
60
+ max_results=5,
61
+ search_depth="basic",
62
+ )
63
+
64
+ llm = ChatGoogleGenerativeAI(
65
+ model="gemini-2.5-flash-lite",
66
+ temperature=0.7,
67
+ google_api_key="<your-google-api-key>",
68
+ )
69
+
70
+ agent = create_agent(
71
+ llm,
72
+ [tavily_search],
73
+ system_prompt=(
74
+ "You are a helpful web search agent. "
75
+ "Use the Tavily tool when you need fresh information from the web."
76
+ ),
77
+ )
78
+
79
+ result = agent.invoke(
80
+ {"messages": [HumanMessage("Top warm countries and weather of each.")]}
81
+ )
82
+ messages = result["messages"]
83
+ ```
84
+
85
+ ### 2. Build the reasoning graph with *agentvis* and get a shareable link in exchange of messages produced by agent
86
+
87
+ ```python
88
+ from agentvis.framework.langchain import LangChainAdapter
89
+ from agentvis.core.export import ExportFactory
90
+
91
+ # Convert LLM messages into an AgentGraph
92
+ graph = LangChainAdapter().build_agent_graph(messages)
93
+
94
+ # Export as a short link you can open in the UI
95
+ link = ExportFactory.export_graph(graph=graph, export_strategy="link") # or "json"
96
+ print(link)
97
+ ```
98
+
99
+ > Open the printed `link` in your browser to inspect the full reasoning trace of the `websearch` agent.
100
+
101
+ ## License
102
+
103
+ This project is licensed under the Apache License 2.0.
@@ -0,0 +1,90 @@
1
+ # agentvis
2
+
3
+ <p align="center">
4
+ <picture align="center">
5
+ <img align="center" alt="agentvis logo" src="https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/968caea6e04577583d7d16f7015b9925362d5f53/packages/ui/public/agentvis_logo.svg" width="560">
6
+ </picture>
7
+ </p>
8
+
9
+
10
+ *agentvis* visualizes an agent’s reasoning trace, giving you clear insight into *why* the agent chose a particular path or triggered a specific tool call.
11
+
12
+ By exposing the influence flow behind each decision, it transforms opaque agent behavior into something understandable and actionable — so you can confidently refine and improve your prompts, which is often the hardest part of building reliable agents. 🧠✨
13
+
14
+ ## *agentvis* reasoning visualization example
15
+
16
+ ![flow_diagram](https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/refs/heads/main/packages/py/images/vis.png)
17
+
18
+ ## Installation
19
+
20
+ Install core:
21
+
22
+ ```bash
23
+ pip install agentvis
24
+ ```
25
+
26
+ With LangChain support:
27
+
28
+ ```bash
29
+ pip install agentvis[langchain]
30
+ ```
31
+
32
+
33
+ ## How to generate an agent reasoning graph
34
+
35
+ ### 1. Define your LangChain / LangGraph agent
36
+
37
+ ```python
38
+ from langchain_tavily import TavilySearch
39
+ from langchain_google_genai import ChatGoogleGenerativeAI
40
+ from langchain.agents import create_agent
41
+ from langchain.messages import HumanMessage
42
+ import os
43
+
44
+ os.environ["TAVILY_API_KEY"] = "<your-tavily-api-key>"
45
+
46
+ tavily_search = TavilySearch(
47
+ max_results=5,
48
+ search_depth="basic",
49
+ )
50
+
51
+ llm = ChatGoogleGenerativeAI(
52
+ model="gemini-2.5-flash-lite",
53
+ temperature=0.7,
54
+ google_api_key="<your-google-api-key>",
55
+ )
56
+
57
+ agent = create_agent(
58
+ llm,
59
+ [tavily_search],
60
+ system_prompt=(
61
+ "You are a helpful web search agent. "
62
+ "Use the Tavily tool when you need fresh information from the web."
63
+ ),
64
+ )
65
+
66
+ result = agent.invoke(
67
+ {"messages": [HumanMessage("Top warm countries and weather of each.")]}
68
+ )
69
+ messages = result["messages"]
70
+ ```
71
+
72
+ ### 2. Build the reasoning graph with *agentvis* and get a shareable link in exchange of messages produced by agent
73
+
74
+ ```python
75
+ from agentvis.framework.langchain import LangChainAdapter
76
+ from agentvis.core.export import ExportFactory
77
+
78
+ # Convert LLM messages into an AgentGraph
79
+ graph = LangChainAdapter().build_agent_graph(messages)
80
+
81
+ # Export as a short link you can open in the UI
82
+ link = ExportFactory.export_graph(graph=graph, export_strategy="link") # or "json"
83
+ print(link)
84
+ ```
85
+
86
+ > Open the printed `link` in your browser to inspect the full reasoning trace of the `websearch` agent.
87
+
88
+ ## License
89
+
90
+ This project is licensed under the Apache License 2.0.
@@ -0,0 +1,10 @@
1
+ """
2
+ Top-level package for the AgentVis Python library.
3
+
4
+ The main, framework-agnostic APIs live under:
5
+
6
+ - ``agentvis.core`` – graph model, export helpers, strategies
7
+ - ``agentvis.framework`` – optional framework adapters (e.g. LangChain)
8
+ """
9
+
10
+ __all__ = ["core", "framework"]
@@ -0,0 +1 @@
1
+ from .main import BusinessLogic
@@ -0,0 +1,3 @@
1
+ from .context import ContextConnectionCreation
2
+ from .strategy_tool_to_tool import StrategyToolToTool
3
+ from .base import ConnectionCreationStrategy
@@ -0,0 +1,14 @@
1
+ from abc import ABC, abstractmethod
2
+ from agentvis.core.models import Node, Connection
3
+
4
+ class ConnectionCreationStrategy(ABC):
5
+ def __init__(self, nodes_matrix: list[list[Node]]):
6
+ self.nodes_matrix = nodes_matrix
7
+ self.connections = []
8
+
9
+ @abstractmethod
10
+ def create_connections(self) -> None:
11
+ pass
12
+
13
+ def get_connections(self) -> list[Connection]:
14
+ return self.connections
@@ -0,0 +1,15 @@
1
+ from agentvis.core.connection_creation_strategy.base import ConnectionCreationStrategy
2
+ from agentvis.core.models import Connection
3
+
4
+ class ContextConnectionCreation:
5
+ def __init__(self):
6
+ self.strategy = None
7
+
8
+ def set_strategy(self, strategy: ConnectionCreationStrategy) -> None:
9
+ self.strategy = strategy
10
+
11
+ def create_connections(self) -> None:
12
+ self.strategy.create_connections()
13
+
14
+ def get_connections(self) -> list[Connection]:
15
+ return self.strategy.get_connections()
@@ -0,0 +1,18 @@
1
+ from difflib import SequenceMatcher
2
+
3
+ def get_best_match(output_text: str, tool_input: str) -> dict:
4
+ matcher = SequenceMatcher(None, output_text, tool_input)
5
+ blocks = matcher.get_matching_blocks()
6
+ real_blocks = [b for b in blocks if b.size > 0]
7
+ if not real_blocks:
8
+ return None
9
+
10
+ best_block = max(real_blocks, key=lambda b: b.size)
11
+ coverage = best_block.size / len(tool_input)
12
+
13
+ return {
14
+ "start": best_block.a,
15
+ "end": best_block.a + best_block.size,
16
+ "matched_text": output_text[best_block.a: best_block.a + best_block.size].strip(),
17
+ "coverage": coverage,
18
+ }
@@ -0,0 +1,55 @@
1
+ from agentvis.core.connection_creation_strategy.base import ConnectionCreationStrategy
2
+ from agentvis.core.models import Connection, MessageType, Node, ConnectionData, ConnectionType, ToolOutputMatchDetails
3
+ from agentvis.core.retriever_strategy import ContextRetrieverStrategy, BM25RetrieverStrategy
4
+ from agentvis.core.connection_creation_strategy.helper import get_best_match
5
+
6
+ class StrategyToolToTool(ConnectionCreationStrategy):
7
+ def __init__(self, nodes_matrix: list[list[Node]]):
8
+ super().__init__(nodes_matrix)
9
+ self.context_retriever = ContextRetrieverStrategy()
10
+
11
+ def create_connections(self) -> None:
12
+ tool_outputs = [] # store tuple (node_id, output)
13
+
14
+ # iterate over each frame
15
+ for frame in self.nodes_matrix:
16
+ for node in frame:
17
+ if node.type != MessageType.ToolMessage.value:
18
+ break
19
+ tool_args = node.data["tool_args"]
20
+ for key,value in tool_args.items():
21
+ if not tool_outputs:
22
+ break
23
+ selected_document = self.context_retriever.retrieve(str(value))
24
+ if selected_document:
25
+ document_index, confidence_score = selected_document.document_index, selected_document.confidence_score
26
+ if confidence_score == 0.0:
27
+ continue
28
+ u,v = tool_outputs[document_index][0],node.id
29
+ tool_output_content = tool_outputs[document_index][1]
30
+ best_match = get_best_match(tool_output_content, value)
31
+ self.connections.append(
32
+ Connection(
33
+ id=f"{u}-{v}",
34
+ source=u,
35
+ target=v,
36
+ data=ConnectionData(
37
+ connection_type=ConnectionType.ToolToTool,
38
+ connection_details=ToolOutputMatchDetails(
39
+ target_tool_arg={key:value},
40
+ source_tool_output_matched_text=best_match["matched_text"] if best_match else "",
41
+ source_tool_ouput_start_index=best_match["start"] if best_match else 0,
42
+ source_tool_ouput_end_index=best_match["end"] if best_match else 0,
43
+ confidence_score=confidence_score
44
+ )
45
+ )
46
+ )
47
+ )
48
+
49
+ for node in frame:
50
+ if node.type != MessageType.ToolMessage.value:
51
+ break
52
+ if node.data["content"]:
53
+ tool_outputs.append((node.id, node.data["content"]))
54
+ self.context_retriever.set_strategy(BM25RetrieverStrategy(documents=[str(tool_output) for _,tool_output in tool_outputs]))
55
+ self.context_retriever.index()
@@ -0,0 +1 @@
1
+ from .main import ExportFactory
@@ -0,0 +1,10 @@
1
+ from abc import ABC, abstractmethod
2
+ from agentvis.core.models import AgentGraph
3
+
4
+ class ExportStrategy(ABC):
5
+ def __init__(self, graph: AgentGraph):
6
+ self.graph = graph
7
+
8
+ @abstractmethod
9
+ def export(self) -> str:
10
+ raise NotImplementedError
@@ -0,0 +1,13 @@
1
+ from agentvis.core.export.base import ExportStrategy
2
+ from agentvis.core.models import AgentGraph
3
+ import json
4
+
5
+ class JSONExportStrategy(ExportStrategy):
6
+ def __init__(self, graph: AgentGraph):
7
+ super().__init__(graph)
8
+
9
+ def export(self) -> str:
10
+ return json.dumps(
11
+ {"frames": [frame.model_dump() for frame in self.graph.frames], "connections": [connection.model_dump() for connection in self.graph.connections]},
12
+ separators=(",", ":")
13
+ )
@@ -0,0 +1,25 @@
1
+ from agentvis.core.export.base import ExportStrategy
2
+ from agentvis.core.models import AgentGraph
3
+ import json
4
+ import zlib
5
+ import base64
6
+
7
+ class LinkExportStrategy(ExportStrategy):
8
+ def __init__(self, graph: AgentGraph):
9
+ super().__init__(graph)
10
+ self.BASE_URL = "https://agentvis.vercel.app"
11
+
12
+ def export(self) -> str:
13
+ data = {
14
+ "frames": [frame.model_dump() for frame in self.graph.frames],
15
+ "connections": [
16
+ connection.model_dump()
17
+ for connection in self.graph.connections
18
+ ],
19
+ }
20
+
21
+ json_string = json.dumps(data, separators=(",", ":"))
22
+ compressed = zlib.compress(json_string.encode("utf-8"))
23
+ encoded = base64.urlsafe_b64encode(compressed).decode("utf-8")
24
+
25
+ return f"{self.BASE_URL}?view={encoded}"
@@ -0,0 +1,15 @@
1
+ from agentvis.core.models import AgentGraph
2
+ from typing import Literal
3
+ from agentvis.core.export.json import JSONExportStrategy
4
+ from agentvis.core.export.link import LinkExportStrategy
5
+
6
+ class ExportFactory:
7
+ @staticmethod
8
+ def export_graph(graph: AgentGraph, export_strategy: Literal["json", "link"] = "json") -> str:
9
+ match export_strategy:
10
+ case "json":
11
+ return JSONExportStrategy(graph).export()
12
+ case "link":
13
+ return LinkExportStrategy(graph).export()
14
+ case _:
15
+ raise ValueError(f"Invalid export strategy: {export_strategy}")
@@ -0,0 +1,51 @@
1
+ from agentvis.core.models import LLMMessage, Frame, Node, MessageType, Connection
2
+ from agentvis.core.connection_creation_strategy import ContextConnectionCreation, StrategyToolToTool
3
+ from collections import defaultdict
4
+
5
+ class BusinessLogic:
6
+ @staticmethod
7
+ def build_frames(messages: list[LLMMessage]) -> list[Frame]:
8
+ frames = []
9
+ tool_msg_start_idx, tool_msg_end_idx = -1, -1
10
+ function_to_args_map = defaultdict(dict)
11
+ for idx, message in enumerate(messages):
12
+ # if tool message, store the start and end index of the tool message
13
+ if message.type == MessageType.ToolMessage.value:
14
+ if tool_msg_start_idx == -1:
15
+ tool_msg_start_idx = idx
16
+ tool_msg_end_idx = idx
17
+ else:
18
+ if tool_msg_start_idx != -1 and tool_msg_end_idx != -1:
19
+ tool_nodes = [Node(id=tool_msg.id, type=tool_msg.type, data={"content": tool_msg.content, "tool_name": tool_msg.tool_name, "tool_args": function_to_args_map[tool_msg.tool_call_id][tool_msg.tool_name]}) for tool_msg in messages[tool_msg_start_idx:tool_msg_end_idx+1]]
20
+ frames.append(Frame(nodes=tool_nodes))
21
+ tool_msg_start_idx, tool_msg_end_idx = -1, -1
22
+ function_to_args_map = defaultdict(dict)
23
+
24
+ # simply add the frame
25
+ frames.append(Frame(
26
+ nodes=[Node(
27
+ id=message.id,
28
+ type=message.type,
29
+ data={"content": message.content}
30
+ )]
31
+ ))
32
+
33
+ # if ai message, store the function calls
34
+ if message.type == MessageType.AIMessage.value:
35
+ if message.tool_calls:
36
+ for tool_call in message.tool_calls:
37
+ function_to_args_map[tool_call.tool_call_id] = {tool_call.name: tool_call.args}
38
+
39
+ return frames
40
+
41
+ @staticmethod
42
+ def create_connections(frames: list[Frame]) -> list[Connection]:
43
+ nodes_matrix = []
44
+
45
+ for frame in frames:
46
+ nodes_matrix.append(frame.nodes)
47
+
48
+ context_connection_creation = ContextConnectionCreation()
49
+ context_connection_creation.set_strategy(StrategyToolToTool(nodes_matrix))
50
+ context_connection_creation.create_connections()
51
+ return context_connection_creation.get_connections()
@@ -0,0 +1,55 @@
1
+ from enum import Enum
2
+ from pydantic import BaseModel
3
+
4
+ # models for tree structure
5
+ class MessageType(str, Enum):
6
+ AIMessage = "AIMessage"
7
+ ToolMessage = "ToolMessage"
8
+ HumanMessage = "HumanMessage"
9
+ SystemMessage = "SystemMessage"
10
+
11
+ class Node(BaseModel):
12
+ id: str
13
+ type: MessageType
14
+ data: dict = {}
15
+
16
+ class ConnectionType(str, Enum):
17
+ ToolToTool = "ToolToTool"
18
+
19
+ class ToolOutputMatchDetails(BaseModel):
20
+ target_tool_arg: dict = {}
21
+ source_tool_output_matched_text: str = ""
22
+ source_tool_ouput_start_index: int = 0
23
+ source_tool_ouput_end_index: int = 0
24
+ confidence_score: float = 0.0
25
+
26
+ class ConnectionData(BaseModel):
27
+ connection_type: ConnectionType
28
+ connection_details: ToolOutputMatchDetails = None
29
+
30
+ class Connection(BaseModel):
31
+ id: str
32
+ source: str
33
+ target: str
34
+ data: ConnectionData = None
35
+
36
+ class Frame(BaseModel):
37
+ nodes: list[Node]
38
+
39
+ class AgentGraph(BaseModel):
40
+ frames: list[Frame]
41
+ connections: list[Connection]
42
+
43
+ # models for LLM messages
44
+ class ToolCall(BaseModel):
45
+ name: str
46
+ args: dict
47
+ tool_call_id: str
48
+
49
+ class LLMMessage(BaseModel):
50
+ id: str
51
+ type: MessageType
52
+ content: str = ""
53
+ tool_calls: list[ToolCall] = []
54
+ tool_name: str = ""
55
+ tool_call_id: str = ""
@@ -0,0 +1,2 @@
1
+ from .context import ContextRetrieverStrategy
2
+ from .bm25 import BM25RetrieverStrategy
@@ -0,0 +1,15 @@
1
+ from abc import ABC, abstractmethod
2
+ from agentvis.core.retriever_strategy.models import SelectedDocument
3
+
4
+ class RetrieverStrategy(ABC):
5
+ def __init__(self, documents: list[str]):
6
+ self.documents = documents
7
+ self.retriever = None
8
+
9
+ @abstractmethod
10
+ def index(self):
11
+ pass
12
+
13
+ @abstractmethod
14
+ def retrieve(self, query: str, k: int = 1) -> SelectedDocument:
15
+ pass
@@ -0,0 +1,24 @@
1
+ import bm25s
2
+ from agentvis.core.retriever_strategy.base import RetrieverStrategy
3
+ from agentvis.core.retriever_strategy.models import SelectedDocument
4
+
5
+ class BM25RetrieverStrategy(RetrieverStrategy):
6
+ def __init__(self, documents: list[str]):
7
+ super().__init__(documents)
8
+
9
+ def tokenize(self, text: str) -> list[str]:
10
+ return text.lower().strip().split()
11
+
12
+ def index(self):
13
+ self.retriever = bm25s.BM25()
14
+ tokenized_corpus = [self.tokenize(doc) for doc in self.documents]
15
+ self.retriever.index(tokenized_corpus)
16
+
17
+ def retrieve(self, query: str, k: int = 1) -> SelectedDocument:
18
+ try:
19
+ return SelectedDocument(
20
+ document_index=int(self.retriever.retrieve([self.tokenize(query)], k=k).documents[0][0]),
21
+ confidence_score=float(self.retriever.retrieve([self.tokenize(query)], k=k).scores[0][0])
22
+ )
23
+ except IndexError:
24
+ return None
@@ -0,0 +1,15 @@
1
+ from agentvis.core.retriever_strategy.base import RetrieverStrategy
2
+ from agentvis.core.retriever_strategy.models import SelectedDocument
3
+
4
+ class ContextRetrieverStrategy:
5
+ def __init__(self):
6
+ self.strategy = None
7
+
8
+ def set_strategy(self, strategy: RetrieverStrategy):
9
+ self.strategy = strategy
10
+
11
+ def index(self):
12
+ self.strategy.index()
13
+
14
+ def retrieve(self, query: str, k: int = 1) -> SelectedDocument:
15
+ return self.strategy.retrieve(query, k)
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+ class SelectedDocument(BaseModel):
4
+ document_index: int
5
+ confidence_score: float
File without changes
@@ -0,0 +1,10 @@
1
+ from abc import ABC, abstractmethod
2
+ from agentvis.core.models import AgentGraph
3
+
4
+ class AIFrameWork(ABC):
5
+ def __init__(self):
6
+ pass
7
+
8
+ @abstractmethod
9
+ def build_agent_graph(self, messages = []) -> AgentGraph:
10
+ raise NotImplementedError
@@ -0,0 +1 @@
1
+ from .main import LangChainAdapter
@@ -0,0 +1,36 @@
1
+ from agentvis.framework.base import AIFrameWork
2
+ from agentvis.core.models import LLMMessage, Frame, ToolCall, AgentGraph
3
+ from agentvis.core import BusinessLogic
4
+ from langchain.messages import ToolMessage, AIMessage, HumanMessage, SystemMessage
5
+
6
+ class LangChainAdapter(AIFrameWork):
7
+ def __init__(self):
8
+ super().__init__()
9
+
10
+ def build_agent_graph(self, messages: list[ToolMessage | AIMessage | HumanMessage | SystemMessage]) -> AgentGraph:
11
+ _messages: list[LLMMessage] = []
12
+
13
+ # convert messages to LLMMessage
14
+ for message in messages:
15
+ tool_calls = []
16
+ tool_name = ""
17
+ tool_call_id = ""
18
+
19
+ if isinstance(message, AIMessage):
20
+ tool_calls = [ToolCall(name=tool_call["name"], args=tool_call["args"], tool_call_id=tool_call["id"]) for tool_call in message.tool_calls]
21
+ if isinstance(message, ToolMessage):
22
+ tool_name = message.name
23
+ tool_call_id = message.tool_call_id
24
+
25
+ _messages.append(LLMMessage(
26
+ id=message.id,
27
+ type=message.__class__.__name__,
28
+ content=message.content,
29
+ tool_calls=tool_calls,
30
+ tool_name=tool_name,
31
+ tool_call_id=tool_call_id
32
+ ))
33
+
34
+ frames = BusinessLogic.build_frames(_messages)
35
+ connections = BusinessLogic.create_connections(frames)
36
+ return AgentGraph(frames=frames, connections=connections)
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentvis
3
+ Version: 0.1.0
4
+ Summary: Framework-agnostic reasoning trace visualization tool for AI agents.
5
+ Author: Nitesh Kumar
6
+ License: Apache License 2.0
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: bm25s==0.3.0
10
+ Requires-Dist: pydantic>=2.0.0
11
+ Provides-Extra: langchain
12
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
13
+
14
+ # agentvis
15
+
16
+ <p align="center">
17
+ <picture align="center">
18
+ <img align="center" alt="agentvis logo" src="https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/968caea6e04577583d7d16f7015b9925362d5f53/packages/ui/public/agentvis_logo.svg" width="560">
19
+ </picture>
20
+ </p>
21
+
22
+
23
+ *agentvis* visualizes an agent’s reasoning trace, giving you clear insight into *why* the agent chose a particular path or triggered a specific tool call.
24
+
25
+ By exposing the influence flow behind each decision, it transforms opaque agent behavior into something understandable and actionable — so you can confidently refine and improve your prompts, which is often the hardest part of building reliable agents. 🧠✨
26
+
27
+ ## *agentvis* reasoning visualization example
28
+
29
+ ![flow_diagram](https://raw.githubusercontent.com/kumarniteshpersonal-beep/agentvis/refs/heads/main/packages/py/images/vis.png)
30
+
31
+ ## Installation
32
+
33
+ Install core:
34
+
35
+ ```bash
36
+ pip install agentvis
37
+ ```
38
+
39
+ With LangChain support:
40
+
41
+ ```bash
42
+ pip install agentvis[langchain]
43
+ ```
44
+
45
+
46
+ ## How to generate an agent reasoning graph
47
+
48
+ ### 1. Define your LangChain / LangGraph agent
49
+
50
+ ```python
51
+ from langchain_tavily import TavilySearch
52
+ from langchain_google_genai import ChatGoogleGenerativeAI
53
+ from langchain.agents import create_agent
54
+ from langchain.messages import HumanMessage
55
+ import os
56
+
57
+ os.environ["TAVILY_API_KEY"] = "<your-tavily-api-key>"
58
+
59
+ tavily_search = TavilySearch(
60
+ max_results=5,
61
+ search_depth="basic",
62
+ )
63
+
64
+ llm = ChatGoogleGenerativeAI(
65
+ model="gemini-2.5-flash-lite",
66
+ temperature=0.7,
67
+ google_api_key="<your-google-api-key>",
68
+ )
69
+
70
+ agent = create_agent(
71
+ llm,
72
+ [tavily_search],
73
+ system_prompt=(
74
+ "You are a helpful web search agent. "
75
+ "Use the Tavily tool when you need fresh information from the web."
76
+ ),
77
+ )
78
+
79
+ result = agent.invoke(
80
+ {"messages": [HumanMessage("Top warm countries and weather of each.")]}
81
+ )
82
+ messages = result["messages"]
83
+ ```
84
+
85
+ ### 2. Build the reasoning graph with *agentvis* and get a shareable link in exchange of messages produced by agent
86
+
87
+ ```python
88
+ from agentvis.framework.langchain import LangChainAdapter
89
+ from agentvis.core.export import ExportFactory
90
+
91
+ # Convert LLM messages into an AgentGraph
92
+ graph = LangChainAdapter().build_agent_graph(messages)
93
+
94
+ # Export as a short link you can open in the UI
95
+ link = ExportFactory.export_graph(graph=graph, export_strategy="link") # or "json"
96
+ print(link)
97
+ ```
98
+
99
+ > Open the printed `link` in your browser to inspect the full reasoning trace of the `websearch` agent.
100
+
101
+ ## License
102
+
103
+ This project is licensed under the Apache License 2.0.
@@ -0,0 +1,30 @@
1
+ README.md
2
+ pyproject.toml
3
+ agentvis/__init__.py
4
+ agentvis.egg-info/PKG-INFO
5
+ agentvis.egg-info/SOURCES.txt
6
+ agentvis.egg-info/dependency_links.txt
7
+ agentvis.egg-info/requires.txt
8
+ agentvis.egg-info/top_level.txt
9
+ agentvis/core/__init__.py
10
+ agentvis/core/main.py
11
+ agentvis/core/models.py
12
+ agentvis/core/connection_creation_strategy/__init__.py
13
+ agentvis/core/connection_creation_strategy/base.py
14
+ agentvis/core/connection_creation_strategy/context.py
15
+ agentvis/core/connection_creation_strategy/helper.py
16
+ agentvis/core/connection_creation_strategy/strategy_tool_to_tool.py
17
+ agentvis/core/export/__init__.py
18
+ agentvis/core/export/base.py
19
+ agentvis/core/export/json.py
20
+ agentvis/core/export/link.py
21
+ agentvis/core/export/main.py
22
+ agentvis/core/retriever_strategy/__init__.py
23
+ agentvis/core/retriever_strategy/base.py
24
+ agentvis/core/retriever_strategy/bm25.py
25
+ agentvis/core/retriever_strategy/context.py
26
+ agentvis/core/retriever_strategy/models.py
27
+ agentvis/framework/__init__.py
28
+ agentvis/framework/base.py
29
+ agentvis/framework/langchain/__init__.py
30
+ agentvis/framework/langchain/main.py
@@ -0,0 +1,5 @@
1
+ bm25s==0.3.0
2
+ pydantic>=2.0.0
3
+
4
+ [langchain]
5
+ langchain>=0.1.0
@@ -0,0 +1 @@
1
+ agentvis
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "agentvis"
3
+ version = "0.1.0"
4
+ description = "Framework-agnostic reasoning trace visualization tool for AI agents."
5
+ readme = "README.md"
6
+ license = { text = "Apache License 2.0" }
7
+ requires-python = ">=3.9"
8
+
9
+ authors = [
10
+ { name = "Nitesh Kumar" }
11
+ ]
12
+
13
+ dependencies = [
14
+ "bm25s==0.3.0",
15
+ "pydantic>=2.0.0",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ langchain = ["langchain>=0.1.0"]
20
+
21
+ [build-system]
22
+ requires = ["setuptools>=61", "wheel"]
23
+ build-backend = "setuptools.build_meta"
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["."]
27
+ include = ["agentvis*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+