-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path15_langgraph-docs.py
More file actions
141 lines (115 loc) · 4.37 KB
/
15_langgraph-docs.py
File metadata and controls
141 lines (115 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import base64
from typing import List, TypedDict, Annotated, Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from IPython.display import Image, display # type: ignore
from langchain_core.runnables.graph_mermaid import MermaidDrawMethod # type: ignore
from dotenv import load_dotenv
load_dotenv()
class AgentState(TypedDict):
# The document provided
input_file: Optional[str] # Contains file path (PDF/PNG)
messages: Annotated[list[AnyMessage], add_messages]
vision_llm = ChatOpenAI(model="gpt-4o")
def extract_text(img_path: str) -> str:
"""
Extract text from an image file using a multimodal model.
Master Wayne often leaves notes with his training regimen or meal plans.
This allows me to properly analyze the contents.
"""
all_text = ""
try:
# Read image and encode as base64
with open(img_path, "rb") as image_file:
image_bytes = image_file.read()
image_base64 = base64.b64encode(image_bytes).decode("utf-8")
# Prepare the prompt including the base64 image data
message = [
HumanMessage(
content=[
{
"type": "text",
"text": (
"Extract all the text from this image. "
"Return only the extracted text, no explanations."
),
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_base64}"
},
},
]
)
]
# Call the vision-capable model
response = vision_llm.invoke(message)
# Append extracted text
all_text += response.content + "\n\n" # type: ignore
return all_text.strip()
except Exception as e:
# A butler should handle errors gracefully
error_msg = f"Error extracting text: {str(e)}"
print(error_msg)
return ""
def divide(a: int, b: int) -> float:
"""Divide a and b - for Master Wayne's occasional calculations."""
return a / b
# Equip the butler with tools
tools = [
divide,
extract_text
]
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
def assistant(state: AgentState):
# System message
textual_description_of_tool="""
extract_text(img_path: str) -> str:
Extract text from an image file using a multimodal model.
Args:
img_path: A local image file path (strings).
Returns:
A single string containing the concatenated text extracted from each image.
divide(a: int, b: int) -> float:
Divide a and b
"""
image=state["input_file"]
sys_msg = SystemMessage(content=f"You are a helpful butler named Alfred that serves Mr. Wayne and Batman. You can analyse documents and run computations with provided tools:\n{textual_description_of_tool} \n You have access to some optional images. Currently the loaded image is: {image}")
return {
"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
"input_file": state["input_file"]
}
# The graph
builder = StateGraph(AgentState)
# Define nodes: these do the work
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
# Define edges: these determine how the control flow moves
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
"assistant",
# If the latest message requires a tool, route to tools
# Otherwise, provide a direct response
tools_condition,
)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()
# Show the butler's thought process
# display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))
display(
Image(
react_graph.get_graph(xray=True).draw_mermaid_png(
draw_method=MermaidDrawMethod.PYPPETEER
)
)
)
messages = [HumanMessage(content="Divide 6790 by 5")]
messages = react_graph.invoke({"messages": messages, "input_file": None}) # type: ignore
# Show the messages
for m in messages['messages']:
m.pretty_print()