agentkit-sdk-python 0.7.4__py3-none-any.whl → 0.7.6__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.
@@ -14,6 +14,7 @@ __all__ = [
14
14
  "LangChainAgentkitBridge",
15
15
  "LangGraphAgentkitBridge",
16
16
  "UnsupportedFrameworkAgentError",
17
+ "load_entry_object",
17
18
  ]
18
19
 
19
20
 
@@ -26,4 +27,8 @@ def __getattr__(name: str) -> Any:
26
27
  from agentkit.frameworks.langgraph import LangGraphAgentkitBridge
27
28
 
28
29
  return LangGraphAgentkitBridge
30
+ if name == "load_entry_object":
31
+ from agentkit.frameworks.migration import load_entry_object
32
+
33
+ return load_entry_object
29
34
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections.abc import AsyncGenerator
6
+ import hashlib
6
7
  import inspect
7
8
  import json
8
9
  from typing import Any
@@ -84,6 +85,35 @@ def _resume_value(text_input: str) -> Any:
84
85
  return text_input
85
86
 
86
87
 
88
+ def _update_output_text(data: Any) -> str:
89
+ """Extract user-facing output from LangGraph update payloads.
90
+
91
+ LangGraph ``stream_mode=["messages", "updates"]`` can emit internal LLM
92
+ message chunks as well as node state updates. Generic recursive extraction
93
+ is too broad for updates such as ``{"classify": {"route": "x"}}``; only
94
+ explicit output-like fields should override streamed message text.
95
+ """
96
+ if not isinstance(data, dict):
97
+ return chunk_to_text(data)
98
+
99
+ for key in ("answer", "output", "text", "content", "messages"):
100
+ if key in data:
101
+ text = chunk_to_text(data[key])
102
+ if text:
103
+ return text
104
+
105
+ latest = ""
106
+ for value in data.values():
107
+ if not isinstance(value, dict):
108
+ continue
109
+ for key in ("answer", "output", "text", "content", "messages"):
110
+ if key in value:
111
+ text = chunk_to_text(value[key])
112
+ if text:
113
+ latest = text
114
+ return latest
115
+
116
+
87
117
  class LangGraphAgentkitBridge(BaseAgent):
88
118
  """Adapt a compiled LangGraph graph to AgentKit's ADK runtime boundary."""
89
119
 
@@ -112,7 +142,15 @@ class LangGraphAgentkitBridge(BaseAgent):
112
142
  session_id = getattr(session, "id", None)
113
143
  if not session_id:
114
144
  return None
115
- return {"configurable": {"thread_id": session_id}}
145
+ app_name = getattr(session, "app_name", None)
146
+ user_id = getattr(session, "user_id", None)
147
+ if app_name and user_id:
148
+ raw_identity = f"{app_name}\0{user_id}\0{session_id}"
149
+ digest = hashlib.sha256(raw_identity.encode("utf-8")).hexdigest()
150
+ thread_id = f"agentkit:{digest}"
151
+ else:
152
+ thread_id = session_id
153
+ return {"configurable": {"thread_id": thread_id}}
116
154
 
117
155
  def _pending_interrupt(self, ctx: InvocationContext) -> Any | None:
118
156
  session = getattr(ctx, "session", None)
@@ -189,32 +227,37 @@ class LangGraphAgentkitBridge(BaseAgent):
189
227
  stream_fn = getattr(self._graph, "stream", None)
190
228
  if not callable(stream_fn):
191
229
  return None
192
- try:
193
- return stream_fn(
194
- payload,
195
- **_method_kwargs(
196
- stream_fn,
197
- {
198
- "config": config,
199
- "stream_mode": ["messages", "updates"],
200
- "version": "v2",
201
- },
202
- ),
203
- )
204
- except TypeError:
205
- try:
206
- return stream_fn(
207
- payload,
208
- **_method_kwargs(
209
- stream_fn,
210
- {
211
- "config": config,
212
- "stream_mode": ["messages", "updates"],
213
- },
214
- ),
215
- )
216
- except TypeError:
217
- return stream_fn(payload, **_method_kwargs(stream_fn, {"config": config}))
230
+ attempts = [
231
+ {
232
+ "config": config,
233
+ "stream_mode": ["messages", "updates"],
234
+ "version": "v2",
235
+ },
236
+ {
237
+ "config": config,
238
+ "stream_mode": ["messages", "updates"],
239
+ },
240
+ {"config": config},
241
+ ]
242
+
243
+ def iterate_attempts():
244
+ last_error: TypeError | None = None
245
+ for kwargs in attempts:
246
+ emitted = False
247
+ try:
248
+ stream = stream_fn(payload, **_method_kwargs(stream_fn, kwargs))
249
+ for item in stream:
250
+ emitted = True
251
+ yield item
252
+ return
253
+ except TypeError as exc:
254
+ if emitted:
255
+ raise
256
+ last_error = exc
257
+ if last_error is not None:
258
+ raise last_error
259
+
260
+ return iterate_attempts()
218
261
 
