agstack 1.11.0__tar.gz → 1.13.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.
Files changed (64) hide show
  1. {agstack-1.11.0 → agstack-1.13.0}/PKG-INFO +1 -1
  2. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/agent.py +7 -3
  3. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/__init__.py +2 -0
  4. agstack-1.13.0/agstack/llm/flow/nodes/echo_node.py +54 -0
  5. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/registry.py +32 -26
  6. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/tool.py +7 -3
  7. {agstack-1.11.0 → agstack-1.13.0}/agstack.egg-info/PKG-INFO +1 -1
  8. {agstack-1.11.0 → agstack-1.13.0}/agstack.egg-info/SOURCES.txt +1 -0
  9. {agstack-1.11.0 → agstack-1.13.0}/pyproject.toml +1 -1
  10. {agstack-1.11.0 → agstack-1.13.0}/LICENSE +0 -0
  11. {agstack-1.11.0 → agstack-1.13.0}/README.md +0 -0
  12. {agstack-1.11.0 → agstack-1.13.0}/agstack/__init__.py +0 -0
  13. {agstack-1.11.0 → agstack-1.13.0}/agstack/config/__init__.py +0 -0
  14. {agstack-1.11.0 → agstack-1.13.0}/agstack/config/logger.py +0 -0
  15. {agstack-1.11.0 → agstack-1.13.0}/agstack/config/manager.py +0 -0
  16. {agstack-1.11.0 → agstack-1.13.0}/agstack/config/types.py +0 -0
  17. {agstack-1.11.0 → agstack-1.13.0}/agstack/contexts.py +0 -0
  18. {agstack-1.11.0 → agstack-1.13.0}/agstack/decorators.py +0 -0
  19. {agstack-1.11.0 → agstack-1.13.0}/agstack/events.py +0 -0
  20. {agstack-1.11.0 → agstack-1.13.0}/agstack/exceptions.py +0 -0
  21. {agstack-1.11.0 → agstack-1.13.0}/agstack/fastapi/__init__.py +0 -0
  22. {agstack-1.11.0 → agstack-1.13.0}/agstack/fastapi/exception.py +0 -0
  23. {agstack-1.11.0 → agstack-1.13.0}/agstack/fastapi/middleware.py +0 -0
  24. {agstack-1.11.0 → agstack-1.13.0}/agstack/fastapi/offline.py +0 -0
  25. {agstack-1.11.0 → agstack-1.13.0}/agstack/fastapi/sse.py +0 -0
  26. {agstack-1.11.0 → agstack-1.13.0}/agstack/infra/db/__init__.py +0 -0
  27. {agstack-1.11.0 → agstack-1.13.0}/agstack/infra/es/__init__.py +0 -0
  28. {agstack-1.11.0 → agstack-1.13.0}/agstack/infra/kg/__init__.py +0 -0
  29. {agstack-1.11.0 → agstack-1.13.0}/agstack/infra/mq/__init__.py +0 -0
  30. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/__init__.py +0 -0
  31. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/client.py +0 -0
  32. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/__init__.py +0 -0
  33. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/context.py +0 -0
  34. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/event.py +0 -0
  35. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/exceptions.py +0 -0
  36. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/factory.py +0 -0
  37. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/flow.py +0 -0
  38. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/loader.py +0 -0
  39. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/agent_node.py +0 -0
  40. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/base.py +0 -0
  41. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/detect_node.py +0 -0
  42. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/llm_chat_node.py +0 -0
  43. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/llm_embed_node.py +0 -0
  44. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/llm_rerank_node.py +0 -0
  45. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/python_node.py +0 -0
  46. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/subflow_node.py +0 -0
  47. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/switch_node.py +0 -0
  48. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/nodes/tool_node.py +0 -0
  49. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/records.py +0 -0
  50. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/sandbox.py +0 -0
  51. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/flow/state.py +0 -0
  52. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/prompts.py +0 -0
  53. {agstack-1.11.0 → agstack-1.13.0}/agstack/llm/token.py +0 -0
  54. {agstack-1.11.0 → agstack-1.13.0}/agstack/schema.py +0 -0
  55. {agstack-1.11.0 → agstack-1.13.0}/agstack/security/__init__.py +0 -0
  56. {agstack-1.11.0 → agstack-1.13.0}/agstack/security/casbin.py +0 -0
  57. {agstack-1.11.0 → agstack-1.13.0}/agstack/security/crypt.py +0 -0
  58. {agstack-1.11.0 → agstack-1.13.0}/agstack/status.py +0 -0
  59. {agstack-1.11.0 → agstack-1.13.0}/agstack.egg-info/dependency_links.txt +0 -0
  60. {agstack-1.11.0 → agstack-1.13.0}/agstack.egg-info/requires.txt +0 -0
  61. {agstack-1.11.0 → agstack-1.13.0}/agstack.egg-info/top_level.txt +0 -0
  62. {agstack-1.11.0 → agstack-1.13.0}/setup.cfg +0 -0
  63. {agstack-1.11.0 → agstack-1.13.0}/tests/test_flow_io.py +0 -0
  64. {agstack-1.11.0 → agstack-1.13.0}/tests/test_flow_switch_subflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agstack
