praval 0.7.6__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.
Files changed (45) hide show
  1. praval/__init__.py +119 -0
  2. praval/composition.py +225 -0
  3. praval/core/__init__.py +1 -0
  4. praval/core/agent.py +621 -0
  5. praval/core/exceptions.py +31 -0
  6. praval/core/reef.py +568 -0
  7. praval/core/registry.py +86 -0
  8. praval/core/secure_reef.py +470 -0
  9. praval/core/secure_spore.py +373 -0
  10. praval/core/storage.py +115 -0
  11. praval/core/tool_registry.py +437 -0
  12. praval/core/transport.py +548 -0
  13. praval/decorators.py +345 -0
  14. praval/memory/__init__.py +29 -0
  15. praval/memory/embedded_store.py +796 -0
  16. praval/memory/episodic_memory.py +326 -0
  17. praval/memory/long_term_memory.py +391 -0
  18. praval/memory/memory_manager.py +525 -0
  19. praval/memory/memory_types.py +115 -0
  20. praval/memory/semantic_memory.py +431 -0
  21. praval/memory/short_term_memory.py +295 -0
  22. praval/providers/__init__.py +1 -0
  23. praval/providers/anthropic.py +129 -0
  24. praval/providers/cohere.py +160 -0
  25. praval/providers/factory.py +45 -0
  26. praval/providers/openai.py +251 -0
  27. praval/storage/__init__.py +56 -0
  28. praval/storage/base_provider.py +399 -0
  29. praval/storage/data_manager.py +496 -0
  30. praval/storage/decorators.py +321 -0
  31. praval/storage/exceptions.py +79 -0
  32. praval/storage/memory_integration.py +422 -0
  33. praval/storage/providers/__init__.py +20 -0
  34. praval/storage/providers/filesystem.py +497 -0
  35. praval/storage/providers/postgresql.py +428 -0
  36. praval/storage/providers/qdrant_provider.py +549 -0
  37. praval/storage/providers/redis_provider.py +483 -0
  38. praval/storage/providers/s3_provider.py +518 -0
  39. praval/storage/storage_registry.py +504 -0
  40. praval/tools.py +344 -0
  41. praval-0.7.6.dist-info/METADATA +1063 -0
  42. praval-0.7.6.dist-info/RECORD +45 -0
  43. praval-0.7.6.dist-info/WHEEL +5 -0
  44. praval-0.7.6.dist-info/licenses/LICENSE +21 -0
  45. praval-0.7.6.dist-info/top_level.txt +1 -0
