userIdc2024's picture
Upload 16 files
de08998 verified
"""
Claude-based researcher implementation.
Uses output_config.format for native structured outputs.
Ref: https://platform.claude.com/docs/en/build-with-claude/structured-outputs
"""
import json
from anthropic import Anthropic
from backend.pydantic_schema import ImageAdEssentialsOutput
from backend.prompt import get_system_prompt, get_user_prompt
from dotenv import load_dotenv
import os
load_dotenv()
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
def _add_additional_properties_false(schema: dict) -> dict:
"""
Recursively add 'additionalProperties': false to all object types.
Required by Claude's structured outputs.
"""
if isinstance(schema, dict):
if schema.get("type") == "object":
schema["additionalProperties"] = False
for value in schema.values():
if isinstance(value, dict):
_add_additional_properties_false(value)
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
_add_additional_properties_false(item)
return schema
def researcher_claude(target_audience: str, product_category: str, product_description: str):
"""
Claude-based researcher function using native structured outputs.
Args:
target_audience: Target audience from the predefined list
product_category: Product category (e.g., "ring", "bangles")
product_description: Description of the product
Returns:
list[ImageAdEssentials]: List of psychology triggers, angles, and concepts
"""
# Initialize Claude client
claude_client = Anthropic(api_key=ANTHROPIC_API_KEY)
# Get prompts
system_prompt = get_system_prompt()
user_prompt = get_user_prompt(target_audience, product_category, product_description)
# Build JSON schema from Pydantic model and add required additionalProperties: false
json_schema = ImageAdEssentialsOutput.model_json_schema()
json_schema = _add_additional_properties_false(json_schema)
# Use Claude's native structured outputs via output_config.format
message = claude_client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=system_prompt,
messages=[
{
"role": "user",
"content": user_prompt
}
],
output_config={
"format": {
"type": "json_schema",
"schema": json_schema
}
}
)
# Check for safety refusal
if message.stop_reason == "refusal":
raise ValueError("Claude refused the request.")
# Check if response was truncated
if message.stop_reason == "max_tokens":
raise ValueError("Claude response was truncated — increase max_tokens.")
# Parse the JSON response and validate with Pydantic
response_data = json.loads(message.content[0].text)
output = ImageAdEssentialsOutput(**response_data)
return output.output