Skip to content

Commit 3372cd7

Browse files
authored
Merge pull request #631 from realpython/langgraph
first langgraph commit
2 parents bc99ab4 + d21d301 commit 3372cd7

File tree

11 files changed

+758
-0
lines changed

11 files changed

+758
-0
lines changed

python-langgraph/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# LangGraph: Build Stateful AI Agents in Python
2+
3+
This folder contains the source code for [LangGraph: Build Stateful AI Agents in Python](https://realpython.com/langgraph-build-stateful-ai-agents-in-python/)
4+
5+
## Setup
6+
7+
Create a new virtual environment, and run the following command to install LangGraph and the additional requirements for this project:
8+
9+
```console
10+
(venv) $ python -m pip install -r requirements.txt
11+
```
12+
13+
You'll use `langchain-openai` to interact with OpenAI LLMs, but keep in mind you can use any LLM provider you like with LangGraph and LangChain. You'll use [`pydantic`](https://realpython.com/python-pydantic/) to validate the information your agent parses from emails.
14+
15+
Before moving forward, if you choose to use OpenAI, make sure you're signed up for an OpenAI account and you have a valid [API key](https://openai.com/api/). You'll need to set the following [environment variable](https://en.wikipedia.org/wiki/Environment_variable) before running any examples in this tutorial:
16+
17+
```dotenv
18+
OPENAI_API_KEY=<YOUR-OPENAI-API-KEY>
19+
```
20+
21+
## Usage
22+
23+
Once your environment is set up, you can run the final graph agent on an example input with the following code:
24+
25+
```python
26+
from graphs.email_agent import email_agent_graph
27+
from example_emails import EMAILS
28+
29+
escalation_criteria = """"There's an immediate risk of electrical,
30+
water, or fire damage"""
31+
32+
message_with_criteria = f"""
33+
The escalation criteria is: {escalation_criteria}
34+
35+
Here's the email:
36+
{EMAILS[3]}
37+
"""
38+
message_3 = {"messages": [("human", message_with_criteria)]}
39+
40+
for chunk in email_agent_graph.stream(message_3, stream_mode="values"):
41+
chunk["messages"][-1].pretty_print()
42+
```
43+
44+
See the tutorial for all the details on what's going on here.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from langchain_core.prompts import ChatPromptTemplate
2+
from langchain_openai import ChatOpenAI
3+
from pydantic import BaseModel, Field
4+
5+
6+
class BinaryAnswer(BaseModel):
7+
is_true: bool = Field(
8+
description="""Whether the answer to the question is yes or no.
9+
True if yes otherwise false."""
10+
)
11+
12+
13+
binary_question_prompt = ChatPromptTemplate.from_messages(
14+
[
15+
(
16+
"system",
17+
"""
18+
Answer this question as True for "yes" and False for "no".
19+
No other answers are allowed:
20+
{question}
21+
""",
22+
)
23+
]
24+
)
25+
26+
binary_question_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
27+
28+
BINARY_QUESTION_CHAIN = (
29+
binary_question_prompt
30+
| binary_question_model.with_structured_output(BinaryAnswer)
31+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from langchain_core.prompts import ChatPromptTemplate
2+
from langchain_openai import ChatOpenAI
3+
from pydantic import BaseModel, Field
4+
5+
6+
class EscalationCheck(BaseModel):
7+
needs_escalation: bool = Field(
8+
description="""Whether the notice requires escalation according
9+
to specified criteria"""
10+
)
11+
12+
13+
escalation_prompt = ChatPromptTemplate.from_messages(
14+
[
15+
(
16+
"system",
17+
"""
18+
Determine whether the following notice received from a regulatory
19+
body requires immediate escalation. Immediate escalation is
20+
required when {escalation_criteria}.
21+
Here's the notice message:
22+
{message}
23+
""",
24+
)
25+
]
26+
)
27+
28+
escalation_check_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
29+
30+
ESCALATION_CHECK_CHAIN = (
31+
escalation_prompt
32+
| escalation_check_model.with_structured_output(EscalationCheck)
33+
)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from datetime import date, datetime
2+
3+
from langchain_core.prompts import ChatPromptTemplate
4+
from langchain_openai import ChatOpenAI
5+
from pydantic import BaseModel, Field, computed_field
6+
7+
8+
class NoticeEmailExtract(BaseModel):
9+
date_of_notice_str: str | None = Field(
10+
default=None,
11+
exclude=True,
12+
repr=False,
13+
description="""The date of the notice (if any) reformatted
14+
to match YYYY-mm-dd""",
15+
)
16+
entity_name: str | None = Field(
17+
default=None,
18+
description="""The name of the entity sending the notice (if present
19+
in the message)""",
20+
)
21+
entity_phone: str | None = Field(
22+
default=None,
23+
description="""The phone number of the entity sending the notice
24+
(if present in the message)""",
25+
)
26+
entity_email: str | None = Field(
27+
default=None,
28+
description="""The email of the entity sending the notice
29+
(if present in the message)""",
30+
)
31+
project_id: int | None = Field(
32+
default=None,
33+
description="""The project ID (if present in the message) -
34+
must be an integer""",
35+
)
36+
site_location: str | None = Field(
37+
default=None,
38+
description="""The site location of the project (if present
39+
in the message). Use the full address if possible.""",
40+
)
41+
violation_type: str | None = Field(
42+
default=None,
43+
description="""The type of violation (if present in the
44+
message)""",
45+
)
46+
required_changes: str | None = Field(
47+
default=None,
48+
description="""The required changes specified by the entity
49+
(if present in the message)""",
50+
)
51+
compliance_deadline_str: str | None = Field(
52+
default=None,
53+
exclude=True,
54+
repr=False,
55+
description="""The date that the company must comply (if any)
56+
reformatted to match YYYY-mm-dd""",
57+
)
58+
max_potential_fine: float | None = Field(
59+
default=None,
60+
description="""The maximum potential fine
61+
(if any)""",
62+
)
63+
64+
@staticmethod
65+
def _convert_string_to_date(date: str | None) -> date | None:
66+
try:
67+
return datetime.strptime(date, "%Y-%m-%d").date()
68+
except Exception as e:
69+
print(e)
70+
return None
71+
72+
@computed_field
73+
@property
74+
def date_of_notice(self) -> date | None:
75+
return self._convert_string_to_date(self.date_of_notice_str)
76+
77+
@computed_field
78+
@property
79+
def compliance_deadline(self) -> date | None:
80+
return self._convert_string_to_date(self.compliance_deadline_str)
81+
82+
83+
info_parse_prompt = ChatPromptTemplate.from_messages(
84+
[
85+
(
86+
"system",
87+
"""
88+
Parse the date of notice, sending entity name, sending entity
89+
phone, sending entity email, project id, site location, violation
90+
type, required changes, compliance deadline, and maximum potential
91+
fine from the message. If any of the fields aren't present, don't
92+
populate them. Try to cast dates into the YYYY-mm-dd format. Don't
93+
populate fields if they're not present in the message.
94+
Here's the notice message:
95+
{message}
96+
""",
97+
)
98+
]
99+
)
100+
101+
notice_parser_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
102+
103+
NOTICE_PARSER_CHAIN = (
104+
info_parse_prompt
105+
| notice_parser_model.with_structured_output(NoticeEmailExtract)
106+
)

python-langgraph/example_emails.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
EMAILS = [
2+
# Email 1
3+
"""
4+
Date: October 15, 2024
5+
From: Occupational Safety and Health Administration (OSHA)
6+
To: Blue Ridge Construction, project 111232345 - Downtown Office Complex
7+
Location: Dallas, TX
8+
9+
During a recent inspection of your construction site at 123 Main Street,
10+
the following safety violations were identified:
11+
12+
Lack of fall protection: Workers on scaffolding above 10 feet were
13+
without required harnesses or other fall protection equipment.
14+
Unsafe scaffolding setup: Several scaffolding structures were noted as
15+
lacking secure base plates and bracing, creating potential collapse risks.
16+
Inadequate personal protective equipment (PPE): Multiple workers were found
17+
without proper PPE, including hard hats and safety glasses.
18+
Required Corrective Actions:
19+
20+
Install guardrails and fall arrest systems on all scaffolding over 10 feet.
21+
Conduct an inspection of all scaffolding structures and reinforce unstable
22+
sections. Ensure all workers on-site are provided with necessary PPE and conduct
23+
safety training on proper usage.
24+
Deadline for Compliance: All violations must be rectified by November 10, 2024.
25+
Failure to comply may result in fines of up to $25,000 per violation.
26+
27+
Contact: For questions or to confirm compliance, please reach out to the OSHA
28+
regional office at (555) 123-4567 or email [email protected].
29+
""",
30+
# Email 2
31+
"""
32+
33+
Hey Betsy,
34+
Here's your invoice for $1000 for the cookies you ordered.
35+
""",
36+
# Email 3
37+
"""
38+
39+
Hi Paul,
40+
We have an issue with the HVAC system your team installed in
41+
apartment 1235. We'd like to request maintenance or a refund.
42+
Thanks,
43+
Terrance
44+
""",
45+
# Email 4
46+
"""
47+
Date: January 10, 2025
48+
From: City of Los Angeles Building and Safety Department
49+
To: West Coast Development, project 345678123 - Sunset Luxury
50+
Condominiums
51+
Location: Los Angeles, CA
52+
Following an inspection of your site at 456 Sunset Boulevard, we have
53+
identified the following building code violations:
54+
Electrical Wiring: Exposed wiring was found in the underground parking
55+
garage, posing a safety hazard. Fire Safety: Insufficient fire
56+
extinguishers were available across multiple floors of the structure
57+
under construction.
58+
Structural Integrity: The temporary support beams in the eastern wing
59+
do not meet the load-bearing standards specified in local building codes.
60+
Required Corrective Actions:
61+
Replace or properly secure exposed wiring to meet electrical safety
62+
standards. Install additional fire extinguishers in compliance with
63+
fire code requirements. Reinforce or replace temporary support beams
64+
to ensure structural stability. Deadline for Compliance: Violations
65+
must be addressed no later than February 5,
66+
2025. Failure to comply may result in
67+
a stop-work order and additional fines.
68+
Contact: For questions or to schedule a re-inspection, please contact
69+
the Building and Safety Department at
70+
(555) 456-7890 or email [email protected].
71+
""",
72+
]

0 commit comments

Comments
 (0)