10xscale-agentflow-cli 0.2.8__tar.gz → 0.2.9__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 (102) hide show
  1. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/PKG-INFO +1 -1
  2. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/PKG-INFO +1 -1
  3. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow.json +2 -1
  4. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/middleware/request_limits.py +1 -2
  5. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/loader.py +36 -1
  6. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/router.py +45 -3
  7. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/services/checkpointer_service.py +2 -1
  8. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/schemas/graph_schemas.py +29 -3
  9. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/services/graph_service.py +20 -4
  10. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/router.py +12 -1
  11. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/services/store_service.py +2 -1
  12. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/pyproject.toml +1 -1
  13. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/SOURCES.txt +0 -0
  14. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/dependency_links.txt +0 -0
  15. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/entry_points.txt +0 -0
  16. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/not-zip-safe +0 -0
  17. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/requires.txt +0 -0
  18. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/10xscale_agentflow_cli.egg-info/top_level.txt +0 -0
  19. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/ActionPlan.md +0 -0
  20. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/MANIFEST.in +0 -0
  21. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/README.md +0 -0
  22. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/__init__.py +0 -0
  23. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/__init__.py +0 -0
  24. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/commands/__init__.py +0 -0
  25. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/commands/api.py +0 -0
  26. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/commands/build.py +0 -0
  27. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/commands/init.py +0 -0
  28. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/commands/version.py +0 -0
  29. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/constants.py +0 -0
  30. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/core/__init__.py +0 -0
  31. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/core/config.py +0 -0
  32. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/core/output.py +0 -0
  33. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/core/validation.py +0 -0
  34. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/exceptions.py +0 -0
  35. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/logger.py +0 -0
  36. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/main.py +0 -0
  37. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/templates/__init__.py +0 -0
  38. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/cli/templates/defaults.py +0 -0
  39. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/__init__.py +0 -0
  40. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/__init__.py +0 -0
  41. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/__init__.py +0 -0
  42. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/__init__.py +0 -0
  43. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/auth_backend.py +0 -0
  44. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/authorization.py +0 -0
  45. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/base_auth.py +0 -0
  46. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/jwt_auth.py +0 -0
  47. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/auth/permissions.py +0 -0
  48. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/__init__.py +0 -0
  49. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/graph_config.py +0 -0
  50. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/sentry_config.py +0 -0
  51. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/settings.py +0 -0
  52. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/setup_logs.py +0 -0
  53. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/setup_middleware.py +0 -0
  54. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/config/worker_middleware.py +0 -0
  55. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/exceptions/__init__.py +0 -0
  56. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/exceptions/general_exception.py +0 -0
  57. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/exceptions/handle_errors.py +0 -0
  58. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/exceptions/resources_exceptions.py +0 -0
  59. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/exceptions/user_exception.py +0 -0
  60. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/middleware/__init__.py +0 -0
  61. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/middleware/security_headers.py +0 -0
  62. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/utils/__init__.py +0 -0
  63. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/core/utils/log_sanitizer.py +0 -0
  64. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/main.py +0 -0
  65. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/__init__.py +0 -0
  66. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/a2a.py +0 -0
  67. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/a2ui.py +0 -0
  68. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/__init__.py +0 -0
  69. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/schemas/__init__.py +0 -0
  70. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/schemas/checkpointer_schemas.py +0 -0
  71. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/checkpointer/services/__init__.py +0 -0
  72. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/__init__.py +0 -0
  73. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/router.py +0 -0
  74. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/schemas/__init__.py +0 -0
  75. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/graph/services/__init__.py +0 -0
  76. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/ping/__init__.py +0 -0
  77. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/ping/router.py +0 -0
  78. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/setup_router.py +0 -0
  79. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/__init__.py +0 -0
  80. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/schemas/__init__.py +0 -0
  81. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/schemas/store_schemas.py +0 -0
  82. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/routers/store/services/__init__.py +0 -0
  83. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/tasks/__init__.py +0 -0
  84. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/tasks/user_tasks.py +0 -0
  85. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/__init__.py +0 -0
  86. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/callable_helper.py +0 -0
  87. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/parse_output.py +0 -0
  88. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/response_helper.py +0 -0
  89. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/schemas/__init__.py +0 -0
  90. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/schemas/output_schemas.py +0 -0
  91. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/schemas/user_schemas.py +0 -0
  92. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/snowflake_id_generator.py +0 -0
  93. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/swagger_helper.py +0 -0
  94. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/utils/thread_name_generator.py +0 -0
  95. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/agentflow_cli/src/app/worker.py +0 -0
  96. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/graph/__init__.py +0 -0
  97. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/graph/react.py +0 -0
  98. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/graph/thread_name_generator.py +0 -0
  99. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/plan.md +0 -0
  100. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/scripts/generate_docs.py +0 -0
  101. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/setup.cfg +0 -0
  102. {10xscale_agentflow_cli-0.2.8 → 10xscale_agentflow_cli-0.2.9}/tests/test_utils_parse_and_callable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 10xscale-agentflow-cli
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: CLI and API for 10xscale AgentFlow
5
5
  Author-email: 10xscale <contact@10xscale.ai>
