agstack 1.21.1__tar.gz → 1.22.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 (77) hide show
  1. {agstack-1.21.1 → agstack-1.22.1}/PKG-INFO +3 -1
  2. {agstack-1.21.1 → agstack-1.22.1}/agstack/config/manager.py +47 -11
  3. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/event.py +4 -4
  4. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/flow.py +12 -8
  5. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/agent_node.py +4 -2
  6. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/base.py +4 -2
  7. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/echo_node.py +3 -2
  8. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/llm_chat_node.py +6 -4
  9. {agstack-1.21.1 → agstack-1.22.1}/agstack.egg-info/PKG-INFO +3 -1
  10. {agstack-1.21.1 → agstack-1.22.1}/agstack.egg-info/requires.txt +3 -0
  11. {agstack-1.21.1 → agstack-1.22.1}/pyproject.toml +2 -1
  12. {agstack-1.21.1 → agstack-1.22.1}/LICENSE +0 -0
  13. {agstack-1.21.1 → agstack-1.22.1}/README.md +0 -0
  14. {agstack-1.21.1 → agstack-1.22.1}/agstack/__init__.py +0 -0
  15. {agstack-1.21.1 → agstack-1.22.1}/agstack/cache/__init__.py +0 -0
  16. {agstack-1.21.1 → agstack-1.22.1}/agstack/cache/base.py +0 -0
  17. {agstack-1.21.1 → agstack-1.22.1}/agstack/cache/memory.py +0 -0
  18. {agstack-1.21.1 → agstack-1.22.1}/agstack/cache/redis.py +0 -0
  19. {agstack-1.21.1 → agstack-1.22.1}/agstack/config/__init__.py +0 -0
  20. {agstack-1.21.1 → agstack-1.22.1}/agstack/config/logger.py +0 -0
  21. {agstack-1.21.1 → agstack-1.22.1}/agstack/config/types.py +0 -0
  22. {agstack-1.21.1 → agstack-1.22.1}/agstack/contexts.py +0 -0
  23. {agstack-1.21.1 → agstack-1.22.1}/agstack/decorators.py +0 -0
  24. {agstack-1.21.1 → agstack-1.22.1}/agstack/events.py +0 -0
  25. {agstack-1.21.1 → agstack-1.22.1}/agstack/exceptions.py +0 -0
  26. {agstack-1.21.1 → agstack-1.22.1}/agstack/fastapi/__init__.py +0 -0
  27. {agstack-1.21.1 → agstack-1.22.1}/agstack/fastapi/exception.py +0 -0
  28. {agstack-1.21.1 → agstack-1.22.1}/agstack/fastapi/middleware.py +0 -0
  29. {agstack-1.21.1 → agstack-1.22.1}/agstack/fastapi/offline.py +0 -0
  30. {agstack-1.21.1 → agstack-1.22.1}/agstack/fastapi/sse.py +0 -0
  31. {agstack-1.21.1 → agstack-1.22.1}/agstack/infra/db/__init__.py +0 -0
  32. {agstack-1.21.1 → agstack-1.22.1}/agstack/infra/es/__init__.py +0 -0
  33. {agstack-1.21.1 → agstack-1.22.1}/agstack/infra/kg/__init__.py +0 -0
  34. {agstack-1.21.1 → agstack-1.22.1}/agstack/infra/mq/__init__.py +0 -0
  35. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/__init__.py +0 -0
  36. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/client.py +0 -0
  37. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/__init__.py +0 -0
  38. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/agent.py +0 -0
  39. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/context.py +0 -0
  40. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/exceptions.py +0 -0
  41. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/factory.py +0 -0
  42. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/loader.py +0 -0
  43. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/__init__.py +0 -0
  44. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/detect_node.py +0 -0
  45. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/llm_embed_node.py +0 -0
  46. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/llm_rerank_node.py +0 -0
  47. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/python_node.py +0 -0
  48. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/subflow_node.py +0 -0
  49. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/switch_node.py +0 -0
  50. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/nodes/tool_node.py +0 -0
  51. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/records.py +0 -0
  52. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/registry.py +0 -0
  53. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/sandbox.py +0 -0
  54. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/state.py +0 -0
  55. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/tool.py +0 -0
  56. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/flow/trace.py +0 -0
  57. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/prompts.py +0 -0
  58. {agstack-1.21.1 → agstack-1.22.1}/agstack/llm/token.py +0 -0
  59. {agstack-1.21.1 → agstack-1.22.1}/agstack/messagebus/__init__.py +0 -0
  60. {agstack-1.21.1 → agstack-1.22.1}/agstack/messagebus/base.py +0 -0
  61. {agstack-1.21.1 → agstack-1.22.1}/agstack/messagebus/memory.py +0 -0
  62. {agstack-1.21.1 → agstack-1.22.1}/agstack/messagebus/redis.py +0 -0
  63. {agstack-1.21.1 → agstack-1.22.1}/agstack/schema.py +0 -0
  64. {agstack-1.21.1 → agstack-1.22.1}/agstack/security/__init__.py +0 -0
  65. {agstack-1.21.1 → agstack-1.22.1}/agstack/security/casbin.py +0 -0
  66. {agstack-1.21.1 → agstack-1.22.1}/agstack/security/crypt.py +0 -0
  67. {agstack-1.21.1 → agstack-1.22.1}/agstack/status.py +0 -0
  68. {agstack-1.21.1 → agstack-1.22.1}/agstack.egg-info/SOURCES.txt +0 -0
  69. {agstack-1.21.1 → agstack-1.22.1}/agstack.egg-info/dependency_links.txt +0 -0
  70. {agstack-1.21.1 → agstack-1.22.1}/agstack.egg-info/top_level.txt +0 -0
  71. {agstack-1.21.1 → agstack-1.22.1}/setup.cfg +0 -0
  72. {agstack-1.21.1 → agstack-1.22.1}/tests/test_cache_memory.py +0 -0
  73. {agstack-1.21.1 → agstack-1.22.1}/tests/test_cache_redis.py +0 -0
  74. {agstack-1.21.1 → agstack-1.22.1}/tests/test_flow_io.py +0 -0
  75. {agstack-1.21.1 → agstack-1.22.1}/tests/test_flow_switch_subflow.py +0 -0
  76. {agstack-1.21.1 → agstack-1.22.1}/tests/test_messagebus_memory.py +0 -0
  77. {agstack-1.21.1 → agstack-1.22.1}/tests/test_messagebus_redis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agstack
