22Nikk0 commited on
Commit
0d53e34
·
verified ·
1 Parent(s): e33b9dd

Update agentic.py

Browse files
Files changed (1) hide show
  1. agentic.py +119 -31
agentic.py CHANGED
@@ -11,6 +11,7 @@ from langchain_community.tools import WikipediaQueryRun
11
  from langchain_community.utilities import WikipediaAPIWrapper
12
  from langchain_aws import ChatBedrock
13
  from langchain_google_genai import ChatGoogleGenerativeAI
 
14
  # from langchain_google_vertexai import ChatVertexAI
15
 
16
  # from langfuse.callback import CallbackHandler
@@ -51,25 +52,26 @@ class State(TypedDict):
51
  video_node_result: AnyMessage
52
  audio_node_result: AnyMessage
53
  code_node_result: AnyMessage
 
54
  next_node: str
55
 
56
  ########################
57
 
58
  ######## MODELS ########
59
- def get_general_model():
60
 
61
  llm_provider = os.getenv("LLM_PROVIDER", "mistral")
62
 
63
  if llm_provider == "mistral":
64
- general_model = ChatMistralAI(
65
- model="mistral-large-2411",#"ministral-8b-latest",#"mistral-small-latest",#
66
  temperature=0,
67
  max_retries=2,
68
  api_key=os.getenv("MISTRAL_API_KEY")
69
  )
70
 
71
  if llm_provider == "aws":
72
- general_model = ChatBedrock(
73
  model_id="arn:aws:bedrock:us-east-1:416545197702:inference-profile/us.amazon.nova-lite-v1:0",
74
  # provider="amazon",
75
  temperature=0,
@@ -78,7 +80,29 @@ def get_general_model():
78
  aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY")
79
  )
80
 
81
- return general_model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  def get_vision_model():
84
 
@@ -87,7 +111,7 @@ def get_vision_model():
87
  if vlm_provider == "openai":
88
  print("Spawning Open AI VLM")