praval/__init__.py ADDED
@@ -0,0 +1,119 @@
1
+ """
2
+ Praval: A composable Python framework for LLM-based agents.
3
+
4
+ Inspired by coral ecosystems where simple organisms create complex structures
5
+ through collaboration, Praval enables simple agents to work together for
6
+ sophisticated behaviors.
7
+
8
+ Version 0.7.6 includes comprehensive tool system with @tool decorator,
9
+ PDF support for knowledge base, Unified Data Storage & Retrieval System with support for
10
+ PostgreSQL, Redis, S3, Qdrant, and filesystem storage providers, plus enhanced
11
+ Secure Spores Enterprise Edition and comprehensive memory system integration.
12
+ """
13
+
14
+ from .core.agent import Agent
15
+ from .core.registry import register_agent, get_registry
16
+ from .core.reef import get_reef, Spore, SporeType
17
+ from .decorators import chat, achat, broadcast, get_agent_info
18
+ from .composition import (
19
+ agent_pipeline, conditional_agent, throttled_agent,
20
+ AgentSession, start_agents
21
+ )
22
+
23
+ # Enhanced agent decorator with memory support (v0.3.0)
24
+ from .decorators import agent, chat, achat, broadcast, get_agent_info
25
+
26
+ # Tool system imports (v0.7.2+)
27
+ try:
28
+ from .tools import (
29
+ tool, get_tool_info, is_tool, discover_tools, list_tools,
30
+ register_tool_with_agent, unregister_tool_from_agent, ToolCollection
31
+ )
32
+ from .core.tool_registry import ToolRegistry, Tool, ToolMetadata, get_tool_registry, reset_tool_registry
33
+ TOOLS_AVAILABLE = True
34
+ except ImportError:
35
+ TOOLS_AVAILABLE = False
36
+ tool = None
37
+ get_tool_info = None
38
+ is_tool = None
39
+ discover_tools = None
40
+ list_tools = None
41
+ register_tool_with_agent = None
42
+ unregister_tool_from_agent = None
43
+ ToolCollection = None
44
+ ToolRegistry = None
45
+ Tool = None
46
+ ToolMetadata = None
47
+ get_tool_registry = None
48
+ reset_tool_registry = None
49
+
50
+ # Memory system imports (optional - graceful fallback if dependencies missing)
51
+ try:
52
+ from .memory import MemoryManager, MemoryType, MemoryEntry, MemoryQuery
53
+ MEMORY_AVAILABLE = True
54
+ except ImportError:
55
+ MEMORY_AVAILABLE = False
56
+ MemoryManager = None
57
+ MemoryType = None
58
+ MemoryEntry = None
59
+ MemoryQuery = None
60
+
61
+ # Storage system imports (optional - graceful fallback if dependencies missing)
62
+ try:
63
+ from .storage import (
64
+ BaseStorageProvider, StorageRegistry, DataManager,
65
+ storage_enabled, requires_storage,
66
+ get_storage_registry, get_data_manager,
67
+ PostgreSQLProvider, RedisProvider, S3Provider,
68
+ FileSystemProvider, QdrantProvider,
69
+ DataReference, StorageResult, StorageType
70
+ )
71
+ STORAGE_AVAILABLE = True
72
+ except ImportError:
73
+ STORAGE_AVAILABLE = False
74
+ BaseStorageProvider = None
75
+ StorageRegistry = None
76
+ DataManager = None
77
+ storage_enabled = None
78
+ requires_storage = None
79
+ get_storage_registry = None
80
+ get_data_manager = None
81
+ PostgreSQLProvider = None
82
+ RedisProvider = None
83
+ S3Provider = None
84
+ FileSystemProvider = None
85
+ QdrantProvider = None
86
+ DataReference = None
87
+ StorageResult = None
88
+ StorageType = None
89
+
90
+ __version__ = "0.7.6"
91
+ __all__ = [
92
+ # Core classes
93
+ "Agent", "register_agent", "get_registry", "get_reef", "Spore", "SporeType",
94
+
95
+ # Enhanced decorator (now with memory support)
96
+ "agent",
97
+
98
+ # Communication and composition
99
+ "chat", "achat", "broadcast", "get_agent_info",
100
+ "agent_pipeline", "conditional_agent", "throttled_agent",
101
+ "AgentSession", "start_agents",
102
+
103
+ # Tool system (if available)
104
+ "tool", "get_tool_info", "is_tool", "discover_tools", "list_tools",
105
+ "register_tool_with_agent", "unregister_tool_from_agent", "ToolCollection",
106
+ "ToolRegistry", "Tool", "ToolMetadata", "get_tool_registry", "reset_tool_registry",
107
+ "TOOLS_AVAILABLE",
108
+
109
+ # Memory system (if available)
110
+ "MemoryManager", "MemoryType", "MemoryEntry", "MemoryQuery", "MEMORY_AVAILABLE",
111
+
112
+ # Storage system (if available)
113
+ "BaseStorageProvider", "StorageRegistry", "DataManager",
114
+ "storage_enabled", "requires_storage",
115
+ "get_storage_registry", "get_data_manager",
116
+ "PostgreSQLProvider", "RedisProvider", "S3Provider",
117
+ "FileSystemProvider", "QdrantProvider",
118
+ "DataReference", "StorageResult", "StorageType", "STORAGE_AVAILABLE"
119
+ ]
praval/composition.py ADDED
@@ -0,0 +1,225 @@
1
+ """
2
+ Composition utilities for decorator-based agents.
3
+
4
+ This module provides utilities for composing and orchestrating agents
5
+ decorated with the @agent decorator.
6
+ """
7
+
8
+ from typing import Callable, List, Dict, Any, Optional
9
+ import time
10
+ import threading
11
+
12
+ from .core.reef import get_reef
13
+ from .decorators import get_agent_info
14
+
15
+
16
+ def agent_pipeline(*agents: Callable, channel: str = "pipeline") -> Callable:
17
+ """
18
+ Compose agents into a pipeline that processes data sequentially.
19
+
20
+ Args:
21
+ *agents: Functions decorated with @agent
22
+ channel: Channel name for pipeline communication
23
+
24
+ Returns:
25
+ Function that triggers the pipeline with initial data
26
+
27
+ Example:
28
+ pipeline = agent_pipeline(explorer, analyzer, reporter)
29
+ pipeline({"task": "analyze sentiment"})
30
+ """
31
+ # Validate all functions are agents
32
+ for agent_func in agents:
33
+ if not hasattr(agent_func, '_praval_agent'):
34
+ raise ValueError(f"Function {agent_func.__name__} is not decorated with @agent")
35
+
36
+ def pipeline_trigger(initial_data: Dict[str, Any]) -> str:
37
+ """Trigger the pipeline with initial data."""
38
+ # Subscribe all agents to pipeline channel
39
+ for agent_func in agents:
40
+ agent_info = get_agent_info(agent_func)
41
+ agent_info["underlying_agent"].subscribe_to_channel(channel)
42
+
43
+ # Broadcast initial data to start pipeline
44
+ reef = get_reef()
45
+ reef.create_channel(channel) # Ensure channel exists
46
+ return reef.system_broadcast(initial_data, channel)
47
+
48
+ # Store metadata
49
+ pipeline_trigger._praval_pipeline = True
50
+ pipeline_trigger._praval_agents = agents
51
+ pipeline_trigger._praval_channel = channel
52
+
53
+ return pipeline_trigger
54
+
55
+
56
+ def conditional_agent(condition_func: Callable[[Any], bool]):
57
+ """
58
+ Decorator for conditional agent execution.
59
+
60
+ Args:
61
+ condition_func: Function that takes a spore and returns bool
62
+
63
+ Example:
64
+ @conditional_agent(lambda spore: spore.knowledge.get("priority") == "high")
65
+ @agent("urgent_processor")
66
+ def process_urgent(spore):
67
+ return {"processed": True}
68
+ """
69
+ def decorator(agent_func: Callable) -> Callable:
70
+ if not hasattr(agent_func, '_praval_agent'):
71
+ raise ValueError("conditional_agent must be applied to @agent decorated functions")
72
+
73
+ # Get the original handler
74
+ underlying_agent = agent_func._praval_agent
75
+ original_handler = underlying_agent._custom_spore_handler
76
+
77
+ def conditional_handler(spore):
78
+ """Only execute if condition is met."""
79
+ if condition_func(spore):
80
+ return original_handler(spore)
81
+
82
+ # Replace handler with conditional version
83
+ underlying_agent.set_spore_handler(conditional_handler)
84
+
85
+ return agent_func
86
+
87
+ return decorator
88
+
89
+
90
+ def throttled_agent(delay_seconds: float):
91
+ """
92
+ Decorator to throttle agent execution.
93
+
94
+ Args:
95
+ delay_seconds: Minimum seconds between executions
96
+
97
+ Example:
98
+ @throttled_agent(2.0) # Max once every 2 seconds
99
+ @agent("slow_processor")
100
+ def process_slowly(spore):
101
+ return {"processed": True}
102
+ """
103
+ def decorator(agent_func: Callable) -> Callable:
104
+ if not hasattr(agent_func, '_praval_agent'):
105
+ raise ValueError("throttled_agent must be applied to @agent decorated functions")
106
+
107
+ last_execution = {"time": 0}
108
+ lock = threading.Lock()
109
+
110
+ # Get the original handler
111
+ underlying_agent = agent_func._praval_agent
112
+ original_handler = underlying_agent._custom_spore_handler
113
+
114
+ def throttled_handler(spore):
115
+ """Only execute if enough time has passed."""
116
+ with lock:
117
+ now = time.time()
118
+ if now - last_execution["time"] >= delay_seconds:
119
+ last_execution["time"] = now
120
+ return original_handler(spore)
121
+
122
+ # Replace handler with throttled version
123
+ underlying_agent.set_spore_handler(throttled_handler)
124
+
125
+ return agent_func
126
+
127
+ return decorator
128
+
129
+
130
+ class AgentSession:
131
+ """
132
+ Context manager for coordinated agent sessions.
133
+
134
+ Example:
135
+ with AgentSession("knowledge_mining") as session:
136
+ session.add_agents(explorer, analyzer, curator)
137
+ session.broadcast({"task": "mine concepts about AI"})
138
+ """
139
+
140
+ def __init__(self, session_name: str):
141
+ self.session_name = session_name
142
+ self.channel_name = f"session_{session_name}"
143
+ self.agents = []
144
+
145
+ def __enter__(self):
146
+ # Create session channel
147
+ reef = get_reef()
148
+ reef.create_channel(self.channel_name)
149
+ return self
150
+
151
+ def __exit__(self, exc_type, exc_val, exc_tb):
152
+ # Cleanup if needed (agents will remain subscribed)
153
+ pass
154
+
155
+ def add_agent(self, agent_func: Callable) -> 'AgentSession':
156
+ """Add an agent to this session."""
157
+ if not hasattr(agent_func, '_praval_agent'):
158
+ raise ValueError(f"Function {agent_func.__name__} is not decorated with @agent")
159
+
160
+ agent_info = get_agent_info(agent_func)
161
+ underlying_agent = agent_info["underlying_agent"]
162
+
163
+ # Subscribe agent to session channel
164
+ underlying_agent.subscribe_to_channel(self.channel_name)
165
+ self.agents.append(agent_func)
166
+
167
+ return self
168
+
169
+ def add_agents(self, *agent_funcs: Callable) -> 'AgentSession':
170
+ """Add multiple agents to this session."""
171
+ for agent_func in agent_funcs:
172
+ self.add_agent(agent_func)
173
+ return self
174
+
175
+ def broadcast(self, data: Dict[str, Any]) -> str:
176
+ """Broadcast data to all agents in this session."""
177
+ reef = get_reef()
178
+ return reef.system_broadcast(
179
+ {**data, "_session": self.session_name},
180
+ self.channel_name
181
+ )
182
+
183
+ def get_stats(self) -> Dict[str, Any]:
184
+ """Get session statistics."""
185
+ return {
186
+ "session_name": self.session_name,
187
+ "channel": self.channel_name,
188
+ "agent_count": len(self.agents),
189
+ "agent_names": [get_agent_info(agent)["name"] for agent in self.agents]
190
+ }
191
+
192
+
193
+ def start_agents(*agent_funcs: Callable, initial_data: Optional[Dict[str, Any]] = None,
194
+ channel: str = "startup") -> str:
195
+ """
196
+ Convenience function to start multiple agents with initial data.
197
+
198
+ Args:
199
+ *agent_funcs: Functions decorated with @agent
200
+ initial_data: Initial data to broadcast (optional)
201
+ channel: Channel to use for startup communication
202
+
203
+ Returns:
204
+ Spore ID of startup broadcast
205
+
206
+ Example:
207
+ start_agents(explorer, analyzer, curator,
208
+ initial_data={"task": "analyze market trends"})
209
+ """
210
+ # Subscribe all agents to startup channel
211
+ reef = get_reef()
212
+ reef.create_channel(channel)
213
+
214
+ for agent_func in agent_funcs:
215
+ if not hasattr(agent_func, '_praval_agent'):
216
+ raise ValueError(f"Function {agent_func.__name__} is not decorated with @agent")
217
+
218
+ agent_info = get_agent_info(agent_func)
219
+ agent_info["underlying_agent"].subscribe_to_channel(channel)
220
+
221
+ # Broadcast initial data if provided
222
+ if initial_data:
223
+ return reef.system_broadcast(initial_data, channel)
224
+ else:
225
+ return reef.system_broadcast({"type": "agents_started"}, channel)
@@ -0,0 +1 @@
1
+ """Core components for Praval framework."""