Skip to main content
Glama
ai_service.py12.4 kB
"""AI service for generating content and analysis.""" from typing import List, Optional, Dict, Any import openai import json from ..config import settings from ..utils.logger import get_logger from ..database.models import Character, CharacterFact, CharacterRelation logger = get_logger(__name__) class AIService: """Service for AI-powered content generation and analysis.""" def __init__(self): self.client = openai.AsyncOpenAI(api_key=settings.openai_api_key) self.model = settings.openai_model async def generate_character_tags( self, character: Character, facts: Optional[List[CharacterFact]] = None ) -> List[str]: """Generate tags for a character using AI. Args: character: Character to generate tags for facts: Optional list of character facts for context Returns: List of generated tags """ try: logger.info(f"Generating tags for character: {character.name}") # Build context from character and facts context = f"Character: {character.name}\nDescription: {character.description}" if facts: context += "\n\nFacts:" for fact in facts: context += f"\n- {fact.fact_type}: {fact.content}" prompt = f""" Analyze the following character information and generate relevant tags that describe their key attributes, personality traits, roles, and characteristics. {context} Generate 5-10 concise, relevant tags for this character. Return only a JSON array of strings. Example format: ["warrior", "brave", "loyal", "leader", "noble"] """ response = await self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": "You are a helpful assistant that generates character tags for creative writing. Return only valid JSON arrays."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=200 ) content = response.choices[0].message.content.strip() # Parse JSON response try: tags = json.loads(content) if isinstance(tags, list) and all(isinstance(tag, str) for tag in tags): logger.info(f"Generated {len(tags)} tags for character {character.name}") return tags else: logger.warning(f"Invalid tag format received: {content}") return [] except json.JSONDecodeError: logger.warning(f"Failed to parse JSON response: {content}") return [] except Exception as e: logger.error(f"Failed to generate tags for character {character.name}: {e}") return [] async def analyze_character_relationships( self, characters: List[Character], existing_relations: Optional[List[CharacterRelation]] = None ) -> List[Dict[str, Any]]: """Analyze relationships between characters using AI. Args: characters: List of characters to analyze existing_relations: Optional list of existing relationships Returns: List of relationship analysis results """ try: logger.info(f"Analyzing relationships between {len(characters)} characters") if len(characters) < 2: logger.warning("Need at least 2 characters for relationship analysis") return [] # Build character context character_info = [] for char in characters: character_info.append(f"ID {char.id}: {char.name} - {char.description}") context = "\n".join(character_info) # Include existing relationships if available existing_context = "" if existing_relations: existing_context = "\n\nExisting relationships:" for rel in existing_relations: existing_context += f"\n- Character {rel.character_a_id} and {rel.character_b_id}: {rel.relation_type} ({rel.description})" prompt = f""" Analyze the relationships between these characters and suggest potential new relationships or improvements to existing ones: {context}{existing_context} For each potential relationship, provide: 1. The two character IDs involved 2. The type of relationship (e.g., "friend", "rival", "mentor", "family", "romantic", etc.) 3. A brief description of the relationship 4. A strength score from 0.0 to 1.0 (how strong/important the relationship is) Return the results as a JSON array of objects with the following structure: [ {{ "character_a_id": 1, "character_b_id": 2, "relation_type": "friend", "description": "Close childhood friends who trust each other completely", "strength": 0.8 }} ] Only suggest meaningful relationships that make sense based on the character descriptions. """ response = await self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": "You are a helpful assistant that analyzes character relationships for creative writing. Return only valid JSON arrays."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=1000 ) content = response.choices[0].message.content.strip() # Parse JSON response try: relationships = json.loads(content) if isinstance(relationships, list): # Validate each relationship object valid_relationships = [] for rel in relationships: if ( isinstance(rel, dict) and "character_a_id" in rel and "character_b_id" in rel and "relation_type" in rel and "description" in rel and "strength" in rel ): valid_relationships.append(rel) logger.info(f"Generated {len(valid_relationships)} relationship suggestions") return valid_relationships else: logger.warning(f"Invalid relationship format received: {content}") return [] except json.JSONDecodeError: logger.warning(f"Failed to parse JSON response: {content}") return [] except Exception as e: logger.error(f"Failed to analyze character relationships: {e}") return [] async def generate_character_summary( self, character: Character, facts: Optional[List[CharacterFact]] = None, relations: Optional[List[CharacterRelation]] = None ) -> Optional[str]: """Generate a comprehensive summary of a character. Args: character: Character to summarize facts: Optional list of character facts relations: Optional list of character relationships Returns: Generated character summary or None if generation fails """ try: logger.info(f"Generating summary for character: {character.name}") # Build context context = f"Character: {character.name}\nDescription: {character.description}" if character.tags: context += f"\nTags: {', '.join(character.tags)}" if facts: context += "\n\nFacts:" for fact in facts: context += f"\n- {fact.fact_type}: {fact.content}" if relations: context += "\n\nRelationships:" for rel in relations: context += f"\n- {rel.relation_type}: {rel.description}" prompt = f""" Create a comprehensive character summary based on the following information: {context} Write a well-structured summary that captures the character's essence, key traits, background, and relationships. The summary should be engaging and useful for creative writing purposes. """ response = await self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": "You are a helpful assistant that creates character summaries for creative writing."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=500 ) summary = response.choices[0].message.content.strip() logger.info(f"Generated summary for character {character.name}") return summary except Exception as e: logger.error(f"Failed to generate summary for character {character.name}: {e}") return None async def suggest_character_development( self, character: Character, context: Optional[str] = None ) -> List[str]: """Suggest character development ideas. Args: character: Character to develop context: Optional story context Returns: List of development suggestions """ try: logger.info(f"Generating development suggestions for character: {character.name}") character_context = f"Character: {character.name}\nDescription: {character.description}" if character.tags: character_context += f"\nTags: {', '.join(character.tags)}" story_context = f"\n\nStory context: {context}" if context else "" prompt = f""" Suggest character development ideas for the following character: {character_context}{story_context} Provide 5-8 specific, actionable development suggestions that could help deepen this character. Consider: - Character growth arcs - Potential conflicts or challenges - Skill development - Relationship dynamics - Backstory elements to explore Return the suggestions as a JSON array of strings. Example format: ["Explore their fear of commitment through a romantic subplot", "Develop their magical abilities through training with a mentor"] """ response = await self.client.chat.completions.create( model=self.model, messages=[ {"role": "system", "content": "You are a helpful assistant that provides character development suggestions for creative writing. Return only valid JSON arrays."}, {"role": "user", "content": prompt} ], temperature=0.8, max_tokens=400 ) content = response.choices[0].message.content.strip() # Parse JSON response try: suggestions = json.loads(content) if isinstance(suggestions, list) and all(isinstance(s, str) for s in suggestions): logger.info(f"Generated {len(suggestions)} development suggestions") return suggestions else: logger.warning(f"Invalid suggestion format received: {content}") return [] except json.JSONDecodeError: logger.warning(f"Failed to parse JSON response: {content}") return [] except Exception as e: logger.error(f"Failed to generate development suggestions: {e}") return []

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/huangjien/writer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server