fix: configure codex with a longer timeout

refactor: param names
This commit is contained in:
Fahad
2025-10-21 10:35:44 +04:00
parent 04132f1459
commit d2773f488a
47 changed files with 232 additions and 194 deletions

View File

@@ -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():

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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"],
},
}

View File

@@ -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:

View File

@@ -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(