| from dotenv import load_dotenv |
| from langchain_openai import ChatOpenAI |
| from typing import TypedDict, List |
| import uuid |
| from langgraph.store.memory import InMemoryStore |
| from pydantic import BaseModel, Field |
|
|
| from langchain_core.messages import HumanMessage |
| from IPython.display import Image, display |
|
|
| from langgraph.checkpoint.memory import MemorySaver |
| from langgraph.graph import StateGraph, MessagesState, START, END |
| from langgraph.store.base import BaseStore |
|
|
| from langchain_core.messages import HumanMessage, SystemMessage, AIMessage |
| from langchain_core.runnables.config import RunnableConfig |
| from trustcall import create_extractor |
|
|
|
|
| load_dotenv() |
| model = ChatOpenAI(model="gpt-4.1-mini", temperature=0) |
|
|
| from typing import TypedDict, List |
|
|
| class UserProfile(TypedDict): |
| """User profile schema with typed fields""" |
| user_name: str |
| interests: List[str] |
|
|
| |
| user_profile: UserProfile = { |
| "user_name": "Lance", |
| "interests": ["biking", "technology", "coffee"] |
| } |
| print("------------------") |
| print("User Profile:") |
| print("------------------") |
| print(user_profile) |
|
|
| |
| in_memory_store = InMemoryStore() |
|
|
| |
| user_id = "1" |
| namespace_for_memory = (user_id, "memory") |
|
|
| |
| key = "user_profile" |
| value = user_profile |
| in_memory_store.put(namespace_for_memory, key, value) |
|
|
| |
| for m in in_memory_store.search(namespace_for_memory): |
| print(m.dict()) |
|
|
| |
| profile = in_memory_store.get(namespace_for_memory, "user_profile") |
| print (profile.value) |
|
|
| |
| model_with_structure = model.with_structured_output(UserProfile) |
|
|
| |
| structured_output = model_with_structure.invoke([HumanMessage("My name is Lance, I like to bike.")]) |
| print("------------------") |
| print("Structured Output:") |
| print("------------------") |
| print(structured_output) |
|
|
| |
| MODEL_SYSTEM_MESSAGE = """You are a helpful assistant with memory that provides information about the user. |
| If you have memory for this user, use it to personalize your responses. |
| Here is the memory (it may be empty): {memory}""" |
|
|
| |
| CREATE_MEMORY_INSTRUCTION = """Create or update a user profile memory based on the user's chat history. |
| This will be saved for long-term memory. If there is an existing memory, simply update it. |
| Here is the existing memory (it may be empty): {memory}""" |
|
|
| def call_model(state: MessagesState, config: RunnableConfig, store: BaseStore): |
|
|
| """Load memory from the store and use it to personalize the chatbot's response.""" |
| |
| |
| user_id = config["configurable"]["user_id"] |
|
|
| |
| namespace = ("memory", user_id) |
| existing_memory = store.get(namespace, "user_memory") |
|
|
| |
| if existing_memory and existing_memory.value: |
| memory_dict = existing_memory.value |
| formatted_memory = ( |
| f"Name: {memory_dict.get('user_name', 'Unknown')}\n" |
| f"Interests: {', '.join(memory_dict.get('interests', []))}" |
| ) |
| else: |
| formatted_memory = None |
|
|
| |
| system_msg = MODEL_SYSTEM_MESSAGE.format(memory=formatted_memory) |
|
|
| |
| response = model.invoke([SystemMessage(content=system_msg)]+state["messages"]) |
|
|
| return {"messages": response} |
|
|
| def write_memory(state: MessagesState, config: RunnableConfig, store: BaseStore): |
|
|
| """Reflect on the chat history and save a memory to the store.""" |
| |
| |
| user_id = config["configurable"]["user_id"] |
|
|
| |
| namespace = ("memory", user_id) |
| existing_memory = store.get(namespace, "user_memory") |
|
|
| |
| if existing_memory and existing_memory.value: |
| memory_dict = existing_memory.value |
| formatted_memory = ( |
| f"Name: {memory_dict.get('user_name', 'Unknown')}\n" |
| f"Interests: {', '.join(memory_dict.get('interests', []))}" |
| ) |
| else: |
| formatted_memory = None |
| |
| |
| system_msg = CREATE_MEMORY_INSTRUCTION.format(memory=formatted_memory) |
|
|
| |
| new_memory = model_with_structure.invoke([SystemMessage(content=system_msg)]+state['messages']) |
|
|
| |
| key = "user_memory" |
| store.put(namespace, key, new_memory) |
|
|
| |
| builder = StateGraph(MessagesState) |
| builder.add_node("call_model", call_model) |
| builder.add_node("write_memory", write_memory) |
| builder.add_edge(START, "call_model") |
| builder.add_edge("call_model", "write_memory") |
| builder.add_edge("write_memory", END) |
|
|
| |
| across_thread_memory = InMemoryStore() |
|
|
| |
| within_thread_memory = MemorySaver() |
|
|
| |
| graph = builder.compile(checkpointer=within_thread_memory, store=across_thread_memory) |
|
|
| with open("memoryschema01.png", "wb") as f: |
| f.write(graph.get_graph().draw_mermaid_png()) |
|
|
| |
| |
| config = {"configurable": {"thread_id": "1", "user_id": "1"}} |
|
|
| |
| input_messages = [HumanMessage(content="Hi, my name is Lance and I like to bike around San Francisco and eat at bakeries.")] |
|
|
| print("------------------") |
| print("Mensaje 1:") |
| print("------------------") |
| |
| for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): |
| chunk["messages"][-1].pretty_print() |
|
|
|
|
| |
| user_id = "1" |
| namespace = ("memory", user_id) |
| existing_memory = across_thread_memory.get(namespace, "user_memory") |
| print("------------------") |
| print("Memory after first message:") |
| print("------------------") |
| print(existing_memory.value) |
|
|
|
|
|
|
| |
| class UserProfile(BaseModel): |
| """ Profile of a user """ |
| user_name: str = Field(description="The user's preferred name") |
| user_location: str = Field(description="The user's location") |
| interests: list = Field(description="A list of the user's interests") |
|
|
| |
| trustcall_extractor = create_extractor( |
| model, |
| tools=[UserProfile], |
| tool_choice="UserProfile", |
| ) |
|
|
| |
| MODEL_SYSTEM_MESSAGE = """You are a helpful assistant with memory that provides information about the user. |
| If you have memory for this user, use it to personalize your responses. |
| Here is the memory (it may be empty): {memory}""" |
|
|
| |
| TRUSTCALL_INSTRUCTION = """Create or update the memory (JSON doc) to incorporate information from the following conversation:""" |
|
|
| def call_model(state: MessagesState, config: RunnableConfig, store: BaseStore): |
|
|
| """Load memory from the store and use it to personalize the chatbot's response.""" |
| |
| |
| user_id = config["configurable"]["user_id"] |
|
|
| |
| namespace = ("memory", user_id) |
| existing_memory = store.get(namespace, "user_memory") |
|
|
| |
| if existing_memory and existing_memory.value: |
| memory_dict = existing_memory.value |
| formatted_memory = ( |
| f"Name: {memory_dict.get('user_name', 'Unknown')}\n" |
| f"Location: {memory_dict.get('user_location', 'Unknown')}\n" |
| f"Interests: {', '.join(memory_dict.get('interests', []))}" |
| ) |
| else: |
| formatted_memory = None |
|
|
| |
| system_msg = MODEL_SYSTEM_MESSAGE.format(memory=formatted_memory) |
|
|
| |
| response = model.invoke([SystemMessage(content=system_msg)]+state["messages"]) |
|
|
| return {"messages": response} |
|
|
| def write_memory(state: MessagesState, config: RunnableConfig, store: BaseStore): |
|
|
| """Reflect on the chat history and save a memory to the store.""" |
| |
| |
| user_id = config["configurable"]["user_id"] |
|
|
| |
| namespace = ("memory", user_id) |
| existing_memory = store.get(namespace, "user_memory") |
| |
| |
| existing_profile = {"UserProfile": existing_memory.value} if existing_memory else None |
| |
| |
| result = trustcall_extractor.invoke({"messages": [SystemMessage(content=TRUSTCALL_INSTRUCTION)]+state["messages"], "existing": existing_profile}) |
| |
| |
| updated_profile = result["responses"][0].model_dump() |
|
|
| |
| key = "user_memory" |
| store.put(namespace, key, updated_profile) |
|
|
| |
| builder = StateGraph(MessagesState) |
| builder.add_node("call_model", call_model) |
| builder.add_node("write_memory", write_memory) |
| builder.add_edge(START, "call_model") |
| builder.add_edge("call_model", "write_memory") |
| builder.add_edge("write_memory", END) |
|
|
| |
| across_thread_memory = InMemoryStore() |
|
|
| |
| within_thread_memory = MemorySaver() |
|
|
| |
| graph = builder.compile(checkpointer=within_thread_memory, store=across_thread_memory) |
|
|
| |
| |
| config = {"configurable": {"thread_id": "1", "user_id": "1"}} |
|
|
| |
| input_messages = [HumanMessage(content="Hi, my name is Lance")] |
|
|
| print("------------------") |
| print("Chatbot with TrustCall: Mensaje 1") |
| print("------------------") |
| |
| for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): |
| chunk["messages"][-1].pretty_print() |
|
|
| |
| input_messages = [HumanMessage(content="I like to bike around San Francisco")] |
|
|
| print("------------------") |
| print("Chatbot with TrustCall: Mensaje 2") |
| print("------------------") |
| |
| for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): |
| chunk["messages"][-1].pretty_print() |
|
|
| print("------------------") |
| print("Chatbot with TrustCall: Memory after messages") |
| print("------------------") |
| |
| user_id = "1" |
| namespace = ("memory", user_id) |
| existing_memory = across_thread_memory.get(namespace, "user_memory") |
| print(existing_memory.dict()) |
|
|
| print("------------------") |
| print("Chatbot with TrustCall: Mensaje 3") |
| print("------------------") |
| |
| input_messages = [HumanMessage(content="I also enjoy going to bakeries")] |
|
|
| |
| for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): |
| chunk["messages"][-1].pretty_print() |
|
|
| print("------------------") |
| print("Chatbot with TrustCall: Mensaje 4") |
| print("------------------") |
| |
| |
| config = {"configurable": {"thread_id": "2", "user_id": "1"}} |
|
|
| |
| input_messages = [HumanMessage(content="What bakeries do you recommend for me?")] |
|
|
| |
| for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): |
| chunk["messages"][-1].pretty_print() |