agentkernel 0.2.5__tar.gz → 0.2.7__tar.gz
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.
- {agentkernel-0.2.5 → agentkernel-0.2.7}/PKG-INFO +4 -2
- {agentkernel-0.2.5 → agentkernel-0.2.7}/pyproject.toml +5 -3
- agentkernel-0.2.7/src/agentkernel/adk.py +8 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/api/__init__.py +2 -2
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/api}/a2a/a2a.py +2 -2
- agentkernel-0.2.5/src/agentkernel/api/a2a.py → agentkernel-0.2.7/src/agentkernel/api/a2a/handler.py +1 -1
- agentkernel-0.2.7/src/agentkernel/api/handler.py +131 -0
- agentkernel-0.2.5/src/agentkernel/api/restapi.py → agentkernel-0.2.7/src/agentkernel/api/http.py +4 -5
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/api}/mcp/akmcp.py +2 -2
- agentkernel-0.2.7/src/agentkernel/aws.py +8 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/__init__.py +12 -1
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/base.py +52 -3
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/builder.py +14 -6
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/config.py +12 -1
- agentkernel-0.2.7/src/agentkernel/core/hooks.py +74 -0
- agentkernel-0.2.7/src/agentkernel/core/model.py +106 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/runtime.py +121 -6
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/service.py +38 -5
- {agentkernel-0.2.5/src/agentkernel/core/sessions → agentkernel-0.2.7/src/agentkernel/core/session}/__init__.py +0 -2
- agentkernel-0.2.7/src/agentkernel/core/session/dynamodb.py +247 -0
- {agentkernel-0.2.5/src/agentkernel/core/sessions → agentkernel-0.2.7/src/agentkernel/core/session}/in_memory.py +1 -1
- {agentkernel-0.2.5/src/agentkernel/core/sessions → agentkernel-0.2.7/src/agentkernel/core/session}/redis.py +8 -42
- agentkernel-0.2.7/src/agentkernel/core/session/serde.py +38 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/util/config_yaml_util.py +6 -9
- agentkernel-0.2.7/src/agentkernel/core/util/key_value_cache.py +120 -0
- agentkernel-0.2.7/src/agentkernel/crewai.py +8 -0
- agentkernel-0.2.7/src/agentkernel/deployment/aws/aklambda.py +103 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/adk/adk.py +36 -15
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/crewai/crewai.py +33 -8
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/langgraph/langgraph.py +32 -12
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/openai/openai.py +35 -11
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/messenger/README.md +1 -1
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/slack/README.md +2 -2
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/slack/__init__.py +1 -1
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/whatsapp/README.md +3 -3
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/whatsapp/__init__.py +1 -1
- agentkernel-0.2.7/src/agentkernel/langgraph.py +8 -0
- agentkernel-0.2.7/src/agentkernel/mcp.py +8 -0
- agentkernel-0.2.7/src/agentkernel/messenger.py +8 -0
- agentkernel-0.2.7/src/agentkernel/openai.py +8 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/slack.py +1 -1
- agentkernel-0.2.7/src/agentkernel/trace/langfuse/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/langfuse/adk.py +7 -5
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/langfuse/crewai.py +7 -5
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/langfuse/langgraph.py +27 -8
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/langfuse/openai.py +7 -5
- agentkernel-0.2.7/src/agentkernel/trace/openllmetry/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/whatsapp.py +1 -1
- agentkernel-0.2.5/src/agentkernel/api/agent.py +0 -93
- agentkernel-0.2.5/src/agentkernel/api/rest_request_handler.py +0 -26
- agentkernel-0.2.5/src/agentkernel/aws/aklambda.py +0 -93
- agentkernel-0.2.5/src/agentkernel/messenger.py +0 -8
- {agentkernel-0.2.5 → agentkernel-0.2.7}/README.md +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/api}/a2a/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/api}/mcp/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/cli/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/cli/cli.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/core/module.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/core/sessions → agentkernel-0.2.7/src/agentkernel/core/session}/base.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/trace/langfuse → agentkernel-0.2.7/src/agentkernel/deployment}/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/deployment}/aws/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/trace/openllmetry → agentkernel-0.2.7/src/agentkernel/framework}/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/adk/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/crewai/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/langgraph/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel → agentkernel-0.2.7/src/agentkernel/framework}/openai/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/messenger/__init__.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/messenger/messenger_chat.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/slack/slack_chat.py +0 -0
- {agentkernel-0.2.5/src/agentkernel/integrations → agentkernel-0.2.7/src/agentkernel/integration}/whatsapp/whatsapp_chat.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/test/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/test/test.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/__init__.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/base.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/langfuse/langfuse.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/openllmetry/adk.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/openllmetry/crewai.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/openllmetry/langgraph.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/openllmetry/openai.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/openllmetry/openllmetry.py +0 -0
- {agentkernel-0.2.5 → agentkernel-0.2.7}/src/agentkernel/trace/trace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: agentkernel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Agent Kernel - Unified AI Agents Runtime
|
|
5
5
|
Author: Yaala Labs
|
|
6
6
|
Author-email: Yaala Labs <agentkernel@yaalalabs.com>
|
|
@@ -10,7 +10,6 @@ Requires-Dist: pydantic>=2.11.7
|
|
|
10
10
|
Requires-Dist: pydantic-settings>=2.10.1
|
|
11
11
|
Requires-Dist: pyyaml>=6.0.2
|
|
12
12
|
Requires-Dist: singleton-type>=0.0.5
|
|
13
|
-
Requires-Dist: redis>=6.4.0
|
|
14
13
|
Requires-Dist: a2a-sdk[http-server]>=0.3.6 ; extra == 'a2a'
|
|
15
14
|
Requires-Dist: google-adk>=1.14.1 ; extra == 'adk'
|
|
16
15
|
Requires-Dist: litellm~=1.74.3 ; extra == 'adk'
|
|
@@ -18,6 +17,7 @@ Requires-Dist: openinference-instrumentation-google-adk>=0.1.6 ; extra == 'adk'
|
|
|
18
17
|
Requires-Dist: fastapi>=0.118.0 ; extra == 'api'
|
|
19
18
|
Requires-Dist: uvicorn>=0.37.0 ; extra == 'api'
|
|
20
19
|
Requires-Dist: gunicorn>=23.0.0 ; extra == 'api'
|
|
20
|
+
Requires-Dist: boto3>=1.41.4 ; extra == 'aws'
|
|
21
21
|
Requires-Dist: crewai>=0.150.0 ; extra == 'crewai'
|
|
22
22
|
Requires-Dist: openinference-instrumentation-crewai>=0.1.16 ; extra == 'crewai'
|
|
23
23
|
Requires-Dist: openinference-instrumentation-litellm>=0.1.28 ; extra == 'crewai'
|
|
@@ -32,6 +32,7 @@ Requires-Dist: httpx>=0.27.0 ; extra == 'messenger'
|
|
|
32
32
|
Requires-Dist: openai-agents>=0.2.3 ; extra == 'openai'
|
|
33
33
|
Requires-Dist: openinference-instrumentation-openai-agents>=1.3.0 ; extra == 'openai'
|
|
34
34
|
Requires-Dist: traceloop-sdk>=0.48.0 ; extra == 'openllmetry'
|
|
35
|
+
Requires-Dist: redis>=7.1.0 ; extra == 'redis'
|
|
35
36
|
Requires-Dist: slack-bolt==1.22.0 ; extra == 'slack'
|
|
36
37
|
Requires-Dist: pytest>=8.4.1 ; extra == 'test'
|
|
37
38
|
Requires-Dist: pytest-asyncio>=1.2.0 ; extra == 'test'
|
|
@@ -53,6 +54,7 @@ Provides-Extra: mcp
|
|
|
53
54
|
Provides-Extra: messenger
|
|
54
55
|
Provides-Extra: openai
|
|
55
56
|
Provides-Extra: openllmetry
|
|
57
|
+
Provides-Extra: redis
|
|
56
58
|
Provides-Extra: slack
|
|
57
59
|
Provides-Extra: test
|
|
58
60
|
Provides-Extra: whatsapp
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentkernel"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.7"
|
|
8
8
|
description = "Agent Kernel - Unified AI Agents Runtime"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -18,7 +18,6 @@ dependencies = [
|
|
|
18
18
|
"pydantic-settings>=2.10.1",
|
|
19
19
|
"pyyaml>=6.0.2",
|
|
20
20
|
"singleton-type>=0.0.5",
|
|
21
|
-
"redis>=6.4.0",
|
|
22
21
|
]
|
|
23
22
|
|
|
24
23
|
[project.optional-dependencies]
|
|
@@ -44,7 +43,10 @@ cli = [
|
|
|
44
43
|
|
|
45
44
|
]
|
|
46
45
|
aws = [
|
|
47
|
-
|
|
46
|
+
"boto3>=1.41.4",
|
|
47
|
+
]
|
|
48
|
+
redis = [
|
|
49
|
+
"redis>=7.1.0",
|
|
48
50
|
]
|
|
49
51
|
openai = [
|
|
50
52
|
"openai-agents>=0.2.3",
|
|
@@ -11,5 +11,5 @@ try:
|
|
|
11
11
|
except importlib.metadata.PackageNotFoundError:
|
|
12
12
|
__version__ = "0.1.0"
|
|
13
13
|
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
14
|
+
from .handler import AgentRESTRequestHandler, RESTRequestHandler
|
|
15
|
+
from .http import RESTAPI
|
|
@@ -9,8 +9,8 @@ from a2a.types import AgentCard, InternalError, UnsupportedOperationError
|
|
|
9
9
|
from a2a.utils import new_agent_text_message
|
|
10
10
|
from a2a.utils.errors import ServerError
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from ...core import Agent, AgentService, GlobalRuntime
|
|
13
|
+
from ...core.config import AKConfig
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class A2A:
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import traceback
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from http import HTTPStatus
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, HTTPException
|
|
8
|
+
from pydantic import BaseModel, ConfigDict
|
|
9
|
+
|
|
10
|
+
from agentkernel.core.model import AgentReplyImage, AgentReplyText, AgentRequestAny, AgentRequestText
|
|
11
|
+
|
|
12
|
+
from ..core import AgentService, GlobalRuntime
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RESTRequestHandler(ABC):
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def get_router(self) -> APIRouter:
|
|
18
|
+
"""
|
|
19
|
+
Returns the APIRouter instance which has configured routes
|
|
20
|
+
E.g.:
|
|
21
|
+
- GET /health: Health check
|
|
22
|
+
- GET /agents: List available agents
|
|
23
|
+
|
|
24
|
+
router = APIRouter()
|
|
25
|
+
|
|
26
|
+
@router.get("/health")
|
|
27
|
+
def health():
|
|
28
|
+
return {"status": "ok"}
|
|
29
|
+
|
|
30
|
+
@router.get("/agents")
|
|
31
|
+
def list_agents():
|
|
32
|
+
return {"agents": list(GlobalRuntime.instance().agents().keys())}
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AgentRESTRequestHandler(RESTRequestHandler):
|
|
39
|
+
"""
|
|
40
|
+
API routers that expose endpoints to interact with Agent Kernel.
|
|
41
|
+
Endpoints:
|
|
42
|
+
- GET /health: Health check
|
|
43
|
+
- GET /agents: List available agents
|
|
44
|
+
- POST /run: Run an agent with a prompt
|
|
45
|
+
Payload JSON: { "prompt": str, "agent": str | null, "session_id": str | null }
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self):
|
|
49
|
+
self._log = logging.getLogger("ak.api.agent")
|
|
50
|
+
|
|
51
|
+
class RunRequest(BaseModel):
|
|
52
|
+
model_config = ConfigDict(extra="allow")
|
|
53
|
+
|
|
54
|
+
prompt: str
|
|
55
|
+
agent: Optional[str] = None
|
|
56
|
+
session_id: Optional[str] = None
|
|
57
|
+
|
|
58
|
+
def get_router(self) -> APIRouter:
|
|
59
|
+
"""
|
|
60
|
+
Returns the APIRouter instance.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
router = APIRouter()
|
|
64
|
+
|
|
65
|
+
@router.get("/health")
|
|
66
|
+
def health():
|
|
67
|
+
return {"status": "ok"}
|
|
68
|
+
|
|
69
|
+
@router.get("/agents")
|
|
70
|
+
def list_agents():
|
|
71
|
+
return {"agents": list(GlobalRuntime.instance().agents().keys())}
|
|
72
|
+
|
|
73
|
+
@router.post("/run")
|
|
74
|
+
async def run(body: AgentRESTRequestHandler.RunRequest):
|
|
75
|
+
return await self.run(body)
|
|
76
|
+
|
|
77
|
+
return router
|
|
78
|
+
|
|
79
|
+
async def run(self, req: RunRequest):
|
|
80
|
+
"""
|
|
81
|
+
Async method to run the agent.
|
|
82
|
+
:param req: Request object containing the prompt, optional agent name, and additional properties.
|
|
83
|
+
"""
|
|
84
|
+
requests = []
|
|
85
|
+
requests.append(AgentRequestText(text=req.prompt))
|
|
86
|
+
|
|
87
|
+
# Pack additional properties into AgentRequestAny
|
|
88
|
+
known_fields = {"prompt", "agent", "session_id"}
|
|
89
|
+
for key, value in req.model_dump().items():
|
|
90
|
+
if key not in known_fields:
|
|
91
|
+
self._log.debug(f"Adding additional context: {key}={value}")
|
|
92
|
+
requests.append(AgentRequestAny(name=key, content=value))
|
|
93
|
+
service = AgentService()
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
service.select(req.session_id, req.agent)
|
|
97
|
+
if not service.agent:
|
|
98
|
+
raise ValueError("No agent available")
|
|
99
|
+
|
|
100
|
+
result = await service.run_multi(requests=requests)
|
|
101
|
+
self._log.debug(f"Result: {result}")
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
"result": (
|
|
105
|
+
str(result)
|
|
106
|
+
if isinstance(result, (AgentReplyText, AgentReplyImage))
|
|
107
|
+
else "Non textual result received"
|
|
108
|
+
), # sending image is not supported at the moment
|
|
109
|
+
"session_id": service.get_response_session_id(req.session_id),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
except HTTPException:
|
|
113
|
+
raise
|
|
114
|
+
except ValueError as e:
|
|
115
|
+
self._log.error(f"POST /run error: {e}\n{traceback.format_exc()}")
|
|
116
|
+
raise HTTPException(
|
|
117
|
+
status_code=HTTPStatus.BAD_REQUEST,
|
|
118
|
+
detail={
|
|
119
|
+
"error": str(e),
|
|
120
|
+
"session_id": service.get_response_session_id(req.session_id),
|
|
121
|
+
},
|
|
122
|
+
)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
self._log.error(f"POST /run error: {e}\n{traceback.format_exc()}")
|
|
125
|
+
raise HTTPException(
|
|
126
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
127
|
+
detail={
|
|
128
|
+
"error": str(e),
|
|
129
|
+
"session_id": service.get_response_session_id(None),
|
|
130
|
+
},
|
|
131
|
+
)
|
agentkernel-0.2.5/src/agentkernel/api/restapi.py → agentkernel-0.2.7/src/agentkernel/api/http.py
RENAMED
|
@@ -5,9 +5,8 @@ from fastapi import APIRouter, FastAPI
|
|
|
5
5
|
from fastapi.middleware.cors import CORSMiddleware
|
|
6
6
|
from fastapi.openapi.utils import get_openapi
|
|
7
7
|
|
|
8
|
-
from ..api.rest_request_handler import RESTRequestHandler
|
|
9
8
|
from ..core.config import AKConfig
|
|
10
|
-
from .
|
|
9
|
+
from .handler import AgentRESTRequestHandler, RESTRequestHandler
|
|
11
10
|
|
|
12
11
|
logging.basicConfig(
|
|
13
12
|
level=logging.DEBUG,
|
|
@@ -22,7 +21,7 @@ class RESTAPI:
|
|
|
22
21
|
Can run any FastAPI app instance or assemble one from routers.
|
|
23
22
|
"""
|
|
24
23
|
|
|
25
|
-
_log = logging.getLogger("ak.api.
|
|
24
|
+
_log = logging.getLogger("ak.api.http")
|
|
26
25
|
_custom_routers = []
|
|
27
26
|
|
|
28
27
|
@classmethod
|
|
@@ -79,12 +78,12 @@ class RESTAPI:
|
|
|
79
78
|
routers.append(handler.get_router())
|
|
80
79
|
|
|
81
80
|
if AKConfig.get().a2a.enabled:
|
|
82
|
-
from .a2a import A2ARESTRequestHandler
|
|
81
|
+
from .a2a.handler import A2ARESTRequestHandler
|
|
83
82
|
|
|
84
83
|
routers.append(A2ARESTRequestHandler.get_catalog_router())
|
|
85
84
|
routers.extend(A2ARESTRequestHandler.get_agent_routers())
|
|
86
85
|
if AKConfig.get().mcp.enabled:
|
|
87
|
-
from
|
|
86
|
+
from .mcp.akmcp import MCP
|
|
88
87
|
|
|
89
88
|
mcp_app = MCP.get_http_app()
|
|
90
89
|
app = cls._create_app(routers=routers, lifespan=mcp_app.lifespan)
|
|
@@ -4,8 +4,8 @@ from typing import Any
|
|
|
4
4
|
from fastmcp import Context, FastMCP
|
|
5
5
|
from fastmcp.server.http import StarletteWithLifespan
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
7
|
+
from ...core import Agent, AgentService, GlobalRuntime
|
|
8
|
+
from ...core.config import AKConfig
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class MCP:
|
|
@@ -12,8 +12,19 @@ except importlib.metadata.PackageNotFoundError:
|
|
|
12
12
|
__version__ = "0.1.0"
|
|
13
13
|
|
|
14
14
|
from .base import Agent, Runner, Session
|
|
15
|
+
from .model import (
|
|
16
|
+
AgentRequest,
|
|
17
|
+
AgentRequestAny,
|
|
18
|
+
AgentRequestFile,
|
|
19
|
+
AgentRequestImage,
|
|
20
|
+
AgentRequestText,
|
|
21
|
+
AgentReply,
|
|
22
|
+
AgentReplyText,
|
|
23
|
+
AgentReplyImage,
|
|
24
|
+
)
|
|
15
25
|
from .config import AKConfig as Config
|
|
16
26
|
from .module import Module
|
|
17
27
|
from .runtime import GlobalRuntime, Runtime
|
|
18
28
|
from .service import AgentService
|
|
19
|
-
from .
|
|
29
|
+
from .hooks import Prehook, Posthook
|
|
30
|
+
from .util.key_value_cache import KeyValueCache
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import contextvars
|
|
1
2
|
import logging
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
3
4
|
from typing import Any, List
|
|
4
5
|
|
|
5
6
|
from .config import AKConfig
|
|
7
|
+
from .model import AgentReply, AgentRequest
|
|
8
|
+
from .util.key_value_cache import KeyValueCache
|
|
9
|
+
|
|
10
|
+
current_session = contextvars.ContextVar("session_id", default="")
|
|
6
11
|
|
|
7
12
|
|
|
8
13
|
class Session:
|
|
@@ -17,6 +22,31 @@ class Session:
|
|
|
17
22
|
the runtime configuration.
|
|
18
23
|
"""
|
|
19
24
|
|
|
25
|
+
VOLATILE_CACHE_KEY = "v_cache"
|
|
26
|
+
NON_VOLATILE_CACHE_KEY = "nv_cache"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_current_session_id(cls) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Returns the current session ID from the context variable.
|
|
32
|
+
:return: The current session ID.
|
|
33
|
+
"""
|
|
34
|
+
return current_session.get()
|
|
35
|
+
|
|
36
|
+
def get_volatile_cache(self) -> KeyValueCache:
|
|
37
|
+
"""
|
|
38
|
+
Returns the volatile key-value cache associated with this session.
|
|
39
|
+
:return: The volatile KeyValueCache instance.
|
|
40
|
+
"""
|
|
41
|
+
return self.get(self.VOLATILE_CACHE_KEY)
|
|
42
|
+
|
|
43
|
+
def get_non_volatile_cache(self) -> KeyValueCache:
|
|
44
|
+
"""
|
|
45
|
+
Returns the non-volatile key-value cache associated with this session.
|
|
46
|
+
:return: The non-volatile KeyValueCache instance.
|
|
47
|
+
"""
|
|
48
|
+
return self.get(self.NON_VOLATILE_CACHE_KEY)
|
|
49
|
+
|
|
20
50
|
def __init__(self, id: str):
|
|
21
51
|
"""
|
|
22
52
|
Initializes a Session instance.
|
|
@@ -26,6 +56,12 @@ class Session:
|
|
|
26
56
|
self._id = id
|
|
27
57
|
self._data = {}
|
|
28
58
|
|
|
59
|
+
# Pre-initialize key-value caches to be used by application code which will not be part of the agent context
|
|
60
|
+
self.set(self.VOLATILE_CACHE_KEY, KeyValueCache())
|
|
61
|
+
self.set(self.NON_VOLATILE_CACHE_KEY, KeyValueCache())
|
|
62
|
+
|
|
63
|
+
self._token = None
|
|
64
|
+
|
|
29
65
|
@property
|
|
30
66
|
def id(self) -> str:
|
|
31
67
|
"""
|
|
@@ -62,6 +98,19 @@ class Session:
|
|
|
62
98
|
self._data[key] = value
|
|
63
99
|
return value
|
|
64
100
|
|
|
101
|
+
def set_context(self):
|
|
102
|
+
"""
|
|
103
|
+
Sets the current session context variable to this session's ID.
|
|
104
|
+
"""
|
|
105
|
+
self._token = current_session.set(self._id)
|
|
106
|
+
|
|
107
|
+
def reset_context(self):
|
|
108
|
+
"""
|
|
109
|
+
Resets the current session context variable to the previous value.
|
|
110
|
+
"""
|
|
111
|
+
if self._token:
|
|
112
|
+
current_session.reset(self._token)
|
|
113
|
+
|
|
65
114
|
|
|
66
115
|
class Runner(ABC):
|
|
67
116
|
"""
|
|
@@ -88,12 +137,12 @@ class Runner(ABC):
|
|
|
88
137
|
return self._name
|
|
89
138
|
|
|
90
139
|
@abstractmethod
|
|
91
|
-
async def run(self, agent: Any, session: Session,
|
|
140
|
+
async def run(self, agent: Any, session: Session, requests: list[AgentRequest]) -> AgentReply:
|
|
92
141
|
"""
|
|
93
|
-
Runs the agent with the provided
|
|
142
|
+
Runs the agent with the provided multi modal inputs.
|
|
94
143
|
:param agent: The agent to run.
|
|
95
144
|
:param session: The session to use for the agent.
|
|
96
|
-
:param
|
|
145
|
+
:param requests: The list of requests to provide to the agent.
|
|
97
146
|
:return: The result of the agent's execution.
|
|
98
147
|
"""
|
|
99
148
|
pass
|
|
@@ -2,9 +2,7 @@ import logging
|
|
|
2
2
|
from enum import StrEnum
|
|
3
3
|
|
|
4
4
|
from .config import AKConfig
|
|
5
|
-
from .
|
|
6
|
-
from .sessions.in_memory import InMemorySessionStore
|
|
7
|
-
from .sessions.redis import RedisDriver, RedisSessionStore
|
|
5
|
+
from .session import SessionStore
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class Builder:
|
|
@@ -29,6 +27,7 @@ class SessionStoreType(StrEnum):
|
|
|
29
27
|
|
|
30
28
|
IN_MEMORY = "IN_MEMORY"
|
|
31
29
|
REDIS = "REDIS"
|
|
30
|
+
DYNAMODB = "DYNAMODB"
|
|
32
31
|
|
|
33
32
|
@classmethod
|
|
34
33
|
def from_str(cls, type_str: str) -> "SessionStoreType":
|
|
@@ -67,9 +66,10 @@ class SessionStoreBuilder(Builder):
|
|
|
67
66
|
|
|
68
67
|
This static method reads the session store type from the application configuration
|
|
69
68
|
and instantiates the appropriate SessionStore implementation. Currently supports
|
|
70
|
-
Redis-backed and in-memory session stores.
|
|
69
|
+
Redis-backed, DynamoDB-backed, and in-memory session stores.
|
|
71
70
|
|
|
72
|
-
:returns: An instance of
|
|
71
|
+
:returns: An instance of RedisSessionStore (if configured type is REDIS),
|
|
72
|
+
DynamoDBSessionStore (if configured type is DYNAMODB),
|
|
73
73
|
or InMemorySessionStore (for all other types).
|
|
74
74
|
|
|
75
75
|
:raises: Any exceptions raised by SessionStoreType.from_str(), AKConfig.get(),
|
|
@@ -79,6 +79,14 @@ class SessionStoreBuilder(Builder):
|
|
|
79
79
|
|
|
80
80
|
Builder._log.info(f"Building {session_store_type} session store")
|
|
81
81
|
if session_store_type == SessionStoreType.REDIS:
|
|
82
|
-
|
|
82
|
+
from .session.redis import RedisSessionStore
|
|
83
|
+
|
|
84
|
+
return RedisSessionStore()
|
|
85
|
+
elif session_store_type == SessionStoreType.DYNAMODB:
|
|
86
|
+
from .session.dynamodb import DynamoDBSessionStore
|
|
87
|
+
|
|
88
|
+
return DynamoDBSessionStore()
|
|
83
89
|
else:
|
|
90
|
+
from .session.in_memory import InMemorySessionStore
|
|
91
|
+
|
|
84
92
|
return InMemorySessionStore()
|
|
@@ -22,9 +22,20 @@ class _RedisConfig(BaseModel):
|
|
|
22
22
|
prefix: str = Field(default="ak:sessions:", description="Key prefix for Redis session storage")
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
class _DynamoDBConfig(BaseModel):
|
|
26
|
+
table_name: str = Field(
|
|
27
|
+
description="DynamoDB table name for session storage. Table should have a partition key named 'session_id' and a sort key named 'key'"
|
|
28
|
+
)
|
|
29
|
+
ttl: int = Field(
|
|
30
|
+
default=604800,
|
|
31
|
+
description="DynamoDB item TTL in seconds (0 disables). Used to compute UNIX epoch 'expiry_time' attribute written per item.",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
25
35
|
class _SessionStoreConfig(BaseModel):
|
|
26
|
-
type: str = Field(default="in_memory", pattern="^(in_memory|redis)$")
|
|
36
|
+
type: str = Field(default="in_memory", pattern="^(in_memory|redis|dynamodb)$")
|
|
27
37
|
redis: Optional[_RedisConfig] = None
|
|
38
|
+
dynamodb: Optional[_DynamoDBConfig] = None
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
class _RoutesConfig(BaseModel):
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from .base import Agent, Session
|
|
4
|
+
from .model import AgentReply, AgentRequest
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Prehook and Posthook classes define the interface for hooks that can be executed before and after an agent's execution respectively.
|
|
8
|
+
These hooks allow for modification of prompts before execution and replies after execution, enabling functionalities such as context injection, validation, moderation, logging, and analytics.
|
|
9
|
+
|
|
10
|
+
Currently, they will get only called for the initial execution of an agent when a user prompt is provided. It's unable to hook into agent-to-agent calls within a workflow. This will be a future enhancement.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Prehook(ABC):
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def on_run(
|
|
17
|
+
self, session: Session, agent: Agent, requests: list[AgentRequest]
|
|
18
|
+
) -> list[AgentRequest] | AgentReply:
|
|
19
|
+
"""
|
|
20
|
+
Hook method called before an agent starts executing a prompt. These hooks can modify the prompt or halt execution.
|
|
21
|
+
Some use cases:
|
|
22
|
+
- RAG context injection
|
|
23
|
+
- Prompt validation like input guard rails
|
|
24
|
+
- Logging or analytics
|
|
25
|
+
|
|
26
|
+
:param: session (Session): The session instance.
|
|
27
|
+
:param: agent (Agent): The agent that will execute the prompt.
|
|
28
|
+
:param: requests (list[AgentRequest]): The list of requests provided to the agent.
|
|
29
|
+
:return:
|
|
30
|
+
- AgentReply: If the hook decides to halt execution, it can return an AgentReply which will be sent
|
|
31
|
+
- list[AgentRequest]: The modified requests or the input list. You can modify the requests in place without taking copies
|
|
32
|
+
You can also add additional content to the requests list. e.g. files, images, etc.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def name(self) -> str:
|
|
39
|
+
"""
|
|
40
|
+
Returns the name of the prehook.
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Posthook(ABC):
|
|
46
|
+
@abstractmethod
|
|
47
|
+
async def on_run(
|
|
48
|
+
self, session: Session, requests: list[AgentRequest], agent: Agent, agent_reply: AgentReply
|
|
49
|
+
) -> AgentReply:
|
|
50
|
+
"""
|
|
51
|
+
Hook method called after an agent finishes executing a prompt. These hooks can modify the agent's reply. Some use cases:
|
|
52
|
+
- Moderation of agent replies. e.g. output guardrails
|
|
53
|
+
- Adding disclaimers or additional information to the reply
|
|
54
|
+
- Logging or analytics
|
|
55
|
+
|
|
56
|
+
Note: if the hook changes the reply, the modified reply will be sent to the next hook for processing.
|
|
57
|
+
The agent_reply parameter contains the unmodified reply from the agent for the first posthook, and the reply modified by previous posthooks for subsequent hooks.
|
|
58
|
+
|
|
59
|
+
:param: session (Session): The session instance.
|
|
60
|
+
:param: requests (list[AgentRequest]): The original requests provided to the agent after any pre-execution hooks have been applied.
|
|
61
|
+
:param: agent (Agent): The agent that executed the prompt.
|
|
62
|
+
:param: agent_reply (AgentReply): The reply to process. For the first posthook, this is the unmodified
|
|
63
|
+
agent reply. For subsequent posthooks, this is the reply modified by previous posthooks in the chain.
|
|
64
|
+
|
|
65
|
+
:return: The modified reply. If not modified, return the current reply.
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def name(self) -> str:
|
|
71
|
+
"""
|
|
72
|
+
:return: the name of the posthook.
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from typing import Any, Literal, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentRequestText(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
AgentRequestText encapsulates a text request to an agent.
|
|
9
|
+
|
|
10
|
+
text: str : This is the user input text
|
|
11
|
+
type: Literal["text"]
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
text: str
|
|
15
|
+
type: Literal["text"] = "text"
|
|
16
|
+
|
|
17
|
+
def __str__(self) -> str:
|
|
18
|
+
return self.text
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentRequestFile(BaseModel):
|
|
22
|
+
"""
|
|
23
|
+
AgentRequestFile encapsulates a file attachment request to an agent
|
|
24
|
+
|
|
25
|
+
file_data: str : This could be base64 encoded string or url
|
|
26
|
+
name: str : name of the file
|
|
27
|
+
type: Literal["file"]
|
|
28
|
+
mime_type: str | None = None : Optional. The IANA standard MIME type of the file
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
file_data: str # This could be base64 encoded string or url
|
|
32
|
+
name: str
|
|
33
|
+
type: Literal["file"] = "file"
|
|
34
|
+
mime_type: str | None = None # Optional. The IANA standard MIME type of the source data
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AgentRequestImage(BaseModel):
|
|
38
|
+
"""
|
|
39
|
+
AgentRequestImage encapsulates an image request to an agent
|
|
40
|
+
|
|
41
|
+
image_data: str : This should be base64 encoded string
|
|
42
|
+
name: str : name of the image
|
|
43
|
+
type: Literal["image"]
|
|
44
|
+
mime_type: str | None = None : Optional. The IANA standard MIME type of the image
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
image_data: str
|
|
48
|
+
name: str
|
|
49
|
+
type: Literal["image"] = "image"
|
|
50
|
+
mime_type: str | None = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentRequestAny(BaseModel):
|
|
54
|
+
"""
|
|
55
|
+
AgentRequestAny encapsulates passing any type of request to be handled by the pre-execution hooks. These are not directly handled by the agent kernel runtime.
|
|
56
|
+
|
|
57
|
+
content: Any : This could be base64 encoded string or bytes or url
|
|
58
|
+
name: str : name of the data
|
|
59
|
+
type: Literal["other"]
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
content: Any
|
|
63
|
+
name: str
|
|
64
|
+
type: Literal["other"] = "other"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AgentReplyText(AgentRequestText):
|
|
68
|
+
"""
|
|
69
|
+
AgentReplyText encapsulates a text reply from an agent.
|
|
70
|
+
|
|
71
|
+
prompt: str : The text prompt sent to the agent
|
|
72
|
+
|
|
73
|
+
Inherits fields `text` and `type` from AgentRequestText.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
prompt: str = ""
|
|
77
|
+
|
|
78
|
+
def __str__(self) -> str:
|
|
79
|
+
return self.text
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AgentReplyImage(BaseModel):
|
|
83
|
+
"""
|
|
84
|
+
AgentReplyImage encapsulates a text & image reply from an agent.
|
|
85
|
+
|
|
86
|
+
text: str : This is the agent output text
|
|
87
|
+
prompt: str : The text prompt sent to the agent
|
|
88
|
+
image_data: str : This should be base64 encoded string
|
|
89
|
+
name: str : name of the image
|
|
90
|
+
type: Literal["image"]
|
|
91
|
+
mime_type: str | None = None : Optional. The IANA standard MIME type of the image
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
text: str
|
|
95
|
+
prompt: str = ""
|
|
96
|
+
image_data: str
|
|
97
|
+
name: str
|
|
98
|
+
type: Literal["image"] = "image"
|
|
99
|
+
mime_type: str | None = None
|
|
100
|
+
|
|
101
|
+
def __str__(self) -> str:
|
|
102
|
+
return f"{self.text}. Image {self.name} is attached."
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
type AgentRequest = Union[AgentRequestText, AgentRequestFile, AgentRequestImage, AgentRequestAny]
|
|
106
|
+
type AgentReply = Union[AgentReplyText, AgentReplyImage]
|