flock-core 0.4.520__py3-none-any.whl → 0.5.0b2__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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (103) hide show
  1. flock/cli/manage_agents.py +3 -3
  2. flock/components/__init__.py +28 -0
  3. flock/components/evaluation/__init__.py +9 -0
  4. flock/components/evaluation/declarative_evaluation_component.py +198 -0
  5. flock/components/routing/__init__.py +15 -0
  6. flock/{routers/conditional/conditional_router.py → components/routing/conditional_routing_component.py} +60 -49
  7. flock/components/routing/default_routing_component.py +103 -0
  8. flock/components/routing/llm_routing_component.py +208 -0
  9. flock/components/utility/__init__.py +15 -0
  10. flock/{modules/enterprise_memory/enterprise_memory_module.py → components/utility/memory_utility_component.py} +195 -173
  11. flock/{modules/performance/metrics_module.py → components/utility/metrics_utility_component.py} +101 -86
  12. flock/{modules/output/output_module.py → components/utility/output_utility_component.py} +49 -49
  13. flock/core/__init__.py +2 -8
  14. flock/core/agent/__init__.py +16 -0
  15. flock/core/agent/flock_agent_components.py +104 -0
  16. flock/core/agent/flock_agent_execution.py +101 -0
  17. flock/core/agent/flock_agent_integration.py +147 -0
  18. flock/core/agent/flock_agent_lifecycle.py +177 -0
  19. flock/core/agent/flock_agent_serialization.py +378 -0
  20. flock/core/component/__init__.py +15 -0
  21. flock/core/{flock_module.py → component/agent_component_base.py} +136 -35
  22. flock/core/component/evaluation_component_base.py +56 -0
  23. flock/core/component/routing_component_base.py +75 -0
  24. flock/core/component/utility_component_base.py +69 -0
  25. flock/core/config/flock_agent_config.py +49 -2
  26. flock/core/evaluation/utils.py +1 -1
  27. flock/core/execution/evaluation_executor.py +1 -1
  28. flock/core/flock.py +137 -483
  29. flock/core/flock_agent.py +151 -1018
  30. flock/core/flock_factory.py +94 -73
  31. flock/core/{flock_registry.py → flock_registry.py.backup} +3 -17
  32. flock/core/logging/logging.py +1 -0
  33. flock/core/mcp/flock_mcp_server.py +42 -37
  34. flock/core/mixin/dspy_integration.py +5 -5
  35. flock/core/orchestration/__init__.py +18 -0
  36. flock/core/orchestration/flock_batch_processor.py +94 -0
  37. flock/core/orchestration/flock_evaluator.py +113 -0
  38. flock/core/orchestration/flock_execution.py +288 -0
  39. flock/core/orchestration/flock_initialization.py +125 -0
  40. flock/core/orchestration/flock_server_manager.py +65 -0
  41. flock/core/orchestration/flock_web_server.py +117 -0
  42. flock/core/registry/__init__.py +39 -0
  43. flock/core/registry/agent_registry.py +69 -0
  44. flock/core/registry/callable_registry.py +139 -0
  45. flock/core/registry/component_discovery.py +142 -0
  46. flock/core/registry/component_registry.py +64 -0
  47. flock/core/registry/config_mapping.py +64 -0
  48. flock/core/registry/decorators.py +137 -0
  49. flock/core/registry/registry_hub.py +202 -0
  50. flock/core/registry/server_registry.py +57 -0
  51. flock/core/registry/type_registry.py +86 -0
  52. flock/core/serialization/flock_serializer.py +33 -30
  53. flock/core/serialization/serialization_utils.py +28 -25
  54. flock/core/util/input_resolver.py +29 -2
  55. flock/platform/docker_tools.py +3 -3
  56. flock/tools/markdown_tools.py +1 -2
  57. flock/tools/text_tools.py +1 -2
  58. flock/webapp/app/main.py +9 -5
  59. flock/workflow/activities.py +59 -84
  60. flock/workflow/activities_unified.py +230 -0
  61. flock/workflow/agent_execution_activity.py +6 -6
  62. flock/workflow/flock_workflow.py +1 -1
  63. {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/METADATA +2 -2
  64. {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/RECORD +67 -68
  65. flock/core/flock_evaluator.py +0 -60
  66. flock/core/flock_router.py +0 -83
  67. flock/evaluators/__init__.py +0 -1
  68. flock/evaluators/declarative/__init__.py +0 -1
  69. flock/evaluators/declarative/declarative_evaluator.py +0 -194
  70. flock/evaluators/memory/memory_evaluator.py +0 -90
  71. flock/evaluators/test/test_case_evaluator.py +0 -38
  72. flock/evaluators/zep/zep_evaluator.py +0 -59
  73. flock/modules/__init__.py +0 -1
  74. flock/modules/assertion/__init__.py +0 -1
  75. flock/modules/assertion/assertion_module.py +0 -286
  76. flock/modules/callback/__init__.py +0 -1
  77. flock/modules/callback/callback_module.py +0 -91
  78. flock/modules/enterprise_memory/README.md +0 -99
  79. flock/modules/mem0/__init__.py +0 -1
  80. flock/modules/mem0/mem0_module.py +0 -126
  81. flock/modules/mem0_async/__init__.py +0 -1
  82. flock/modules/mem0_async/async_mem0_module.py +0 -126
  83. flock/modules/memory/__init__.py +0 -1
  84. flock/modules/memory/memory_module.py +0 -429
  85. flock/modules/memory/memory_parser.py +0 -125
  86. flock/modules/memory/memory_storage.py +0 -736
  87. flock/modules/output/__init__.py +0 -1
  88. flock/modules/performance/__init__.py +0 -1
  89. flock/modules/zep/__init__.py +0 -1
  90. flock/modules/zep/zep_module.py +0 -192
  91. flock/routers/__init__.py +0 -1
  92. flock/routers/agent/__init__.py +0 -1
  93. flock/routers/agent/agent_router.py +0 -236
  94. flock/routers/agent/handoff_agent.py +0 -58
  95. flock/routers/default/__init__.py +0 -1
  96. flock/routers/default/default_router.py +0 -80
  97. flock/routers/feedback/feedback_router.py +0 -114
  98. flock/routers/list_generator/list_generator_router.py +0 -166
  99. flock/routers/llm/__init__.py +0 -1
  100. flock/routers/llm/llm_router.py +0 -365
  101. {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/WHEEL +0 -0
  102. {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/entry_points.txt +0 -0
  103. {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/licenses/LICENSE +0 -0
@@ -1,286 +0,0 @@
1
- # src/flock/modules/asserts/assertion_module.py (New File)
2
-
3
- import json
4
- from collections.abc import Callable
5
- from typing import Any, Literal
6
-
7
- import dspy # For potential LLM-based rule checking
8
- from pydantic import BaseModel, Field, PrivateAttr, ValidationError
9
-
10
- from flock.core.context.context import FlockContext
11
- from flock.core.flock_agent import FlockAgent
12
- from flock.core.flock_module import FlockModule, FlockModuleConfig
13
-
14
- # Need registry access if rules are callables defined elsewhere
15
- from flock.core.flock_registry import flock_component, get_registry
16
- from flock.core.logging.logging import get_logger
17
-
18
- logger = get_logger("module.assertion")
19
-
20
- # --- Rule Definition ---
21
- # Rules can be defined in several ways:
22
- # 1. Python lambda/function: (result: Dict, inputs: Dict, context: FlockContext) -> bool | Tuple[bool, str]
23
- # 2. String referencing a registered callable: "my_validation_function"
24
- # 3. Natural language rule string: "The summary must contain the keyword 'Flock'." (requires LLM judge)
25
- # 4. Pydantic Model: The output must conform to this Pydantic model.
26
-
27
- RuleType = (
28
- Callable[[dict, dict, FlockContext | None], bool | tuple[bool, str]]
29
- | str
30
- | type[BaseModel]
31
- )
32
-
33
-
34
- class Rule(BaseModel):
35
- """Container for a single assertion rule."""
36
-
37
- condition: RuleType = Field(
38
- ...,
39
- description="""
40
- # --- Rule Definition ---
41
- # Rules can be defined in several ways:
42
- # 1. Python lambda/function: (result: Dict, inputs: Dict, context: FlockContext) -> bool | Tuple[bool, str]
43
- # 2. String referencing a registered callable: "my_validation_function"
44
- # 3. Natural language rule string: "The summary must contain the keyword 'Flock'." (requires LLM judge)
45
- # 4. Pydantic Model: The output must conform to this Pydantic model.
46
- """,
47
- )
48
- fail_message: str # Message to provide as feedback on failure
49
- name: str | None = None # Optional name for clarity
50
-
51
- def __post_init__(self):
52
- # Basic validation of fail_message
53
- if not isinstance(self.fail_message, str) or not self.fail_message:
54
- raise ValueError("Rule fail_message must be a non-empty string.")
55
-
56
-
57
- class AssertionModuleConfig(FlockModuleConfig):
58
- """--- Rule Definition ---
59
- Rules can be defined in several ways:
60
- 1. Python lambda/function: (result: Dict, inputs: Dict, context: FlockContext) -> bool | Tuple[bool, str]
61
- 2. String referencing a registered callable: "my_validation_function"
62
- 3. Natural language rule string: "The summary must contain the keyword 'Flock'." (requires LLM judge)
63
- 4. Pydantic Model: The output must conform to this Pydantic model.
64
- """
65
-
66
- rules: list[Rule] = Field(
67
- default_factory=list,
68
- description="List of rules to check against the agent's output.",
69
- )
70
- # Optional LLM for evaluating natural language rules
71
- judge_lm_model: str | None = Field(
72
- None, description="LLM model to use for judging natural language rules."
73
- )
74
- # How to handle failure
75
- on_failure: Literal["add_feedback", "raise_error", "log_warning"] = Field(
76
- default="add_feedback",
77
- description="Action on rule failure: 'add_feedback' to context, 'raise_error', 'log_warning'.",
78
- )
79
- feedback_context_key: str = Field(
80
- default="flock.assertion_feedback",
81
- description="Context key to store failure messages for retry loops.",
82
- )
83
- clear_feedback_on_success: bool = Field(
84
- default=True,
85
- description="Clear the feedback key from context if all assertions pass.",
86
- )
87
-
88
-
89
- @flock_component(config_class=AssertionModuleConfig)
90
- class AssertionCheckerModule(FlockModule):
91
- """Checks the output of an agent against a set of defined rules.
92
-
93
- Can trigger different actions on failure, including adding feedback
94
- to the context to enable self-correction loops via routing.
95
- """
96
-
97
- name: str = "assertion_checker"
98
- config: AssertionModuleConfig = Field(default_factory=AssertionModuleConfig)
99
- _judge_lm: dspy.LM | None = PrivateAttr(None) # Initialize lazily
100
-
101
- def _get_judge_lm(self) -> dspy.LM | None:
102
- """Initializes the judge LM if needed."""
103
- if self.config.judge_lm_model and self._judge_lm is None:
104
- try:
105
- self._judge_lm = dspy.LM(self.config.judge_lm_model)
106
- except Exception as e:
107
- logger.error(
108
- f"Failed to initialize judge LM '{self.config.judge_lm_model}': {e}"
109
- )
110
- # Proceed without judge LM for other rule types
111
- return self._judge_lm
112
-
113
- async def on_post_evaluate(
114
- self,
115
- agent: FlockAgent,
116
- inputs: dict[str, Any],
117
- context: FlockContext | None = None,
118
- result: dict[str, Any] | None = None,
119
- ) -> dict[str, Any]:
120
- """Checks rules after the main evaluator runs."""
121
- if not self.config.rules:
122
- return result # No rules to check
123
-
124
- logger.debug(f"Running assertion checks for agent '{agent.name}'...")
125
- all_passed = True
126
- failed_messages = []
127
- registry = get_registry() # Needed for callable lookup
128
-
129
- for i, rule in enumerate(self.config.rules):
130
- rule_name = rule.name or f"Rule_{i + 1}"
131
- passed = False
132
- eval_result = None
133
- feedback_msg = rule.fail_message
134
-
135
- try:
136
- condition = rule.condition
137
- if callable(condition):
138
- # Rule is a Python function/lambda
139
- logger.debug(f"Checking callable rule: {rule_name}")
140
- eval_result = condition(result, inputs, context)
141
- elif isinstance(condition, str) and registry.contains(
142
- condition
143
- ):
144
- # Rule is a string referencing a registered callable
145
- logger.debug(
146
- f"Checking registered callable rule: '{condition}'"
147
- )
148
- rule_func = registry.get_callable(condition)
149
- eval_result = rule_func(result, inputs, context)
150
- elif isinstance(condition, str):
151
- # Rule is a natural language string (requires judge LLM)
152
- logger.debug(
153
- f"Checking natural language rule: '{condition}'"
154
- )
155
- judge_lm = self._get_judge_lm()
156
- if judge_lm:
157
- # Define a simple judge signature dynamically or use a predefined one
158
- class JudgeSignature(dspy.Signature):
159
- """Evaluate if the output meets the rule based on input and output."""
160
-
161
- program_input: str = dspy.InputField(
162
- desc="Input provided to the agent."
163
- )
164
- program_output: str = dspy.InputField(
165
- desc="Output generated by the agent."
166
- )
167
- rule_to_check: str = dspy.InputField(
168
- desc="The rule to verify."
169
- )
170
- is_met: bool = dspy.OutputField(
171
- desc="True if the rule is met, False otherwise."
172
- )
173
- reasoning: str = dspy.OutputField(
174
- desc="Brief reasoning for the decision."
175
- )
176
-
177
- judge_predictor = dspy.Predict(
178
- JudgeSignature, llm=judge_lm
179
- )
180
- # Convert complex dicts/lists to strings for the judge prompt
181
- input_str = json.dumps(inputs, default=str, indent=2)
182
- result_str = json.dumps(result, default=str, indent=2)
183
- judge_pred = judge_predictor(
184
- program_input=input_str,
185
- program_output=result_str,
186
- rule_to_check=condition,
187
- )
188
- passed = judge_pred.is_met
189
- feedback_msg = f"{rule.fail_message} (Reason: {judge_pred.reasoning})"
190
- logger.debug(
191
- f"LLM Judge result for rule '{condition}': {passed} ({judge_pred.reasoning})"
192
- )
193
- else:
194
- logger.warning(
195
- f"Cannot evaluate natural language rule '{condition}' - no judge_lm_model configured."
196
- )
197
- passed = True # Default to pass if no judge available? Or fail? Let's pass.
198
-
199
- elif isinstance(condition, type) and issubclass(
200
- condition, BaseModel
201
- ):
202
- # Rule is a Pydantic model for validation
203
- logger.debug(
204
- f"Checking Pydantic validation rule: {condition.__name__}"
205
- )
206
- try:
207
- # Assumes the *entire* result dict should match the model
208
- # More specific logic might be needed (e.g., validate only a specific key)
209
- condition.model_validate(result)
210
- passed = True
211
- except ValidationError as e:
212
- passed = False
213
- feedback_msg = (
214
- f"{rule.fail_message} (Validation Error: {e})"
215
- )
216
- else:
217
- logger.warning(
218
- f"Unsupported rule type for rule '{rule_name}': {type(condition)}"
219
- )
220
- continue # Skip rule
221
-
222
- # Process result if it was a callable returning bool or (bool, msg)
223
- if eval_result is not None:
224
- if (
225
- isinstance(eval_result, tuple)
226
- and len(eval_result) == 2
227
- and isinstance(eval_result[0], bool)
228
- ):
229
- passed, custom_msg = eval_result
230
- if not passed and custom_msg:
231
- feedback_msg = (
232
- custom_msg # Use custom message on failure
233
- )
234
- elif isinstance(eval_result, bool):
235
- passed = eval_result
236
- else:
237
- logger.warning(
238
- f"Rule callable '{rule_name}' returned unexpected type: {type(eval_result)}. Rule skipped."
239
- )
240
- continue
241
-
242
- # Handle failure
243
- if not passed:
244
- all_passed = False
245
- failed_messages.append(feedback_msg)
246
- logger.warning(
247
- f"Assertion Failed for agent '{agent.name}': {feedback_msg}"
248
- )
249
- # Optionally break early? For now, check all rules.
250
-
251
- except Exception as e:
252
- logger.error(
253
- f"Error executing rule '{rule_name}' for agent '{agent.name}': {e}",
254
- exc_info=True,
255
- )
256
- all_passed = False
257
- failed_messages.append(
258
- f"Error checking rule '{rule_name}': {e}"
259
- )
260
- # Treat error during check as failure
261
-
262
- # --- Take action based on results ---
263
- if not all_passed:
264
- logger.warning(f"Agent '{agent.name}' failed assertion checks.")
265
- if self.config.on_failure == "add_feedback" and context:
266
- context.set_variable(
267
- self.config.feedback_context_key, "\n".join(failed_messages)
268
- )
269
- logger.debug(
270
- f"Added assertion feedback to context key '{self.config.feedback_context_key}'"
271
- )
272
- elif self.config.on_failure == "raise_error":
273
- # Maybe wrap in a specific FlockAssertionError
274
- raise AssertionError(
275
- f"Agent '{agent.name}' failed assertions: {'; '.join(failed_messages)}"
276
- )
277
- # else "log_warning" is default behavior
278
- elif context and self.config.clear_feedback_on_success:
279
- # Clear feedback key if all rules passed and key exists
280
- if self.config.feedback_context_key in context.state:
281
- del context.state[self.config.feedback_context_key]
282
- logger.debug(
283
- f"Cleared assertion feedback key '{self.config.feedback_context_key}' on success."
284
- )
285
-
286
- return result # Return the original result unmodified
@@ -1 +0,0 @@
1
- # Package for modules
@@ -1,91 +0,0 @@
1
- """Callback module for handling agent lifecycle hooks."""
2
-
3
- from collections.abc import Awaitable, Callable
4
- from typing import Any
5
-
6
- from pydantic import Field
7
-
8
- from flock.core import FlockModule, FlockModuleConfig
9
- from flock.core.context.context import FlockContext
10
- from flock.core.flock_registry import flock_component
11
-
12
-
13
- class CallbackModuleConfig(FlockModuleConfig):
14
- """Configuration for callback module."""
15
-
16
- initialize_callback: (
17
- Callable[[Any, dict[str, Any]], Awaitable[None]] | None
18
- ) = Field(
19
- default=None,
20
- description="Optional callback function for initialization",
21
- )
22
- evaluate_callback: (
23
- Callable[[Any, dict[str, Any]], Awaitable[dict[str, Any]]] | None
24
- ) = Field(
25
- default=None, description="Optional callback function for evaluate"
26
- )
27
- terminate_callback: (
28
- Callable[[Any, dict[str, Any], dict[str, Any]], Awaitable[None]] | None
29
- ) = Field(
30
- default=None, description="Optional callback function for termination"
31
- )
32
- on_error_callback: (
33
- Callable[[Any, Exception, dict[str, Any]], Awaitable[None]] | None
34
- ) = Field(
35
- default=None,
36
- description="Optional callback function for error handling",
37
- )
38
-
39
-
40
- @flock_component(config_class=CallbackModuleConfig)
41
- class CallbackModule(FlockModule):
42
- """Module that provides callback functionality for agent lifecycle events."""
43
-
44
- name: str = "callbacks"
45
- config: CallbackModuleConfig = Field(
46
- default_factory=CallbackModuleConfig,
47
- description="Callback module configuration",
48
- )
49
-
50
- async def pre_initialize(
51
- self,
52
- agent: Any,
53
- inputs: dict[str, Any],
54
- context: FlockContext | None = None,
55
- ) -> None:
56
- """Run initialize callback if configured."""
57
- if self.config.initialize_callback:
58
- await self.config.initialize_callback(agent, inputs)
59
-
60
- async def on_pre_evaluate(
61
- self,
62
- agent: Any,
63
- inputs: dict[str, Any],
64
- context: FlockContext | None = None,
65
- ) -> dict[str, Any]:
66
- """Run evaluate callback if configured."""
67
- if self.config.evaluate_callback:
68
- return await self.config.evaluate_callback(agent, inputs)
69
- return inputs
70
-
71
- async def pre_terminate(
72
- self,
73
- agent: Any,
74
- inputs: dict[str, Any],
75
- context: FlockContext | None = None,
76
- result: dict[str, Any] | None = None,
77
- ) -> None:
78
- """Run terminate callback if configured."""
79
- if self.config.terminate_callback:
80
- await self.config.terminate_callback(agent, inputs, result)
81
-
82
- async def on_error(
83
- self,
84
- agent: Any,
85
- error: Exception,
86
- inputs: dict[str, Any],
87
- context: FlockContext | None = None,
88
- ) -> None:
89
- """Run error callback if configured."""
90
- if self.config.on_error_callback:
91
- await self.config.on_error_callback(agent, error, inputs)
@@ -1,99 +0,0 @@
1
- # Enterprise Memory Module
2
-
3
- The **EnterpriseMemoryModule** brings durable, scalable memory to Flock agents by
4
- combining a true vector store (Chroma) with a property-graph database
5
- (Neo4j / Memgraph). It is a drop‐in replacement for the default
6
- `memory` module when you need:
7
-
8
- * millions of memory chunks without exhausting RAM
9
- * concurrent writers (many agents / processes / machines)
10
- * rich concept-graph queries and visualisation
11
-
12
- ---
13
- ## How it works
14
-
15
- | Concern | Technology |
16
- |--------------------- |------------|
17
- | Vector similarity | **Pinecone**, **Chroma**, **Azure Cognitive Search** |
18
- | Concept graph | **Cypher** database (Neo4j / Memgraph) |
19
- | Embeddings | `sentence-transformers` (`all-MiniLM-L6-v2`) |
20
- | Concept extraction | Agent's LLM via DSPy signature |
21
-
22
- * Each memory chunk is embedded and added to the Chroma collection.
23
- * Concepts are extracted; duplicates are eliminated via case-insensitive
24
- and fuzzy matching (≥ 0.85 similarity).
25
- * Memory nodes and `(:Memory)-[:MENTIONS]->(:Concept)` edges are merged
26
- into the graph DB in batched transactions.
27
- * Optional: export a PNG of the concept graph after every update.
28
-
29
- ---
30
- ## Configuration options (`EnterpriseMemoryModuleConfig`)
31
-
32
- ```yaml
33
- chroma_path: ./vector_store # disk path if running embedded
34
- chroma_host: null # host of remote Chroma server (optional)
35
- chroma_port: 8000
36
- chroma_collection: flock_memories # collection name
37
-
38
- # or Pinecone
39
- vector_backend: pinecone
40
- pinecone_api_key: <YOUR_KEY>
41
- pinecone_env: gcp-starter
42
- pinecone_index: flock-memories
43
-
44
- # or Azure Cognitive Search
45
- vector_backend: azure
46
- azure_search_endpoint: https://<service>.search.windows.net
47
- azure_search_key: <KEY>
48
- azure_search_index_name: flock-memories
49
-
50
- cypher_uri: bolt://localhost:7687
51
- cypher_username: neo4j
52
- cypher_password: password
53
-
54
- similarity_threshold: 0.5 # for retrieval
55
- max_results: 10
56
- number_of_concepts_to_extract: 3
57
- save_interval: 10 # batch size before commit
58
-
59
- export_graph_image: false # set true to emit PNGs
60
- graph_image_dir: ./concept_graphs # where to store images
61
- ```
62
-
63
- ---
64
- ## Dependencies
65
-
66
- Add the following to your project (examples with pip):
67
-
68
- ```bash
69
- pip install chromadb>=0.4.20
70
- pip install neo4j>=5.14.0
71
- pip install sentence-transformers>=2.7.0
72
- pip install matplotlib networkx # only needed when export_graph_image = true
73
- pip install pinecone-client # if using Pinecone
74
- pip install azure-search-documents # if using Azure Search
75
- ```
76
-
77
- You also need a running Neo4j **or** Memgraph instance. The module uses
78
- the Bolt protocol and Cypher `MERGE`, which works on both.
79
-
80
- ---
81
- ## Usage
82
-
83
- ```python
84
- from flock.modules.enterprise_memory.enterprise_memory_module import (
85
- EnterpriseMemoryModule, EnterpriseMemoryModuleConfig,
86
- )
87
-
88
- agent.add_module(
89
- EnterpriseMemoryModule(
90
- name="enterprise_memory",
91
- config=EnterpriseMemoryModuleConfig(
92
- cypher_password=os.environ["NEO4J_PASSWORD"],
93
- export_graph_image=True,
94
- ),
95
- )
96
- )
97
- ```
98
-
99
- The rest of the agent code stays unchanged.
@@ -1 +0,0 @@
1
- # Package for modules
@@ -1,126 +0,0 @@
1
- from typing import Any
2
-
3
- # from mem0.client.main import MemoryClient
4
- # from mem0.memory.main import Memory
5
- from mem0 import Memory, MemoryClient
6
- from pydantic import Field
7
-
8
- from flock.core.context.context import FlockContext
9
- from flock.core.flock_agent import FlockAgent
10
- from flock.core.flock_module import FlockModule, FlockModuleConfig
11
- from flock.core.flock_registry import flock_component
12
- from flock.core.logging.logging import get_logger
13
-
14
- logger = get_logger("module.mem0")
15
-
16
-
17
- config = {
18
- "vector_store": {
19
- "provider": "chroma",
20
- "config": {
21
- "collection_name": "flock_memory",
22
- "path": ".flock/memory",
23
- }
24
- }
25
- }
26
-
27
-
28
- class Mem0ModuleConfig(FlockModuleConfig):
29
- top_k: int = Field(default=10, description="Number of memories to retrieve")
30
- user_id: str = Field(default="flock", description="User ID the memories will be associated with")
31
- agent_id: str = Field(default="flock", description="Agent ID the memories will be associated with")
32
- memory_input_key: str | None = Field(default=None, description="Input key to use for memory, if none the description of the agent will be used")
33
- api_key: str | None = Field(default=None, description="API key for mem0 Platform")
34
- config: dict[str, Any] = Field(default=config, description="Configuration for mem0")
35
-
36
-
37
- @flock_component(config_class=Mem0ModuleConfig)
38
- class Mem0Module(FlockModule):
39
-
40
- name: str = "mem0"
41
- config: Mem0ModuleConfig = Mem0ModuleConfig()
42
-
43
-
44
- def __init__(self, name, config: Mem0ModuleConfig) -> None:
45
- global memory
46
- """Initialize Mem0 module."""
47
- super().__init__(name=name, config=config)
48
- logger.debug("Initializing Mem0 module")
49
-
50
-
51
-
52
-
53
- def dict_to_str_repr(self,d: dict) -> str:
54
- return repr(d)
55
-
56
-
57
- async def on_post_evaluate(
58
- self,
59
- agent: FlockAgent,
60
- inputs: dict[str, Any],
61
- context: FlockContext | None = None,
62
- result: dict[str, Any] | None = None,
63
- ) -> dict[str, Any]:
64
- if self.config.api_key:
65
- memory = MemoryClient(api_key=self.config.api_key)
66
- else:
67
- memory = Memory.from_config(config_dict=self.config.config)
68
-
69
- agent_id = self.config.agent_id if self.config.agent_id else agent.name
70
-
71
- # get the result without the inputs
72
- filtered_result = {k: v for k, v in result.items() if k not in inputs}
73
- # get the inputs without memory
74
- filtered_inputs = {k: v for k, v in inputs.items() if k not in [self.config.memory_input_key]}
75
-
76
- # add memories about the user inputs
77
- added_user_memory = memory.add(self.dict_to_str_repr(filtered_inputs), user_id=self.config.user_id)
78
- logger.info(f"Added caller memory: {added_user_memory}")
79
-
80
- # add memories about the agent result
81
- added_agent_memory = memory.add(self.dict_to_str_repr(filtered_result), agent_id=agent_id)
82
- logger.info(f"Added agent memory: {added_agent_memory}")
83
-
84
-
85
- return result
86
-
87
- async def on_pre_evaluate(
88
- self,
89
- agent: FlockAgent,
90
- inputs: dict[str, Any],
91
- context: FlockContext | None = None,
92
- ) -> dict[str, Any]:
93
- if self.config.api_key:
94
- memory = MemoryClient(api_key=self.config.api_key)
95
- else:
96
- memory = Memory.from_config(config_dict=self.config.config)
97
-
98
- message = self.dict_to_str_repr(inputs)
99
- agent_id = self.config.agent_id if self.config.agent_id else agent.name
100
-
101
- relevant_agent_memories = memory.search(query=message, agent_id=agent_id, limit=self.config.top_k)
102
- logger.info(f"Relevant agent memories: {relevant_agent_memories}")
103
-
104
- relevant_user_memories = memory.search(query=message, user_id=self.config.user_id, limit=self.config.top_k)
105
- logger.info(f"Relevant user memories: {relevant_user_memories}")
106
-
107
- if relevant_agent_memories or relevant_user_memories:
108
- memories_str = ''
109
- if "results" in relevant_agent_memories:
110
- memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_agent_memories["results"])
111
- else:
112
- memories_str = "\n".join(f"- {entry}" for entry in relevant_agent_memories)
113
-
114
- if "results" in relevant_user_memories:
115
- memories_str = memories_str + "\n" + "\n".join(f"- {entry['memory']}" for entry in relevant_user_memories["results"])
116
- else:
117
- memories_str = memories_str + "\n" + "\n".join(f"- {entry}" for entry in relevant_user_memories)
118
-
119
- if memories_str:
120
- if self.config.memory_input_key:
121
- inputs[self.config.memory_input_key] = memories_str
122
- else:
123
- agent.description = agent.description + "\n\n Memories:" + memories_str
124
-
125
-
126
- return inputs
@@ -1 +0,0 @@
1
- # Package for modules