fix: configure codex with a longer timeout
refactor: param names
This commit is contained in:
@@ -27,15 +27,14 @@ from .simple.base import SimpleTool
|
||||
# Field descriptions matching the original Chat tool exactly
|
||||
CHAT_FIELD_DESCRIPTIONS = {
|
||||
"prompt": (
|
||||
"Your question or idea for collaborative thinking. Provide detailed context, including your goal, what you've tried, and any specific challenges. "
|
||||
"Your question or idea for collaborative thinking to be sent to the external model. Provide detailed context, "
|
||||
"including your goal, what you've tried, and any specific challenges. "
|
||||
"WARNING: Large inline code must NOT be shared in prompt. Provide full-path to files on disk as separate parameter."
|
||||
),
|
||||
"files": (
|
||||
"Absolute file or folder paths for code context. Required whenever you reference source code—supply the FULL absolute path (do not shorten)."
|
||||
),
|
||||
"absolute_file_paths": ("Full, absolute file paths to relevant code in order to share with external model"),
|
||||
"images": "Image paths (absolute) or base64 strings for optional visual context.",
|
||||
"working_directory": (
|
||||
"Absolute directory path where generated code artifacts are stored. The directory must already exist."
|
||||
"working_directory_absolute_path": (
|
||||
"Absolute path to an existing directory where generated code artifacts can be saved."
|
||||
),
|
||||
}
|
||||
|
||||
@@ -44,9 +43,15 @@ class ChatRequest(ToolRequest):
|
||||
"""Request model for Chat tool"""
|
||||
|
||||
prompt: str = Field(..., description=CHAT_FIELD_DESCRIPTIONS["prompt"])
|
||||
files: Optional[list[str]] = Field(default_factory=list, description=CHAT_FIELD_DESCRIPTIONS["files"])
|
||||
absolute_file_paths: Optional[list[str]] = Field(
|
||||
default_factory=list,
|
||||
description=CHAT_FIELD_DESCRIPTIONS["absolute_file_paths"],
|
||||
)
|
||||
images: Optional[list[str]] = Field(default_factory=list, description=CHAT_FIELD_DESCRIPTIONS["images"])
|
||||
working_directory: str = Field(..., description=CHAT_FIELD_DESCRIPTIONS["working_directory"])
|
||||
working_directory_absolute_path: str = Field(
|
||||
...,
|
||||
description=CHAT_FIELD_DESCRIPTIONS["working_directory_absolute_path"],
|
||||
)
|
||||
|
||||
|
||||
class ChatTool(SimpleTool):
|
||||
@@ -105,7 +110,7 @@ class ChatTool(SimpleTool):
|
||||
def get_input_schema(self) -> dict[str, Any]:
|
||||
"""Generate input schema matching the original Chat tool expectations."""
|
||||
|
||||
required_fields = ["prompt", "working_directory"]
|
||||
required_fields = ["prompt", "working_directory_absolute_path"]
|
||||
if self.is_effective_auto_mode():
|
||||
required_fields.append("model")
|
||||
|
||||
@@ -116,19 +121,19 @@ class ChatTool(SimpleTool):
|
||||
"type": "string",
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["prompt"],
|
||||
},
|
||||
"files": {
|
||||
"absolute_file_paths": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["files"],
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["absolute_file_paths"],
|
||||
},
|
||||
"images": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["images"],
|
||||
},
|
||||
"working_directory": {
|
||||
"working_directory_absolute_path": {
|
||||
"type": "string",
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["working_directory"],
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["working_directory_absolute_path"],
|
||||
},
|
||||
"model": self.get_model_field_schema(),
|
||||
"temperature": {
|
||||
@@ -161,21 +166,25 @@ class ChatTool(SimpleTool):
|
||||
"type": "string",
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["prompt"],
|
||||
},
|
||||
"files": {
|
||||
"absolute_file_paths": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["files"],
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["absolute_file_paths"],
|
||||
},
|
||||
"images": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["images"],
|
||||
},
|
||||
"working_directory_absolute_path": {
|
||||
"type": "string",
|
||||
"description": CHAT_FIELD_DESCRIPTIONS["working_directory_absolute_path"],
|
||||
},
|
||||
}
|
||||
|
||||
def get_required_fields(self) -> list[str]:
|
||||
"""Required fields for ChatSimple tool"""
|
||||
return ["prompt", "working_directory"]
|
||||
return ["prompt", "working_directory_absolute_path"]
|
||||
|
||||
# === Hook Method Implementations ===
|
||||
|
||||
@@ -209,17 +218,18 @@ class ChatTool(SimpleTool):
|
||||
if error:
|
||||
return error
|
||||
|
||||
working_directory = getattr(request, "working_directory", None)
|
||||
working_directory = request.working_directory_absolute_path
|
||||
if working_directory:
|
||||
expanded = os.path.expanduser(working_directory)
|
||||
if not os.path.isabs(expanded):
|
||||
return (
|
||||
"Error: 'working_directory' must be an absolute path (you may use '~' which will be expanded). "
|
||||
"Error: 'working_directory_absolute_path' must be an absolute path (you may use '~' which will be expanded). "
|
||||
f"Received: {working_directory}"
|
||||
)
|
||||
if not os.path.isdir(expanded):
|
||||
return (
|
||||
"Error: 'working_directory' must reference an existing directory. " f"Received: {working_directory}"
|
||||
"Error: 'working_directory_absolute_path' must reference an existing directory. "
|
||||
f"Received: {working_directory}"
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -235,12 +245,13 @@ class ChatTool(SimpleTool):
|
||||
block, remainder, _ = self._extract_generated_code_block(response)
|
||||
if block:
|
||||
sanitized_text = remainder.strip()
|
||||
target_directory = request.working_directory_absolute_path
|
||||
try:
|
||||
artifact_path = self._persist_generated_code_block(block, request.working_directory)
|
||||
artifact_path = self._persist_generated_code_block(block, target_directory)
|
||||
except Exception as exc: # pragma: no cover - rare filesystem failures
|
||||
logger.error("Failed to persist generated code block: %s", exc, exc_info=True)
|
||||
warning = (
|
||||
f"WARNING: Unable to write zen_generated.code inside '{request.working_directory}'. "
|
||||
f"WARNING: Unable to write zen_generated.code inside '{target_directory}'. "
|
||||
"Check the path permissions and re-run. The generated code block is included below for manual handling."
|
||||
)
|
||||
|
||||
@@ -326,7 +337,7 @@ class ChatTool(SimpleTool):
|
||||
expanded = os.path.expanduser(working_directory)
|
||||
target_dir = Path(expanded).resolve()
|
||||
if not target_dir.is_dir():
|
||||
raise FileNotFoundError(f"Working directory '{working_directory}' does not exist")
|
||||
raise FileNotFoundError(f"Absolute working directory path '{working_directory}' does not exist")
|
||||
|
||||
target_file = target_dir / "zen_generated.code"
|
||||
if target_file.exists():
|
||||
|
||||
@@ -38,9 +38,9 @@ class CLinkRequest(BaseModel):
|
||||
default=None,
|
||||
description="Optional role preset defined in the CLI configuration (defaults to 'default').",
|
||||
)
|
||||
files: list[str] = Field(
|
||||
absolute_file_paths: list[str] = Field(
|
||||
default_factory=list,
|
||||
description=COMMON_FIELD_DESCRIPTIONS["files"],
|
||||
description=COMMON_FIELD_DESCRIPTIONS["absolute_file_paths"],
|
||||
)
|
||||
images: list[str] = Field(
|
||||
default_factory=list,
|
||||
@@ -140,7 +140,7 @@ class CLinkTool(SimpleTool):
|
||||
"enum": self._all_roles or ["default"],
|
||||
"description": role_description,
|
||||
},
|
||||
"files": SchemaBuilder.SIMPLE_FIELD_SCHEMAS["files"],
|
||||
"absolute_file_paths": SchemaBuilder.SIMPLE_FIELD_SCHEMAS["absolute_file_paths"],
|
||||
"images": SchemaBuilder.COMMON_FIELD_SCHEMAS["images"],
|
||||
"continuation_id": SchemaBuilder.COMMON_FIELD_SCHEMAS["continuation_id"],
|
||||
}
|
||||
@@ -183,7 +183,7 @@ class CLinkTool(SimpleTool):
|
||||
except KeyError as exc:
|
||||
self._raise_tool_error(str(exc))
|
||||
|
||||
files = self.get_request_files(request)
|
||||
absolute_file_paths = self.get_request_files(request)
|
||||
images = self.get_request_images(request)
|
||||
continuation_id = self.get_request_continuation_id(request)
|
||||
|
||||
@@ -209,7 +209,7 @@ class CLinkTool(SimpleTool):
|
||||
role=role_config,
|
||||
prompt=prompt_text,
|
||||
system_prompt=system_prompt_text if system_prompt_text.strip() else None,
|
||||
files=files,
|
||||
files=absolute_file_paths,
|
||||
images=images,
|
||||
)
|
||||
except CLIAgentError as exc:
|
||||
|
||||
@@ -218,7 +218,7 @@ class PlannerTool(WorkflowTool):
|
||||
"temperature", # Planning doesn't need temperature control
|
||||
"thinking_mode", # Planning doesn't need thinking mode
|
||||
"images", # Planning doesn't use images
|
||||
"files", # Planning doesn't use files
|
||||
"absolute_file_paths", # Planning doesn't use file attachments
|
||||
]
|
||||
|
||||
# Build schema with proper field exclusion (following consensus pattern)
|
||||
|
||||
@@ -29,7 +29,7 @@ COMMON_FIELD_DESCRIPTIONS = {
|
||||
"files, and findings so the agent can resume seamlessly."
|
||||
),
|
||||
"images": "Optional absolute image paths or base64 blobs for visual context.",
|
||||
"files": "Optional absolute file or folder paths (do not shorten).",
|
||||
"absolute_file_paths": "Full paths to relevant code",
|
||||
}
|
||||
|
||||
# Workflow-specific field descriptions
|
||||
|
||||
@@ -667,7 +667,7 @@ class BaseTool(ABC):
|
||||
"""
|
||||
# Only validate files/paths if they exist in the request
|
||||
file_fields = [
|
||||
"files",
|
||||
"absolute_file_paths",
|
||||
"file",
|
||||
"path",
|
||||
"directory",
|
||||
@@ -885,7 +885,7 @@ class BaseTool(ABC):
|
||||
|
||||
def handle_prompt_file(self, files: Optional[list[str]]) -> tuple[Optional[str], Optional[list[str]]]:
|
||||
"""
|
||||
Check for and handle prompt.txt in the files list.
|
||||
Check for and handle prompt.txt in the absolute file paths list.
|
||||
|
||||
If prompt.txt is found, reads its content and removes it from the files list.
|
||||
This file is treated specially as the main prompt, not as an embedded file.
|
||||
@@ -895,7 +895,7 @@ class BaseTool(ABC):
|
||||
mechanism to bypass token constraints while preserving response capacity.
|
||||
|
||||
Args:
|
||||
files: List of file paths (will be translated for current environment)
|
||||
files: List of absolute file paths (will be translated for current environment)
|
||||
|
||||
Returns:
|
||||
tuple: (prompt_content, updated_files_list)
|
||||
@@ -983,7 +983,7 @@ class BaseTool(ABC):
|
||||
f"MANDATORY ACTION REQUIRED: The prompt is too large for MCP's token limits (>{MCP_PROMPT_SIZE_LIMIT:,} characters). "
|
||||
"YOU MUST IMMEDIATELY save the prompt text to a temporary file named 'prompt.txt' in the working directory. "
|
||||
"DO NOT attempt to shorten or modify the prompt. SAVE IT AS-IS to 'prompt.txt'. "
|
||||
"Then resend the request with the absolute file path to 'prompt.txt' in the files parameter (must be FULL absolute path - DO NOT SHORTEN), "
|
||||
"Then resend the request, passing the absolute file path to 'prompt.txt' as part of the tool call, "
|
||||
"along with any other files you wish to share as context. Leave the prompt text itself empty or very brief in the new request. "
|
||||
"This is the ONLY way to handle large prompts - you MUST follow these exact steps."
|
||||
),
|
||||
@@ -991,7 +991,7 @@ class BaseTool(ABC):
|
||||
"metadata": {
|
||||
"prompt_size": len(text),
|
||||
"limit": MCP_PROMPT_SIZE_LIMIT,
|
||||
"instructions": "MANDATORY: Save prompt to 'prompt.txt' in current folder and include absolute path in files parameter. DO NOT modify or shorten the prompt.",
|
||||
"instructions": "MANDATORY: Save prompt to 'prompt.txt' in current folder and provide full path when recalling this tool.",
|
||||
},
|
||||
}
|
||||
return None
|
||||
|
||||
@@ -45,10 +45,10 @@ class SchemaBuilder:
|
||||
|
||||
# Simple tool-specific field schemas (workflow tools use relevant_files instead)
|
||||
SIMPLE_FIELD_SCHEMAS = {
|
||||
"files": {
|
||||
"absolute_file_paths": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["files"],
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["absolute_file_paths"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Base class for simple MCP tools.
|
||||
|
||||
Simple tools follow a straightforward pattern:
|
||||
1. Receive request
|
||||
2. Prepare prompt (with files, context, etc.)
|
||||
2. Prepare prompt (with absolute file paths, context, etc.)
|
||||
3. Call AI model
|
||||
4. Format and return response
|
||||
|
||||
@@ -50,7 +50,7 @@ class SimpleTool(BaseTool):
|
||||
"type": "string",
|
||||
"description": "Your question or idea...",
|
||||
},
|
||||
"files": SimpleTool.FILES_FIELD,
|
||||
"absolute_file_paths": SimpleTool.FILES_FIELD,
|
||||
}
|
||||
|
||||
def get_required_fields(self) -> List[str]:
|
||||
@@ -58,7 +58,7 @@ class SimpleTool(BaseTool):
|
||||
"""
|
||||
|
||||
# Common field definitions that simple tools can reuse
|
||||
FILES_FIELD = SchemaBuilder.SIMPLE_FIELD_SCHEMAS["files"]
|
||||
FILES_FIELD = SchemaBuilder.SIMPLE_FIELD_SCHEMAS["absolute_file_paths"]
|
||||
IMAGES_FIELD = SchemaBuilder.COMMON_FIELD_SCHEMAS["images"]
|
||||
|
||||
@abstractmethod
|
||||
@@ -79,7 +79,7 @@ class SimpleTool(BaseTool):
|
||||
"type": "string",
|
||||
"description": "The user's question or request",
|
||||
},
|
||||
"files": SimpleTool.FILES_FIELD, # Reuse common field
|
||||
"absolute_file_paths": SimpleTool.FILES_FIELD, # Reuse common field
|
||||
"max_tokens": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
@@ -230,11 +230,14 @@ class SimpleTool(BaseTool):
|
||||
return None
|
||||
|
||||
def get_request_files(self, request) -> list:
|
||||
"""Get files from request. Override for custom file handling."""
|
||||
"""Get absolute file paths from request. Override for custom file handling."""
|
||||
try:
|
||||
return request.files if request.files is not None else []
|
||||
files = request.absolute_file_paths
|
||||
except AttributeError:
|
||||
files = None
|
||||
if files is None:
|
||||
return []
|
||||
return files
|
||||
|
||||
def get_request_as_dict(self, request) -> dict:
|
||||
"""Convert request to dictionary. Override for custom serialization."""
|
||||
@@ -250,11 +253,10 @@ class SimpleTool(BaseTool):
|
||||
return {"prompt": self.get_request_prompt(request)}
|
||||
|
||||
def set_request_files(self, request, files: list) -> None:
|
||||
"""Set files on request. Override for custom file setting."""
|
||||
"""Set absolute file paths on request. Override for custom file setting."""
|
||||
try:
|
||||
request.files = files
|
||||
request.absolute_file_paths = files
|
||||
except AttributeError:
|
||||
# If request doesn't support file setting, ignore silently
|
||||
pass
|
||||
|
||||
def get_actually_processed_files(self) -> list:
|
||||
@@ -882,7 +884,7 @@ Please provide a thoughtful, comprehensive response:"""
|
||||
Raises:
|
||||
ValueError: If prompt is too large for MCP transport
|
||||
"""
|
||||
# Check for prompt.txt in files
|
||||
# Check for prompt.txt in provided absolute file paths
|
||||
files = self.get_request_files(request)
|
||||
if files:
|
||||
prompt_content, updated_files = self.handle_prompt_file(files)
|
||||
@@ -950,7 +952,7 @@ Please provide a thoughtful, comprehensive response:"""
|
||||
"""
|
||||
import os
|
||||
|
||||
# Check if request has 'files' attribute (used by most tools)
|
||||
# Check if request has absolute file paths attribute (legacy tools may still provide 'files')
|
||||
files = self.get_request_files(request)
|
||||
if files:
|
||||
for file_path in files:
|
||||
|
||||
@@ -227,7 +227,7 @@ class TracerTool(WorkflowTool):
|
||||
excluded_common_fields = [
|
||||
"temperature", # Tracing doesn't need temperature control
|
||||
"thinking_mode", # Tracing doesn't need thinking mode
|
||||
"files", # Tracing uses relevant_files instead
|
||||
"absolute_file_paths", # Tracing uses relevant_files instead
|
||||
]
|
||||
|
||||
return WorkflowSchemaBuilder.build_schema(
|
||||
|
||||
Reference in New Issue
Block a user