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.
- dataknobs_bots/__init__.py +42 -0
- dataknobs_bots/api/__init__.py +42 -0
- dataknobs_bots/api/dependencies.py +140 -0
- dataknobs_bots/api/exceptions.py +289 -0
- dataknobs_bots/bot/__init__.py +15 -0
- dataknobs_bots/bot/base.py +1091 -0
- dataknobs_bots/bot/context.py +102 -0
- dataknobs_bots/bot/manager.py +430 -0
- dataknobs_bots/bot/registry.py +629 -0
- dataknobs_bots/config/__init__.py +39 -0
- dataknobs_bots/config/resolution.py +353 -0
- dataknobs_bots/knowledge/__init__.py +82 -0
- dataknobs_bots/knowledge/query/__init__.py +25 -0
- dataknobs_bots/knowledge/query/expander.py +262 -0
- dataknobs_bots/knowledge/query/transformer.py +288 -0
- dataknobs_bots/knowledge/rag.py +738 -0
- dataknobs_bots/knowledge/retrieval/__init__.py +23 -0
- dataknobs_bots/knowledge/retrieval/formatter.py +249 -0
- dataknobs_bots/knowledge/retrieval/merger.py +279 -0
- dataknobs_bots/memory/__init__.py +56 -0
- dataknobs_bots/memory/base.py +38 -0
- dataknobs_bots/memory/buffer.py +58 -0
- dataknobs_bots/memory/vector.py +188 -0
- dataknobs_bots/middleware/__init__.py +11 -0
- dataknobs_bots/middleware/base.py +92 -0
- dataknobs_bots/middleware/cost.py +421 -0
- dataknobs_bots/middleware/logging.py +184 -0
- dataknobs_bots/reasoning/__init__.py +65 -0
- dataknobs_bots/reasoning/base.py +50 -0
- dataknobs_bots/reasoning/react.py +299 -0
- dataknobs_bots/reasoning/simple.py +51 -0
- dataknobs_bots/registry/__init__.py +41 -0
- dataknobs_bots/registry/backend.py +181 -0
- dataknobs_bots/registry/memory.py +244 -0
- dataknobs_bots/registry/models.py +102 -0
- dataknobs_bots/registry/portability.py +210 -0
- dataknobs_bots/tools/__init__.py +5 -0
- dataknobs_bots/tools/knowledge_search.py +113 -0
- dataknobs_bots/utils/__init__.py +1 -0
- dataknobs_bots-0.2.4.dist-info/METADATA +591 -0
- dataknobs_bots-0.2.4.dist-info/RECORD +42 -0
- dataknobs_bots-0.2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""DataKnobs Bots - Configuration-driven AI agents."""
|
|
2
|
+
|
|
3
|
+
from .bot import BotContext, BotManager, BotRegistry, DynaBot
|
|
4
|
+
from .knowledge import RAGKnowledgeBase, create_knowledge_base_from_config
|
|
5
|
+
from .memory import BufferMemory, Memory, VectorMemory, create_memory_from_config
|
|
6
|
+
from .middleware import CostTrackingMiddleware, LoggingMiddleware, Middleware
|
|
7
|
+
from .reasoning import (
|
|
8
|
+
ReActReasoning,
|
|
9
|
+
ReasoningStrategy,
|
|
10
|
+
SimpleReasoning,
|
|
11
|
+
create_reasoning_from_config,
|
|
12
|
+
)
|
|
13
|
+
from .tools import KnowledgeSearchTool
|
|
14
|
+
|
|
15
|
+
__version__ = "0.2.4"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
# Bot
|
|
19
|
+
"DynaBot",
|
|
20
|
+
"BotContext",
|
|
21
|
+
"BotManager",
|
|
22
|
+
"BotRegistry",
|
|
23
|
+
# Memory
|
|
24
|
+
"Memory",
|
|
25
|
+
"BufferMemory",
|
|
26
|
+
"VectorMemory",
|
|
27
|
+
"create_memory_from_config",
|
|
28
|
+
# Knowledge
|
|
29
|
+
"RAGKnowledgeBase",
|
|
30
|
+
"create_knowledge_base_from_config",
|
|
31
|
+
# Tools
|
|
32
|
+
"KnowledgeSearchTool",
|
|
33
|
+
# Reasoning
|
|
34
|
+
"ReasoningStrategy",
|
|
35
|
+
"SimpleReasoning",
|
|
36
|
+
"ReActReasoning",
|
|
37
|
+
"create_reasoning_from_config",
|
|
38
|
+
# Middleware
|
|
39
|
+
"Middleware",
|
|
40
|
+
"CostTrackingMiddleware",
|
|
41
|
+
"LoggingMiddleware",
|
|
42
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""FastAPI integration components for dataknobs_bots."""
|
|
2
|
+
|
|
3
|
+
from .dependencies import (
|
|
4
|
+
BotManagerDep,
|
|
5
|
+
get_bot_manager,
|
|
6
|
+
init_bot_manager,
|
|
7
|
+
reset_bot_manager,
|
|
8
|
+
)
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
APIError,
|
|
11
|
+
BotCreationError,
|
|
12
|
+
BotNotFoundError,
|
|
13
|
+
ConfigurationError,
|
|
14
|
+
ConversationNotFoundError,
|
|
15
|
+
RateLimitError,
|
|
16
|
+
ValidationError,
|
|
17
|
+
api_error_handler,
|
|
18
|
+
general_exception_handler,
|
|
19
|
+
http_exception_handler,
|
|
20
|
+
register_exception_handlers,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
# Dependencies
|
|
25
|
+
"get_bot_manager",
|
|
26
|
+
"init_bot_manager",
|
|
27
|
+
"reset_bot_manager",
|
|
28
|
+
"BotManagerDep",
|
|
29
|
+
# Exceptions
|
|
30
|
+
"APIError",
|
|
31
|
+
"BotNotFoundError",
|
|
32
|
+
"BotCreationError",
|
|
33
|
+
"ConversationNotFoundError",
|
|
34
|
+
"ValidationError",
|
|
35
|
+
"ConfigurationError",
|
|
36
|
+
"RateLimitError",
|
|
37
|
+
# Handlers
|
|
38
|
+
"api_error_handler",
|
|
39
|
+
"http_exception_handler",
|
|
40
|
+
"general_exception_handler",
|
|
41
|
+
"register_exception_handlers",
|
|
42
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Dependency injection helpers for FastAPI.
|
|
2
|
+
|
|
3
|
+
This module provides singleton management and FastAPI dependency injection
|
|
4
|
+
for bot-related services.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
```python
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from dataknobs_bots.api.dependencies import (
|
|
10
|
+
init_bot_manager,
|
|
11
|
+
BotManagerDep,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
app = FastAPI()
|
|
15
|
+
|
|
16
|
+
@app.on_event("startup")
|
|
17
|
+
async def startup():
|
|
18
|
+
# Initialize with a config loader
|
|
19
|
+
init_bot_manager(config_loader=my_loader)
|
|
20
|
+
|
|
21
|
+
@app.post("/chat/{bot_id}")
|
|
22
|
+
async def chat(
|
|
23
|
+
bot_id: str,
|
|
24
|
+
message: str,
|
|
25
|
+
manager: BotManagerDep,
|
|
26
|
+
):
|
|
27
|
+
bot = await manager.get_or_create(bot_id)
|
|
28
|
+
return await bot.chat(message, context)
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import logging
|
|
35
|
+
from typing import Annotated, Any
|
|
36
|
+
|
|
37
|
+
from dataknobs_bots.bot.manager import BotManager, ConfigLoaderType
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class _BotManagerSingleton:
|
|
44
|
+
"""Singleton container for BotManager instance.
|
|
45
|
+
|
|
46
|
+
Using a class-based approach avoids global statement warnings
|
|
47
|
+
while maintaining singleton semantics.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
_instance: BotManager | None = None
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def get(cls) -> BotManager:
|
|
54
|
+
"""Get the singleton instance, creating with defaults if needed."""
|
|
55
|
+
if cls._instance is None:
|
|
56
|
+
cls._instance = BotManager()
|
|
57
|
+
logger.info("Created default BotManager singleton (no config loader)")
|
|
58
|
+
return cls._instance
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def init(
|
|
62
|
+
cls,
|
|
63
|
+
config_loader: ConfigLoaderType | None = None,
|
|
64
|
+
**kwargs: Any,
|
|
65
|
+
) -> BotManager:
|
|
66
|
+
"""Initialize the singleton with configuration."""
|
|
67
|
+
cls._instance = BotManager(config_loader=config_loader, **kwargs)
|
|
68
|
+
logger.info("Initialized BotManager singleton")
|
|
69
|
+
return cls._instance
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def reset(cls) -> None:
|
|
73
|
+
"""Reset the singleton instance."""
|
|
74
|
+
cls._instance = None
|
|
75
|
+
logger.info("Reset BotManager singleton")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_bot_manager() -> BotManager:
|
|
79
|
+
"""Get or create BotManager singleton instance.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
BotManager instance
|
|
83
|
+
|
|
84
|
+
Note:
|
|
85
|
+
Call `init_bot_manager()` during app startup to configure
|
|
86
|
+
the singleton before using this dependency.
|
|
87
|
+
"""
|
|
88
|
+
return _BotManagerSingleton.get()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def init_bot_manager(
|
|
92
|
+
config_loader: ConfigLoaderType | None = None,
|
|
93
|
+
**kwargs: Any,
|
|
94
|
+
) -> BotManager:
|
|
95
|
+
"""Initialize the BotManager singleton with configuration.
|
|
96
|
+
|
|
97
|
+
Call this during application startup to configure the singleton.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
config_loader: Optional configuration loader for bots
|
|
101
|
+
**kwargs: Additional arguments passed to BotManager
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Configured BotManager instance
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
```python
|
|
108
|
+
@app.on_event("startup")
|
|
109
|
+
async def startup():
|
|
110
|
+
init_bot_manager(
|
|
111
|
+
config_loader=MyConfigLoader("./configs")
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
"""
|
|
115
|
+
return _BotManagerSingleton.init(config_loader=config_loader, **kwargs)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def reset_bot_manager() -> None:
|
|
119
|
+
"""Reset the BotManager singleton.
|
|
120
|
+
|
|
121
|
+
Useful for testing or when reconfiguring the application.
|
|
122
|
+
"""
|
|
123
|
+
_BotManagerSingleton.reset()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# Dependency function for FastAPI
|
|
127
|
+
def _get_bot_manager_dep() -> BotManager:
|
|
128
|
+
"""Dependency function for FastAPI."""
|
|
129
|
+
return get_bot_manager()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# Type alias for FastAPI dependency injection
|
|
133
|
+
# Usage: async def endpoint(manager: BotManagerDep):
|
|
134
|
+
try:
|
|
135
|
+
from fastapi import Depends
|
|
136
|
+
|
|
137
|
+
BotManagerDep = Annotated[BotManager, Depends(_get_bot_manager_dep)]
|
|
138
|
+
except ImportError:
|
|
139
|
+
# FastAPI not installed - provide a placeholder
|
|
140
|
+
BotManagerDep = BotManager # type: ignore
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Custom exceptions and exception handlers for FastAPI applications.
|
|
2
|
+
|
|
3
|
+
This module provides a consistent exception hierarchy and handlers
|
|
4
|
+
for bot-related API errors. The exceptions extend from dataknobs_common
|
|
5
|
+
for consistency across the codebase.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
```python
|
|
9
|
+
from fastapi import FastAPI
|
|
10
|
+
from dataknobs_bots.api.exceptions import (
|
|
11
|
+
register_exception_handlers,
|
|
12
|
+
BotNotFoundError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
app = FastAPI()
|
|
16
|
+
register_exception_handlers(app)
|
|
17
|
+
|
|
18
|
+
@app.get("/bots/{bot_id}")
|
|
19
|
+
async def get_bot(bot_id: str):
|
|
20
|
+
bot = await manager.get(bot_id)
|
|
21
|
+
if not bot:
|
|
22
|
+
raise BotNotFoundError(bot_id)
|
|
23
|
+
return {"bot_id": bot_id}
|
|
24
|
+
```
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from datetime import datetime, timezone
|
|
30
|
+
from typing import TYPE_CHECKING, Any
|
|
31
|
+
|
|
32
|
+
from dataknobs_common.exceptions import (
|
|
33
|
+
ConfigurationError as CommonConfigurationError,
|
|
34
|
+
)
|
|
35
|
+
from dataknobs_common.exceptions import (
|
|
36
|
+
DataknobsError,
|
|
37
|
+
)
|
|
38
|
+
from dataknobs_common.exceptions import (
|
|
39
|
+
NotFoundError as CommonNotFoundError,
|
|
40
|
+
)
|
|
41
|
+
from dataknobs_common.exceptions import (
|
|
42
|
+
ValidationError as CommonValidationError,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from fastapi import FastAPI, HTTPException, Request
|
|
47
|
+
from fastapi.responses import JSONResponse
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class APIError(DataknobsError):
|
|
51
|
+
"""Base exception for API errors.
|
|
52
|
+
|
|
53
|
+
Extends DataknobsError to provide HTTP-specific error handling
|
|
54
|
+
with status codes and structured error responses.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
message: Error message
|
|
58
|
+
status_code: HTTP status code
|
|
59
|
+
detail: Error details (maps to DataknobsError.context)
|
|
60
|
+
error_code: Machine-readable error code
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
message: str,
|
|
66
|
+
status_code: int = 500,
|
|
67
|
+
detail: dict[str, Any] | None = None,
|
|
68
|
+
error_code: str | None = None,
|
|
69
|
+
):
|
|
70
|
+
"""Initialize API error.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
message: Human-readable error message
|
|
74
|
+
status_code: HTTP status code (default: 500)
|
|
75
|
+
detail: Optional dictionary with error details
|
|
76
|
+
error_code: Optional machine-readable error code
|
|
77
|
+
"""
|
|
78
|
+
# Pass detail as context to DataknobsError
|
|
79
|
+
super().__init__(message, context=detail)
|
|
80
|
+
self.status_code = status_code
|
|
81
|
+
self.error_code = error_code or self.__class__.__name__
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def detail(self) -> dict[str, Any]:
|
|
85
|
+
"""Alias for context to maintain API compatibility."""
|
|
86
|
+
return self.context
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> dict[str, Any]:
|
|
89
|
+
"""Convert error to dictionary for JSON response.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Dictionary representation of the error
|
|
93
|
+
"""
|
|
94
|
+
return {
|
|
95
|
+
"error": self.error_code,
|
|
96
|
+
"message": str(self),
|
|
97
|
+
"detail": self.context,
|
|
98
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class BotNotFoundError(APIError, CommonNotFoundError):
|
|
103
|
+
"""Exception raised when bot instance is not found."""
|
|
104
|
+
|
|
105
|
+
def __init__(self, bot_id: str):
|
|
106
|
+
APIError.__init__(
|
|
107
|
+
self,
|
|
108
|
+
message=f"Bot with ID '{bot_id}' not found",
|
|
109
|
+
status_code=404,
|
|
110
|
+
detail={"bot_id": bot_id},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class BotCreationError(APIError):
|
|
115
|
+
"""Exception raised when bot creation fails."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, bot_id: str, reason: str):
|
|
118
|
+
super().__init__(
|
|
119
|
+
message=f"Failed to create bot '{bot_id}': {reason}",
|
|
120
|
+
status_code=500,
|
|
121
|
+
detail={"bot_id": bot_id, "reason": reason},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ConversationNotFoundError(APIError, CommonNotFoundError):
|
|
126
|
+
"""Exception raised when conversation is not found."""
|
|
127
|
+
|
|
128
|
+
def __init__(self, conversation_id: str):
|
|
129
|
+
APIError.__init__(
|
|
130
|
+
self,
|
|
131
|
+
message=f"Conversation with ID '{conversation_id}' not found",
|
|
132
|
+
status_code=404,
|
|
133
|
+
detail={"conversation_id": conversation_id},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ValidationError(APIError, CommonValidationError):
|
|
138
|
+
"""Exception raised when input validation fails."""
|
|
139
|
+
|
|
140
|
+
def __init__(self, message: str, detail: dict[str, Any] | None = None):
|
|
141
|
+
APIError.__init__(
|
|
142
|
+
self,
|
|
143
|
+
message=message,
|
|
144
|
+
status_code=422,
|
|
145
|
+
detail=detail,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ConfigurationError(APIError, CommonConfigurationError):
|
|
150
|
+
"""Exception raised when configuration is invalid."""
|
|
151
|
+
|
|
152
|
+
def __init__(self, message: str, config_key: str | None = None):
|
|
153
|
+
detail = {}
|
|
154
|
+
if config_key:
|
|
155
|
+
detail["config_key"] = config_key
|
|
156
|
+
APIError.__init__(
|
|
157
|
+
self,
|
|
158
|
+
message=message,
|
|
159
|
+
status_code=500,
|
|
160
|
+
detail=detail,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class RateLimitError(APIError):
|
|
165
|
+
"""Exception raised when rate limit is exceeded."""
|
|
166
|
+
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
message: str = "Rate limit exceeded",
|
|
170
|
+
retry_after: int | None = None,
|
|
171
|
+
):
|
|
172
|
+
detail = {}
|
|
173
|
+
if retry_after:
|
|
174
|
+
detail["retry_after"] = retry_after
|
|
175
|
+
super().__init__(
|
|
176
|
+
message=message,
|
|
177
|
+
status_code=429,
|
|
178
|
+
detail=detail,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Exception Handlers
|
|
183
|
+
# Note: These use TYPE_CHECKING imports to avoid requiring FastAPI at import time
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
async def api_error_handler(
|
|
187
|
+
request: Request, # type: ignore[name-defined]
|
|
188
|
+
exc: APIError,
|
|
189
|
+
) -> JSONResponse: # type: ignore[name-defined]
|
|
190
|
+
"""Handle API errors with standardized response format.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
request: FastAPI request object
|
|
194
|
+
exc: API error exception
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
JSON response with error details
|
|
198
|
+
"""
|
|
199
|
+
from fastapi.responses import JSONResponse
|
|
200
|
+
|
|
201
|
+
return JSONResponse(
|
|
202
|
+
status_code=exc.status_code,
|
|
203
|
+
content=exc.to_dict(),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
async def http_exception_handler(
|
|
208
|
+
request: Request, # type: ignore[name-defined]
|
|
209
|
+
exc: HTTPException, # type: ignore[name-defined]
|
|
210
|
+
) -> JSONResponse: # type: ignore[name-defined]
|
|
211
|
+
"""Handle FastAPI HTTP exceptions.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
request: FastAPI request object
|
|
215
|
+
exc: HTTP exception
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
JSON response with error details
|
|
219
|
+
"""
|
|
220
|
+
from fastapi.responses import JSONResponse
|
|
221
|
+
|
|
222
|
+
return JSONResponse(
|
|
223
|
+
status_code=exc.status_code,
|
|
224
|
+
content={
|
|
225
|
+
"error": "HTTPException",
|
|
226
|
+
"message": str(exc.detail),
|
|
227
|
+
"detail": {},
|
|
228
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
229
|
+
},
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
async def general_exception_handler(
|
|
234
|
+
request: Request, # type: ignore[name-defined]
|
|
235
|
+
exc: Exception,
|
|
236
|
+
) -> JSONResponse: # type: ignore[name-defined]
|
|
237
|
+
"""Handle unexpected exceptions.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
request: FastAPI request object
|
|
241
|
+
exc: Generic exception
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
JSON response with error details
|
|
245
|
+
|
|
246
|
+
Note:
|
|
247
|
+
This handler logs the full exception but returns a generic
|
|
248
|
+
message to avoid leaking internal details.
|
|
249
|
+
"""
|
|
250
|
+
import logging
|
|
251
|
+
|
|
252
|
+
from fastapi.responses import JSONResponse
|
|
253
|
+
|
|
254
|
+
logger = logging.getLogger(__name__)
|
|
255
|
+
logger.exception(f"Unhandled exception: {exc}")
|
|
256
|
+
|
|
257
|
+
return JSONResponse(
|
|
258
|
+
status_code=500,
|
|
259
|
+
content={
|
|
260
|
+
"error": "InternalServerError",
|
|
261
|
+
"message": "An unexpected error occurred",
|
|
262
|
+
"detail": {"exception_type": type(exc).__name__},
|
|
263
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
264
|
+
},
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def register_exception_handlers(
|
|
269
|
+
app: FastAPI, # type: ignore[name-defined]
|
|
270
|
+
) -> None:
|
|
271
|
+
"""Register all exception handlers with a FastAPI app.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
app: FastAPI application instance
|
|
275
|
+
|
|
276
|
+
Example:
|
|
277
|
+
```python
|
|
278
|
+
from fastapi import FastAPI
|
|
279
|
+
from dataknobs_bots.api.exceptions import register_exception_handlers
|
|
280
|
+
|
|
281
|
+
app = FastAPI()
|
|
282
|
+
register_exception_handlers(app)
|
|
283
|
+
```
|
|
284
|
+
"""
|
|
285
|
+
from fastapi import HTTPException
|
|
286
|
+
|
|
287
|
+
app.add_exception_handler(APIError, api_error_handler) # type: ignore
|
|
288
|
+
app.add_exception_handler(HTTPException, http_exception_handler) # type: ignore
|
|
289
|
+
app.add_exception_handler(Exception, general_exception_handler)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Bot core components."""
|
|
2
|
+
|
|
3
|
+
from .base import DynaBot
|
|
4
|
+
from .context import BotContext
|
|
5
|
+
from .manager import BotManager
|
|
6
|
+
from .registry import BotRegistry, InMemoryBotRegistry, create_memory_registry
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"DynaBot",
|
|
10
|
+
"BotContext",
|
|
11
|
+
"BotManager",
|
|
12
|
+
"BotRegistry",
|
|
13
|
+
"InMemoryBotRegistry",
|
|
14
|
+
"create_memory_registry",
|
|
15
|
+
]
|