3
- Version: 1.21.1
3
+ Version: 1.22.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>
@@ -43,6 +43,8 @@ Provides-Extra: graph
43
43
  Requires-Dist: nebula3-python>=3.8.3; extra == "graph"
44
44
  Provides-Extra: redis
45
45
  Requires-Dist: redis>=5.0; extra == "redis"
46
+ Provides-Extra: yaml
47
+ Requires-Dist: pyyaml>=6.0; extra == "yaml"
46
48
  Provides-Extra: std
47
49
  Requires-Dist: agstack[mq]; extra == "std"
48
50
  Requires-Dist: agstack[db]; extra == "std"
@@ -10,14 +10,42 @@ from .logger import setup_logger
10
10
  from .types import AppConfig
11
11
 
12
12
 
13
+ _CONFIG_CANDIDATES = ("config.toml", "config.yaml", "config.yml")
14
+
15
+
16
+ def _find_config_file(approot: Path) -> Path:
17
+ """按优先级查找配置文件"""
18
+ for name in _CONFIG_CANDIDATES:
19
+ path = approot / name
20
+ if path.exists():
21
+ return path
22
+ return approot / "config.toml"
23
+
24
+
13
25
  def _load_config_file(config_path: Path) -> dict[str, Any]:
14
- """加载配置文件(容错处理)"""
26
+ """加载配置文件(支持 TOML 和 YAML 格式)"""
15
27
  if not config_path.exists():
16
28
  return {}
17
29
 
30
+ suffix = config_path.suffix.lower()
18
31
  try:
19
- with open(config_path, "rb") as f:
20
- return tomllib.load(f)
32
+ if suffix == ".toml":
33
+ with open(config_path, "rb") as f:
34
+ return tomllib.load(f)
35
+ elif suffix in (".yaml", ".yml"):
36
+ try:
37
+ import yaml
38
+ except ImportError as err:
39
+ raise ImportError(
40
+ f"PyYAML is required to load {config_path}. Install it with: pip install agstack[yaml]"
41
+ ) from err
42
+ with open(config_path) as f:
43
+ return yaml.safe_load(f) or {}
44
+ else:
45
+ print(f"Unsupported config format: {suffix}")
46
+ return {}
47
+ except ImportError:
48
+ raise
21
49
  except Exception: # noqa
22
50
  print(f"Failed to load config file: {config_path}")
23
51
  return {}
@@ -95,37 +123,45 @@ def _determine_log_level(config_data: dict[str, Any]) -> str:
95
123
  return config_data.get("logger", {}).get("level", "INFO")
96
124
 
97
125
 
