-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathreact_to_me.py
More file actions
141 lines (117 loc) · 5.11 KB
/
Copy pathreact_to_me.py
File metadata and controls
141 lines (117 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
from typing import Any, Literal
from langchain_core.embeddings import Embeddings
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.runnables import Runnable, RunnableConfig
from langchain_openai import ChatOpenAI
from langgraph.graph.state import StateGraph
from agent.profiles.base import (SAFETY_SAFE, SAFETY_UNSAFE, BaseGraphBuilder,
BaseState)
from agent.tasks.final_answer_generation.unsafe_question import \
create_unsafe_answer_generator
from retrievers.reactome.rag import create_reactome_rag
class ReactToMeState(BaseState):
"""ReactToMe state extends BaseState with all preprocessing results."""
pass
class ReactToMeGraphBuilder(BaseGraphBuilder):
"""Graph builder for ReactToMe profile with Reactome-specific functionality."""
def __init__(self, llm: BaseChatModel, embedding: Embeddings) -> None:
"""Initialize ReactToMe graph builder with required components."""
super().__init__(llm, embedding)
# Create a streaming LLM instance only for final answer generation
streaming_llm = ChatOpenAI(
model=llm.model_name if hasattr(llm, "model_name") else "gpt-4o-mini",
temperature=0.0,
streaming=True,
)
self.unsafe_answer_generator = create_unsafe_answer_generator(streaming_llm)
self.reactome_rag: Runnable = create_reactome_rag(
streaming_llm, embedding, streaming=True
)
self.uncompiled_graph: StateGraph = self._build_workflow()
def _build_workflow(self) -> StateGraph:
"""Build and configure the ReactToMe workflow graph."""
state_graph = StateGraph(ReactToMeState)
# Add nodes
state_graph.add_node("preprocess", self.preprocess)
state_graph.add_node("model", self.call_model)
state_graph.add_node("generate_unsafe_response", self.generate_unsafe_response)
state_graph.add_node("postprocess", self.postprocess)
# Add edges
state_graph.set_entry_point("preprocess")
state_graph.add_conditional_edges(
"preprocess",
self.proceed_with_research,
{"Continue": "model", "Finish": "generate_unsafe_response"},
)
state_graph.add_edge("model", "postprocess")
state_graph.add_edge("generate_unsafe_response", "postprocess")
state_graph.set_finish_point("postprocess")
return state_graph
async def preprocess(
self, state: ReactToMeState, config: RunnableConfig
) -> ReactToMeState:
"""Run preprocessing workflow."""
result = await super().preprocess(state, config)
return ReactToMeState(**result)
async def proceed_with_research(
self, state: ReactToMeState
) -> Literal["Continue", "Finish"]:
"""Determine whether to proceed with research based on safety check."""
return "Continue" if state["safety"] == SAFETY_SAFE else "Finish"
async def generate_unsafe_response(
self, state: ReactToMeState, config: RunnableConfig
) -> ReactToMeState:
"""Generate appropriate refusal response for unsafe queries."""
final_answer_message = await self.unsafe_answer_generator.ainvoke(
{
"language": state["detected_language"],
"user_input": state["rephrased_input"],
"reason_unsafe": state["reason_unsafe"],
},
config,
)
final_answer = (
final_answer_message.content
if hasattr(final_answer_message, "content")
else str(final_answer_message)
)
return ReactToMeState(
chat_history=[
HumanMessage(state["user_input"]),
(
final_answer_message
if hasattr(final_answer_message, "content")
else AIMessage(final_answer)
),
],
answer=final_answer,
safety=SAFETY_UNSAFE,
additional_content={"search_results": []},
)
async def call_model(
self, state: ReactToMeState, config: RunnableConfig
) -> ReactToMeState:
"""Generate response using Reactome RAG for safe queries."""
result: dict[str, Any] = await self.reactome_rag.ainvoke(
{
"input": state["rephrased_input"],
"expanded_queries": state.get("expanded_queries", []),
"chat_history": (
state["chat_history"]
if state["chat_history"]
else [HumanMessage(state["user_input"])]
),
},
config,
)
return ReactToMeState(
chat_history=[
HumanMessage(state["user_input"]),
AIMessage(result["answer"]),
],
answer=result["answer"],
)
def create_reactome_graph(llm: BaseChatModel, embedding: Embeddings) -> StateGraph:
"""Create and return the ReactToMe workflow graph."""
return ReactToMeGraphBuilder(llm, embedding).uncompiled_graph