6
6
  Maintainer-email: Shudipto Trafder <shudiptotrafder@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 10xscale-agentflow-cli
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: CLI and API for 10xscale AgentFlow
5
5
  Author-email: 10xscale <contact@10xscale.ai>
6
6
  Maintainer-email: Shudipto Trafder <shudiptotrafder@gmail.com>
@@ -2,5 +2,6 @@
2
2
  "agent": "graph.react:app",
3
3
  "thread_name_generator": "graph.thread_name_generator:MyNameGenerator",
4
4
  "env": ".env",
5
- "auth": null
5
+ "auth": null,
6
+ "agent_card": "agent_card.md"
6
7
  }
@@ -56,8 +56,7 @@ class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
56
56
  "error": {
57
57
  "code": "REQUEST_TOO_LARGE",
58
58
  "message": (
59
- f"Request body too large. "
60
- f"Maximum size is {self.max_size_mb:.1f}MB"
59
+ f"Request body too large. Maximum size is {self.max_size_mb:.1f}MB"
61
60
  ),
62
61
  "max_size_bytes": self.max_size,
63
62
  "max_size_mb": self.max_size_mb,
@@ -22,6 +22,9 @@ logger = logging.getLogger("agentflow-cli.loader")
22
22
 
23
23
 
24
24
  async def load_graph(path: str) -> CompiledGraph | None:
25
+ if ":" not in path:
26
+ raise ValueError(f"Invalid graph path format '{path}'. Expected 'module:attribute'.")
27
+
25
28
  module_name_importable, function_name = path.split(":")
26
29
 
27
30
  try:
@@ -44,6 +47,12 @@ async def load_graph(path: str) -> CompiledGraph | None:
44
47
  else:
45
48
  raise TypeError("Loaded object is not a CompiledGraph.")
46
49
 
50
+ except ModuleNotFoundError as e:
51
+ logger.error(f"Module not found when loading graph from {path}: {e}")
52
+ raise ModuleNotFoundError(f"Module not found for graph path '{path}': {e}")
53
+ except AttributeError as e:
54
+ logger.error(f"Attribute not found when loading graph from {path}: {e}")
55
+ raise AttributeError(f"Attribute not found for graph path '{path}': {e}")
47
56
  except Exception as e:
48
57
  logger.error(f"Error loading graph from {path}: {e}")
49
58
  raise Exception(f"Failed to load graph from {path}: {e}")
@@ -55,6 +64,9 @@ def load_checkpointer(path: str | None) -> BaseCheckpointer | None:
55
64
  if not path:
56
65
  return None
57
66
 
67
+ if ":" not in path:
68
+ raise ValueError(f"Invalid checkpointer path format '{path}'. Expected 'module:attribute'.")
69
+
58
70
  module_name_importable, function_name = path.split(":")
59
71
 
60
72
  try:
@@ -69,6 +81,12 @@ def load_checkpointer(path: str | None) -> BaseCheckpointer | None:
69
81
  logger.info(f"Successfully loaded BaseCheckpointer '{function_name}' from {path}.")
70
82
  else:
71
83
  raise TypeError("Loaded object is not a BaseCheckpointer.")
84
+ except ModuleNotFoundError as e:
85
+ logger.error(f"Module not found when loading BaseCheckpointer from {path}: {e}")
86
+ raise ModuleNotFoundError(f"Module not found for checkpointer path '{path}': {e}")
87
+ except AttributeError as e:
88
+ logger.error(f"Attribute not found when loading BaseCheckpointer from {path}: {e}")
89
+ raise AttributeError(f"Attribute not found for checkpointer path '{path}': {e}")
72
90
  except Exception as e:
73
91
  logger.error(f"Error loading BaseCheckpointer from {path}: {e}")
74
92
  raise Exception(f"Failed to load BaseCheckpointer from {path}: {e}")
@@ -80,6 +98,9 @@ def load_store(path: str | None) -> BaseStore | None:
80
98
  if not path:
81
99
  return None
82
100
 
101
+ if ":" not in path:
102
+ raise ValueError(f"Invalid store path format '{path}'. Expected 'module:attribute'.")
103
+
83
104
  module_name_importable, function_name = path.split(":")
84
105
 
85
106
  try:
@@ -94,6 +115,12 @@ def load_store(path: str | None) -> BaseStore | None:
94
115
  logger.info(f"Successfully loaded graph '{function_name}' from {path}.")
95
116
  else:
96
117
  raise TypeError("Loaded object is not a BaseStore.")
118
+ except ModuleNotFoundError as e:
119
+ logger.error(f"Module not found when loading BaseStore from {path}: {e}")
120
+ raise ModuleNotFoundError(f"Module not found for store path '{path}': {e}")
121
+ except AttributeError as e:
122
+ logger.error(f"Attribute not found when loading BaseStore from {path}: {e}")
123
+ raise AttributeError(f"Attribute not found for store path '{path}': {e}")
97
124
  except Exception as e:
98
125
  logger.error(f"Error loading BaseStore from {path}: {e}")
99
126
  raise Exception(f"Failed to load BaseStore from {path}: {e}")
@@ -134,13 +161,15 @@ def load_auth(path: str | None) -> BaseAuth | None:
134
161
  if not path:
135
162
  return None
136
163
 
164
+ if ":" not in path:
165
+ raise ValueError(f"Invalid auth path format '{path}'. Expected 'module:attribute'.")
166
+
137
167
  module_name_importable, function_name = path.split(":")
138
168
 
139
169
  try:
140
170
  module = importlib.import_module(module_name_importable)
141
171
  entry_point_obj = getattr(module, function_name)
142
172
 
143
- # If it's a class, instantiate it; if it's an instance, use as is
144
173
  if inspect.isclass(entry_point_obj) and issubclass(entry_point_obj, BaseAuth):
145
174
  auth = entry_point_obj()
146
175
  elif isinstance(entry_point_obj, BaseAuth):
@@ -149,6 +178,12 @@ def load_auth(path: str | None) -> BaseAuth | None:
149
178
  raise TypeError("Loaded object is not a subclass or instance of BaseAuth.")
150
179
 
151
180
  logger.info(f"Successfully loaded BaseAuth '{function_name}' from {path}.")
181
+ except ModuleNotFoundError as e:
182
+ logger.error(f"Module not found when loading BaseAuth from {path}: {e}")
183
+ raise ModuleNotFoundError(f"Module not found for auth path '{path}': {e}")
184
+ except AttributeError as e:
185
+ logger.error(f"Attribute not found when loading BaseAuth from {path}: {e}")
186
+ raise AttributeError(f"Attribute not found for auth path '{path}': {e}")
152
187
  except Exception as e:
153
188
  logger.error(f"Error loading BaseAuth from {path}: {e}")
154
189
  raise Exception(f"Failed to load BaseAuth from {path}: {e}")
@@ -3,7 +3,7 @@
3
3
  from typing import Any
4
4
 
5
5
  from agentflow.state import Message
6
- from fastapi import APIRouter, Depends, Request, status
6
+ from fastapi import APIRouter, Depends, HTTPException, Request, status
7
7
  from injectq.integrations import InjectAPI
8
8
 
9
9
  from agentflow_cli.src.app.core.auth.permissions import RequirePermission
@@ -26,6 +26,26 @@ from .services.checkpointer_service import CheckpointerService
26
26
  router = APIRouter(tags=["checkpointer"])
27
27
 
28
28
 
29
+ def validate_thread_id(thread_id: int | str) -> None:
30
+ if isinstance(thread_id, str):
31
+ if not thread_id.strip():
32
+ raise HTTPException(
33
+ status_code=422,
34
+ detail="thread_id cannot be empty or whitespace"
35
+ )
36
+ elif isinstance(thread_id, int):
37
+ if thread_id < 1:
38
+ raise HTTPException(
39
+ status_code=422,
40
+ detail="thread_id must be a non-negative integer"
41
+ )
42
+ else:
43
+ raise HTTPException(
44
+ status_code=422,
45
+ detail="thread_id must be a string or integer"
46
+ )
47
+
48
+
29
49
  @router.get(
30
50
  "/v1/threads/{thread_id}/state",
31
51
  status_code=status.HTTP_200_OK,
@@ -48,6 +68,8 @@ async def get_state(
48
68
  Returns:
49
69
  State response with state data or error
50
70
  """
71
+ validate_thread_id(thread_id)
72
+
51
73
  config = {"thread_id": thread_id}
52
74
 
53
75
  result = await service.get_state(
@@ -86,11 +108,11 @@ async def put_state(
86
108
  Returns:
87
109
  Success response or error
88
110
  """
111
+ validate_thread_id(thread_id)
89
112
  config = {"thread_id": thread_id}
90
113
  if payload.config:
91
114
  config.update(payload.config)
92
115
 
93
- # State is provided as dict; service will handle merging/reconstruction
94
116
  res = await service.put_state(
95
117
  config,
96
118
  user,
@@ -127,6 +149,7 @@ async def clear_state(
127
149
  Returns:
128
150
  Success response or error
129
151
  """
152
+ validate_thread_id(thread_id)
130
153
  config = {"thread_id": thread_id}
131
154
 
132
155
  res = await service.clear_state(
@@ -168,7 +191,10 @@ async def put_messages(
168
191
  Returns:
169
192
  Success response or error
170
193
  """
171
- # Convert message dicts to Message objects if needed
194
+ validate_thread_id(thread_id)
195
+ if not payload.messages:
196
+ raise HTTPException(status_code=422, detail="messages must not be empty")
197
+
172
198
  config = {"thread_id": thread_id}
173
199
  if payload.config:
174
200
  config.update(payload.config)
@@ -213,6 +239,10 @@ async def get_message(
213
239
  Returns:
214
240
  Message response with message data or error
215
241
  """
242
+ validate_thread_id(thread_id)
243
+ if not message_id or (isinstance(message_id, str) and not str(message_id).strip()):
244
+ raise HTTPException(status_code=422, detail="message_id is required and cannot be empty")
245
+
216
246
  config = {"thread_id": thread_id}
217
247
 
218
248
  result = await service.get_message(
@@ -255,6 +285,12 @@ async def list_messages(
255
285
  Returns:
256
286
  Messages list response with messages data or error
257
287
  """
288
+ validate_thread_id(thread_id)
289
+ if offset is not None and offset < 0:
290
+ raise HTTPException(status_code=422, detail="offset must be >= 0")
291
+ if limit is not None and limit <= 0:
292
+ raise HTTPException(status_code=422, detail="limit must be > 0")
293
+
258
294
  config = {"thread_id": thread_id}
259
295
 
260
296
  result = await service.get_messages(
@@ -297,6 +333,10 @@ async def delete_message(
297
333
  Returns:
298
334
  Success response or error
299
335
  """
336
+ validate_thread_id(thread_id)
337
+ if not message_id or (isinstance(message_id, str) and not str(message_id).strip()):
338
+ raise HTTPException(status_code=422, detail="message_id is required and cannot be empty")
339
+
300
340
  config = {"thread_id": thread_id}
301
341
  if payload.config:
302
342
  config.update(payload.config)
@@ -340,6 +380,7 @@ async def get_thread(
340
380
  Returns:
341
381
  Thread response with thread data or error
342
382
  """
383
+ validate_thread_id(thread_id)
343
384
  result = await service.get_thread(
344
385
  {"thread_id": thread_id},
345
386
  user,
@@ -415,6 +456,7 @@ async def delete_thread(
415
456
  Returns:
416
457
  Success response or error
417
458
  """
459
+ validate_thread_id(thread_id)
418
460
  config = {"thread_id": thread_id}
419
461
  if payload.config:
420
462
  config.update(payload.config)
@@ -2,6 +2,7 @@ from typing import Any
2
2
 
3
3
  from agentflow.checkpointer import BaseCheckpointer
4
4
  from agentflow.state import AgentState, Message
5
+ from fastapi import HTTPException
5
6
  from injectq import inject, singleton
6
7
 
7
8
  from agentflow_cli.src.app.core import logger
@@ -26,7 +27,7 @@ class CheckpointerService:
26
27
 
27
28
  def _config(self, config: dict[str, Any] | None, user: dict) -> dict[str, Any]:
28
29
  if not self.checkpointer:
29
- raise ValueError("Checkpointer is not configured")
30
+ raise HTTPException(status_code=503, detail="Checkpointer service is not available")
30
31
 
31
32
  cfg: dict[str, Any] = dict(config or {})
32
33
  cfg["user"] = user
@@ -2,7 +2,7 @@ from typing import Any
2
2
 
3
3
  from agentflow.state import Message
4
4
  from agentflow.utils import ResponseGranularity
5
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel, Field, field_validator
6
6
 
7
7
 
8
8
  class GraphInputSchema(BaseModel):
@@ -23,8 +23,18 @@ class GraphInputSchema(BaseModel):
23
23
  )
24
24
  recursion_limit: int = Field(
25
25
  default=25,
26
+ ge=1,
27
+ le=100,
26
28
  description="Maximum recursion limit for graph execution",
27
29
  )
30
+
31
+ @field_validator("messages")
32
+ @classmethod
33
+ def messages_must_not_be_empty(cls, v: list[Message]) -> list[Message]:
34
+ if not v:
35
+ raise ValueError("messages must contain at least one message")
36
+ return v
37
+
28
38
  response_granularity: ResponseGranularity = Field(
29
39
  default=ResponseGranularity.LOW,
30
40
  description="Granularity of the response (full, partial, low)",
@@ -111,7 +121,15 @@ class GraphSchema(BaseModel):
111
121
  class GraphStopSchema(BaseModel):
112
122
  """Schema for stopping graph execution."""
113
123
 
114
- thread_id: str = Field(..., description="Thread ID to stop execution for")
124
+ thread_id: str = Field(..., min_length=1, description="Thread ID to stop execution for")
125
+
126
+ @field_validator("thread_id")
127
+ @classmethod
128
+ def thread_id_not_blank(cls, v: str) -> str:
129
+ if not v.strip():
130
+ raise ValueError("thread_id cannot be empty or whitespace")
131
+ return v.strip()
132
+
115
133
  config: dict[str, Any] | None = Field(
116
134
  default=None, description="Optional configuration for the stop operation"
117
135
  )
@@ -137,7 +155,15 @@ class GraphSetupSchema(BaseModel):
137
155
  class FixGraphRequestSchema(BaseModel):
138
156
  """Schema for fixing graph state by removing messages with empty tool call content."""
139
157
 
140
- thread_id: str = Field(..., description="Thread ID to fix the graph state for")
158
+ thread_id: str = Field(..., min_length=1, description="Thread ID to fix the graph state for")
159
+
160
+ @field_validator("thread_id")
161
+ @classmethod
162
+ def thread_id_not_blank(cls, v: str) -> str:
163
+ if not v.strip():
164
+ raise ValueError("thread_id cannot be empty or whitespace")
165
+ return v.strip()
166
+
141
167
  config: dict[str, Any] | None = Field(
142
168
  default=None, description="Optional configuration for the fix operation"
143
169
  )
@@ -154,6 +154,9 @@ class GraphService:
154
154
  logger.info(f"Graph stop completed for thread {thread_id}: {result}")
155
155
  return result
156
156
 
157
+ except ValueError as e:
158
+ logger.warning(f"Graph stop input validation failed for thread {thread_id}: {e}")
159
+ raise HTTPException(status_code=422, detail=str(e))
157
160
  except Exception as e:
158
161
  logger.error(f"Graph stop failed for thread {thread_id}: {e}")
159
162
  raise HTTPException(
@@ -166,8 +169,8 @@ class GraphService:
166
169
  ):
167
170
  is_new_thread = False
168
171
  config = graph_input.config or {}
169
- if "thread_id" in config:
170
- thread_id = config["thread_id"]
172
+ if config.get("thread_id") and str(config["thread_id"]).strip():
173
+ thread_id = str(config["thread_id"]).strip()
171
174
  else:
172
175
  thread_id = await InjectQ.get_instance().atry_get("generated_id") or str(uuid4())
173
176
  is_new_thread = True
@@ -258,6 +261,9 @@ class GraphService:
258
261
  meta=meta,
259
262
  )
260
263
 
264
+ except ValueError as e:
265
+ logger.warning(f"Graph input validation failed: {e}")
266
+ raise HTTPException(status_code=422, detail=str(e))
261
267
  except Exception as e:
262
268
  logger.error(f"Graph execution failed: {e}")
263
269
  raise HTTPException(status_code=500, detail=f"Graph execution failed: {e!s}")
@@ -334,6 +340,9 @@ class GraphService:
334
340
  + "\n"
335
341
  )
336
342
 
343
+ except ValueError as e:
344
+ logger.warning(f"Graph stream input validation failed: {e}")
345
+ raise HTTPException(status_code=422, detail=str(e))
337
346
  except Exception as e:
338
347
  logger.error(f"Graph streaming failed: {e}")
339
348
  raise HTTPException(status_code=500, detail=f"Graph streaming failed: {e!s}")
@@ -341,9 +350,11 @@ class GraphService:
341
350
  async def graph_details(self) -> GraphSchema:
342
351
  try:
343
352
  logger.info("Getting graph details")
344
- # Fetch and return graph details
345
353
  res = self._graph.generate_graph()
346
354
  return GraphSchema(**res)
355
+ except ValueError as e:
356
+ logger.warning(f"Graph details validation failed: {e}")
357
+ raise HTTPException(status_code=422, detail=str(e))
347
358
  except Exception as e:
348
359
  logger.error(f"Failed to get graph details: {e}")
349
360
  raise HTTPException(status_code=500, detail=f"Failed to get graph details: {e!s}")
@@ -351,9 +362,11 @@ class GraphService:
351
362
  async def get_state_schema(self) -> dict:
352
363
  try:
353
364
  logger.info("Getting state schema")
354
- # Fetch and return state schema
355
365
  res: BaseModel = self._graph._state
356
366
  return res.model_json_schema()
367
+ except ValueError as e:
368
+ logger.warning(f"State schema validation failed: {e}")
369
+ raise HTTPException(status_code=422, detail=str(e))
357
370
  except Exception as e:
358
371
  logger.error(f"Failed to get state schema: {e}")
359
372
  raise HTTPException(status_code=500, detail=f"Failed to get state schema: {e!s}")
@@ -451,6 +464,9 @@ class GraphService:
451
464
  "removed_count": removed_count,
452
465
  "state": state.model_dump_json(serialize_as_any=True),
453
466
  }
467
+ except ValueError as e:
468
+ logger.warning(f"Fix graph input validation failed: {e}")
469
+ raise HTTPException(status_code=422, detail=str(e))
454
470
  except Exception as e:
455
471
  logger.error(f"Fix graph operation failed: {e}")
456
472
  raise HTTPException(status_code=500, detail=f"Fix graph operation failed: {e!s}")
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
- from fastapi import APIRouter, Body, Depends, Request, status
7
+ from fastapi import APIRouter, Body, Depends, HTTPException, Request, status
8
8
  from injectq.integrations import InjectAPI
9
9
 
10
10
  from agentflow_cli.src.app.core.auth.permissions import RequirePermission
@@ -64,6 +64,8 @@ async def search_memories(
64
64
  user: dict[str, Any] = Depends(RequirePermission("store", "read")),
65
65
  ):
66
66
  """Search stored memories."""
67
+ if not payload.query or (isinstance(payload.query, str) and not payload.query.strip()):
68
+ raise HTTPException(status_code=422, detail="query is required and cannot be empty")
67
69
 
68
70
  result = await service.search_memories(payload, user)
69
71
  return success_response(result, request)
@@ -87,6 +89,8 @@ async def get_memory(
87
89
  user: dict[str, Any] = Depends(RequirePermission("store", "read")),
88
90
  ):
89
91
  """Get a memory by ID."""
92
+ if not memory_id or not memory_id.strip():
93
+ raise HTTPException(status_code=422, detail="memory_id is required and cannot be empty")
90
94
 
91
95
  cfg = payload.config if payload else {}
92
96
  opts = payload.options if payload else None
@@ -112,6 +116,9 @@ async def list_memories(
112
116
  ):
113
117
  """List stored memories."""
114
118
 
119
+ if payload and payload.limit is not None and payload.limit <= 0:
120
+ raise HTTPException(status_code=422, detail="limit must be > 0")
121
+
115
122
  if payload is None:
116
123
  payload = ListMemoriesSchema()
117
124
  cfg = payload.config or {}
@@ -135,6 +142,8 @@ async def update_memory(
135
142
  user: dict[str, Any] = Depends(RequirePermission("store", "write")),
136
143
  ):
137
144
  """Update a stored memory."""
145
+ if not memory_id or not memory_id.strip():
146
+ raise HTTPException(status_code=422, detail="memory_id is required and cannot be empty")
138
147
 
139
148
  result = await service.update_memory(memory_id, payload, user)
140
149
  return success_response(result, request, message="Memory updated successfully")
@@ -158,6 +167,8 @@ async def delete_memory(
158
167
  user: dict[str, Any] = Depends(RequirePermission("store", "delete")),
159
168
  ):
160
169
  """Delete a stored memory."""
170
+ if not memory_id or not memory_id.strip():
171
+ raise HTTPException(status_code=422, detail="memory_id is required and cannot be empty")
161
172
 
162
173
  config_payload = payload.config if payload else {}
163
174
  options_payload = payload.options if payload else None
@@ -4,6 +4,7 @@ from typing import Any
4
4
 
5
5
  from agentflow.state import Message
6
6
  from agentflow.store import BaseStore
7
+ from fastapi import HTTPException
7
8
  from injectq import inject, singleton
8
9
 
9
10
  from agentflow_cli.src.app.core import logger
@@ -30,7 +31,7 @@ class StoreService:
30
31
 
31
32
  def _get_store(self) -> BaseStore:
32
33
  if not self.store:
33
- raise ValueError("Store is not configured")
34
+ raise HTTPException(status_code=503, detail="Store service is not available")
34
35
  return self.store
35
36
 
36
37
  def _config(self, config: dict[str, Any] | None, user: dict[str, Any]) -> dict[str, Any]:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "10xscale-agentflow-cli"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  description = "CLI and API for 10xscale AgentFlow"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}