Spaces:
Sleeping
Sleeping
File size: 6,175 Bytes
fff68ed 3d905bb fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed a2b2c84 c5c6464 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd fff68ed 1918fdd | 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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | # utils/memory_nodes.py - Memory retrieval and storage nodes with lazy init
from .state import AgentState
from langchain_core.messages import HumanMessage, AIMessage
import time
import structlog
log = structlog.get_logger("atelier.memory.nodes")
# Lazy initialization - don't create connection until first use
_memory_store = None
def get_memory_store():
"""Lazy initialization of memory store."""
global _memory_store
if _memory_store is None:
log.info("memory_store_initializing")
try:
from .pinecone_memory import PineconeMemoryStore
_memory_store = PineconeMemoryStore()
log.info("memory_store_initialized")
except Exception as e:
log.error("memory_store_init_failed", error=str(e), exc_info=True)
_memory_store = False # Mark as failed to avoid retrying
return _memory_store if _memory_store is not False else None
def retrieve_semantic_memory(state: AgentState) -> AgentState:
"""Retrieve relevant memories from Weaviate before processing."""
t0 = time.perf_counter()
store = get_memory_store()
if store is None:
log.warning("memory_store_unavailable", node="retrieve_semantic_memory")
return state
user_id = state.get("user_id", "default_user")
messages = state["messages"]
# Build query from recent user messages
recent_context = ""
for msg in reversed(messages[-3:]):
if isinstance(msg, HumanMessage) and hasattr(msg, "content"):
recent_context = msg.content + " " + recent_context
recent_context = recent_context.strip()
if not recent_context:
log.debug("memory_retrieval_skipped", reason="no_recent_context", user_id=user_id)
return state
try:
preferences = store.get_relevant_preferences(user_id=user_id, query=recent_context, limit=3)
similar_convos = store.retrieve_similar_conversations(user_id=user_id, current_context=recent_context, limit=2)
patterns = store.find_similar_patterns(user_id=user_id, task_description=recent_context, limit=3)
log.info(
"memory_retrieved",
user_id=user_id,
preferences_count=len(preferences),
conversations_count=len(similar_convos),
patterns_count=len(patterns),
latency_ms=round((time.perf_counter() - t0) * 1000, 2),
)
return {
**state,
"relevant_preferences": preferences,
"similar_conversations": similar_convos,
"scheduling_patterns": patterns,
}
except Exception as e:
log.error(
"memory_retrieval_failed",
user_id=user_id,
error=str(e),
exc_info=True,
)
return state # degrade gracefully — don't break the graph
def store_interaction_memory(state: AgentState) -> AgentState:
"""Store completed interaction in Weaviate for future learning."""
t0 = time.perf_counter()
store = get_memory_store()
if store is None:
log.warning("memory_store_unavailable", node="store_interaction_memory")
return state
user_id = state.get("user_id", "default_user")
thread_id = state.get("thread_id", "default_thread")
messages = state["messages"]
task_type = state.get("task_type", "unknown")
try:
# Find last user and assistant messages
user_msg = None
assistant_msg = None
for msg in reversed(messages):
if isinstance(msg, HumanMessage) and user_msg is None:
user_msg = msg.content
elif isinstance(msg, AIMessage) and assistant_msg is None:
assistant_msg = msg.content
if user_msg and assistant_msg:
break
if not (user_msg and assistant_msg):
log.warning(
"memory_store_skipped",
reason="missing_user_or_assistant_message",
user_id=user_id,
thread_id=thread_id,
)
return state
store.store_conversation_turn(
user_id=user_id,
thread_id=thread_id,
user_message=user_msg,
assistant_response=assistant_msg,
task_type=task_type,
successful=True,
)
# Store per-task scheduling patterns
tasks = state.get("tasks", [])
failed_patterns = 0
for task in tasks:
summary = task.get("summary", "Untitled Task")
start_time = task.get("start", "")
if isinstance(start_time, dict):
start_time = start_time.get("dateTime", "")
start_time_str = str(start_time) if start_time else "unspecified time"
pattern_desc = f"User scheduled '{summary}' at {start_time_str}"
try:
store.store_scheduling_pattern(
user_id=user_id,
pattern_description=pattern_desc,
task_type=task_type,
task_data=task,
)
except Exception as e:
failed_patterns += 1
log.error(
"scheduling_pattern_store_failed",
user_id=user_id,
task_summary=summary,
error=str(e),
)
log.info(
"interaction_memory_stored",
user_id=user_id,
thread_id=thread_id,
task_type=task_type,
patterns_stored=len(tasks) - failed_patterns,
patterns_failed=failed_patterns,
latency_ms=round((time.perf_counter() - t0) * 1000, 2),
)
return state
except Exception as e:
log.error(
"store_interaction_memory_failed",
user_id=user_id,
thread_id=thread_id,
error=str(e),
exc_info=True,
)
return state # degrade gracefully
def get_memory_store_for_cleanup():
"""Get memory store instance for cleanup."""
return _memory_store if isinstance(_memory_store, object) else None |