Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4a4773c
Deepagent v1
nhuang-lc Sep 26, 2025
4fcbc80
Update cron to work w LangSmith OAuth
nhuang-lc Sep 26, 2025
b9ec44e
Fix write_response tool
nhuang-lc Sep 26, 2025
4d72bab
Add test file for ease of use
nhuang-lc Sep 26, 2025
f2a5815
Add better tool configs
nhuang-lc Sep 26, 2025
df91ddb
Update cron graph, and add middleware to inject a file into the end o…
nhuang-lc Sep 27, 2025
6374c6b
Add slack bot in
nhuang-lc Sep 27, 2025
449bdc5
Update .env.example
nhuang-lc Sep 27, 2025
b304283
Update nick's config
nhuang-lc Sep 27, 2025
840f0bf
Separate deepagent graph from existing cron
nhuang-lc Sep 29, 2025
c93882e
Use config
nhuang-lc Sep 29, 2025
66a5497
Use langchain api key
nhuang-lc Sep 29, 2025
04e5f46
Move slack user id and email references to use context
nhuang-lc Sep 29, 2025
3b26008
Update context access
nhuang-lc Sep 29, 2025
5bd12db
Scope down to user
nhuang-lc Sep 29, 2025
b0ec3dd
Update question format
nhuang-lc Sep 30, 2025
bff7bec
Fix context bug by upgrading langgraph-api
nhuang-lc Sep 30, 2025
bfca9d0
Remove mre graph
nhuang-lc Sep 30, 2025
6c263b9
Only send slack on interrupt tool calls
nhuang-lc Sep 30, 2025
01082de
Update to use longterm mem
nhuang-lc Sep 30, 2025
c8d6807
Handle follow-up emails, and update prompts to update instructions
nhuang-lc Oct 1, 2025
dfa0a4a
Add tests for tool calling
nhuang-lc Oct 1, 2025
5d923c4
Add more tests around remembering
nhuang-lc Oct 2, 2025
18871ca
Fix prompt around memories
nhuang-lc Oct 2, 2025
40a2c26
Update prompts
nhuang-lc Oct 2, 2025
5aeae85
Add cc fix
nhuang-lc Oct 3, 2025
68c50d1
Fix all dangling tool calls from broken AI messages
nhuang-lc Oct 3, 2025
0d3ef62
First stab at genui components
nhuang-lc Oct 7, 2025
0a644a4
Update messages for interrupts
nhuang-lc Oct 7, 2025
772a225
Update edit type response args
nhuang-lc Oct 7, 2025
0fe0ca0
Merge pull request #35 from langchain-ai/nh/genui
nhuang-lc Oct 7, 2025
064849a
Update dependencies?
nhuang-lc Oct 7, 2025
642df82
Update dependencies
nhuang-lc Oct 7, 2025
a386387
Update dependencies
nhuang-lc Oct 7, 2025
07eb8d9
Update dependencies
nhuang-lc Oct 7, 2025
f94b33e
Freeze at langchain a10
nhuang-lc Oct 7, 2025
335f8d8
Update auth provider
nhuang-lc Oct 7, 2025
813d919
Try another dependency array
nhuang-lc Oct 7, 2025
595931f
Move package.json to root
nhuang-lc Oct 7, 2025
d238813
Update genui
nhuang-lc Oct 8, 2025
1606362
Update components
nhuang-lc Oct 8, 2025
c785173
Cleanups
nhuang-lc Oct 8, 2025
53b253b
Other UI improvements
nhuang-lc Oct 8, 2025
eb1fc8c
Wider email
nhuang-lc Oct 8, 2025
26658b6
Update langchain-anthropic to fix empty AIMessage error
nhuang-lc Oct 9, 2025
51ebd39
When new email comes in from cron, reset the notification logic
nhuang-lc Oct 10, 2025
09b4b1a
Upgrade to deepagents on langchain v1
nhuang-lc Oct 13, 2025
7f001e3
Fix for messages state
nhuang-lc Oct 13, 2025
7d5a9ce
Resolve merge conflicts
nhuang-lc Oct 13, 2025
3b80168
Fix imports
nhuang-lc Oct 13, 2025
8037f07
Update tests
nhuang-lc Oct 13, 2025
f94b8a3
fix cron
nhuang-lc Oct 14, 2025
7464156
Merge pull request #36 from langchain-ai/nh/langchain-v1
nhuang-lc Oct 14, 2025
7af2c0c
Update langchain version to install from github
nhuang-lc Oct 14, 2025
6c8f64a
Pin langgraph and langchain core versions
nhuang-lc Oct 14, 2025
f09ccec
Fix new recipients
nhuang-lc Oct 15, 2025
eab3a42
Update to latest deepagents and update genui components for HITL
nhuang-lc Oct 16, 2025
0000d0a
update deepagents to rc
nhuang-lc Oct 16, 2025
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
13 changes: 8 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
LANGSMITH_API_KEY=...
OPENAI_API_KEY=...
GMAIL_SECRET=...
GMAIL_TOKEN=...
ANTHROPIC_API_KEY=...
LANGSMITH_API_KEY=
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
LANGSMITH_TRACING=true
EMAIL=
LANGSMITH_PROJECT=
SLACK_BOT_TOKEN=
SLACK_USER_ID=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,5 @@ cython_debug/