98
- def setup_config(appname: str, envprefix: str = "APP") -> AppConfig:
126
+ def setup_config(appname: str, envprefix: str = "APP", config_file: str | Path | None = None) -> AppConfig:
99
127
  """初始化配置
100
128
 
101
129
  Args:
102
130
  appname: 应用名称
103
131
  envprefix: 环境变量前缀
132
+ config_file: 配置文件路径(支持 .toml / .yaml / .yml),为 None 时自动查找
104
133
 
105
134
  Returns:
106
135
  AppConfig: 配置实例
107
136
  """
108
137
  approot = Path.cwd()
109
- config_path = approot / "config.toml"
110
138
 
111
- # 1. 加载配置文件(容错)
139
+ # 1. 确定配置文件路径
140
+ if config_file:
141
+ config_path = Path(config_file)
142
+ if not config_path.is_absolute():
143
+ config_path = approot / config_path
144
+ else:
145
+ config_path = _find_config_file(approot)
146
+
147
+ # 2. 加载配置文件(容错)
112
148
  config_data = _load_config_file(config_path)
113
149
 
114
- # 2. 加载环境变量覆盖
150
+ # 3. 加载环境变量覆盖
115
151
  env_overrides = _load_env_overrides(envprefix)
116
152
  config_data.update(env_overrides)
117
153
 
118
- # 3. 设置应用信息
154
+ # 4. 设置应用信息
119
155
  config_data.update({"appname": appname, "approot": approot})
120
156
 
121
- # 4. 确定日志级别(优先级处理)
157
+ # 5. 确定日志级别(优先级处理)
122
158
  log_level = _determine_log_level(config_data)
123
159
  config_data.setdefault("logger", {})["level"] = log_level
124
160
 
125
- # 5. 初始化日志(使用字典配置)
161
+ # 6. 初始化日志(使用字典配置)
126
162
  setup_logger(appname, **config_data["logger"])
127
163
 
128
- # 8. 创建配置实例(最后验证)
164
+ # 7. 创建配置实例(最后验证)
129
165
  config = AppConfig.model_validate(config_data)
130
166
 
131
167
  return config
@@ -97,12 +97,12 @@ def run_error(*, message: str, code: str | None = None) -> dict[str, Any]:
97
97
  # ── Step ──
98
98
 
99
99
 
100
- def step_started(*, step_name: str) -> dict[str, Any]:
101
- return _ev(EventType.STEP_STARTED, step_name=step_name)
100
+ def step_started(*, step_name: str, step_id: str | None = None) -> dict[str, Any]:
101
+ return _ev(EventType.STEP_STARTED, step_name=step_name, step_id=step_id or str(uuid4()))
102
102
 
103
103
 
104
- def step_finished(*, step_name: str) -> dict[str, Any]:
105
- return _ev(EventType.STEP_FINISHED, step_name=step_name)
104
+ def step_finished(*, step_name: str, step_id: str) -> dict[str, Any]:
105
+ return _ev(EventType.STEP_FINISHED, step_name=step_name, step_id=step_id)
106
106
 
107
107
 
108
108
  # ── State ──
@@ -370,7 +370,8 @@ class Flow:
370
370
  async def stream(self, context: "FlowContext") -> AsyncIterator[dict[str, Any]]:
371
371
  """流式执行 Flow(输出 AG-UI 标准事件)"""
372
372
  context.trace.started_at = time.time()
373
- yield event.step_started(step_name=f"flow:{self.name}")
373
+ flow_sid = str(uuid4())
374
+ yield event.step_started(step_name=f"flow:{self.name}", step_id=flow_sid)
374
375
 
375
376
  try:
376
377
  if not self.edges:
@@ -386,7 +387,7 @@ class Flow:
386
387
  context.trace.finished_at = time.time()
387
388
  context.trace.total_usage = context.usage
388
389
 
389
- yield event.step_finished(step_name=f"flow:{self.name}")
390
+ yield event.step_finished(step_name=f"flow:{self.name}", step_id=flow_sid)
390
391
 
391
392
  async def _stream_sequential(self, context: "FlowContext") -> AsyncIterator[dict[str, Any]]:
392
393
  """顺序流式执行"""
@@ -437,7 +438,8 @@ class Flow:
437
438
  context.trace.record_node_start(current_node_id, "message", inputs=msg_config)
438
439
 
439
440
  # message 节点增加 STEP 事件
440
- step_evt = event.step_started(step_name=f"message:{current_node_id}")
441
+ msg_sid = str(uuid4())
442
+ step_evt = event.step_started(step_name=f"message:{current_node_id}", step_id=msg_sid)
441
443
  step_evt["_node_id"] = current_node_id
