-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: implement ArtifactStore for retrievable tool output overflow #2149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7cd1853
a91d600
8297cea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,7 @@ | |
| from typing import TYPE_CHECKING | ||
| if TYPE_CHECKING: | ||
| from ..compaction.strategy import CompactionStrategy | ||
| from ..context.artifact_store import FileSystemArtifactStore | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move the concrete artifact-store import out of module scope. Line [45] pulls a concrete filesystem implementation into a core config module at import time. This tightens coupling and adds unnecessary import-path work even when artifact spilling is disabled. Keep this as a lazy/runtime import in the wiring path instead. As per coding guidelines, "Core SDK (praisonaiagents) must use Protocol-Driven Core architecture with only protocols, hooks, adapters, base classes, and decorators - no heavy implementations" and "All optional dependencies must use lazy imports - never import heavy dependencies at module level; instead import inside functions to avoid import-time performance impact". 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
|
|
||
| class MemoryBackend(str, Enum): | ||
|
|
@@ -935,7 +936,8 @@ class ToolConfig: | |
| """ | ||
| Configuration for tool execution behavior. | ||
|
|
||
| Configuration for tool execution behavior including timeout, retry policy, and parallel execution. | ||
| Configuration for tool execution behavior including timeout, retry policy, parallel execution, | ||
| and artifact storage for large outputs. | ||
|
|
||
| Usage: | ||
| # Simple enable with defaults | ||
|
|
@@ -948,8 +950,12 @@ class ToolConfig: | |
| parallel=True, | ||
| )) | ||
|
|
||
| # With timeout only | ||
| Agent(tool_config=ToolConfig(timeout=30)) | ||
| # With artifact storage for large outputs | ||
| Agent(tool_config=ToolConfig( | ||
| output_limit=32000, | ||
| enable_artifacts=True, | ||
| artifact_retention_days=14, | ||
| )) | ||
| """ | ||
| # Tool execution timeout in seconds | ||
| timeout: Optional[int] = None | ||
|
|
@@ -960,6 +966,26 @@ class ToolConfig: | |
| # Enable parallel execution of batched LLM tool calls | ||
| parallel: bool = False | ||
|
|
||
| # Tool output handling and artifact storage | ||
| output_limit: int = 16000 # Maximum bytes before spilling to artifact store | ||
| output_max_lines: Optional[int] = None # Maximum lines before spilling | ||
| output_direction: str = "both" # Truncation direction: "head", "tail", or "both" | ||
| enable_artifacts: bool = False # Whether to enable artifact storage (default False for backward compat) | ||
| artifact_retention_days: int = 7 # Days to retain artifacts before garbage collection | ||
| artifact_store: Optional[Any] = None # Custom artifact store instance | ||
| redact_secrets: bool = True # Whether to redact secrets from artifacts | ||
|
|
||
| def __post_init__(self) -> None: | ||
| """Validate configuration after initialization.""" | ||
| if self.output_limit <= 0: | ||
| raise ValueError("tool_config.output_limit must be > 0") | ||
| if self.output_max_lines is not None and self.output_max_lines <= 0: | ||
| raise ValueError("tool_config.output_max_lines must be > 0 when provided") | ||
| if self.output_direction not in {"head", "tail", "both"}: | ||
| raise ValueError("tool_config.output_direction must be one of: 'head', 'tail', 'both'") | ||
| if self.artifact_retention_days < 0: | ||
| raise ValueError("tool_config.artifact_retention_days must be >= 0") | ||
|
|
||
| def to_dict(self) -> Dict[str, Any]: | ||
| """Convert to dictionary.""" | ||
| return { | ||
|
|
@@ -970,6 +996,13 @@ def to_dict(self) -> Dict[str, Any]: | |
| else self.retry_policy | ||
| ), | ||
| "parallel": self.parallel, | ||
| "output_limit": self.output_limit, | ||
| "output_max_lines": self.output_max_lines, | ||
| "output_direction": self.output_direction, | ||
| "enable_artifacts": self.enable_artifacts, | ||
| "artifact_retention_days": self.artifact_retention_days, | ||
| "artifact_store": self.artifact_store, | ||
| "redact_secrets": self.redact_secrets, | ||
| } | ||
|
|
||
| @classmethod | ||
|
|
@@ -988,6 +1021,13 @@ def from_dict(cls, data: Dict[str, Any]) -> "ToolConfig": | |
| timeout=data.get("timeout"), | ||
| retry_policy=retry_policy, | ||
| parallel=data.get("parallel", False), | ||
| output_limit=data.get("output_limit", 16000), | ||
| output_max_lines=data.get("output_max_lines"), | ||
| output_direction=data.get("output_direction", "both"), | ||
| enable_artifacts=data.get("enable_artifacts", False), | ||
| artifact_retention_days=data.get("artifact_retention_days", 7), | ||
| artifact_store=data.get("artifact_store"), | ||
| redact_secrets=data.get("redact_secrets", True), | ||
| ) | ||
|
|
||
|
|
||
|
|
@@ -1513,6 +1553,8 @@ def resolve_tools(value: ToolParam) -> Optional[ToolConfig]: | |
| return value | ||
|
|
||
|
|
||
|
|
||
|
|
||
| __all__ = [ | ||
| # Enums | ||
| "MemoryBackend", | ||
|
|
@@ -1562,6 +1604,7 @@ def resolve_tools(value: ToolParam) -> Optional[ToolConfig]: | |
| "AutonomyParam", | ||
| "ToolSearchParam", | ||
| "ToolParam", | ||
| "ToolOutputParam", | ||
| # Precedence ladder resolvers | ||
| "resolve_memory", | ||
| "resolve_knowledge", | ||
|
|
@@ -1573,4 +1616,5 @@ def resolve_tools(value: ToolParam) -> Optional[ToolConfig]: | |
| "resolve_autonomy", | ||
| "resolve_tool_search", | ||
| "resolve_tools", | ||
| "resolve_tool_output", | ||
|
Comment on lines
1607
to
+1619
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both |
||
| ] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Register all artifact retrieval tools, not just a subset.
artifact_loadandartifact_listexist inartifact_tools.pybut are never appended here, so agents can’t access full-content load or listing after spill.➕ Suggested fix
tools_to_add = [ artifact_tools.artifact_head, artifact_tools.artifact_tail, artifact_tools.artifact_grep, artifact_tools.artifact_chunk, + artifact_tools.artifact_load, + artifact_tools.artifact_list, ]📝 Committable suggestion
🤖 Prompt for AI Agents