subashpoudel commited on
Commit
7a3f093
·
1 Parent(s): 90026fe

Refactored context analysis

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.gitignore CHANGED
@@ -1,2 +1,4 @@
1
  .env
2
  myenv
 
 
 
1
  .env
2
  myenv
3
+ *.pyc
4
+ __pycache__/
api/__pycache__/stored_data.cpython-313.pyc CHANGED
Binary files a/api/__pycache__/stored_data.cpython-313.pyc and b/api/__pycache__/stored_data.cpython-313.pyc differ
 
api/routers/context_analysis.py CHANGED
@@ -4,62 +4,37 @@ import logging
4
  from fastapi import APIRouter
5
  from fastapi.responses import StreamingResponse
6
  from api.stored_data import stored_data
7
- from src.genai.context_analysis_agent.agent import IntroductionChatbot
 
8
  from api.schemas.context_analysis import UserMessage
9
 
10
  app_logger = logging.getLogger("app_logger")
11
  error_logger = logging.getLogger("error_logger")
12
 
13
  router = APIRouter()
14
- context_analysis_graph = IntroductionChatbot()
15
-
16
  @router.post("/context-analysis")
17
  def context_analysis(msg: UserMessage):
18
- def event_generator():
19
- try:
20
- accumulated_response = ""
21
- for chunk in context_analysis_graph.chat(msg.message):
22
- accumulated_response += chunk
23
- payload = {
24
- "streamed_response": chunk,
25
- }
26
- yield json.dumps(payload) + "\n"
27
- app_logger.info('Executed context analysis agent')
28
 
29
- last_response = context_analysis_graph.messages[-1]["content"]
30
- print('Last-Response:', last_response)
 
 
 
31
 
32
- if context_analysis_graph.is_complete(last_response) == 'complete':
33
- app_logger.info('Context analysis completed.')
34
- try:
35
- details = context_analysis_graph.extract_details()
36
- app_logger.info('Details extracted after context analysis.')
37
- except Exception as e:
38
- error_logger.error(f'Unable to extract details: {e}')
39
 
40
- if type(details) != dict:details = details.model_dump()
41
- if isinstance(details, str):details = ast.literal_eval(details)
42
-
43
- stored_data["business_details"] = details
44
- try:
45
- context_analysis_graph.reset()
46
- app_logger.info('Context analysis memory reset after details extraction.')
47
- except Exception as e:
48
- error_logger.error('Unable to reset memory:', e)
49
 
50
- final_payload = {
51
- "response": accumulated_response,
52
- "complete": True,
53
- "business_details": details
54
- }
55
- else:
56
- final_payload = {
57
- "response": accumulated_response,
58
- "complete": False
59
- }
60
 
61
- yield json.dumps(final_payload) + "\n"
62
- except Exception as e:
63
- error_logger.error('Unable to run context analysis agent:', e)
64
 
65
- return StreamingResponse(event_generator(), media_type="text/event-stream")
 
4
  from fastapi import APIRouter
5
  from fastapi.responses import StreamingResponse
6
  from api.stored_data import stored_data
7
+ from src.genai.context_analysis_agent.agent import ContextAnalysisAgent
8
+ from src.genai.context_analysis_agent.utils.utils import DetailsExtractorNode , SaveToDB
9
  from api.schemas.context_analysis import UserMessage
10
 
11
  app_logger = logging.getLogger("app_logger")
12
  error_logger = logging.getLogger("error_logger")
13
 
14
  router = APIRouter()
15
+ agent=ContextAnalysisAgent()
16
+ context_graph = agent.context_analysis_graph()
17
  @router.post("/context-analysis")
18
  def context_analysis(msg: UserMessage):
19
+ stored_data['context_analysis_interactions'].append({'role':'human', 'content':msg.message})
20
+ config={"configurable": {"thread_id": "context_analysis_1"}}
21
+ try:
22
+ result=context_graph.invoke({'messages':msg.message},config=config)
23
+ stored_data['context_analysis_interactions'].append({'role':'ai', 'content':result['messages'][-1].content})
24
+ if result['completion']==True:
25
+ formatted_details=DetailsExtractorNode(stored_data['context_analysis_interactions']).run()
26
+ app_logger.info('Context Analysis Completed.')
 
 
27
 
