agstack 1.5.0__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agstack-1.5.0 → agstack-1.7.0}/PKG-INFO +1 -1
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/__init__.py +4 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/agent.py +16 -11
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/context.py +23 -25
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/flow.py +123 -225
- agstack-1.7.0/agstack/llm/flow/nodes/__init__.py +39 -0
- agstack-1.7.0/agstack/llm/flow/nodes/agent_node.py +48 -0
- agstack-1.7.0/agstack/llm/flow/nodes/base.py +51 -0
- agstack-1.7.0/agstack/llm/flow/nodes/detect_node.py +93 -0
- agstack-1.7.0/agstack/llm/flow/nodes/llm_chat_node.py +152 -0
- agstack-1.7.0/agstack/llm/flow/nodes/llm_embed_node.py +38 -0
- agstack-1.7.0/agstack/llm/flow/nodes/llm_rerank_node.py +48 -0
- agstack-1.5.0/agstack/llm/flow/sandbox.py → agstack-1.7.0/agstack/llm/flow/nodes/python_node.py +42 -6
- agstack-1.7.0/agstack/llm/flow/nodes/tool_node.py +34 -0
- agstack-1.7.0/agstack/llm/flow/sandbox.py +8 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/tool.py +9 -10
- {agstack-1.5.0 → agstack-1.7.0}/agstack.egg-info/PKG-INFO +1 -1
- {agstack-1.5.0 → agstack-1.7.0}/agstack.egg-info/SOURCES.txt +11 -1
- {agstack-1.5.0 → agstack-1.7.0}/pyproject.toml +2 -1
- agstack-1.7.0/tests/test_flow_io.py +435 -0
- {agstack-1.5.0 → agstack-1.7.0}/LICENSE +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/README.md +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/config/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/config/logger.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/config/manager.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/config/types.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/contexts.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/decorators.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/events.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/exceptions.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/fastapi/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/fastapi/exception.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/fastapi/middleware.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/fastapi/offline.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/fastapi/sse.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/infra/db/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/infra/es/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/infra/kg/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/infra/mq/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/client.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/event.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/exceptions.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/factory.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/loader.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/records.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/registry.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/flow/state.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/prompts.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/llm/token.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/registry.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/schema.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/security/__init__.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/security/casbin.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/security/crypt.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack/status.py +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack.egg-info/dependency_links.txt +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack.egg-info/requires.txt +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/agstack.egg-info/top_level.txt +0 -0
- {agstack-1.5.0 → agstack-1.7.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agstack
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: Production-ready toolkit for building FastAPI and LLM applications
|
|
5
5
|
Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
6
6
|
Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
|
|
@@ -18,6 +18,7 @@ from .exceptions import (
|
|
|
18
18
|
from .factory import create_agent, create_tool
|
|
19
19
|
from .flow import Flow
|
|
20
20
|
from .loader import FlowLoader
|
|
21
|
+
from .nodes import NodeHandler, register_node_handler
|
|
21
22
|
from .records import Record, Status
|
|
22
23
|
from .registry import registry
|
|
23
24
|
from .state import FlowState
|
|
@@ -32,6 +33,9 @@ __all__ = [
|
|
|
32
33
|
"Flow",
|
|
33
34
|
"FlowContext",
|
|
34
35
|
"Usage",
|
|
36
|
+
# 节点处理器
|
|
37
|
+
"NodeHandler",
|
|
38
|
+
"register_node_handler",
|
|
35
39
|
# AG-UI 协议
|
|
36
40
|
"EventType",
|
|
37
41
|
"event",
|
|
@@ -64,23 +64,29 @@ class Agent:
|
|
|
64
64
|
return tool
|
|
65
65
|
return None
|
|
66
66
|
|
|
67
|
-
async def run(self, context: "FlowContext") -> str:
|
|
67
|
+
async def run(self, context: "FlowContext", inputs: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
68
68
|
"""执行 Agent 逻辑"""
|
|
69
69
|
content_parts = []
|
|
70
|
-
async for evt in self.stream(context):
|
|
70
|
+
async for evt in self.stream(context, inputs):
|
|
71
71
|
# AG-UI 事件格式
|
|
72
72
|
if isinstance(evt, dict):
|
|
73
73
|
if evt.get("type") == EventType.TEXT_MESSAGE_CONTENT:
|
|
74
74
|
content_parts.append(evt.get("delta", ""))
|
|
75
75
|
elif evt.get("type") == EventType.RUN_ERROR:
|
|
76
76
|
raise FlowError("AGENT_EXECUTION_FAILED", 500, {"error": evt.get("message")})
|
|
77
|
-
return "".join(content_parts)
|
|
77
|
+
return {"result": "".join(content_parts)}
|
|
78
78
|
|
|
79
|
-
async def stream(
|
|
79
|
+
async def stream(
|
|
80
|
+
self, context: "FlowContext", inputs: dict[str, Any] | None = None
|
|
81
|
+
) -> AsyncIterator[dict[str, Any]]:
|
|
80
82
|
"""流式执行 Agent,输出 AG-UI 标准事件"""
|
|
81
83
|
|
|
82
|
-
# 输入来源:优先
|
|
83
|
-
user_input =
|
|
84
|
+
# 输入来源:优先 inputs 参数,回退到 context.variables
|
|
85
|
+
user_input = ""
|
|
86
|
+
if inputs:
|
|
87
|
+
user_input = inputs.get("input", "")
|
|
88
|
+
if not user_input:
|
|
89
|
+
user_input = context.get_variable("input") or context.get_variable("query", "")
|
|
84
90
|
msg_id = context.message_id or str(uuid4())
|
|
85
91
|
|
|
86
92
|
# 添加用户消息(scoped by agent name)
|
|
@@ -214,7 +220,7 @@ class Agent:
|
|
|
214
220
|
# 如果没有工具调用,结束循环
|
|
215
221
|
if not tool_calls:
|
|
216
222
|
# 存储结果供 Flow/A2A 使用
|
|
217
|
-
context.
|
|
223
|
+
context.set_output(self.name, {"result": assistant_content})
|
|
218
224
|
# AG-UI: TEXT_MESSAGE_END
|
|
219
225
|
yield event.text_message_end(message_id=msg_id)
|
|
220
226
|
return
|
|
@@ -237,15 +243,14 @@ class Agent:
|
|
|
237
243
|
)
|
|
238
244
|
continue
|
|
239
245
|
|
|
240
|
-
# 解析 LLM
|
|
246
|
+
# 解析 LLM 返回的工具参数
|
|
241
247
|
try:
|
|
242
248
|
tool_args = json.loads(tool_call["arguments"]) if tool_call["arguments"] else {}
|
|
243
249
|
except json.JSONDecodeError:
|
|
244
250
|
tool_args = {}
|
|
245
|
-
context.update_variables(tool_args)
|
|
246
251
|
|
|
247
|
-
#
|
|
248
|
-
result = await tool.execute_async(context)
|
|
252
|
+
# 执行工具(传入 LLM 解析的参数作为 inputs)
|
|
253
|
+
result = await tool.execute_async(context, tool_args)
|
|
249
254
|
|
|
250
255
|
# 保存工具结果
|
|
251
256
|
result_content = json.dumps(result.result) if result.success else json.dumps({"error": result.error})
|
|
@@ -50,7 +50,7 @@ class FlowContext:
|
|
|
50
50
|
message_id: str | None = None
|
|
51
51
|
|
|
52
52
|
# 图执行状态
|
|
53
|
-
|
|
53
|
+
outputs: dict[str, Any] = field(default_factory=dict)
|
|
54
54
|
current_node: str | None = None
|
|
55
55
|
|
|
56
56
|
# 执行记录(可选)
|
|
@@ -115,33 +115,31 @@ class FlowContext:
|
|
|
115
115
|
self.messages.clear()
|
|
116
116
|
self.turn_count = 0
|
|
117
117
|
|
|
118
|
-
def resolve_reference(self, ref:
|
|
119
|
-
"""解析变量引用
|
|
120
|
-
if not isinstance(ref, str) or not ref.startswith("{"):
|
|
121
|
-
return ref
|
|
118
|
+
def resolve_reference(self, ref: Any) -> Any:
|
|
119
|
+
"""解析变量引用
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
$o.node_id.field.subfield → context.outputs["node_id"]["field"]["subfield"]
|
|
122
|
+
$v.key → context.variables["key"]
|
|
123
|
+
其他字符串 → 原样返回(字面值)
|
|
124
|
+
"""
|
|
125
|
+
if not isinstance(ref, str):
|
|
126
|
+
return ref
|
|
127
|
+
if ref.startswith("$o."):
|
|
128
|
+
parts = ref[3:].split(".")
|
|
129
|
+
result = self.outputs.get(parts[0])
|
|
130
|
+
for part in parts[1:]:
|
|
131
|
+
if isinstance(result, dict):
|
|
132
|
+
result = result.get(part)
|
|
133
|
+
else:
|
|
134
|
+
result = getattr(result, part, None)
|
|
129
135
|
return result
|
|
136
|
+
if ref.startswith("$v."):
|
|
137
|
+
return self.variables.get(ref[3:])
|
|
138
|
+
return ref
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# 支持嵌套字段访问 variable.field.subfield
|
|
135
|
-
for field_name in var_path.split("."):
|
|
136
|
-
if isinstance(result, dict):
|
|
137
|
-
result = result.get(field_name)
|
|
138
|
-
else:
|
|
139
|
-
result = getattr(result, field_name, None)
|
|
140
|
-
return result
|
|
141
|
-
|
|
142
|
-
def set_node_result(self, node_id: str, result: Any):
|
|
143
|
-
"""设置节点执行结果"""
|
|
144
|
-
self.node_results[node_id] = result
|
|
140
|
+
def set_output(self, node_id: str, result: Any):
|
|
141
|
+
"""设置节点输出"""
|
|
142
|
+
self.outputs[node_id] = result
|
|
145
143
|
|
|
146
144
|
def add_execution_record(self, task_id: str, status: str, **kwargs) -> None:
|
|
147
145
|
"""添加执行记录"""
|