mcal-ai 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcal/__init__.py +165 -0
- mcal/backends/__init__.py +42 -0
- mcal/backends/base.py +383 -0
- mcal/baselines/__init__.py +1 -0
- mcal/core/__init__.py +101 -0
- mcal/core/embeddings.py +266 -0
- mcal/core/extraction_cache.py +398 -0
- mcal/core/goal_retriever.py +539 -0
- mcal/core/intent_tracker.py +734 -0
- mcal/core/models.py +445 -0
- mcal/core/rate_limiter.py +372 -0
- mcal/core/reasoning_store.py +1061 -0
- mcal/core/retry.py +188 -0
- mcal/core/storage.py +456 -0
- mcal/core/streaming.py +254 -0
- mcal/core/unified_extractor.py +1466 -0
- mcal/core/vector_index.py +206 -0
- mcal/evaluation/__init__.py +1 -0
- mcal/integrations/__init__.py +88 -0
- mcal/integrations/autogen.py +95 -0
- mcal/integrations/crewai.py +92 -0
- mcal/integrations/langchain.py +112 -0
- mcal/integrations/langgraph.py +50 -0
- mcal/mcal.py +1697 -0
- mcal/providers/bedrock.py +217 -0
- mcal/storage/__init__.py +1 -0
- mcal_ai-0.1.0.dist-info/METADATA +319 -0
- mcal_ai-0.1.0.dist-info/RECORD +32 -0
- mcal_ai-0.1.0.dist-info/WHEEL +5 -0
- mcal_ai-0.1.0.dist-info/entry_points.txt +2 -0
- mcal_ai-0.1.0.dist-info/licenses/LICENSE +21 -0
- mcal_ai-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vector Index for MCAL Graph Nodes
|
|
3
|
+
|
|
4
|
+
FAISS-based vector similarity search for semantic queries on graph nodes.
|
|
5
|
+
|
|
6
|
+
Performance (from pre-implementation analysis):
|
|
7
|
+
- Build time: <0.2ms for 1000 nodes
|
|
8
|
+
- Search time: <0.04ms per query
|
|
9
|
+
- Memory: ~1.5KB per node (normalized float32)
|
|
10
|
+
|
|
11
|
+
Index Type: IndexFlatIP (Inner Product)
|
|
12
|
+
- With normalized vectors = cosine similarity
|
|
13
|
+
- Exact search (no approximation)
|
|
14
|
+
- Perfect for small-medium graphs (<10K nodes)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import TYPE_CHECKING, Optional
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import faiss
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from .unified_extractor import GraphNode
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VectorIndex:
|
|
32
|
+
"""
|
|
33
|
+
FAISS-based vector index for graph nodes.
|
|
34
|
+
|
|
35
|
+
Uses IndexFlatIP (inner product) with L2 normalization for cosine similarity.
|
|
36
|
+
Designed for graphs up to ~10K nodes where exact search is fast enough.
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
index = VectorIndex()
|
|
40
|
+
index.add_batch([(node_id, embedding_bytes), ...])
|
|
41
|
+
results = index.search(query_embedding, k=10)
|
|
42
|
+
# results = [(node_id, similarity_score), ...]
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
DIMENSION = 384 # all-MiniLM-L6-v2 output dimension
|
|
46
|
+
|
|
47
|
+
def __init__(self, dimension: int = DIMENSION):
|
|
48
|
+
"""
|
|
49
|
+
Initialize empty FAISS index.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
dimension: Embedding dimension (default: 384 for MiniLM)
|
|
53
|
+
"""
|
|
54
|
+
self.dimension = dimension
|
|
55
|
+
self.index = faiss.IndexFlatIP(dimension) # Inner product for cosine sim
|
|
56
|
+
self._id_map: list[str] = [] # node_id at each index position
|
|
57
|
+
self._node_count = 0
|
|
58
|
+
|
|
59
|
+
def add(self, node_id: str, embedding: bytes) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Add a single node embedding to the index.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
node_id: Unique identifier for the node
|
|
65
|
+
embedding: Float16 binary embedding (from GraphNode.embedding)
|
|
66
|
+
"""
|
|
67
|
+
vec = self._bytes_to_normalized_vector(embedding)
|
|
68
|
+
self.index.add(vec.reshape(1, -1))
|
|
69
|
+
self._id_map.append(node_id)
|
|
70
|
+
self._node_count += 1
|
|
71
|
+
|
|
72
|
+
def add_batch(self, nodes: list[tuple[str, bytes]]) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Add multiple node embeddings at once (4.7x faster than individual adds).
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
nodes: List of (node_id, embedding_bytes) tuples
|
|
78
|
+
"""
|
|
79
|
+
if not nodes:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
ids = []
|
|
83
|
+
vectors = []
|
|
84
|
+
|
|
85
|
+
for node_id, embedding in nodes:
|
|
86
|
+
ids.append(node_id)
|
|
87
|
+
vectors.append(self._bytes_to_normalized_vector(embedding))
|
|
88
|
+
|
|
89
|
+
# Stack and add to FAISS
|
|
90
|
+
vecs = np.vstack(vectors)
|
|
91
|
+
self.index.add(vecs)
|
|
92
|
+
self._id_map.extend(ids)
|
|
93
|
+
self._node_count += len(nodes)
|
|
94
|
+
|
|
95
|
+
def search(
|
|
96
|
+
self,
|
|
97
|
+
query_embedding: bytes,
|
|
98
|
+
k: int = 10
|
|
99
|
+
) -> list[tuple[str, float]]:
|
|
100
|
+
"""
|
|
101
|
+
Search for k most similar nodes.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
query_embedding: Float16 binary embedding for query
|
|
105
|
+
k: Number of results to return
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
List of (node_id, similarity_score) tuples, sorted by score descending
|
|
109
|
+
"""
|
|
110
|
+
if self._node_count == 0:
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
# Ensure we don't request more than we have
|
|
114
|
+
k = min(k, self._node_count)
|
|
115
|
+
|
|
116
|
+
# Convert and normalize query
|
|
117
|
+
query_vec = self._bytes_to_normalized_vector(query_embedding)
|
|
118
|
+
|
|
119
|
+
# Search
|
|
120
|
+
scores, indices = self.index.search(query_vec.reshape(1, -1), k)
|
|
121
|
+
|
|
122
|
+
# Map indices back to node IDs
|
|
123
|
+
results = []
|
|
124
|
+
for score, idx in zip(scores[0], indices[0]):
|
|
125
|
+
if 0 <= idx < len(self._id_map):
|
|
126
|
+
results.append((self._id_map[idx], float(score)))
|
|
127
|
+
|
|
128
|
+
return results
|
|
129
|
+
|
|
130
|
+
def search_text(
|
|
131
|
+
self,
|
|
132
|
+
query_text: str,
|
|
133
|
+
k: int = 10
|
|
134
|
+
) -> list[tuple[str, float]]:
|
|
135
|
+
"""
|
|
136
|
+
Search using text query (embeds the query first).
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
query_text: Text to search for
|
|
140
|
+
k: Number of results
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
List of (node_id, similarity_score) tuples
|
|
144
|
+
"""
|
|
145
|
+
from .embeddings import EmbeddingService
|
|
146
|
+
|
|
147
|
+
service = EmbeddingService()
|
|
148
|
+
query_embedding = service.embed_text(query_text)
|
|
149
|
+
return self.search(query_embedding, k)
|
|
150
|
+
|
|
151
|
+
def clear(self) -> None:
|
|
152
|
+
"""Clear the index and reset state."""
|
|
153
|
+
self.index = faiss.IndexFlatIP(self.dimension)
|
|
154
|
+
self._id_map = []
|
|
155
|
+
self._node_count = 0
|
|
156
|
+
|
|
157
|
+
def _bytes_to_normalized_vector(self, data: bytes) -> np.ndarray:
|
|
158
|
+
"""
|
|
159
|
+
Convert Float16 bytes to normalized Float32 vector.
|
|
160
|
+
|
|
161
|
+
FAISS requires float32, and we normalize for cosine similarity.
|
|
162
|
+
"""
|
|
163
|
+
# Convert from Float16 bytes to Float32
|
|
164
|
+
vec = np.frombuffer(data, dtype=np.float16).astype(np.float32)
|
|
165
|
+
|
|
166
|
+
# Normalize for cosine similarity (IndexFlatIP + normalized = cosine)
|
|
167
|
+
norm = np.linalg.norm(vec)
|
|
168
|
+
if norm > 0:
|
|
169
|
+
vec = vec / norm
|
|
170
|
+
|
|
171
|
+
return vec
|
|
172
|
+
|
|
173
|
+
def __len__(self) -> int:
|
|
174
|
+
"""Return number of vectors in index."""
|
|
175
|
+
return self._node_count
|
|
176
|
+
|
|
177
|
+
def __contains__(self, node_id: str) -> bool:
|
|
178
|
+
"""Check if a node ID is in the index."""
|
|
179
|
+
return node_id in self._id_map
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def build_index_from_nodes(nodes: list["GraphNode"]) -> VectorIndex:
|
|
183
|
+
"""
|
|
184
|
+
Build a VectorIndex from a list of graph nodes.
|
|
185
|
+
|
|
186
|
+
Only includes nodes that have embeddings.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
nodes: List of GraphNode objects
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
VectorIndex populated with node embeddings
|
|
193
|
+
"""
|
|
194
|
+
index = VectorIndex()
|
|
195
|
+
|
|
196
|
+
nodes_with_embeddings = [
|
|
197
|
+
(node.id, node.embedding)
|
|
198
|
+
for node in nodes
|
|
199
|
+
if node.embedding is not None
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
if nodes_with_embeddings:
|
|
203
|
+
index.add_batch(nodes_with_embeddings)
|
|
204
|
+
logger.debug(f"Built index with {len(nodes_with_embeddings)} nodes")
|
|
205
|
+
|
|
206
|
+
return index
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MCAL Evaluation metrics and benchmarks."""
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCAL Framework Integrations
|
|
3
|
+
|
|
4
|
+
Provides seamless integration with popular AI agent frameworks.
|
|
5
|
+
|
|
6
|
+
Installation:
|
|
7
|
+
pip install mcal[langgraph] # LangGraph integration
|
|
8
|
+
pip install mcal[crewai] # CrewAI integration
|
|
9
|
+
pip install mcal[autogen] # AutoGen integration
|
|
10
|
+
pip install mcal[integrations] # All integrations
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
# Option 1: Explicit import (recommended)
|
|
14
|
+
from mcal.integrations.langgraph import MCALMemory
|
|
15
|
+
|
|
16
|
+
# Option 2: Namespace access
|
|
17
|
+
from mcal import integrations
|
|
18
|
+
memory = integrations.langgraph.MCALMemory()
|
|
19
|
+
|
|
20
|
+
# Option 3: Direct shortcut
|
|
21
|
+
from mcal import LangGraphMemory
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
|
|
26
|
+
# Lazy imports to avoid loading dependencies until needed
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from mcal.integrations import langgraph
|
|
29
|
+
from mcal.integrations import crewai
|
|
30
|
+
from mcal.integrations import autogen
|
|
31
|
+
from mcal.integrations import langchain
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __getattr__(name: str):
|
|
35
|
+
"""Lazy load integrations to avoid import errors when extras not installed."""
|
|
36
|
+
if name == "langgraph":
|
|
37
|
+
try:
|
|
38
|
+
from mcal.integrations import langgraph as _langgraph
|
|
39
|
+
return _langgraph
|
|
40
|
+
except ImportError as e:
|
|
41
|
+
raise ImportError(
|
|
42
|
+
f"LangGraph integration requires extra dependencies.\n"
|
|
43
|
+
f"Install with: pip install mcal[langgraph]\n"
|
|
44
|
+
f"Original error: {e}"
|
|
45
|
+
) from e
|
|
46
|
+
|
|
47
|
+
elif name == "crewai":
|
|
48
|
+
try:
|
|
49
|
+
from mcal.integrations import crewai as _crewai
|
|
50
|
+
return _crewai
|
|
51
|
+
except ImportError as e:
|
|
52
|
+
raise ImportError(
|
|
53
|
+
f"CrewAI integration requires extra dependencies.\n"
|
|
54
|
+
f"Install with: pip install mcal[crewai]\n"
|
|
55
|
+
f"Original error: {e}"
|
|
56
|
+
) from e
|
|
57
|
+
|
|
58
|
+
elif name == "autogen":
|
|
59
|
+
try:
|
|
60
|
+
from mcal.integrations import autogen as _autogen
|
|
61
|
+
return _autogen
|
|
62
|
+
except ImportError as e:
|
|
63
|
+
raise ImportError(
|
|
64
|
+
f"AutoGen integration requires extra dependencies.\n"
|
|
65
|
+
f"Install with: pip install mcal[autogen]\n"
|
|
66
|
+
f"Original error: {e}"
|
|
67
|
+
) from e
|
|
68
|
+
|
|
69
|
+
elif name == "langchain":
|
|
70
|
+
try:
|
|
71
|
+
from mcal.integrations import langchain as _langchain
|
|
72
|
+
return _langchain
|
|
73
|
+
except ImportError as e:
|
|
74
|
+
raise ImportError(
|
|
75
|
+
f"LangChain integration requires extra dependencies.\n"
|
|
76
|
+
f"Install with: pip install mcal[langchain]\n"
|
|
77
|
+
f"Original error: {e}"
|
|
78
|
+
) from e
|
|
79
|
+
|
|
80
|
+
raise AttributeError(f"module 'mcal.integrations' has no attribute '{name}'")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def __dir__():
|
|
84
|
+
"""List available integrations."""
|
|
85
|
+
return ["langgraph", "crewai", "autogen", "langchain"]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ["langgraph", "crewai", "autogen", "langchain"]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCAL AutoGen Integration
|
|
3
|
+
|
|
4
|
+
Provides memory components for Microsoft AutoGen agent workflows.
|
|
5
|
+
|
|
6
|
+
Installation:
|
|
7
|
+
pip install mcal[autogen]
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from mcal.integrations.autogen import MCALMemoryAgent
|
|
11
|
+
|
|
12
|
+
memory_agent = MCALMemoryAgent(llm_provider="anthropic")
|
|
13
|
+
# Use in AutoGen group chat
|
|
14
|
+
|
|
15
|
+
Status: Coming Soon
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
# Check for AutoGen availability
|
|
21
|
+
try:
|
|
22
|
+
import autogen
|
|
23
|
+
AUTOGEN_AVAILABLE = True
|
|
24
|
+
except ImportError:
|
|
25
|
+
AUTOGEN_AVAILABLE = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _check_autogen():
|
|
29
|
+
"""Raise helpful error if AutoGen not installed."""
|
|
30
|
+
if not AUTOGEN_AVAILABLE:
|
|
31
|
+
raise ImportError(
|
|
32
|
+
"AutoGen integration requires pyautogen package.\n"
|
|
33
|
+
"Install with: pip install mcal[autogen]"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MCALMemoryAgent:
|
|
38
|
+
"""
|
|
39
|
+
MCAL Memory agent for AutoGen workflows.
|
|
40
|
+
|
|
41
|
+
Provides a specialized agent that manages goal-aware memory
|
|
42
|
+
for AutoGen group chats and workflows.
|
|
43
|
+
|
|
44
|
+
Status: Coming Soon - Basic structure implemented.
|
|
45
|
+
|
|
46
|
+
Usage:
|
|
47
|
+
from mcal.integrations.autogen import MCALMemoryAgent
|
|
48
|
+
from autogen import AssistantAgent, UserProxyAgent
|
|
49
|
+
|
|
50
|
+
memory_agent = MCALMemoryAgent(
|
|
51
|
+
name="memory_manager",
|
|
52
|
+
llm_provider="anthropic",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Add to group chat
|
|
56
|
+
group_chat = GroupChat(
|
|
57
|
+
agents=[user, assistant, memory_agent],
|
|
58
|
+
...
|
|
59
|
+
)
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
name: str = "memory_manager",
|
|
65
|
+
llm_provider: str = "anthropic",
|
|
66
|
+
embedding_provider: str = "openai",
|
|
67
|
+
storage_path: Optional[str] = None,
|
|
68
|
+
**mcal_kwargs
|
|
69
|
+
):
|
|
70
|
+
_check_autogen()
|
|
71
|
+
|
|
72
|
+
self.name = name
|
|
73
|
+
|
|
74
|
+
# Import MCAL here to avoid circular imports
|
|
75
|
+
from mcal import MCAL
|
|
76
|
+
|
|
77
|
+
self._mcal = MCAL(
|
|
78
|
+
llm_provider=llm_provider,
|
|
79
|
+
embedding_provider=embedding_provider,
|
|
80
|
+
storage_path=storage_path,
|
|
81
|
+
**mcal_kwargs
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
async def process_message(self, message: str, sender: str) -> Optional[str]:
|
|
85
|
+
"""Process a message and update memory."""
|
|
86
|
+
# TODO: Implement AutoGen-specific message processing
|
|
87
|
+
raise NotImplementedError("AutoGen integration coming soon")
|
|
88
|
+
|
|
89
|
+
async def get_context(self, query: str) -> str:
|
|
90
|
+
"""Get relevant context for a query."""
|
|
91
|
+
# TODO: Implement AutoGen-specific context retrieval
|
|
92
|
+
raise NotImplementedError("AutoGen integration coming soon")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = ["MCALMemoryAgent", "AUTOGEN_AVAILABLE"]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCAL CrewAI Integration
|
|
3
|
+
|
|
4
|
+
Provides memory components for CrewAI agent crews.
|
|
5
|
+
|
|
6
|
+
Installation:
|
|
7
|
+
pip install mcal[crewai]
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from mcal.integrations.crewai import MCALCrewMemory
|
|
11
|
+
|
|
12
|
+
memory = MCALCrewMemory(llm_provider="anthropic")
|
|
13
|
+
crew = Crew(
|
|
14
|
+
agents=[...],
|
|
15
|
+
memory=memory,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
Status: Coming Soon
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
# Check for CrewAI availability
|
|
24
|
+
try:
|
|
25
|
+
import crewai
|
|
26
|
+
CREWAI_AVAILABLE = True
|
|
27
|
+
except ImportError:
|
|
28
|
+
CREWAI_AVAILABLE = False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _check_crewai():
|
|
32
|
+
"""Raise helpful error if CrewAI not installed."""
|
|
33
|
+
if not CREWAI_AVAILABLE:
|
|
34
|
+
raise ImportError(
|
|
35
|
+
"CrewAI integration requires crewai package.\n"
|
|
36
|
+
"Install with: pip install mcal[crewai]"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MCALCrewMemory:
|
|
41
|
+
"""
|
|
42
|
+
MCAL Memory component for CrewAI workflows.
|
|
43
|
+
|
|
44
|
+
Provides goal-aware memory that preserves reasoning context
|
|
45
|
+
across agent crew interactions.
|
|
46
|
+
|
|
47
|
+
Status: Coming Soon - Basic structure implemented.
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
from mcal.integrations.crewai import MCALCrewMemory
|
|
51
|
+
from crewai import Crew, Agent
|
|
52
|
+
|
|
53
|
+
memory = MCALCrewMemory(llm_provider="anthropic")
|
|
54
|
+
|
|
55
|
+
crew = Crew(
|
|
56
|
+
agents=[agent1, agent2],
|
|
57
|
+
tasks=[task1, task2],
|
|
58
|
+
memory=memory, # MCAL provides the memory layer
|
|
59
|
+
)
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
llm_provider: str = "anthropic",
|
|
65
|
+
embedding_provider: str = "openai",
|
|
66
|
+
storage_path: Optional[str] = None,
|
|
67
|
+
**mcal_kwargs
|
|
68
|
+
):
|
|
69
|
+
_check_crewai()
|
|
70
|
+
|
|
71
|
+
# Import MCAL here to avoid circular imports
|
|
72
|
+
from mcal import MCAL
|
|
73
|
+
|
|
74
|
+
self._mcal = MCAL(
|
|
75
|
+
llm_provider=llm_provider,
|
|
76
|
+
embedding_provider=embedding_provider,
|
|
77
|
+
storage_path=storage_path,
|
|
78
|
+
**mcal_kwargs
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
async def save(self, key: str, value: Any, agent_name: str = "default") -> None:
|
|
82
|
+
"""Save information to memory."""
|
|
83
|
+
# TODO: Implement CrewAI-specific save
|
|
84
|
+
raise NotImplementedError("CrewAI integration coming soon")
|
|
85
|
+
|
|
86
|
+
async def search(self, query: str, agent_name: str = "default") -> List[Dict[str, Any]]:
|
|
87
|
+
"""Search memory for relevant information."""
|
|
88
|
+
# TODO: Implement CrewAI-specific search
|
|
89
|
+
raise NotImplementedError("CrewAI integration coming soon")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
__all__ = ["MCALCrewMemory", "CREWAI_AVAILABLE"]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCAL LangChain Integration
|
|
3
|
+
|
|
4
|
+
Provides memory components for LangChain chains and agents.
|
|
5
|
+
|
|
6
|
+
Installation:
|
|
7
|
+
pip install mcal[langchain]
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from mcal.integrations.langchain import MCALChatMemory
|
|
11
|
+
|
|
12
|
+
memory = MCALChatMemory(llm_provider="anthropic")
|
|
13
|
+
chain = ConversationChain(llm=llm, memory=memory)
|
|
14
|
+
|
|
15
|
+
Status: Coming Soon
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any, Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
# Check for LangChain availability
|
|
21
|
+
try:
|
|
22
|
+
from langchain_core.memory import BaseMemory
|
|
23
|
+
from langchain_core.messages import BaseMessage
|
|
24
|
+
LANGCHAIN_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
LANGCHAIN_AVAILABLE = False
|
|
27
|
+
BaseMemory = object # Placeholder for type hints
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _check_langchain():
|
|
31
|
+
"""Raise helpful error if LangChain not installed."""
|
|
32
|
+
if not LANGCHAIN_AVAILABLE:
|
|
33
|
+
raise ImportError(
|
|
34
|
+
"LangChain integration requires langchain-core package.\n"
|
|
35
|
+
"Install with: pip install mcal[langchain]"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MCALChatMemory(BaseMemory if LANGCHAIN_AVAILABLE else object):
|
|
40
|
+
"""
|
|
41
|
+
MCAL Memory for LangChain conversation chains.
|
|
42
|
+
|
|
43
|
+
Provides goal-aware memory that preserves reasoning context
|
|
44
|
+
across LangChain conversations.
|
|
45
|
+
|
|
46
|
+
Status: Coming Soon - Basic structure implemented.
|
|
47
|
+
|
|
48
|
+
Usage:
|
|
49
|
+
from mcal.integrations.langchain import MCALChatMemory
|
|
50
|
+
from langchain.chains import ConversationChain
|
|
51
|
+
|
|
52
|
+
memory = MCALChatMemory(llm_provider="anthropic")
|
|
53
|
+
|
|
54
|
+
chain = ConversationChain(
|
|
55
|
+
llm=llm,
|
|
56
|
+
memory=memory,
|
|
57
|
+
)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# LangChain memory interface
|
|
61
|
+
memory_key: str = "history"
|
|
62
|
+
return_messages: bool = True
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
llm_provider: str = "anthropic",
|
|
67
|
+
embedding_provider: str = "openai",
|
|
68
|
+
storage_path: Optional[str] = None,
|
|
69
|
+
user_id: str = "default",
|
|
70
|
+
**mcal_kwargs
|
|
71
|
+
):
|
|
72
|
+
_check_langchain()
|
|
73
|
+
|
|
74
|
+
if LANGCHAIN_AVAILABLE:
|
|
75
|
+
super().__init__()
|
|
76
|
+
|
|
77
|
+
self.user_id = user_id
|
|
78
|
+
|
|
79
|
+
# Import MCAL here to avoid circular imports
|
|
80
|
+
from mcal import MCAL
|
|
81
|
+
|
|
82
|
+
self._mcal = MCAL(
|
|
83
|
+
llm_provider=llm_provider,
|
|
84
|
+
embedding_provider=embedding_provider,
|
|
85
|
+
storage_path=storage_path,
|
|
86
|
+
**mcal_kwargs
|
|
87
|
+
)
|
|
88
|
+
self._messages: List[Any] = []
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def memory_variables(self) -> List[str]:
|
|
92
|
+
"""Memory variables provided to prompts."""
|
|
93
|
+
return [self.memory_key]
|
|
94
|
+
|
|
95
|
+
def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
|
96
|
+
"""Load memory variables for chain."""
|
|
97
|
+
# TODO: Implement LangChain-specific memory loading
|
|
98
|
+
if self.return_messages:
|
|
99
|
+
return {self.memory_key: self._messages}
|
|
100
|
+
return {self.memory_key: ""}
|
|
101
|
+
|
|
102
|
+
def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
|
|
103
|
+
"""Save context from chain run."""
|
|
104
|
+
# TODO: Implement LangChain-specific context saving
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
def clear(self) -> None:
|
|
108
|
+
"""Clear memory."""
|
|
109
|
+
self._messages = []
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
__all__ = ["MCALChatMemory", "LANGCHAIN_AVAILABLE"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCAL LangGraph Integration - Backward Compatibility Shim
|
|
3
|
+
|
|
4
|
+
DEPRECATED: This module is deprecated. Use mcal-langgraph package instead.
|
|
5
|
+
|
|
6
|
+
Old way (deprecated):
|
|
7
|
+
from mcal.integrations.langgraph import MCALStore
|
|
8
|
+
|
|
9
|
+
New way (recommended):
|
|
10
|
+
pip install mcal-langgraph
|
|
11
|
+
from mcal_langgraph import MCALStore
|
|
12
|
+
|
|
13
|
+
This module re-exports from mcal_langgraph for backward compatibility.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import warnings
|
|
19
|
+
|
|
20
|
+
# Emit deprecation warning on import
|
|
21
|
+
warnings.warn(
|
|
22
|
+
"mcal.integrations.langgraph is deprecated and will be removed in v1.0. "
|
|
23
|
+
"Install mcal-langgraph and use 'from mcal_langgraph import MCALStore' instead.",
|
|
24
|
+
DeprecationWarning,
|
|
25
|
+
stacklevel=2,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Try to import from new package first
|
|
29
|
+
try:
|
|
30
|
+
from mcal_langgraph import (
|
|
31
|
+
MCALStore,
|
|
32
|
+
MCALMemory,
|
|
33
|
+
MCALMemoryConfig,
|
|
34
|
+
MCALCheckpointer,
|
|
35
|
+
LANGGRAPH_AVAILABLE,
|
|
36
|
+
)
|
|
37
|
+
except ImportError:
|
|
38
|
+
# New package not installed - raise helpful error
|
|
39
|
+
raise ImportError(
|
|
40
|
+
"mcal-langgraph package is required for LangGraph integration.\n"
|
|
41
|
+
"Install with: pip install mcal-langgraph"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"MCALStore",
|
|
46
|
+
"MCALMemory",
|
|
47
|
+
"MCALMemoryConfig",
|
|
48
|
+
"MCALCheckpointer",
|
|
49
|
+
"LANGGRAPH_AVAILABLE",
|
|
50
|
+
]
|