28
+ return {'response':result['messages'][-1].content , 'complete':True , 'business_details': formatted_details}
29
+
30
+ else:
31
+ return {'response':result['messages'][-1].content , 'complete':False}
32
+
33
 
34
+ except Exception as e:
35
+ error_logger.error('Failed Executing context analysis:', e)
36
+ raise
 
 
 
 
37
 
 
 
 
 
 
 
 
 
 
38
 
 
 
 
 
 
 
 
 
 
 
39
 
 
 
 
40
 
 
api/stored_data.py CHANGED
@@ -8,4 +8,5 @@ stored_data['business_details'] = {
8
  "Challenges_faced": "attracting loyal members, standing out in a competitive market, and promoting consistent engagement"
9
  }
10
  stored_data['human_ideation_interactions'] = []
11
- stored_data['brainstorming_response']={}
 
 
8
  "Challenges_faced": "attracting loyal members, standing out in a competitive market, and promoting consistent engagement"
9
  }
10
  stored_data['human_ideation_interactions'] = []
11
+ stored_data['brainstorming_response']={}
12
+ stored_data['context_analysis_interactions']= []
logs/access.log CHANGED
@@ -482,3 +482,62 @@
482
  2025-08-22 16:40:10,680 | INFO | access_logger | api\main.py:21 | Response status: 200
483
  2025-08-22 16:45:45,350 | INFO | access_logger | api\main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
484
  2025-08-22 16:45:46,127 | INFO | access_logger | api\main.py:21 | Response status: 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  2025-08-22 16:40:10,680 | INFO | access_logger | api\main.py:21 | Response status: 200
483
  2025-08-22 16:45:45,350 | INFO | access_logger | api\main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
484
  2025-08-22 16:45:46,127 | INFO | access_logger | api\main.py:21 | Response status: 200
