Can we have a feed from the GitHub to announcements

Hey everyone,

I thought your initiative here was really cool. It shows our community is looking to help each other out, which is great.

To chip in, I’m sharing a simple draft of one way to tackle this need. It’s all pretty basic, still in the early stages, but maybe it can help you guys develop a more robust and professional solution.

I proposed an automated workflow that basically:

  • Monitors: Checks if a new version of the crewai library has been released.
  • Analyzes: If there’s a new version, it figures out which important code files changed between the new version and the previous one (e.g., by comparing git tags).
  • Summarizes: For each important changed file, it generates a concise, easy-to-understand summary of the modifications using an LLM.
  • Augments: It grabs the official release notes (which might be brief) and beefs them up with the generated file summaries.
  • Reports: It creates a final report in Markdown format, perfect for posting in a forum, detailing the changes in the new release comprehensively.

I didn’t go down the route of handling issues or ongoing PRs because a lot of those might just get discarded anyway. That could quickly take us from “we have too little information” to “we have way too much information.” :grin:

The basic structure uses Flows, with some cooler features like Conditional Logic and state persistence between runs.

Hope the draft and the code comments are useful for your initiative, even if it’s just to serve as an example of how not to do things! :sweat_smile:

Directory Structure:

crewai_release_watch_flow/
├── config/
│   ├── agents.yaml
│   ├── llms.yaml
│   └── tasks.yaml
├── models/
│   ├── __init__.py
│   └── datamodels.py
├── tools/
│   ├── __init__.py
│   └── releasewatchtools.py
├── main.py
└── utils.py

main.py file:

import logging
from typing import List, Dict, Any

from packaging.version import parse as parse_version

from crewai.flow.flow import Flow, listen, start, router, or_
from crewai.flow.persistence import persist
from pydantic import BaseModel, Field

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

# Define a consistent ID for state persistence
# This ID ensures the flow retrieves the correct state across runs.
FLOW_STATE_ID = "crewai-release-monitor-state"

# --- Data Models (models/datamodels.py) ---

class ChangeSummary(BaseModel):
    """
    Represents a summary of changes for a specific file.
    """

    file_changed: str = Field(..., description="Path of the file that changed.")
    summary: str = Field(..., description="Generated summary of the changes.")

class FlowState(BaseModel):
    """
    Holds the state of the CrewAI release monitoring flow.

    This state is persisted between runs using the specified 'id'.
    Versions are stored as strings and compared using packaging.version.
    """

    id: str = Field(
        default=FLOW_STATE_ID,
        description="Unique identifier for persisting flow state.",
    )
    new_release_found: bool = Field(
        default=False,
        description="Flag indicating if a new release was detected.",
    )
    new_release: str = Field(
        default="0.2.0",
        description="Version string of the most recent release checked.",
    )
    previous_release: str = Field(
        default="0.1.0",
        description="Version string of the release before the new_release.",
    )
    official_release_note: str = Field(
        default="",
        description="Official release notes fetched from the source.",
    )
    change_summaries_list: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="List of dictionaries, each summarizing changes in a file.",
    )
    output_report_generated: bool = Field(
        default=False,
        description="Flag indicating if the final report has been generated.",
    )

# --- CrewAI Flow Definition ---

