amitbhatt6075 commited on
Commit
cd0ab55
·
1 Parent(s): 4198319

feat: added dynamic config support for ai endpoints

Browse files
Files changed (2) hide show
  1. api/main.py +78 -83
  2. core/strategist.py +23 -6
api/main.py CHANGED
@@ -82,11 +82,15 @@ class ChatResponseRequest(BaseModel): prompt: str = Field(..., description="The
82
  class ChatResponsePayload(BaseModel): response: str
83
  class CaptionRequest(BaseModel): caption: str; action: str
84
  class CaptionResponse(BaseModel): new_caption: str
85
- class BudgetRequest(BaseModel): campaign_goal: str; influencer_count: int; platform: str; location: str; category: str; final_reach: int
 
 
86
  class BudgetResponse(BaseModel): predicted_budget_usd: float
87
  class MatcherRequest(BaseModel): campaign_description: str; target_audience_age: str; target_audience_gender: str; engagement_rate: float; followers: int; country: str; niche: str
88
  class MatcherResponse(BaseModel): suggested_influencer_ids: List[int]
89
- class PerformanceRequest(BaseModel): budget_usd: float; influencer_count: int; platform: str; location: str; category: str; budget: float
 
 
90
  class PerformanceResponse(BaseModel): predicted_engagement_rate: float; predicted_reach: int
91
  class StrategyRequest(BaseModel): prompt: str
92
  class StrategyResponse(BaseModel): response: str
@@ -121,9 +125,13 @@ class RankedCampaignResult(BaseModel): campaign_id: int; score: float
121
  class RankCampaignsResponse(BaseModel): ranked_campaigns: List[RankedCampaignResult]
122
  class CaptionAssistRequest(BaseModel): caption: str; action: str = Field(..., description="Action to perform: 'improve', 'hashtags', or 'check_guidelines'"); guidelines: Optional[str] = None
123
  class CaptionAssistResponse(BaseModel): new_text: str
124
- class ForecastRequest(BaseModel): budget: float; category: str; follower_count: int; engagement_rate: float
 
 
125
  class PerformanceForecast(BaseModel): predicted_engagement_rate: float; predicted_reach: int
126
- class PayoutForecast(BaseModel): estimated_earning: float
 
 
127
  class ForecastResponse(BaseModel): performance: PerformanceForecast; payout: PayoutForecast
128
  class InfluencerKpiData(BaseModel): totalReach: int; totalLikes: int; totalComments: int; avgEngagementRate: float; totalSubmissions: int
129
  class InfluencerAnalyticsSummaryResponse(BaseModel): summary: str
@@ -163,6 +171,14 @@ class WeeklyPlanContext(BaseModel): niche: str; current_mood: str; recent_achiev
163
  class WeeklyPlanRequest(BaseModel): context: WeeklyPlanContext
164
  class PlanOption(BaseModel): type: str; title: str; platform: str; contentType: str; instructions: str; reasoning: str
165
  class WeeklyPlanResponse(BaseModel): options: List[PlanOption]
 
 
 
 
 
 
 
 
166
 
167
  # --- FastAPI App ---
168
  app = FastAPI(title="Reachify AI Service (Deploy-Ready)", version="11.0.0")
@@ -395,11 +411,18 @@ async def generate_strategy_route(request: StrategyRequest):
395
  except Exception as e:
396
  raise HTTPException(status_code=500, detail=f"An internal error occurred in the AI model: {e}")
397
 
398
- @app.post("/api/v1/predict/budget", response_model=BudgetResponse, summary="Predict Campaign Budget")
399
  async def predict_budget(request: BudgetRequest):
400
- if not _budget_predictor: raise HTTPException(status_code=503, detail="Budget predictor is not available.")
401
- input_data = pd.DataFrame([request.model_dump()])
402
- prediction = _budget_predictor.predict(input_data)[0]
 
 
 
 
 
 
 
403
  return BudgetResponse(predicted_budget_usd=round(prediction, 2))
404
 
405
  @app.post("/api/v1/match/influencers", response_model=MatcherResponse, summary="Match Influencers to Campaign")
@@ -658,36 +681,17 @@ def generate_weekly_summary_route(request: WeeklySummaryRequest):
658
  print(f"🚨 An error occurred in /strategist/generate-weekly-summary: {e}")
659
  raise HTTPException(status_code=500, detail=str(e))
660
 
661
- @app.post("/predict/payout_forecast", response_model=PayoutForecastOutput, summary="Predicts future influencer payouts for a manager")
662
  def predict_payout(data: PayoutForecastInput):
663
- """
664
- Predicts the estimated influencer payout for the next 30 days
665
- based on the total budget of a manager's active campaigns.
666
- """
667
- print(f"\n✅ Received request on /predict/payout_forecast")
668
- if not _payout_forecaster:
669
- raise HTTPException(status_code=503, detail="Model is not available. Please train the payout forecaster model first.")
670
 
671
- try:
672
- # Prediction ke liye data ko sahi DataFrame format mein convert karo
673
- input_df = pd.DataFrame([{'budget': data.total_budget_active_campaigns}])
674
-
675
- # Prediction karo
676
- prediction = _payout_forecaster.predict(input_df)[0]
677
-
678
- # Ensure the prediction is never negative
679
- forecasted_amount = max(0, float(prediction))
680
-
681
- print(f" - ✅ Generated payout forecast: {forecasted_amount}")
682
- return {
683
- "forecastedAmount": forecasted_amount,
684
- "commentary": "Based on the total budget of your current active campaigns."
685
- }
686
-
687
- except Exception as e:
688
- print(f"🚨 An error occurred in /predict/payout_forecast endpoint:")
689
- traceback.print_exc()
690
- raise HTTPException(status_code=500, detail=f"An error occurred during prediction: {str(e)}")
691
 
692
 
693
  @app.post("/analyze/content_quality", response_model=ContentQualityResponse, summary="Analyzes a caption for a quality score")
@@ -838,58 +842,29 @@ async def caption_assistant_route(request: CaptionAssistRequest):
838
  raise HTTPException(status_code=500, detail=str(e))
839
 
840
 
841
- @app.post("/predict/campaign-outcome", response_model=ForecastResponse, summary="Forecasts influencer performance and earnings for a campaign")
842
  async def predict_campaign_outcome(request: ForecastRequest):
843
- """
844
- Takes campaign and influencer stats and uses ML models to predict
845
- performance (reach, engagement) and potential earnings.
846
- """
847
- print(f"\n✅ Received request on /predict/campaign-outcome")
848
-
849
- if not _performance_predictor or not _payout_forecaster:
850
- raise HTTPException(status_code=503, detail="Forecasting models are not available.")
851
 
852
- try:
853
- # THE FIX IS HERE: Create a single 'budget' column.
854
- # Column names MUST match the training script's columns.
855
- input_data = pd.DataFrame([{
856
- 'budget': request.budget,
857
- 'category': request.category,
858
- 'influencer_count': 1,
859
- 'platform': 'instagram',
860
- 'location': 'USA',
861
- 'followers': request.follower_count,
862
- 'engagement_rate': request.engagement_rate
863
- }])
864
-
865
- # --- Performance Prediction ---
866
- print(" - Predicting performance...")
867
- # ✅ THE FIX: Pass the columns the model ACTUALLY needs.
868
- performance_model_cols = ['budget', 'influencer_count', 'platform', 'location', 'category']
869
- reach_prediction = _performance_predictor.predict(input_data[performance_model_cols])[0]
870
- engagement_prediction = request.engagement_rate * 100
871
-
872
- perf_forecast = PerformanceForecast(
873
- predicted_reach=int(reach_prediction),
874
- predicted_engagement_rate=round(engagement_prediction, 2)
875
- )
876
 
877
- # --- Payout Prediction ---
878
- print(" - Predicting payout...")
879
- # This model only needs 'budget'
880
- payout_prediction = _payout_forecaster.predict(input_data[['budget']])[0]
881
-
882
- payout_forecast = PayoutForecast(
883
- estimated_earning=max(0, float(payout_prediction))
884
- )
885
 
886
- print(" - Successfully generated forecasts.")
887
- return ForecastResponse(performance=perf_forecast, payout=payout_forecast)
 
 
 
 
 
888
 
889
- except Exception as e:
890
- print(f"🚨 An error occurred during outcome prediction:")
891
- traceback.print_exc()
892
- raise HTTPException(status_code=500, detail=str(e))
893
 
894
  @app.post("/ai/summarize/influencer-analytics", response_model=InfluencerAnalyticsSummaryResponse, summary="Generates a summary for the influencer's analytics page")
895
  async def summarize_influencer_analytics(request: InfluencerKpiData):
@@ -1643,3 +1618,23 @@ def finalize_script_endpoint(request: FinalizeScriptRequest):
1643
  print(f"🚨 Finalize Script Error: {e}")
1644
  traceback.print_exc()
1645
  raise HTTPException(status_code=500, detail="Failed to generate the final plan.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  class ChatResponsePayload(BaseModel): response: str
83
  class CaptionRequest(BaseModel): caption: str; action: str
84
  class CaptionResponse(BaseModel): new_caption: str
85
+ class BudgetRequest(BaseModel):
86
+ campaign_goal: str; influencer_count: int; platform: str; location: str; category: str; final_reach: int
87
+ config: Optional[Dict[str, str]] = None
88
  class BudgetResponse(BaseModel): predicted_budget_usd: float
89
  class MatcherRequest(BaseModel): campaign_description: str; target_audience_age: str; target_audience_gender: str; engagement_rate: float; followers: int; country: str; niche: str
90
  class MatcherResponse(BaseModel): suggested_influencer_ids: List[int]
91
+ class PerformanceRequest(BaseModel):
92
+ budget_usd: float; influencer_count: int; platform: str; location: str; category: str; budget: float
93
+ config: Optional[Dict[str, str]] = None
94
  class PerformanceResponse(BaseModel): predicted_engagement_rate: float; predicted_reach: int
95
  class StrategyRequest(BaseModel): prompt: str
96
  class StrategyResponse(BaseModel): response: str
 
125
  class RankCampaignsResponse(BaseModel): ranked_campaigns: List[RankedCampaignResult]
126
  class CaptionAssistRequest(BaseModel): caption: str; action: str = Field(..., description="Action to perform: 'improve', 'hashtags', or 'check_guidelines'"); guidelines: Optional[str] = None
127
  class CaptionAssistResponse(BaseModel): new_text: str
128
+ class ForecastRequest(BaseModel):
129
+ budget: float; category: str; follower_count: int; engagement_rate: float
130
+ config: Optional[Dict[str, str]] = None
131
  class PerformanceForecast(BaseModel): predicted_engagement_rate: float; predicted_reach: int
132
+ class PayoutForecastInput(BaseModel):
133
+ total_budget_active_campaigns: float
134
+ config: Optional[Dict[str, str]] = None
135
  class ForecastResponse(BaseModel): performance: PerformanceForecast; payout: PayoutForecast
136
  class InfluencerKpiData(BaseModel): totalReach: int; totalLikes: int; totalComments: int; avgEngagementRate: float; totalSubmissions: int
137
  class InfluencerAnalyticsSummaryResponse(BaseModel): summary: str
 
171
  class WeeklyPlanRequest(BaseModel): context: WeeklyPlanContext
172
  class PlanOption(BaseModel): type: str; title: str; platform: str; contentType: str; instructions: str; reasoning: str
173
  class WeeklyPlanResponse(BaseModel): options: List[PlanOption]
174
+ class RequestConfig(BaseModel):
175
+ model_name: Optional[str] = "phi-2"
176
+ temperature: Optional[float] = 0.7
177
+ system_prompt: Optional[str] = None
178
+
179
+ class DirectPromptPayload(BaseModel):
180
+ prompt: str
181
+ config: Optional[RequestConfig] = None
182
 
183
  # --- FastAPI App ---
184
  app = FastAPI(title="Reachify AI Service (Deploy-Ready)", version="11.0.0")
 
411
  except Exception as e:
412
  raise HTTPException(status_code=500, detail=f"An internal error occurred in the AI model: {e}")
413
 
414
+ @app.post("/api/v1/predict/budget", response_model=BudgetResponse)
415
  async def predict_budget(request: BudgetRequest):
416
+ if not _budget_predictor: raise HTTPException(status_code=503, detail="Predictor Unavailable")
417
+
418
+ input_data = pd.DataFrame([request.model_dump(exclude={'config'})])
419
+ prediction = float(_budget_predictor.predict(input_data)[0])
420
+
421
+ # ⚙️ CONTROL: Admin Multiplier Check
422
+ if request.config:
423
+ multiplier = float(request.config.get("budget_multiplier", 1.0))
424
+ prediction = prediction * multiplier
425
+
426
  return BudgetResponse(predicted_budget_usd=round(prediction, 2))
427
 
428
  @app.post("/api/v1/match/influencers", response_model=MatcherResponse, summary="Match Influencers to Campaign")
 
681
  print(f"🚨 An error occurred in /strategist/generate-weekly-summary: {e}")
682
  raise HTTPException(status_code=500, detail=str(e))
683
 
684
+ @app.post("/predict/payout_forecast", response_model=PayoutForecastOutput)
685
  def predict_payout(data: PayoutForecastInput):
686
+ if not _payout_forecaster: raise HTTPException(status_code=503, detail="Model Unavailable")
 
 
 
 
 
 
687
 
688
+ pred = float(_payout_forecaster.predict(pd.DataFrame([{'budget': data.total_budget_active_campaigns}]))[0])
689
+
690
+ # ⚙️ CONTROL
691
+ if data.config:
692
+ pred = pred * float(data.config.get("budget_multiplier", 1.0))
693
+
694
+ return {"forecastedAmount": max(0, pred), "commentary": "Based on budget trends."}
 
 
 
 
 
 
 
 
 
 
 
 
 
695
 
696
 
697
  @app.post("/analyze/content_quality", response_model=ContentQualityResponse, summary="Analyzes a caption for a quality score")
 
842
  raise HTTPException(status_code=500, detail=str(e))
843
 
844
 
845
+ @app.post("/predict/campaign-outcome", response_model=ForecastResponse)
846
  async def predict_campaign_outcome(request: ForecastRequest):
847
+ if not _performance_predictor or not _payout_forecaster: raise HTTPException(status_code=503, detail="Models Unavailable")
 
 
 
 
 
 
 
848
 
849
+ input_df = pd.DataFrame([request.model_dump(exclude={'config'})])
850
+ input_df['influencer_count'] = 1; input_df['platform'] = 'instagram'; input_df['location'] = 'USA'; input_df['followers'] = request.follower_count
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
851
 
852
+ # Predict
853
+ reach = _performance_predictor.predict(input_df[['budget','influencer_count','platform','location','category']])[0]
854
+ payout = float(_payout_forecaster.predict(input_df[['budget']])[0])
 
 
 
 
 
855
 
856
+ # ⚙️ CONTROL: Adjust Values if needed
857
+ if request.config:
858
+ payout_multiplier = float(request.config.get("budget_multiplier", 1.0)) # Shared Logic for simplicity
859
+ payout = payout * payout_multiplier
860
+ # Ensure Minimum Payout (Floor)
861
+ min_payout = float(request.config.get("ml_payout_floor", 0))
862
+ payout = max(min_payout, payout)
863
 
864
+ return ForecastResponse(
865
+ performance=PerformanceForecast(predicted_reach=int(reach), predicted_engagement_rate=round(request.engagement_rate*100, 2)),
866
+ payout=PayoutForecast(estimated_earning=max(0, payout))
867
+ )
868
 
869
  @app.post("/ai/summarize/influencer-analytics", response_model=InfluencerAnalyticsSummaryResponse, summary="Generates a summary for the influencer's analytics page")
870
  async def summarize_influencer_analytics(request: InfluencerKpiData):
 
1618
  print(f"🚨 Finalize Script Error: {e}")
1619
  traceback.print_exc()
1620
  raise HTTPException(status_code=500, detail="Failed to generate the final plan.")
1621
+
1622
+
1623
+ @app.post("/api/v1/generate-campaign-from-prompt")
1624
+ def create_campaign_from_prompt_endpoint(payload: DirectPromptPayload):
1625
+ # Check if Strategist is loaded
1626
+ if not _ai_strategist:
1627
+ raise HTTPException(status_code=503, detail="AI Strategist model unavailable.")
1628
+
1629
+ # Use Config or Default
1630
+ current_config = payload.config if payload.config else RequestConfig()
1631
+
1632
+ try:
1633
+ # Core Logic Call (Make sure Core Logic updated too)
1634
+ response_text = _ai_strategist.generate_strategy_from_prompt(
1635
+ user_prompt=payload.prompt,
1636
+ config=current_config
1637
+ )
1638
+ return {"response": response_text}
1639
+ except Exception as e:
1640
+ raise HTTPException(status_code=500, detail=str(e))
core/strategist.py CHANGED
@@ -64,25 +64,42 @@ class AIStrategist:
64
  print(f"--- Strategist Skill MOCK ERROR: {e}")
65
  return {"error": "An internal error occurred in the mock data generator."}
66
 
67
- def generate_strategy_from_prompt(self, user_prompt: str) -> str:
 
68
  """
69
- Generates a general strategy from a raw prompt.
70
  """
71
- print(f"--- Strategist Skill (General): Received prompt: '{user_prompt[:50]}...'")
 
 
 
 
 
 
 
 
 
 
 
 
72
  try:
 
73
  response = self.llm(
74
- user_prompt,
75
  max_tokens=750,
76
- temperature=0.75,
77
  stop=["User:", "Client:", "System:"],
78
  )
 
79
  generated_text = response['choices'][0]['text'].strip()
80
- print("--- Strategist Skill (General): Received response from LLM.")
81
  return generated_text
 
82
  except Exception as e:
83
  print(f"--- Strategist Skill (General) ERROR: {e}")
84
  traceback.print_exc()
85
  return "An error occurred in the AI model while generating the strategy."
 
86
 
87
  def generate_weekly_summary(self, metrics: Dict[str, Any]) -> str:
88
  """
 
64
  print(f"--- Strategist Skill MOCK ERROR: {e}")
65
  return {"error": "An internal error occurred in the mock data generator."}
66
 
67
+
68
+ def generate_strategy_from_prompt(self, user_prompt: str, config=None) -> str:
69
  """
70
+ Generates a strategy using Dynamic Config from Backend.
71
  """
72
+ print(f"--- Strategist Skill (General): Received prompt. Config: {config}")
73
+
74
+ # 1. Temperature Setup (Default 0.75 if config missing)
75
+ temp = config.temperature if config else 0.75
76
+
77
+ # 2. System Prompt Logic
78
+ final_prompt = user_prompt
79
+
80
+ # Agar admin ne koi 'System Prompt' set kiya hai (Jaise "Be concise"), to use jod do
81
+ if config and config.system_prompt:
82
+ # Llama model format: [SYSTEM] instruction [USER] input
83
+ final_prompt = f"[SYSTEM]\n{config.system_prompt}\n\n[USER]\n{user_prompt}"
84
+
85
  try:
86
+ # 3. Call Llama with DYNAMIC Temp
87
  response = self.llm(
88
+ final_prompt,
89
  max_tokens=750,
90
+ temperature=temp, # ✅ Magic yahan hai!
91
  stop=["User:", "Client:", "System:"],
92
  )
93
+
94
  generated_text = response['choices'][0]['text'].strip()
95
+ print("--- Strategist Skill (General): Response Generated.")
96
  return generated_text
97
+
98
  except Exception as e:
99
  print(f"--- Strategist Skill (General) ERROR: {e}")
100
  traceback.print_exc()
101
  return "An error occurred in the AI model while generating the strategy."
102
+
103
 
104
  def generate_weekly_summary(self, metrics: Dict[str, Any]) -> str:
105
  """