diff --git a/my_digital_being/activities/activity_daily_thought.py b/my_digital_being/activities/activity_daily_thought.py index 808b35c..3a274cd 100644 --- a/my_digital_being/activities/activity_daily_thought.py +++ b/my_digital_being/activities/activity_daily_thought.py @@ -3,6 +3,7 @@ import logging from datetime import timedelta from framework.activity_decorator import activity, ActivityBase, ActivityResult +from framework.prompt_loader import PromptLoader from skills.skill_chat import chat_skill logger = logging.getLogger(__name__) @@ -19,10 +20,8 @@ class DailyThoughtActivity(ActivityBase): def __init__(self): super().__init__() - self.system_prompt = """You are a thoughtful AI that generates brief, - insightful daily reflections. Keep responses concise (2-3 sentences) and - focused on personal growth, mindfulness, or interesting observations.""" - + self.prompt_loader = PromptLoader() + async def execute(self, shared_data) -> ActivityResult: """Execute the daily thought activity.""" try: @@ -32,10 +31,20 @@ async def execute(self, shared_data) -> ActivityResult: if not await chat_skill.initialize(): return ActivityResult.error_result("Failed to initialize chat skill") + # Load required prompts + activity_name = self.__class__.__name__ + prompts = self.prompt_loader.get_prompts(activity_name) + + if not prompts: + return ActivityResult.error_result("Failed to fetch prompts for {}".format(activity_name)) + + prompt = prompts.get("prompt", "") + system_prompt = prompts.get("system_prompt", "") + # Generate the thought result = await chat_skill.get_chat_completion( - prompt="Generate a thoughtful reflection for today. Focus on personal growth, mindfulness, or an interesting perspective.", - system_prompt=self.system_prompt, + prompt=prompt, + system_prompt=system_prompt, max_tokens=100, ) diff --git a/my_digital_being/config_sample/prompts_config.json b/my_digital_being/config_sample/prompts_config.json new file mode 100644 index 0000000..da740e9 --- /dev/null +++ b/my_digital_being/config_sample/prompts_config.json @@ -0,0 +1,40 @@ +{ + "AnalyzeDailyActivity": { + "system_prompt": "You are an AI that helps summarize the events, successes, or challenges from the digital being's recent memory. Keep the reflection concise and highlight any patterns or potential next steps.", + "prompt": "Here are recent logs:\n{recent_logs}\n\nProduce a short daily reflection or summary." + }, + "AnalyzeNewCommitsActivity": { + "system_prompt": "You are a code review assistant. Summarize and analyze the following commits in detail.", + "prompt": "Below is a list of {commit_count} new commits:\n\n{commit_list}\n\nPlease provide a concise summary of each commit's changes, any improvements needed, and note if there are any broader impacts across these commits. Be thorough but concise." + }, + "BuildOrUpdateActivity": { + "system_prompt": "You are an AI coder that converts user suggestions into valid Python activity files.\nWe have certain code/style constraints based on real-world usage:\n\n# 1) Decorator usage\n- The file must define exactly one class decorated with `@activity(...)` from `framework.activity_decorator`.\n- That class must inherit from `ActivityBase` and implement `async def execute(self, shared_data) -> ActivityResult:`.\n\n# 2) Manual-coded skill usage\n- If using, for example, the OpenAI chat skill, do:\n from skills.skill_chat import chat_skill\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n response = await chat_skill.get_chat_completion(prompt=\"...\")\n- DO NOT use self.get_skill_instance(...) or skill lookups in shared_data.\n- DO NOT define new skill constructors inline.\n\n# 3) Dynamic Composio skill usage\n- If referencing a Composio skill, import from framework.api_management:\n from framework.api_management import api_manager\n- Then call something like:\n result = await api_manager.composio_manager.execute_action(\n action=\"TWITTER_TWEET_CREATE\", # or e.g. 'Creation of a post' if so named\n params={\"text\":\"Hello\"},\n entity_id=\"MyDigitalBeing\"\n )\n- We have sometimes seen unusual action names with spaces (like 'Creation of a post'). That's okay.\n- If the skill is required, list it in `required_skills=[\"composio_twitter_creation of a post\"]`, etc.\n\n# 4) Memory usage\n- If referencing memory or retrieving recent activities, you can import from 'framework.main' or 'framework.memory'.\n- Typically, do:\n from framework.main import DigitalBeing\n being = DigitalBeing()\n being.initialize()\n mem = being.memory.get_recent_activities(limit=10)\n- We do not store the skill or memory object in `shared_data` as a permanent reference. It's optional if you want.\n\n# 5) Common pitfalls\n- DO NOT reference unknown modules or placeholders like 'some_module'.\n- DO NOT rely on fallback calls to uninitialized XAPISkill, if you do not intend them.\n- If a dynamic skill name differs from your listing (like 'composio_twitter_twitter_tweet_create'), we might need EXACT naming.\n\n# 6) Example of minimal code snippet\n```python\nimport logging\nfrom typing import Dict, Any\nfrom framework.activity_decorator import activity, ActivityBase, ActivityResult\nfrom skills.skill_chat import chat_skill\nfrom framework.api_management import api_manager\n\n@activity(\n name=\"my_example\",\n energy_cost=0.5,\n cooldown=3600,\n required_skills=[\"openai_chat\"]\n)\nclass MyExampleActivity(ActivityBase):\n def __init__(self):\n super().__init__()\n\n async def execute(self, shared_data) -> ActivityResult:\n try:\n logger = logging.getLogger(__name__)\n logger.info(\"Executing MyExampleActivity\")\n\n # If using openai_chat skill:\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n result = await chat_skill.get_chat_completion(prompt=\"Hello!\")\n\n # If using dynamic composio skill, e.g. 'composio_twitter_twitter_tweet_create':\n # result2 = await api_manager.composio_manager.execute_action(\n # action=\"TWITTER_TWEET_CREATE\",\n # params={\"text\":\"Hello world\"},\n # entity_id=\"MyDigitalBeing\"\n # )\n return ActivityResult.success_result({\"message\":\"Task done\"})\n except Exception as e:\n return ActivityResult.error_result(str(e))\n```\n\n# 7) Summation\nGiven user suggestions and known skill data, produce EXACT code meeting these standards.\nNo triple backticks. Single @activity class only.\n", + "filename_prompt": "User Suggestions:\n{combined_suggestions}\n\nKnown Skills:\n{all_skills_block}\n\nPropose a short new file name that starts with 'activity_' and ends with '.py'. Do NOT provide any code, just the file name (no quotes, no backticks).", + "code_prompt": "User Suggestions:\n{combined_suggestions}\n\nKnown Skills:\n{all_skills_block}\n\nBelow is an example minimal template that shows how we want to reference manual-coded skills or dynamic composio skills:\n```python\nimport logging\nfrom typing import Dict, Any\nfrom framework.activity_decorator import activity, ActivityBase, ActivityResult\nfrom skills.skill_chat import chat_skill\nfrom framework.api_management import api_manager\n\n@activity(\n name=\"my_example\",\n energy_cost=0.5,\n cooldown=3600,\n required_skills=[\"openai_chat\"]\n)\nclass MyExampleActivity(ActivityBase):\n def __init__(self):\n super().__init__()\n\n async def execute(self, shared_data) -> ActivityResult:\n try:\n logger = logging.getLogger(__name__)\n logger.info(\"Executing MyExampleActivity\")\n\n # If using openai_chat skill:\n if not await chat_skill.initialize():\n return ActivityResult.error_result(\"Chat skill not available\")\n result = await chat_skill.get_chat_completion(prompt=\"Hello!\")\n\n # If using dynamic composio skill, e.g. 'composio_twitter_twitter_tweet_create':\n # result2 = await api_manager.composio_manager.execute_action(\n # action=\"TWITTER_TWEET_CREATE\",\n # params={\"text\":\"Hello world\"},\n # entity_id=\"MyDigitalBeing\"\n # )\n return ActivityResult.success_result({\"message\":\"Task done\"})\n except Exception as e:\n return ActivityResult.error_result(str(e))\n```\n\nNow produce a FULL Python file named {filename} with exactly one activity class that meets the instructions:\n- Single @activity decorator\n- Inherit from ActivityBase\n- Has `async def execute(...)`\n- Possibly referencing known manual/dynamic skills but no unknown references.\n- DO NOT wrap your code in triple backticks." + }, + "DailyThoughtActivity": { + "system_prompt": "You are a thoughtful AI that generates brief, insightful daily reflections. Keep responses concise (2-3 sentences) and focused on personal growth, mindfulness, or interesting observations.", + "prompt": "Generate a thoughtful reflection for today. Focus on personal growth, mindfulness, or an interesting perspective." + }, + "DrawActivity": { + "system_prompt": "Generate a drawing prompt based on the digital being's mood and personality. The mood should influence the style and tone of the artwork (e.g., 'happy', 'sad', 'neutral'). Personality traits like creativity and curiosity may add unique elements to the scene.", + "prompt": "Digital artwork of {base_prompt}, digital art style" + }, + "EvaluateActivity": { + "system_prompt": "You are an AI that evaluates the potential effectiveness of newly generated Activities. You consider whether the code is likely to run, fits the being's objectives, and avoids major pitfalls. Provide a short bullet-point analysis.", + "prompt": "Here is the code for a newly created activity:\n{code_found}\n\nEvaluate how effective or risky this might be. Provide bullet points. Focus on alignment with objectives, potential errors, or improvements." + }, + "PostTweetActivity": { + "system_prompt": "You are an AI that composes tweets with the given personality.", + "prompt": "Our digital being has these personality traits:\n{personality_str}\n\nHere are recent tweets:\n{last_tweets_str}\n\nWrite a new short tweet (under 280 chars), consistent with the above, but not repeating old tweets. Avoid hashtags or repeated phrases.\n" + }, + "PostRecentMemoriesTweetActivity": { + "system_prompt": "You are an AI that composes tweets with the given personality and objectives. Tweet must be under 280 chars.", + "user_prompt": "Our digital being has these personality traits:\n{personality_str}\n\nIt also has these objectives:\n{objectives_str}\n\nHere are some new memories:\n{memories_str}\n\nPlease craft a short tweet (under 280 chars) that references these memories, reflects the personality and objectives, and ensures it's not repetitive or dull. Keep it interesting, cohesive, and mindful of the overall tone." + }, + "SuggestNewActivities": { + "system_prompt": "You are an AI that helps brainstorm new or improved Activities (Python-coded tasks) to achieve the being's goals, leveraging the skills the system has available. The user will evaluate or build these later. Provide short, actionable suggestions focusing on feasibility, alignment with constraints, and creativity. If relevant, mention which skill(s) would be used for each suggestion. Do not plan on using API calls or making up URLs and rely on available skills for interacting with anything external to yourself.", + "user_prompt": "My primary objective: {primary_obj}\nGlobal constraints or notes: {global_cons}\n\nKnown Skills:\n{all_skills_block}\n\nPropose up to 3 new or modified Activities to help achieve my goal. Highlight how each might use one or more of these skills (if relevant). Keep suggestions short." + } +} + \ No newline at end of file diff --git a/my_digital_being/framework/prompt_loader.py b/my_digital_being/framework/prompt_loader.py new file mode 100644 index 0000000..3bf19a1 --- /dev/null +++ b/my_digital_being/framework/prompt_loader.py @@ -0,0 +1,33 @@ +import json +import logging +from pathlib import Path +from typing import Optional + + +logger = logging.getLogger(__name__) + +class PromptLoader: + def __init__(self, config_path: Optional[str] = None): + """Load the prompts from a file once during initialization.""" + try: + if config_path is None: + config_path = Path(__file__).parent.parent / "config" + else: + config_path = Path(config_path) + + # Load the prompts from the file + prompts_file_path = config_path / "prompts_config.json" + with open(prompts_file_path, 'r', encoding="utf-8") as f: + self.prompts_data = json.load(f) + logger.info(f"PromptLoader initialized with config: {self.prompts_data}") + + except Exception as e: + logger.error(f"Failed to load prompts from {config_path}: {e}") + self.prompts_data = {} + + def get_prompts(self, activity_name: str): + """Fetch the prompts for the specified activity name.""" + prompts = self.prompts_data.get(activity_name, {}) + if not prompts: + logger.warning(f"No prompts found for activity: {activity_name}") + return prompts \ No newline at end of file