219
262
  async def _stream_graph(self, payload: Any, config: dict[str, Any] | None, *, prefer_sync: bool = False):
220
263
  if prefer_sync:
@@ -226,35 +269,33 @@ class LangGraphAgentkitBridge(BaseAgent):
226
269
 
227
270
  astream = getattr(self._graph, "astream", None)
228
271
  if callable(astream):
229
- try:
230
- stream = astream(
231
- payload,
232
- **_method_kwargs(
233
- astream,
234
- {
235
- "config": config,
236
- "stream_mode": ["messages", "updates"],
237
- "version": "v2",
238
- },
239
- ),
240
- )
241
- except TypeError:
272
+ attempts = [
273
+ {
274
+ "config": config,
275
+ "stream_mode": ["messages", "updates"],
276
+ "version": "v2",
277
+ },
278
+ {
279
+ "config": config,
280
+ "stream_mode": ["messages", "updates"],
281
+ },
282
+ {"config": config},
283
+ ]
284
+ last_error: TypeError | None = None
285
+ for kwargs in attempts:
286
+ emitted = False
242
287
  try:
243
- stream = astream(
244
- payload,
245
- **_method_kwargs(
246
- astream,
247
- {
248
- "config": config,
249
- "stream_mode": ["messages", "updates"],
250
- },
251
- ),
252
- )
253
- except TypeError:
254
- stream = astream(payload, **_method_kwargs(astream, {"config": config}))
255
- async for item in stream:
256
- yield item
257
- return
288
+ stream = astream(payload, **_method_kwargs(astream, kwargs))
289
+ async for item in stream:
290
+ emitted = True
291
+ yield item
292
+ return
293
+ except TypeError as exc:
294
+ if emitted:
295
+ raise
296
+ last_error = exc
297
+ if last_error is not None:
298
+ raise last_error
258
299
 
259
300
  stream = self._sync_stream(payload, config)
260
301
  if stream is not None:
@@ -318,9 +359,7 @@ class LangGraphAgentkitBridge(BaseAgent):
318
359
  )
319
360
  return
320
361
  last_update = data
321
- if saw_messages:
322
- continue
323
- text = chunk_to_text(data)
362
+ text = _update_output_text(data)
324
363
  if text:
325
364
  pending_update_text = text
326
365
  continue
@@ -334,10 +373,11 @@ class LangGraphAgentkitBridge(BaseAgent):
334
373
  yield adk_event(ctx, self.name, delta, partial=True)
335
374
 
336
375
  if streamed:
376
+ update_text = pending_update_text or _update_output_text(last_update)
337
377
  if saw_messages or has_output:
338
- final_text = accumulated_text
378
+ final_text = update_text or accumulated_text
339
379
  else:
340
- final_text = pending_update_text or chunk_to_text(last_update)
380
+ final_text = update_text
341
381
  if final_text:
342
382
  yield adk_event(ctx, self.name, final_text, partial=True)
343
383
  else:
@@ -0,0 +1,135 @@
1
+ """Helpers used by generated framework migration apps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import importlib.util
7
+ import inspect
8
+ from pathlib import Path
9
+ import sys
10
+ from types import ModuleType
11
+ from typing import Any
12
+
13
+
14
+ def _prepend_sys_path(path: Path) -> None:
15
+ value = str(path)
16
+ if value not in sys.path:
17
+ sys.path.insert(0, value)
18
+
19
+
20
+ def _load_module_from_file(entry_path: Path, import_name: str) -> ModuleType:
21
+ if not entry_path.is_file():
22
+ raise FileNotFoundError(f"Entry file does not exist: {entry_path}")
23
+
24
+ spec = importlib.util.spec_from_file_location(import_name, entry_path)
25
+ if spec is None or spec.loader is None:
26
+ raise RuntimeError(f"Cannot load entry module from {entry_path}")
27
+
28
+ module = importlib.util.module_from_spec(spec)
29
+ sys.modules[import_name] = module
30
+ try:
31
+ spec.loader.exec_module(module)
32
+ except Exception:
33
+ if sys.modules.get(import_name) is module:
34
+ del sys.modules[import_name]
35
+ raise
36
+ return module
37
+
38
+
39
+ def _resolve_object(module: ModuleType, object_path: str) -> Any:
40
+ if not object_path:
41
+ raise ValueError("entry object path is required")
42
+
43
+ target: Any = module
44
+ for attr in object_path.split("."):
45
+ if not attr:
46
+ raise ValueError(
47
+ f"entry object path contains an empty attribute: {object_path!r}"
48
+ )
49
+ try:
50
+ target = getattr(target, attr)
51
+ except AttributeError as exc:
52
+ raise AttributeError(
53
+ f"Entry object {object_path!r} was not found; "
54
+ f"missing attribute {attr!r} on {target!r}."
55
+ ) from exc
56
+ return target
57
+
58
+
59
+ def _call_zero_arg_factory(target: Any, object_path: str) -> Any:
60
+ if not callable(target):
61
+ raise TypeError(
62
+ f"Entry object {object_path!r} was marked as a factory, "
63
+ "but the loaded object is not callable."
64
+ )
65
+
66
+ try:
67
+ signature = inspect.signature(target)
68
+ except (TypeError, ValueError) as exc:
69
+ raise TypeError(
70
+ f"Entry factory {object_path!r} has no inspectable signature. "
71
+ "Expose a zero-argument factory or a constructed agent object."
72
+ ) from exc
73
+
74
+ required_params = [
75
+ name
76
+ for name, param in signature.parameters.items()
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 required_params:
86
+ formatted = ", ".join(required_params)
87
+ raise TypeError(
88
+ f"Entry factory {object_path!r} requires arguments: {formatted}. "
89
+ "agentkit migrate can only call zero-argument factories; expose a "
90
+ "thin zero-argument entry object for migration."
91
+ )
92
+
93
+ result = target()
94
+ if inspect.isawaitable(result):
95
+ raise TypeError(
96
+ f"Entry factory {object_path!r} returned an awaitable object. "
97
+ "Async factories are not supported by generated migration apps; "
98
+ "expose a synchronous factory or a constructed agent object."
99
+ )
100
+ return result
101
+
102
+
103
+ def load_entry_object(
104
+ *,
105
+ file: str,
106
+ object_path: str,
107
+ module: str | None = None,
108
+ project_root: str | Path = ".",
109
+ base_dir: str | Path | None = None,
110
+ import_name: str = "agentkit_migrated_entry",
111
+ call_factory: bool = False,
112
+ ) -> Any:
113
+ """Load an object from a migrated project's original entry reference.
114
+
115
+ The generated migration app lives beside or below the user's project files.
116
+ This helper keeps that app small while preserving the import behavior that
117
+ users expect from running their original project.
118
+ """
119
+
120
+ base_path = (
121
+ Path(base_dir).resolve() if base_dir is not None else Path.cwd().resolve()
122
+ )
123
+ project_root_path = (base_path / project_root).resolve()
124
+ _prepend_sys_path(project_root_path)
125
+
126
+ if module:
127
+ loaded_module = importlib.import_module(module)
128
+ else:
129
+ entry_path = (base_path / file).resolve()
130
+ loaded_module = _load_module_from_file(entry_path, import_name)
131
+
132
+ target = _resolve_object(loaded_module, object_path)
133
+ if call_factory:
134
+ return _call_zero_arg_factory(target, object_path)
135
+ return target
agentkit/version.py CHANGED
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- VERSION = "0.7.4"
15
+ VERSION = "0.7.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentkit-sdk-python
3
- Version: 0.7.4
3
+ Version: 0.7.6
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
@@ -340,6 +340,40 @@ pip install agentkit-sdk-python==1.0.0.dev1
340
340
 
341
341
  **Note**: Development versions may contain bugs and are not recommended for production use.
342
342
 
343
+ ## Release Process
344
+
345
+ Stable releases are published from GitHub tags. The source tree keeps the
346
+ release version in `pyproject.toml` and `agentkit/version.py`, and the GitHub
347
+ workflow verifies that the pushed tag matches those files before building and
348
+ publishing the package.
349
+
350
+ The release workflow must already exist on `main` before you push the release
351
+ tag. For `0.7.5`, merge the tag-release automation change first, then create
352
+ and push `v0.7.5`.
353
+
354
+ Use the following workflow for a stable release:
355
+
356
+ ```bash
357
+ git checkout main
358
+ git pull github main
359
+ git tag v0.7.5
360
+ git push github v0.7.5
361
+ ```
362
+
363
+ When the tag reaches GitHub, `.github/workflows/release-pypi.yml` will:
364
+
365
+ - verify the tag is reachable from `main`
366
+ - verify that `pyproject.toml` and `agentkit/version.py` match the tag version
367
+ - build the wheel and source distribution from the tagged commit
368
+ - verify the built artifacts contain the expected version metadata
369
+ - publish the release to PyPI using the GitHub Actions secret `PYPI_API_TOKEN`
370
+
371
+ The internal `local_build.py` helper is a separate local-only workflow and is
372
+ not used by the GitHub tag release.
373
+
374
+ Before using the GitHub tag release workflow, configure a project-scoped PyPI
375
+ API token in the repository's GitHub Actions secrets as `PYPI_API_TOKEN`.
376
+
343
377
  ## Security and privacy
344
378
 
345
379
  This project takes security seriously.
@@ -1,6 +1,6 @@
1
1
  agentkit/__init__.py,sha256=l27ZMDslc3VhmmnPZJyrqVvTDoZ0LqhCtM5hw0caHcU,1021
2
2
  agentkit/errors.py,sha256=UWlXv0ZsXd_oLTMlxuf4u9-XkTdRYeVZueE--Z3JsHU,1214
3
- agentkit/version.py,sha256=S9zUkhZqaZ0llqw_JF_CXK5OsV7vd2EIpTBKb38hgCI,653
3
+ agentkit/version.py,sha256=oFLDChAr94DBKT7M7QxgZ_7Ypdo7s4CRVCDwIb-kkCk,653
4
4
  agentkit/apps/__init__.py,sha256=-oXTjxV3gejpJ8pffTUcUAXw0LfxKCYZJk-_hIJhtLM,1921
5
5
  agentkit/apps/base_app.py,sha256=3hZZExL1wyTGWveJEZZoqXN086MzmkVS_WU1vyulIWg,754
6
6
  agentkit/apps/utils.py,sha256=IzimIDmT6FS6PV9MLimPh46mq5zT-EjVmnYPxBdyEq0,1934
@@ -40,10 +40,11 @@ agentkit/client/__init__.py,sha256=WHKgNGkpYsJp0xD20drYcxAMDclwDqrfURqxEVv0V1Y,1
40
40
  agentkit/client/base_agentkit_client.py,sha256=NV1m2FtboUcMdVUJL2ep75nAwDQ42roiWgl3YKaUSn8,2955
41
41
  agentkit/client/base_iam_client.py,sha256=F3ggcZjt14CAcgDYpncF2XGr1zTGnhaOzqEJl4NGHwU,2432
42
42
  agentkit/client/base_service_client.py,sha256=zpPUidGMDRAMDnSVjrX7BwuOp4BQdAEk0b8HoXks0TE,15049
43
- agentkit/frameworks/__init__.py,sha256=NPT7kxcY7shMyA2m1QoxhXzAc9JEvDCNFyWupedTCSY,808
43
+ agentkit/frameworks/__init__.py,sha256=cfUdMeixph0xAo6B5qGQBBdm8LeQbWZIl1vmO9cEL3E,971
44
44
  agentkit/frameworks/_common.py,sha256=0L0cyBQxtlOdi3GbQ_UgaOoHWwenztlKrxUsyvV3hXw,3859
45
45
  agentkit/frameworks/langchain.py,sha256=Tcy2HGYqKmcugDSn3OMaD_6uxhsJtZI3o5E8Rw7UJrg,5947
46
- agentkit/frameworks/langgraph.py,sha256=CHhaX6DcE8McC6Cs31gexEJHqJJ7cFiqh8D6vyQ8FVE,12770
46
+ agentkit/frameworks/langgraph.py,sha256=FX8QHjfS4QHSTGq4oYwQ7MytrUejDFmVczihNPCts4A,14201
47
+ agentkit/frameworks/migration.py,sha256=j8MxFvAFKkDsTy_e1l5bN-B276AE7rwxLrHuBodNcfc,4385
47
48
  agentkit/frameworks/serving/__init__.py,sha256=EaR0jzP6np8v1gyD6CYlaRoIY1KgBaFosqCmRUgMc2E,302
48
49
  agentkit/frameworks/serving/fastapi_mount.py,sha256=LV6gJunwd7aU8MFdDYSxeaIUYWvYwMl2Am1J07ddP0A,1958
49
50
  agentkit/frameworks/serving/langserve.py,sha256=R2R0g95LHcSFJ1BnOm2cxnULv3wQaZNUdm5Aqd-Oq5s,17238
@@ -178,10 +179,6 @@ agentkit/toolkit/resources/samples/financial_analyst.py,sha256=5lkkvtG6u2SlLg_Mw
178
179
  agentkit/toolkit/resources/samples/langchain_basic_stream.py,sha256=hSnysOQxnzyZ4NmHUyxmJdmEMsVWlRlg8HUz12Gt5fY,3229
179
180
  agentkit/toolkit/resources/samples/simple_app_veadk.py,sha256=pzO1cCwZsNdAT-to7xRivBDBfqek_zGxhT5xdGJOVkY,2215
180
181
  agentkit/toolkit/resources/samples/simple_mcp_veadk.py,sha256=VguGNLGIbv1O-11bgbNX4UIEX0lpeULLZw0Cn1ayWEA,1696
181
- agentkit/toolkit/resources/samples/__pycache__/customer_support_assistant.cpython-312.pyc,sha256=KQUfeIRfNZRGcd7dCWavgrmOJ2alT8qTUq4p56TGTTo,208
182
- agentkit/toolkit/resources/samples/__pycache__/financial_analyst.cpython-312.pyc,sha256=EkaSa3UQaNUbvtxblrjV3KQtbywIqaZTWSkzaF31-AE,4373
183
- agentkit/toolkit/resources/samples/__pycache__/simple_app_veadk.cpython-312.pyc,sha256=p3D45XVsVFUZf5Z3bhaGwj4vqSZ2tcqM2t8Zq2hhoR4,2332
184
- agentkit/toolkit/resources/samples/__pycache__/simple_mcp_veadk.cpython-312.pyc,sha256=w47SK2g3D4H8JHryuvJQF2oU9EYYQBPdsGr9DOzG0oQ,1722
185
182
  agentkit/toolkit/resources/samples/eino_a2a/agent.go,sha256=__TJxBHJ4o3SPv2VUVGiNP32Hd8aYd79i22GlXFl1Xo,2010
186
183
  agentkit/toolkit/resources/samples/eino_a2a/build.sh,sha256=GbUBI8p_hs6j1LW93VG1RmwWfo5ECW3Bpvoc96bLe9s,1683
187
184
  agentkit/toolkit/resources/samples/eino_a2a/go.mod,sha256=ruHkgBsD0mCutShombeZVH1Kh0S0sydXGSVoY3VCkM8,6742
@@ -244,9 +241,9 @@ agentkit/utils/redact.py,sha256=RAtiggxH74eb1Da6Gg0G5SLTVWRaIE5RqYnCozSZozQ,2167
244
241
  agentkit/utils/request.py,sha256=1IGxPS3BSCKk1K1_j862_v34xzLEx04CIvrLVb1MsGQ,1625
245
242
  agentkit/utils/template_utils.py,sha256=Qjg9V6dEpjAd_yYezIIPwuDsDeeMmp6XTMn5ZECifNc,6336
246
243
  agentkit/utils/ve_sign.py,sha256=JfKig7zni8KvdhgbLJXm3Zy_ttyzKrYGUmBY_82_A4Q,12762
247
- agentkit_sdk_python-0.7.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
248
- agentkit_sdk_python-0.7.4.dist-info/METADATA,sha256=s47zJ5ciuJGmqJoi4WcqSMqz8lUM4HWt3eAHAQ4kKGo,19880
249
- agentkit_sdk_python-0.7.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
250
- agentkit_sdk_python-0.7.4.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
251
- agentkit_sdk_python-0.7.4.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
252
- agentkit_sdk_python-0.7.4.dist-info/RECORD,,
244
+ agentkit_sdk_python-0.7.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
245
+ agentkit_sdk_python-0.7.6.dist-info/METADATA,sha256=qyc849JYVuXgxK90Hcc_20C9n8xCerH5S8Z9WnV8ReU,21144
246
+ agentkit_sdk_python-0.7.6.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
247
+ agentkit_sdk_python-0.7.6.dist-info/entry_points.txt,sha256=fhzZUsvsLXeB4mPaa0SBQiTBqFT404uDAKPaePAOUAE,58
248
+ agentkit_sdk_python-0.7.6.dist-info/top_level.txt,sha256=ipy8JF-QQ-V0C1oRFLxsyaW8zwrasfJ-zjAh9vgOc7U,9
249
+ agentkit_sdk_python-0.7.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (83.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5