485
+ 2025-08-25 11:46:36,225 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/docs
486
+ 2025-08-25 11:46:36,225 | INFO | access_logger | api/main.py:21 | Response status: 200
487
+ 2025-08-25 11:46:36,824 | INFO | access_logger | api/main.py:19 | Request: GET http://127.0.0.1:8000/openapi.json
488
+ 2025-08-25 11:46:36,827 | INFO | access_logger | api/main.py:21 | Response status: 200
489
+ 2025-08-25 11:46:49,089 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
490
+ 2025-08-25 11:49:19,613 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
491
+ 2025-08-25 12:04:01,090 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
492
+ 2025-08-25 12:04:04,849 | INFO | access_logger | api/main.py:21 | Response status: 200
493
+ 2025-08-25 12:04:30,095 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
494
+ 2025-08-25 12:04:32,379 | INFO | access_logger | api/main.py:21 | Response status: 200
495
+ 2025-08-25 12:04:41,276 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
496
+ 2025-08-25 12:04:43,228 | INFO | access_logger | api/main.py:21 | Response status: 200
497
+ 2025-08-25 12:04:52,951 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
498
+ 2025-08-25 12:04:54,492 | INFO | access_logger | api/main.py:21 | Response status: 200
499
+ 2025-08-25 12:05:57,204 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
500
+ 2025-08-25 12:05:59,251 | INFO | access_logger | api/main.py:21 | Response status: 200
501
+ 2025-08-25 12:06:08,161 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
502
+ 2025-08-25 12:06:09,967 | INFO | access_logger | api/main.py:21 | Response status: 200
503
+ 2025-08-25 12:06:20,187 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
504
+ 2025-08-25 12:06:22,149 | INFO | access_logger | api/main.py:21 | Response status: 200
505
+ 2025-08-25 12:06:33,957 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
506
+ 2025-08-25 12:06:36,278 | INFO | access_logger | api/main.py:21 | Response status: 200
507
+ 2025-08-25 12:06:51,855 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
508
+ 2025-08-25 12:09:41,824 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
509
+ 2025-08-25 12:09:45,123 | INFO | access_logger | api/main.py:21 | Response status: 200
510
+ 2025-08-25 12:09:55,885 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
511
+ 2025-08-25 12:09:56,982 | INFO | access_logger | api/main.py:21 | Response status: 200
512
+ 2025-08-25 12:10:01,302 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
513
+ 2025-08-25 12:10:03,720 | INFO | access_logger | api/main.py:21 | Response status: 200
514
+ 2025-08-25 12:10:11,125 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
515
+ 2025-08-25 12:10:12,957 | INFO | access_logger | api/main.py:21 | Response status: 200
516
+ 2025-08-25 12:10:27,560 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
517
+ 2025-08-25 12:10:28,712 | INFO | access_logger | api/main.py:21 | Response status: 200
518
+ 2025-08-25 12:10:37,201 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
519
+ 2025-08-25 12:10:38,967 | INFO | access_logger | api/main.py:21 | Response status: 200
520
+ 2025-08-25 12:10:47,639 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
521
+ 2025-08-25 12:10:49,416 | INFO | access_logger | api/main.py:21 | Response status: 200
522
+ 2025-08-25 12:10:58,887 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
523
+ 2025-08-25 12:11:00,409 | INFO | access_logger | api/main.py:21 | Response status: 200
524
+ 2025-08-25 12:11:21,585 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
525
+ 2025-08-25 12:11:23,105 | INFO | access_logger | api/main.py:21 | Response status: 200
526
+ 2025-08-25 12:15:18,358 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
527
+ 2025-08-25 12:15:21,822 | INFO | access_logger | api/main.py:21 | Response status: 200
528
+ 2025-08-25 12:15:30,647 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
529
+ 2025-08-25 12:15:32,245 | INFO | access_logger | api/main.py:21 | Response status: 200
530
+ 2025-08-25 12:15:37,780 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
531
+ 2025-08-25 12:15:38,744 | INFO | access_logger | api/main.py:21 | Response status: 200
532
+ 2025-08-25 12:15:45,022 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
533
+ 2025-08-25 12:15:46,481 | INFO | access_logger | api/main.py:21 | Response status: 200
534
+ 2025-08-25 12:15:56,436 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
535
+ 2025-08-25 12:15:58,052 | INFO | access_logger | api/main.py:21 | Response status: 200
536
+ 2025-08-25 12:16:09,272 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
537
+ 2025-08-25 12:16:12,900 | INFO | access_logger | api/main.py:21 | Response status: 200
538
+ 2025-08-25 12:16:21,623 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
539
+ 2025-08-25 12:16:22,893 | INFO | access_logger | api/main.py:21 | Response status: 200
540
+ 2025-08-25 12:16:32,047 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
541
+ 2025-08-25 12:16:33,281 | INFO | access_logger | api/main.py:21 | Response status: 200
542
+ 2025-08-25 12:16:43,062 | INFO | access_logger | api/main.py:19 | Request: POST http://127.0.0.1:8000/api/context-analysis
543
+ 2025-08-25 12:16:46,807 | INFO | access_logger | api/main.py:21 | Response status: 200
src/genai/context_analysis_agent/agent.py CHANGED
@@ -1,55 +1,22 @@
1
- import logging
2
- from langchain_core.messages import SystemMessage , HumanMessage
3
- from langgraph.graph import StateGraph, MessagesState, START, END
4
  from langgraph.checkpoint.memory import MemorySaver
5
- from .utils.state import State, CompletionFormatter
6
  from .utils.nodes import IntroductionNode
7
- from .utils.utils import DetailsExtractor
8
- from src.genai.utils.models_loader import llm_gpt
9
- from .utils.prompts import completion_check_prompt
10
 
11
 
12
- business_state = State()
13
-
14
- class IntroductionChatbot:
15
  def __init__(self):
16
  self.memory = MemorySaver()