@persist(verbose=True)
class CrewAIReleaseWatchFlow(Flow[FlowState]):
    """
    A CrewAI Flow to monitor CrewAI releases, summarize changes,
    and generate an augmented release report.
    """

    @start()
    def check_for_new_version(self):
        """
        Starting node: Checks if a new version of CrewAI has been released
        by comparing the latest fetched version with the 'new_release'
        stored in the state, using packaging.version.parse.

        If a new version is found, resets the state variables related to
        change summaries and reports for the new analysis cycle.
        """
        logger.info("Checking for new CrewAI releases...")

        # --- Placeholder Logic ---
        # TODO: Implement actual version checking logic here.
        # 1. Fetch the latest version string GitHub Releases.
        # 2. Parse it using packaging.version.parse
        # 3. Compare with self.state.last_release
        #    new_release_found = latest_release > current_known_release
        # -------------------------

        # Simulate a new release
        simulated_new_release: str = "0.3.0"
        simulated_previous_release: str = "0.2.0"

        # --- State Update ---

        logger.info(
            f"Comparing latest fetched release "
            f"('{simulated_new_release}') with known latest "
            f"release ('{self.state.new_release}')."
        )

        try:
            latest_release = parse_version(simulated_new_release)
            current_known_release = parse_version(self.state.new_release)
            new_release_found = latest_release > current_known_release
        except Exception as e:
            logger.error(f"Failed to parse releases for comparison: {e}")
            new_release_found = False  # Treat parse error as no new release

        if new_release_found:
            logger.info(
                f"New version found: {simulated_new_release} "
                f"(Previous was: {self.state.new_release})"
            )

            # --- Placeholder Logic ---
            # TODO: Fetch actual official release notes for the new version
            # official_notes = fetch_release_notes(new_release)
            # -------------------------
            official_notes = f"What's New in {simulated_new_release}..."

            # --- State Update ---
            self.state.new_release_found = True
            self.state.new_release = simulated_new_release
            self.state.previous_release = simulated_previous_release
            self.state.official_release_note = official_notes
            self.state.change_summaries_list = []
            self.state.output_report_generated = False
            logger.info(
                f"State updated: previous_release='{simulated_previous_release}', "
                f"new_release='{simulated_new_release}'."
            )
        else:
            logger.info(
                f"No new version found (latest is still '{self.state.new_release}')."
            )
            self.state.new_release_found = False

    @router(check_for_new_version)
    def route_after_check(self):
        """
        Routes the flow based on whether a new version was found.
        """
        if self.state.new_release_found:
            logger.info("Routing to: summarize_changed_files")
            return "new_version_found"
        else:
            logger.info("Routing to: end_flow (no new version)")
            return "no_new_version"

    @listen("new_version_found")
    def summarize_changed_files(self):
        """
        Analyzes changes between the 'previous_release' and 'new_release'
        versions stored in the state and generates summaries.
        """
        logger.info("Starting analysis of changed files...")
        # Retrieve version strings directly from state
        version_new = self.state.new_release
        version_old = self.state.previous_release
        logger.info(f"Analyzing changes between {version_old} and {version_new}")

        # --- Placeholder Logic ---
        # TODO: Implement logic to get changed files between releases
        #       using version_old and version_new (e.g., git tags).
        # TODO: Filter files (analyze code and docs only).
        # TODO: Use LLM to summarize diffs.
        # -------------------------

        # Simulate a summary
        file_changed = "src/crewai/crew.py"
        summary_text = (
            f"Introduced new collaboration modes for crews "
            f"between versions {version_old} and {version_new}."
        )

        change_summary = ChangeSummary(file_changed=file_changed, summary=summary_text)

        self.state.change_summaries_list.append(change_summary.model_dump())
        logger.info(f"Generated summary for: {file_changed}")

        logger.info("File change analysis complete. Routing to compile report.")

    @listen(summarize_changed_files)
    def compile_final_report(self):
        """
        Generates the final Markdown report using the 'new_release' version,
        official notes, and generated summaries from the state.
        """
        logger.info("Compiling the final release report...")

        official_notes = self.state.official_release_note
        summaries = self.state.change_summaries_list
        version = self.state.new_release

        # --- Placeholder Logic ---
        # TODO: Implement the actual Markdown generation logic.
        # -------------------------

        final_report_md = f"# CrewAI v{version} Report\n\n"
        final_report_md += "## Official Notes\n\n"
        final_report_md += f"{official_notes}\n\n"
        final_report_md += "## File Summaries\n\n"
        if summaries:
            for summary_dict in summaries:
                final_report_md += f"- `{summary_dict['file_changed']}`: "
                final_report_md += f"{summary_dict['summary']}\n"
        else:
            final_report_md += "- N/A\n"

        # --- Placeholder Logic ---
        # TODO: Implement saving or posting the report.
        # -------------------------

        self.state.output_report_generated = True
        logger.info("Report generation complete. Routing to end_flow.")

    @listen(or_("no_new_version", compile_final_report))
    def end_flow(self):
        """
        Final node: Logs the completion of the flow run.
        """
        logger.info("-- CrewAI Release Watch Flow finished --")


# --- Flow Execution ---
if __name__ == "__main__":
    logger.info("Initializing CrewAIReleaseWatchFlow...")
    flow = CrewAIReleaseWatchFlow()

    logger.info(f"Kicking off flow with state ID: {FLOW_STATE_ID}")
    flow.kickoff(
        inputs={
            "id": FLOW_STATE_ID,
        }
    )

    logger.info("Final flow state after run:")
    logger.info(flow.state.model_dump_json(indent=2))
1 Like