Gaykar commited on
Commit
4995bb4
·
1 Parent(s): 0c0b2ef
app/agents/context_agent.py CHANGED
@@ -16,7 +16,7 @@ context_agent = create_agent(
16
  middleware=[
17
  ToolCallLimitMiddleware[Any,None](
18
  tool_name="search_memory",
19
- run_limit=7,
20
  thread_limit=10,
21
  )
22
  ] ,
 
16
  middleware=[
17
  ToolCallLimitMiddleware[Any,None](
18
  tool_name="search_memory",
19
+ run_limit=5,
20
  thread_limit=10,
21
  )
22
  ] ,
app/core/config.py CHANGED
@@ -5,24 +5,16 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent
5
 
6
  class Settings(BaseSettings):
7
  PROJECT_NAME: str = "Email Assistant Project"
8
-
9
  GROQ_API_KEY: str
10
- # PINECONE_API_KEY: str
11
-
12
- # CLOUDINARY_CLOUD_NAME: str
13
- # CLOUDINARY_API_KEY: str
14
- # CLOUDINARY_API_SECRET: str
15
-
16
-
17
- # DB_URL_FOR_CHECKPOINTER_STORE: str
18
 
19
- # DB_URL_FOR_SQL_AL:str
20
 
21
- # model_config = SettingsConfigDict(
22
- # env_file=str(BASE_DIR / ".env"),
23
- # env_file_encoding="utf-8",
24
- # extra="ignore"
25
- # )
26
 
27
  settings = Settings()
28
 
 
5
 
6
  class Settings(BaseSettings):
7
  PROJECT_NAME: str = "Email Assistant Project"
 
8
  GROQ_API_KEY: str
9
+ DB_URL_FOR_CHECKPOINTER_STORE: str
 
 
 
 
 
 
 
10
 
11
+ DB_URL_FOR_SQL_AL:str
12
 
13
+ model_config = SettingsConfigDict(
14
+ env_file=str(BASE_DIR / ".env"),
15
+ env_file_encoding="utf-8",
16
+ extra="ignore"
17
+ )
18
 
19
  settings = Settings()
20
 
app/database/connection.py CHANGED
@@ -1,6 +1,9 @@
1
  from sqlalchemy import create_engine
2
  from sqlalchemy.orm import sessionmaker, Session
3
- from app.core.config import DB_URL_FOR_SQL_AL
 
 
 
4
  engine = create_engine(
5
  DB_URL_FOR_SQL_AL,
6
  echo=False,
 
1
  from sqlalchemy import create_engine
2
  from sqlalchemy.orm import sessionmaker, Session
3
+ from app.core.config import settings
4
+
5
+ DB_URL_FOR_SQL_AL=settings.DB_URL_FOR_SQL_AL
6
+
7
  engine = create_engine(
8
  DB_URL_FOR_SQL_AL,
9
  echo=False,
app/nodes/check_token_count_node.py CHANGED
@@ -1,14 +1,7 @@
1
  from app.state.state import EmailAgentState
2
  from langchain_groq import ChatGroq
 
3
 
4
- llm_for_token_count=ChatGroq(
5
- model="meta-llama/llama-4-scout-17b-16e-instruct",
6
- temperature=0.1,
7
- )
8
-
9
- def count_input_tokens(subject:str ,body:str) -> int:
10
- text=subject+body
11
- return llm_for_token_count.get_num_tokens(text)
12
 
13
  def check_token_count_node(state: EmailAgentState):
14
  """
 
1
  from app.state.state import EmailAgentState
2
  from langchain_groq import ChatGroq
3
+ from app.utils.token_utils import count_input_tokens
4
 
 
 
 
 
 
 
 
 
5
 
6
  def check_token_count_node(state: EmailAgentState):
7
  """
app/nodes/store_memory_data_node.py CHANGED
@@ -6,6 +6,8 @@ from app.state.state import EmailAgentState
6
  from app.agents.memory_manager_agent import memory_manager_agent
7
  from app.database.connection import get_session
8
  from app.database.utils import save_sent_email, save_received_email
 
 
9
 
10
  def store_memory_and_data_node(state: EmailAgentState, config: RunnableConfig):
11
  """
@@ -13,21 +15,38 @@ def store_memory_and_data_node(state: EmailAgentState, config: RunnableConfig):
13
  """
14
  print("--- Memory Node: Persisting interaction to DB ---")
15
 
 
 
 
 
 
 
 
 
16
  # 1. Prepare the memory summary
17
  prompt = memory_agent_template.invoke({
18
  "user_name": state["user_name"],
19
  "senders_email_id": state["sender_email_id"],
20
  "user_email_id": state["user_email_id"],
21
- "sent_email_body": state.get("reply_email_body", "No response found"), # Use .get() for safety
22
  "incoming_email_body": state["sender_email_body"],
23
  })
24
 
25
  # 2. Invoke memory agent logic
26
- memory_manager_agent.invoke(
 
 
 
 
 
27
  {"messages": prompt.to_messages()},
28
  config=config
29
  )
30
 
 
 
 
 
31
  # 3. Robust Database Operations
32
  sender_id = state['user_id']
33
  thread_id = config.get("configurable", {}).get("thread_id")
@@ -44,4 +63,4 @@ def store_memory_and_data_node(state: EmailAgentState, config: RunnableConfig):
44
  print(f"--- Memory Node Error: {e} ---")
45
  raise e
46
 
47
- return {"memory_agent_messages": [AIMessage(content="Memory successfully persisted.")],"email_sent": True}
 
6
  from app.agents.memory_manager_agent import memory_manager_agent
7
  from app.database.connection import get_session
8
  from app.database.utils import save_sent_email, save_received_email
9
+ from langchain_core.messages import AIMessage
10
+ from app.utils.token_utils import *
11
 
12
  def store_memory_and_data_node(state: EmailAgentState, config: RunnableConfig):
13
  """
 
15
  """
16
  print("--- Memory Node: Persisting interaction to DB ---")
17
 
18
+
19
+ body_for_memory_agent=state.get("reply_email_body")
20
+ reply_subject=state.get("reply_subject")
21
+
22
+
23
+ if count_input_tokens(body_for_memory_agent,reply_subject)>100000:
24
+ body_for_memory_agent=summarise_email_body(body_for_memory_agent)
25
+
26
  # 1. Prepare the memory summary
27
  prompt = memory_agent_template.invoke({
28
  "user_name": state["user_name"],
29
  "senders_email_id": state["sender_email_id"],
30
  "user_email_id": state["user_email_id"],
31
+ "sent_email_body": body_for_memory_agent,
32
  "incoming_email_body": state["sender_email_body"],
33
  })
34
 
35
  # 2. Invoke memory agent logic
36
+
37
+
38
+ print("invoking memory manager")
39
+
40
+
41
+ response = memory_manager_agent.invoke(
42
  {"messages": prompt.to_messages()},
43
  config=config
44
  )
45
 
46
+ print(response)
47
+
48
+ memory_stored_summary = response[0]['value']['content']['summary']
49
+
50
  # 3. Robust Database Operations
51
  sender_id = state['user_id']
52
  thread_id = config.get("configurable", {}).get("thread_id")
 
63
  print(f"--- Memory Node Error: {e} ---")
64
  raise e
65
 
66
+ return {"memory_agent_messages": [AIMessage(content=memory_stored_summary)],"email_sent": True}
app/nodes/summarise_email_body_node.py CHANGED
@@ -5,22 +5,9 @@ from langchain_classic.text_splitter import RecursiveCharacterTextSplitter
5
  from langchain_core.documents import Document
6
  from langchain_classic.prompts import PromptTemplate
7
  from app.agents.summarizer_agent import summarizer_agent
8
- from app.prompts.summarizer_agent_prompt import summarize_agent_initial_prompt_template,summarize_agent_refine_template
9
- def summarise_email_body(body: str):
10
- # Tip: 100 is very small for an email; 500-1000 is usually better
11
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
12
- docs = [Document(page_content=t) for t in text_splitter.split_text(body)]
13
-
14
- chain = load_summarize_chain(
15
- llm=summarizer_agent,
16
- chain_type="refine",
17
- question_prompt=summarize_agent_initial_prompt,
18
- refine_prompt=summarize_agent_refine_prompt,
19
- document_variable_name="body" # <--- ADD THIS LINE
20
- )
21
- summary = chain.invoke(docs)
22
- return summary['output_text']
23
-
24
 
25
  def summarise_email_body_node(state:EmailAgentState)->dict:
26
 
 
5
  from langchain_core.documents import Document
6
  from langchain_classic.prompts import PromptTemplate
7
  from app.agents.summarizer_agent import summarizer_agent
8
+ from langchain_groq import ChatGroq
9
+ from app.prompts.summarizer_agent_prompt import *
10
+ from app.utils.token_utils import summarise_email_body
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  def summarise_email_body_node(state:EmailAgentState)->dict:
13
 
app/prompts/email_writing_agent_prompt.py CHANGED
@@ -24,11 +24,14 @@ Output: "Stored."
24
  <rules>
25
  1. NEW DRAFT: Call `create_gmail_draft` first.
26
  2. REJECTION: If "DRAFT REJECTED", rewrite and call `create_gmail_draft_tool` again.
27
- 3. SENDING: Only call `send_draft_by_id` when user explicitly orders to send. **dont call send_draft_by_id until user allows you to send the draft**
28
  4. ARCHIVING: After `send_draft_by_id` returns a Message ID.
29
  important:You are not allowed to send until the user explicitly orders to send.
30
  5.Use **{sender_email_id}** as the recipient not the name.
 
31
  </rules>
 
 
32
 
33
  """)
34
  ])
 
24
  <rules>
25
  1. NEW DRAFT: Call `create_gmail_draft` first.
26
  2. REJECTION: If "DRAFT REJECTED", rewrite and call `create_gmail_draft_tool` again.
27
+ 3. SENDING: Only call `send_draft_by_id` when user explicitly orders to send.
28
  4. ARCHIVING: After `send_draft_by_id` returns a Message ID.
29
  important:You are not allowed to send until the user explicitly orders to send.
30
  5.Use **{sender_email_id}** as the recipient not the name.
31
+ **dont call send_draft_by_id until user allows you to send the draft**
32
  </rules>
33
+
34
+
35
 
36
  """)
37
  ])
