-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Refactor AI News Generator to use CrewAI Flows #175
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
Refactor AI News Generator to use CrewAI Flows #175
Conversation
- Implement NewsGeneratorFlow class with @start and @listen decorators - Add event-driven workflow with research → content writing phases - Replace simple Crew usage with modular Flow architecture - Add comprehensive state management for data passing between phases - Create main.py with Flow implementation and CLI interface - Update Streamlit app.py to use new Flow-based architecture - Add requirements.txt with all necessary dependencies - Include test_flow.py for testing Flow implementation - Update README with detailed Flow usage instructions and examples - Add .gitignore for Python and generated files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
WalkthroughThis update introduces a major refactor of the AI news generator project, transitioning from a monolithic CrewAI agent/task setup to a modular, event-driven CrewAI Flows architecture. It adds new modules, documentation, environment configuration, and tests, while updating the app's UI and internal logic to utilize the new flow-based design for research and content generation. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant StreamlitApp
participant NewsGeneratorFlow
participant ResearchAgent
participant WriterAgent
User->>StreamlitApp: Enter topic & temperature, click "Generate"
StreamlitApp->>NewsGeneratorFlow: generate_news_content(topic, temperature)
NewsGeneratorFlow->>ResearchAgent: research_topic()
ResearchAgent->>ResearchAgent: Use search tool, gather info
ResearchAgent-->>NewsGeneratorFlow: Return research results
NewsGeneratorFlow->>WriterAgent: write_content(research_results)
WriterAgent-->>NewsGeneratorFlow: Return final article
NewsGeneratorFlow-->>StreamlitApp: Return research + article
StreamlitApp-->>User: Display research & article results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Possibly related PRs
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 7
🧹 Nitpick comments (4)
ai_news_generator/requirements.txt (1)
1-5
: Consider using more specific version constraints for production stability.Using
>=
without upper bounds can lead to unexpected breaking changes when packages release new major versions. Consider pinning to specific major versions or using compatible release clauses.-crewai>=0.80.0 -crewai-tools>=0.15.0 -streamlit>=1.30.0 -python-dotenv>=1.0.0 -pydantic>=2.0.0 +crewai~=0.80.0 +crewai-tools~=0.15.0 +streamlit~=1.30.0 +python-dotenv~=1.0.0 +pydantic~=2.0.0The
~=
operator allows patch-level updates but prevents potentially breaking minor/major version updates.ai_news_generator/test_flow.py (1)
12-95
: Consider using a proper testing framework.While the current tests work, consider migrating to
pytest
orunittest
for better test organization, fixtures, and reporting.Example with pytest:
import pytest from main import NewsGeneratorFlow, NewsGeneratorState class TestNewsGeneratorFlow: def test_flow_structure(self): """Test the basic flow structure and initialization""" flow = NewsGeneratorFlow(topic="Test Topic", temperature=0.5) assert flow.topic == "Test Topic" assert flow.temperature == 0.5 assert flow.research_results == "" assert flow.final_content == "" def test_state_model(self): """Test the Pydantic state model""" state = NewsGeneratorState( topic="AI Technology", research_results="Mock research data", final_content="Mock article content", temperature=0.8 ) assert state.topic == "AI Technology" assert state.research_results == "Mock research data" assert state.final_content == "Mock article content" assert state.temperature == 0.8 def test_flow_methods_exist(self): """Test that required flow methods exist""" flow = NewsGeneratorFlow(topic="Test Topic") assert hasattr(flow, 'research_topic') assert hasattr(flow, 'write_content') assert callable(flow.research_topic) assert callable(flow.write_content)Then run with:
pytest test_flow.py -v
ai_news_generator/app.py (1)
58-60
: Consider removing unnecessary wrapper function.The
generate_content
function is a simple pass-through that doesn't add value.-def generate_content(topic, temperature=0.7): - """Generate content using the new CrewAI Flow implementation""" - return generate_news_content(topic, temperature) # Main content area if generate_button: if not topic.strip(): st.warning("Please enter a topic to generate content.") else: with st.spinner('🚀 Executing CrewAI Flow... This may take a moment.'): try: # Generate content using CrewAI Flow - result = generate_content(topic, temperature) + result = generate_news_content(topic, temperature)ai_news_generator/README.md (1)
119-127
: Add a language identifier to the fenced code block (MD040)
markdownlint
flags this section because the fenced block lacks a language spec. Adding one (e.g.,text
) improves syntax highlighting and keeps the docs lint-clean.-``` +```text ai_news_generator/ ├── main.py # CrewAI Flow implementation ├── app.py # Streamlit web interface ├── test_flow.py # Test suite for Flow ├── requirements.txt # Python dependencies ├── README.md # This file └── .env # Environment variables (create this)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
ai_news_generator/.gitignore
(1 hunks)ai_news_generator/README.md
(2 hunks)ai_news_generator/app.py
(3 hunks)ai_news_generator/main.py
(1 hunks)ai_news_generator/requirements.txt
(1 hunks)ai_news_generator/test_flow.py
(1 hunks)
🧰 Additional context used
🪛 Ruff (0.12.2)
ai_news_generator/test_flow.py
9-9: os
imported but unused
Remove unused import: os
(F401)
ai_news_generator/main.py
1-1: os
imported but unused
Remove unused import: os
(F401)
🪛 markdownlint-cli2 (0.17.2)
ai_news_generator/README.md
119-119: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 LanguageTool
ai_news_generator/README.md
[style] ~158-~158: Using many exclamation marks might seem excessive (in this case: 5 exclamation marks for a text that’s 3045 characters long)
Context: ... Contribution Contributions are welcome! The new CrewAI Flow architecture makes ...
(EN_EXCESSIVE_EXCLAMATION)
🔇 Additional comments (5)
ai_news_generator/.gitignore (1)
1-47
: LGTM! Comprehensive .gitignore configuration.The .gitignore file properly covers Python artifacts, environment files, IDE configurations, generated outputs, and OS-specific files. Good attention to security by excluding environment files.
ai_news_generator/main.py (2)
192-214
: LGTM! Well-structured helper function.The function provides a clean interface for flow execution with comprehensive result return.
216-250
: LGTM! Good example implementation.The main block provides a clear example of flow usage with proper error handling and file output.
ai_news_generator/app.py (2)
63-106
: LGTM! Well-designed UI with good user experience.The implementation provides:
- Clear input validation
- Tabbed results for better organization
- Download functionality for both research and final content
- Proper error handling with helpful messages
107-123
: LGTM! Informative footer and documentation.Good addition of flow execution details to help users understand the underlying architecture.
@@ -0,0 +1,250 @@ | |||
import os |
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.
Remove unused import.
The os
module is imported but never used in the code.
-import os
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import os |
🧰 Tools
🪛 Ruff (0.12.2)
1-1: os
imported but unused
Remove unused import: os
(F401)
🤖 Prompt for AI Agents
In ai_news_generator/main.py at line 1, the 'os' module is imported but not used
anywhere in the code. Remove the import statement for 'os' to clean up the code
and avoid unnecessary imports.
def __init__(self, topic: str, temperature: float = 0.7): | ||
"""Initialize the flow with topic and configuration""" | ||
super().__init__() | ||
|
||
# Store configuration as instance variables | ||
self.topic = topic | ||
self.temperature = temperature | ||
self.research_results = "" | ||
self.final_content = "" | ||
|
||
# Initialize LLM | ||
self.llm = LLM( | ||
model="command-r", | ||
temperature=temperature | ||
) | ||
|
||
# Initialize search tool | ||
self.search_tool = SerperDevTool(n_results=10) | ||
|
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.
🛠️ Refactor suggestion
Improve initialization with validation and proper state management.
The current implementation has a few issues:
- Duplicates state storage (instance variables vs state model)
- No validation for temperature range
- No error handling for service initialization
def __init__(self, topic: str, temperature: float = 0.7):
"""Initialize the flow with topic and configuration"""
+ # Validate temperature
+ if not 0.0 <= temperature <= 1.0:
+ raise ValueError("Temperature must be between 0.0 and 1.0")
+
super().__init__()
- # Store configuration as instance variables
- self.topic = topic
- self.temperature = temperature
- self.research_results = ""
- self.final_content = ""
+ # Initialize state properly
+ self.state = NewsGeneratorState(
+ topic=topic,
+ temperature=temperature
+ )
- # Initialize LLM
- self.llm = LLM(
- model="command-r",
- temperature=temperature
- )
-
- # Initialize search tool
- self.search_tool = SerperDevTool(n_results=10)
+ try:
+ # Initialize LLM
+ self.llm = LLM(
+ model="command-r",
+ temperature=temperature
+ )
+
+ # Initialize search tool
+ self.search_tool = SerperDevTool(n_results=10)
+ except Exception as e:
+ raise RuntimeError(f"Failed to initialize services: {e}")
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def __init__(self, topic: str, temperature: float = 0.7): | |
"""Initialize the flow with topic and configuration""" | |
super().__init__() | |
# Store configuration as instance variables | |
self.topic = topic | |
self.temperature = temperature | |
self.research_results = "" | |
self.final_content = "" | |
# Initialize LLM | |
self.llm = LLM( | |
model="command-r", | |
temperature=temperature | |
) | |
# Initialize search tool | |
self.search_tool = SerperDevTool(n_results=10) | |
def __init__(self, topic: str, temperature: float = 0.7): | |
"""Initialize the flow with topic and configuration""" | |
# Validate temperature | |
if not 0.0 <= temperature <= 1.0: | |
raise ValueError("Temperature must be between 0.0 and 1.0") | |
super().__init__() | |
# Initialize state properly | |
self.state = NewsGeneratorState( | |
topic=topic, | |
temperature=temperature | |
) | |
try: | |
# Initialize LLM | |
self.llm = LLM( | |
model="command-r", | |
temperature=temperature | |
) | |
# Initialize search tool | |
self.search_tool = SerperDevTool(n_results=10) | |
except Exception as e: | |
raise RuntimeError(f"Failed to initialize services: {e}") |
🤖 Prompt for AI Agents
In ai_news_generator/main.py around lines 28 to 46, improve the __init__ method
by removing duplicate state storage and consolidating configuration into a
single source of truth, add validation to ensure the temperature parameter is
within an acceptable range (e.g., 0 to 1), and wrap the initialization of the
LLM and search tool in try-except blocks to handle potential errors gracefully,
logging or raising informative exceptions as needed.
@start() | ||
def research_topic(self) -> str: | ||
""" | ||
Phase 1: Research the given topic comprehensively | ||
|
||
Returns: | ||
str: Research results | ||
""" | ||
print(f"🔍 Starting research phase for topic: {self.topic}") | ||
|
||
# Create Senior Research Analyst agent | ||
senior_research_analyst = Agent( | ||
role="Senior Research Analyst", | ||
goal=f"Research, analyze, and synthesize comprehensive information on {self.topic} from reliable web sources", | ||
backstory="You're an expert research analyst with advanced web research skills. " | ||
"You excel at finding, analyzing, and synthesizing information from " | ||
"across the internet using search tools. You're skilled at " | ||
"distinguishing reliable sources from unreliable ones, " | ||
"fact-checking, cross-referencing information, and " | ||
"identifying key patterns and insights. You provide " | ||
"well-organized research briefs with proper citations " | ||
"and source verification. Your analysis includes both " | ||
"raw data and interpreted insights, making complex " | ||
"information accessible and actionable.", | ||
allow_delegation=False, | ||
verbose=True, | ||
tools=[self.search_tool], | ||
llm=self.llm | ||
) | ||
|
||
# Research Task | ||
research_task = Task( | ||
description=(f""" | ||
1. Conduct comprehensive research on {self.topic} including: | ||
- Recent developments and news | ||
- Key industry trends and innovations | ||
- Expert opinions and analyses | ||
- Statistical data and market insights | ||
2. Evaluate source credibility and fact-check all information | ||
3. Organize findings into a structured research brief | ||
4. Include all relevant citations and sources | ||
"""), | ||
expected_output="""A detailed research report containing: | ||
- Executive summary of key findings | ||
- Comprehensive analysis of current trends and developments | ||
- List of verified facts and statistics | ||
- All citations and links to original sources | ||
- Clear categorization of main themes and patterns | ||
Please format with clear sections and bullet points for easy reference.""", | ||
agent=senior_research_analyst | ||
) | ||
|
||
# Create and execute research crew | ||
research_crew = Crew( | ||
agents=[senior_research_analyst], | ||
tasks=[research_task], | ||
verbose=True | ||
) | ||
|
||
research_results = research_crew.kickoff(inputs={"topic": self.topic}) | ||
|
||
# Store research results | ||
self.research_results = str(research_results) | ||
|
||
print("✅ Research phase completed") | ||
return self.research_results | ||
|
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.
🛠️ Refactor suggestion
Use state model consistently and add error handling.
The method should use the state model for consistency and include error handling for the crew execution.
@start()
def research_topic(self) -> str:
"""
Phase 1: Research the given topic comprehensively
Returns:
str: Research results
"""
- print(f"🔍 Starting research phase for topic: {self.topic}")
+ print(f"🔍 Starting research phase for topic: {self.state.topic}")
# Create Senior Research Analyst agent
senior_research_analyst = Agent(
role="Senior Research Analyst",
- goal=f"Research, analyze, and synthesize comprehensive information on {self.topic} from reliable web sources",
+ goal=f"Research, analyze, and synthesize comprehensive information on {self.state.topic} from reliable web sources",
# ... rest of agent config ...
)
# Research Task
research_task = Task(
description=(f"""
- 1. Conduct comprehensive research on {self.topic} including:
+ 1. Conduct comprehensive research on {self.state.topic} including:
# ... rest of description ...
"""),
# ... rest of task config ...
)
# Create and execute research crew
research_crew = Crew(
agents=[senior_research_analyst],
tasks=[research_task],
verbose=True
)
- research_results = research_crew.kickoff(inputs={"topic": self.topic})
+ try:
+ research_results = research_crew.kickoff(inputs={"topic": self.state.topic})
+ except Exception as e:
+ print(f"❌ Research phase failed: {e}")
+ raise
# Store research results
- self.research_results = str(research_results)
+ self.state.research_results = str(research_results)
print("✅ Research phase completed")
- return self.research_results
+ return self.state.research_results
🤖 Prompt for AI Agents
In ai_news_generator/main.py from lines 47 to 113, the research_topic method
currently does not use the state model and lacks error handling for the crew
execution. Refactor the method to update and use the state model consistently
for managing research results and status. Add try-except blocks around the
research_crew.kickoff call to catch and handle any exceptions, logging errors
appropriately and updating the state to reflect failure if needed.
@listen(research_topic) | ||
def write_content(self, research_results: str) -> str: | ||
""" | ||
Phase 2: Transform research into engaging blog content | ||
|
||
Args: | ||
research_results: Output from research_topic method | ||
|
||
Returns: | ||
str: Final blog content | ||
""" | ||
print("✍️ Starting content writing phase") | ||
|
||
# Create Content Writer agent | ||
content_writer = Agent( | ||
role="Content Writer", | ||
goal="Transform research findings into engaging blog posts while maintaining accuracy", | ||
backstory="You're a skilled content writer specialized in creating " | ||
"engaging, accessible content from technical research. " | ||
"You work closely with the Senior Research Analyst and excel at maintaining the perfect " | ||
"balance between informative and entertaining writing, " | ||
"while ensuring all facts and citations from the research " | ||
"are properly incorporated. You have a talent for making " | ||
"complex topics approachable without oversimplifying them.", | ||
allow_delegation=False, | ||
verbose=True, | ||
llm=self.llm | ||
) | ||
|
||
# Writing Task | ||
writing_task = Task( | ||
description=(f""" | ||
Using the research brief provided, create an engaging blog post about {self.topic} that: | ||
1. Transforms technical information into accessible content | ||
2. Maintains all factual accuracy and citations from the research | ||
3. Includes: | ||
- Attention-grabbing introduction | ||
- Well-structured body sections with clear headings | ||
- Compelling conclusion | ||
4. Preserves all source citations in [Source: URL] format | ||
5. Includes a References section at the end | ||
"""), | ||
expected_output="""A polished blog post in markdown format that: | ||
- Engages readers while maintaining accuracy | ||
- Contains properly structured sections | ||
- Includes inline citations hyperlinked to the original source url | ||
- Presents information in an accessible yet informative way | ||
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", | ||
agent=content_writer, | ||
context=[research_results] if isinstance(research_results, str) else [] | ||
) | ||
|
||
# Create and execute writing crew | ||
writing_crew = Crew( | ||
agents=[content_writer], | ||
tasks=[writing_task], | ||
verbose=True | ||
) | ||
|
||
final_content = writing_crew.kickoff(inputs={ | ||
"topic": self.topic, | ||
"research_results": research_results | ||
}) | ||
|
||
# Store final content | ||
self.final_content = str(final_content) | ||
|
||
print("✅ Content writing phase completed") | ||
return self.final_content | ||
|
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.
🛠️ Refactor suggestion
Improve state management and error handling in write_content.
Similar to research_topic, this method should use state consistently and handle errors.
@listen(research_topic)
def write_content(self, research_results: str) -> str:
"""
Phase 2: Transform research into engaging blog content
Args:
research_results: Output from research_topic method
Returns:
str: Final blog content
"""
print("✍️ Starting content writing phase")
# Create Content Writer agent
content_writer = Agent(
role="Content Writer",
goal="Transform research findings into engaging blog posts while maintaining accuracy",
backstory="...",
allow_delegation=False,
verbose=True,
llm=self.llm
)
# Writing Task
writing_task = Task(
description=(f"""
- Using the research brief provided, create an engaging blog post about {self.topic} that:
+ Using the research brief provided, create an engaging blog post about {self.state.topic} that:
# ... rest of description ...
"""),
# ... rest of task config ...
)
# Create and execute writing crew
writing_crew = Crew(
agents=[content_writer],
tasks=[writing_task],
verbose=True
)
- final_content = writing_crew.kickoff(inputs={
- "topic": self.topic,
- "research_results": research_results
- })
+ try:
+ final_content = writing_crew.kickoff(inputs={
+ "topic": self.state.topic,
+ "research_results": research_results
+ })
+ except Exception as e:
+ print(f"❌ Content writing phase failed: {e}")
+ raise
# Store final content
- self.final_content = str(final_content)
+ self.state.final_content = str(final_content)
print("✅ Content writing phase completed")
- return self.final_content
+ return self.state.final_content
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
@listen(research_topic) | |
def write_content(self, research_results: str) -> str: | |
""" | |
Phase 2: Transform research into engaging blog content | |
Args: | |
research_results: Output from research_topic method | |
Returns: | |
str: Final blog content | |
""" | |
print("✍️ Starting content writing phase") | |
# Create Content Writer agent | |
content_writer = Agent( | |
role="Content Writer", | |
goal="Transform research findings into engaging blog posts while maintaining accuracy", | |
backstory="You're a skilled content writer specialized in creating " | |
"engaging, accessible content from technical research. " | |
"You work closely with the Senior Research Analyst and excel at maintaining the perfect " | |
"balance between informative and entertaining writing, " | |
"while ensuring all facts and citations from the research " | |
"are properly incorporated. You have a talent for making " | |
"complex topics approachable without oversimplifying them.", | |
allow_delegation=False, | |
verbose=True, | |
llm=self.llm | |
) | |
# Writing Task | |
writing_task = Task( | |
description=(f""" | |
Using the research brief provided, create an engaging blog post about {self.topic} that: | |
1. Transforms technical information into accessible content | |
2. Maintains all factual accuracy and citations from the research | |
3. Includes: | |
- Attention-grabbing introduction | |
- Well-structured body sections with clear headings | |
- Compelling conclusion | |
4. Preserves all source citations in [Source: URL] format | |
5. Includes a References section at the end | |
"""), | |
expected_output="""A polished blog post in markdown format that: | |
- Engages readers while maintaining accuracy | |
- Contains properly structured sections | |
- Includes inline citations hyperlinked to the original source url | |
- Presents information in an accessible yet informative way | |
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", | |
agent=content_writer, | |
context=[research_results] if isinstance(research_results, str) else [] | |
) | |
# Create and execute writing crew | |
writing_crew = Crew( | |
agents=[content_writer], | |
tasks=[writing_task], | |
verbose=True | |
) | |
final_content = writing_crew.kickoff(inputs={ | |
"topic": self.topic, | |
"research_results": research_results | |
}) | |
# Store final content | |
self.final_content = str(final_content) | |
print("✅ Content writing phase completed") | |
return self.final_content | |
@listen(research_topic) | |
def write_content(self, research_results: str) -> str: | |
""" | |
Phase 2: Transform research into engaging blog content | |
Args: | |
research_results: Output from research_topic method | |
Returns: | |
str: Final blog content | |
""" | |
print("✍️ Starting content writing phase") | |
# Create Content Writer agent | |
content_writer = Agent( | |
role="Content Writer", | |
goal="Transform research findings into engaging blog posts while maintaining accuracy", | |
backstory="You're a skilled content writer specialized in creating " | |
"engaging, accessible content from technical research. " | |
"You work closely with the Senior Research Analyst and excel at maintaining the perfect " | |
"balance between informative and entertaining writing, " | |
"while ensuring all facts and citations from the research " | |
"are properly incorporated. You have a talent for making " | |
"complex topics approachable without oversimplifying them.", | |
allow_delegation=False, | |
verbose=True, | |
llm=self.llm | |
) | |
# Writing Task | |
writing_task = Task( | |
description=(f""" | |
Using the research brief provided, create an engaging blog post about {self.state.topic} that: | |
1. Transforms technical information into accessible content | |
2. Maintains all factual accuracy and citations from the research | |
3. Includes: | |
- Attention-grabbing introduction | |
- Well-structured body sections with clear headings | |
- Compelling conclusion | |
4. Preserves all source citations in [Source: URL] format | |
5. Includes a References section at the end | |
"""), | |
expected_output="""A polished blog post in markdown format that: | |
- Engages readers while maintaining accuracy | |
- Contains properly structured sections | |
- Includes inline citations hyperlinked to the original source url | |
- Presents information in an accessible yet informative way | |
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", | |
agent=content_writer, | |
context=[research_results] if isinstance(research_results, str) else [] | |
) | |
# Create and execute writing crew | |
writing_crew = Crew( | |
agents=[content_writer], | |
tasks=[writing_task], | |
verbose=True | |
) | |
try: | |
final_content = writing_crew.kickoff(inputs={ | |
"topic": self.state.topic, | |
"research_results": research_results | |
}) | |
except Exception as e: | |
print(f"❌ Content writing phase failed: {e}") | |
raise | |
# Store final content | |
self.state.final_content = str(final_content) | |
print("✅ Content writing phase completed") | |
return self.state.final_content |
🤖 Prompt for AI Agents
In ai_news_generator/main.py between lines 114 and 183, the write_content method
lacks consistent state management and error handling similar to research_topic.
Refactor the method to update the object's state appropriately during the
content writing phase, such as setting a state attribute before and after the
task execution. Add try-except blocks around the main logic to catch and handle
exceptions gracefully, logging errors and preventing crashes. Ensure the method
updates the state to reflect success or failure and returns or raises errors as
needed.
- Presents information in an accessible yet informative way | ||
- Follows proper markdown formatting, use H1 for the title and H3 for the sub-sections""", | ||
agent=content_writer, | ||
context=[research_results] if isinstance(research_results, str) else [] |
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.
Remove unnecessary type check.
The parameter research_results
is already typed as str
, making the isinstance check redundant.
- context=[research_results] if isinstance(research_results, str) else []
+ context=[research_results]
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
context=[research_results] if isinstance(research_results, str) else [] | |
context=[research_results] |
🤖 Prompt for AI Agents
In ai_news_generator/main.py at line 163, remove the isinstance check for
research_results since it is already typed as a string. Replace the conditional
expression with directly assigning context to a list containing
research_results.
def get_final_content(self) -> str: | ||
"""Get the final generated content""" | ||
return self.final_content | ||
|
||
def get_research_results(self) -> str: | ||
"""Get the research results""" | ||
return self.research_results |
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.
🛠️ Refactor suggestion
Update getter methods to use state model.
For consistency, getter methods should return values from the state model.
def get_final_content(self) -> str:
"""Get the final generated content"""
- return self.final_content
+ return self.state.final_content
def get_research_results(self) -> str:
"""Get the research results"""
- return self.research_results
+ return self.state.research_results
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def get_final_content(self) -> str: | |
"""Get the final generated content""" | |
return self.final_content | |
def get_research_results(self) -> str: | |
"""Get the research results""" | |
return self.research_results | |
def get_final_content(self) -> str: | |
"""Get the final generated content""" | |
return self.state.final_content | |
def get_research_results(self) -> str: | |
"""Get the research results""" | |
return self.state.research_results |
🤖 Prompt for AI Agents
In ai_news_generator/main.py around lines 184 to 190, the getter methods
get_final_content and get_research_results currently return values directly from
instance variables. Update these methods to return the corresponding values from
the state model instead, ensuring consistency by accessing
self.state.final_content and self.state.research_results respectively.
focusing on the flow structure and state management. | ||
""" | ||
|
||
import os |
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.
Remove unused import.
The os
module is imported but never used.
-import os
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import os |
🧰 Tools
🪛 Ruff (0.12.2)
9-9: os
imported but unused
Remove unused import: os
(F401)
🤖 Prompt for AI Agents
In ai_news_generator/test_flow.py at line 9, the os module is imported but not
used anywhere in the file. Remove the import statement for os to clean up the
code and avoid unnecessary imports.
Summary
This PR refactors the AI News Generator to use CrewAI Flows for better workflow orchestration, replacing the simple Crew usage with an agentic, event-driven approach.
Key Changes
NewsGeneratorFlow
class with@start
and@listen
decoratorsFlow Architecture
Files Added/Modified
main.py
- New CrewAI Flow implementation with CLI interfaceapp.py
- Updated Streamlit interface using Flow architecturerequirements.txt
- Dependencies for Flow implementationtest_flow.py
- Test suite for Flow functionalityREADME.md
- Comprehensive documentation with examples.gitignore
- Ignore Python cache and generated filesTesting
The implementation has been tested with:
Benefits
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Bug Fixes
Tests
Chores
.gitignore
andrequirements.txt
for environment setup and proper file management.