# Secrets for Google Workspace (Gmail, GCal)
.secrets/

node_modules/
3 changes: 2 additions & 1 deletion eaia/cron_graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import hashlib
import operator
import uuid
from typing import Annotated, TypedDict
from typing import Annotated
from typing_extensions import TypedDict

import httpx
import langsmith as ls
Expand Down
45 changes: 45 additions & 0 deletions eaia/deepagent/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
email: [email protected]
full_name: Nick Huang
name: Nick
background: Nick is a software engineer at LangChain. LangChain is a startup building tools for developers to build LLM applications.
schedule_preferences: By default, unless specified otherwise, you should make meetings 30 minutes long.
background_preferences: |
Nick works on the Applied AI team at LangChain. He loves to meet with VCs and other engineers building product, but is not interested in talking to recruiters.
timezone: "America/New_York"
writing_preferences: |
Nick has a few rules for how he likes his emails to be written:
- Nick likes to write emails in a cheery way that makes others feel warm and welcomed.
- He generally likes to match their tone. If they write formally, then so will he. If they write casually, then he should write casual
- If working with an assistant to schedule, then he is usually more formal
- If conversing with someone he seems to know well, then he is more direct and to the fact
- Nick REALLY does not want anyone to know he is using an assistant, so you should try not to sound too much like an assistant and rather sound like Nick
- When Nick is casual, he generally does not include any greetings or sign offs, and just directly says his message
- When Nick is formal, he likes ending emails with 'Warm Wishes'
triage_no: |
- Automated emails from services that are spamming Nick
- Cold outreach from vendors - this happens a lot as people try to sell Nick things. He is not interested in these
- Cold outreach from recruiters - Nick is not interested in talking to recruiters.
- Emails where they are asking questions that can best be answered by other people on the thread. \
Nick is often on a lot of threads with people from his company (LangChain) but often times he does not need to chime in. \
the exception to this is if Nick is the main driver of the conversation. \
You can usually tell this by whether Nick was the one who sent the last email
- Generally do not need to see emails from Ramp, Rewatch, Stripe, Railway
- Notifications of comments on Google Docs
- Automated calendar invitations
triage_notify: |
- Google docs that were shared with him (do NOT notify him on comments, just net new ones)
- Anything that is pretty technically detailed about LangChain. Nick sometimes gets asked questions about LangChain, \
while he may not always respond to those he likes getting notified about them
- Emails where there is a clear action item from Nick based on a previous conversation, like adding people to a slack channel
triage_email: |
- Emails from clients that explicitly ask Nick a question
- Emails from clients where someone else has scheduled a meeting for Nick, and Nick has not already chimed in to express his excitement
- Emails from clients or potential customers where Nick is the main driver of the conversation
- Emails from other LangChain team members that explicitly ask Nick a question
- Emails where Nick has gotten added to a thread with a customer and he hasn't yet said hello
- Email from clients where they are trying to set up a time to meet
- Emails where it seems like Nick has a pre-existing relationship with the sender. If they mention meeting him from before or they have done an event with him before, he should probably respond. If it seems like they are referencing an event or a conversation they had before, Nick should probably respond.
- Emails from friends - even these don't ask an explicit question, if it seems like something a good friend would respond to, Nick should do so.