3
- Version: 1.11.0
3
+ Version: 1.13.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>
@@ -30,7 +30,9 @@ class Agent:
30
30
  temperature: float = 0.7,
31
31
  max_tokens: int | None = None,
32
32
  max_turns: int = 10,
33
- display_name: str | None = None,
33
+ *,
34
+ label: str | None = None,
35
+ echo: bool = False,
34
36
  ):
35
37
  """初始化 Agent
36
38
 
@@ -41,7 +43,8 @@ class Agent:
41
43
  :param temperature: 温度参数
42
44
  :param max_tokens: 最大 token 数
43
45
  :param max_turns: 最大轮次
44
- :param display_name: 面向用户的展示名称(用于前端脱敏展示)
46
+ :param label: 面向用户的展示名称(控制 STEP 进度事件可见性)
47
+ :param echo: 是否转发 TEXT_MESSAGE 给用户
45
48
  """
46
49
  self.name = name
47
50
  self.instructions = instructions or f"You are {name}, a helpful AI assistant."
@@ -50,7 +53,8 @@ class Agent:
50
53
  self.temperature = temperature
51
54
  self.max_tokens = max_tokens
52
55
  self.max_turns = max_turns
53
- self.display_name = display_name
56
+ self.label = label
57
+ self.echo = echo
54
58
 
55
59
  def get_system_message(self) -> dict[str, Any]:
56
60
  """获取系统消息"""
@@ -5,6 +5,7 @@
5
5
  from .agent_node import AgentNodeHandler
6
6
  from .base import NodeHandler
7
7
  from .detect_node import DetectNodeHandler
8
+ from .echo_node import EchoNodeHandler
8
9
  from .llm_chat_node import LLMChatNodeHandler
9
10
  from .llm_embed_node import LLMEmbedNodeHandler
10
11
  from .llm_rerank_node import LLMRerankNodeHandler
