noesium 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.
Files changed (86) hide show
  1. noesium/core/__init__.py +4 -0
  2. noesium/core/agent/__init__.py +14 -0
  3. noesium/core/agent/base.py +227 -0
  4. noesium/core/consts.py +6 -0
  5. noesium/core/goalith/conflict/conflict.py +104 -0
  6. noesium/core/goalith/conflict/detector.py +53 -0
  7. noesium/core/goalith/decomposer/__init__.py +6 -0
  8. noesium/core/goalith/decomposer/base.py +46 -0
  9. noesium/core/goalith/decomposer/callable_decomposer.py +65 -0
  10. noesium/core/goalith/decomposer/llm_decomposer.py +326 -0
  11. noesium/core/goalith/decomposer/prompts.py +140 -0
  12. noesium/core/goalith/decomposer/simple_decomposer.py +61 -0
  13. noesium/core/goalith/errors.py +22 -0
  14. noesium/core/goalith/goalgraph/graph.py +526 -0
  15. noesium/core/goalith/goalgraph/node.py +179 -0
  16. noesium/core/goalith/replanner/base.py +31 -0
  17. noesium/core/goalith/replanner/replanner.py +36 -0
  18. noesium/core/goalith/service.py +26 -0
  19. noesium/core/llm/__init__.py +154 -0
  20. noesium/core/llm/base.py +152 -0
  21. noesium/core/llm/litellm.py +528 -0
  22. noesium/core/llm/llamacpp.py +487 -0
  23. noesium/core/llm/message.py +184 -0
  24. noesium/core/llm/ollama.py +459 -0
  25. noesium/core/llm/openai.py +520 -0
  26. noesium/core/llm/openrouter.py +89 -0
  27. noesium/core/llm/prompt.py +551 -0
  28. noesium/core/memory/__init__.py +11 -0
  29. noesium/core/memory/base.py +464 -0
  30. noesium/core/memory/memu/__init__.py +24 -0
  31. noesium/core/memory/memu/config/__init__.py +26 -0
  32. noesium/core/memory/memu/config/activity/config.py +46 -0
  33. noesium/core/memory/memu/config/event/config.py +46 -0
  34. noesium/core/memory/memu/config/markdown_config.py +241 -0
  35. noesium/core/memory/memu/config/profile/config.py +48 -0
  36. noesium/core/memory/memu/llm_adapter.py +129 -0
  37. noesium/core/memory/memu/memory/__init__.py +31 -0
  38. noesium/core/memory/memu/memory/actions/__init__.py +40 -0
  39. noesium/core/memory/memu/memory/actions/add_activity_memory.py +299 -0
  40. noesium/core/memory/memu/memory/actions/base_action.py +342 -0
  41. noesium/core/memory/memu/memory/actions/cluster_memories.py +262 -0
  42. noesium/core/memory/memu/memory/actions/generate_suggestions.py +198 -0
  43. noesium/core/memory/memu/memory/actions/get_available_categories.py +66 -0
  44. noesium/core/memory/memu/memory/actions/link_related_memories.py +515 -0
  45. noesium/core/memory/memu/memory/actions/run_theory_of_mind.py +254 -0
  46. noesium/core/memory/memu/memory/actions/update_memory_with_suggestions.py +514 -0
  47. noesium/core/memory/memu/memory/embeddings.py +130 -0
  48. noesium/core/memory/memu/memory/file_manager.py +306 -0
  49. noesium/core/memory/memu/memory/memory_agent.py +578 -0
  50. noesium/core/memory/memu/memory/recall_agent.py +376 -0
  51. noesium/core/memory/memu/memory_store.py +628 -0
  52. noesium/core/memory/models.py +149 -0
  53. noesium/core/msgbus/__init__.py +12 -0
  54. noesium/core/msgbus/base.py +395 -0
  55. noesium/core/orchestrix/__init__.py +0 -0
  56. noesium/core/py.typed +0 -0
  57. noesium/core/routing/__init__.py +20 -0
  58. noesium/core/routing/base.py +66 -0
  59. noesium/core/routing/router.py +241 -0
  60. noesium/core/routing/strategies/__init__.py +9 -0
  61. noesium/core/routing/strategies/dynamic_complexity.py +361 -0
  62. noesium/core/routing/strategies/self_assessment.py +147 -0
  63. noesium/core/routing/types.py +38 -0
  64. noesium/core/toolify/__init__.py +39 -0
  65. noesium/core/toolify/base.py +360 -0
  66. noesium/core/toolify/config.py +138 -0
  67. noesium/core/toolify/mcp_integration.py +275 -0
  68. noesium/core/toolify/registry.py +214 -0
  69. noesium/core/toolify/toolkits/__init__.py +1 -0
  70. noesium/core/tracing/__init__.py +37 -0
  71. noesium/core/tracing/langgraph_hooks.py +308 -0
  72. noesium/core/tracing/opik_tracing.py +144 -0
  73. noesium/core/tracing/token_tracker.py +166 -0
  74. noesium/core/utils/__init__.py +10 -0
  75. noesium/core/utils/logging.py +172 -0
  76. noesium/core/utils/statistics.py +12 -0
  77. noesium/core/utils/typing.py +17 -0
  78. noesium/core/vector_store/__init__.py +79 -0
  79. noesium/core/vector_store/base.py +94 -0
  80. noesium/core/vector_store/pgvector.py +304 -0
  81. noesium/core/vector_store/weaviate.py +383 -0
  82. noesium-0.1.0.dist-info/METADATA +525 -0
  83. noesium-0.1.0.dist-info/RECORD +86 -0
  84. noesium-0.1.0.dist-info/WHEEL +5 -0
  85. noesium-0.1.0.dist-info/licenses/LICENSE +21 -0
  86. noesium-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,551 @@
