agstack 1.15.0__tar.gz → 1.16.1__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.15.0 → agstack-1.16.1}/PKG-INFO +1 -1
  2. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/flow.py +56 -4
  3. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/agent_node.py +18 -8
  4. {agstack-1.15.0 → agstack-1.16.1}/agstack.egg-info/PKG-INFO +1 -1
  5. {agstack-1.15.0 → agstack-1.16.1}/pyproject.toml +1 -1
  6. {agstack-1.15.0 → agstack-1.16.1}/LICENSE +0 -0
  7. {agstack-1.15.0 → agstack-1.16.1}/README.md +0 -0
  8. {agstack-1.15.0 → agstack-1.16.1}/agstack/__init__.py +0 -0
  9. {agstack-1.15.0 → agstack-1.16.1}/agstack/config/__init__.py +0 -0
  10. {agstack-1.15.0 → agstack-1.16.1}/agstack/config/logger.py +0 -0
  11. {agstack-1.15.0 → agstack-1.16.1}/agstack/config/manager.py +0 -0
  12. {agstack-1.15.0 → agstack-1.16.1}/agstack/config/types.py +0 -0
  13. {agstack-1.15.0 → agstack-1.16.1}/agstack/contexts.py +0 -0
  14. {agstack-1.15.0 → agstack-1.16.1}/agstack/decorators.py +0 -0
  15. {agstack-1.15.0 → agstack-1.16.1}/agstack/events.py +0 -0
  16. {agstack-1.15.0 → agstack-1.16.1}/agstack/exceptions.py +0 -0
  17. {agstack-1.15.0 → agstack-1.16.1}/agstack/fastapi/__init__.py +0 -0
  18. {agstack-1.15.0 → agstack-1.16.1}/agstack/fastapi/exception.py +0 -0
  19. {agstack-1.15.0 → agstack-1.16.1}/agstack/fastapi/middleware.py +0 -0
  20. {agstack-1.15.0 → agstack-1.16.1}/agstack/fastapi/offline.py +0 -0
  21. {agstack-1.15.0 → agstack-1.16.1}/agstack/fastapi/sse.py +0 -0
  22. {agstack-1.15.0 → agstack-1.16.1}/agstack/infra/db/__init__.py +0 -0
  23. {agstack-1.15.0 → agstack-1.16.1}/agstack/infra/es/__init__.py +0 -0
  24. {agstack-1.15.0 → agstack-1.16.1}/agstack/infra/kg/__init__.py +0 -0
  25. {agstack-1.15.0 → agstack-1.16.1}/agstack/infra/mq/__init__.py +0 -0
  26. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/__init__.py +0 -0
  27. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/client.py +0 -0
  28. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/__init__.py +0 -0
  29. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/agent.py +0 -0
  30. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/context.py +0 -0
  31. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/event.py +0 -0
  32. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/exceptions.py +0 -0
  33. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/factory.py +0 -0
  34. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/loader.py +0 -0
  35. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/__init__.py +0 -0
  36. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/base.py +0 -0
  37. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/detect_node.py +0 -0
  38. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/echo_node.py +0 -0
  39. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/llm_chat_node.py +0 -0
  40. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/llm_embed_node.py +0 -0
  41. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/llm_rerank_node.py +0 -0
  42. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/python_node.py +0 -0
  43. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/subflow_node.py +0 -0
  44. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/switch_node.py +0 -0
  45. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/nodes/tool_node.py +0 -0
  46. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/records.py +0 -0
  47. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/registry.py +0 -0
  48. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/sandbox.py +0 -0
  49. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/state.py +0 -0
  50. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/flow/tool.py +0 -0
  51. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/prompts.py +0 -0
  52. {agstack-1.15.0 → agstack-1.16.1}/agstack/llm/token.py +0 -0
  53. {agstack-1.15.0 → agstack-1.16.1}/agstack/schema.py +0 -0
  54. {agstack-1.15.0 → agstack-1.16.1}/agstack/security/__init__.py +0 -0
  55. {agstack-1.15.0 → agstack-1.16.1}/agstack/security/casbin.py +0 -0
  56. {agstack-1.15.0 → agstack-1.16.1}/agstack/security/crypt.py +0 -0
  57. {agstack-1.15.0 → agstack-1.16.1}/agstack/status.py +0 -0
  58. {agstack-1.15.0 → agstack-1.16.1}/agstack.egg-info/SOURCES.txt +0 -0
  59. {agstack-1.15.0 → agstack-1.16.1}/agstack.egg-info/dependency_links.txt +0 -0
  60. {agstack-1.15.0 → agstack-1.16.1}/agstack.egg-info/requires.txt +0 -0
  61. {agstack-1.15.0 → agstack-1.16.1}/agstack.egg-info/top_level.txt +0 -0
  62. {agstack-1.15.0 → agstack-1.16.1}/setup.cfg +0 -0
  63. {agstack-1.15.0 → agstack-1.16.1}/tests/test_flow_io.py +0 -0
  64. {agstack-1.15.0 → agstack-1.16.1}/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.15.0
