Coverage for src / dataknobs_llm / prompts / implementations / config_library.py: 24%
99 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-15 10:28 -0700
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-15 10:28 -0700
1r"""Configuration-based prompt library implementation.
3This module provides a prompt library that loads prompts from Python dictionaries.
4Useful for programmatic prompt definition and testing.
6Example:
7 config = {
8 "system": {
9 "analyze_code": {
10 "template": "Analyze this {{language}} code:\\n{{code}}",
11 "defaults": {"language": "python"},
12 "validation": {
13 "level": "error",
14 "required_params": ["code"]
15 }
16 }
17 },
18 "user": {
19 "code_question": {
20 "template": "Please analyze the following code...",
21 "rag_configs": [...]
22 },
23 "followup_question": {
24 "template": "Additionally, check for..."
25 }
26 }
27 }
29 library = ConfigPromptLibrary(config)
30 template = library.get_system_prompt("analyze_code")
31"""
33import logging
34from typing import Any, Dict, List
36from ..base import (
37 BasePromptLibrary,
38 PromptTemplateDict,
39 RAGConfig,
40 MessageIndex,
41)
43logger = logging.getLogger(__name__)
46class ConfigPromptLibrary(BasePromptLibrary):
47 """Prompt library that loads prompts from configuration dictionaries.
49 Features:
50 - Direct in-memory configuration
51 - No filesystem dependencies
52 - Useful for testing and programmatic definition
53 - Supports all prompt types (system, user, messages, RAG)
55 Example:
56 >>> config = {
57 ... "system": {"greet": {"template": "Hello {{name}}!"}},
58 ... "user": {"ask": {"template": "Tell me about {{topic}}"}}
59 ... }
60 >>> library = ConfigPromptLibrary(config)
61 >>> template = library.get_system_prompt("greet")
62 """
64 def __init__(self, config: Dict[str, Any] | None = None):
65 """Initialize configuration-based prompt library.
67 Args:
68 config: Configuration dictionary with structure:
69 {
70 "system": {name: PromptTemplateDict, ...},
71 "user": {name: PromptTemplateDict, ...},
72 "messages": {name: MessageIndex, ...},
73 "rag": {name: RAGConfig, ...}
74 }
75 """
76 super().__init__()
77 self._config = config or {}
79 # Load prompts from config
80 self._load_from_config()
82 def _load_from_config(self) -> None:
83 """Load all prompts from the configuration dictionary."""
84 self._load_system_prompts()
85 self._load_user_prompts()
86 self._load_message_indexes()
87 self._load_rag_configs() # Load standalone RAG configs
89 def _load_system_prompts(self) -> None:
90 """Load system prompts from config."""
91 system_config = self._config.get("system", {})
93 for name, data in system_config.items():
94 try:
95 template = self._parse_prompt_template(data)
96 self._cache_system_prompt(name, template)
97 logger.debug(f"Loaded system prompt from config: {name}")
98 except Exception as e:
99 logger.error(f"Error loading system prompt {name}: {e}")
101 def _load_user_prompts(self) -> None:
102 """Load user prompts from config."""
103 user_config = self._config.get("user", {})
105 for name, data in user_config.items():
106 try:
107 template = self._parse_prompt_template(data)
108 self._cache_user_prompt(name, template)
109 logger.debug(f"Loaded user prompt from config: {name}")
110 except Exception as e:
111 logger.error(f"Error loading user prompt {name}: {e}")
113 def _load_message_indexes(self) -> None:
114 """Load message indexes from config."""
115 messages_config = self._config.get("messages", {})
117 for name, data in messages_config.items():
118 try:
119 message_index = self._parse_message_index(data)
120 self._cache_message_index(name, message_index)
121 logger.debug(f"Loaded message index from config: {name}")
122 except Exception as e:
123 logger.error(f"Error loading message index {name}: {e}")
125 def _load_rag_configs(self) -> None:
126 """Load RAG configurations from config."""
127 rag_config = self._config.get("rag", {})
129 for name, data in rag_config.items():
130 try:
131 rag = self._parse_rag_config(data)
132 self._cache_rag_config(name, rag)
133 logger.debug(f"Loaded RAG config from config: {name}")
134 except Exception as e:
135 logger.error(f"Error loading RAG config {name}: {e}")
137 # Note: _parse_prompt_template(), _parse_validation_config(), and
138 # _parse_rag_config() are now inherited from BasePromptLibrary
140 def _parse_message_index(self, data: Dict[str, Any]) -> MessageIndex:
141 """Parse message index from config data.
143 Args:
144 data: Message index data
146 Returns:
147 MessageIndex dictionary
148 """
149 message_index: MessageIndex = {
150 "messages": data.get("messages", []),
151 }
153 # Add optional fields
154 if "rag_configs" in data:
155 message_index["rag_configs"] = [
156 self._parse_rag_config(rag_data)
157 for rag_data in data["rag_configs"]
158 ]
160 if "metadata" in data:
161 message_index["metadata"] = data["metadata"]
163 return message_index
165 def get_system_prompt(self, name: str, **kwargs: Any) -> PromptTemplateDict | None:
166 """Get a system prompt by name.
168 Args:
169 name: System prompt name
170 **kwargs: Additional arguments (unused)
172 Returns:
173 PromptTemplateDict if found, None otherwise
174 """
175 return self._get_cached_system_prompt(name)
177 def get_user_prompt(self, name: str, **kwargs: Any) -> PromptTemplateDict | None:
178 """Get a user prompt by name.
180 Args:
181 name: User prompt name
182 **kwargs: Additional arguments (unused)
184 Returns:
185 PromptTemplateDict if found, None otherwise
186 """
187 return self._get_cached_user_prompt(name)
189 def get_message_index(self, name: str, **kwargs: Any) -> MessageIndex | None:
190 """Get a message index by name.
192 Args:
193 name: Message index name
194 **kwargs: Additional arguments (unused)
196 Returns:
197 MessageIndex if found, None otherwise
198 """
199 return self._get_cached_message_index(name)
201 def get_rag_config(self, name: str, **kwargs: Any) -> RAGConfig | None:
202 """Get a standalone RAG configuration by name.
204 Args:
205 name: RAG config name
206 **kwargs: Additional arguments (unused)
208 Returns:
209 RAGConfig if found, None otherwise
210 """
211 return self._get_cached_rag_config(name)
213 def get_prompt_rag_configs(
214 self,
215 prompt_name: str,
216 prompt_type: str = "user",
217 **kwargs: Any
218 ) -> List[RAGConfig]:
219 """Get RAG configurations for a specific prompt.
221 Resolves both inline RAG configs and references to standalone configs.
223 Args:
224 prompt_name: Prompt name
225 prompt_type: Type of prompt ("user" or "system")
226 **kwargs: Additional arguments (unused)
228 Returns:
229 List of RAGConfig (empty if none defined)
230 """
231 # Get the prompt template
232 if prompt_type == "system":
233 template = self.get_system_prompt(prompt_name)
234 else:
235 template = self.get_user_prompt(prompt_name)
237 if template is None:
238 return []
240 configs = []
242 # Get inline RAG configs from template
243 if "rag_configs" in template:
244 configs.extend(template["rag_configs"])
246 # Resolve RAG config references
247 if "rag_config_refs" in template:
248 for ref_name in template["rag_config_refs"]:
249 ref_config = self.get_rag_config(ref_name)
250 if ref_config:
251 configs.append(ref_config)
252 else:
253 logger.warning(
254 f"RAG config reference '{ref_name}' not found "
255 f"for prompt '{prompt_name}'"
256 )
258 return configs
260 def add_system_prompt(self, name: str, template: PromptTemplateDict) -> None:
261 """Add or update a system prompt.
263 Args:
264 name: System prompt name
265 template: Prompt template to add
266 """
267 self._cache_system_prompt(name, template)
268 logger.debug(f"Added/updated system prompt: {name}")
270 def add_user_prompt(self, name: str, template: PromptTemplateDict) -> None:
271 """Add or update a user prompt.
273 Args:
274 name: User prompt name
275 template: Prompt template to add
276 """
277 self._cache_user_prompt(name, template)
278 logger.debug(f"Added/updated user prompt: {name}")
280 def add_message_index(self, name: str, message_index: MessageIndex) -> None:
281 """Add or update a message index.
283 Args:
284 name: Message index name
285 message_index: Message index to add
286 """
287 self._cache_message_index(name, message_index)
288 logger.debug(f"Added/updated message index: {name}")
290 def add_rag_config(self, name: str, rag_config: RAGConfig) -> None:
291 """Add or update a RAG configuration.
293 Args:
294 name: RAG config name
295 rag_config: RAG configuration to add
296 """
297 self._cache_rag_config(name, rag_config)
298 logger.debug(f"Added/updated RAG config: {name}")
300 def list_system_prompts(self) -> List[str]:
301 """List all available system prompt names.
303 Returns:
304 List of system prompt identifiers
305 """
306 return list(self._system_prompt_cache.keys())
308 def list_user_prompts(self) -> List[str]:
309 """List available user prompts.
311 Returns:
312 List of user prompt names
313 """
314 return list(self._user_prompt_cache.keys())
316 def list_message_indexes(self) -> List[str]:
317 """List all available message index names.
319 Returns:
320 List of message index identifiers
321 """
322 return list(self._message_index_cache.keys())