agentkit-sdk-python 0.7.10__py3-none-any.whl → 0.7.12__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.
- agentkit/apps/__init__.py +11 -0
- agentkit/apps/agent_server_app/agent_server_app.py +81 -17
- agentkit/apps/langgraph_server_app/__init__.py +5 -0
- agentkit/apps/langgraph_server_app/langgraph_server_app.py +741 -0
- agentkit/auth/admin.py +4 -1
- agentkit/frameworks/langgraph.py +91 -11
- agentkit/frameworks/migration.py +3 -0
- agentkit/frameworks/strands.py +2 -0
- agentkit/toolkit/cli/sandbox/model_config.py +15 -5
- agentkit/version.py +1 -1
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/METADATA +1 -1
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/RECORD +16 -14
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/WHEEL +0 -0
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/entry_points.txt +0 -0
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/licenses/LICENSE +0 -0
- {agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/top_level.txt +0 -0
agentkit/apps/__init__.py
CHANGED
|
@@ -17,6 +17,9 @@ from typing import TYPE_CHECKING
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from agentkit.apps.a2a_app.a2a_app import AgentkitA2aApp
|
|
19
19
|
from agentkit.apps.agent_server_app.agent_server_app import AgentkitAgentServerApp
|
|
20
|
+
from agentkit.apps.langgraph_server_app.langgraph_server_app import (
|
|
21
|
+
AgentkitLangGraphServerApp,
|
|
22
|
+
)
|
|
20
23
|
from agentkit.apps.mcp_app.mcp_app import AgentkitMCPApp
|
|
21
24
|
from agentkit.apps.simple_app.simple_app import AgentkitSimpleApp
|
|
22
25
|
|
|
@@ -28,6 +31,7 @@ def __getattr__(
|
|
|
28
31
|
| type["AgentkitMCPApp"]
|
|
29
32
|
| type["AgentkitSimpleApp"]
|
|
30
33
|
| type["AgentkitAgentServerApp"]
|
|
34
|
+
| type["AgentkitLangGraphServerApp"]
|
|
31
35
|
):
|
|
32
36
|
if name == "AgentkitA2aApp":
|
|
33
37
|
from agentkit.apps.a2a_app.a2a_app import AgentkitA2aApp
|
|
@@ -47,6 +51,12 @@ def __getattr__(
|
|
|
47
51
|
)
|
|
48
52
|
|
|
49
53
|
return AgentkitAgentServerApp
|
|
54
|
+
if name == "AgentkitLangGraphServerApp":
|
|
55
|
+
from agentkit.apps.langgraph_server_app.langgraph_server_app import (
|
|
56
|
+
AgentkitLangGraphServerApp,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return AgentkitLangGraphServerApp
|
|
50
60
|
raise AttributeError(f"module 'agentkit.apps' has no attribute '{name}'")
|
|
51
61
|
|
|
52
62
|
|
|
@@ -55,4 +65,5 @@ __all__ = [
|
|
|
55
65
|
"AgentkitMCPApp",
|
|
56
66
|
"AgentkitSimpleApp",
|
|
57
67
|
"AgentkitAgentServerApp",
|
|
68
|
+
"AgentkitLangGraphServerApp",
|
|
58
69
|
]
|
|
@@ -39,14 +39,22 @@ from google.adk.evaluation.local_eval_set_results_manager import (
|
|
|
39
39
|
)
|
|
40
40
|
from google.adk.evaluation.local_eval_sets_manager import LocalEvalSetsManager
|
|
41
41
|
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
|
|
42
|
+
from google.adk.runners import Runner
|
|
42
43
|
from google.adk.sessions.base_session_service import BaseSessionService
|
|
44
|
+
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
|
43
45
|
from google.adk.utils.context_utils import Aclosing
|
|
44
46
|
from google.genai import types
|
|
45
47
|
from opentelemetry import trace
|
|
46
48
|
from typing_extensions import override
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
from veadk
|
|
49
|
+
|
|
50
|
+
try: # pragma: no cover - exercised when users install veadk explicitly.
|
|
51
|
+
from veadk import Agent as VeadkAgent
|
|
52
|
+
from veadk.memory.short_term_memory import ShortTermMemory
|
|
53
|
+
from veadk.runner import Runner as VeadkRunner
|
|
54
|
+
except ImportError: # pragma: no cover - default migration path avoids veadk.
|
|
55
|
+
VeadkAgent = None # type: ignore[assignment]
|
|
56
|
+
ShortTermMemory = None # type: ignore[assignment,misc]
|
|
57
|
+
VeadkRunner = None # type: ignore[assignment]
|
|
50
58
|
|
|
51
59
|
from agentkit.apps.agent_server_app.middleware import (
|
|
52
60
|
AgentkitTelemetryHTTPMiddleware,
|
|
@@ -64,6 +72,65 @@ from agentkit.apps.base_app import BaseAgentkitApp
|
|
|
64
72
|
logger = logging.getLogger(__name__)
|
|
65
73
|
|
|
66
74
|
|
|
75
|
+
def _is_veadk_short_term_memory(value: Any) -> bool:
|
|
76
|
+
return ShortTermMemory is not None and isinstance(value, ShortTermMemory)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _is_veadk_agent(value: Any) -> bool:
|
|
80
|
+
return VeadkAgent is not None and isinstance(value, VeadkAgent)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _resolve_session_service(
|
|
84
|
+
short_term_memory: BaseSessionService | Any | None,
|
|
85
|
+
) -> BaseSessionService:
|
|
86
|
+
if short_term_memory is None:
|
|
87
|
+
return InMemorySessionService()
|
|
88
|
+
if isinstance(short_term_memory, BaseSessionService):
|
|
89
|
+
return short_term_memory
|
|
90
|
+
if _is_veadk_short_term_memory(short_term_memory):
|
|
91
|
+
return short_term_memory.session_service
|
|
92
|
+
raise TypeError(
|
|
93
|
+
"short_term_memory must be a google.adk BaseSessionService, "
|
|
94
|
+
"a veadk ShortTermMemory, or None."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _resolve_memory_service(root_agent: BaseAgent) -> Any:
|
|
99
|
+
if _is_veadk_agent(root_agent):
|
|
100
|
+
long_term_memory = getattr(root_agent, "long_term_memory", None)
|
|
101
|
+
if long_term_memory:
|
|
102
|
+
return long_term_memory
|
|
103
|
+
return InMemoryMemoryService()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _create_a2a_runner(
|
|
107
|
+
*,
|
|
108
|
+
root_agent: BaseAgent,
|
|
109
|
+
short_term_memory: BaseSessionService | Any | None,
|
|
110
|
+
session_service: BaseSessionService,
|
|
111
|
+
memory_service: Any,
|
|
112
|
+
artifact_service: InMemoryArtifactService,
|
|
113
|
+
credential_service: InMemoryCredentialService,
|
|
114
|
+
) -> Any:
|
|
115
|
+
if VeadkRunner is not None and (
|
|
116
|
+
_is_veadk_agent(root_agent) or _is_veadk_short_term_memory(short_term_memory)
|
|
117
|
+
):
|
|
118
|
+
return VeadkRunner(
|
|
119
|
+
agent=root_agent,
|
|
120
|
+
short_term_memory=short_term_memory
|
|
121
|
+
if _is_veadk_short_term_memory(short_term_memory)
|
|
122
|
+
else None,
|
|
123
|
+
)
|
|
124
|
+
return Runner(
|
|
125
|
+
agent=root_agent,
|
|
126
|
+
app_name=root_agent.name,
|
|
127
|
+
session_service=session_service,
|
|
128
|
+
memory_service=memory_service,
|
|
129
|
+
artifact_service=artifact_service,
|
|
130
|
+
credential_service=credential_service,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
67
134
|
async def _call_lifecycle_handler(handler: Any) -> None:
|
|
68
135
|
result = handler()
|
|
69
136
|
if inspect.isawaitable(result):
|
|
@@ -143,9 +210,6 @@ class AgentkitAgentServerApp(BaseAgentkitApp):
|
|
|
143
210
|
) -> None:
|
|
144
211
|
super().__init__()
|
|
145
212
|
|
|
146
|
-
if short_term_memory is None:
|
|
147
|
-
raise TypeError("short_term_memory is required.")
|
|
148
|
-
|
|
149
213
|
if app is not None and agent is not None:
|
|
150
214
|
raise TypeError("Only one of 'agent' or 'app' can be provided.")
|
|
151
215
|
|
|
@@ -154,6 +218,8 @@ class AgentkitAgentServerApp(BaseAgentkitApp):
|
|
|
154
218
|
raise TypeError("Either 'agent' or 'app' must be provided.")
|
|
155
219
|
|
|
156
220
|
root_agent = entry.root_agent if isinstance(entry, App) else entry
|
|
221
|
+
session_service = _resolve_session_service(short_term_memory)
|
|
222
|
+
memory_service = _resolve_memory_service(root_agent)
|
|
157
223
|
|
|
158
224
|
_artifact_service = InMemoryArtifactService()
|
|
159
225
|
_credential_service = InMemoryCredentialService()
|
|
@@ -163,12 +229,8 @@ class AgentkitAgentServerApp(BaseAgentkitApp):
|
|
|
163
229
|
|
|
164
230
|
self.server = AdkWebServer(
|
|
165
231
|
agent_loader=AgentKitAgentLoader(entry),
|
|
166
|
-
session_service=
|
|
167
|
-
|
|
168
|
-
else short_term_memory.session_service,
|
|
169
|
-
memory_service=root_agent.long_term_memory
|
|
170
|
-
if isinstance(root_agent, Agent) and root_agent.long_term_memory
|
|
171
|
-
else InMemoryMemoryService(),
|
|
232
|
+
session_service=session_service,
|
|
233
|
+
memory_service=memory_service,
|
|
172
234
|
artifact_service=_artifact_service,
|
|
173
235
|
credential_service=_credential_service,
|
|
174
236
|
eval_sets_manager=_eval_sets_manager,
|
|
@@ -176,11 +238,13 @@ class AgentkitAgentServerApp(BaseAgentkitApp):
|
|
|
176
238
|
agents_dir=".",
|
|
177
239
|
)
|
|
178
240
|
|
|
179
|
-
runner =
|
|
180
|
-
|
|
181
|
-
short_term_memory=short_term_memory
|
|
182
|
-
|
|
183
|
-
|
|
241
|
+
runner = _create_a2a_runner(
|
|
242
|
+
root_agent=root_agent,
|
|
243
|
+
short_term_memory=short_term_memory,
|
|
244
|
+
session_service=session_service,
|
|
245
|
+
memory_service=memory_service,
|
|
246
|
+
artifact_service=_artifact_service,
|
|
247
|
+
credential_service=_credential_service,
|
|
184
248
|
)
|
|
185
249
|
_a2a_server_app = to_a2a(agent=root_agent, runner=runner)
|
|
186
250
|
|
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
"""AgentKit protocol routes for LangGraph Server applications."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
from uuid import NAMESPACE_URL, uuid4, uuid5
|
|
13
|
+
|
|
14
|
+
import uvicorn
|
|
15
|
+
from fastapi import Request
|
|
16
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
17
|
+
from pydantic import AliasChoices, BaseModel, ConfigDict, Field
|
|
18
|
+
from starlette.responses import Response
|
|
19
|
+
from starlette.routing import Mount
|
|
20
|
+
from typing_extensions import override
|
|
21
|
+
|
|
22
|
+
from agentkit.apps.base_app import BaseAgentkitApp
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
_OUTPUT_FIELD_KEYS = ("answer", "output", "final", "response", "text", "messages")
|
|
27
|
+
_THREAD_ID_NAMESPACE = "agentkit:langgraph-server"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgentkitRunRequest(BaseModel):
|
|
31
|
+
"""Minimal AgentKit run request model for LangGraph Server mode."""
|
|
32
|
+
|
|
33
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
34
|
+
|
|
35
|
+
app_name: str = Field(validation_alias=AliasChoices("appName", "app_name"))
|
|
36
|
+
user_id: str = Field(validation_alias=AliasChoices("userId", "user_id"))
|
|
37
|
+
session_id: str = Field(validation_alias=AliasChoices("sessionId", "session_id"))
|
|
38
|
+
new_message: Any | None = Field(
|
|
39
|
+
default=None,
|
|
40
|
+
validation_alias=AliasChoices("newMessage", "new_message"),
|
|
41
|
+
)
|
|
42
|
+
streaming: bool | None = None
|
|
43
|
+
state_delta: dict[str, Any] | None = Field(
|
|
44
|
+
default=None,
|
|
45
|
+
validation_alias=AliasChoices("stateDelta", "state_delta"),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_json(path: Path) -> dict[str, Any]:
|
|
50
|
+
try:
|
|
51
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
52
|
+
except FileNotFoundError as exc:
|
|
53
|
+
raise FileNotFoundError(f"LangGraph config file not found: {path}") from exc
|
|
54
|
+
except json.JSONDecodeError as exc:
|
|
55
|
+
raise ValueError(f"LangGraph config file is not valid JSON: {path}") from exc
|
|
56
|
+
if not isinstance(data, dict):
|
|
57
|
+
raise ValueError("LangGraph config must be a JSON object.")
|
|
58
|
+
return data
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _load_dotenv(path: Path) -> dict[str, str]:
|
|
62
|
+
if not path.exists():
|
|
63
|
+
return {}
|
|
64
|
+
try:
|
|
65
|
+
from dotenv.main import DotEnv
|
|
66
|
+
except ImportError:
|
|
67
|
+
logger.warning("python-dotenv is not installed; skipping %s.", path)
|
|
68
|
+
return {}
|
|
69
|
+
values = DotEnv(dotenv_path=path).dict() or {}
|
|
70
|
+
return {key: value for key, value in values.items() if value is not None}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _set_env_json(name: str, value: Any) -> None:
|
|
74
|
+
if value is None:
|
|
75
|
+
os.environ.pop(name, None)
|
|
76
|
+
else:
|
|
77
|
+
os.environ[name] = json.dumps(value)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _json_text(value: Any) -> str:
|
|
81
|
+
try:
|
|
82
|
+
return json.dumps(value, ensure_ascii=False, default=str)
|
|
83
|
+
except Exception:
|
|
84
|
+
return str(value)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _content_value_to_text(content: Any) -> str:
|
|
88
|
+
if content is None:
|
|
89
|
+
return ""
|
|
90
|
+
if isinstance(content, str):
|
|
91
|
+
return content
|
|
92
|
+
if isinstance(content, dict):
|
|
93
|
+
for key in ("text", "content", "output", "answer"):
|
|
94
|
+
if key in content:
|
|
95
|
+
text = _content_value_to_text(content[key])
|
|
96
|
+
if text:
|
|
97
|
+
return text
|
|
98
|
+
return _json_text(content)
|
|
99
|
+
if isinstance(content, list):
|
|
100
|
+
return "".join(_content_value_to_text(item) for item in content)
|
|
101
|
+
text = getattr(content, "text", None)
|
|
102
|
+
if isinstance(text, str):
|
|
103
|
+
return text
|
|
104
|
+
return str(content)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _chunk_to_text(value: Any) -> str:
|
|
108
|
+
if value is None:
|
|
109
|
+
return ""
|
|
110
|
+
if isinstance(value, str):
|
|
111
|
+
return value
|
|
112
|
+
content = getattr(value, "content", None)
|
|
113
|
+
if content is not None:
|
|
114
|
+
return _content_value_to_text(content)
|
|
115
|
+
text = getattr(value, "text", None)
|
|
116
|
+
if isinstance(text, str):
|
|
117
|
+
return text
|
|
118
|
+
if isinstance(value, dict):
|
|
119
|
+
for key in ("output", "answer", "text", "content"):
|
|
120
|
+
if key in value:
|
|
121
|
+
text = _chunk_to_text(value[key])
|
|
122
|
+
if text:
|
|
123
|
+
return text
|
|
124
|
+
messages = value.get("messages")
|
|
125
|
+
if isinstance(messages, (list, tuple)) and messages:
|
|
126
|
+
return _chunk_to_text(messages[-1])
|
|
127
|
+
for nested in value.values():
|
|
128
|
+
text = _chunk_to_text(nested)
|
|
129
|
+
if text:
|
|
130
|
+
return text
|
|
131
|
+
return _json_text(value)
|
|
132
|
+
if isinstance(value, (list, tuple)):
|
|
133
|
+
for item in reversed(value):
|
|
134
|
+
text = _chunk_to_text(item)
|
|
135
|
+
if text:
|
|
136
|
+
return text
|
|
137
|
+
return ""
|
|
138
|
+
return str(value)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _chunk_delta(accumulated: str, text: str) -> str:
|
|
142
|
+
if not accumulated:
|
|
143
|
+
return text
|
|
144
|
+
if text.startswith(accumulated):
|
|
145
|
+
return text[len(accumulated) :]
|
|
146
|
+
return text
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _normalize_import_path(config_dir: Path, value: Any) -> Any:
|
|
150
|
+
if not isinstance(value, str) or ":" not in value:
|
|
151
|
+
return value
|
|
152
|
+
path_or_module, variable = value.rsplit(":", 1)
|
|
153
|
+
if not path_or_module or os.path.isabs(path_or_module):
|
|
154
|
+
return value
|
|
155
|
+
if "/" not in path_or_module and not path_or_module.endswith(".py"):
|
|
156
|
+
return value
|
|
157
|
+
return f"{(config_dir / path_or_module).resolve()}:{variable}"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _normalize_path_config(config_dir: Path, value: Any) -> Any:
|
|
161
|
+
if not isinstance(value, dict):
|
|
162
|
+
return value
|
|
163
|
+
normalized = dict(value)
|
|
164
|
+
if "path" in normalized:
|
|
165
|
+
normalized["path"] = _normalize_import_path(config_dir, normalized["path"])
|
|
166
|
+
if "app" in normalized:
|
|
167
|
+
normalized["app"] = _normalize_import_path(config_dir, normalized["app"])
|
|
168
|
+
return normalized
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _normalize_config_paths(config_dir: Path, config: dict[str, Any]) -> dict[str, Any]:
|
|
172
|
+
normalized = dict(config)
|
|
173
|
+
graphs = config.get("graphs")
|
|
174
|
+
if isinstance(graphs, dict):
|
|
175
|
+
normalized_graphs: dict[str, Any] = {}
|
|
176
|
+
for graph_id, graph_config in graphs.items():
|
|
177
|
+
if isinstance(graph_config, str):
|
|
178
|
+
normalized_graphs[graph_id] = _normalize_import_path(config_dir, graph_config)
|
|
179
|
+
elif isinstance(graph_config, dict):
|
|
180
|
+
normalized_graphs[graph_id] = _normalize_path_config(config_dir, graph_config)
|
|
181
|
+
else:
|
|
182
|
+
normalized_graphs[graph_id] = graph_config
|
|
183
|
+
normalized["graphs"] = normalized_graphs
|
|
184
|
+
for key in ("auth", "checkpointer", "http"):
|
|
185
|
+
normalized[key] = _normalize_path_config(config_dir, config.get(key))
|
|
186
|
+
return normalized
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _graph_source(value: Any) -> str | None:
|
|
190
|
+
if isinstance(value, str):
|
|
191
|
+
return value
|
|
192
|
+
if isinstance(value, dict) and isinstance(value.get("path"), str):
|
|
193
|
+
return value["path"]
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _materialize_lazy_graph_exports(graphs: Any) -> None:
|
|
198
|
+
if not isinstance(graphs, dict):
|
|
199
|
+
return
|
|
200
|
+
for graph_config in graphs.values():
|
|
201
|
+
source = _graph_source(graph_config)
|
|
202
|
+
if not source or ":" not in source:
|
|
203
|
+
continue
|
|
204
|
+
module_name, variable = source.rsplit(":", 1)
|
|
205
|
+
if "/" in module_name or module_name.endswith(".py"):
|
|
206
|
+
continue
|
|
207
|
+
try:
|
|
208
|
+
module = importlib.import_module(module_name)
|
|
209
|
+
except Exception:
|
|
210
|
+
# This helper only makes lazy module exports visible to LangGraph's
|
|
211
|
+
# loader. Import errors still belong to the official loader path.
|
|
212
|
+
continue
|
|
213
|
+
if variable in module.__dict__:
|
|
214
|
+
continue
|
|
215
|
+
try:
|
|
216
|
+
module.__dict__[variable] = getattr(module, variable)
|
|
217
|
+
except Exception:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _extend_python_path(config_dir: Path, config: dict[str, Any]) -> None:
|
|
222
|
+
candidates = [config_dir]
|
|
223
|
+
dependencies = config.get("dependencies")
|
|
224
|
+
if isinstance(dependencies, list):
|
|
225
|
+
for dependency in dependencies:
|
|
226
|
+
if not isinstance(dependency, str):
|
|
227
|
+
continue
|
|
228
|
+
dependency_path = (config_dir / dependency).resolve()
|
|
229
|
+
if dependency_path.is_dir():
|
|
230
|
+
candidates.append(dependency_path)
|
|
231
|
+
for candidate in reversed(candidates):
|
|
232
|
+
value = str(candidate)
|
|
233
|
+
if value not in sys.path:
|
|
234
|
+
sys.path.insert(0, value)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _prepare_langgraph_environment(
|
|
238
|
+
config_path: Path,
|
|
239
|
+
config: dict[str, Any],
|
|
240
|
+
*,
|
|
241
|
+
allow_blocking: bool | None = None,
|
|
242
|
+
) -> None:
|
|
243
|
+
config_dir = config_path.parent
|
|
244
|
+
env_config = config.get("env")
|
|
245
|
+
if isinstance(env_config, str):
|
|
246
|
+
env_path = (config_dir / env_config).resolve()
|
|
247
|
+
for key, value in _load_dotenv(env_path).items():
|
|
248
|
+
os.environ.setdefault(key, value)
|
|
249
|
+
elif isinstance(env_config, dict):
|
|
250
|
+
for key, value in env_config.items():
|
|
251
|
+
if isinstance(key, str) and value is not None:
|
|
252
|
+
os.environ.setdefault(key, str(value))
|
|
253
|
+
|
|
254
|
+
os.environ["LANGSERVE_GRAPHS"] = json.dumps(config.get("graphs", {}))
|
|
255
|
+
_set_env_json("LANGGRAPH_AUTH", config.get("auth"))
|
|
256
|
+
_set_env_json("LANGGRAPH_HTTP", config.get("http"))
|
|
257
|
+
_set_env_json("LANGGRAPH_STORE", config.get("store"))
|
|
258
|
+
_set_env_json("LANGGRAPH_UI", config.get("ui"))
|
|
259
|
+
_set_env_json("LANGGRAPH_WEBHOOKS", config.get("webhooks"))
|
|
260
|
+
_set_env_json("LANGGRAPH_UI_CONFIG", config.get("ui_config"))
|
|
261
|
+
_set_env_json("LANGGRAPH_CHECKPOINTER", config.get("checkpointer"))
|
|
262
|
+
os.environ.setdefault("DATABASE_URI", ":memory:")
|
|
263
|
+
os.environ.setdefault("REDIS_URI", "fake")
|
|
264
|
+
os.environ.setdefault("MIGRATIONS_PATH", "__inmem")
|
|
265
|
+
os.environ.setdefault("N_JOBS_PER_WORKER", "1")
|
|
266
|
+
os.environ.setdefault("LANGSMITH_LANGGRAPH_API_VARIANT", "local_dev")
|
|
267
|
+
os.environ.setdefault("LANGGRAPH_RUNTIME_EDITION", "inmem")
|
|
268
|
+
os.environ.setdefault("LANGGRAPH_API_URL", os.environ.get("AGENTKIT_BASE_URL", "http://localhost:8000"))
|
|
269
|
+
os.environ.setdefault("LANGGRAPH_DISABLE_FILE_PERSISTENCE", "false")
|
|
270
|
+
if allow_blocking is None:
|
|
271
|
+
os.environ.setdefault("LANGGRAPH_ALLOW_BLOCKING", "false")
|
|
272
|
+
else:
|
|
273
|
+
os.environ["LANGGRAPH_ALLOW_BLOCKING"] = str(allow_blocking).lower()
|
|
274
|
+
os.environ.setdefault("ALLOW_PRIVATE_NETWORK", "true")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _resolve_graph_id(config: dict[str, Any], graph_id: str | None) -> str:
|
|
278
|
+
graphs = config.get("graphs")
|
|
279
|
+
if not isinstance(graphs, dict) or not graphs:
|
|
280
|
+
raise ValueError("LangGraph config must define at least one graph.")
|
|
281
|
+
if graph_id:
|
|
282
|
+
if graph_id not in graphs:
|
|
283
|
+
raise ValueError(f"Graph id {graph_id!r} was not found in langgraph.json.")
|
|
284
|
+
return graph_id
|
|
285
|
+
if len(graphs) > 1:
|
|
286
|
+
choices = ", ".join(str(key) for key in graphs)
|
|
287
|
+
raise ValueError(f"Multiple LangGraph graphs found; specify graph_id. Available: {choices}.")
|
|
288
|
+
return next(iter(graphs))
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _content_text(content: Any | None) -> str:
|
|
292
|
+
if content is None:
|
|
293
|
+
return ""
|
|
294
|
+
if isinstance(content, str):
|
|
295
|
+
return content
|
|
296
|
+
if isinstance(content, dict):
|
|
297
|
+
parts = content.get("parts") or []
|
|
298
|
+
if not parts and isinstance(content.get("text"), str):
|
|
299
|
+
return content["text"]
|
|
300
|
+
if not parts and isinstance(content.get("content"), str):
|
|
301
|
+
return content["content"]
|
|
302
|
+
else:
|
|
303
|
+
parts = getattr(content, "parts", None) or []
|
|
304
|
+
texts: list[str] = []
|
|
305
|
+
for part in parts:
|
|
306
|
+
text = part.get("text") if isinstance(part, dict) else getattr(part, "text", None)
|
|
307
|
+
if text:
|
|
308
|
+
texts.append(str(text))
|
|
309
|
+
return "\n".join(texts)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _input_payload(text: str, input_key: str | None) -> dict[str, Any]:
|
|
313
|
+
if input_key:
|
|
314
|
+
return {input_key: text}
|
|
315
|
+
return {"messages": [{"role": "user", "content": text}]}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _update_output_text(data: Any) -> str:
|
|
319
|
+
if not isinstance(data, dict):
|
|
320
|
+
return _chunk_to_text(data)
|
|
321
|
+
for key in _OUTPUT_FIELD_KEYS:
|
|
322
|
+
if key in data:
|
|
323
|
+
text = _stream_message_text(data[key]) if key == "messages" else _chunk_to_text(data[key])
|
|
324
|
+
if text:
|
|
325
|
+
return text
|
|
326
|
+
latest = ""
|
|
327
|
+
for value in data.values():
|
|
328
|
+
if not isinstance(value, dict):
|
|
329
|
+
continue
|
|
330
|
+
for key in _OUTPUT_FIELD_KEYS:
|
|
331
|
+
if key in value:
|
|
332
|
+
text = _stream_message_text(value[key]) if key == "messages" else _chunk_to_text(value[key])
|
|
333
|
+
if text:
|
|
334
|
+
latest = text
|
|
335
|
+
return latest
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _stream_chunk(chunk: Any) -> tuple[str, Any]:
|
|
339
|
+
if isinstance(chunk, dict):
|
|
340
|
+
return str(chunk.get("type") or chunk.get("event") or ""), chunk.get("data")
|
|
341
|
+
event = getattr(chunk, "event", "")
|
|
342
|
+
data = getattr(chunk, "data", None)
|
|
343
|
+
return str(event or ""), data
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _jsonable(value: Any) -> Any:
|
|
347
|
+
try:
|
|
348
|
+
json.dumps(value, ensure_ascii=False)
|
|
349
|
+
return value
|
|
350
|
+
except Exception:
|
|
351
|
+
return str(value)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _interrupt_payload(data: Any) -> Any | None:
|
|
355
|
+
if not isinstance(data, dict):
|
|
356
|
+
return None
|
|
357
|
+
if "__interrupt__" in data:
|
|
358
|
+
interrupts = data.get("__interrupt__")
|
|
359
|
+
if not isinstance(interrupts, (list, tuple)) or not interrupts:
|
|
360
|
+
return _jsonable(interrupts)
|
|
361
|
+
values: list[dict[str, Any]] = []
|
|
362
|
+
for interrupt in interrupts:
|
|
363
|
+
values.append(
|
|
364
|
+
{
|
|
365
|
+
"id": getattr(interrupt, "id", None),
|
|
366
|
+
"value": _jsonable(getattr(interrupt, "value", interrupt)),
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
return values
|
|
370
|
+
for value in data.values():
|
|
371
|
+
interrupt = _interrupt_payload(value)
|
|
372
|
+
if interrupt is not None:
|
|
373
|
+
return interrupt
|
|
374
|
+
return None
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _message_type(value: Any) -> str:
|
|
378
|
+
if isinstance(value, dict):
|
|
379
|
+
raw = value.get("type") or value.get("role") or ""
|
|
380
|
+
else:
|
|
381
|
+
raw = getattr(value, "type", None) or getattr(value, "role", None) or value.__class__.__name__
|
|
382
|
+
return str(raw).lower()
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _is_ai_message(value: Any) -> bool:
|
|
386
|
+
message_type = _message_type(value)
|
|
387
|
+
return message_type in {"ai", "assistant", "model", "aimessage", "aimessagechunk"}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _is_empty_message_content(value: Any) -> bool:
|
|
391
|
+
if isinstance(value, dict):
|
|
392
|
+
content = value.get("content")
|
|
393
|
+
has_text = isinstance(value.get("text"), str) and bool(value.get("text"))
|
|
394
|
+
else:
|
|
395
|
+
content = getattr(value, "content", None)
|
|
396
|
+
has_text = isinstance(getattr(value, "text", None), str) and bool(getattr(value, "text", None))
|
|
397
|
+
return not has_text and (content is None or content == "" or content == {} or content == [])
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _message_candidates(data: Any) -> list[Any]:
|
|
401
|
+
if isinstance(data, dict):
|
|
402
|
+
messages = data.get("messages")
|
|
403
|
+
if isinstance(messages, (list, tuple)):
|
|
404
|
+
return list(messages)
|
|
405
|
+
return [data]
|
|
406
|
+
if isinstance(data, (list, tuple)):
|
|
407
|
+
return list(data)
|
|
408
|
+
return [data]
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _stream_message_text(data: Any) -> str:
|
|
412
|
+
latest = ""
|
|
413
|
+
for candidate in _message_candidates(data):
|
|
414
|
+
if _is_ai_message(candidate) and not _is_empty_message_content(candidate):
|
|
415
|
+
text = _chunk_to_text(candidate)
|
|
416
|
+
if text:
|
|
417
|
+
latest = text
|
|
418
|
+
return latest
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _agentkit_text_event(agent_name: str, text: str, *, partial: bool) -> dict[str, Any]:
|
|
422
|
+
return {
|
|
423
|
+
"invocationId": f"agentkit-langgraph-{uuid4()}",
|
|
424
|
+
"author": agent_name,
|
|
425
|
+
"partial": partial,
|
|
426
|
+
"content": {"role": "model", "parts": [{"text": text}]},
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _interrupt_event(agent_name: str, interrupt: Any) -> dict[str, Any]:
|
|
431
|
+
return {
|
|
432
|
+
"invocationId": f"agentkit-langgraph-{uuid4()}",
|
|
433
|
+
"author": agent_name,
|
|
434
|
+
"interrupted": True,
|
|
435
|
+
"errorCode": "LANGGRAPH_INTERRUPT",
|
|
436
|
+
"errorMessage": "LangGraph execution interrupted and requires resume input.",
|
|
437
|
+
"customMetadata": {"langgraph_interrupt": interrupt},
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _event_json(event: dict[str, Any]) -> str:
|
|
442
|
+
return json.dumps(event, ensure_ascii=False, separators=(",", ":"))
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _agentkit_thread_id(app_name: str, user_id: str, session_id: str) -> str:
|
|
446
|
+
raw = f"{_THREAD_ID_NAMESPACE}:{app_name}:{user_id}:{session_id}"
|
|
447
|
+
return str(uuid5(NAMESPACE_URL, raw))
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _session_body(app_name: str, user_id: str, session_id: str, state: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
451
|
+
return {
|
|
452
|
+
"id": session_id,
|
|
453
|
+
"appName": app_name,
|
|
454
|
+
"userId": user_id,
|
|
455
|
+
"state": state or {},
|
|
456
|
+
"events": [],
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
async def _json_body_or_empty(request: Request) -> dict[str, Any]:
|
|
461
|
+
body = await request.body()
|
|
462
|
+
if not body:
|
|
463
|
+
return {}
|
|
464
|
+
try:
|
|
465
|
+
payload = json.loads(body.decode("utf-8"))
|
|
466
|
+
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
467
|
+
return {}
|
|
468
|
+
return payload if isinstance(payload, dict) else {}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class AgentkitLangGraphServerApp(BaseAgentkitApp):
|
|
472
|
+
"""Run a LangGraph Server app and expose AgentKit-compatible routes.
|
|
473
|
+
|
|
474
|
+
LangGraph remains the execution runtime. AgentKit routes translate requests
|
|
475
|
+
into LangGraph threads/runs so persistence, interrupts, runtime context, and
|
|
476
|
+
graph factories keep their original LangGraph Server semantics.
|
|
477
|
+
"""
|
|
478
|
+
|
|
479
|
+
def __init__(
|
|
480
|
+
self,
|
|
481
|
+
*,
|
|
482
|
+
config_path: str | Path = "langgraph.json",
|
|
483
|
+
graph_id: str | None = None,
|
|
484
|
+
input_key: str | None = None,
|
|
485
|
+
allow_blocking: bool | None = None,
|
|
486
|
+
) -> None:
|
|
487
|
+
super().__init__()
|
|
488
|
+
self.config_path = Path(config_path).resolve()
|
|
489
|
+
raw_config = _load_json(self.config_path)
|
|
490
|
+
self.config = _normalize_config_paths(self.config_path.parent, raw_config)
|
|
491
|
+
self.graph_id = _resolve_graph_id(self.config, graph_id)
|
|
492
|
+
self.input_key = input_key
|
|
493
|
+
_extend_python_path(self.config_path.parent, self.config)
|
|
494
|
+
_prepare_langgraph_environment(self.config_path, self.config, allow_blocking=allow_blocking)
|
|
495
|
+
_materialize_lazy_graph_exports(self.config.get("graphs"))
|
|
496
|
+
try:
|
|
497
|
+
server_module = importlib.import_module("langgraph_api.server")
|
|
498
|
+
from langgraph_sdk import get_client
|
|
499
|
+
except ImportError as exc:
|
|
500
|
+
raise ImportError(
|
|
501
|
+
"LangGraph server mode requires langgraph-api and langgraph-sdk. "
|
|
502
|
+
"Install langgraph-cli[inmem] or use the requirements generated by agentkit migrate."
|
|
503
|
+
) from exc
|
|
504
|
+
self.app = server_module.app
|
|
505
|
+
self._client = get_client(url=None, api_key=None)
|
|
506
|
+
self._attach_agentkit_routes()
|
|
507
|
+
|
|
508
|
+
def _run_kwargs(self, req: AgentkitRunRequest) -> dict[str, Any]:
|
|
509
|
+
app_name = req.app_name or self.graph_id
|
|
510
|
+
thread_id = _agentkit_thread_id(app_name, req.user_id, req.session_id)
|
|
511
|
+
text = _content_text(req.new_message)
|
|
512
|
+
configurable = {
|
|
513
|
+
"thread_id": thread_id,
|
|
514
|
+
"user_id": req.user_id,
|
|
515
|
+
"agentkit_session_id": req.session_id,
|
|
516
|
+
"agentkit_thread_id": thread_id,
|
|
517
|
+
}
|
|
518
|
+
if req.state_delta:
|
|
519
|
+
configurable.update(req.state_delta)
|
|
520
|
+
return {
|
|
521
|
+
"thread_id": thread_id,
|
|
522
|
+
"assistant_id": self.graph_id,
|
|
523
|
+
"input": _input_payload(text, self.input_key),
|
|
524
|
+
"config": {"configurable": configurable},
|
|
525
|
+
"metadata": {
|
|
526
|
+
"agentkit_user_id": req.user_id,
|
|
527
|
+
"agentkit_session_id": req.session_id,
|
|
528
|
+
"agentkit_app_name": req.app_name or self.graph_id,
|
|
529
|
+
},
|
|
530
|
+
"if_not_exists": "create",
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async def _ensure_thread(self, app_name: str, user_id: str, session_id: str, state: dict[str, Any] | None = None) -> None:
|
|
534
|
+
thread_id = _agentkit_thread_id(app_name, user_id, session_id)
|
|
535
|
+
metadata = {
|
|
536
|
+
"agentkit_app_name": app_name,
|
|
537
|
+
"agentkit_user_id": user_id,
|
|
538
|
+
"agentkit_session_id": session_id,
|
|
539
|
+
**(state or {}),
|
|
540
|
+
}
|
|
541
|
+
await self._client.threads.create(
|
|
542
|
+
thread_id=thread_id,
|
|
543
|
+
if_exists="do_nothing",
|
|
544
|
+
graph_id=self.graph_id,
|
|
545
|
+
metadata=metadata,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
async def _stream_events(self, req: AgentkitRunRequest):
|
|
549
|
+
await self._ensure_thread(req.app_name or self.graph_id, req.user_id, req.session_id)
|
|
550
|
+
accumulated_text = ""
|
|
551
|
+
final_text = ""
|
|
552
|
+
async for chunk in self._client.runs.stream(
|
|
553
|
+
**self._run_kwargs(req),
|
|
554
|
+
stream_mode=["messages", "updates", "values"],
|
|
555
|
+
version="v2",
|
|
556
|
+
):
|
|
557
|
+
mode, data = _stream_chunk(chunk)
|
|
558
|
+
if mode == "messages" or mode.startswith("messages/"):
|
|
559
|
+
text = _stream_message_text(data)
|
|
560
|
+
delta = _chunk_delta(accumulated_text, text)
|
|
561
|
+
if delta:
|
|
562
|
+
accumulated_text += delta
|
|
563
|
+
yield _agentkit_text_event(self.graph_id, delta, partial=True)
|
|
564
|
+
if text:
|
|
565
|
+
final_text = text
|
|
566
|
+
continue
|
|
567
|
+
if mode in {"updates", "values"}:
|
|
568
|
+
interrupt = _interrupt_payload(data)
|
|
569
|
+
if interrupt is not None:
|
|
570
|
+
yield _interrupt_event(self.graph_id, interrupt)
|
|
571
|
+
return
|
|
572
|
+
text = _update_output_text(data)
|
|
573
|
+
if text:
|
|
574
|
+
final_text = text
|
|
575
|
+
final_text = final_text or accumulated_text
|
|
576
|
+
if final_text:
|
|
577
|
+
yield _agentkit_text_event(self.graph_id, final_text, partial=False)
|
|
578
|
+
|
|
579
|
+
def _attach_agentkit_routes(self) -> None:
|
|
580
|
+
async def health(request: Request) -> JSONResponse:
|
|
581
|
+
del request
|
|
582
|
+
return JSONResponse({"status": "ok"})
|
|
583
|
+
|
|
584
|
+
async def list_apps(request: Request) -> JSONResponse:
|
|
585
|
+
detailed = request.query_params.get("detailed") == "true"
|
|
586
|
+
if detailed:
|
|
587
|
+
return JSONResponse(
|
|
588
|
+
{
|
|
589
|
+
"apps": [
|
|
590
|
+
{
|
|
591
|
+
"name": self.graph_id,
|
|
592
|
+
"root_agent_name": self.graph_id,
|
|
593
|
+
"description": "LangGraph Server app exposed through AgentKit routes",
|
|
594
|
+
"language": "python",
|
|
595
|
+
}
|
|
596
|
+
]
|
|
597
|
+
}
|
|
598
|
+
)
|
|
599
|
+
return JSONResponse([self.graph_id])
|
|
600
|
+
|
|
601
|
+
async def get_session(request: Request) -> JSONResponse:
|
|
602
|
+
session_id = request.path_params["session_id"]
|
|
603
|
+
app_name = request.path_params["app_name"]
|
|
604
|
+
user_id = request.path_params["user_id"]
|
|
605
|
+
thread_id = _agentkit_thread_id(app_name, user_id, session_id)
|
|
606
|
+
response = await self._client.threads.get(thread_id)
|
|
607
|
+
metadata = response.get("metadata", {}) if isinstance(response, dict) else {}
|
|
608
|
+
return JSONResponse(_session_body(app_name, user_id, session_id, metadata))
|
|
609
|
+
|
|
610
|
+
async def list_sessions(request: Request) -> JSONResponse:
|
|
611
|
+
app_name = request.path_params["app_name"]
|
|
612
|
+
user_id = request.path_params["user_id"]
|
|
613
|
+
response = await self._client.threads.search(
|
|
614
|
+
metadata={"agentkit_app_name": app_name, "agentkit_user_id": user_id},
|
|
615
|
+
limit=100,
|
|
616
|
+
)
|
|
617
|
+
sessions = [
|
|
618
|
+
_session_body(
|
|
619
|
+
app_name,
|
|
620
|
+
user_id,
|
|
621
|
+
item.get("metadata", {}).get("agentkit_session_id", item.get("thread_id", "")),
|
|
622
|
+
item.get("metadata", {}),
|
|
623
|
+
)
|
|
624
|
+
for item in response
|
|
625
|
+
if isinstance(item, dict) and item.get("thread_id")
|
|
626
|
+
]
|
|
627
|
+
return JSONResponse(sessions)
|
|
628
|
+
|
|
629
|
+
async def create_session(request: Request) -> JSONResponse:
|
|
630
|
+
app_name = request.path_params["app_name"]
|
|
631
|
+
user_id = request.path_params["user_id"]
|
|
632
|
+
payload = await _json_body_or_empty(request)
|
|
633
|
+
session_id = str(payload.get("sessionId") or payload.get("session_id") or uuid4())
|
|
634
|
+
state = payload.get("state") if isinstance(payload.get("state"), dict) else {}
|
|
635
|
+
await self._ensure_thread(app_name, user_id, session_id, state)
|
|
636
|
+
return JSONResponse(_session_body(app_name, user_id, session_id, state))
|
|
637
|
+
|
|
638
|
+
async def create_session_with_id(request: Request) -> JSONResponse:
|
|
639
|
+
app_name = request.path_params["app_name"]
|
|
640
|
+
user_id = request.path_params["user_id"]
|
|
641
|
+
session_id = request.path_params["session_id"]
|
|
642
|
+
payload = await _json_body_or_empty(request)
|
|
643
|
+
state = payload
|
|
644
|
+
await self._ensure_thread(app_name, user_id, session_id, state)
|
|
645
|
+
return JSONResponse(_session_body(app_name, user_id, session_id, state))
|
|
646
|
+
|
|
647
|
+
async def delete_session(request: Request) -> Response:
|
|
648
|
+
thread_id = _agentkit_thread_id(
|
|
649
|
+
request.path_params["app_name"],
|
|
650
|
+
request.path_params["user_id"],
|
|
651
|
+
request.path_params["session_id"],
|
|
652
|
+
)
|
|
653
|
+
await self._client.threads.delete(thread_id)
|
|
654
|
+
return Response(status_code=204)
|
|
655
|
+
|
|
656
|
+
async def update_session(request: Request) -> JSONResponse:
|
|
657
|
+
app_name = request.path_params["app_name"]
|
|
658
|
+
user_id = request.path_params["user_id"]
|
|
659
|
+
session_id = request.path_params["session_id"]
|
|
660
|
+
payload = await _json_body_or_empty(request)
|
|
661
|
+
state_delta = payload.get("stateDelta") or payload.get("state_delta")
|
|
662
|
+
state = state_delta if isinstance(state_delta, dict) else {}
|
|
663
|
+
thread_id = _agentkit_thread_id(app_name, user_id, session_id)
|
|
664
|
+
await self._client.threads.update(thread_id, metadata=state)
|
|
665
|
+
return JSONResponse(_session_body(app_name, user_id, session_id, state))
|
|
666
|
+
|
|
667
|
+
async def run_agent(request: Request) -> JSONResponse:
|
|
668
|
+
req = AgentkitRunRequest.model_validate(await request.json())
|
|
669
|
+
events = [event async for event in self._stream_events(req)]
|
|
670
|
+
return JSONResponse(events)
|
|
671
|
+
|
|
672
|
+
def stream_response(req: AgentkitRunRequest) -> StreamingResponse:
|
|
673
|
+
async def event_generator():
|
|
674
|
+
try:
|
|
675
|
+
async for event in self._stream_events(req):
|
|
676
|
+
yield f"data: {_event_json(event)}\n\n"
|
|
677
|
+
except Exception as exc:
|
|
678
|
+
logger.exception("Error in LangGraph Server AgentKit stream: %s", exc)
|
|
679
|
+
yield f"data: {json.dumps({'error': str(exc)}, ensure_ascii=False)}\n\n"
|
|
680
|
+
|
|
681
|
+
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
682
|
+
|
|
683
|
+
async def run_agent_sse(request: Request) -> StreamingResponse:
|
|
684
|
+
req = AgentkitRunRequest.model_validate(await request.json())
|
|
685
|
+
return stream_response(req)
|
|
686
|
+
|
|
687
|
+
async def invoke_compat(request: Request) -> StreamingResponse:
|
|
688
|
+
try:
|
|
689
|
+
payload = await request.json()
|
|
690
|
+
except Exception:
|
|
691
|
+
payload = {}
|
|
692
|
+
prompt = payload.get("prompt") if isinstance(payload, dict) else None
|
|
693
|
+
if prompt is None:
|
|
694
|
+
prompt = json.dumps(payload, ensure_ascii=False) if payload else ""
|
|
695
|
+
user_id = request.headers.get("user_id") or "agentkit_user"
|
|
696
|
+
session_id = request.headers.get("session_id") or str(uuid4())
|
|
697
|
+
req = AgentkitRunRequest(
|
|
698
|
+
app_name=self.graph_id,
|
|
699
|
+
user_id=user_id,
|
|
700
|
+
session_id=session_id,
|
|
701
|
+
new_message={"role": "user", "parts": [{"text": str(prompt)}]},
|
|
702
|
+
streaming=True,
|
|
703
|
+
)
|
|
704
|
+
return stream_response(req)
|
|
705
|
+
|
|
706
|
+
routes = [
|
|
707
|
+
("/health", health, ["GET"]),
|
|
708
|
+
("/list-apps", list_apps, ["GET"]),
|
|
709
|
+
("/apps/{app_name}/users/{user_id}/sessions/{session_id}", get_session, ["GET"]),
|
|
710
|
+
("/apps/{app_name}/users/{user_id}/sessions", list_sessions, ["GET"]),
|
|
711
|
+
("/apps/{app_name}/users/{user_id}/sessions", create_session, ["POST"]),
|
|
712
|
+
("/apps/{app_name}/users/{user_id}/sessions/{session_id}", create_session_with_id, ["POST"]),
|
|
713
|
+
("/apps/{app_name}/users/{user_id}/sessions/{session_id}", delete_session, ["DELETE"]),
|
|
714
|
+
("/apps/{app_name}/users/{user_id}/sessions/{session_id}", update_session, ["PATCH"]),
|
|
715
|
+
("/run", run_agent, ["POST"]),
|
|
716
|
+
("/run_sse", run_agent_sse, ["POST"]),
|
|
717
|
+
("/invoke", invoke_compat, ["POST"]),
|
|
718
|
+
]
|
|
719
|
+
existing = {
|
|
720
|
+
(getattr(route, "path", ""), tuple(sorted(getattr(route, "methods", ()) or ())))
|
|
721
|
+
for route in getattr(self.app, "routes", [])
|
|
722
|
+
}
|
|
723
|
+
for path, endpoint, methods in routes:
|
|
724
|
+
key = (path, tuple(sorted(methods)))
|
|
725
|
+
if key not in existing:
|
|
726
|
+
self.app.add_route(path, endpoint, methods=methods)
|
|
727
|
+
route = self.app.routes.pop()
|
|
728
|
+
insert_at = next(
|
|
729
|
+
(
|
|
730
|
+
index
|
|
731
|
+
for index, existing_route in enumerate(self.app.routes)
|
|
732
|
+
if isinstance(existing_route, Mount)
|
|
733
|
+
and getattr(existing_route, "path", None) == ""
|
|
734
|
+
),
|
|
735
|
+
len(self.app.routes),
|
|
736
|
+
)
|
|
737
|
+
self.app.routes.insert(insert_at, route)
|
|
738
|
+
|
|
739
|
+
@override
|
|
740
|
+
def run(self, host: str | None = None, port: int = 8000) -> None:
|
|
741
|
+
uvicorn.run(self.app, host=host or "0.0.0.0", port=port)
|
agentkit/auth/admin.py
CHANGED
|
@@ -344,6 +344,7 @@ def sso_setup(
|
|
|
344
344
|
bucket = bucket or f"agentkit-cli-{acct}"
|
|
345
345
|
url = publish_discovery(
|
|
346
346
|
coords, bucket=bucket, custom_domain=custom_domain, access_key=api.ak, secret_key=api.sk,
|
|
347
|
+
session_token=api.token,
|
|
347
348
|
)
|
|
348
349
|
if custom_domain:
|
|
349
350
|
manual.append(
|
|
@@ -360,6 +361,7 @@ def publish_discovery(
|
|
|
360
361
|
custom_domain: str | None = None,
|
|
361
362
|
access_key: str | None = None,
|
|
362
363
|
secret_key: str | None = None,
|
|
364
|
+
session_token: str | None = None,
|
|
363
365
|
) -> str:
|
|
364
366
|
"""Publish the discovery doc to TOS static hosting (real https). Returns the login URL.
|
|
365
367
|
|
|
@@ -379,8 +381,9 @@ def publish_discovery(
|
|
|
379
381
|
) from exc
|
|
380
382
|
ak = access_key or _os.getenv("VOLCENGINE_ACCESS_KEY")
|
|
381
383
|
sk = secret_key or _os.getenv("VOLCENGINE_SECRET_KEY")
|
|
384
|
+
token = session_token or _os.getenv("VOLCENGINE_SESSION_TOKEN")
|
|
382
385
|
endpoint = f"tos-{coords.region}.volces.com"
|
|
383
|
-
client = tos.TosClientV2(ak, sk, endpoint, coords.region)
|
|
386
|
+
client = tos.TosClientV2(ak, sk, endpoint, coords.region, security_token=token)
|
|
384
387
|
try:
|
|
385
388
|
client.create_bucket(bucket, acl=tos.ACLType.ACL_Public_Read)
|
|
386
389
|
except Exception as exc: # noqa: BLE001
|
agentkit/frameworks/langgraph.py
CHANGED
|
@@ -6,6 +6,7 @@ from collections.abc import AsyncGenerator, Iterable
|
|
|
6
6
|
import hashlib
|
|
7
7
|
import inspect
|
|
8
8
|
import json
|
|
9
|
+
from dataclasses import dataclass
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
11
12
|
try:
|
|
@@ -40,6 +41,12 @@ LANGGRAPH_PENDING_INTERRUPT_STATE_KEY = "agentkit:langgraph:pending_interrupt"
|
|
|
40
41
|
_OUTPUT_FIELD_KEYS = ("answer", "output", "final", "response", "text", "messages")
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class _FactoryCall:
|
|
46
|
+
args: tuple[Any, ...]
|
|
47
|
+
kwargs: dict[str, Any]
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
def _method_kwargs(method: Any, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
44
51
|
try:
|
|
45
52
|
signature = inspect.signature(method)
|
|
@@ -51,6 +58,66 @@ def _method_kwargs(method: Any, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
|
51
58
|
return {key: value for key, value in kwargs.items() if accepts_kwargs or key in parameters}
|
|
52
59
|
|
|
53
60
|
|
|
61
|
+
def _factory_call(factory: Any, config: dict[str, Any] | None) -> _FactoryCall:
|
|
62
|
+
if not callable(factory):
|
|
63
|
+
raise UnsupportedFrameworkAgentError(
|
|
64
|
+
"LangGraph graph_factory=True requires a callable entry that "
|
|
65
|
+
"returns a compiled graph-like object."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
signature = inspect.signature(factory)
|
|
70
|
+
except (TypeError, ValueError):
|
|
71
|
+
return _FactoryCall(args=(config,), kwargs={})
|
|
72
|
+
|
|
73
|
+
parameters = list(signature.parameters.values())
|
|
74
|
+
required = [
|
|
75
|
+
param
|
|
76
|
+
for param in parameters
|
|
77
|
+
if param.default is inspect.Parameter.empty
|
|
78
|
+
and param.kind
|
|
79
|
+
in {
|
|
80
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
81
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
82
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
if len(required) > 1:
|
|
86
|
+
names = ", ".join(param.name for param in required)
|
|
87
|
+
raise UnsupportedFrameworkAgentError(
|
|
88
|
+
"LangGraph Server graph factory entries may require at most one "
|
|
89
|
+
f"RunnableConfig argument; required parameters found: {names}."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if any(param.kind == inspect.Parameter.VAR_POSITIONAL for param in parameters):
|
|
93
|
+
return _FactoryCall(args=(config,), kwargs={})
|
|
94
|
+
|
|
95
|
+
if required:
|
|
96
|
+
param = required[0]
|
|
97
|
+
if param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
98
|
+
return _FactoryCall(args=(), kwargs={param.name: config})
|
|
99
|
+
return _FactoryCall(args=(config,), kwargs={})
|
|
100
|
+
|
|
101
|
+
optional_config = next(
|
|
102
|
+
(
|
|
103
|
+
param
|
|
104
|
+
for param in parameters
|
|
105
|
+
if param.name in {"config", "runnable_config"}
|
|
106
|
+
and param.kind
|
|
107
|
+
in {
|
|
108
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
109
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
None,
|
|
113
|
+
)
|
|
114
|
+
if optional_config is None:
|
|
115
|
+
return _FactoryCall(args=(), kwargs={})
|
|
116
|
+
if optional_config.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
117
|
+
return _FactoryCall(args=(), kwargs={optional_config.name: config})
|
|
118
|
+
return _FactoryCall(args=(config,), kwargs={})
|
|
119
|
+
|
|
120
|
+
|
|
54
121
|
def _jsonable(value: Any) -> Any:
|
|
55
122
|
try:
|
|
56
123
|
json.dumps(value, ensure_ascii=False)
|
|
@@ -119,6 +186,7 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
119
186
|
"""Adapt a compiled LangGraph graph to AgentKit's ADK runtime boundary."""
|
|
120
187
|
|
|
121
188
|
_graph: Any = PrivateAttr()
|
|
189
|
+
_graph_factory: bool = PrivateAttr(default=False)
|
|
122
190
|
_input_key: str | None = PrivateAttr(default=None)
|
|
123
191
|
_stream_nodes: frozenset[str] = PrivateAttr(default_factory=frozenset)
|
|
124
192
|
|
|
@@ -130,9 +198,11 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
130
198
|
description: str = "LangGraph agent adapted for AgentKit runtime",
|
|
131
199
|
input_key: str | None = None,
|
|
132
200
|
stream_nodes: Iterable[str] | str | None = None,
|
|
201
|
+
graph_factory: bool = False,
|
|
133
202
|
) -> None:
|
|
134
203
|
super().__init__(name=name, description=description)
|
|
135
204
|
self._graph = graph
|
|
205
|
+
self._graph_factory = graph_factory
|
|
136
206
|
self._input_key = input_key
|
|
137
207
|
if isinstance(stream_nodes, str):
|
|
138
208
|
nodes = (stream_nodes,)
|
|
@@ -160,6 +230,15 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
160
230
|
thread_id = session_id
|
|
161
231
|
return {"configurable": {"thread_id": thread_id}}
|
|
162
232
|
|
|
233
|
+
async def _graph_for_run(self, config: dict[str, Any] | None) -> Any:
|
|
234
|
+
if not self._graph_factory:
|
|
235
|
+
return self._graph
|
|
236
|
+
call = _factory_call(self._graph, config)
|
|
237
|
+
graph = self._graph(*call.args, **call.kwargs)
|
|
238
|
+
if inspect.isawaitable(graph):
|
|
239
|
+
graph = await graph
|
|
240
|
+
return graph
|
|
241
|
+
|
|
163
242
|
def _pending_interrupt(self, ctx: InvocationContext) -> Any | None:
|
|
164
243
|
session = getattr(ctx, "session", None)
|
|
165
244
|
state = getattr(session, "state", None)
|
|
@@ -217,12 +296,12 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
217
296
|
actions=EventActions(state_delta={LANGGRAPH_PENDING_INTERRUPT_STATE_KEY: None}),
|
|
218
297
|
)
|
|
219
298
|
|
|
220
|
-
async def _call_once(self, payload: Any, config: dict[str, Any] | None) -> Any:
|
|
221
|
-
ainvoke = getattr(
|
|
299
|
+
async def _call_once(self, graph: Any, payload: Any, config: dict[str, Any] | None) -> Any:
|
|
300
|
+
ainvoke = getattr(graph, "ainvoke", None)
|
|
222
301
|
if callable(ainvoke):
|
|
223
302
|
return await ainvoke(payload, **_method_kwargs(ainvoke, {"config": config}))
|
|
224
303
|
|
|
225
|
-
invoke = getattr(
|
|
304
|
+
invoke = getattr(graph, "invoke", None)
|
|
226
305
|
if callable(invoke):
|
|
227
306
|
return invoke(payload, **_method_kwargs(invoke, {"config": config}))
|
|
228
307
|
|
|
@@ -236,8 +315,8 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
236
315
|
return ["messages", "updates"]
|
|
237
316
|
return "updates"
|
|
238
317
|
|
|
239
|
-
def _sync_stream(self, payload: Any, config: dict[str, Any] | None):
|
|
240
|
-
stream_fn = getattr(
|
|
318
|
+
def _sync_stream(self, graph: Any, payload: Any, config: dict[str, Any] | None):
|
|
319
|
+
stream_fn = getattr(graph, "stream", None)
|
|
241
320
|
if not callable(stream_fn):
|
|
242
321
|
return None
|
|
243
322
|
stream_mode = self._stream_mode()
|
|
@@ -273,15 +352,15 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
273
352
|
|
|
274
353
|
return iterate_attempts()
|
|
275
354
|
|
|
276
|
-
async def _stream_graph(self, payload: Any, config: dict[str, Any] | None, *, prefer_sync: bool = False):
|
|
355
|
+
async def _stream_graph(self, graph: Any, payload: Any, config: dict[str, Any] | None, *, prefer_sync: bool = False):
|
|
277
356
|
if prefer_sync:
|
|
278
|
-
stream = self._sync_stream(payload, config)
|
|
357
|
+
stream = self._sync_stream(graph, payload, config)
|
|
279
358
|
if stream is not None:
|
|
280
359
|
for item in stream:
|
|
281
360
|
yield item
|
|
282
361
|
return
|
|
283
362
|
|
|
284
|
-
astream = getattr(
|
|
363
|
+
astream = getattr(graph, "astream", None)
|
|
285
364
|
if callable(astream):
|
|
286
365
|
stream_mode = self._stream_mode()
|
|
287
366
|
attempts = [
|
|
@@ -312,7 +391,7 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
312
391
|
if last_error is not None:
|
|
313
392
|
raise last_error
|
|
314
393
|
|
|
315
|
-
stream = self._sync_stream(payload, config)
|
|
394
|
+
stream = self._sync_stream(graph, payload, config)
|
|
316
395
|
if stream is not None:
|
|
317
396
|
for item in stream:
|
|
318
397
|
yield item
|
|
@@ -337,12 +416,13 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
337
416
|
config = self._graph_config(ctx)
|
|
338
417
|
resume_pending = self._pending_interrupt(ctx) is not None
|
|
339
418
|
payload = self._run_payload(ctx)
|
|
419
|
+
graph = await self._graph_for_run(config)
|
|
340
420
|
accumulated_text = ""
|
|
341
421
|
last_update: Any = None
|
|
342
422
|
pending_update_text = ""
|
|
343
423
|
streamed = False
|
|
344
424
|
|
|
345
|
-
async for item in self._stream_graph(payload, config, prefer_sync=resume_pending):
|
|
425
|
+
async for item in self._stream_graph(graph, payload, config, prefer_sync=resume_pending):
|
|
346
426
|
streamed = True
|
|
347
427
|
mode = ""
|
|
348
428
|
data = item
|
|
@@ -400,7 +480,7 @@ class LangGraphAgentkitBridge(BaseAgent):
|
|
|
400
480
|
update_text = pending_update_text or _update_output_text(last_update)
|
|
401
481
|
final_text = update_text or accumulated_text
|
|
402
482
|
else:
|
|
403
|
-
final_text = _update_output_text(await self._call_once(payload, config))
|
|
483
|
+
final_text = _update_output_text(await self._call_once(graph, payload, config))
|
|
404
484
|
|
|
405
485
|
if final_text:
|
|
406
486
|
yield self._final_event(ctx, final_text, clear_pending_interrupt=resume_pending)
|
agentkit/frameworks/migration.py
CHANGED
|
@@ -92,6 +92,9 @@ def _call_zero_arg_factory(target: Any, object_path: str) -> Any:
|
|
|
92
92
|
|
|
93
93
|
result = target()
|
|
94
94
|
if inspect.isawaitable(result):
|
|
95
|
+
close = getattr(result, "close", None)
|
|
96
|
+
if callable(close):
|
|
97
|
+
close()
|
|
95
98
|
raise TypeError(
|
|
96
99
|
f"Entry factory {object_path!r} returned an awaitable object. "
|
|
97
100
|
"Async factories are not supported by generated migration apps; "
|
agentkit/frameworks/strands.py
CHANGED
|
@@ -330,6 +330,8 @@ def _result_to_text(result: Any) -> str:
|
|
|
330
330
|
message_text = _message_to_text(getattr(result, "message", None))
|
|
331
331
|
if message_text:
|
|
332
332
|
return message_text
|
|
333
|
+
if hasattr(result, "message") or hasattr(result, "structured_output"):
|
|
334
|
+
return ""
|
|
333
335
|
if isinstance(result, Exception):
|
|
334
336
|
return str(result)
|
|
335
337
|
if isinstance(getattr(result, "results", None), dict):
|
|
@@ -83,8 +83,12 @@ MODEL_PROVIDER_CONFIGS: dict[str, ModelProviderConfig] = {
|
|
|
83
83
|
ModelProviderType.MODEL_SQUARE.value: ModelProviderConfig(
|
|
84
84
|
model_base_url="https://ark.cn-beijing.volces.com/api/v3",
|
|
85
85
|
anthropic_base_url="https://ark.cn-beijing.volces.com/api/compatible",
|
|
86
|
-
default_model_name="
|
|
86
|
+
default_model_name="glm-5-2-260617",
|
|
87
87
|
models={
|
|
88
|
+
"glm-5-2-260617": ModelSpec(
|
|
89
|
+
supports_reasoning_summaries=True,
|
|
90
|
+
context_window=DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
91
|
+
),
|
|
88
92
|
"doubao-seed-2-0-pro-260215": ModelSpec(
|
|
89
93
|
supports_reasoning_summaries=True,
|
|
90
94
|
context_window=LIMITED_MODEL_CONTEXT_WINDOW,
|
|
@@ -435,7 +439,11 @@ def build_codex_config_toml(
|
|
|
435
439
|
'wire_api = "responses"',
|
|
436
440
|
# OAuth (ChatGPT login) providers carry no API key - codex uses the injected
|
|
437
441
|
# auth.json via `requires_openai_auth`; all others use the CODEX_API_KEY env.
|
|
438
|
-
(
|
|
442
|
+
(
|
|
443
|
+
"requires_openai_auth = true"
|
|
444
|
+
if requires_openai_auth
|
|
445
|
+
else 'env_key = "CODEX_API_KEY"'
|
|
446
|
+
),
|
|
439
447
|
"",
|
|
440
448
|
"[tui]",
|
|
441
449
|
"show_tooltips = false",
|
|
@@ -552,7 +560,7 @@ if [ -n "$REQ_CODEX_BASE_URL" ]; then
|
|
|
552
560
|
CODEX_BASE_URL="$REQ_CODEX_BASE_URL"
|
|
553
561
|
fi
|
|
554
562
|
|
|
555
|
-
CODEX_MODEL="${CODEX_MODEL:-
|
|
563
|
+
CODEX_MODEL="${CODEX_MODEL:-glm-5-2-260617}"
|
|
556
564
|
CODEX_BASE_URL="${CODEX_BASE_URL:-https://ark.cn-beijing.volces.com/api/v3}"
|
|
557
565
|
|
|
558
566
|
: >"$ENV_FILE"
|
|
@@ -653,8 +661,10 @@ def _build_model_catalog_item(model_name: str, spec: ModelSpec) -> dict:
|
|
|
653
661
|
|
|
654
662
|
def infer_model_spec(model_name: str) -> ModelSpec:
|
|
655
663
|
normalized_model_name = model_name.strip().lower()
|
|
656
|
-
if
|
|
657
|
-
"
|
|
664
|
+
if (
|
|
665
|
+
normalized_model_name == "glm-5.2"
|
|
666
|
+
or normalized_model_name.startswith("glm-5-")
|
|
667
|
+
or normalized_model_name.startswith("deepseek-v4")
|
|
658
668
|
):
|
|
659
669
|
context_window = DEFAULT_MODEL_CONTEXT_WINDOW
|
|
660
670
|
else:
|
agentkit/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentkit-sdk-python
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.12
|
|
4
4
|
Summary: Python SDK for transforming any AI agent into a production-ready application. Framework-agnostic primitives for runtime, memory, authentication, and tools with volcengine-managed infrastructure.
|
|
5
5
|
Author-email: Xiangrui Cheng <innsdcc@gmail.com>, Yumeng Bao <baoyumeng.123@gmail.com>, Yaozheng Fang <fangyozheng@gmail.com>, Guodong Li <cu.eric.lee@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
agentkit/__init__.py,sha256=l27ZMDslc3VhmmnPZJyrqVvTDoZ0LqhCtM5hw0caHcU,1021
|
|
2
2
|
agentkit/errors.py,sha256=UWlXv0ZsXd_oLTMlxuf4u9-XkTdRYeVZueE--Z3JsHU,1214
|
|
3
|
-
agentkit/version.py,sha256=
|
|
4
|
-
agentkit/apps/__init__.py,sha256
|
|
3
|
+
agentkit/version.py,sha256=UZFajXkO3LMaoMxV_yKH2_pQpvhunh05rfkX_ftKnUQ,654
|
|
4
|
+
agentkit/apps/__init__.py,sha256=_Na1A4NSaMbVfPbsFKj2U-yI5mytD7W5skMaAOGbauY,2328
|
|
5
5
|
agentkit/apps/base_app.py,sha256=3hZZExL1wyTGWveJEZZoqXN086MzmkVS_WU1vyulIWg,754
|
|
6
6
|
agentkit/apps/utils.py,sha256=IzimIDmT6FS6PV9MLimPh46mq5zT-EjVmnYPxBdyEq0,1934
|
|
7
7
|
agentkit/apps/a2a_app/__init__.py,sha256=pkSabKw7_ai4NOo56pXKL40EcaxIDh6HYxPXOY7qWbo,634
|
|
8
8
|
agentkit/apps/a2a_app/a2a_app.py,sha256=mFuPFuqRlGdQ2VIjQq2_gtQKWwnkwSjSmIMRnammWJU,7605
|
|
9
9
|
agentkit/apps/a2a_app/telemetry.py,sha256=vR8tf7EHLor62cxs5PBx4mrfHeOaV1a7Xryqlu8nGCU,4290
|
|
10
10
|
agentkit/apps/agent_server_app/__init__.py,sha256=pkSabKw7_ai4NOo56pXKL40EcaxIDh6HYxPXOY7qWbo,634
|
|
11
|
-
agentkit/apps/agent_server_app/agent_server_app.py,sha256=
|
|
11
|
+
agentkit/apps/agent_server_app/agent_server_app.py,sha256=jq0DR6AZObeBQ1zZO_-qzO6n_ABov9lolL2g0n_ywPE,19535
|
|
12
12
|
agentkit/apps/agent_server_app/middleware.py,sha256=2gLCRN-JYjYsTQzHqRBU69S2OI8gD5AdbrFeoGzDcM0,2962
|
|
13
13
|
agentkit/apps/agent_server_app/origin.py,sha256=hzpftk8cW71uFw0KKsBAvrXiIOF8E6Bjo_MS5gDxSYM,5785
|
|
14
14
|
agentkit/apps/agent_server_app/telemetry.py,sha256=FLJsxJHj4Vg3sfIZnWYm442_Dv-zmxTIPqRgvgwVIFQ,4398
|
|
15
|
+
agentkit/apps/langgraph_server_app/__init__.py,sha256=ta7KORxpAc8cUGFusoTFBZFHuVaQbcsWst8CKBepg1c,146
|
|
16
|
+
agentkit/apps/langgraph_server_app/langgraph_server_app.py,sha256=gEqH6CTeuwE255m06IaTOoc7l5V3_OJF0tiForJqLaQ,29146
|
|
15
17
|
agentkit/apps/mcp_app/__init__.py,sha256=pkSabKw7_ai4NOo56pXKL40EcaxIDh6HYxPXOY7qWbo,634
|
|
16
18
|
agentkit/apps/mcp_app/mcp_app.py,sha256=2ngFbKcC3OsWNPwU0aoS-D8JVBC7-mFpXqj7sgcvUY8,5886
|
|
17
19
|
agentkit/apps/mcp_app/telemetry.py,sha256=ObzEoJrQK9Yu_3KsCslJejIhKkACZl5c87KHMFblvgI,3929
|
|
@@ -23,7 +25,7 @@ agentkit/auth/__init__.py,sha256=eIW6ziL3rtBYEupfmqq8MshZ40wL3muYjCQRikFG_j0,364
|
|
|
23
25
|
agentkit/auth/_openapi.py,sha256=ofFX7Ch-1xQeOvwUYRU4YwGKUqDgIfI8lsnk8Vbi12s,8230
|
|
24
26
|
agentkit/auth/_redact.py,sha256=Ef3yLlWj6jw0G6gZKy3cY64X6sm0jhzor8qx8b0Dp4A,911
|
|
25
27
|
agentkit/auth/_sigv4.py,sha256=yAbFD57g3cBvCkGTVJap0BCakQeukyR_FlzMDYZdTkw,3375
|
|
26
|
-
agentkit/auth/admin.py,sha256=
|
|
28
|
+
agentkit/auth/admin.py,sha256=JDyKIptLKMQOAp8DjUvrCRnJ2jER6cZUrXoUJcojMDU,23554
|
|
27
29
|
agentkit/auth/credential_hosting.py,sha256=lFFYseYHkqCUsv1djtK3EFR1XvESmw6l1Zgu1CcwIa8,24280
|
|
28
30
|
agentkit/auth/errors.py,sha256=eNNdzXP35QAlYy-jM5I9CQZCSS9rW0cp5_1F0I75JcU,1448
|
|
29
31
|
agentkit/auth/model_login.py,sha256=_30pbZgs22o4UtGF03dMgFRN9yE4OSyLqXJm9JhEe7E,13775
|
|
@@ -44,10 +46,10 @@ agentkit/frameworks/__init__.py,sha256=a9fu6cjLFn8Mq82sIo7rPT1gZGzi_QfCuFWpyoa2I
|
|
|
44
46
|
agentkit/frameworks/_common.py,sha256=0L0cyBQxtlOdi3GbQ_UgaOoHWwenztlKrxUsyvV3hXw,3859
|
|
45
47
|
agentkit/frameworks/agentcore.py,sha256=L5bdqzVukm7V6esIttPZSHFEc8JiqysU8YxA6_pBYRQ,12849
|
|
46
48
|
agentkit/frameworks/langchain.py,sha256=Tcy2HGYqKmcugDSn3OMaD_6uxhsJtZI3o5E8Rw7UJrg,5947
|
|
47
|
-
agentkit/frameworks/langgraph.py,sha256=
|
|
48
|
-
agentkit/frameworks/migration.py,sha256=
|
|
49
|
+
agentkit/frameworks/langgraph.py,sha256=hSYfh9Gy2vj3UM7zbpl4ZoawypTy6fFbz-Y0YrSS8p0,17651
|
|
50
|
+
agentkit/frameworks/migration.py,sha256=OTIsrqxVOWlrP10D7_2Zt1tGFNWEmmAHqsTuLB7YBhY,4480
|
|
49
51
|
agentkit/frameworks/model_replacement.py,sha256=EtRl617CZqYLNQ6CYKtK4XzObsv778jQCgWpBaJJahk,5196
|
|
50
|
-
agentkit/frameworks/strands.py,sha256=
|
|
52
|
+
agentkit/frameworks/strands.py,sha256=KFeTsdVpOyOh-SJvC3jcW8FdhR_5Y3NaGDd6zbuYrXE,20885
|
|
51
53
|
agentkit/frameworks/serving/__init__.py,sha256=EaR0jzP6np8v1gyD6CYlaRoIY1KgBaFosqCmRUgMc2E,302
|
|
52
54
|
agentkit/frameworks/serving/fastapi_mount.py,sha256=LV6gJunwd7aU8MFdDYSxeaIUYWvYwMl2Am1J07ddP0A,1958
|
|
53
55
|
agentkit/frameworks/serving/langserve.py,sha256=R2R0g95LHcSFJ1BnOm2cxnULv3wQaZNUdm5Aqd-Oq5s,17238
|
|
@@ -137,7 +139,7 @@ agentkit/toolkit/cli/sandbox/cli_shell.py,sha256=f-PJmIClEgsNrY-e8wKhc9th7r7H5_Z
|
|
|
137
139
|
agentkit/toolkit/cli/sandbox/cli_web.py,sha256=nvBEp99yqA3-sDNA_PBn7jOG_VgkHTw_8lDSvRNYSRA,2496
|
|
138
140
|
agentkit/toolkit/cli/sandbox/env_config.py,sha256=mQKKtRG7FXG0vhizEMpkzH9_g2agCQt9FDKgSGf-Xjs,26184
|
|
139
141
|
agentkit/toolkit/cli/sandbox/git_config.py,sha256=y0omhCCsH33XCMEpmbtAdHM8pevRFQdil1-JfOV8RTo,6097
|
|
140
|
-
agentkit/toolkit/cli/sandbox/model_config.py,sha256=
|
|
142
|
+
agentkit/toolkit/cli/sandbox/model_config.py,sha256=A3TagXhjYB1HWtsEDIRVUPmVOfc6c_e7qcipSkqCBLk,23752
|
|
141
143
|
agentkit/toolkit/cli/sandbox/sandbox_client.py,sha256=6HEmHOzXW7LVohGi4NmmeX3d-GQHyz6-C4svwI_Sfr8,15007
|
|
142
144
|
agentkit/toolkit/cli/sandbox/session_create.py,sha256=LOUqRxhvY4YmaVTnxLQBlLswpq6J6TDtOR6o9yt6Iz0,14947
|
|
143
145
|
agentkit/toolkit/cli/sandbox/session_sync.py,sha256=yLcRN4dw0dHHYk1kgDvjF8jBv5GnHV8xOLtKtsQTm6s,3152
|
|
@@ -251,9 +253,9 @@ agentkit/utils/redact.py,sha256=RAtiggxH74eb1Da6Gg0G5SLTVWRaIE5RqYnCozSZozQ,2167
|
|
|
251
253
|
agentkit/utils/request.py,sha256=1IGxPS3BSCKk1K1_j862_v34xzLEx04CIvrLVb1MsGQ,1625
|
|
252
254
|
agentkit/utils/template_utils.py,sha256=Qjg9V6dEpjAd_yYezIIPwuDsDeeMmp6XTMn5ZECifNc,6336
|
|
253
255
|
agentkit/utils/ve_sign.py,sha256=IsRKRshTKpOMkNv4bDJoNWUcsBaMGeq-jHCniPMAyo8,13327
|
|
254
|
-
agentkit_sdk_python-0.7.
|
|
255
|
-
agentkit_sdk_python-0.7.
|
|
256
|
-
agentkit_sdk_python-0.7.
|
|
257
|
-
agentkit_sdk_python-0.7.
|
|
258
|
-
agentkit_sdk_python-0.7.
|
|
259
|
-
agentkit_sdk_python-0.7.
|
|
256
|
+
agentkit_sdk_python-0.7.12.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
257
|
+
agentkit_sdk_python-0.7.12.dist-info/METADATA,sha256=K90dgH6c8sePnET9IhGsL-u8BwcOcT0E58EdsKMXG4U,21409
|
|
258
|
+
agentkit_sdk_python-0.7.12.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
259
|
+
agentkit_sdk_python-0.7.12.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
|
|
260
|
+
agentkit_sdk_python-0.7.12.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
|
|
261
|
+
agentkit_sdk_python-0.7.12.dist-info/RECORD,,
|
|
File without changes
|
{agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{agentkit_sdk_python-0.7.10.dist-info → agentkit_sdk_python-0.7.12.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|