gohumanloop 0.0.10__py3-none-any.whl → 0.0.11__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.
- gohumanloop/adapters/base_adapter.py +33 -13
- gohumanloop/utils/__init__.py +2 -4
- gohumanloop/utils/context_formatter.py +136 -64
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/METADATA +1 -1
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/RECORD +9 -9
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/WHEEL +0 -0
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/entry_points.txt +0 -0
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/licenses/LICENSE +0 -0
- {gohumanloop-0.0.10.dist-info → gohumanloop-0.0.11.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,7 @@ from inspect import iscoroutinefunction
|
|
19
19
|
from contextlib import asynccontextmanager, contextmanager
|
20
20
|
import logging
|
21
21
|
|
22
|
-
from gohumanloop.utils import run_async_safely
|
22
|
+
from gohumanloop.utils import run_async_safely, generate_function_summary
|
23
23
|
from gohumanloop.core.interface import (
|
24
24
|
HumanLoopRequest,
|
25
25
|
HumanLoopResult,
|
@@ -241,6 +241,12 @@ class HumanloopAdapter:
|
|
241
241
|
|
242
242
|
@wraps(fn)
|
243
243
|
async def async_wrapper(*args: Any, **kwargs: Any) -> R:
|
244
|
+
# Check if ret_key exists in function parameters
|
245
|
+
if ret_key not in fn.__code__.co_varnames:
|
246
|
+
raise ValueError(
|
247
|
+
f"Function {fn.__name__} must have parameter named {ret_key}"
|
248
|
+
)
|
249
|
+
|
244
250
|
# Determine if callback is instance or factory function
|
245
251
|
cb = None
|
246
252
|
if callable(callback) and not isinstance(callback, HumanLoopCallback):
|
@@ -255,7 +261,8 @@ class HumanloopAdapter:
|
|
255
261
|
conversation_id=conversation_id,
|
256
262
|
loop_type=HumanLoopType.APPROVAL,
|
257
263
|
context={
|
258
|
-
"message":
|
264
|
+
"message": generate_function_summary(fn, args, kwargs),
|
265
|
+
"function": {
|
259
266
|
"function_name": fn.__name__,
|
260
267
|
"function_signature": str(fn.__code__.co_varnames),
|
261
268
|
"arguments": str(args),
|
@@ -289,7 +296,9 @@ class HumanloopAdapter:
|
|
289
296
|
"error": result.error,
|
290
297
|
}
|
291
298
|
|
292
|
-
|
299
|
+
# Inject approval info into kwargs
|
300
|
+
kwargs[ret_key] = approval_info
|
301
|
+
|
293
302
|
# Check approval result
|
294
303
|
if isinstance(result, HumanLoopResult):
|
295
304
|
# Handle based on approval status
|
@@ -426,6 +435,12 @@ class HumanloopAdapter:
|
|
426
435
|
|
427
436
|
@wraps(fn)
|
428
437
|
async def async_wrapper(*args: Any, **kwargs: Any) -> R:
|
438
|
+
# Check if ret_key exists in function parameters
|
439
|
+
if ret_key not in fn.__code__.co_varnames:
|
440
|
+
raise ValueError(
|
441
|
+
f"Function {fn.__name__} must have parameter named {ret_key}"
|
442
|
+
)
|
443
|
+
|
429
444
|
# Determine if callback is instance or factory function
|
430
445
|
cb = None
|
431
446
|
state = args[0] if args else None
|
@@ -516,8 +531,8 @@ class HumanloopAdapter:
|
|
516
531
|
"responded_at": result.responded_at,
|
517
532
|
"error": result.error,
|
518
533
|
}
|
519
|
-
|
520
|
-
|
534
|
+
# Inject conversation info into kwargs
|
535
|
+
kwargs[ret_key] = conversation_info
|
521
536
|
|
522
537
|
if isinstance(result, HumanLoopResult):
|
523
538
|
if iscoroutinefunction(fn):
|
@@ -631,6 +646,12 @@ class HumanloopAdapter:
|
|
631
646
|
|
632
647
|
@wraps(fn)
|
633
648
|
async def async_wrapper(*args: Any, **kwargs: Any) -> R:
|
649
|
+
# Check if ret_key exists in function parameters
|
650
|
+
if ret_key not in fn.__code__.co_varnames:
|
651
|
+
raise ValueError(
|
652
|
+
f"Function {fn.__name__} must have parameter named {ret_key}"
|
653
|
+
)
|
654
|
+
|
634
655
|
# Determine if callback is an instance or factory function
|
635
656
|
# callback: can be HumanLoopCallback instance or factory function
|
636
657
|
# - If factory function: accepts state parameter and returns HumanLoopCallback instance
|
@@ -665,12 +686,11 @@ class HumanloopAdapter:
|
|
665
686
|
provider_id=provider_id,
|
666
687
|
blocking=True,
|
667
688
|
)
|
668
|
-
|
669
|
-
# 初始化审批结果对象为None
|
689
|
+
# Initialize response info object as None
|
670
690
|
resp_info = None
|
671
691
|
|
672
692
|
if isinstance(result, HumanLoopResult):
|
673
|
-
#
|
693
|
+
# If result is HumanLoopResult type, build complete response info
|
674
694
|
resp_info = {
|
675
695
|
"conversation_id": result.conversation_id,
|
676
696
|
"request_id": result.request_id,
|
@@ -682,12 +702,12 @@ class HumanloopAdapter:
|
|
682
702
|
"responded_at": result.responded_at,
|
683
703
|
"error": result.error,
|
684
704
|
}
|
705
|
+
# Inject approval info into kwargs
|
706
|
+
kwargs[ret_key] = resp_info
|
685
707
|
|
686
|
-
|
687
|
-
|
688
|
-
# 检查结果是否有效
|
708
|
+
# Check if result is valid
|
689
709
|
if isinstance(result, HumanLoopResult):
|
690
|
-
#
|
710
|
+
# Return the information result, let user decide whether to use it
|
691
711
|
if iscoroutinefunction(fn):
|
692
712
|
ret = await fn(*args, **kwargs)
|
693
713
|
else:
|
@@ -701,7 +721,7 @@ class HumanloopAdapter:
|
|
701
721
|
ret = run_async_safely(async_wrapper(*args, **kwargs))
|
702
722
|
return cast(R, ret)
|
703
723
|
|
704
|
-
#
|
724
|
+
# Return corresponding wrapper based on decorated function type
|
705
725
|
if iscoroutinefunction(fn):
|
706
726
|
return async_wrapper
|
707
727
|
return sync_wrapper
|
gohumanloop/utils/__init__.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
from .utils import run_async_safely, get_secret_from_env
|
2
|
+
from .context_formatter import generate_function_summary
|
2
3
|
|
3
4
|
|
4
|
-
__all__ = [
|
5
|
-
"run_async_safely",
|
6
|
-
"get_secret_from_env",
|
7
|
-
]
|
5
|
+
__all__ = ["run_async_safely", "get_secret_from_env", "generate_function_summary"]
|
@@ -1,64 +1,136 @@
|
|
1
|
-
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
1
|
+
import inspect
|
2
|
+
from typing import Callable, Any, Dict, Union, cast
|
3
|
+
|
4
|
+
|
5
|
+
def _param_detail_zh(p: inspect.Parameter) -> str:
|
6
|
+
return (
|
7
|
+
f" {p.name}: "
|
8
|
+
+ (f"类型[{p.annotation}] " if p.annotation != p.empty else "")
|
9
|
+
+ (f"(默认值={p.default})" if p.default != p.empty else "")
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
def _param_detail_en(p: inspect.Parameter) -> str:
|
14
|
+
return (
|
15
|
+
f" {p.name}: "
|
16
|
+
+ (f"Type[{p.annotation}] " if p.annotation != p.empty else "")
|
17
|
+
+ (f"(default={p.default})" if p.default != p.empty else "")
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
def generate_function_summary(
|
22
|
+
fn: Callable,
|
23
|
+
*args: Any,
|
24
|
+
language: str = "zh",
|
25
|
+
**kwargs: Any,
|
26
|
+
) -> str:
|
27
|
+
"""生成支持中英文切换的函数说明模板
|
28
|
+
|
29
|
+
Args:
|
30
|
+
fn: 目标函数
|
31
|
+
*args: 位置参数示例
|
32
|
+
**kwargs: 关键字参数示例
|
33
|
+
language: 输出语言 ('zh'/'en')
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
纯文本格式的函数说明
|
37
|
+
"""
|
38
|
+
|
39
|
+
# 定义翻译类型
|
40
|
+
|
41
|
+
# 语言配置
|
42
|
+
translations: Dict[
|
43
|
+
str, Dict[str, Union[str, Callable[[inspect.Parameter], str]]]
|
44
|
+
] = {
|
45
|
+
"zh": {
|
46
|
+
"title": "函数说明",
|
47
|
+
"name": "函数名称",
|
48
|
+
"module": "所属模块",
|
49
|
+
"description": "功能描述",
|
50
|
+
"params": "参数列表",
|
51
|
+
"param_detail": _param_detail_zh,
|
52
|
+
"return": "返回值类型",
|
53
|
+
"current_input": "当前输入",
|
54
|
+
"positional_args": "位置参数",
|
55
|
+
"keyword_args": "关键字参数",
|
56
|
+
"usage": "调用方式",
|
57
|
+
"approval": "审批状态",
|
58
|
+
"no_doc": "无文档说明",
|
59
|
+
},
|
60
|
+
"en": {
|
61
|
+
"title": "Function Documentation",
|
62
|
+
"name": "Function Name",
|
63
|
+
"module": "Module",
|
64
|
+
"description": "Description",
|
65
|
+
"params": "Parameters",
|
66
|
+
"param_detail": _param_detail_en,
|
67
|
+
"return": "Return Type",
|
68
|
+
"current_input": "Current Input",
|
69
|
+
"positional_args": "Positional Args",
|
70
|
+
"keyword_args": "Keyword Args",
|
71
|
+
"usage": "Usage",
|
72
|
+
"approval": "Approval Status",
|
73
|
+
"no_doc": "No documentation",
|
74
|
+
},
|
75
|
+
}
|
76
|
+
|
77
|
+
lang = translations.get(language, translations["zh"])
|
78
|
+
|
79
|
+
# 明确告诉类型检查器这是一个可调用对象
|
80
|
+
param_detail_func = cast(Callable[[inspect.Parameter], str], lang["param_detail"])
|
81
|
+
|
82
|
+
# 获取函数信息
|
83
|
+
func_name = fn.__name__
|
84
|
+
module_obj = inspect.getmodule(fn)
|
85
|
+
module = module_obj.__name__ if module_obj is not None else str(lang["no_doc"])
|
86
|
+
doc = (fn.__doc__ or str(lang["no_doc"])).strip()
|
87
|
+
sig = inspect.signature(fn)
|
88
|
+
|
89
|
+
# 构建模板
|
90
|
+
template = (
|
91
|
+
f"""
|
92
|
+
- {lang['title']}: {func_name}
|
93
|
+
- {lang['name']}: {func_name}
|
94
|
+
- {lang['module']}: {module}
|
95
|
+
|
96
|
+
- {lang['description']}:
|
97
|
+
{doc}
|
98
|
+
|
99
|
+
- {lang['params']}:
|
100
|
+
"""
|
101
|
+
+ "\n".join(param_detail_func(p) for p in sig.parameters.values())
|
102
|
+
+ f"""
|
103
|
+
|
104
|
+
- {lang['return']}: {sig.return_annotation if sig.return_annotation != sig.empty else lang['no_doc']}
|
105
|
+
|
106
|
+
- {lang['current_input']}:
|
107
|
+
{lang['positional_args']}: {args}
|
108
|
+
{lang['keyword_args']}: {kwargs}
|
109
|
+
|
110
|
+
- {lang['usage']}: {func_name}(*{args}, **{kwargs})
|
111
|
+
|
112
|
+
"""
|
113
|
+
)
|
114
|
+
return template.strip()
|
115
|
+
|
116
|
+
|
117
|
+
if __name__ == "__main__":
|
118
|
+
# 示例函数
|
119
|
+
def calculate(a: int, b: float = 1.0) -> float:
|
120
|
+
"""计算两个数的乘积/Calculate the product of two numbers"""
|
121
|
+
return a * b
|
122
|
+
|
123
|
+
# 中文输出
|
124
|
+
print("==== 中文版 ====")
|
125
|
+
print(generate_function_summary(calculate, 3, b=2.5))
|
126
|
+
|
127
|
+
# 英文输出
|
128
|
+
print("\n==== English Version ====")
|
129
|
+
print(
|
130
|
+
generate_function_summary(
|
131
|
+
calculate,
|
132
|
+
3,
|
133
|
+
language="en",
|
134
|
+
b=2.5,
|
135
|
+
)
|
136
|
+
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
gohumanloop/__init__.py,sha256=KiY2ncM6OQvkBYcz5rsbob_GuV8hQHOARA-EsxAylEU,2134
|
2
2
|
gohumanloop/__main__.py,sha256=zdGKN92H9SgwZfL4xLqPkE1YaiRcHhVg_GqC-H1VurA,75
|
3
3
|
gohumanloop/adapters/__init__.py,sha256=_XqJ6eaUBLJJJyjfBQJykBZ4X9HvC3VYOVTn2KnBlqo,484
|
4
|
-
gohumanloop/adapters/base_adapter.py,sha256=
|
4
|
+
gohumanloop/adapters/base_adapter.py,sha256=v5oEeWhdnUI6QL9G-JziMIJmg30RxSozVttAaxZwmmo,33926
|
5
5
|
gohumanloop/adapters/langgraph_adapter.py,sha256=AHJv_b6g8xjeq_9X2cGiW76pSGlfgCGBUvjrZQcbR9s,11621
|
6
6
|
gohumanloop/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
gohumanloop/cli/main.py,sha256=Txjk31MlkiX9zeHZfFEXEu0s2IBESY8sxrxBEzZKk2U,767
|
@@ -19,13 +19,13 @@ gohumanloop/providers/base.py,sha256=rOA9xRTh-rn-64RIZpzcIhWNar8LEzYrEXS5H5r7PpM
|
|
19
19
|
gohumanloop/providers/email_provider.py,sha256=kQA6kG7dqpV6R1cJUeaanLmxMVk9pcIwweNZA4IC-2o,45947
|
20
20
|
gohumanloop/providers/ghl_provider.py,sha256=NmOac2LR0d87pc91sMuYtvTYsKdp4wZrhfmSYuZL2ko,2350
|
21
21
|
gohumanloop/providers/terminal_provider.py,sha256=aBNgGpspiu7fGigN6K-gqGlyjFDFvIqKMGp58PxGyPg,15447
|
22
|
-
gohumanloop/utils/__init__.py,sha256=
|
23
|
-
gohumanloop/utils/context_formatter.py,sha256=
|
22
|
+
gohumanloop/utils/__init__.py,sha256=WoxyK2JXOp_JtKGTxyH3Drly8hpDXxCTV2-QoHY5N0s,199
|
23
|
+
gohumanloop/utils/context_formatter.py,sha256=2ybnESv4-f4F1VKPkm81sbWE6e5h3LHljm5CczdznXg,3783
|
24
24
|
gohumanloop/utils/threadsafedict.py,sha256=9uyewnwmvS3u1fCx3SK0YWFxHMcyIwlye1Ev7WW7WHA,9588
|
25
25
|
gohumanloop/utils/utils.py,sha256=O0zFtMFloAkWylmZE7WpmAjbDe385ZBfuUEh7NIwgGE,2148
|
26
|
-
gohumanloop-0.0.
|
27
|
-
gohumanloop-0.0.
|
28
|
-
gohumanloop-0.0.
|
29
|
-
gohumanloop-0.0.
|
30
|
-
gohumanloop-0.0.
|
31
|
-
gohumanloop-0.0.
|
26
|
+
gohumanloop-0.0.11.dist-info/licenses/LICENSE,sha256=-U5tuCcSpndQwSKWtZbFbazb-_AtZcZL2kQgHbSLg-M,1064
|
27
|
+
gohumanloop-0.0.11.dist-info/METADATA,sha256=F3dCnFSie_t_P7Ttw93XPCQCHeRvs-N4oPPQeXU_76w,11428
|
28
|
+
gohumanloop-0.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
29
|
+
gohumanloop-0.0.11.dist-info/entry_points.txt,sha256=wM6jqRRD8bQXkvIduRVCuAJIlbyWg_F5EDXo5OZ_PwY,88
|
30
|
+
gohumanloop-0.0.11.dist-info/top_level.txt,sha256=LvOXBqS6Mspmcuqp81uz0Vjx_m_YI0w06DOPCiI1BfY,12
|
31
|
+
gohumanloop-0.0.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|