89
  vision_model = ChatOpenAI(
90
- model="gpt-4o",#-mini",
91
  temperature=0,
92
  max_tokens=None,
93
  timeout=None,
@@ -257,17 +281,19 @@ def download_input_file(task_id: str) -> str:
257
 
258
  ######## LLM associations ########
259
 
260
- general_model = get_vision_model()
 
 
 
261
  vision_model = get_vision_model()
262
  video_handler_model = get_video_handler_model()
263
  audio_handler_model = get_audio_handler_model()
264
 
 
265
  ########################
266
 
267
  ######## Nodes Definition ########
268
 
269
- general_model = get_general_model()
270
-
271
  search_tools = [
272
  web_search,
273
  wikipedia_search,
@@ -275,7 +301,7 @@ search_tools = [
275
 
276
  download_file_tool = [ download_input_file ]
277
 
278
- web_search_node_agent = general_model.bind_tools(search_tools, parallel_tool_calls=False)
279
 
280
  def thinking_node(state: State) -> dict:
281
  """
@@ -313,7 +339,7 @@ Now provide your response immediately without any preamble in text but not in ma
313
 
314
  sys_msg = SystemMessage(content=prompt)
315
 
316
- thinking_node_response = [general_model.invoke([sys_msg] + [state["thinking_node_result"]])]
317
 
318
  thinking_node_response[-1].pretty_print()
319
 
@@ -357,7 +383,7 @@ Now provide your response immediately without any preamble in text but not in ma
357
 
358
  sys_msg = SystemMessage(content=prompt)
359
 
360
- code_node_response = [general_model.invoke([sys_msg])]
361
 
362
  code_node_response[-1].pretty_print()
363
 
@@ -425,7 +451,6 @@ Now provide your response immediately without any preamble in text but not in ma
425
  "web_wiki_search_node_result": web_wiki_search_node_response,
426
  }
427
 
428
-
429
  def vision_node(state: State) -> dict:
430
  """
431
  Vision model that can analyze images and pictures and answer questions about them.
@@ -446,9 +471,9 @@ You do not handle videos
446
  You do not handle audio
447
  You do not handle code
448
 
449
- 1. You need to fully understand the question
450
- 2. You must think hard about what is relevant in the image to make the best answer to the question
451
- 4. Report your thought process in detail, explaining your reasoning step-by-step.
452
 
453
  Here is the question {state['question']}
454
  Now provide your response immediately without any preamble in text but not in markdown.
@@ -532,7 +557,7 @@ You do not handle images or pictures.
532
  You do not handle audio.
533
  You do not handle code.
534
 
535
- 1. You need to fully understand the question
536
  2. Carefully observe the video, paying attention to relevant details, actions, and context.
537
  3. Focus on the user's question.
538
  4. If the question requires counting, identifying, or describing, be precise and clear in your response.
@@ -615,7 +640,7 @@ You do not handle images or pictures.
615
  You do not handle video.
616
  You do not handle code.
617
 
618
- 1. You need to fully understand the question:
619
  2. Carefully listen to the audio, paying attention to relevant details, actions, and context.
620
  3. Focus on the user's question.
621
  4. If the question requires counting, identifying, or describing, be precise and clear in your response.
@@ -669,7 +694,69 @@ Now provide your response immediately without any preamble in text but not in ma
669
  "audio_node_result": audio_node_response
670
  }
671
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
  def format_answer_node(state: State):
 
 
 
 
 
 
 
 
 
 
 
 
 
673
 
674
  prompt = """
675
  You are the best assistant for final answer formating.
@@ -696,7 +783,6 @@ FINAL ANSWER: [YOUR FINAL ANSWER]
696
  - If asked for a comma-separated list:
697
  - Apply the above rules for numbers and strings to each element in the list.
698
  - And take care of having a space after each comma.
699
- - If there is a list with single letter for each item, write them with the same case as the question.
700
 
701
  ## Constraints
702
  - You must not answer if the constraints above are not respected.
@@ -710,7 +796,7 @@ Now provide your response immediately without any preamble in text but not in ma
710
 
711
  question = [HumanMessage(content=state["question"])]
712
 
713
- for node_result in ["web_wiki_search_node_result", "vision_node_result", "video_node_result", "audio_node_result", "thinking_node_result", "code_node_result"]:
714
  result = state.get(node_result, "")
715
  if result:
716
  # Ensure result is a string. If it's a message object, extract its content.
@@ -722,7 +808,7 @@ Now provide your response immediately without any preamble in text but not in ma
722
 
723
  sys_msg = SystemMessage(content=prompt)
724
 
725
- response = [general_model.invoke([sys_msg] + state["messages"]+ question + nodes_response)]
726
 
727
  return {
728
  "messages": response,
@@ -748,6 +834,7 @@ You do not handle code
748
  - .wav or .mp3 is for audio
749
  - a youtube url is for video
750
  - a .jpg, .png, .jpeg is for image
 
751
  3. You must think hard about what is relevant in the question to make the best choice for the next node
752
  4. You must not answer the question by yourself
753
  5. Report your thought process in detail, explaining your reasoning step-by-step.
@@ -759,6 +846,7 @@ Here are the nodes you can choose:
759
  - video_node: {video_node.__doc__}
760
  - audio_node: {audio_node.__doc__}
761
  - code_node: {code_node.__doc__}
 
762
 
763
  Here is the question : {state['question']}
764
  Here is the file : {state.get("input_file", "no file to handle")}
@@ -773,11 +861,11 @@ Now provide your response immediately respecting this format in lower case: 'nex
773
 
774
  sys_msg = SystemMessage(content=system_prompt)
775
 
776
- entry_node_response = [general_model.invoke([sys_msg] + state["messages"])]
777
 
778
  entry_node_response[-1].pretty_print()
779
 
780
- regex_result = re.search(r'.*next\s*node:\s*(?P<next_node>[a-z_]*).*$', entry_node_response[-1].content, re.IGNORECASE)
781
 
782
  next_node = "END"
783
  if regex_result:
@@ -805,6 +893,7 @@ def build_graph():
805
  builder.add_node("audio_node", audio_node)
806
  builder.add_node("code_node", code_node)
807
  builder.add_node("thinking_node", thinking_node)
 
808
  builder.add_node("format_answer_node", format_answer_node)
809
 
810
  builder.add_edge(START, "entry_node")
@@ -819,6 +908,7 @@ def build_graph():
819
  "video_node": "video_node",
820
  "audio_node": "audio_node",
821
  "code_node": "code_node",
 
822
  "thinking_node": "thinking_node"
823
  }
824
  )
@@ -828,6 +918,7 @@ def build_graph():
828
  builder.add_edge("video_node", "format_answer_node")
829
  builder.add_edge("audio_node", "format_answer_node")
830
  builder.add_edge("code_node", "format_answer_node")
 
831
  builder.add_edge("thinking_node", "format_answer_node")
832
  builder.add_edge("format_answer_node", END)
833
 
@@ -848,12 +939,11 @@ if __name__ == "__main__":
848
  with open("./responses.json", "r") as responses:
849
  json_responses = json.loads(responses.read())
850
 
851
- # input = {
852
- # "task_id": "cca530fc-4052-43b2-b130-b30968d8aa44",
853
- # "question": "Review the chess position provided in the image. It is black's turn. Provide the correct next move for black which guarantees a win. Please provide your response in algebraic notation.",
854
- # "Level": "1",
855
- # "file_name": "cca530fc-4052-43b2-b130-b30968d8aa44.png"
856
- # }
857
 
858
  with open("questions.json", "r") as questions:
859
  json_questions = json.loads(questions.read())
@@ -897,5 +987,3 @@ if __name__ == "__main__":
897
  print(f"Expected: {json_responses.get(task_id, '')}")
898
  print(f"Got: {answer}")
899
 
900
- time.sleep(10)
901
-
 
11
  from langchain_community.utilities import WikipediaAPIWrapper
12
  from langchain_aws import ChatBedrock
13
  from langchain_google_genai import ChatGoogleGenerativeAI
14
+ from langchain_community.document_loaders import UnstructuredExcelLoader
15
  # from langchain_google_vertexai import ChatVertexAI
16
 
17
  # from langfuse.callback import CallbackHandler
 
52
  video_node_result: AnyMessage
53
  audio_node_result: AnyMessage
54
  code_node_result: AnyMessage
55
+ excel_node_result: AnyMessage
56
  next_node: str
57
 
58
  ########################
59
 
60
  ######## MODELS ########
61
+ def get_light_model():
62
 
63
  llm_provider = os.getenv("LLM_PROVIDER", "mistral")
64
 
65
  if llm_provider == "mistral":
66
+ light_model = ChatMistralAI(
67
+ model="mistral-small-latest",
68
  temperature=0,
69
  max_retries=2,
70
  api_key=os.getenv("MISTRAL_API_KEY")
71
  )
72
 
73
  if llm_provider == "aws":
74
+ light_model = ChatBedrock(
75
  model_id="arn:aws:bedrock:us-east-1:416545197702:inference-profile/us.amazon.nova-lite-v1:0",
76
  # provider="amazon",
77
  temperature=0,
 
80
  aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY")
81
  )
82
 
83
+ return light_model
84
+
85
+ def get_medium_model():
86
+
87
+ medium_model = ChatMistralAI(
88
+ model="ministral-8b-latest",#"mistral-large-2411",
89
+ temperature=0,
90
+ max_retries=2,
91
+ api_key=os.getenv("MISTRAL_API_KEY")
92
+ )
93
+
94
+ return medium_model
95
+
96
+ def get_big_model():
97
+
98
+ big_model = ChatMistralAI(
99
+ model="mistral-medium-2505",#"mistral-large-2411"
100
+ temperature=0,
101
+ max_retries=2,
102
+ api_key=os.getenv("MISTRAL_API_KEY")
103
+ )
104
+
105
+ return big_model
106
 
107
  def get_vision_model():
108
 
 
111
  if vlm_provider == "openai":
112
  print("Spawning Open AI VLM")
113
  vision_model = ChatOpenAI(
114
+ model="gpt-4o",
115
  temperature=0,
116
  max_tokens=None,
117
  timeout=None,
 
281
 
282
  ######## LLM associations ########
283
 
284
+ medium_model = get_medium_model()
285
+ light_model = get_light_model()
286
+ big_model = get_big_model()
287
+
288
  vision_model = get_vision_model()
289
  video_handler_model = get_video_handler_model()
290
  audio_handler_model = get_audio_handler_model()
291
 
292
+
293
  ########################
294
 
295
  ######## Nodes Definition ########
296
 
 
 
297
  search_tools = [
298
  web_search,
299
  wikipedia_search,
 
301
 
302
  download_file_tool = [ download_input_file ]
303
 
304
+ web_search_node_agent = big_model.bind_tools(search_tools, parallel_tool_calls=False)
305
 
306
  def thinking_node(state: State) -> dict:
307
  """
 
339
 
340
  sys_msg = SystemMessage(content=prompt)
341
 
342
+ thinking_node_response = [big_model.invoke([sys_msg] + [state["thinking_node_result"]])]
343
 
344
  thinking_node_response[-1].pretty_print()
345
 
 
383
 
384
  sys_msg = SystemMessage(content=prompt)
385
 
386
+ code_node_response = [big_model.invoke([sys_msg])]
387
 
388
  code_node_response[-1].pretty_print()
389
 
 
451
  "web_wiki_search_node_result": web_wiki_search_node_response,
452
  }
453
 
 
454
  def vision_node(state: State) -> dict:
455
  """
456
  Vision model that can analyze images and pictures and answer questions about them.
 
471
  You do not handle audio
472
  You do not handle code
473
 
474
+ 1. You need to fully understand the question.
475
+ 2. You must think hard about what is relevant in the image to make the best answer to the question.
476
+ 3. Report your thought process in detail, explaining your reasoning step-by-step.
477
 
478
  Here is the question {state['question']}
479
  Now provide your response immediately without any preamble in text but not in markdown.
 
557
  You do not handle audio.
558
  You do not handle code.
559
 
560
+ 1. You need to fully understand the question.
561
  2. Carefully observe the video, paying attention to relevant details, actions, and context.
562
  3. Focus on the user's question.
563
  4. If the question requires counting, identifying, or describing, be precise and clear in your response.
 
640
  You do not handle video.
641
  You do not handle code.
642
 
643
+ 1. You need to fully understand the question.
644
  2. Carefully listen to the audio, paying attention to relevant details, actions, and context.
645
  3. Focus on the user's question.
646
  4. If the question requires counting, identifying, or describing, be precise and clear in your response.
 
694
  "audio_node_result": audio_node_response
695
  }
696
 
697
+ def excel_node(state: State):
698
+ """
699
+ Excel handler model that can analyze excel files and answer questions about it.
700
+ This node does not handle images or pictures.
701
+ This node does not handle video.
702
+ This node does not handle code.
703
+ This node does not handle audio.
704
+
705
+ Args:
706
+ state (State): with question key inside
707
+
708
+ Returns:
709
+ dict: A dictionary containing the response from the excel handler node, with the key 'excel_node_result' holding the list of messages generated by the excel handler model.
710
+ """
711
+
712
+ loader = UnstructuredExcelLoader(state["downloaded_file"], mode="elements")
713
+ docs = loader.load()
714
+
715
+ prompt = f"""
716
+ You are a powerful assistant which handles excel files.
717
+ You do not handle images or pictures.
718
+ You do not handle video.
719
+ You do not handle code.
720
+ You do not handle audio
721
+
722
+ 1. You need to fully understand the question.
723
+ 2. You must analyze the excel file to answer the question.
724
+ 3. If the question requires counting, identifying, or describing, be precise and clear in your response.
725
+ 4. Do not make up information that is not in the excel file.
726
+
727
+ Here is the question {state['question']}
728
+ Here is the excel file loaded in a Document object: {docs}. You will find htlm content of the file in the 'text_as_html' key.
729
+
730
+ Now provide your response immediately without any preamble in text but not in markdown.
731
+ """
732
+
733
+ response = big_model.invoke(
734
+ input=prompt,
735
+ # config={
736
+ # "callbacks": [langfuse_handler]
737
+ # }
738
+ )
739
+
740
+ response.pretty_print()
741
+
742
+ return {
743
+ "excel_node_result": response
744
+ }
745
+
746
  def format_answer_node(state: State):
747
+ """
748
+ Format answer node that formats the answer of the last node.
749
+ This node does not handle images or pictures.
750
+ This node does not handle video.
751
+ This node does not handle audio.
752
+ This node does not handle code.
753
+
754
+ Args:
755
+ state (State): with question key inside, and all other nodes results
756
+
757
+ Returns:
758
+ dict: A dictionary containing the response from the format answer node, with the key 'format_answer_node_result' holding the list of messages generated by the format answer model.
759
+ """
760
 
761
  prompt = """
762
  You are the best assistant for final answer formating.
 
783
  - If asked for a comma-separated list:
784
  - Apply the above rules for numbers and strings to each element in the list.
785
  - And take care of having a space after each comma.
 
786
 
787
  ## Constraints
788
  - You must not answer if the constraints above are not respected.
 
796
 
797
  question = [HumanMessage(content=state["question"])]
798
 
799
+ for node_result in ["web_wiki_search_node_result", "vision_node_result", "video_node_result", "audio_node_result", "thinking_node_result", "code_node_result", "excel_node_result"]:
800
  result = state.get(node_result, "")
801
  if result:
802
  # Ensure result is a string. If it's a message object, extract its content.
 
808
 
809
  sys_msg = SystemMessage(content=prompt)
810
 
811
+ response = [big_model.invoke([sys_msg] + state["messages"]+ question + nodes_response)]
812
 
813
  return {
814
  "messages": response,
 
834
  - .wav or .mp3 is for audio
835
  - a youtube url is for video
836
  - a .jpg, .png, .jpeg is for image
837
+ - a .xlsx or .xls is for excel
838
  3. You must think hard about what is relevant in the question to make the best choice for the next node
839
  4. You must not answer the question by yourself
840
  5. Report your thought process in detail, explaining your reasoning step-by-step.
 
846
  - video_node: {video_node.__doc__}
847
  - audio_node: {audio_node.__doc__}
848
  - code_node: {code_node.__doc__}
849
+ - excel_node: {excel_node.__doc__}
850
 
851
  Here is the question : {state['question']}
852
  Here is the file : {state.get("input_file", "no file to handle")}
 
861
 
862
  sys_msg = SystemMessage(content=system_prompt)
863
 
864
+ entry_node_response = [light_model.invoke([sys_msg] + state["messages"])]
865
 
866
  entry_node_response[-1].pretty_print()
867
 
868
+ regex_result = re.search(r'.*next\s*node.*(?P<next_node>thinking_node|web_wiki_search_node|vision_node|video_node|audio_node|code_node|excel_node).*$', entry_node_response[-1].content, re.IGNORECASE)
869
 
870
  next_node = "END"
871
  if regex_result:
 
893
  builder.add_node("audio_node", audio_node)
894
  builder.add_node("code_node", code_node)
895
  builder.add_node("thinking_node", thinking_node)
896
+ builder.add_node("excel_node", excel_node)
897
  builder.add_node("format_answer_node", format_answer_node)
898
 
899
  builder.add_edge(START, "entry_node")
 
908
  "video_node": "video_node",
909
  "audio_node": "audio_node",
910
  "code_node": "code_node",
911
+ "excel_node": "excel_node",
912
  "thinking_node": "thinking_node"
913
  }
914
  )
 
918
  builder.add_edge("video_node", "format_answer_node")
919
  builder.add_edge("audio_node", "format_answer_node")
920
  builder.add_edge("code_node", "format_answer_node")
921
+ builder.add_edge("excel_node", "format_answer_node")
922
  builder.add_edge("thinking_node", "format_answer_node")
923
  builder.add_edge("format_answer_node", END)
924
 
 
939
  with open("./responses.json", "r") as responses:
940
  json_responses = json.loads(responses.read())
941
 
942
+ # json_questions = [{
943
+ # "question": "The attached Excel file contains the sales of menu items for a local fast-food chain. What were the total sales that the chain made from food (not including drinks)? Express your answer in USD with two decimal places.",
944
+ # "file_name": "7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx",
945
+ # "task_id": "7bd855d8-463d-4ed5-93ca-5fe35145f733"
946
+ # }]
 
947
 
948
  with open("questions.json", "r") as questions:
949
  json_questions = json.loads(questions.read())
 
987
  print(f"Expected: {json_responses.get(task_id, '')}")
988
  print(f"Got: {answer}")
989