dataknobs-bots 0.2.4__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 (42) hide show
  1. dataknobs_bots/__init__.py +42 -0
  2. dataknobs_bots/api/__init__.py +42 -0
  3. dataknobs_bots/api/dependencies.py +140 -0
  4. dataknobs_bots/api/exceptions.py +289 -0
  5. dataknobs_bots/bot/__init__.py +15 -0
  6. dataknobs_bots/bot/base.py +1091 -0
  7. dataknobs_bots/bot/context.py +102 -0
  8. dataknobs_bots/bot/manager.py +430 -0
  9. dataknobs_bots/bot/registry.py +629 -0
  10. dataknobs_bots/config/__init__.py +39 -0
  11. dataknobs_bots/config/resolution.py +353 -0
  12. dataknobs_bots/knowledge/__init__.py +82 -0
  13. dataknobs_bots/knowledge/query/__init__.py +25 -0
  14. dataknobs_bots/knowledge/query/expander.py +262 -0
  15. dataknobs_bots/knowledge/query/transformer.py +288 -0
  16. dataknobs_bots/knowledge/rag.py +738 -0
  17. dataknobs_bots/knowledge/retrieval/__init__.py +23 -0
  18. dataknobs_bots/knowledge/retrieval/formatter.py +249 -0
  19. dataknobs_bots/knowledge/retrieval/merger.py +279 -0
  20. dataknobs_bots/memory/__init__.py +56 -0
  21. dataknobs_bots/memory/base.py +38 -0
  22. dataknobs_bots/memory/buffer.py +58 -0
  23. dataknobs_bots/memory/vector.py +188 -0
  24. dataknobs_bots/middleware/__init__.py +11 -0
  25. dataknobs_bots/middleware/base.py +92 -0
  26. dataknobs_bots/middleware/cost.py +421 -0
  27. dataknobs_bots/middleware/logging.py +184 -0
  28. dataknobs_bots/reasoning/__init__.py +65 -0
  29. dataknobs_bots/reasoning/base.py +50 -0
  30. dataknobs_bots/reasoning/react.py +299 -0
  31. dataknobs_bots/reasoning/simple.py +51 -0
  32. dataknobs_bots/registry/__init__.py +41 -0
  33. dataknobs_bots/registry/backend.py +181 -0
  34. dataknobs_bots/registry/memory.py +244 -0
  35. dataknobs_bots/registry/models.py +102 -0
  36. dataknobs_bots/registry/portability.py +210 -0
  37. dataknobs_bots/tools/__init__.py +5 -0
  38. dataknobs_bots/tools/knowledge_search.py +113 -0
  39. dataknobs_bots/utils/__init__.py +1 -0
  40. dataknobs_bots-0.2.4.dist-info/METADATA +591 -0
  41. dataknobs_bots-0.2.4.dist-info/RECORD +42 -0
  42. dataknobs_bots-0.2.4.dist-info/WHEEL +4 -0
