Unable to deploy my Crewai flow

Hello,

I deployed my crewai flow on crewai amp but its not showing me my API URL, this made me to miss the deadline submission for a hackathon. Pls check the uploaded image for the deployment logs and evidence. As you can see in the first image, its showing deployment error but still saying crew is online. What is the solution to this pls? Below is my pyproject.toml and main.py codes, maybe i got something wrong there

Pyproject.toml:

[project]
name = “real_ai_agents”
version = “0.1.0”
description = “Real_ai_agents using crewAI”
authors = [{ name = “Emmanuel Ezeokeke”, email = “email” }]
requires-python = “>=3.10,<3.14”
dependencies = [
“crawl4ai[all]>=0.8.0”,
“crewai[tools]==1.9.3”,
“google-genai>=1.60.0”,
“matplotlib>=3.10.8”,
“networkx>=3.4.2”,
“requests>=2.32.5”,
“scikit-learn>=1.7.2”,
“tavily-python>=0.7.19”
]

[project.scripts]
kickoff = “real_ai_agents.main:kickoff”
run_crew = “real_ai_agents.main:kickoff”
plot = “real_ai_agents.main:plot”
run_with_trigger = “real_ai_agents.main:run_with_trigger”

[build-system]
requires = [“hatchling”]
build-backend = “hatchling.build”

[tool.crewai]
type = “flow”

Main.py file:

import json

import asyncio

from typing import List, Optional

from pydantic import BaseModel, Field




from crewai.flow.flow import Flow, listen, start

from crewai.flow.persistence import persist

from crewai.flow.human_feedback import human_feedback, HumanFeedbackResult




from real_ai_agents.crews.research_crew.research_crew import ResearchCrew

from real_ai_agents.crews.location_analyzer_crew.location_analyzer_crew import LocationAnalyzerCrew

from real_ai_agents.crews.interior_design_crew.interior_design_crew import InteriorDesignCrew




# ============== DEBUG HOOK: Log all tool calls ==============

import crewai.tools.tool_usage as tool_module




_original_call = tool_module.ToolUsage._render




def debug_tool_run(self, *args, **kwargs):

    print(f"\n🔧 TOOL CALLED: {self.tools_handler.tools}")

    return _original_call(self, *args, **kwargs)




tool_module.ToolUsage._render = debug_tool_run

# =============================================================





class SearchCriteria(BaseModel):

    """User search criteria for property discovery."""

    location: str = Field(description="City, neighborhood, or area to search")

    property_type: str = Field(default="apartment", description="Type: apartment, house, condo, shortlet, hotel, etc.")

    bedrooms: Optional[int] = Field(default=None, description="Number of bedrooms")

    bathrooms: Optional[int] = Field(default=None, description="Number of bathrooms")

    max_price: Optional[float] = Field(default=None, description="Maximum price/rent")

    rent_frequency: str = Field(default="monthly", description="Payment frequency: daily, weekly, monthly, yearly")

    additional_requirements: Optional[str] = Field(default=None, description="Other requirements")





class RealEstateState(BaseModel):

    """State model for the Real Estate Agent Flow."""

    search_criteria: Optional[SearchCriteria] = None

    design_style_preference: str = "modern minimalist"

    approved_property_ids: List[str] = Field(default_factory=list)

    excluded_sites: List[str] = Field(default_factory=list)

    retry_count: int = 0

    user_feedback: Optional[str] = None

    research_results: Optional[str] = None

    filtered_research_results: Optional[str] = None

    location_results: Optional[str] = None

    design_results: Optional[str] = None

    properties_found: int = 0

    properties_approved: int = 0

    properties_analyzed: int = 0

    rooms_redesigned: int = 0 





@persist()