@@ -23,6 +24,7 @@ builtin_handlers: list[NodeHandler] = [
23
24
  LLMEmbedNodeHandler(),
24
25
  LLMRerankNodeHandler(),
25
26
  DetectNodeHandler(),
27
+ EchoNodeHandler(),
26
28
  SwitchNodeHandler(),
27
29
  SubflowNodeHandler(),
28
30
  ]
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2020-2026 XtraVisions, All rights reserved.
2
+
3
+ """Echo 节点 — 将 context 中的文本作为 TEXT_MESSAGE 事件流式输出(不调用 LLM)"""
4
+
5
+ from typing import TYPE_CHECKING, Any, AsyncIterator
6
+ from uuid import uuid4
7
+
8
+ from agstack.llm.flow import event
9
+ from agstack.llm.flow.nodes.base import NodeHandler
10
+
11
+
12
+ if TYPE_CHECKING:
13
+ from agstack.llm.flow.context import FlowContext
14
+
15
+
16
+ class EchoNodeHandler(NodeHandler):
17
+ """Echo 节点:从 context 读取文本并流式输出为 TEXT_MESSAGE 事件
18
+
19
+ 用于将内部 agent 的结果展示给用户,不产生额外 LLM 调用。
20
+ """
21
+
22
+ node_type = "echo"
23
+
24
+ async def execute(self, node: dict, context: "FlowContext") -> Any:
25
+ config = node.get("config", {})
26
+ resolved = self.resolve_inputs(config, context)
27
+ content = resolved.get("content", "")
28
+ if isinstance(content, dict):
29
+ content = content.get("result", "")
30
+ return {"result": content}
31
+
32
+ async def stream(self, node: dict, context: "FlowContext", node_id: str) -> AsyncIterator[dict[str, Any]]:
33
+ config = node.get("config", {})
34
+ step_name = self.get_step_name(node, node_id)
35
+
36
+ yield event.step_started(step_name=step_name)
37
+
38
+ resolved = self.resolve_inputs(config, context)
39
+ content = resolved.get("content", "")
40
+ if isinstance(content, dict):
41
+ content = content.get("result", "")
42
+
43
+ if content:
44
+ msg_id = context.message_id or str(uuid4())
45
+ yield event.text_message_start(message_id=msg_id, role="assistant")
46
+
47
+ chunk_size = 80
48
+ for i in range(0, len(content), chunk_size):
49
+ yield event.text_message_content(message_id=msg_id, delta=content[i : i + chunk_size])
50
+
51
+ yield event.text_message_end(message_id=msg_id)
52
+
53
+ context.set_output(node_id, {"result": content})
54
+ yield event.step_finished(step_name=step_name)
@@ -19,8 +19,10 @@ class FlowRegistry:
19
19
  self._flows: dict[str, Any] = {}
20
20
  self._node_handlers: dict[str, Any] = {}
21
21
  self._builtins_loaded = False
22
- self._tool_display_names: dict[str, str] = {}
23
- self._agent_display_names: dict[str, str] = {}
22
+ self._tool_labels: dict[str, str] = {}
23
+ self._tool_echo: dict[str, bool] = {}
24
+ self._agent_labels: dict[str, str] = {}
25
+ self._agent_echo: dict[str, bool] = {}
24
26
 
25
27
  def _ensure_builtins(self) -> None:
26
28
  """延迟加载内置 node handlers(避免循环导入)"""
@@ -34,17 +36,23 @@ class FlowRegistry:
34
36
 
35
37
  # ── 注册 ──
36
38
 
37
- def register_tool(self, name: str, tool_class, display_name: str | None = None) -> None:
39
+ def register_tool(self, name: str, tool_class, *, label: str | None = None, echo: bool = False) -> None:
38
40
  """注册工具工厂/类/实例"""
39
41
  self._tools[name] = tool_class
40
- if display_name is not None:
41
- self._tool_display_names[name] = display_name
42
-
43
- def register_agent(self, name: str, agent_class: type[Agent], display_name: str | None = None) -> None:
42
+ if label is not None:
43
+ self._tool_labels[name] = label
44
+ if echo:
45
+ self._tool_echo[name] = True
46
+
47
+ def register_agent(
48
+ self, name: str, agent_class: type[Agent], *, label: str | None = None, echo: bool = False
49
+ ) -> None:
44
50
  """注册 Agent 类型"""
45
51
  self._agents[name] = agent_class
46
- if display_name is not None:
47
- self._agent_display_names[name] = display_name
52
+ if label is not None:
53
+ self._agent_labels[name] = label
54
+ if echo:
55
+ self._agent_echo[name] = True
48
56
 
49
57
  def register_flow(self, name: str, flow_class: type) -> None:
50
58
  """注册 Flow 类型"""
@@ -93,23 +101,21 @@ class FlowRegistry:
93
101
 