3
+ Version: 1.16.1
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>
@@ -146,6 +146,34 @@ class Flow:
146
146
 
147
147
  # ── 带重试的节点执行(统一走 NodeHandler) ──
148
148
 
149
+ def _resolve_visibility(self, node_type: str, config: dict) -> tuple[str | None, bool]:
150
+ """从 node config 和 registry 解析可见性(label, echo)
151
+
152
+ 优先级:flow node config > registry 注册属性
153
+ """
154
+ from .registry import registry
155
+
156
+ label = config.get("label")
157
+ explicit_echo = "echo" in config
158
+ echo = config.get("echo", False)
159
+
160
+ if not explicit_echo and label is None:
161
+ if node_type == "agent":
162
+ agent_name = config.get("agent_name", "")
163
+ label = registry.get_agent_label(agent_name)
164
+ if not label:
165
+ echo = registry.get_agent_echo(agent_name)
166
+ elif node_type == "tool":
167
+ tool_name = config.get("tool_name", "")
168
+ label = registry.get_tool_label(tool_name)
169
+ if not label:
170
+ echo = registry.get_tool_echo(tool_name)
171
+
172
+ if label and not explicit_echo:
173
+ echo = True
174
+
175
+ return label, echo
176
+
149
177
  async def _execute_node_with_retry(
150
178
  self,
151
179
  node: dict,
@@ -164,6 +192,7 @@ class Flow:
164
192
 
165
193
  policy = self._get_retry_policy(node)
166
194
  label = handler.get_step_name(node, node_id)
195
+ vis_label, vis_echo = self._resolve_visibility(node_type, node.get("config", {}))
167
196
  last_error: Exception | None = None
168
197
 
169
198
  for attempt in range(policy.max_retries + 1):
@@ -184,6 +213,9 @@ class Flow:
184
213
  )
185
214
 
186
215
  async for evt in handler.stream(node, context, node_id):
216
+ evt["_node_id"] = node_id
217
+ evt["_label"] = vis_label
218
+ evt["_echo"] = vis_echo
187
219
  yield evt
188
220
  return
189
221
 
@@ -375,14 +407,22 @@ class Flow:
375
407
  node_type: str = node.get("type", "")
376
408
 
377
409
  if node_type == "message":
410
+ msg_config = node.get("config", {})
378
411
  async for evt in self._emit_message(node, context):
412
+ evt["_node_id"] = current_node_id
413
+ evt["_label"] = msg_config.get("label")
414
+ evt["_echo"] = msg_config.get("echo", True)
379
415
  yield evt
380
416
  current_node_id = self._resolve_next_node(current_node_id, context)
381
417
 
382
418
  elif node_type == "parallel":
383
419
  config = node.get("config", {})
384
420
  branches = config.get("branches", [])
385
- yield event.step_started(step_name=f"parallel:{current_node_id}")
421
+ step_evt = event.step_started(step_name=f"parallel:{current_node_id}")
422
+ step_evt["_node_id"] = current_node_id
423
+ step_evt["_label"] = None
424
+ step_evt["_echo"] = False
425
+ yield step_evt
386
426
 
387
427
  async def _exec_branch(branch_id: str) -> None:
388
428
  branch_node = self.get_node_config(branch_id)
@@ -402,7 +442,11 @@ class Flow:
402
442
  if isinstance(branch_result, dict):
403
443
  merged.update(branch_result)
404
444
  context.set_output(current_node_id, merged)
405
- yield event.step_finished(step_name=f"parallel:{current_node_id}")
445
+ fin_evt = event.step_finished(step_name=f"parallel:{current_node_id}")
446
+ fin_evt["_node_id"] = current_node_id
447
+ fin_evt["_label"] = None
448
+ fin_evt["_echo"] = False
449
+ yield fin_evt
406
450
  current_node_id = self._resolve_next_node(current_node_id, context)