17
- self.llm = llm_gpt
18
- self.workflow = self._initialize_workflow()
19
- self.interact_agent = self.workflow.compile(checkpointer=self.memory)
20
- self.messages = []
21
-
22
-
23
- def _initialize_workflow(self):
24
- workflow = StateGraph(MessagesState)
25
- workflow.add_node("chatbot", lambda state: IntroductionNode().run(state, self.llm))
26
- workflow.add_edge(START, "chatbot")
27
- workflow.add_edge("chatbot", END)
28
- return workflow
29
-
30
- def chat(self, user_input: str):
31
- self.messages.append({"role": "user", "content": user_input})
32
- config = {"configurable": {"thread_id": "1"}}
33
- for message_chunk, metadata in self.interact_agent.stream(
34
- {"messages": [user_input]},
35
- config=config,
36
- stream_mode="messages"
37
- ):
38
- yield message_chunk.content
39
-
40
-
41
- def is_complete(self, latest_response: str) -> bool:
42
- messages = [SystemMessage(content=completion_check_prompt()),HumanMessage(content=f'''The response of assistant is: {latest_response}''')]
43
- response = llm_gpt.with_structured_output(CompletionFormatter).invoke(messages)
44
- print('Completion response:', response.completion)
45
- return response.completion
46
-
47
- def extract_details(self):
48
- response = DetailsExtractor(business_state.interactions).run()
49
- return response
50
-
51
- def reset(self):
52
- self.memory= MemorySaver()
53
- self.interact_agent = self.workflow.compile(checkpointer=self.memory)
54
 
55
 
 
1
+ from langgraph.graph import StateGraph, START, END
 
 
2
  from langgraph.checkpoint.memory import MemorySaver
3
+ from .utils.state import State
4
  from .utils.nodes import IntroductionNode
 
 
 
5
 
6
 
7
+ class ContextAnalysisAgent:
 
 
8
  def __init__(self):
9
  self.memory = MemorySaver()
10
+
11
+ def context_analysis_graph(self):
12
+ graph_builder= StateGraph(State)
13
+ graph_builder.add_node("details_collector", IntroductionNode().run)
14
+ graph_builder.add_edge(START, "details_collector")
15
+ graph_builder.add_edge("details_collector", END)
16
+
17
+
18
+
19
+ return graph_builder.compile(checkpointer=self.memory)
20
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
 
src/genai/context_analysis_agent/utils/nodes.py CHANGED
@@ -1,25 +1,25 @@
1
  from langchain_core.messages import SystemMessage
2
  from src.genai.utils.models_loader import llm_gpt
3
  from .prompts import introduction_prompt
4
- from .state import ConversationFormatter
5
 
6
 
7
  class IntroductionNode:
8
  def __init__(self):
9
  self.llm = llm_gpt
10
 
11
- def run(self, state, llm):
12
  template = introduction_prompt
13
  messages = [SystemMessage(content=template)] + state["messages"]
14
- response = llm.with_structured_output(ConversationFormatter).invoke(messages)
15
- print('The response:', response)
16
- print('Type of response:', type(response))
17
- if 'True' in response.complete:
18
- return {'messages':['completed']}
19
- else:
20
- return {"messages": [response.response]}
 
 
21
 
22
 
23
 
24
-
25
-
 
1
  from langchain_core.messages import SystemMessage
2
  from src.genai.utils.models_loader import llm_gpt
3
  from .prompts import introduction_prompt
4
+ from .state import ConversationFormatter, State
5
 
6
 
7
  class IntroductionNode:
8
  def __init__(self):
9
  self.llm = llm_gpt
10
 
11
+ def run(self, state:State):
12
  template = introduction_prompt
13
  messages = [SystemMessage(content=template)] + state["messages"]
14
+ result = self.llm.with_structured_output(ConversationFormatter).invoke(messages)
15
+ print('Response:', result)
16
+ return {
17
+ "messages": [{"role": "assistant", "content": result.response}],
18
+ "response": result.response,
19
+ "completion": result.completion,
20
+ }
21
+
22
+
23
 
24
 
25
 
 
 
src/genai/context_analysis_agent/utils/prompts.py CHANGED
@@ -36,12 +36,10 @@ Output Format:
36
  Just give the output in one word. Either **completed** or **not completed**.
37
  '''
38
 
39
- def details_extract_prompt(interactions):
40
  return( f''' You're provided with the messages of business interactions between the business and AI assistant.
41
  Extract the following details of the business from the conversation as it is. Don't trim or simplify any of the business details. You must not have to lose any information.
42
  No problem if the details are long, but give the business details as it is from the conversation.
43
-
44
- Now, start doing your work:\n
45
- The conversation is:\n{interactions}\n
46
  ''')
47
 
 
36
  Just give the output in one word. Either **completed** or **not completed**.
37
  '''
