swiftagentx 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.
- swiftagent/__init__.py +69 -0
- swiftagent/admin/__init__.py +13 -0
- swiftagent/admin/fastapi_admin.py +96 -0
- swiftagent/admin/flask_admin.py +85 -0
- swiftagent/admin/service.py +254 -0
- swiftagent/core/__init__.py +21 -0
- swiftagent/core/agent.py +586 -0
- swiftagent/core/cache.py +248 -0
- swiftagent/core/log_context.py +39 -0
- swiftagent/core/memory.py +122 -0
- swiftagent/core/model_client.py +178 -0
- swiftagent/core/parameter.py +84 -0
- swiftagent/core/pipeline.py +127 -0
- swiftagent/core/prompt.py +157 -0
- swiftagent/core/router.py +136 -0
- swiftagent/knowledge_base/__init__.py +18 -0
- swiftagent/knowledge_base/base.py +69 -0
- swiftagent/knowledge_base/document.py +22 -0
- swiftagent/knowledge_base/memory.py +184 -0
- swiftagent/knowledge_base/stage.py +66 -0
- swiftagent/knowledge_base/tool.py +77 -0
- swiftagent/middleware/__init__.py +3 -0
- swiftagent/middleware/base.py +90 -0
- swiftagent/models/__init__.py +17 -0
- swiftagent/models/config.py +57 -0
- swiftagent/models/schema.py +226 -0
- swiftagent/providers/__init__.py +3 -0
- swiftagent/providers/openai_compatible.py +205 -0
- swiftagent/py.typed +0 -0
- swiftagent/storage/__init__.py +4 -0
- swiftagent/storage/base.py +32 -0
- swiftagent/storage/memory.py +57 -0
- swiftagent/stream/__init__.py +4 -0
- swiftagent/stream/adapter.py +88 -0
- swiftagent/stream/builder.py +125 -0
- swiftagent/tools/__init__.py +11 -0
- swiftagent/tools/base.py +100 -0
- swiftagent/tools/executor.py +99 -0
- swiftagent/tools/registry.py +70 -0
- swiftagent/tools/scenario.py +168 -0
- swiftagent/tools/termination.py +77 -0
- swiftagent/web/__init__.py +0 -0
- swiftagent/web/fastapi_adapter.py +71 -0
- swiftagent/web/flask_adapter.py +110 -0
- swiftagentx-0.1.0.dist-info/METADATA +834 -0
- swiftagentx-0.1.0.dist-info/RECORD +48 -0
- swiftagentx-0.1.0.dist-info/WHEEL +4 -0
- swiftagentx-0.1.0.dist-info/licenses/LICENSE +189 -0
swiftagent/__init__.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SwiftAgent — Enterprise-grade fast-response Agent framework.
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
- Dual-model strategy (light for classification, heavy for execution)
|
|
6
|
+
- Scenario toolchains (skip ReAct for high-frequency patterns)
|
|
7
|
+
- Three-level cache (KB / tool result / session)
|
|
8
|
+
- SSE streaming with fine-grained events
|
|
9
|
+
- Production-ready (middleware, tracing, exponential backoff)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .core.agent import Agent
|
|
13
|
+
from .core.model_client import ModelClient, ModelResponse, DummyModelClient, ModelClientFactory
|
|
14
|
+
from .core.memory import Message, SessionMemory
|
|
15
|
+
from .core.cache import CacheManager
|
|
16
|
+
from .core.prompt import PromptManager, PromptTemplate
|
|
17
|
+
from .core.router import IntentLevel, IntentResult, IntentRouter
|
|
18
|
+
from .core.pipeline import PipelineStage, StageResult, RequestPipeline
|
|
19
|
+
from .tools.base import Tool, ToolOutput, ToolOutputType, AgentContext
|
|
20
|
+
from .tools.registry import ToolRegistry
|
|
21
|
+
from .tools.executor import ToolExecutor
|
|
22
|
+
from .tools.scenario import ScenarioConfig, ToolChainStep, ScenarioEngine
|
|
23
|
+
from .stream.adapter import SSEStreamAdapter
|
|
24
|
+
from .stream.builder import SSEEventBuilder
|
|
25
|
+
from .models.schema import AgentRequest, AgentResponse, SessionContext
|
|
26
|
+
from .models.config import SwiftAgentConfig, ModelTier
|
|
27
|
+
from .middleware.base import Middleware, MiddlewareChain
|
|
28
|
+
from .storage.base import StorageBackend
|
|
29
|
+
from .storage.memory import MemoryStorage
|
|
30
|
+
from .knowledge_base.document import Document, SearchResult
|
|
31
|
+
from .knowledge_base.base import KnowledgeBase
|
|
32
|
+
from .knowledge_base.memory import MemoryKnowledgeBase
|
|
33
|
+
from .knowledge_base.tool import KnowledgeBaseTool
|
|
34
|
+
from .knowledge_base.stage import KnowledgeBaseStage
|
|
35
|
+
from .admin.service import AdminService
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Core
|
|
41
|
+
"Agent",
|
|
42
|
+
"ModelClient", "ModelResponse", "DummyModelClient", "ModelClientFactory",
|
|
43
|
+
"Message", "SessionMemory",
|
|
44
|
+
"CacheManager",
|
|
45
|
+
"PromptManager", "PromptTemplate",
|
|
46
|
+
"IntentLevel", "IntentResult", "IntentRouter",
|
|
47
|
+
"PipelineStage", "StageResult", "RequestPipeline",
|
|
48
|
+
# Tools
|
|
49
|
+
"Tool", "ToolOutput", "ToolOutputType", "AgentContext",
|
|
50
|
+
"ToolRegistry", "ToolExecutor",
|
|
51
|
+
"ScenarioConfig", "ToolChainStep", "ScenarioEngine",
|
|
52
|
+
# Stream
|
|
53
|
+
"SSEStreamAdapter", "SSEEventBuilder",
|
|
54
|
+
# Models
|
|
55
|
+
"AgentRequest", "AgentResponse", "SessionContext",
|
|
56
|
+
"SwiftAgentConfig", "ModelTier",
|
|
57
|
+
# Middleware
|
|
58
|
+
"Middleware", "MiddlewareChain",
|
|
59
|
+
# Storage
|
|
60
|
+
"StorageBackend", "MemoryStorage",
|
|
61
|
+
# Knowledge Base
|
|
62
|
+
"Document", "SearchResult",
|
|
63
|
+
"KnowledgeBase", "MemoryKnowledgeBase",
|
|
64
|
+
"KnowledgeBaseTool", "KnowledgeBaseStage",
|
|
65
|
+
# Admin
|
|
66
|
+
"AdminService",
|
|
67
|
+
# Version
|
|
68
|
+
"__version__",
|
|
69
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Admin module — management API for SwiftAgent.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .service import AdminService
|
|
6
|
+
from .flask_admin import create_flask_admin_blueprint
|
|
7
|
+
from .fastapi_admin import create_fastapi_admin_router
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AdminService",
|
|
11
|
+
"create_flask_admin_blueprint",
|
|
12
|
+
"create_fastapi_admin_router",
|
|
13
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Router for the admin API.
|
|
3
|
+
|
|
4
|
+
Usage::
|
|
5
|
+
|
|
6
|
+
from swiftagent.admin import AdminService, create_fastapi_admin_router
|
|
7
|
+
|
|
8
|
+
service = AdminService(agent)
|
|
9
|
+
router = create_fastapi_admin_router(service)
|
|
10
|
+
app.include_router(router, prefix="/admin")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .service import AdminService
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_fastapi_admin_router(
|
|
22
|
+
service: "AdminService",
|
|
23
|
+
prefix: str = "/admin",
|
|
24
|
+
tags: Optional[List[str]] = None,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Create a FastAPI APIRouter wired to *service*.
|
|
28
|
+
|
|
29
|
+
Returns a ``fastapi.APIRouter`` — include it in your FastAPI app.
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
from fastapi import APIRouter
|
|
33
|
+
from pydantic import BaseModel
|
|
34
|
+
except ImportError:
|
|
35
|
+
raise ImportError("FastAPI is required for the admin router: pip install fastapi")
|
|
36
|
+
|
|
37
|
+
router = APIRouter(prefix=prefix, tags=tags or ["admin"])
|
|
38
|
+
|
|
39
|
+
# -- Request models --
|
|
40
|
+
|
|
41
|
+
class CacheClearRequest(BaseModel):
|
|
42
|
+
level: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
class KBSearchRequest(BaseModel):
|
|
45
|
+
query: str
|
|
46
|
+
top_k: int = 5
|
|
47
|
+
|
|
48
|
+
class KBAddDocumentsRequest(BaseModel):
|
|
49
|
+
documents: List[Dict[str, Any]]
|
|
50
|
+
|
|
51
|
+
class ConfigUpdateRequest(BaseModel):
|
|
52
|
+
updates: Dict[str, Any] = {}
|
|
53
|
+
|
|
54
|
+
# -- Endpoints --
|
|
55
|
+
|
|
56
|
+
@router.get("/status")
|
|
57
|
+
async def status():
|
|
58
|
+
return service.get_status()
|
|
59
|
+
|
|
60
|
+
@router.get("/tools")
|
|
61
|
+
async def tools():
|
|
62
|
+
return service.get_tools()
|
|
63
|
+
|
|
64
|
+
@router.get("/cache/stats")
|
|
65
|
+
async def cache_stats():
|
|
66
|
+
return service.get_cache_stats()
|
|
67
|
+
|
|
68
|
+
@router.post("/cache/clear")
|
|
69
|
+
async def cache_clear(body: CacheClearRequest):
|
|
70
|
+
return service.clear_cache(body.level)
|
|
71
|
+
|
|
72
|
+
@router.get("/config")
|
|
73
|
+
async def get_config():
|
|
74
|
+
return service.get_config()
|
|
75
|
+
|
|
76
|
+
@router.put("/config")
|
|
77
|
+
async def update_config(body: ConfigUpdateRequest):
|
|
78
|
+
return service.update_config(body.updates)
|
|
79
|
+
|
|
80
|
+
@router.post("/kb/search")
|
|
81
|
+
async def kb_search(body: KBSearchRequest):
|
|
82
|
+
return await service.kb_search_async(body.query, top_k=body.top_k)
|
|
83
|
+
|
|
84
|
+
@router.post("/kb/documents")
|
|
85
|
+
async def kb_add_documents(body: KBAddDocumentsRequest):
|
|
86
|
+
return await service.kb_add_documents_async(body.documents)
|
|
87
|
+
|
|
88
|
+
@router.delete("/kb/documents/{doc_id}")
|
|
89
|
+
async def kb_delete_document(doc_id: str):
|
|
90
|
+
return await service.kb_delete_document_async(doc_id)
|
|
91
|
+
|
|
92
|
+
@router.get("/kb/stats")
|
|
93
|
+
async def kb_stats():
|
|
94
|
+
return await service.kb_stats_async()
|
|
95
|
+
|
|
96
|
+
return router
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flask Blueprint for the admin API.
|
|
3
|
+
|
|
4
|
+
Usage::
|
|
5
|
+
|
|
6
|
+
from swiftagent.admin import AdminService, create_flask_admin_blueprint
|
|
7
|
+
|
|
8
|
+
service = AdminService(agent)
|
|
9
|
+
bp = create_flask_admin_blueprint(service)
|
|
10
|
+
app.register_blueprint(bp, url_prefix="/admin")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .service import AdminService
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_flask_admin_blueprint(
|
|
22
|
+
service: "AdminService",
|
|
23
|
+
url_prefix: str = "/admin",
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Create a Flask Blueprint wired to *service*.
|
|
27
|
+
|
|
28
|
+
Returns a ``flask.Blueprint`` — register it with your Flask app.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
from flask import Blueprint, jsonify, request
|
|
32
|
+
except ImportError:
|
|
33
|
+
raise ImportError("Flask is required for the admin blueprint: pip install flask")
|
|
34
|
+
|
|
35
|
+
bp = Blueprint("swiftagent_admin", __name__, url_prefix=url_prefix)
|
|
36
|
+
|
|
37
|
+
@bp.route("/status", methods=["GET"])
|
|
38
|
+
def status():
|
|
39
|
+
return jsonify(service.get_status())
|
|
40
|
+
|
|
41
|
+
@bp.route("/tools", methods=["GET"])
|
|
42
|
+
def tools():
|
|
43
|
+
return jsonify(service.get_tools())
|
|
44
|
+
|
|
45
|
+
@bp.route("/cache/stats", methods=["GET"])
|
|
46
|
+
def cache_stats():
|
|
47
|
+
return jsonify(service.get_cache_stats())
|
|
48
|
+
|
|
49
|
+
@bp.route("/cache/clear", methods=["POST"])
|
|
50
|
+
def cache_clear():
|
|
51
|
+
body = request.get_json(silent=True) or {}
|
|
52
|
+
level = body.get("level")
|
|
53
|
+
return jsonify(service.clear_cache(level))
|
|
54
|
+
|
|
55
|
+
@bp.route("/config", methods=["GET"])
|
|
56
|
+
def get_config():
|
|
57
|
+
return jsonify(service.get_config())
|
|
58
|
+
|
|
59
|
+
@bp.route("/config", methods=["PUT"])
|
|
60
|
+
def update_config():
|
|
61
|
+
body = request.get_json(silent=True) or {}
|
|
62
|
+
return jsonify(service.update_config(body))
|
|
63
|
+
|
|
64
|
+
@bp.route("/kb/search", methods=["POST"])
|
|
65
|
+
def kb_search():
|
|
66
|
+
body = request.get_json(silent=True) or {}
|
|
67
|
+
query = body.get("query", "")
|
|
68
|
+
top_k = body.get("top_k", 5)
|
|
69
|
+
return jsonify(service.kb_search(query, top_k=top_k))
|
|
70
|
+
|
|
71
|
+
@bp.route("/kb/documents", methods=["POST"])
|
|
72
|
+
def kb_add_documents():
|
|
73
|
+
body = request.get_json(silent=True) or {}
|
|
74
|
+
documents = body.get("documents", [])
|
|
75
|
+
return jsonify(service.kb_add_documents(documents))
|
|
76
|
+
|
|
77
|
+
@bp.route("/kb/documents/<doc_id>", methods=["DELETE"])
|
|
78
|
+
def kb_delete_document(doc_id: str):
|
|
79
|
+
return jsonify(service.kb_delete_document(doc_id))
|
|
80
|
+
|
|
81
|
+
@bp.route("/kb/stats", methods=["GET"])
|
|
82
|
+
def kb_stats():
|
|
83
|
+
return jsonify(service.kb_stats())
|
|
84
|
+
|
|
85
|
+
return bp
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AdminService — framework-agnostic management logic for a SwiftAgent agent.
|
|
3
|
+
|
|
4
|
+
Provides status queries, configuration management, cache operations,
|
|
5
|
+
and knowledge base management. Web framework adapters (Flask, FastAPI)
|
|
6
|
+
call into this service layer.
|
|
7
|
+
|
|
8
|
+
WARNING: Admin endpoints expose agent internals. Always add authentication
|
|
9
|
+
middleware (Flask before_request / FastAPI Depends) in production.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import concurrent.futures
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
import time
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..core.agent import Agent
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Fields that may be updated at runtime via the admin API.
|
|
27
|
+
_MUTABLE_CONFIG_FIELDS = frozenset({
|
|
28
|
+
"max_iterations", "max_retries", "enable_cache",
|
|
29
|
+
"kb_cache_ttl", "code_cache_ttl", "max_input_length",
|
|
30
|
+
"sse_heartbeat_interval", "sse_timeout", "log_level",
|
|
31
|
+
"kb_exact_match_threshold", "debug",
|
|
32
|
+
"max_cache_entries_per_level", "enable_request_tracing",
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _run_sync(coro):
|
|
37
|
+
"""Run a coroutine synchronously, safe in both sync and async contexts."""
|
|
38
|
+
try:
|
|
39
|
+
asyncio.get_running_loop()
|
|
40
|
+
in_async = True
|
|
41
|
+
except RuntimeError:
|
|
42
|
+
in_async = False
|
|
43
|
+
|
|
44
|
+
if in_async:
|
|
45
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
46
|
+
return pool.submit(asyncio.run, coro).result()
|
|
47
|
+
else:
|
|
48
|
+
return asyncio.run(coro)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AdminService:
|
|
52
|
+
"""
|
|
53
|
+
Stateless admin operations backed by an Agent instance.
|
|
54
|
+
|
|
55
|
+
Usage::
|
|
56
|
+
|
|
57
|
+
service = AdminService(agent)
|
|
58
|
+
status = service.get_status()
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, agent: "Agent") -> None:
|
|
62
|
+
self.agent = agent
|
|
63
|
+
self._start_time = time.time()
|
|
64
|
+
|
|
65
|
+
# ---- Status ----
|
|
66
|
+
|
|
67
|
+
def get_status(self) -> Dict[str, Any]:
|
|
68
|
+
"""Agent name, tool count, cache stats, uptime."""
|
|
69
|
+
return {
|
|
70
|
+
"name": self.agent.name,
|
|
71
|
+
"tools": self.agent.tool_registry.list_tools(),
|
|
72
|
+
"tool_count": self.agent.tool_registry.count(),
|
|
73
|
+
"cache_stats": self.agent.cache.get_stats(),
|
|
74
|
+
"has_knowledge_base": self.agent.knowledge_base is not None,
|
|
75
|
+
"uptime_seconds": round(time.time() - self._start_time, 1),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def get_tools(self) -> List[Dict[str, Any]]:
|
|
79
|
+
"""Return JSON schema for every registered tool."""
|
|
80
|
+
return [
|
|
81
|
+
tool.get_schema()
|
|
82
|
+
for tool in self.agent.tool_registry.get_all().values()
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def get_cache_stats(self) -> Dict[str, Any]:
|
|
86
|
+
return self.agent.cache.get_stats()
|
|
87
|
+
|
|
88
|
+
# ---- Configuration ----
|
|
89
|
+
|
|
90
|
+
def get_config(self) -> Dict[str, Any]:
|
|
91
|
+
"""Return current config with sensitive values masked."""
|
|
92
|
+
raw = self.agent.config.model_dump()
|
|
93
|
+
return self._mask_secrets(raw)
|
|
94
|
+
|
|
95
|
+
def update_config(self, updates: Dict[str, Any]) -> Dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Update config fields at runtime.
|
|
98
|
+
|
|
99
|
+
Only whitelisted mutable fields are accepted; values are validated
|
|
100
|
+
through Pydantic by re-constructing the config model.
|
|
101
|
+
Returns the full (masked) config after the update.
|
|
102
|
+
"""
|
|
103
|
+
from ..models.config import SwiftAgentConfig
|
|
104
|
+
|
|
105
|
+
filtered = {k: v for k, v in updates.items() if k in _MUTABLE_CONFIG_FIELDS}
|
|
106
|
+
if not filtered:
|
|
107
|
+
return self.get_config()
|
|
108
|
+
|
|
109
|
+
current = self.agent.config.model_dump()
|
|
110
|
+
current.update(filtered)
|
|
111
|
+
try:
|
|
112
|
+
self.agent.config = SwiftAgentConfig(**current)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.warning(f"Config update rejected: {e}")
|
|
115
|
+
return {"error": f"Invalid config: {e}"}
|
|
116
|
+
|
|
117
|
+
return self.get_config()
|
|
118
|
+
|
|
119
|
+
# ---- Cache management ----
|
|
120
|
+
|
|
121
|
+
def clear_cache(self, level: Optional[str] = None) -> Dict[str, Any]:
|
|
122
|
+
"""
|
|
123
|
+
Clear cache entries.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
level: One of "level_1", "level_2", "level_3", "scenario", or None (all).
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Confirmation dict.
|
|
130
|
+
"""
|
|
131
|
+
if level is None:
|
|
132
|
+
self.agent.cache.clear_all()
|
|
133
|
+
return {"cleared": "all"}
|
|
134
|
+
|
|
135
|
+
cache = self.agent.cache
|
|
136
|
+
if level == "level_1":
|
|
137
|
+
cache.level_1_cache.clear()
|
|
138
|
+
elif level == "level_2":
|
|
139
|
+
cache.level_2_cache.clear()
|
|
140
|
+
elif level == "level_3":
|
|
141
|
+
cache.level_3_cache.clear()
|
|
142
|
+
elif level == "scenario":
|
|
143
|
+
cache.clear_scenario_cache()
|
|
144
|
+
else:
|
|
145
|
+
return {"error": f"Unknown cache level: {level}"}
|
|
146
|
+
|
|
147
|
+
return {"cleared": level}
|
|
148
|
+
|
|
149
|
+
# ---- Knowledge base (sync wrappers) ----
|
|
150
|
+
|
|
151
|
+
def kb_search(self, query: str, top_k: int = 5) -> List[Dict[str, Any]]:
|
|
152
|
+
"""Synchronous wrapper — safe in both sync and async contexts."""
|
|
153
|
+
kb = self.agent.knowledge_base
|
|
154
|
+
if kb is None:
|
|
155
|
+
return []
|
|
156
|
+
results = _run_sync(kb.search(query, top_k=top_k))
|
|
157
|
+
return self._format_search_results(results)
|
|
158
|
+
|
|
159
|
+
def kb_add_documents(self, documents: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
160
|
+
kb = self.agent.knowledge_base
|
|
161
|
+
if kb is None:
|
|
162
|
+
return {"error": "No knowledge base configured"}
|
|
163
|
+
from ..knowledge_base.document import Document
|
|
164
|
+
try:
|
|
165
|
+
docs = [Document(**d) for d in documents]
|
|
166
|
+
except Exception as e:
|
|
167
|
+
return {"error": f"Invalid document data: {e}"}
|
|
168
|
+
added = _run_sync(kb.add_documents(docs))
|
|
169
|
+
return {"added": added}
|
|
170
|
+
|
|
171
|
+
def kb_delete_document(self, doc_id: str) -> Dict[str, Any]:
|
|
172
|
+
kb = self.agent.knowledge_base
|
|
173
|
+
if kb is None:
|
|
174
|
+
return {"error": "No knowledge base configured"}
|
|
175
|
+
deleted = _run_sync(kb.delete_document(doc_id))
|
|
176
|
+
return {"deleted": deleted, "doc_id": doc_id}
|
|
177
|
+
|
|
178
|
+
def kb_stats(self) -> Dict[str, Any]:
|
|
179
|
+
kb = self.agent.knowledge_base
|
|
180
|
+
if kb is None:
|
|
181
|
+
return {"error": "No knowledge base configured"}
|
|
182
|
+
count = _run_sync(kb.count())
|
|
183
|
+
return {"document_count": count, "provider": type(kb).__name__}
|
|
184
|
+
|
|
185
|
+
# ---- Knowledge base (async) ----
|
|
186
|
+
|
|
187
|
+
async def kb_search_async(self, query: str, top_k: int = 5) -> List[Dict[str, Any]]:
|
|
188
|
+
kb = self.agent.knowledge_base
|
|
189
|
+
if kb is None:
|
|
190
|
+
return []
|
|
191
|
+
results = await kb.search(query, top_k=top_k)
|
|
192
|
+
return self._format_search_results(results)
|
|
193
|
+
|
|
194
|
+
async def kb_add_documents_async(self, documents: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
195
|
+
kb = self.agent.knowledge_base
|
|
196
|
+
if kb is None:
|
|
197
|
+
return {"error": "No knowledge base configured"}
|
|
198
|
+
from ..knowledge_base.document import Document
|
|
199
|
+
try:
|
|
200
|
+
docs = [Document(**d) for d in documents]
|
|
201
|
+
except Exception as e:
|
|
202
|
+
return {"error": f"Invalid document data: {e}"}
|
|
203
|
+
added = await kb.add_documents(docs)
|
|
204
|
+
return {"added": added}
|
|
205
|
+
|
|
206
|
+
async def kb_delete_document_async(self, doc_id: str) -> Dict[str, Any]:
|
|
207
|
+
kb = self.agent.knowledge_base
|
|
208
|
+
if kb is None:
|
|
209
|
+
return {"error": "No knowledge base configured"}
|
|
210
|
+
deleted = await kb.delete_document(doc_id)
|
|
211
|
+
return {"deleted": deleted, "doc_id": doc_id}
|
|
212
|
+
|
|
213
|
+
async def kb_stats_async(self) -> Dict[str, Any]:
|
|
214
|
+
kb = self.agent.knowledge_base
|
|
215
|
+
if kb is None:
|
|
216
|
+
return {"error": "No knowledge base configured"}
|
|
217
|
+
count = await kb.count()
|
|
218
|
+
return {"document_count": count, "provider": type(kb).__name__}
|
|
219
|
+
|
|
220
|
+
# ---- Helpers ----
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def _format_search_results(results) -> List[Dict[str, Any]]:
|
|
224
|
+
return [
|
|
225
|
+
{
|
|
226
|
+
"doc_id": r.document.doc_id,
|
|
227
|
+
"content": r.document.content,
|
|
228
|
+
"score": r.score,
|
|
229
|
+
"match_type": r.match_type,
|
|
230
|
+
"metadata": r.document.metadata,
|
|
231
|
+
}
|
|
232
|
+
for r in results
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def _mask_secrets(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
237
|
+
"""Mask values that look like API keys or secrets."""
|
|
238
|
+
masked = {}
|
|
239
|
+
secret_pattern = re.compile(
|
|
240
|
+
r"(key|secret|token|password|credential)", re.IGNORECASE
|
|
241
|
+
)
|
|
242
|
+
for key, value in data.items():
|
|
243
|
+
if isinstance(value, str) and secret_pattern.search(key) and len(value) > 4:
|
|
244
|
+
masked[key] = value[:4] + "***"
|
|
245
|
+
elif isinstance(value, dict):
|
|
246
|
+
masked[key] = AdminService._mask_secrets(value)
|
|
247
|
+
elif isinstance(value, list):
|
|
248
|
+
masked[key] = [
|
|
249
|
+
AdminService._mask_secrets(item) if isinstance(item, dict) else item
|
|
250
|
+
for item in value
|
|
251
|
+
]
|
|
252
|
+
else:
|
|
253
|
+
masked[key] = value
|
|
254
|
+
return masked
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .memory import Message, SessionMemory
|
|
2
|
+
from .model_client import ModelResponse, ModelClient, ModelClientFactory
|
|
3
|
+
from .cache import CacheEntry, CacheManager
|
|
4
|
+
from .prompt import PromptTemplate, PromptManager
|
|
5
|
+
from .parameter import ParameterManager, get_parameter_manager
|
|
6
|
+
from .log_context import set_request_id, get_request_id, RequestIdFilter
|
|
7
|
+
from .agent import Agent
|
|
8
|
+
from .router import IntentLevel, IntentResult, IntentRouter
|
|
9
|
+
from .pipeline import PipelineStage, StageResult, RequestPipeline
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Message", "SessionMemory",
|
|
13
|
+
"ModelResponse", "ModelClient", "ModelClientFactory",
|
|
14
|
+
"CacheEntry", "CacheManager",
|
|
15
|
+
"PromptTemplate", "PromptManager",
|
|
16
|
+
"ParameterManager", "get_parameter_manager",
|
|
17
|
+
"set_request_id", "get_request_id", "RequestIdFilter",
|
|
18
|
+
"Agent",
|
|
19
|
+
"IntentLevel", "IntentResult", "IntentRouter",
|
|
20
|
+
"PipelineStage", "StageResult", "RequestPipeline",
|
|
21
|
+
]
|