Spaces:
Sleeping
Sleeping
from fastapi import FastAPI, HTTPException, Request | |
from fastapi.responses import StreamingResponse | |
from fastapi.staticfiles import StaticFiles | |
from fastapi.templating import Jinja2Templates | |
from pydantic import BaseModel | |
from app.controllers.f5_model import TextGenerationHandler | |
from typing import List, Optional | |
import logging | |
from app.helpers.plan_parser import parse_plan_sections | |
import json | |
# Configure logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s', | |
handlers=[ | |
logging.StreamHandler(), | |
logging.FileHandler('app.log') | |
] | |
) | |
logger = logging.getLogger(__name__) | |
app = FastAPI(title="F5 Model Test Application") | |
templates = Jinja2Templates(directory="app/templates") | |
# Initialize the F5 model | |
model_handler = TextGenerationHandler() | |
class ChatMessage(BaseModel): | |
role: str | |
content: str | |
class ChatRequest(BaseModel): | |
messages: List[ChatMessage] | |
stream: Optional[bool] = False | |
class FeatureRequest(BaseModel): | |
requirements: str | |
class Feature(BaseModel): | |
feature: str | |
short_description: str | |
class FeaturesResponse(BaseModel): | |
features: List[Feature] | |
class ProjectPlanRequest(BaseModel): | |
project_title: str | |
requirements: str | |
features: List[str] | |
platform: str = "AWS" # Default to AWS | |
additional_requirements: str = "" | |
class ProjectSection(BaseModel): | |
title: str | |
content: str | |
class ProjectPlan(BaseModel): | |
executive_summary: str | |
scope_objectives: dict | |
architecture_overview: str | |
component_design: List[dict] | |
security_compliance: List[str] | |
deployment_testing: List[str] | |
team_roles: List[dict] | |
cost_estimates: dict | |
project_phases: List[dict] | |
async def index(request: Request): | |
return templates.TemplateResponse("index.html", {"request": request}) | |
async def chat(request: ChatRequest): | |
try: | |
logger.info(f"Chat request received with {len(request.messages)} messages") | |
# Improve the prompt with better context | |
prompt = ( | |
"You are a helpful AI assistant specializing in SaaS applications and software development. " | |
"Please provide detailed and professional responses.\n\n" | |
) | |
for msg in request.messages: | |
if msg.role == "user": | |
prompt += f"[INST] {msg.content} [/INST]\n" | |
else: | |
prompt += f"{msg.content}\n" | |
logger.info(f"Formatted prompt: {prompt}") | |
if request.stream: | |
logger.info("Starting streaming response") | |
async def generate(): | |
async for chunk in model_handler.stream_response(prompt): | |
logger.debug(f"Streaming chunk: {chunk}") | |
yield f"data: {chunk}\n\n" | |
return StreamingResponse(generate(), media_type="text/event-stream") | |
else: | |
response = await model_handler.generate_response(prompt) | |
logger.info(f"Generated response: {response}") | |
return {"response": response} | |
except Exception as e: | |
logger.error(f"Error in chat endpoint: {str(e)}", exc_info=True) | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def generate_features(request: FeatureRequest): | |
try: | |
logger.info(f"Feature generation request received with requirements: {request.requirements}") | |
# Improved prompt for better feature generation | |
prompt = ( | |
"You are a SaaS product expert. Generate 20 practical features for a SaaS application.\n" | |
"For each feature:\n" | |
"1. Provide a short, clear feature name\n" | |
"2. Write a concise description explaining its value\n" | |
"Format each feature as:\n" | |
"Feature Name\n" | |
"Clear description of what the feature does and its benefits.\n\n" | |
f"Requirements: {request.requirements}\n\n" | |
"Generate 20 features in this exact format." | |
) | |
logger.info(f"Generated prompt: {prompt}") | |
response = await model_handler.generate_response(prompt) | |
logger.info(f"Model response: {response}") | |
# Parse the response into features | |
features = [] | |
lines = response.split('\n') | |
current_feature = None | |
current_description = [] | |
for line in lines: | |
line = line.strip() | |
if not line: | |
if current_feature and current_description: | |
features.append(Feature( | |
feature=current_feature, | |
short_description=' '.join(current_description) | |
)) | |
current_feature = None | |
current_description = [] | |
elif not current_feature: | |
current_feature = line | |
else: | |
current_description.append(line) | |
# Add the last feature if exists | |
if current_feature and current_description: | |
features.append(Feature( | |
feature=current_feature, | |
short_description=' '.join(current_description) | |
)) | |
# If no features were parsed, provide fallback features | |
if not features: | |
features = [ | |
Feature( | |
feature="User Management", | |
short_description="Complete user authentication and authorization system" | |
), | |
Feature( | |
feature="Subscription Billing", | |
short_description="Automated billing and subscription management" | |
), | |
Feature( | |
feature="Analytics Dashboard", | |
short_description="Real-time metrics and usage analytics" | |
), | |
Feature( | |
feature="API Integration", | |
short_description="RESTful API endpoints for third-party integration" | |
), | |
Feature( | |
feature="Multi-tenant Architecture", | |
short_description="Secure data isolation for multiple customers" | |
) | |
] | |
logger.info(f"Returning features: {features}") | |
return FeaturesResponse(features=features) | |
except Exception as e: | |
logger.error(f"Error in generate-features endpoint: {str(e)}", exc_info=True) | |
raise HTTPException(status_code=500, detail=str(e)) | |
async def generate_plan(request: ProjectPlanRequest): | |
try: | |
logger.info(f"Plan generation request received for project: {request.project_title}") | |
async def generate(): | |
features_list = request.features | |
if features_list and isinstance(features_list[0], dict): | |
features_str = ', '.join([f.get('feature', '') for f in features_list]) | |
else: | |
features_str = ', '.join(features_list) | |
prompt = f"""As a technical project planner, create a detailed project plan for a {request.platform} SaaS application named '{request.project_title}'. | |
Follow this structure exactly, replacing the placeholders with detailed content: | |
1. Executive Summary | |
Brief overview of the {request.project_title} SaaS application | |
Key business objectives | |
Technical approach using {request.platform} | |
Expected outcomes | |
2. Project Scope and Objectives | |
Core Features: | |
- {features_str} | |
Technical Goals: | |
- Scalable {request.platform} architecture | |
- Secure data handling | |
- High availability | |
3. Architecture Overview | |
{request.platform} Components: | |
- Frontend: [Specify technology] | |
- Backend: [Specify {request.platform} services] | |
- Database: [Specify database solution] | |
- Storage: [Specify storage solution] | |
- Authentication: [Specify auth service] | |
4. Component Design | |
[For each major component, specify: | |
- Technical specifications | |
- Integration points | |
- Performance requirements] | |
5. Security and Compliance | |
{request.platform}-specific security measures: | |
- Data encryption | |
- Access controls | |
- Compliance requirements | |
6. Deployment Strategy | |
- CI/CD pipeline | |
- Testing approach | |
- Monitoring setup | |
- {request.platform} specific considerations | |
7. Team Requirements | |
- Required roles | |
- Technical skills | |
- Team structure | |
8. Cost Estimation | |
- {request.platform} service costs | |
- Development costs | |
- Operational costs | |
9. Project Timeline | |
Phase 1: Setup and Infrastructure | |
Phase 2: Core Development | |
Phase 3: Testing and Deployment | |
Phase 4: Launch and Monitoring | |
Additional Context: | |
Requirements: {request.requirements} | |
Additional Requirements: {request.additional_requirements}""" | |
response_content = "" | |
async for chunk in model_handler.stream_response(prompt): | |
if chunk["type"] == "content": | |
response_content = chunk["content"] | |
sections = parse_plan_sections(response_content) | |
# The `yield` keyword in Python is used in generator functions to return a value | |
# to the caller and temporarily suspend the function's execution. In the context | |
# of asynchronous programming, `yield` is used to create asynchronous generators | |
# that can produce values asynchronously. | |
yield f"""data: {json.dumps({ | |
'type': 'complete', | |
'project_title': request.project_title, | |
'sections': sections, | |
'raw_content': response_content | |
})}\n\n""" | |
elif chunk["type"] == "error": | |
yield f"data: {json.dumps({ | |
'type': 'error', | |
'content': chunk['content'] | |
})}\n\n" | |
return StreamingResponse(generate(), media_type="text/event-stream") | |
except Exception as e: | |
logger.error(f"Error generating plan: {str(e)}", exc_info=True) | |
raise HTTPException(status_code=500, detail=str(e)) | |
if __name__ == "__main__": | |
import uvicorn | |
uvicorn.run(app, host="0.0.0.0", port=8000) | |