442
444
  step_evt["_label"] = msg_config.get("label")
443
445
  step_evt["_echo"] = msg_config.get("echo", True)
@@ -454,7 +456,7 @@ class Flow:
454
456
  text = template.format_map(_SafeFormatDict(context.variables))
455
457
  context.set_output(current_node_id, {"result": text})
456
458
 
457
- fin_evt = event.step_finished(step_name=f"message:{current_node_id}")
459
+ fin_evt = event.step_finished(step_name=f"message:{current_node_id}", step_id=msg_sid)
458
460
  fin_evt["_node_id"] = current_node_id
459
461
  fin_evt["_label"] = msg_config.get("label")
460
462
  fin_evt["_echo"] = msg_config.get("echo", True)
@@ -469,7 +471,8 @@ class Flow:
469
471
 
470
472
  context.trace.record_node_start(current_node_id, "parallel", inputs=config)
471
473
 
472
- step_evt = event.step_started(step_name=f"parallel:{current_node_id}")
474
+ parallel_sid = str(uuid4())
475
+ step_evt = event.step_started(step_name=f"parallel:{current_node_id}", step_id=parallel_sid)
473
476
  step_evt["_node_id"] = current_node_id
474
477
  step_evt["_label"] = None
475
478
  step_evt["_echo"] = False
@@ -510,7 +513,7 @@ class Flow:
510
513
  merged.update(branch_result)
511
514
  context.set_output(current_node_id, merged)
512
515
 
513
- fin_evt = event.step_finished(step_name=f"parallel:{current_node_id}")
516
+ fin_evt = event.step_finished(step_name=f"parallel:{current_node_id}", step_id=parallel_sid)
514
517
  fin_evt["_node_id"] = current_node_id
515
518
  fin_evt["_label"] = None
516
519
  fin_evt["_echo"] = False
@@ -533,7 +536,8 @@ class Flow:
533
536
 
534
537
  context.trace.record_node_start(current_node_id, "iteration", inputs=config)
535
538
 
536
- step_evt = event.step_started(step_name=f"iteration:{current_node_id}")
539
+ iter_sid = str(uuid4())
540
+ step_evt = event.step_started(step_name=f"iteration:{current_node_id}", step_id=iter_sid)
537
541
  step_evt["_node_id"] = current_node_id
538
542
  step_evt["_label"] = None
539
543
  step_evt["_echo"] = False
@@ -574,7 +578,7 @@ class Flow:
574
578
  iteration_output = {"results": results}
575
579
  context.set_output(current_node_id, iteration_output)
576
580
 
577
- fin_evt = event.step_finished(step_name=f"iteration:{current_node_id}")
581
+ fin_evt = event.step_finished(step_name=f"iteration:{current_node_id}", step_id=iter_sid)
578
582
  fin_evt["_node_id"] = current_node_id
579
583
  fin_evt["_label"] = None
580
584
  fin_evt["_echo"] = False
@@ -3,6 +3,7 @@
3
3
  """Agent 节点处理器 — 从 flow.py 提取"""
4
4
 
5
5
  from typing import TYPE_CHECKING, Any, AsyncIterator
6
+ from uuid import uuid4
6
7
 
7
8
  from .. import event
8
9
  from ..exceptions import FlowError
@@ -46,8 +47,9 @@ class AgentNodeHandler(NodeHandler):
46
47
  async def stream(self, node: dict, context: "FlowContext", node_id: str) -> AsyncIterator[dict[str, Any]]:
47
48
  config = node.get("config", {})
48
49
  step_name = self.get_step_name(node, node_id)
50
+ sid = str(uuid4())
49
51
 
50
- yield event.step_started(step_name=step_name)
52
+ yield event.step_started(step_name=step_name, step_id=sid)
51
53
  resolved = self.resolve_inputs(config, context)
52
54
  ag = self._create_agent(config, context)
53
55
  async for evt in ag.stream(context, inputs=resolved):
@@ -63,4 +65,4 @@ class AgentNodeHandler(NodeHandler):
63
65
  tool_calls = context.pop_execution_records()
64
66
  context.set_variable("_last_node_tool_calls", tool_calls)
65
67
 
66
- yield event.step_finished(step_name=step_name)
68
+ yield event.step_finished(step_name=step_name, step_id=sid)
@@ -3,6 +3,7 @@
3
3
  """NodeHandler 基类 — 所有执行类节点的公共接口"""
4
4
 
5
5
  from typing import TYPE_CHECKING, Any, AsyncIterator
6
+ from uuid import uuid4
6
7
 
7
8
  from .. import event
