agstack 1.15.0__tar.gz → 1.16.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.15.0 → agstack-1.16.0}/PKG-INFO +1 -1
  2. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/flow.py +55 -4
  3. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/agent_node.py +18 -8
  4. {agstack-1.15.0 → agstack-1.16.0}/agstack.egg-info/PKG-INFO +1 -1
  5. {agstack-1.15.0 → agstack-1.16.0}/pyproject.toml +1 -1
  6. {agstack-1.15.0 → agstack-1.16.0}/LICENSE +0 -0
  7. {agstack-1.15.0 → agstack-1.16.0}/README.md +0 -0
  8. {agstack-1.15.0 → agstack-1.16.0}/agstack/__init__.py +0 -0
  9. {agstack-1.15.0 → agstack-1.16.0}/agstack/config/__init__.py +0 -0
  10. {agstack-1.15.0 → agstack-1.16.0}/agstack/config/logger.py +0 -0
  11. {agstack-1.15.0 → agstack-1.16.0}/agstack/config/manager.py +0 -0
  12. {agstack-1.15.0 → agstack-1.16.0}/agstack/config/types.py +0 -0
  13. {agstack-1.15.0 → agstack-1.16.0}/agstack/contexts.py +0 -0
  14. {agstack-1.15.0 → agstack-1.16.0}/agstack/decorators.py +0 -0
  15. {agstack-1.15.0 → agstack-1.16.0}/agstack/events.py +0 -0
  16. {agstack-1.15.0 → agstack-1.16.0}/agstack/exceptions.py +0 -0
  17. {agstack-1.15.0 → agstack-1.16.0}/agstack/fastapi/__init__.py +0 -0
  18. {agstack-1.15.0 → agstack-1.16.0}/agstack/fastapi/exception.py +0 -0
  19. {agstack-1.15.0 → agstack-1.16.0}/agstack/fastapi/middleware.py +0 -0
  20. {agstack-1.15.0 → agstack-1.16.0}/agstack/fastapi/offline.py +0 -0
  21. {agstack-1.15.0 → agstack-1.16.0}/agstack/fastapi/sse.py +0 -0
  22. {agstack-1.15.0 → agstack-1.16.0}/agstack/infra/db/__init__.py +0 -0
  23. {agstack-1.15.0 → agstack-1.16.0}/agstack/infra/es/__init__.py +0 -0
  24. {agstack-1.15.0 → agstack-1.16.0}/agstack/infra/kg/__init__.py +0 -0
  25. {agstack-1.15.0 → agstack-1.16.0}/agstack/infra/mq/__init__.py +0 -0
  26. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/__init__.py +0 -0
  27. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/client.py +0 -0
  28. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/__init__.py +0 -0
  29. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/agent.py +0 -0
  30. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/context.py +0 -0
  31. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/event.py +0 -0
  32. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/exceptions.py +0 -0
  33. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/factory.py +0 -0
  34. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/loader.py +0 -0
  35. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/__init__.py +0 -0
  36. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/base.py +0 -0
  37. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/detect_node.py +0 -0
  38. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/echo_node.py +0 -0
  39. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/llm_chat_node.py +0 -0
  40. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/llm_embed_node.py +0 -0
  41. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/llm_rerank_node.py +0 -0
  42. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/python_node.py +0 -0
  43. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/subflow_node.py +0 -0
  44. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/switch_node.py +0 -0
  45. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/nodes/tool_node.py +0 -0
  46. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/records.py +0 -0
  47. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/registry.py +0 -0
  48. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/sandbox.py +0 -0
  49. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/state.py +0 -0
  50. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/flow/tool.py +0 -0
  51. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/prompts.py +0 -0
  52. {agstack-1.15.0 → agstack-1.16.0}/agstack/llm/token.py +0 -0
  53. {agstack-1.15.0 → agstack-1.16.0}/agstack/schema.py +0 -0
  54. {agstack-1.15.0 → agstack-1.16.0}/agstack/security/__init__.py +0 -0
  55. {agstack-1.15.0 → agstack-1.16.0}/agstack/security/casbin.py +0 -0
  56. {agstack-1.15.0 → agstack-1.16.0}/agstack/security/crypt.py +0 -0
  57. {agstack-1.15.0 → agstack-1.16.0}/agstack/status.py +0 -0
  58. {agstack-1.15.0 → agstack-1.16.0}/agstack.egg-info/SOURCES.txt +0 -0
  59. {agstack-1.15.0 → agstack-1.16.0}/agstack.egg-info/dependency_links.txt +0 -0
  60. {agstack-1.15.0 → agstack-1.16.0}/agstack.egg-info/requires.txt +0 -0
  61. {agstack-1.15.0 → agstack-1.16.0}/agstack.egg-info/top_level.txt +0 -0
  62. {agstack-1.15.0 → agstack-1.16.0}/setup.cfg +0 -0
  63. {agstack-1.15.0 → agstack-1.16.0}/tests/test_flow_io.py +0 -0
  64. {agstack-1.15.0 → agstack-1.16.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.15.0
3
+ Version: 1.16.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>
@@ -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
 
@@ -376,13 +408,20 @@ class Flow:
376
408
 
377
409
  if node_type == "message":
378
410
  async for evt in self._emit_message(node, context):
411
+ evt["_node_id"] = current_node_id
412
+ evt["_label"] = None
413
+ evt["_echo"] = False
379
414
  yield evt
380
415
  current_node_id = self._resolve_next_node(current_node_id, context)
381
416
 
382
417
  elif node_type == "parallel":
383
418
  config = node.get("config", {})
384
419
  branches = config.get("branches", [])
385
- yield event.step_started(step_name=f"parallel:{current_node_id}")
420
+ step_evt = event.step_started(step_name=f"parallel:{current_node_id}")
421
+ step_evt["_node_id"] = current_node_id
422
+ step_evt["_label"] = None
423
+ step_evt["_echo"] = False
424
+ yield step_evt
386
425
 
387
426
  async def _exec_branch(branch_id: str) -> None:
388
427
  branch_node = self.get_node_config(branch_id)
@@ -402,7 +441,11 @@ class Flow:
402
441
  if isinstance(branch_result, dict):
403
442
  merged.update(branch_result)
404
443
  context.set_output(current_node_id, merged)
405
- yield event.step_finished(step_name=f"parallel:{current_node_id}")
444
+ fin_evt = event.step_finished(step_name=f"parallel:{current_node_id}")
445
+ fin_evt["_node_id"] = current_node_id
446
+ fin_evt["_label"] = None
447
+ fin_evt["_echo"] = False
448
+ yield fin_evt
406
449
  current_node_id = self._resolve_next_node(current_node_id, context)
407
450
 
408
451
  elif node_type == "iteration":
@@ -417,7 +460,11 @@ class Flow:
417
460
  body_nodes: list[str] = config.get("body", [])
418
461
  results: list[Any] = []
419
462
 
420
- yield event.step_started(step_name=f"iteration:{current_node_id}")
463
+ step_evt = event.step_started(step_name=f"iteration:{current_node_id}")
464
+ step_evt["_node_id"] = current_node_id
465
+ step_evt["_label"] = None
466
+ step_evt["_echo"] = False
467
+ yield step_evt
421
468
  for idx, item in enumerate(items):
422
469
  context.set_variable(item_var, item)
423
470
  context.set_variable(index_var, idx)
@@ -434,7 +481,11 @@ class Flow:
434
481
  results.append(context.outputs.get(body_nodes[-1]))
435
482
 
436
483
  context.set_output(current_node_id, {"results": results})
437
- yield event.step_finished(step_name=f"iteration:{current_node_id}")
484
+ fin_evt = event.step_finished(step_name=f"iteration:{current_node_id}")
485
+ fin_evt["_node_id"] = current_node_id
486
+ fin_evt["_label"] = None
487
+ fin_evt["_echo"] = False
488
+ yield fin_evt
438
489
  current_node_id = self._resolve_next_node(current_node_id, context)
439
490
 
440
491
  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.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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agstack"
3
- version = "1.15.0"
3
+ version = "1.16.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