1
+ """
2
+ Independent Prompts Module
3
+
4
+ A comprehensive and extensible prompt management system for handling various
5
+ prompt templates, dynamic variable injection, and multi-format support.
6
+
7
+ This module is self-contained and doesn't depend on other core modules.
8
+ """
9
+
10
+ import json
11
+ import re
12
+ from abc import ABC, abstractmethod
13
+ from datetime import datetime
14
+ from enum import Enum
15
+ from pathlib import Path
16
+ from string import Template
17
+ from typing import Any, Callable, Dict, List, Optional, Union
18
+
19
+ import yaml
20
+ from pydantic import BaseModel, Field
21
+
22
+ # Try to import external dependencies
23
+ try:
24
+ from jinja2 import BaseLoader, Environment, FileSystemLoader
25
+
26
+ HAS_JINJA2 = True
27
+ except ImportError:
28
+ HAS_JINJA2 = False
29
+
30
+ # Import message types from message.py
31
+ from .message import AssistantMessage, BaseMessage, SystemMessage, UserMessage
32
+
33
+ # ============================================================================
34
+ # Prompt Management System
35
+ # ============================================================================
36
+
37
+
38
+ class TemplateEngine(Enum):
39
+ """Supported template engines"""
40
+
41
+ STRING = "string" # Python string.Template
42
+ JINJA2 = "jinja2" # Jinja2 templates
43
+ FORMAT = "format" # Python str.format()
44
+
45
+
46
+ class PromptMetadata(BaseModel):
47
+ """Metadata for a prompt template"""
48
+
49
+ name: str
50
+ description: str = ""
51
+ version: str = "1.0"
52
+ author: str = ""
53
+ created_at: Optional[str] = None
54
+ tags: List[str] = Field(default_factory=list)
55
+ required_variables: List[str] = Field(default_factory=list)
56
+ optional_variables: Dict[str, Any] = Field(default_factory=dict)
57
+ template_engine: TemplateEngine = TemplateEngine.JINJA2
58
+
59
+
60
+ class MessageTemplate(BaseModel):
61
+ """Template for a single message"""
62
+
63
+ role: str # "system", "user", "assistant"
64
+ content: str
65
+ name: Optional[str] = None
66
+ cache: bool = False
67
+ condition: Optional[str] = None # Conditional inclusion
68
+
69
+
70
+ class PromptTemplate(BaseModel):
71
+ """Complete prompt template with metadata and messages"""
72
+
73
+ metadata: PromptMetadata
74
+ messages: List[MessageTemplate]
75
+ global_variables: Dict[str, Any] = Field(default_factory=dict)
76
+ includes: List[str] = Field(default_factory=list)
77
+ extends: Optional[str] = None
78
+
79
+
80
+ class TemplateProcessor(ABC):
81
+ """Abstract base class for template processors"""
82
+
83
+ @abstractmethod
84
+ def render(self, template: str, variables: Dict[str, Any]) -> str:
85
+ """Render template with variables"""
86
+
87
+ @abstractmethod
88
+ def validate_template(self, template: str) -> bool:
89
+ """Validate template syntax"""
90
+
91
+
92
+ class StringTemplateProcessor(TemplateProcessor):
93
+ """Python string.Template processor"""
94
+
95
+ def render(self, template: str, variables: Dict[str, Any]) -> str:
96
+ try:
97
+ t = Template(template)
98
+ return t.safe_substitute(**variables)
99
+ except Exception as e:
100
+ raise ValueError(f"Template rendering failed: {e}")
101
+
102
+ def validate_template(self, template: str) -> bool:
103
+ try:
104
+ Template(template)
105
+ return True
106
+ except Exception:
107
+ return False
108
+
109
+
110
+ class FormatTemplateProcessor(TemplateProcessor):
111
+ """Python str.format() processor"""
112
+
113
+ def render(self, template: str, variables: Dict[str, Any]) -> str:
114
+ try:
115
+ return template.format(**variables)
116
+ except Exception as e:
117
+ raise ValueError(f"Template rendering failed: {e}")
118
+
119
+ def validate_template(self, template: str) -> bool:
120
+ try:
121
+ # Try to format with empty dict to check syntax
122
+ template.format()
123
+ return True
124
+ except (ValueError, KeyError):
125
+ # KeyError is expected if variables are missing
126
+ return True
127
+ except Exception:
128
+ return False
129
+
130
+
131
+ class Jinja2TemplateProcessor(TemplateProcessor):
132
+ """Jinja2 template processor"""
133
+
134
+ def __init__(self, template_dir: Optional[str] = None):
135
+ if not HAS_JINJA2:
136
+ raise ImportError("Jinja2 not available. Install with: pip install jinja2")
137
+
138
+ if template_dir:
139
+ loader = FileSystemLoader(template_dir)
140
+ else:
141
+ loader = BaseLoader()
142
+
143
+ self.env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
144
+
145
+ # Add custom filters
146
+ self.env.filters["jsonify"] = json.dumps
147
+ self.env.filters["yamlify"] = yaml.dump
148
+ self.env.globals["datetime"] = datetime
149
+
150
+ def render(self, template: str, variables: Dict[str, Any]) -> str:
151
+ try:
152
+ t = self.env.from_string(template)
153
+ return t.render(**variables)
154
+ except Exception as e:
155
+ raise ValueError(f"Template rendering failed: {e}")
156
+
157
+ def validate_template(self, template: str) -> bool:
158
+ try:
159
+ self.env.from_string(template)
160
+ return True
161
+ except Exception:
162
+ return False
163
+
164
+
165
+ class PromptLoader:
166
+ """Load prompts from various sources"""
167
+
168
+ @staticmethod
169
+ def from_markdown(file_path: Union[str, Path]) -> PromptTemplate:
170
+ """Load prompt from markdown file with YAML frontmatter"""
171
+ path = Path(file_path)
172
+ if not path.exists():
173
+ raise FileNotFoundError(f"Prompt file not found: {file_path}")
174
+
175
+ content = path.read_text(encoding="utf-8")
176
+ return PromptLoader.from_markdown_string(content, name=path.stem)
177
+
178
+ @staticmethod
179
+ def from_markdown_string(content: str, name: str = "inline") -> PromptTemplate:
180
+ """Load prompt from markdown string with YAML frontmatter"""
181
+ # Check for YAML frontmatter
182
+ if content.startswith("---"):
183
+ parts = content.split("---", 2)
184
+ if len(parts) >= 3:
185
+ yaml_content = parts[1].strip()
186
+ markdown_content = parts[2].strip()
187
+ else:
188
+ yaml_content = ""
189
+ markdown_content = content
190
+ else:
191
+ yaml_content = ""
192
+ markdown_content = content
193
+
194
+ # Parse metadata
195
+ metadata_dict = yaml.safe_load(yaml_content) if yaml_content else {}
196
+ metadata = PromptMetadata(
197
+ name=metadata_dict.get("name", name),
198
+ description=metadata_dict.get("description", ""),
199
+ version=metadata_dict.get("version", "1.0"),
200
+ author=metadata_dict.get("author", ""),
201
+ created_at=metadata_dict.get("created_at"),
202
+ tags=metadata_dict.get("tags", []),
203
+ required_variables=metadata_dict.get("required_variables", []),
204
+ optional_variables=metadata_dict.get("optional_variables", {}),
205
+ template_engine=TemplateEngine(metadata_dict.get("template_engine", "jinja2")),
206
+ )
207
+
208
+ # Parse messages from markdown
209
+ messages = PromptLoader._parse_markdown_messages(markdown_content, metadata_dict)
210
+
211
+ return PromptTemplate(
212
+ metadata=metadata,
213
+ messages=messages,
214
+ global_variables=metadata_dict.get("global_variables", {}),
215
+ includes=metadata_dict.get("includes", []),
216
+ extends=metadata_dict.get("extends"),
217
+ )
218
+
219
+ @staticmethod
220
+ def from_yaml(file_path: Union[str, Path]) -> PromptTemplate:
221
+ """Load prompt from YAML file"""
222
+ path = Path(file_path)
223
+ if not path.exists():
224
+ raise FileNotFoundError(f"Prompt file not found: {file_path}")
225
+
226
+ with open(path, "r", encoding="utf-8") as f:
227
+ data = yaml.safe_load(f)
228
+
229
+ return PromptLoader._dict_to_prompt_template(data)
230
+
231
+ @staticmethod
232
+ def from_json(file_path: Union[str, Path]) -> PromptTemplate:
233
+ """Load prompt from JSON file"""
234
+ path = Path(file_path)
235
+ if not path.exists():
236
+ raise FileNotFoundError(f"Prompt file not found: {file_path}")
237
+
238
+ with open(path, "r", encoding="utf-8") as f:
239
+ data = json.load(f)
240
+
241
+ return PromptLoader._dict_to_prompt_template(data)
242
+
243
+ @staticmethod
244
+ def from_string(content: str, name: str = "inline", role: str = "system", **metadata_kwargs) -> PromptTemplate:
245
+ """Create prompt from string content"""
246
+ metadata = PromptMetadata(name=name, **metadata_kwargs)
247
+
248
+ # Simple string becomes a message with specified role
249
+ messages = [MessageTemplate(role=role, content=content)]
250
+
251
+ return PromptTemplate(metadata=metadata, messages=messages)
252
+
253
+ @staticmethod
254
+ def _parse_markdown_messages(content: str, metadata: Dict[str, Any]) -> List[MessageTemplate]:
255
+ """Parse message blocks from markdown content"""
256
+ messages = []
257
+
258
+ # Look for message blocks: ## role: content or ### role: content
259
+ message_pattern = r"^#+\s*(system|user|assistant)(?:\s*:\s*(.*))?$"
260
+ lines = content.split("\n")
261
+
262
+ current_message = None
263
+ current_content = []
264
+
265
+ for line in lines:
266
+ match = re.match(message_pattern, line.strip(), re.IGNORECASE)
267
+ if match:
268
+ # Save previous message
269
+ if current_message:
270
+ current_message.content = "\n".join(current_content).strip()
271
+ messages.append(current_message)
272
+
273
+ # Start new message
274
+ role = match.group(1).lower()
275
+ title_content = match.group(2) or ""
276
+ current_message = MessageTemplate(role=role, content="", cache=metadata.get("cache", False))
277
+ current_content = [title_content] if title_content else []
278
+ else:
279
+ if current_message:
280
+ current_content.append(line)
281
+
282
+ # Save last message
283
+ if current_message:
284
+ current_message.content = "\n".join(current_content).strip()
285
+ messages.append(current_message)
286
+
287
+ # If no structured messages found, treat entire content as system message
288
+ if not messages:
289
+ messages = [MessageTemplate(role="system", content=content)]
290
+
291
+ return messages
292
+
293
+ @staticmethod
294
+ def _dict_to_prompt_template(data: Dict[str, Any]) -> PromptTemplate:
295
+ """Convert dictionary to PromptTemplate"""
296
+ metadata_dict = data.get("metadata", {})
297
+ metadata = PromptMetadata(
298
+ name=metadata_dict.get("name", "unnamed"),
299
+ description=metadata_dict.get("description", ""),
300
+ version=metadata_dict.get("version", "1.0"),
301
+ author=metadata_dict.get("author", ""),
302
+ created_at=metadata_dict.get("created_at"),
303
+ tags=metadata_dict.get("tags", []),
304
+ required_variables=metadata_dict.get("required_variables", []),
305
+ optional_variables=metadata_dict.get("optional_variables", {}),
306
+ template_engine=TemplateEngine(metadata_dict.get("template_engine", "jinja2")),
307
+ )
308
+
309
+ messages_data = data.get("messages", [])
310
+ messages = [
311
+ MessageTemplate(
312
+ role=msg.get("role", "user"),
313
+ content=msg.get("content", ""),
314
+ name=msg.get("name"),
315
+ cache=msg.get("cache", False),
316
+ condition=msg.get("condition"),
317
+ )
318
+ for msg in messages_data
319
+ ]
320
+
321
+ return PromptTemplate(
322
+ metadata=metadata,
323
+ messages=messages,
324
+ global_variables=data.get("global_variables", {}),
325
+ includes=data.get("includes", []),
326
+ extends=data.get("extends"),
327
+ )
328
+
329
+
330
+ class PromptManager:
331
+ """Main prompt management system"""
332
+
333
+ def __init__(
334
+ self,
335
+ template_dirs: Optional[List[str]] = None,
336
+ default_engine: TemplateEngine = TemplateEngine.JINJA2,
337
+ enable_cache: bool = True,
338
+ ):
339
+ self.template_dirs = [Path(d) for d in (template_dirs or [])]
340
+ self.default_engine = default_engine
341
+ self.enable_cache = enable_cache
342
+
343
+ # Template cache
344
+ self._template_cache: Dict[str, PromptTemplate] = {}
345
+
346
+ # Processors
347
+ self._processors = {
348
+ TemplateEngine.STRING: StringTemplateProcessor(),
349
+ TemplateEngine.FORMAT: FormatTemplateProcessor(),
350
+ }
351
+
352
+ if HAS_JINJA2:
353
+ template_dir = str(self.template_dirs[0]) if self.template_dirs else None
354
+ self._processors[TemplateEngine.JINJA2] = Jinja2TemplateProcessor(template_dir)
355
+
356
+ # Custom functions for templates
357
+ self._custom_functions: Dict[str, Callable] = {}
358
+
359
+ def register_custom_function(self, name: str, func: Callable):
360
+ """Register custom function for use in templates"""
361
+ self._custom_functions[name] = func
362
+
363
+ def load_prompt(
364
+ self,
365
+ file_path: Optional[Union[str, Path]] = None,
366
+ content: Optional[str] = None,
367
+ name: Optional[str] = None,
368
+ **metadata_kwargs,
369
+ ) -> PromptTemplate:
370
+ """Load a prompt template"""
371
+
372
+ if file_path:
373
+ path = Path(file_path)
374
+
375
+ # Try to find in template directories
376
+ if not path.is_absolute():
377
+ for template_dir in self.template_dirs:
378
+ candidate = template_dir / path
379
+ if candidate.exists():
380
+ path = candidate
381
+ break
382
+
383
+ cache_key = str(path)
384
+
385
+ # Check cache
386
+ if self.enable_cache and cache_key in self._template_cache:
387
+ return self._template_cache[cache_key]
388
+
389
+ # Load based on file extension
390
+ if path.suffix.lower() == ".md":
391
+ template = PromptLoader.from_markdown(path)
392
+ elif path.suffix.lower() in [".yaml", ".yml"]:
393
+ template = PromptLoader.from_yaml(path)
394
+ elif path.suffix.lower() == ".json":
395
+ template = PromptLoader.from_json(path)
396
+ else:
397
+ # Try markdown first, then treat as plain text
398
+ try:
399
+ template = PromptLoader.from_markdown(path)
400
+ except:
401
+ content = path.read_text(encoding="utf-8")
402
+ template = PromptLoader.from_string(content, name or path.stem)
403
+
404
+ # Cache template
405
+ if self.enable_cache:
406
+ self._template_cache[cache_key] = template
407
+
408
+ return template
409
+
410
+ elif content:
411
+ return PromptLoader.from_string(content, name or "inline", **metadata_kwargs)
412
+
413
+ else:
414
+ raise ValueError("Either file_path or content must be provided")
415
+
416
+ def render_prompt(
417
+ self, template: Union[PromptTemplate, str, Path], variables: Optional[Dict[str, Any]] = None, **kwargs
418
+ ) -> List[BaseMessage]:
419
+ """Render a prompt template to messages"""
420
+
421
+ if not isinstance(template, PromptTemplate):
422
+ template = self.load_prompt(template)
423
+
424
+ variables = variables or {}
425
+ variables.update(kwargs)
426
+
427
+ # Add global variables and custom functions
428
+ variables.update(template.global_variables)
429
+ variables.update(self._custom_functions)
430
+
431
+ # Validate required variables
432
+ missing_vars = set(template.metadata.required_variables) - set(variables.keys())
433
+ if missing_vars:
434
+ raise ValueError(f"Missing required variables: {missing_vars}")
435
+
436
+ # Add optional variables with defaults
437
+ for var, default in template.metadata.optional_variables.items():
438
+ if var not in variables:
439
+ variables[var] = default
440
+
441
+ # Get template processor
442
+ engine = template.metadata.template_engine
443
+ if engine not in self._processors:
444
+ engine = self.default_engine
445
+
446
+ processor = self._processors[engine]
447
+
448
+ # Render messages
449
+ messages = []
450
+ for msg_template in template.messages:
451
+ # Check condition
452
+ if msg_template.condition:
453
+ try:
454
+ condition_result = eval(msg_template.condition, {"__builtins__": {}}, variables)
455
+ if not condition_result:
456
+ continue
457
+ except Exception as e:
458
+ print(f"Warning: Condition evaluation failed for message: {e}")
459
+ continue
460
+
461
+ # Render content
462
+ rendered_content = processor.render(msg_template.content, variables)
463
+
464
+ # Create message object
465
+ if msg_template.role == "system":
466
+ message = SystemMessage(content=rendered_content, name=msg_template.name, cache=msg_template.cache)
467
+ elif msg_template.role == "user":
468
+ message = UserMessage(content=rendered_content, name=msg_template.name)
469
+ elif msg_template.role == "assistant":
470
+ message = AssistantMessage(content=rendered_content, name=msg_template.name)
471
+ else:
472
+ raise ValueError(f"Unknown role: {msg_template.role}")
473
+
474
+ messages.append(message)
475
+
476
+ return messages
477
+
478
+ def list_templates(self, tag: Optional[str] = None) -> List[str]:
479
+ """List available templates"""
480
+ templates = []
481
+
482
+ for template_dir in self.template_dirs:
483
+ if not template_dir.exists():
484
+ continue
485
+
486
+ for file_path in template_dir.rglob("*"):
487
+ if file_path.is_file() and file_path.suffix.lower() in [".md", ".yaml", ".yml", ".json"]:
488
+ try:
489
+ template = self.load_prompt(file_path)
490
+ if not tag or tag in template.metadata.tags:
491
+ templates.append(str(file_path.relative_to(template_dir)))
492
+ except Exception:
493
+ continue
494
+
495
+ return sorted(templates)
496
+
497
+ def get_template_info(self, template_path: Union[str, Path]) -> PromptMetadata:
498
+ """Get metadata for a template"""
499
+ template = self.load_prompt(template_path)
500
+ return template.metadata
501
+
502
+ def validate_template(self, template_path: Union[str, Path]) -> Dict[str, Any]:
503
+ """Validate a template"""
504
+ result = {"valid": True, "errors": [], "warnings": []}
505
+
506
+ try:
507
+ template = self.load_prompt(template_path)
508
+
509
+ # Check engine availability
510
+ engine = template.metadata.template_engine
511
+ if engine not in self._processors:
512
+ result["warnings"].append(
513
+ f"Template engine {engine.value} not available, using {self.default_engine.value}"
514
+ )
515
+ engine = self.default_engine
516
+
517
+ processor = self._processors[engine]
518
+
519
+ # Validate each message template
520
+ for i, msg_template in enumerate(template.messages):
521
+ if not processor.validate_template(msg_template.content):
522
+ result["valid"] = False
523
+ result["errors"].append(f"Invalid template syntax in message {i+1}")
524
+
525
+ except Exception as e:
526
+ result["valid"] = False
527
+ result["errors"].append(str(e))
528
+
529
+ return result
530
+
531
+ def clear_cache(self):
532
+ """Clear template cache"""
533
+ self._template_cache.clear()
534
+
535
+
536
+ # ============================================================================
537
+ # Convenience Functions
538
+ # ============================================================================
539
+
540
+
541
+ def create_simple_prompt(content: str, role: str = "system", **kwargs) -> List[BaseMessage]:
542
+ """Quick way to create a simple prompt"""
543
+ manager = PromptManager()
544
+ template = PromptLoader.from_string(content, role=role)
545
+ return manager.render_prompt(template, **kwargs)
546
+
547
+
548
+ def load_and_render(file_path: Union[str, Path], **variables) -> List[BaseMessage]:
549
+ """Quick way to load and render a prompt file"""
550
+ manager = PromptManager()
551
+ return manager.render_prompt(file_path, variables)
@@ -0,0 +1,11 @@
1
+ from .base import BaseMemoryManager, BaseMemoryStore
2
+ from .models import MemoryFilter, MemoryItem, MemoryStats, SearchResult
3
+
4
+ __all__ = [
5
+ "BaseMemoryStore",
6
+ "BaseMemoryManager",
7
+ "MemoryItem",
8
+ "MemoryFilter",
9
+ "MemoryStats",
10
+ "SearchResult",
11
+ ]