8
9
 
@@ -45,7 +46,8 @@ class NodeHandler:
45
46
  需要流式输出的节点(如 agent, llm_chat)应覆盖此方法。
46
47
  """
47
48
  step_name = self.get_step_name(node, node_id)
48
- yield event.step_started(step_name=step_name)
49
+ sid = str(uuid4())
50
+ yield event.step_started(step_name=step_name, step_id=sid)
49
51
  result = await self.execute(node, context)
50
52
  context.set_output(node_id, result)
51
- yield event.step_finished(step_name=step_name)
53
+ yield event.step_finished(step_name=step_name, step_id=sid)
@@ -32,8 +32,9 @@ class EchoNodeHandler(NodeHandler):
32
32
  async def stream(self, node: dict, context: "FlowContext", node_id: str) -> AsyncIterator[dict[str, Any]]:
33
33
  config = node.get("config", {})
34
34
  step_name = self.get_step_name(node, node_id)
35
+ sid = str(uuid4())
35
36
 
36
- yield event.step_started(step_name=step_name)
37
+ yield event.step_started(step_name=step_name, step_id=sid)
37
38
 
38
39
  resolved = self.resolve_inputs(config, context)
39
40
  content = resolved.get("content", "")
@@ -51,4 +52,4 @@ class EchoNodeHandler(NodeHandler):
51
52
  yield event.text_message_end(message_id=msg_id)
52
53
 
53
54
  context.set_output(node_id, {"result": content})
54
- yield event.step_finished(step_name=step_name)
55
+ yield event.step_finished(step_name=step_name, step_id=sid)
@@ -92,15 +92,17 @@ class LLMChatNodeHandler(NodeHandler):
92
92
  if not use_stream:
93
93
  # 非流式:走默认 execute 路径
94
94
  step_name = self.get_step_name(node, node_id)
95
- yield event.step_started(step_name=step_name)
95
+ sid = str(uuid4())
96
+ yield event.step_started(step_name=step_name, step_id=sid)
96
97
  result = await self.execute(node, context)
97
98
  context.set_output(node_id, result)
98
- yield event.step_finished(step_name=step_name)
99
+ yield event.step_finished(step_name=step_name, step_id=sid)
99
100
  return
100
101
 
101
102
  # 流式输出
102
103
  step_name = self.get_step_name(node, node_id)
103
- yield event.step_started(step_name=step_name)
104
+ sid = str(uuid4())
105
+ yield event.step_started(step_name=step_name, step_id=sid)
104
106
 
105
107
  resolved_inputs = self.resolve_inputs(config, context)
106
108
  prompt_text = self._build_prompt(config.get("prompt", ""), resolved_inputs)
@@ -153,4 +155,4 @@ class LLMChatNodeHandler(NodeHandler):
153
155
  result_text = "".join(content_parts)
154
156
  context.set_output(node_id, {"result": result_text})
155
157
 
156
- yield event.step_finished(step_name=step_name)
158
+ yield event.step_finished(step_name=step_name, step_id=sid)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agstack
3
- Version: 1.21.1
3
+ Version: 1.22.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>
@@ -43,6 +43,8 @@ Provides-Extra: graph
43
43
  Requires-Dist: nebula3-python>=3.8.3; extra == "graph"
44
44
  Provides-Extra: redis
45
45
  Requires-Dist: redis>=5.0; extra == "redis"
46
+ Provides-Extra: yaml
47
+ Requires-Dist: pyyaml>=6.0; extra == "yaml"
46
48
  Provides-Extra: std
47
49
  Requires-Dist: agstack[mq]; extra == "std"
48
50
  Requires-Dist: agstack[db]; extra == "std"
@@ -38,3 +38,6 @@ redis>=5.0
38
38
  [std]
39
39
  agstack[mq]
40
40
  agstack[db]
41
+
42
+ [yaml]
43
+ pyyaml>=6.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agstack"
3
- version = "1.21.1"
3
+ version = "1.22.1"
4
4
  description = "Production-ready toolkit for building FastAPI and LLM applications"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -60,6 +60,7 @@ db = ["asyncpg>=0.30.0"]
60
60
  es = ["elasticsearch[async]>=9.3.0"]
61
61
  graph = ["nebula3-python>=3.8.3"]
62
62
  redis = ["redis>=5.0"]
63
+ yaml = ["pyyaml>=6.0"]
63
64
  std = ["agstack[mq]", "agstack[db]"]
64
65
  pro = ["agstack[std]", "agstack[es]"]
65
66
  ent = ["agstack[pro]", "agstack[graph]"]
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
File without changes