38
 
39
+ def details_extract_prompt():
40
  return( f''' You're provided with the messages of business interactions between the business and AI assistant.
41
  Extract the following details of the business from the conversation as it is. Don't trim or simplify any of the business details. You must not have to lose any information.
42
  No problem if the details are long, but give the business details as it is from the conversation.
43
+ The interactions will be provided to you through HumanMessage.
 
 
44
  ''')
45
 
src/genai/context_analysis_agent/utils/state.py CHANGED
@@ -1,9 +1,14 @@
1
  from pydantic import BaseModel, ConfigDict , Field
2
- from typing import Optional
 
3
 
4
- class State(BaseModel):
5
- interactions: Optional[list] = []
6
- model_config = ConfigDict(arbitrary_types_allowed=True)
 
 
 
 
7
 
8
  # Pydantic model for extracted business info
9
  class DetailsFormatter(BaseModel):
@@ -19,10 +24,8 @@ class DetailsFormatter(BaseModel):
19
  Challenges_faced: str = Field(description="The challenges faced by the business")
20
  additional_informations: str = Field(description="Additional queries or details regarding the video or idea creation.")
21
 
22
- class CompletionFormatter(BaseModel):
23
- completion: str = Field(description="Just the one word result: **completed** or **not completed**")
24
 
25
  class ConversationFormatter(BaseModel):
26
- response: str = Field(description="The entire response of the assistant.")
27
- complete: str = Field(description= "Return 'True' if the details extraction is completed. Return 'False' if not completed.")
28
 
 
1
  from pydantic import BaseModel, ConfigDict , Field
2
+ from typing import Annotated , TypedDict
3
+ from langgraph.graph.message import add_messages
4
 
5
+
6
+ class State(TypedDict):
7
+ messages: Annotated[list, add_messages]
8
+ response: str
9
+ completion: bool
10
+ interactions: list
11
+ formatted_details: dict
12
 
13
  # Pydantic model for extracted business info
14
  class DetailsFormatter(BaseModel):
 
24
  Challenges_faced: str = Field(description="The challenges faced by the business")
25
  additional_informations: str = Field(description="Additional queries or details regarding the video or idea creation.")
26
 
 
 
27
 
28
  class ConversationFormatter(BaseModel):
29
+ response: str= Field(description="The entire response of the assistant.")
30
+ completion: bool=Field(description=''''Return 'True' if the details extraction is completed. Return 'False' if not completed.''')
31
 
src/genai/context_analysis_agent/utils/utils.py CHANGED
@@ -2,19 +2,20 @@ import pandas as pd
2
  from src.genai.utils.data_loader import caption_df
3
  from src.genai.utils.models_loader import llm_gpt
4
  from .prompts import details_extract_prompt
5
- from langchain_core.messages import SystemMessage
6
  from .state import DetailsFormatter
7
 
8
- class DetailsExtractor:
9
  def __init__(self, interactions):
10
  self.llm = llm_gpt
11
  self.interactions = interactions
12
 
13
  def run(self):
14
- template = details_extract_prompt(self.interactions)
15
- messages = [SystemMessage(content=template)]
16
  response=llm_gpt.with_structured_output(DetailsFormatter).invoke(messages)
17
- return response
 
18
 
19
  class SaveToDB:
20
  def __init__(self, caption_df):
 
2
  from src.genai.utils.data_loader import caption_df
3
  from src.genai.utils.models_loader import llm_gpt
4
  from .prompts import details_extract_prompt
5
+ from langchain_core.messages import SystemMessage, HumanMessage
6
  from .state import DetailsFormatter
7
 
8
+ class DetailsExtractorNode:
9
  def __init__(self, interactions):
10
  self.llm = llm_gpt
11
  self.interactions = interactions
12
 
13
  def run(self):
14
+ template = details_extract_prompt()
15
+ messages = [SystemMessage(content=template), HumanMessage(content=str(self.interactions))]
16
  response=llm_gpt.with_structured_output(DetailsFormatter).invoke(messages)
17
+ return response.model_dump()
18
+
19
 
20
  class SaveToDB:
21
  def __init__(self, caption_df):