app/state/state.py CHANGED
@@ -61,7 +61,7 @@ class EmailAgentState(TypedDict):
61
 
62
  email_sent: Optional[bool]
63
 
64
-
65
  human_approved: Optional[bool]
66
  reply_email_body:Optional[str]
67
 
 
61
 
62
  email_sent: Optional[bool]
63
 
64
+
65
  human_approved: Optional[bool]
66
  reply_email_body:Optional[str]
67
 
app/utils/token_utils.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_groq import ChatGroq
2
+ from langchain_classic.chains.summarize import load_summarize_chain
3
+ from langchain_classic.text_splitter import RecursiveCharacterTextSplitter
4
+ from langchain_core.documents import Document
5
+ from app.agents.summarizer_agent import summarizer_agent
6
+ from app.prompts.summarizer_agent_prompt import *
7
+
8
+ llm_for_token_count=ChatGroq(
9
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
10
+ temperature=0.1,
11
+ )
12
+
13
+ def count_input_tokens(subject:str ,body:str) -> int:
14
+ text=subject+body
15
+ return llm_for_token_count.get_num_tokens(text)
16
+
17
+ def summarise_email_body(body: str):
18
+ # Tip: 100 is very small for an email; 500-1000 is usually better
19
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
20
+ docs = [Document(page_content=t) for t in text_splitter.split_text(body)]
21
+
22
+ chain = load_summarize_chain(
23
+ llm=summarizer_agent,
24
+ chain_type="refine",
25
+ question_prompt=summarize_agent_initial_prompt,
26
+ refine_prompt=summarize_agent_refine_prompt,
27
+ document_variable_name="body" # <--- ADD THIS LINE
28
+ )
29
+ summary = chain.invoke(docs)
30
+ return summary['output_text']