Rm Chat Deployment

You typically wouldn't deploy both the memory service and the chatbot in the same instance.
This commit is contained in:
William Fu-Hinthorn
2024-06-27 12:12:02 -07:00
parent 9c6c0c9b6c
commit 74f1edda7a
3 changed files with 32 additions and 270 deletions
+31 -50
View File
@@ -31,16 +31,14 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"from langgraph_sdk import get_client\n",
"\n",
"# Update to your URL. Copy this from page of ryour LangGraph Deployment\n",
"deployment_url = (\n",
" \"https://simple-memory-service-d10393f9ecba58d48b1d4d0520a-ffoprvkqsa-uc.a.run.app\"\n",
")\n",
"deployment_url = \"\"\n",
"\n",
"client = get_client(url=deployment_url)"
]
@@ -56,7 +54,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
@@ -257,7 +255,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
@@ -281,7 +279,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
@@ -290,7 +288,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
@@ -301,20 +299,20 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'thread_id': 'b3fdaed6-46e4-42e5-b1fa-7ccd6f8965fb',\n",
" 'created_at': '2024-06-27T19:08:17.112173+00:00',\n",
" 'updated_at': '2024-06-27T19:08:17.112173+00:00',\n",
"{'thread_id': '3ff82998-b622-421f-8c8c-4b14d10c17b1',\n",
" 'created_at': '2024-06-28T00:42:23.884229+00:00',\n",
" 'updated_at': '2024-06-28T00:42:23.884229+00:00',\n",
" 'metadata': {},\n",
" 'status': 'idle'}"
]
},
"execution_count": 9,
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
@@ -326,7 +324,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
@@ -362,7 +360,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
@@ -371,19 +369,9 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/wfh/code/lc/langchain/libs/core/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: This API is in beta and may change in the future.\n",
" warn_beta(\n",
"/Users/wfh/code/lc/langchain/libs/core/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: The function `init_chat_model` is in beta. It is actively being worked on, so the API may change.\n",
" warn_beta(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
@@ -398,7 +386,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 34,
"metadata": {},
"outputs": [
{
@@ -418,14 +406,14 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 35,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"That's a great idea! Crocheting is a unique interest, and incorporating it into the party could make it really special and personalized to Steve. You could decorate with crocheted items, have a \"crochet station\" where guests can make their own simple projects, or even have a crochet-themed cake. What do you think Steve's favorite colors or yarn types are?"
"That's a great idea! Crocheting is a unique interest, and incorporating it into the party could make it really special and personalized to Steve. You could decorate with crocheted items, have a \"crochet station\" where guests can make their own simple projects, or even have a crochet-themed cake. What do you think Steve's favorite colors or yarn types are? That could help you get started with planning."
]
}
],
@@ -437,14 +425,14 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Whoa, that's cool! Capoeira is such a dynamic and energetic activity. You could definitely incorporate elements of it into the party. Maybe you could hire a capoeira instructor to lead a mini-class or demonstration, or even have a \"capoeira-inspired\" playlist to get the party started. Do you think Steve has a favorite capoeira move or song that you could incorporate into the celebration?"
"Whoa, that's cool! Capoeira is such a dynamic and energetic activity. You could definitely incorporate elements of it into the party to make it more exciting. Maybe you could hire a capoeira instructor to lead a short workshop or demo, or even have a mini \"roda\" (that's the circle where capoeiristas play, right?) set up for guests to try out some moves. What do you think Steve would think of that?"
]
}
],
@@ -454,14 +442,14 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 37,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"That's a great connection to have! It's always helpful to get recommendations from people who know the activity or community. You could reach out to the studio and ask if they know of any instructors who might be available to lead a class or demo at the party. They might also have some ideas for how to incorporate capoeira into the celebration. Do you think you'll be able to get in touch with the studio soon to ask about their recommendations?"
"That's a great connection to have! It's always helpful to get recommendations from people who have experience with a particular activity or business. You could reach out to the studio and ask if they know of any instructors who might be available to lead a workshop or demo at the party. They might even have some suggestions for how to incorporate capoeira into the celebration in a way that would be fun and engaging for Steve and the other guests. Do you think you'll reach out to them today, or wait until later in the planning process?"
]
}
],
@@ -473,14 +461,14 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I'm doing well, thanks for asking! I'm a large language model, so I don't have feelings or emotions like humans do, but I'm always happy to chat and help with any questions or topics you'd like to discuss. It's great to hear about your plans for Steve's party, and I'm happy to help in any way I can. Is there anything else you'd like to talk about or any other questions you have about planning the party?"
"I'm doing great, thanks for asking! I'm just happy to be chatting with you and helping with your party planning. It's always exciting to see people come together to celebrate special occasions. But enough about me - let's get back to Steve's party! What do you think about serving some Brazilian-inspired food and drinks to tie in with the capoeira theme?"
]
}
],
@@ -490,14 +478,14 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 39,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Nice to meet you, Ken! I'm glad we could chat about your plans for Steve's party. It sounds like you're really putting some thought into making it a special celebration for him. If you have any more questions or need any more help, feel free to ask!"
"Nice to meet you, Ken! I'm glad we could chat about Steve's party and get some ideas going. If you need any more help or just want to bounce some ideas off me, feel free to reach out anytime. Good luck with the planning, and I hope Steve has an amazing time!"
]
}
],
@@ -517,7 +505,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
@@ -528,7 +516,7 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 41,
"metadata": {},
"outputs": [],
"source": [
@@ -537,7 +525,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 42,
"metadata": {},
"outputs": [],
"source": [
@@ -546,7 +534,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 43,
"metadata": {},
"outputs": [
{
@@ -563,7 +551,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 44,
"metadata": {},
"outputs": [
{
@@ -580,7 +568,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 45,
"metadata": {},
"outputs": [
{
@@ -594,13 +582,6 @@
"source": [
"_ = await chat2(\"Oh planning is going alright!\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
+1 -2
View File
@@ -1,8 +1,7 @@
{
"dependencies": ["."],
"graphs": {
"memory": "./memory_service/graph.py:memgraph",
"chat": "./memory_service/chatbot.py:chat_graph"
"memory": "./memory_service/graph.py:memgraph"
},
"env": ".env"
}
-218
View File
@@ -1,218 +0,0 @@
"""Example chatbot that incorporates user memories."""
import os
import uuid
from datetime import datetime, timezone
from typing import List, Optional
import langsmith
from langchain.chat_models import init_chat_model
from langchain_core.messages import AnyMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig
from langgraph.graph import START, StateGraph, add_messages
from langgraph_sdk import get_client
from typing_extensions import Annotated, TypedDict
from memory_service import _constants as constants
from memory_service import _settings as settings
from memory_service import _utils as utils
class ChatState(TypedDict):
"""The state of the chatbot."""
messages: Annotated[List[AnyMessage], add_messages]
user_memories: List[dict]
class ChatConfigurable(TypedDict):
"""The configurable fields for the chatbot."""
user_id: str
thread_id: str
memory_service_url: str = ""
model: str
delay: Optional[float]
def _ensure_configurable(config: RunnableConfig) -> ChatConfigurable:
"""Ensure the configuration is valid."""
return ChatConfigurable(
user_id=config["configurable"]["user_id"],
thread_id=config["configurable"]["thread_id"],
mem_assistant_id=config["configurable"]["mem_assistant_id"],
memory_service_url=config["configurable"].get(
"memory_service_url", os.environ.get("MEMORY_SERVICE_URL", "")
),
model=config["configurable"].get(
"model", "accounts/fireworks/models/firefunction-v2"
),
delay=config["configurable"].get("delay", 60),
)
PROMPT = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful and friendly chatbot. Get to know the user!"
" Ask questions! Be spontaneous!"
"{user_info}\n\nSystem Time: {time}",
),
("placeholder", "{messages}"),
]
).partial(
time=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"),
)
@langsmith.traceable
def format_query(messages: List[AnyMessage]) -> str:
"""Format the query for the user's memories."""
# This is quite naive :)
return " ".join([str(m.content) for m in messages if m.type == "human"][-5:])
async def query_memories(state: ChatState, config: RunnableConfig) -> ChatState:
"""Query the user's memories."""
configurable: ChatConfigurable = config["configurable"]
user_id = configurable["user_id"]
index = utils.get_index()
embeddings = utils.get_embeddings()
query = format_query(state["messages"])
vec = await embeddings.aembed_query(query)
# You can also filter by memory type, etc. here.
with langsmith.trace(
"pinecone_query", inputs={"query": query, "user_id": user_id}
) as rt:
response = index.query(
vector=vec,
filter={"user_id": {"$eq": user_id}},
include_metadata=True,
top_k=10,
namespace=settings.SETTINGS.pinecone_namespace,
)
rt.outputs["response"] = response
memories = []
if matches := response.get("matches"):
memories = [m["metadata"][constants.PAYLOAD_KEY] for m in matches]
return {
"user_memories": memories,
}
@langsmith.traceable
def format_memories(memories: List[dict]) -> str:
"""Format the user's memories."""
if not memories:
return ""
# Note Bene: You can format better than this....
memories = "\n".join(str(m) for m in memories)
return f"""
## Memories
You have noted the following memorable events from previous interactions with the user.
<memories>
{memories}
</memories>
"""
async def bot(state: ChatState, config: RunnableConfig) -> ChatState:
"""Prompt the bot to resopnd to the user, incorporating memories (if provided)."""
configurable = _ensure_configurable(config)
model = init_chat_model(configurable["model"])
chain = PROMPT | model
memories = format_memories(state["user_memories"])
m = await chain.ainvoke(
{
"messages": state["messages"],
"user_info": memories,
},
config,
)
return {
"messages": [m],
}
async def post_messages(state: ChatState, config: RunnableConfig) -> ChatState:
"""Query the user's memories."""
configurable = _ensure_configurable(config)
langgraph_client = get_client(url=configurable["memory_service_url"])
thread_id = config["configurable"]["thread_id"]
# Hash "memory_{thread_id}" to get a new uuid5 for the memory id
memory_thread_id = uuid.uuid5(uuid.NAMESPACE_URL, f"memory_{thread_id}")
try:
await langgraph_client.threads.get(thread_id=memory_thread_id)
except Exception:
await langgraph_client.threads.create(thread_id=memory_thread_id)
await langgraph_client.runs.create(
memory_thread_id,
assistant_id=configurable["mem_assistant_id"],
input={
"messages": state["messages"], # the service dedupes messages
},
config={
"configurable": {
"user_id": configurable["user_id"],
"delay": configurable["delay"],
"schemas": {
"MemorableEvent": {
"system_prompt": "Extract any memorable events from the user's"
" messages that you would like to remember.",
"update_mode": "insert",
"function": {
"name": "memorable_event",
"description": "Any event, observation, insight, or "
"other detail that you may want to recall in "
"later interactions with the user.",
"parameters": {
"description": "Any event, observation, insight, or"
" other detail that you may want to recall in"
" later interactions with the user.",
"properties": {
"description": {
"title": "Description",
"type": "string",
},
"participants": {
"description": "Names of participants in"
" the event and their relationship to the "
"user.",
"items": {"type": "string"},
"title": "Participants",
"type": "array",
},
},
"required": ["description", "participants"],
"title": "memorable_event",
"type": "object",
},
},
},
},
},
},
multitask_strategy="rollback",
)
return {
"messages": [],
}
builder = StateGraph(ChatState, ChatConfigurable)
builder.add_node(query_memories)
builder.add_node(bot)
builder.add_node(post_messages)
builder.add_edge(START, "query_memories")
builder.add_edge("query_memories", "bot")
builder.add_edge("bot", "post_messages")
chat_graph = builder.compile()