Reminder - automated calendar invites do NOT count as real emails
memory: true
120 changes: 120 additions & 0 deletions eaia/deepagent/cron_deepagent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import hashlib
import operator
import uuid
from typing import Annotated
from typing_extensions import TypedDict

import httpx
import langsmith as ls
from langgraph.graph import END, START, StateGraph
from langgraph_sdk import get_client
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import HumanMessage
from eaia.deepagent.prompts import EMAIL_INPUT_PROMPT
from eaia.deepagent.google_utils import fetch_group_emails
from eaia.deepagent.utils import FILE_TEMPLATE
from dotenv import load_dotenv
load_dotenv("../.env")

client = get_client()


class JobKickoff(TypedDict):
minutes_since: int
assistant_id: str
count: Annotated[int, operator.add]


async def main(state: JobKickoff, config: RunnableConfig):
minutes_since: int = state["minutes_since"]
assistant_id = state["assistant_id"]
try:
assistant = await client.assistants.get(assistant_id)
except httpx.HTTPStatusError as e:
raise e
assistant_config = assistant["config"]["configurable"]
user_email = assistant_config["email"]
if not user_email:
raise ValueError("Email is not set in the assistant config")
count = 0
async for email in fetch_group_emails(
user_email,
minutes_since=minutes_since,
):
thread_id = str(
uuid.UUID(hex=hashlib.md5(email["thread_id"].encode("UTF-8")).hexdigest())
)
async with ls.trace(
"Schedule processing",
inputs={"thread_id": thread_id},
metadata={"email": email},
) as rt:
try:
thread_info = await client.threads.get(thread_id)
except httpx.HTTPStatusError as e:
if "user_respond" in email:
rt.metadata["end_reason"] = "user_respond"
continue
if e.response.status_code == 404:
thread_info = await client.threads.create(
thread_id=thread_id, if_exists="do_nothing"
)
else:
rt.metadata["end_reason"] = "unknown_error"
rt.error = str(e)
raise e
if "user_respond" in email:
rt.metadata["end_reason"] = "user_respond"
await client.threads.update_state(thread_id, None, as_node="__end__")
continue
recent_email = thread_info["metadata"].get("email_id")
if recent_email == email["id"]:
print(f"Duplicate email: {email}")
rt.metadata["end_reason"] = "duplicate"
# continue
await client.threads.update(thread_id, metadata={"email_id": email["id"]})
rt.metadata["end_reason"] = "success"
rt.add_outputs({"email": email})
count += 1

# Pass in email through the filesystem as well as state
email_str = FILE_TEMPLATE.format(
id=email["id"],
thread_id=email["thread_id"],
send_time=email["send_time"],
subject=email["subject"],
to=email["to_email"],
_from=email["from_email"],
page_content=email["page_content"],
)
await client.runs.create(
thread_id,
assistant_id,
input={
"email": email,
"messages": HumanMessage(content=EMAIL_INPUT_PROMPT.format(
author=email["from_email"],
to=email["to_email"],
subject=email["subject"],
email_thread=email["page_content"]
)),
"files": {
"email.txt": {
"content": [email_str],
"created_at": email["send_time"],
"modified_at": email["send_time"],
}
},
"notified": False
},
multitask_strategy="rollback",
config={**assistant_config, "recursion_limit": 1000},
)

return {"count": count}

graph = StateGraph(JobKickoff)
graph.add_node(main)
graph.add_edge(START, "main")
graph.add_edge("main", END)
graph = graph.compile()
Loading