@@ -0,0 +1,629 @@
1
+ """Multi-tenant bot registry with pluggable storage backends.
2
+
3
+ This module provides a registry for managing bot configurations and instances
4
+ across multiple tenants. It combines:
5
+ - Pluggable storage backends (via RegistryBackend protocol)
6
+ - Environment-aware configuration resolution
7
+ - Portability validation for cross-environment deployments
8
+ - Bot instance caching with TTL
9
+
10
+ Example:
11
+ ```python
12
+ from dataknobs_bots.bot import BotRegistry
13
+ from dataknobs_bots.registry import InMemoryBackend
14
+
15
+ # Create registry with in-memory storage
16
+ registry = BotRegistry(
17
+ backend=InMemoryBackend(),
18
+ environment="production",
19
+ )
20
+ await registry.initialize()
21
+
22
+ # Register a portable bot configuration
23
+ await registry.register("my-bot", {
24
+ "bot": {
25
+ "llm": {"$resource": "default", "type": "llm_providers"},
26
+ "conversation_storage": {"$resource": "db", "type": "databases"},
27
+ }
28
+ })
29
+
30
+ # Get bot instance (resolves $resource references)
31
+ bot = await registry.get_bot("my-bot")
32
+ response = await bot.chat(message, context)
33
+
34
+ # Cleanup
35
+ await registry.close()
36
+ ```
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import asyncio
42
+ import logging
43
+ import time
44
+ from pathlib import Path
45
+ from typing import TYPE_CHECKING, Any
46
+
47
+ from ..registry import InMemoryBackend, RegistryBackend, validate_portability
48
+ from .base import DynaBot
49
+
50
+ if TYPE_CHECKING:
51
+ from dataknobs_config import EnvironmentConfig
52
+
53
+ from ..registry import Registration
54
+
55
+ logger = logging.getLogger(__name__)
56
+
57
+
58
+ class BotRegistry:
59
+ """Multi-tenant bot registry with caching and environment support.
60
+
61
+ The BotRegistry manages multiple bot instances for different clients/tenants.
62
+ It provides:
63
+ - Pluggable storage backends via RegistryBackend protocol
64
+ - Environment-aware configuration resolution
65
+ - Portability validation to ensure configs work across environments
66
+ - LRU-style caching with TTL for bot instances
67
+ - Thread-safe access
68
+
69
+ This enables:
70
+ - Multi-tenant SaaS platforms
71
+ - A/B testing with different bot configurations
72
+ - Horizontal scaling with stateless bot instances
73
+ - Cross-environment deployment with portable configs
74
+
75
+ Attributes:
76
+ backend: Storage backend for configurations
77
+ environment: Environment for $resource resolution
78
+ cache_ttl: Time-to-live for cached bots in seconds
79
+ max_cache_size: Maximum number of bots to cache
80
+
81
+ Example:
82
+ ```python
83
+ from dataknobs_bots.bot import BotRegistry
84
+ from dataknobs_bots.registry import InMemoryBackend
85
+
86
+ # Create registry
87
+ registry = BotRegistry(
88
+ backend=InMemoryBackend(),
89
+ environment="production",
90
+ cache_ttl=300,
91
+ )
92
+ await registry.initialize()
93
+
94
+ # Register portable configuration
95
+ await registry.register("client-123", {
96
+ "bot": {
97
+ "llm": {"$resource": "default", "type": "llm_providers"},
98
+ }
99
+ })
100
+
101
+ # Get bot for a client
102
+ bot = await registry.get_bot("client-123")
103
+
104
+ # Use the bot
105
+ response = await bot.chat(message, context)
106
+ ```
107
+ """
108
+
109
+ def __init__(
110
+ self,
111
+ backend: RegistryBackend | None = None,
112
+ environment: EnvironmentConfig | str | None = None,
113
+ env_dir: str | Path = "config/environments",
114
+ cache_ttl: int = 300,
115
+ max_cache_size: int = 1000,
116
+ validate_on_register: bool = True,
117
+ config_key: str = "bot",
118
+ ):
119
+ """Initialize bot registry.
120
+
121
+ Args:
122
+ backend: Storage backend for configurations.
123
+ If None, uses InMemoryBackend.
124
+ environment: Environment name or EnvironmentConfig for
125
+ $resource resolution. If None, configs are used as-is
126
+ without environment resolution.
127
+ env_dir: Directory containing environment config files.
128
+ Only used if environment is a string name.
129
+ cache_ttl: Cache time-to-live in seconds (default: 300)
130
+ max_cache_size: Maximum cached bots (default: 1000)
131
+ validate_on_register: If True, validate config portability
132
+ when registering (default: True)
133
+ config_key: Key within config containing bot configuration.
134
+ Defaults to "bot". Used during environment resolution.
135
+ """
136
+ self._backend = backend or InMemoryBackend()
137
+ self._env_dir = Path(env_dir)
138
+ self._cache_ttl = cache_ttl
139
+ self._max_cache_size = max_cache_size
140
+ self._validate_on_register = validate_on_register
141
+ self._config_key = config_key
142
+
143
+ # Bot instance cache: bot_id -> (DynaBot, cached_timestamp)
144
+ self._cache: dict[str, tuple[DynaBot, float]] = {}
145
+ self._lock = asyncio.Lock()
146
+ self._initialized = False
147
+
148
+ # Load environment config if specified
149
+ self._environment: EnvironmentConfig | None = None
150
+ if environment is not None:
151
+ try:
152
+ from dataknobs_config import EnvironmentConfig as EnvConfig
153
+
154
+ if isinstance(environment, str):
155
+ self._environment = EnvConfig.load(environment, env_dir)
156
+ else:
157
+ self._environment = environment
158
+ logger.info(f"BotRegistry using environment: {self._environment.name}")
159
+ except ImportError:
160
+ logger.warning(
161
+ "dataknobs_config not installed, environment-aware features disabled"
162
+ )
163
+
164
+ @property
165
+ def backend(self) -> RegistryBackend:
166
+ """Get the storage backend."""
167
+ return self._backend
168
+
169
+ @property
170
+ def environment(self) -> EnvironmentConfig | None:
171
+ """Get current environment config, or None if not environment-aware."""
172
+ return self._environment
173
+
174
+ @property
175
+ def environment_name(self) -> str | None:
176
+ """Get current environment name, or None if not environment-aware."""
177
+ return self._environment.name if self._environment else None
178
+
179
+ @property
180
+ def cache_ttl(self) -> int:
181
+ """Get cache TTL in seconds."""
182
+ return self._cache_ttl
183
+
184
+ @property
185
+ def max_cache_size(self) -> int:
186
+ """Get maximum cache size."""
187
+ return self._max_cache_size
188
+
189
+ async def initialize(self) -> None:
190
+ """Initialize the registry and backend.
191
+
192
+ Must be called before using the registry.
193
+ """
194
+ if not self._initialized:
195
+ await self._backend.initialize()
196
+ self._initialized = True
197
+ logger.info("BotRegistry initialized")
198
+
199
+ async def close(self) -> None:
200
+ """Close the registry and backend.
201
+
202
+ Clears the bot cache and closes the storage backend.
203
+ """
204
+ async with self._lock:
205
+ self._cache.clear()
206
+ await self._backend.close()
207
+ self._initialized = False
208
+ logger.info("BotRegistry closed")
209
+
210
+ async def register(
211
+ self,
212
+ bot_id: str,
213
+ config: dict[str, Any],
214
+ status: str = "active",
215
+ skip_validation: bool = False,
216
+ ) -> Registration:
217
+ """Register or update a bot configuration.
218
+
219
+ Stores a portable configuration in the backend. By default, validates
220
+ that the configuration is portable (no resolved local values).
221
+
222
+ Args:
223
+ bot_id: Unique bot identifier
224
+ config: Bot configuration dictionary (should be portable)
225
+ status: Registration status (default: active)
226
+ skip_validation: If True, skip portability validation
227
+
228
+ Returns:
229
+ Registration object with metadata
230
+
231
+ Raises:
232
+ PortabilityError: If config is not portable and validation is enabled
233
+
234
+ Example:
235
+ ```python
236
+ # Register with portable config
237
+ reg = await registry.register("support-bot", {
238
+ "bot": {
239
+ "llm": {"$resource": "default", "type": "llm_providers"},
240
+ }
241
+ })
242
+ print(f"Registered at: {reg.created_at}")
243
+
244
+ # Update existing registration
245
+ reg = await registry.register("support-bot", new_config)
246
+ print(f"Updated at: {reg.updated_at}")
247
+ ```
248
+ """
249
+ # Validate portability if enabled
250
+ if self._validate_on_register and not skip_validation:
251
+ validate_portability(config)
252
+
253
+ # Store in backend
254
+ registration = await self._backend.register(bot_id, config, status)
255
+
256
+ # Invalidate cache for this bot
257
+ async with self._lock:
258
+ if bot_id in self._cache:
259
+ del self._cache[bot_id]
260
+ logger.debug(f"Invalidated cache for bot: {bot_id}")
261
+
262
+ logger.info(f"Registered bot: {bot_id}")
263
+ return registration
264
+
265
+ async def get_bot(
266
+ self,
267
+ bot_id: str,
268
+ force_refresh: bool = False,
269
+ ) -> DynaBot:
270
+ """Get bot instance for a client.
271
+
272
+ Bots are cached for performance. If a cached bot exists and hasn't
273
+ expired, it's returned. Otherwise, a new bot is created from the
274
+ stored configuration with environment resolution applied.
275
+
276
+ Args:
277
+ bot_id: Bot identifier
278
+ force_refresh: If True, bypass cache and create fresh bot
279
+
280
+ Returns:
281
+ DynaBot instance for the client
282
+
283
+ Raises:
284
+ KeyError: If no registration exists for the bot_id
285
+ ValueError: If bot configuration is invalid
286
+
287
+ Example:
288
+ ```python
289
+ # Get cached bot
290
+ bot = await registry.get_bot("client-123")
291
+
292
+ # Force refresh (e.g., after config change)
293
+ bot = await registry.get_bot("client-123", force_refresh=True)
294
+ ```
295
+ """
296
+ async with self._lock:
297
+ # Check cache
298
+ if not force_refresh and bot_id in self._cache:
299
+ bot, cached_at = self._cache[bot_id]
300
+ if time.time() - cached_at < self._cache_ttl:
301
+ logger.debug(f"Returning cached bot: {bot_id}")
302
+ return bot
303
+
304
+ # Load configuration from backend
305
+ config = await self._backend.get_config(bot_id)
306
+ if config is None:
307
+ raise KeyError(f"No bot configuration found for: {bot_id}")
308
+
309
+ # Create bot with environment resolution if configured
310
+ if self._environment is not None:
311
+ logger.debug(f"Creating bot with environment resolution: {bot_id}")
312
+ bot = await DynaBot.from_environment_aware_config(
313
+ config,
314
+ environment=self._environment,
315
+ env_dir=self._env_dir,
316
+ config_key=self._config_key,
317
+ )
318
+ else:
319
+ # Traditional path - use config as-is
320
+ # Extract bot config if wrapped in config_key
321
+ bot_config = config.get(self._config_key, config)
322
+ logger.debug(f"Creating bot without environment resolution: {bot_id}")
323
+ bot = await DynaBot.from_config(bot_config)
324
+
325
+ # Cache the bot
326
+ self._cache[bot_id] = (bot, time.time())
327
+ logger.info(f"Created bot: {bot_id}")
328
+
329
+ # Evict old entries if cache is full
330
+ if len(self._cache) > self._max_cache_size:
331
+ self._evict_oldest()
332
+
333
+ return bot
334
+
335
+ async def get_config(self, bot_id: str) -> dict[str, Any] | None:
336
+ """Get stored configuration for a bot.
337
+
338
+ Returns the portable configuration as stored, without
339
+ environment resolution applied.
340
+
341
+ Args:
342
+ bot_id: Bot identifier
343
+
344
+ Returns:
345
+ Configuration dict if found, None otherwise
346
+ """
347
+ return await self._backend.get_config(bot_id)
348
+
349
+ async def get_registration(self, bot_id: str) -> Registration | None:
350
+ """Get full registration including metadata.
351
+
352
+ Args:
353
+ bot_id: Bot identifier
354
+
355
+ Returns:
356
+ Registration if found, None otherwise
357
+ """
358
+ return await self._backend.get(bot_id)
359
+
360
+ async def unregister(self, bot_id: str) -> bool:
361
+ """Remove a bot registration (hard delete).
362
+
363
+ Args:
364
+ bot_id: Bot identifier
365
+
366
+ Returns:
367
+ True if removed, False if not found
368
+ """
369
+ # Remove from cache
370
+ async with self._lock:
371
+ if bot_id in self._cache:
372
+ del self._cache[bot_id]
373
+
374
+ result = await self._backend.unregister(bot_id)
375
+ if result:
376
+ logger.info(f"Unregistered bot: {bot_id}")
377
+ return result
378
+
379
+ async def deactivate(self, bot_id: str) -> bool:
380
+ """Deactivate a bot registration (soft delete).
381
+
382
+ Args:
383
+ bot_id: Bot identifier
384
+
385
+ Returns:
386
+ True if deactivated, False if not found
387
+ """
388
+ # Remove from cache
389
+ async with self._lock:
390
+ if bot_id in self._cache:
391
+ del self._cache[bot_id]
392
+
393
+ result = await self._backend.deactivate(bot_id)
394
+ if result:
395
+ logger.info(f"Deactivated bot: {bot_id}")
396
+ return result
397
+
398
+ async def exists(self, bot_id: str) -> bool:
399
+ """Check if an active bot registration exists.
400
+
401
+ Args:
402
+ bot_id: Bot identifier
403
+
404
+ Returns:
405
+ True if registration exists and is active
406
+ """
407
+ return await self._backend.exists(bot_id)
408
+
409
+ async def list_bots(self) -> list[str]:
410
+ """List all active bot IDs.
411
+
412
+ Returns:
413
+ List of active bot identifiers
414
+ """
415
+ return await self._backend.list_ids()
416
+
417
+ async def count(self) -> int:
418
+ """Count active bot registrations.
419
+
420
+ Returns:
421
+ Number of active registrations
422
+ """
423
+ return await self._backend.count()
424
+
425
+ def get_cached_bots(self) -> list[str]:
426
+ """Get list of currently cached bot IDs.
427
+
428
+ Returns:
429
+ List of bot IDs with cached instances
430
+ """
431
+ return list(self._cache.keys())
432
+
433
+ def clear_cache(self) -> None:
434
+ """Clear all cached bot instances.
435
+
436
+ Does not affect stored registrations.
437
+ """
438
+ self._cache.clear()
439
+ logger.debug("Cleared bot cache")
440
+
441
+ def _evict_oldest(self) -> None:
442
+ """Evict oldest cache entries when cache is full.
443
+
444
+ Removes 10% of the oldest entries to make room for new ones.
445
+ """
446
+ # Sort by timestamp (oldest first)
447
+ sorted_items = sorted(self._cache.items(), key=lambda x: x[1][1])
448
+
449
+ # Remove oldest 10%
450
+ num_to_remove = max(1, len(sorted_items) // 10)
451
+ for bot_id, _ in sorted_items[:num_to_remove]:
452
+ del self._cache[bot_id]
453
+ logger.debug(f"Evicted {num_to_remove} bots from cache")
454
+
455
+ # Legacy compatibility methods
456
+
457
+ async def register_client(
458
+ self, client_id: str, bot_config: dict[str, Any]
459
+ ) -> None:
460
+ """Register or update a client's bot configuration.
461
+
462
+ .. deprecated::
463
+ Use :meth:`register` instead.
464
+
465
+ Args:
466
+ client_id: Client/tenant identifier
467
+ bot_config: Bot configuration dictionary
468
+ """
469
+ await self.register(client_id, bot_config)
470
+
471
+ async def remove_client(self, client_id: str) -> None:
472
+ """Remove a client from the registry.
473
+
474
+ .. deprecated::
475
+ Use :meth:`unregister` instead.
476
+
477
+ Args:
478
+ client_id: Client/tenant identifier
479
+ """
480
+ await self.unregister(client_id)
481
+
482
+ def get_cached_clients(self) -> list[str]:
483
+ """Get list of currently cached client IDs.
484
+
485
+ .. deprecated::
486
+ Use :meth:`get_cached_bots` instead.
487
+
488
+ Returns:
489
+ List of client IDs with cached bots
490
+ """
491
+ return self.get_cached_bots()
492
+
493
+ def __repr__(self) -> str:
494
+ """String representation."""
495
+ env = f", environment={self._environment.name!r}" if self._environment else ""
496
+ return (
497
+ f"BotRegistry(backend={self._backend!r}, "
498
+ f"cached={len(self._cache)}{env})"
499
+ )
500
+
501
+
502
+ class InMemoryBotRegistry(BotRegistry):
503
+ """BotRegistry with in-memory storage backend.
504
+
505
+ A convenience subclass that uses InMemoryBackend for storage,
506
+ suitable for testing, CLIs, and single-instance deployments.
507
+
508
+ Unlike the base BotRegistry which accepts a pluggable backend,
509
+ this class always uses in-memory storage and doesn't require
510
+ external dependencies like databases.
511
+
512
+ Example:
513
+ ```python
514
+ from dataknobs_bots.bot import InMemoryBotRegistry
515
+
516
+ # For testing - no environment resolution
517
+ registry = InMemoryBotRegistry(validate_on_register=False)
518
+ await registry.initialize()
519
+
520
+ await registry.register("test-bot", {"llm": {"provider": "echo"}})
521
+ bot = await registry.get_bot("test-bot")
522
+
523
+ # For development with environment
524
+ registry = InMemoryBotRegistry(environment="development")
525
+ await registry.initialize()
526
+ ```
527
+ """
528
+
529
+ def __init__(
530
+ self,
531
+ environment: EnvironmentConfig | str | None = None,
532
+ env_dir: str | Path = "config/environments",
533
+ cache_ttl: int = 300,
534
+ max_cache_size: int = 1000,
535
+ validate_on_register: bool = True,
536
+ config_key: str = "bot",
537
+ ):
538
+ """Initialize in-memory bot registry.
539
+
540
+ Args:
541
+ environment: Environment name or EnvironmentConfig for
542
+ $resource resolution. If None, configs are used as-is
543
+ without environment resolution.
544
+ env_dir: Directory containing environment config files.
545
+ Only used if environment is a string name.
546
+ cache_ttl: Cache time-to-live in seconds (default: 300)
547
+ max_cache_size: Maximum cached bots (default: 1000)
548
+ validate_on_register: If True, validate config portability
549
+ when registering (default: True)
550
+ config_key: Key within config containing bot configuration.
551
+ Defaults to "bot". Used during environment resolution.
552
+ """
553
+ super().__init__(
554
+ backend=InMemoryBackend(),
555
+ environment=environment,
556
+ env_dir=env_dir,
557
+ cache_ttl=cache_ttl,
558
+ max_cache_size=max_cache_size,
559
+ validate_on_register=validate_on_register,
560
+ config_key=config_key,
561
+ )
562
+
563
+ async def clear(self) -> None:
564
+ """Clear all registrations and cached bots.
565
+
566
+ Convenience method for test cleanup that clears both the
567
+ backend storage and the bot instance cache.
568
+
569
+ Example:
570
+ ```python
571
+ # In tests - reset between test cases
572
+ await registry.clear()
573
+ assert await registry.count() == 0
574
+ ```
575
+ """
576
+ await self._backend.clear()
577
+ self._cache.clear()
578
+ logger.debug("Cleared all registrations and cache")
579
+
580
+ def __repr__(self) -> str:
581
+ """String representation."""
582
+ env = f", environment={self._environment.name!r}" if self._environment else ""
583
+ return f"InMemoryBotRegistry(cached={len(self._cache)}{env})"
584
+
585
+
586
+ def create_memory_registry(
587
+ environment: EnvironmentConfig | str | None = None,
588
+ env_dir: str | Path = "config/environments",
589
+ cache_ttl: int = 300,
590
+ max_cache_size: int = 1000,
591
+ validate_on_register: bool = True,
592
+ config_key: str = "bot",
593
+ ) -> InMemoryBotRegistry:
594
+ """Create an InMemoryBotRegistry.
595
+
596
+ Convenience factory for creating in-memory registries suitable for
597
+ testing, CLIs, or single-instance deployments.
598
+
599
+ Args:
600
+ environment: Environment name or EnvironmentConfig for
601
+ $resource resolution. If None, configs are used as-is.
602
+ env_dir: Directory containing environment config files.
603
+ cache_ttl: Cache time-to-live in seconds (default: 300)
604
+ max_cache_size: Maximum cached bots (default: 1000)
605
+ validate_on_register: If True, validate config portability
606
+ config_key: Key within config containing bot configuration
607
+
608
+ Returns:
609
+ InMemoryBotRegistry instance
610
+
611
+ Example:
612
+ ```python
613
+ from dataknobs_bots.bot import create_memory_registry
614
+
615
+ registry = create_memory_registry(validate_on_register=False)
616
+ await registry.initialize()
617
+
618
+ await registry.register("test-bot", {"llm": {"provider": "echo"}})
619
+ bot = await registry.get_bot("test-bot")
620
+ ```
621
+ """
622
+ return InMemoryBotRegistry(
623
+ environment=environment,
624
+ env_dir=env_dir,
625
+ cache_ttl=cache_ttl,
626
+ max_cache_size=max_cache_size,
627
+ validate_on_register=validate_on_register,
628
+ config_key=config_key,
629
+ )
@@ -0,0 +1,39 @@
1
+ """Configuration utilities for DynaBot.
2
+
3
+ This module provides utilities for resource resolution and configuration
4
+ binding with environment-aware support.
5
+
6
+ Example:
7
+ ```python
8
+ from dataknobs_config import EnvironmentConfig
9
+ from dataknobs_bots.config import create_bot_resolver, BotResourceResolver
10
+
11
+ # Low-level: Create resolver and resolve manually
12
+ env = EnvironmentConfig.load("production")
13
+ resolver = create_bot_resolver(env)
14
+ llm = resolver.resolve("llm_providers", "default")
15
+
16
+ # High-level: Use BotResourceResolver for initialized resources
17
+ bot_resolver = BotResourceResolver(env)
18
+ llm = await bot_resolver.get_llm("default")
19
+ db = await bot_resolver.get_database("conversations")
20
+ ```
21
+ """
22
+
23
+ from .resolution import (
24
+ BotResourceResolver,
25
+ create_bot_resolver,
26
+ register_database_factory,
27
+ register_embedding_factory,
28
+ register_llm_factory,
29
+ register_vector_store_factory,
30
+ )
31
+
32
+ __all__ = [
33
+ "create_bot_resolver",
34
+ "BotResourceResolver",
35
+ "register_llm_factory",
36
+ "register_database_factory",
37
+ "register_vector_store_factory",
38
+ "register_embedding_factory",
39
+ ]