new site
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
"""
|
||||
Claude Haiku service for content generation.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from anthropic import Anthropic
|
||||
|
||||
from ..config import get_settings
|
||||
from ..models.schemas import HaikuTagsResponse, HaikuContentResponse, TagCategory
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HaikuService:
|
||||
"""Service for AI content generation using Claude Haiku."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Anthropic client."""
|
||||
settings = get_settings()
|
||||
self.client = Anthropic(api_key=settings.anthropic_api_key)
|
||||
self.model = "claude-3-5-haiku-20241022"
|
||||
|
||||
def _call_haiku(
|
||||
self,
|
||||
system_prompt: str,
|
||||
user_prompt: str,
|
||||
max_tokens: int = 4096,
|
||||
) -> str:
|
||||
"""
|
||||
Make a call to Claude Haiku.
|
||||
|
||||
Args:
|
||||
system_prompt: System instructions
|
||||
user_prompt: User message
|
||||
max_tokens: Maximum response tokens
|
||||
|
||||
Returns:
|
||||
Response text
|
||||
"""
|
||||
response = self.client.messages.create(
|
||||
model=self.model,
|
||||
max_tokens=max_tokens,
|
||||
system=system_prompt,
|
||||
messages=[{"role": "user", "content": user_prompt}],
|
||||
)
|
||||
return response.content[0].text
|
||||
|
||||
def extract_tags(
|
||||
self,
|
||||
company_name: str,
|
||||
website_markdown: Optional[str],
|
||||
address: Optional[str] = None,
|
||||
) -> HaikuTagsResponse:
|
||||
"""
|
||||
Extract tags from company information.
|
||||
|
||||
Args:
|
||||
company_name: Company name
|
||||
website_markdown: Scraped website content
|
||||
address: Company address
|
||||
|
||||
Returns:
|
||||
Extracted tags with confidence
|
||||
"""
|
||||
system_prompt = """You are an expert at analyzing Odoo partner companies.
|
||||
Extract relevant tags from the provided company information.
|
||||
|
||||
Respond with valid JSON only, no other text. Use this exact structure:
|
||||
{
|
||||
"services": ["list of services they offer"],
|
||||
"industries": ["list of industries they serve"],
|
||||
"modules": ["list of Odoo modules they specialize in"],
|
||||
"partner_level": "Ready Partner|Silver Partner|Gold Partner|Platinum Partner or null",
|
||||
"confidence": 0.0 to 1.0
|
||||
}
|
||||
|
||||
Valid services: Implementation, Customization, Training, Support, Migration, Integration, Consulting, Hosting, Development
|
||||
Valid industries: Manufacturing, Retail, E-commerce, Healthcare, Education, Finance, Logistics, Construction, Food & Beverage, Professional Services
|
||||
Valid modules: Sales, CRM, Inventory, Accounting, Manufacturing, Website, E-commerce, HR, Project, Purchase, Point of Sale
|
||||
|
||||
Only include tags you can confidently identify from the content."""
|
||||
|
||||
user_prompt = f"""Company: {company_name}
|
||||
Address: {address or "Not provided"}
|
||||
|
||||
Website content:
|
||||
{website_markdown[:8000] if website_markdown else "No website content available"}
|
||||
|
||||
Extract tags for this Odoo partner company."""
|
||||
|
||||
try:
|
||||
response = self._call_haiku(system_prompt, user_prompt, max_tokens=1024)
|
||||
# Parse JSON response
|
||||
data = json.loads(response)
|
||||
return HaikuTagsResponse(**data)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse tags response: {e}")
|
||||
return HaikuTagsResponse(confidence=0.0)
|
||||
except Exception as e:
|
||||
logger.error(f"Tag extraction failed: {e}")
|
||||
return HaikuTagsResponse(confidence=0.0)
|
||||
|
||||
def generate_company_content(
|
||||
self,
|
||||
company_name: str,
|
||||
city_name: str,
|
||||
country_name: str,
|
||||
website_markdown: Optional[str],
|
||||
services: List[str],
|
||||
industries: List[str],
|
||||
modules: List[str],
|
||||
partner_level: Optional[str] = None,
|
||||
language: str = "de",
|
||||
) -> HaikuContentResponse:
|
||||
"""
|
||||
Generate SEO-optimized company page content.
|
||||
|
||||
Args:
|
||||
company_name: Company name
|
||||
city_name: City name
|
||||
country_name: Country name
|
||||
website_markdown: Scraped website content
|
||||
services: List of service tags
|
||||
industries: List of industry tags
|
||||
modules: List of module tags
|
||||
partner_level: Partner certification level
|
||||
language: Output language code
|
||||
|
||||
Returns:
|
||||
Generated content
|
||||
"""
|
||||
lang_instructions = {
|
||||
"de": "Write all content in German (Deutsch).",
|
||||
"en": "Write all content in English.",
|
||||
"es": "Write all content in Spanish (Español).",
|
||||
"fr": "Write all content in French (Français).",
|
||||
"pt": "Write all content in Portuguese (Português).",
|
||||
"ar": "Write all content in Arabic (العربية).",
|
||||
}
|
||||
|
||||
system_prompt = f"""You are an expert SEO content writer for Odoo partner directories.
|
||||
{lang_instructions.get(language, lang_instructions["en"])}
|
||||
|
||||
Create engaging, informative content about an Odoo partner company.
|
||||
The content should be at least 800 words and SEO-optimized.
|
||||
|
||||
Respond with valid JSON only:
|
||||
{{
|
||||
"description": "2-3 sentence company description",
|
||||
"content_html": "Full HTML content with headings, paragraphs, lists",
|
||||
"meta_title": "SEO title (50-60 chars)",
|
||||
"meta_description": "SEO meta description (150-160 chars)"
|
||||
}}
|
||||
|
||||
Include:
|
||||
- Company overview
|
||||
- Services offered
|
||||
- Industries served
|
||||
- Odoo modules expertise
|
||||
- Why choose this partner
|
||||
- Location information
|
||||
|
||||
Use semantic HTML: <h2>, <h3>, <p>, <ul>, <li>, <strong>"""
|
||||
|
||||
tags_info = f"""
|
||||
Services: {", ".join(services) if services else "Various Odoo services"}
|
||||
Industries: {", ".join(industries) if industries else "Multiple industries"}
|
||||
Modules: {", ".join(modules) if modules else "Various Odoo modules"}
|
||||
Partner Level: {partner_level or "Odoo Partner"}"""
|
||||
|
||||
user_prompt = f"""Create content for:
|
||||
|
||||
Company: {company_name}
|
||||
Location: {city_name}, {country_name}
|
||||
{tags_info}
|
||||
|
||||
Website information:
|
||||
{website_markdown[:6000] if website_markdown else "Limited information available"}
|
||||
|
||||
Generate comprehensive SEO content for this Odoo partner's directory page."""
|
||||
|
||||
try:
|
||||
response = self._call_haiku(system_prompt, user_prompt, max_tokens=4096)
|
||||
data = json.loads(response)
|
||||
return HaikuContentResponse(**data)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse content response: {e}")
|
||||
# Return fallback content
|
||||
return self._generate_fallback_content(
|
||||
company_name, city_name, country_name, services, language
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Content generation failed: {e}")
|
||||
return self._generate_fallback_content(
|
||||
company_name, city_name, country_name, services, language
|
||||
)
|
||||
|
||||
def _generate_fallback_content(
|
||||
self,
|
||||
company_name: str,
|
||||
city_name: str,
|
||||
country_name: str,
|
||||
services: List[str],
|
||||
language: str,
|
||||
) -> HaikuContentResponse:
|
||||
"""Generate basic fallback content when AI generation fails."""
|
||||
services_text = ", ".join(services) if services else "Odoo implementation"
|
||||
|
||||
if language == "de":
|
||||
description = f"{company_name} ist ein Odoo Partner in {city_name}, {country_name}."
|
||||
content = f"""<h2>Über {company_name}</h2>
|
||||
<p>{company_name} ist ein zertifizierter Odoo Partner mit Sitz in {city_name}.
|
||||
Das Unternehmen bietet professionelle Odoo-Dienstleistungen für Unternehmen in der Region.</p>
|
||||
|
||||
<h3>Dienstleistungen</h3>
|
||||
<p>Als Odoo Partner bietet {company_name} folgende Leistungen: {services_text}.</p>
|
||||
|
||||
<h3>Standort</h3>
|
||||
<p>{company_name} befindet sich in {city_name}, {country_name} und betreut Kunden in der gesamten Region.</p>"""
|
||||
meta_title = f"{company_name} - Odoo Partner in {city_name}"
|
||||
meta_desc = f"{company_name} ist Ihr Odoo Partner in {city_name}. Professionelle ERP-Beratung und Implementierung."
|
||||
else:
|
||||
description = f"{company_name} is an Odoo partner located in {city_name}, {country_name}."
|
||||
content = f"""<h2>About {company_name}</h2>
|
||||
<p>{company_name} is a certified Odoo partner based in {city_name}.
|
||||
The company provides professional Odoo services for businesses in the region.</p>
|
||||
|
||||
<h3>Services</h3>
|
||||
<p>As an Odoo partner, {company_name} offers: {services_text}.</p>
|
||||
|
||||
<h3>Location</h3>
|
||||
<p>{company_name} is located in {city_name}, {country_name} and serves clients throughout the region.</p>"""
|
||||
meta_title = f"{company_name} - Odoo Partner in {city_name}"
|
||||
meta_desc = f"{company_name} is your Odoo partner in {city_name}. Professional ERP consulting and implementation."
|
||||
|
||||
return HaikuContentResponse(
|
||||
description=description,
|
||||
content_html=content,
|
||||
meta_title=meta_title[:60],
|
||||
meta_description=meta_desc[:160],
|
||||
)
|
||||
|
||||
def generate_city_hub_content(
|
||||
self,
|
||||
city_name: str,
|
||||
country_name: str,
|
||||
company_count: int,
|
||||
top_services: List[str],
|
||||
language: str = "de",
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Generate city hub page content.
|
||||
|
||||
Args:
|
||||
city_name: City name
|
||||
country_name: Country name
|
||||
company_count: Number of companies in city
|
||||
top_services: Most common services
|
||||
language: Output language
|
||||
|
||||
Returns:
|
||||
Dictionary with content sections
|
||||
"""
|
||||
lang_instructions = {
|
||||
"de": "Write in German.",
|
||||
"en": "Write in English.",
|
||||
}
|
||||
|
||||
system_prompt = f"""You are an SEO expert writing city hub pages for an Odoo partner directory.
|
||||
{lang_instructions.get(language, "Write in English.")}
|
||||
|
||||
Create compelling content (1000+ words) about finding Odoo partners in this city.
|
||||
|
||||
Return valid JSON:
|
||||
{{
|
||||
"intro": "Introduction paragraph about Odoo partners in this city",
|
||||
"content_html": "Full HTML content about Odoo services in this city",
|
||||
"meta_title": "SEO title (50-60 chars)",
|
||||
"meta_description": "Meta description (150-160 chars)"
|
||||
}}"""
|
||||
|
||||
user_prompt = f"""City: {city_name}, {country_name}
|
||||
Number of Odoo Partners: {company_count}
|
||||
Top Services: {", ".join(top_services)}
|
||||
|
||||
Create engaging city hub content for this Odoo partner directory."""
|
||||
|
||||
try:
|
||||
response = self._call_haiku(system_prompt, user_prompt)
|
||||
return json.loads(response)
|
||||
except Exception as e:
|
||||
logger.error(f"City hub content generation failed: {e}")
|
||||
return {
|
||||
"intro": f"Find Odoo partners in {city_name}.",
|
||||
"content_html": f"<p>Discover {company_count} Odoo partners in {city_name}, {country_name}.</p>",
|
||||
"meta_title": f"Odoo Partners in {city_name}",
|
||||
"meta_description": f"Find {company_count} certified Odoo partners in {city_name}, {country_name}.",
|
||||
}
|
||||
|
||||
def generate_country_hub_content(
|
||||
self,
|
||||
country_name: str,
|
||||
city_count: int,
|
||||
company_count: int,
|
||||
top_cities: List[str],
|
||||
language: str = "de",
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Generate country hub page content.
|
||||
|
||||
Args:
|
||||
country_name: Country name
|
||||
city_count: Number of cities with partners
|
||||
company_count: Total companies in country
|
||||
top_cities: Cities with most partners
|
||||
language: Output language
|
||||
|
||||
Returns:
|
||||
Dictionary with content sections
|
||||
"""
|
||||
lang_instructions = {
|
||||
"de": "Write in German.",
|
||||
"en": "Write in English.",
|
||||
}
|
||||
|
||||
system_prompt = f"""You are an SEO expert writing country hub pages for an Odoo partner directory.
|
||||
{lang_instructions.get(language, "Write in English.")}
|
||||
|
||||
Create comprehensive content (1200+ words) about Odoo partners in this country.
|
||||
|
||||
Return valid JSON:
|
||||
{{
|
||||
"intro": "Introduction about Odoo partners in this country",
|
||||
"content_html": "Full HTML content about the Odoo ecosystem in this country",
|
||||
"meta_title": "SEO title (50-60 chars)",
|
||||
"meta_description": "Meta description (150-160 chars)"
|
||||
}}"""
|
||||
|
||||
user_prompt = f"""Country: {country_name}
|
||||
Number of Cities: {city_count}
|
||||
Total Odoo Partners: {company_count}
|
||||
Top Cities: {", ".join(top_cities)}
|
||||
|
||||
Create engaging country hub content for this Odoo partner directory."""
|
||||
|
||||
try:
|
||||
response = self._call_haiku(system_prompt, user_prompt)
|
||||
return json.loads(response)
|
||||
except Exception as e:
|
||||
logger.error(f"Country hub content generation failed: {e}")
|
||||
return {
|
||||
"intro": f"Find Odoo partners across {country_name}.",
|
||||
"content_html": f"<p>Discover {company_count} Odoo partners in {city_count} cities across {country_name}.</p>",
|
||||
"meta_title": f"Odoo Partners in {country_name}",
|
||||
"meta_description": f"Find {company_count} certified Odoo partners in {city_count} cities across {country_name}.",
|
||||
}
|
||||
Reference in New Issue
Block a user