mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
146 lines
4.9 KiB
Python
146 lines
4.9 KiB
Python
"""
|
||
Proposal Agent — generates tailored proposals using Claude.
|
||
وكيل العروض — يُعدّ عروضاً مخصصة باستخدام Claude.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, field
|
||
from datetime import datetime, timedelta
|
||
from typing import Any
|
||
|
||
from auto_client_acquisition.agents.icp_matcher import FitScore
|
||
from auto_client_acquisition.agents.intake import Lead
|
||
from core.agents.base import BaseAgent
|
||
from core.config.models import Task
|
||
from core.config.settings import get_settings
|
||
from core.llm.base import Message
|
||
from core.prompts import get_prompt
|
||
from core.utils import generate_id, utcnow
|
||
|
||
|
||
@dataclass
|
||
class Proposal:
|
||
id: str
|
||
lead_id: str
|
||
company_name: str
|
||
sector: str | None
|
||
locale: str
|
||
body_markdown: str
|
||
budget_min: float
|
||
budget_max: float
|
||
currency: str
|
||
valid_until: datetime
|
||
created_at: datetime = field(default_factory=utcnow)
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
return {
|
||
"id": self.id,
|
||
"lead_id": self.lead_id,
|
||
"company_name": self.company_name,
|
||
"sector": self.sector,
|
||
"locale": self.locale,
|
||
"body_markdown": self.body_markdown,
|
||
"budget_min": self.budget_min,
|
||
"budget_max": self.budget_max,
|
||
"currency": self.currency,
|
||
"valid_until": self.valid_until.isoformat(),
|
||
"created_at": self.created_at.isoformat(),
|
||
}
|
||
|
||
|
||
class ProposalAgent(BaseAgent):
|
||
"""Generates an LLM-authored proposal tailored to the lead."""
|
||
|
||
name = "proposal"
|
||
|
||
def __init__(self) -> None:
|
||
super().__init__()
|
||
self.settings = get_settings()
|
||
|
||
async def run(
|
||
self,
|
||
*,
|
||
lead: Lead,
|
||
fit_score: FitScore | None = None,
|
||
outcomes: list[str] | None = None,
|
||
start_date: datetime | None = None,
|
||
**_: Any,
|
||
) -> Proposal:
|
||
"""Generate a proposal tailored to the lead context."""
|
||
outcomes = outcomes or [
|
||
"Reduce manual work by 50%+",
|
||
"Increase qualified pipeline by 2–3x",
|
||
"Cut response time from hours to minutes",
|
||
]
|
||
start_date = start_date or (utcnow() + timedelta(days=14))
|
||
|
||
# Determine pricing tier based on region
|
||
budget_min, budget_max, currency = self._pricing_for_region(lead.region)
|
||
|
||
prompt = get_prompt(
|
||
"proposal_generation",
|
||
locale=lead.locale,
|
||
company_name=lead.company_name or "Your Company",
|
||
sector=lead.sector or "General",
|
||
pain_points="; ".join(lead.pain_points) or lead.message or "To be confirmed",
|
||
outcomes="; ".join(outcomes),
|
||
budget_min=f"{budget_min:,.0f}",
|
||
budget_max=f"{budget_max:,.0f}",
|
||
start_date=start_date.strftime("%Y-%m-%d"),
|
||
)
|
||
|
||
response = await self.router.run(
|
||
task=Task.PROPOSAL,
|
||
messages=[Message(role="user", content=prompt)],
|
||
max_tokens=3000,
|
||
temperature=0.5,
|
||
)
|
||
|
||
proposal = Proposal(
|
||
id=generate_id("prop"),
|
||
lead_id=lead.id,
|
||
company_name=lead.company_name,
|
||
sector=lead.sector,
|
||
locale=lead.locale,
|
||
body_markdown=response.content,
|
||
budget_min=budget_min,
|
||
budget_max=budget_max,
|
||
currency=currency,
|
||
valid_until=utcnow() + timedelta(days=30),
|
||
)
|
||
|
||
self.log.info(
|
||
"proposal_generated",
|
||
lead_id=lead.id,
|
||
proposal_id=proposal.id,
|
||
locale=lead.locale,
|
||
budget_range=f"{budget_min:,.0f}-{budget_max:,.0f} {currency}",
|
||
)
|
||
return proposal
|
||
|
||
# ── Pricing logic ───────────────────────────────────────────
|
||
def _pricing_for_region(self, region: str | None) -> tuple[float, float, str]:
|
||
"""Return (setup_min, setup_max, currency) for the lead's region."""
|
||
s = self.settings
|
||
if not region:
|
||
return float(s.pricing_sa_setup_min), float(s.pricing_sa_setup_max), "SAR"
|
||
|
||
region_lower = region.lower()
|
||
gcc_tokens = {"uae", "kuwait", "bahrain", "qatar", "oman", "الإمارات", "الكويت"}
|
||
if any(t in region_lower for t in gcc_tokens):
|
||
return (
|
||
float(s.pricing_gcc_setup_min),
|
||
float(s.pricing_gcc_setup_max),
|
||
"SAR-equivalent",
|
||
)
|
||
sa_tokens = {"saudi", "ksa", "sa", "riyadh", "jeddah", "السعودية"}
|
||
if any(t in region_lower for t in sa_tokens):
|
||
return (float(s.pricing_sa_setup_min), float(s.pricing_sa_setup_max), "SAR")
|
||
# Global
|
||
return (
|
||
float(s.pricing_global_setup_min_usd),
|
||
float(s.pricing_global_setup_max_usd),
|
||
"USD",
|
||
)
|