🚀 Major Enhancement: Workflow-Based Tool Architecture v5.5.0 (#95)
* WIP: new workflow architecture * WIP: further improvements and cleanup * WIP: cleanup and docks, replace old tool with new * WIP: cleanup and docks, replace old tool with new * WIP: new planner implementation using workflow * WIP: precommit tool working as a workflow instead of a basic tool Support for passing False to use_assistant_model to skip external models completely and use Claude only * WIP: precommit workflow version swapped with old * WIP: codereview * WIP: replaced codereview * WIP: replaced codereview * WIP: replaced refactor * WIP: workflow for thinkdeep * WIP: ensure files get embedded correctly * WIP: thinkdeep replaced with workflow version * WIP: improved messaging when an external model's response is received * WIP: analyze tool swapped * WIP: updated tests * Extract only the content when building history * Use "relevant_files" for workflow tools only * WIP: updated tests * Extract only the content when building history * Use "relevant_files" for workflow tools only * WIP: fixed get_completion_next_steps_message missing param * Fixed tests Request for files consistently * Fixed tests Request for files consistently * Fixed tests * New testgen workflow tool Updated docs * Swap testgen workflow * Fix CI test failures by excluding API-dependent tests - Update GitHub Actions workflow to exclude simulation tests that require API keys - Fix collaboration tests to properly mock workflow tool expert analysis calls - Update test assertions to handle new workflow tool response format - Ensure unit tests run without external API dependencies in CI 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * WIP - Update tests to match new tools * WIP - Update tests to match new tools --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
4dae6e457e
commit
69a3121452
19
tools/shared/__init__.py
Normal file
19
tools/shared/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Shared infrastructure for Zen MCP tools.
|
||||
|
||||
This module contains the core base classes and utilities that are shared
|
||||
across all tool types. It provides the foundation for the tool architecture.
|
||||
"""
|
||||
|
||||
from .base_models import BaseWorkflowRequest, ConsolidatedFindings, ToolRequest, WorkflowRequest
|
||||
from .base_tool import BaseTool
|
||||
from .schema_builders import SchemaBuilder
|
||||
|
||||
__all__ = [
|
||||
"BaseTool",
|
||||
"ToolRequest",
|
||||
"BaseWorkflowRequest",
|
||||
"WorkflowRequest",
|
||||
"ConsolidatedFindings",
|
||||
"SchemaBuilder",
|
||||
]
|
||||
188
tools/shared/base_models.py
Normal file
188
tools/shared/base_models.py
Normal file
@@ -0,0 +1,188 @@
|
||||
"""
|
||||
Base models for Zen MCP tools.
|
||||
|
||||
This module contains the shared Pydantic models used across all tools,
|
||||
extracted to avoid circular imports and promote code reuse.
|
||||
|
||||
Key Models:
|
||||
- ToolRequest: Base request model for all tools
|
||||
- WorkflowRequest: Extended request model for workflow-based tools
|
||||
- ConsolidatedFindings: Model for tracking workflow progress
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Shared field descriptions to avoid duplication
|
||||
COMMON_FIELD_DESCRIPTIONS = {
|
||||
"model": (
|
||||
"Model to use. See tool's input schema for available models and their capabilities. "
|
||||
"Use 'auto' to let Claude select the best model for the task."
|
||||
),
|
||||
"temperature": (
|
||||
"Temperature for response (0.0 to 1.0). Lower values are more focused and deterministic, "
|
||||
"higher values are more creative. Tool-specific defaults apply if not specified."
|
||||
),
|
||||
"thinking_mode": (
|
||||
"Thinking depth: minimal (0.5% of model max), low (8%), medium (33%), high (67%), "
|
||||
"max (100% of model max). Higher modes enable deeper reasoning at the cost of speed."
|
||||
),
|
||||
"use_websearch": (
|
||||
"Enable web search for documentation, best practices, and current information. "
|
||||
"When enabled, the model can request Claude to perform web searches and share results back "
|
||||
"during conversations. Particularly useful for: brainstorming sessions, architectural design "
|
||||
"discussions, exploring industry best practices, working with specific frameworks/technologies, "
|
||||
"researching solutions to complex problems, or when current documentation and community insights "
|
||||
"would enhance the analysis."
|
||||
),
|
||||
"continuation_id": (
|
||||
"Thread continuation ID for multi-turn conversations. When provided, the complete conversation "
|
||||
"history is automatically embedded as context. Your response should build upon this history "
|
||||
"without repeating previous analysis or instructions. Focus on providing only new insights, "
|
||||
"additional findings, or answers to follow-up questions. Can be used across different tools."
|
||||
),
|
||||
"images": (
|
||||
"Optional image(s) for visual context. Accepts absolute file paths or "
|
||||
"base64 data URLs. Only provide when user explicitly mentions images. "
|
||||
"When including images, please describe what you believe each image contains "
|
||||
"to aid with contextual understanding. Useful for UI discussions, diagrams, "
|
||||
"visual problems, error screens, architecture mockups, and visual analysis tasks."
|
||||
),
|
||||
"files": ("Optional files for context (must be FULL absolute paths to real files / folders - DO NOT SHORTEN)"),
|
||||
}
|
||||
|
||||
# Workflow-specific field descriptions
|
||||
WORKFLOW_FIELD_DESCRIPTIONS = {
|
||||
"step": "Current work step content and findings from your overall work",
|
||||
"step_number": "Current step number in the work sequence (starts at 1)",
|
||||
"total_steps": "Estimated total steps needed to complete the work",
|
||||
"next_step_required": "Whether another work step is needed after this one",
|
||||
"findings": "Important findings, evidence and insights discovered in this step of the work",
|
||||
"files_checked": "List of files examined during this work step",
|
||||
"relevant_files": "Files identified as relevant to the issue/goal",
|
||||
"relevant_context": "Methods/functions identified as involved in the issue",
|
||||
"issues_found": "Issues identified with severity levels during work",
|
||||
"confidence": "Confidence level in findings: exploring, low, medium, high, certain",
|
||||
"hypothesis": "Current theory about the issue/goal based on work",
|
||||
"backtrack_from_step": "Step number to backtrack from if work needs revision",
|
||||
"use_assistant_model": (
|
||||
"Whether to use assistant model for expert analysis after completing the workflow steps. "
|
||||
"Set to False to skip expert analysis and rely solely on Claude's investigation. "
|
||||
"Defaults to True for comprehensive validation."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class ToolRequest(BaseModel):
|
||||
"""
|
||||
Base request model for all Zen MCP tools.
|
||||
|
||||
This model defines common fields that all tools accept, including
|
||||
model selection, temperature control, and conversation threading.
|
||||
Tool-specific request models should inherit from this class.
|
||||
"""
|
||||
|
||||
# Model configuration
|
||||
model: Optional[str] = Field(None, description=COMMON_FIELD_DESCRIPTIONS["model"])
|
||||
temperature: Optional[float] = Field(None, ge=0.0, le=1.0, description=COMMON_FIELD_DESCRIPTIONS["temperature"])
|
||||
thinking_mode: Optional[str] = Field(None, description=COMMON_FIELD_DESCRIPTIONS["thinking_mode"])
|
||||
|
||||
# Features
|
||||
use_websearch: Optional[bool] = Field(True, description=COMMON_FIELD_DESCRIPTIONS["use_websearch"])
|
||||
|
||||
# Conversation support
|
||||
continuation_id: Optional[str] = Field(None, description=COMMON_FIELD_DESCRIPTIONS["continuation_id"])
|
||||
|
||||
# Visual context
|
||||
images: Optional[list[str]] = Field(None, description=COMMON_FIELD_DESCRIPTIONS["images"])
|
||||
|
||||
|
||||
class BaseWorkflowRequest(ToolRequest):
|
||||
"""
|
||||
Minimal base request model for workflow tools.
|
||||
|
||||
This provides only the essential fields that ALL workflow tools need,
|
||||
allowing for maximum flexibility in tool-specific implementations.
|
||||
"""
|
||||
|
||||
# Core workflow fields that ALL workflow tools need
|
||||
step: str = Field(..., description=WORKFLOW_FIELD_DESCRIPTIONS["step"])
|
||||
step_number: int = Field(..., ge=1, description=WORKFLOW_FIELD_DESCRIPTIONS["step_number"])
|
||||
total_steps: int = Field(..., ge=1, description=WORKFLOW_FIELD_DESCRIPTIONS["total_steps"])
|
||||
next_step_required: bool = Field(..., description=WORKFLOW_FIELD_DESCRIPTIONS["next_step_required"])
|
||||
|
||||
|
||||
class WorkflowRequest(BaseWorkflowRequest):
|
||||
"""
|
||||
Extended request model for workflow-based tools.
|
||||
|
||||
This model extends ToolRequest with fields specific to the workflow
|
||||
pattern, where tools perform multi-step work with forced pauses between steps.
|
||||
|
||||
Used by: debug, precommit, codereview, refactor, thinkdeep, analyze
|
||||
"""
|
||||
|
||||
# Required workflow fields
|
||||
step: str = Field(..., description=WORKFLOW_FIELD_DESCRIPTIONS["step"])
|
||||
step_number: int = Field(..., ge=1, description=WORKFLOW_FIELD_DESCRIPTIONS["step_number"])
|
||||
total_steps: int = Field(..., ge=1, description=WORKFLOW_FIELD_DESCRIPTIONS["total_steps"])
|
||||
next_step_required: bool = Field(..., description=WORKFLOW_FIELD_DESCRIPTIONS["next_step_required"])
|
||||
|
||||
# Work tracking fields
|
||||
findings: str = Field(..., description=WORKFLOW_FIELD_DESCRIPTIONS["findings"])
|
||||
files_checked: list[str] = Field(default_factory=list, description=WORKFLOW_FIELD_DESCRIPTIONS["files_checked"])
|
||||
relevant_files: list[str] = Field(default_factory=list, description=WORKFLOW_FIELD_DESCRIPTIONS["relevant_files"])
|
||||
relevant_context: list[str] = Field(
|
||||
default_factory=list, description=WORKFLOW_FIELD_DESCRIPTIONS["relevant_context"]
|
||||
)
|
||||
issues_found: list[dict] = Field(default_factory=list, description=WORKFLOW_FIELD_DESCRIPTIONS["issues_found"])
|
||||
confidence: str = Field("low", description=WORKFLOW_FIELD_DESCRIPTIONS["confidence"])
|
||||
|
||||
# Optional workflow fields
|
||||
hypothesis: Optional[str] = Field(None, description=WORKFLOW_FIELD_DESCRIPTIONS["hypothesis"])
|
||||
backtrack_from_step: Optional[int] = Field(
|
||||
None, ge=1, description=WORKFLOW_FIELD_DESCRIPTIONS["backtrack_from_step"]
|
||||
)
|
||||
use_assistant_model: Optional[bool] = Field(True, description=WORKFLOW_FIELD_DESCRIPTIONS["use_assistant_model"])
|
||||
|
||||
@field_validator("files_checked", "relevant_files", "relevant_context", mode="before")
|
||||
@classmethod
|
||||
def convert_string_to_list(cls, v):
|
||||
"""Convert string inputs to empty lists to handle malformed inputs gracefully."""
|
||||
if isinstance(v, str):
|
||||
logger.warning(f"Field received string '{v}' instead of list, converting to empty list")
|
||||
return []
|
||||
return v
|
||||
|
||||
|
||||
class ConsolidatedFindings(BaseModel):
|
||||
"""
|
||||
Model for tracking consolidated findings across workflow steps.
|
||||
|
||||
This model accumulates findings, files, methods, and issues
|
||||
discovered during multi-step work. It's used by
|
||||
BaseWorkflowMixin to track progress across workflow steps.
|
||||
"""
|
||||
|
||||
files_checked: set[str] = Field(default_factory=set, description="All files examined across all steps")
|
||||
relevant_files: set[str] = Field(
|
||||
default_factory=set,
|
||||
description="A subset of files_checked that have been identified as relevant for the work at hand",
|
||||
)
|
||||
relevant_context: set[str] = Field(
|
||||
default_factory=set, description="All methods/functions identified during overall work being performed"
|
||||
)
|
||||
findings: list[str] = Field(default_factory=list, description="Chronological list of findings from each work step")
|
||||
hypotheses: list[dict] = Field(default_factory=list, description="Evolution of hypotheses across work steps")
|
||||
issues_found: list[dict] = Field(default_factory=list, description="All issues found with severity levels")
|
||||
images: list[str] = Field(default_factory=list, description="Images collected during overall work")
|
||||
confidence: str = Field("low", description="Latest confidence level from work steps")
|
||||
|
||||
|
||||
# Tool-specific field descriptions are now declared in each tool file
|
||||
# This keeps concerns separated and makes each tool self-contained
|
||||
1200
tools/shared/base_tool.py
Normal file
1200
tools/shared/base_tool.py
Normal file
File diff suppressed because it is too large
Load Diff
163
tools/shared/schema_builders.py
Normal file
163
tools/shared/schema_builders.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
Core schema building functionality for Zen MCP tools.
|
||||
|
||||
This module provides base schema generation functionality for simple tools.
|
||||
Workflow-specific schema building is located in workflow/schema_builders.py
|
||||
to maintain proper separation of concerns.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .base_models import COMMON_FIELD_DESCRIPTIONS
|
||||
|
||||
|
||||
class SchemaBuilder:
|
||||
"""
|
||||
Base schema builder for simple MCP tools.
|
||||
|
||||
This class provides static methods to build consistent schemas for simple tools.
|
||||
Workflow tools use WorkflowSchemaBuilder in workflow/schema_builders.py.
|
||||
"""
|
||||
|
||||
# Common field schemas that can be reused across all tool types
|
||||
COMMON_FIELD_SCHEMAS = {
|
||||
"temperature": {
|
||||
"type": "number",
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["temperature"],
|
||||
"minimum": 0.0,
|
||||
"maximum": 1.0,
|
||||
},
|
||||
"thinking_mode": {
|
||||
"type": "string",
|
||||
"enum": ["minimal", "low", "medium", "high", "max"],
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["thinking_mode"],
|
||||
},
|
||||
"use_websearch": {
|
||||
"type": "boolean",
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["use_websearch"],
|
||||
"default": True,
|
||||
},
|
||||
"continuation_id": {
|
||||
"type": "string",
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["continuation_id"],
|
||||
},
|
||||
"images": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["images"],
|
||||
},
|
||||
}
|
||||
|
||||
# Simple tool-specific field schemas (workflow tools use relevant_files instead)
|
||||
SIMPLE_FIELD_SCHEMAS = {
|
||||
"files": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": COMMON_FIELD_DESCRIPTIONS["files"],
|
||||
},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def build_schema(
|
||||
tool_specific_fields: dict[str, dict[str, Any]] = None,
|
||||
required_fields: list[str] = None,
|
||||
model_field_schema: dict[str, Any] = None,
|
||||
auto_mode: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Build complete schema for simple tools.
|
||||
|
||||
Args:
|
||||
tool_specific_fields: Additional fields specific to the tool
|
||||
required_fields: List of required field names
|
||||
model_field_schema: Schema for the model field
|
||||
auto_mode: Whether the tool is in auto mode (affects model requirement)
|
||||
|
||||
Returns:
|
||||
Complete JSON schema for the tool
|
||||
"""
|
||||
properties = {}
|
||||
|
||||
# Add common fields (temperature, thinking_mode, etc.)
|
||||
properties.update(SchemaBuilder.COMMON_FIELD_SCHEMAS)
|
||||
|
||||
# Add simple tool-specific fields (files field for simple tools)
|
||||
properties.update(SchemaBuilder.SIMPLE_FIELD_SCHEMAS)
|
||||
|
||||
# Add model field if provided
|
||||
if model_field_schema:
|
||||
properties["model"] = model_field_schema
|
||||
|
||||
# Add tool-specific fields if provided
|
||||
if tool_specific_fields:
|
||||
properties.update(tool_specific_fields)
|
||||
|
||||
# Build required fields list
|
||||
required = required_fields or []
|
||||
if auto_mode and "model" not in required:
|
||||
required.append("model")
|
||||
|
||||
# Build the complete schema
|
||||
schema = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": properties,
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
if required:
|
||||
schema["required"] = required
|
||||
|
||||
return schema
|
||||
|
||||
@staticmethod
|
||||
def get_common_fields() -> dict[str, dict[str, Any]]:
|
||||
"""Get the standard field schemas for simple tools."""
|
||||
return SchemaBuilder.COMMON_FIELD_SCHEMAS.copy()
|
||||
|
||||
@staticmethod
|
||||
def create_field_schema(
|
||||
field_type: str,
|
||||
description: str,
|
||||
enum_values: list[str] = None,
|
||||
minimum: float = None,
|
||||
maximum: float = None,
|
||||
items_type: str = None,
|
||||
default: Any = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Helper method to create field schemas with common patterns.
|
||||
|
||||
Args:
|
||||
field_type: JSON schema type ("string", "number", "array", etc.)
|
||||
description: Human-readable description of the field
|
||||
enum_values: For enum fields, list of allowed values
|
||||
minimum: For numeric fields, minimum value
|
||||
maximum: For numeric fields, maximum value
|
||||
items_type: For array fields, type of array items
|
||||
default: Default value for the field
|
||||
|
||||
Returns:
|
||||
JSON schema object for the field
|
||||
"""
|
||||
schema = {
|
||||
"type": field_type,
|
||||
"description": description,
|
||||
}
|
||||
|
||||
if enum_values:
|
||||
schema["enum"] = enum_values
|
||||
|
||||
if minimum is not None:
|
||||
schema["minimum"] = minimum
|
||||
|
||||
if maximum is not None:
|
||||
schema["maximum"] = maximum
|
||||
|
||||
if items_type and field_type == "array":
|
||||
schema["items"] = {"type": items_type}
|
||||
|
||||
if default is not None:
|
||||
schema["default"] = default
|
||||
|
||||
return schema
|
||||
Reference in New Issue
Block a user