407
451
 
408
452
  elif node_type == "iteration":
@@ -417,7 +461,11 @@ class Flow:
417
461
  body_nodes: list[str] = config.get("body", [])
418
462
  results: list[Any] = []
419
463
 
420
- yield event.step_started(step_name=f"iteration:{current_node_id}")
464
+ step_evt = event.step_started(step_name=f"iteration:{current_node_id}")
465
+ step_evt["_node_id"] = current_node_id
466
+ step_evt["_label"] = None
467
+ step_evt["_echo"] = False
468
+ yield step_evt
421
469
  for idx, item in enumerate(items):
422
470
  context.set_variable(item_var, item)
423
471
  context.set_variable(index_var, idx)
@@ -434,7 +482,11 @@ class Flow:
434
482
  results.append(context.outputs.get(body_nodes[-1]))
435
483
 
436
484
  context.set_output(current_node_id, {"results": results})
437
- yield event.step_finished(step_name=f"iteration:{current_node_id}")
485
+ fin_evt = event.step_finished(step_name=f"iteration:{current_node_id}")
486
+ fin_evt["_node_id"] = current_node_id
487
+ fin_evt["_label"] = None
488
+ fin_evt["_echo"] = False
489
+ yield fin_evt
438
490
  current_node_id = self._resolve_next_node(current_node_id, context)
439
491
 
440
492
  elif node_type in self._node_handlers:
@@ -19,13 +19,17 @@ class AgentNodeHandler(NodeHandler):
19
19
 
20
20
  node_type = "agent"
21
21
 
22
- _NODE_KEYS = frozenset({"agent_name", "inputs"})
22
+ _NODE_KEYS = frozenset({"agent_name", "inputs", "label", "echo"})
23
23
 
24
- def _create_agent(self, config: dict):
24
+ def _create_agent(self, config: dict, context: "FlowContext"):
25
25
  agent_name = config.get("agent_name")
26
26
  if not agent_name:
27
27
  raise FlowError("MISSING_AGENT_NAME", 400)
28
- agent_kwargs = {k: v for k, v in config.items() if k not in self._NODE_KEYS}
28
+ agent_kwargs = {
29
+ k: context.resolve_reference(v) if isinstance(v, str) else v
30
+ for k, v in config.items()
31
+ if k not in self._NODE_KEYS
32
+ }
29
33
  agent = registry.create_agent(agent_name, **agent_kwargs)
30
34
  if not agent:
31
35
  raise FlowError("AGENT_NOT_FOUND", 404, {"agent_name": agent_name})
@@ -34,8 +38,10 @@ class AgentNodeHandler(NodeHandler):
34
38
  async def execute(self, node: dict, context: "FlowContext") -> Any:
35
39
  config = node.get("config", {})
36
40
  resolved = self.resolve_inputs(config, context)
37
- ag = self._create_agent(config)
38
- return await ag.run(context, inputs=resolved)
41
+ ag = self._create_agent(config, context)
42
+ result = await ag.run(context, inputs=resolved)
43
+ structured = context.outputs.pop(ag.name, None)
44
+ return structured if structured is not None else result
39
45
 
40
46
  async def stream(self, node: dict, context: "FlowContext", node_id: str) -> AsyncIterator[dict[str, Any]]:
41
47
  config = node.get("config", {})
@@ -43,9 +49,13 @@ class AgentNodeHandler(NodeHandler):
43
49
 
44
50
  yield event.step_started(step_name=step_name)
45
51
  resolved = self.resolve_inputs(config, context)
46
- ag = self._create_agent(config)
52
+ ag = self._create_agent(config, context)
47
53
  async for evt in ag.stream(context, inputs=resolved):
48
54
  yield evt
49
- result = context.get_last_output(ag.name) or ""
50
- context.set_output(node_id, {"result": result})
55
+ structured = context.outputs.pop(ag.name, None)
56
+ if structured is not None:
57
+ context.set_output(node_id, structured)
58
+ else:
59
+ result = context.get_last_output(ag.name) or ""
60
+ context.set_output(node_id, {"result": result})
51
61
  yield event.step_finished(step_name=step_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agstack
3
- Version: 1.15.0
3
+ Version: 1.16.1
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agstack"
3
- version = "1.15.0"
3
+ version = "1.16.1"
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