fix: fix multi agent paper writer langgraph script, mypy and pylint issues

This commit is contained in:
2025-10-13 18:35:37 +05:30
parent 3aa3d62389
commit fe7cb7ac47
4 changed files with 39 additions and 83 deletions
+4 -4
View File
@@ -70,8 +70,8 @@ class Agent(ABC):
def _setup_processing_pipeline(self) -> None:
"""Set up the reactive processing pipeline."""
self.input_stream.pipe(
ops.map(self._process_wrapper), # type: ignore[arg-type]
ops.flat_map(rx.from_future), # type: ignore[arg-type]
ops.map(self._process_wrapper),
ops.flat_map(rx.from_future),
).subscribe(
on_next=self.output_stream.on_next, on_error=self.output_stream.on_error
)
@@ -306,7 +306,7 @@ class StreamableAgent(Agent):
scheduler=scheduler,
)
return rx.create(subscribe) # type: ignore[no-untyped-call]
return rx.create(subscribe)
return operator
@@ -331,7 +331,7 @@ class StreamableAgent(Agent):
return await result_future
return ops.map(process_value) # type: ignore[arg-type]
return ops.map(process_value)
def filter_operator(self, condition_func: Callable[[Any], bool]) -> Any:
"""
+27 -73
View File
@@ -96,6 +96,7 @@ class ToolAgent(Agent):
Raises:
json.JSONDecodeError: If message appears to be JSON but is malformed.
"""
# pylint: disable=too-many-return-statements
message_stripped = message.strip()
# If message looks like JSON (starts with { and ends with }), it must be valid JSON
@@ -111,14 +112,11 @@ class ToolAgent(Agent):
code_block_pattern = r'```(?:json)?\s*(\{.*?\})\s*```'
match = re.search(code_block_pattern, message, re.DOTALL)
if match:
try:
parsed = json.loads(match.group(1))
if isinstance(parsed, dict): # ✅ Add type check
return parsed
return None # Not a dict
except json.JSONDecodeError:
# Code block had invalid JSON, raise error
raise
# Code block had JSON, parse it (let JSONDecodeError propagate if invalid)
parsed = json.loads(match.group(1))
if isinstance(parsed, dict):
return parsed
return None # Not a dict
# Try to find any JSON object in the message
json_pattern = r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}'
@@ -367,48 +365,6 @@ class ToolAgent(Agent):
except Exception as e:
raise ExecutionError(f"File read failed: {e}") from e
def _clean_control_markers(self, content: str) -> str:
"""
Remove control markers from content before writing to files.
This ensures that workflow control signals like [SECTION_COMPLETE],
[PAPER_COMPLETE], [WRITER SPEAKING], etc., don't end up in the output files.
Args:
content: The raw content with potential control markers
Returns:
Cleaned content without control markers
"""
import re
# List of control markers to remove
control_markers = [
r'\[SECTION_COMPLETE\]',
r'\[PAPER_COMPLETE\]',
r'\[WRITER SPEAKING\]',
r'\[RESEARCHER SPEAKING\]',
r'\[REVIEWER SPEAKING\]',
r'\[REQUIREMENTS_COMPLETE\]',
r'\[REVIEW_COMPLETE\]',
r'\[ROUTE:\w+\]',
r'\[FILE_READ_SUCCESS\]',
r'\[FILE_CONTENT_START\]',
r'\[FILE_CONTENT_END\]',
]
# Remove each marker
cleaned = content
for marker in control_markers:
cleaned = re.sub(marker, '', cleaned)
# Clean up excessive whitespace that might result from marker removal
# But preserve intentional paragraph breaks
cleaned = re.sub(r'\n\n\n+', '\n\n', cleaned)
cleaned = cleaned.strip()
return cleaned
def _validate_file_write_args(
self, filepath: str, content: str
) -> None:
@@ -435,16 +391,16 @@ class ToolAgent(Agent):
raise ExecutionError("Unsafe file path blocked in safe mode")
def _prepare_append_content(
self, filepath: str, cleaned_content: str
self, filepath: str, content: str
) -> str:
"""Prepare content for append mode with proper spacing for sections."""
# Only add spacing if content looks like a document section (starts with #)
# This prevents breaking simple append operations
is_section = cleaned_content.lstrip().startswith('#')
is_section = content.lstrip().startswith('#')
if not is_section:
# Simple append without spacing
return cleaned_content
return content
try:
with open(filepath, "r", encoding="utf-8") as f:
@@ -461,10 +417,13 @@ class ToolAgent(Agent):
except FileNotFoundError:
prefix = '' # File doesn't exist yet
return prefix + cleaned_content
return prefix + content
def _handle_insert_position(
self, filepath: str, cleaned_content: str, position: Union[None, int, Literal["start", "end"]]
self,
filepath: str,
content: str,
position: Union[None, int, Literal["start", "end"]],
) -> tuple[list[str], int]:
"""Handle insert mode positioning logic."""
try:
@@ -475,8 +434,8 @@ class ToolAgent(Agent):
# Ensure content ends with newline
formatted_content = (
cleaned_content if cleaned_content.endswith('\n')
else cleaned_content + '\n'
content if content.endswith('\n')
else content + '\n'
)
# Determine insertion position
@@ -509,10 +468,6 @@ class ToolAgent(Agent):
# Validate inputs
self._validate_file_write_args(filepath, content)
# Clean control markers from content before writing
cleaned_content = self._clean_control_markers(content)
# Check unsafe mode requirement
unsafe_mode = context and context.get("_unsafe_mode", False)
if not unsafe_mode:
@@ -526,32 +481,31 @@ class ToolAgent(Agent):
if mode == "w":
# Standard write (overwrite)
with open(filepath, "w", encoding="utf-8") as f:
f.write(cleaned_content)
return f"Successfully wrote {len(cleaned_content)} characters to {filepath}"
f.write(content)
return f"Successfully wrote {len(content)} characters to {filepath}"
elif mode == "a":
if mode == "a":
# Append mode with proper spacing
content_to_append = self._prepare_append_content(filepath, cleaned_content)
content_to_append = self._prepare_append_content(filepath, content)
with open(filepath, "a", encoding="utf-8") as f:
f.write(content_to_append)
return f"Successfully appended {len(cleaned_content)} characters to {filepath}"
return f"Successfully appended {len(content)} characters to {filepath}"
elif mode == "insert":
if mode == "insert":
# Insert mode at specified position
existing_lines, insert_location = self._handle_insert_position(
filepath, cleaned_content, position
filepath, content, position
)
with open(filepath, "w", encoding="utf-8") as f:
f.writelines(existing_lines)
return (
f"Successfully inserted {len(cleaned_content)} characters "
f"Successfully inserted {len(content)} characters "
f"at line {insert_location} in {filepath}"
)
else:
raise ExecutionError(
f"Invalid mode '{mode}'. Use 'w' (write), 'a' (append), or 'insert'."
)
raise ExecutionError(
f"Invalid mode '{mode}'. Use 'w' (write), 'a' (append), or 'insert'."
)
except Exception as e:
logger.error("File write failed for %s: %s", filepath, e)
raise ExecutionError(f"File write failed: {e}") from e
+4 -2
View File
@@ -44,6 +44,8 @@ from cleveragents.templates.registry import TemplateRegistry
from cleveragents.templates.renderer import TemplateEngine
from cleveragents.templates.renderer import TemplateRenderer
logger = logging.getLogger(__name__)
class ReactiveCleverAgentsApp: # pylint: disable=too-many-instance-attributes
"""
@@ -92,7 +94,7 @@ class ReactiveCleverAgentsApp: # pylint: disable=too-many-instance-attributes
self.loop = asyncio.get_event_loop()
self.scheduler = AsyncIOScheduler(self.loop)
self.stream_router = ReactiveStreamRouter(self.scheduler)
self.config_parser = ReactiveConfigParser() # type: ignore[no-untyped-call]
self.config_parser = ReactiveConfigParser()
self.agent_factory: Optional[AgentFactory] = None
# Initialize LangGraph bridge
@@ -414,7 +416,7 @@ class ReactiveCleverAgentsApp: # pylint: disable=too-many-instance-attributes
self._use_enhanced_registry = True
self.logger.info("Using enhanced template registry for complex templates")
else:
self.template_registry = TemplateRegistry() # type: ignore[no-untyped-call]
self.template_registry = TemplateRegistry()
# Register all templates
if self.config.templates:
@@ -18,7 +18,7 @@ echo "=========================================="
echo ""
echo "Testing complete workflow:"
echo " 1. Coordinator routes requests"
echo " 2. Researcher gathers requirements"
echo " 2. Researcher gathers information"
echo " 3. Writer creates the paper"
echo " 4. Reviewer provides feedback"
echo " 5. Writer applies suggestions"
@@ -34,8 +34,8 @@ echo ""
# Run the interactive session with incremental commands
cat << 'EOF' | timeout 240 python -m cleveragents interactive -c examples/multi_agent_paper_writer_langgraph.yaml --unsafe 2>&1 | tee /tmp/paper_writer_test.log
/graph paper_writing_workflow hello, who are you and how can you help me?
/graph paper_writing_workflow I want to write a paper about quantum computing advantages
/graph paper_writing_workflow yes, write a brief 250 word paper on that topic
/graph paper_writing_workflow I want to write a paper about quantum computing advantages. First, research the key advantages of quantum computing including speed, parallelism, and applications.
/graph paper_writing_workflow Now write a brief 250 word paper on quantum computing advantages covering: computational speed, quantum parallelism, and real-world applications. Target audience is general tech professionals.
/graph paper_writing_workflow please review the paper that was just written
/graph paper_writing_workflow apply the reviewer's suggestions and improve the paper
/graph paper_writing_workflow save the final paper to quantum_test_paper.txt
@@ -70,7 +70,7 @@ if [ -f "quantum_test_paper.txt" ]; then
echo "----------------------------------------"
head -20 quantum_test_paper.txt
echo "----------------------------------------"
# Verify content quality
if grep -qi "quantum" quantum_test_paper.txt && \
grep -qi "computing" quantum_test_paper.txt; then