class RealEstateFlow(Flow[RealEstateState]): 

    """AI Real Estate Agent Flow with human-in-the-loop property approval."""




    @start()

    def initialize_search(self, crewai_trigger_payload: dict = None):

        """Initialize the flow with search criteria."""

        print("\n" + "=" * 60)

        print("🏠 AI Real Estate Agent - Find & Redesign")

        print("=" * 60)

        

        # Priority: 

        # 1. Payload passed directly to this method (e.g. via API webhook)

        # 2. Inputs passed to kickoff() which are stored in self.state by CrewAI Flow (if structured properly)

        

        inputs = crewai_trigger_payload or {}

        

        # If inputs are empty, use defaults

        if not inputs.get("search_criteria"):

            inputs = {

                "search_criteria": {

                    "location": "Ojodu, Lagos, Nigeria",

                    "property_type": "apartment, Flat", 

                    "bedrooms": 2,

                    "max_price": 3000000,

                    "rent_frequency": "yearly/annually"

                },

                "design_style": "modern minimalist"

            }

            

        self.state.search_criteria = SearchCriteria(**inputs.get("search_criteria", {}))

        self.state.design_style_preference = inputs.get("design_style", "modern minimalist")

        

        print(f"   Location: {self.state.search_criteria.location}")

        print(f"   Bedrooms: {self.state.search_criteria.bedrooms}")

        print("-" * 60)




    @listen(initialize_search)

    def run_research_phase(self):

        """Phase 1: Run Research Crew to find properties."""

        print("\n🔍 Phase 1: Property Research")

        

        criteria = self.state.search_criteria

        search_query = f"{criteria.bedrooms or ''} bedroom {criteria.property_type} in {criteria.location}"

        if criteria.max_price:

            search_query += f" under {criteria.max_price}"

        search_query += f" ({criteria.rent_frequency} rent)"

        

        result = ResearchCrew().crew().kickoff(inputs={

            "search_criteria": search_query.strip(),

            "excluded_sites": self.state.excluded_sites

        })

        self.state.research_results = result.raw

        

        try:

            data = json.loads(result.raw) if isinstance(result.raw, str) else result.raw

            if isinstance(data, dict) and "properties" in data:

                self.state.properties_found = len(data["properties"])

            elif isinstance(data, dict) and "listings" in data:

                self.state.properties_found = len(data["listings"])

        except:

            pass

        

        print(f"   ✅ Found {self.state.properties_found} properties")

        return self.state.research_results




    @listen(run_research_phase)

    @human_feedback(

        message="Review the properties and select which ones to proceed with. "

                "Respond with a JSON array of property IDs, e.g. ['prop_001', 'prop_002']. "

                "Or type 'retry' with feedback to search again.",

        emit=["approved", "retry"],

        llm="gemini/gemini-3-flash-preview",

        default_outcome="approved",

    )

    def await_property_approval(self):

        """PAUSE: Wait for user to select properties."""

        print("\n⏸️  Awaiting Property Approval...")

        return self.state.research_results




    @listen("approved")

    def filter_approved_properties(self, result: HumanFeedbackResult):

        """Process user feedback and filter properties."""

        print("\n✅ Processing Property Selection")

        

        try:

            feedback = result.feedback

            if isinstance(feedback, str) and feedback.startswith('['):

                approved_ids = json.loads(feedback)

            else:

                approved_ids = [id.strip().strip("'\"") for id in feedback.split(',')]

            self.state.approved_property_ids = approved_ids

        except:

            self.state.approved_property_ids = []

        

        try:

            data = json.loads(self.state.research_results)

            key = "properties" if "properties" in data else "listings"

            if key in data:

                data[key] = [p for p in data[key] if p.get("id") in self.state.approved_property_ids]

                self.state.properties_approved = len(data[key])

            self.state.filtered_research_results = json.dumps(data)

        except:

            self.state.filtered_research_results = self.state.research_results

        

        print(f"   ✅ Approved {self.state.properties_approved} properties")




    @listen("retry")

    def handle_retry_search(self, result: HumanFeedbackResult):

        """Handle user request to retry search with new criteria."""

        print("\n🔄 Retry Requested")

        

        self.state.retry_count += 1

        self.state.user_feedback = result.feedback

        

        # Parse excluded sites from feedback

        feedback_lower = result.feedback.lower()

        if "apartmenthomeliving" in feedback_lower:

            self.state.excluded_sites.append("apartmenthomeliving.com")

        

        print(f"   Retry #{self.state.retry_count}")

        print(f"   Excluded sites: {self.state.excluded_sites}")

        

        # Re-run research phase

        return self.run_research_phase()




    @listen(filter_approved_properties)

    async def run_parallel_action_phase(self):

        """Phase 2: Run Location and Design Crews in PARALLEL."""

        print("\n🚀 Phase 2: Parallel Execution (Location & Design)")

        

        if self.state.properties_approved == 0:

            print("   ⚠️ No properties approved, skipping...")

            return




        # Prepare inputs

        inputs = {

            "research_results": self.state.filtered_research_results,

            "design_style": self.state.design_style_preference

        }




        # Create tasks for parallel execution

        location_task = asyncio.create_task(

            LocationAnalyzerCrew().crew().kickoff_async(inputs=inputs)

        )

        design_task = asyncio.create_task(

            InteriorDesignCrew().crew().kickoff_async(inputs=inputs)

        )




        # Wait for both to complete

        results = await asyncio.gather(location_task, design_task)

        

        # Unpack results

        location_result, design_result = results

        

        self.state.location_results = location_result.raw

        self.state.design_results = design_result.raw

        

        # Update metrics

        self.state.properties_analyzed = self.state.properties_approved

        try:

            design_data = json.loads(design_result.raw)

            self.state.rooms_redesigned = design_data.get("metadata", {}).get("total_rooms_redesigned", 0)

        except:

            pass




        print(f"   ✅ Parallel Phase Complete")

        print(f"      - Analyzed {self.state.properties_analyzed} locations")

        print(f"      - Redesigned {self.state.rooms_redesigned} rooms")




    @listen(run_parallel_action_phase)

    def compile_final_report(self):

        """Final: Compile unified report."""

        print("\n📊 Compiling Final Report")

        

        final_report = {

            "search_criteria": self.state.search_criteria.model_dump() if self.state.search_criteria else {},

            "summary": {

                "properties_found": self.state.properties_found,

                "properties_approved": self.state.properties_approved,

                "properties_analyzed": self.state.properties_analyzed,

                "rooms_redesigned": self.state.rooms_redesigned

            },

            "approved_property_ids": self.state.approved_property_ids,

            "phases": {

                "research": self.state.research_results,

                "location": self.state.location_results,

                "design": self.state.design_results

            }

        }

        

        with open("output/unified_report.json", "w") as f:

            json.dump(final_report, f, indent=2)

        

        print("   ✅ Report saved to output/unified_report.json")

        print("\n🏠 Flow Complete!")

        return final_report





def kickoff(inputs: dict = None):

    """

    Run the Real Estate Agent flow.

    

    Args:

        inputs (dict): Input parameters for the flow.

                       Expected keys: 'search_criteria', 'design_style'

    """

    # Default inputs if none provided

    if inputs is None:

        inputs = {

            "search_criteria": {

                "location": "Ojodu, Lagos, Nigeria",

                "property_type": "apartment, Flat",

                "bedrooms": 2,

                "max_price": 3000000,

                "rent_frequency": "yearly/annually"

            },

            "design_style": "modern minimalist"

        }

        

    print(f"🚀 Kicking off flow with inputs: {json.dumps(inputs, indent=2)}")

    RealEstateFlow().kickoff(inputs=inputs)





def plot():

    """Generate flow visualization."""

    RealEstateFlow().plot()

if __name__ == "__main__":

    kickoff()





Summary

This text will be hidden


I recommend reaching out to the crewai.com support as they will help you

Did you get the resolution? ALso, Are you usung paid version of AMP?

No i havent gotten any resolution and i am using the free version of AMP