94
102
  # ── 查询 ──
95
103
 
96
- def get_tool_display_name(self, name: str) -> str | None:
97
- """获取工具的展示名称(用于前端脱敏展示)"""
98
- if name in self._tool_display_names:
99
- return self._tool_display_names[name]
100
- component = self._tools.get(name)
101
- if component and hasattr(component, "display_name"):
102
- return component.display_name
103
- return None
104
-
105
- def get_agent_display_name(self, name: str) -> str | None:
106
- """获取 Agent 的展示名称(用于前端脱敏展示)"""
107
- if name in self._agent_display_names:
108
- return self._agent_display_names[name]
109
- component = self._agents.get(name)
110
- if component and hasattr(component, "display_name"):
111
- return getattr(component, "display_name", None)
112
- return None
104
+ def get_tool_label(self, name: str) -> str | None:
105
+ """获取工具的展示名称"""
106
+ return self._tool_labels.get(name)
107
+
108
+ def get_tool_echo(self, name: str) -> bool:
109
+ """获取工具是否转发 TEXT_MESSAGE(label 隐含 echo)"""
110
+ return name in self._tool_labels or self._tool_echo.get(name, False)
111
+
112
+ def get_agent_label(self, name: str) -> str | None:
113
+ """获取 Agent 的展示名称"""
114
+ return self._agent_labels.get(name)
115
+
116
+ def get_agent_echo(self, name: str) -> bool:
117
+ """获取 Agent 是否转发 TEXT_MESSAGE(label 隐含 echo)"""
118
+ return name in self._agent_labels or self._agent_echo.get(name, False)
113
119
 
114
120
  def get_tool_class(self, name: str) -> Any | None:
115
121
  """获取工具工厂/类"""
@@ -33,7 +33,9 @@ class Tool:
33
33
  description: str,
34
34
  function: Callable,
35
35
  parameters: dict[str, Any] | None = None,
36
- display_name: str | None = None,
36
+ *,
37
+ label: str | None = None,
38
+ echo: bool = False,
37
39
  ):
38
40
  """初始化工具
39
41
 
@@ -41,13 +43,15 @@ class Tool:
41
43
  :param description: 工具描述
42
44
  :param function: 工具函数,签名 fn(context, inputs) -> dict[str, Any]
43
45
  :param parameters: JSON Schema 参数定义(用于 LLM 调用)
44
- :param display_name: 面向用户的展示名称(用于前端脱敏展示)
46
+ :param label: 面向用户的展示名称(控制 STEP/TOOL_CALL 进度事件可见性)
47
+ :param echo: 是否转发 TEXT_MESSAGE 给用户
45
48
  """
46
49
  self.name = name
47
50
  self.description = description
48
51
  self.function = function
49
52
  self.parameters = parameters or {"type": "object", "properties": {}, "required": []}
50
- self.display_name = display_name
53
+ self.label = label
54
+ self.echo = echo
51
55
 
52
56
  async def execute_async(self, context: "FlowContext", inputs: dict[str, Any] | None = None) -> ToolResult:
53
57
  """异步执行工具"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agstack
3
- Version: 1.11.0
3
+ Version: 1.13.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>
@@ -47,6 +47,7 @@ agstack/llm/flow/nodes/__init__.py
47
47
  agstack/llm/flow/nodes/agent_node.py
48
48
  agstack/llm/flow/nodes/base.py
49
49
  agstack/llm/flow/nodes/detect_node.py
50
+ agstack/llm/flow/nodes/echo_node.py
50
51
  agstack/llm/flow/nodes/llm_chat_node.py
51
52
  agstack/llm/flow/nodes/llm_embed_node.py
52
53
  agstack/llm/flow/nodes/llm_rerank_node.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agstack"
3
- version = "1.11.0"
3
+ version = "1.13.0"
4
4
  description = "Production-ready toolkit for building FastAPI and LLM applications"
5
5
  readme = "README.md"
6
6
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes