360 lines
13 KiB
Python
360 lines
13 KiB
Python
"""
|
|
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}.",
|
|
}
|