Compare commits
2 Commits
a2f909db41
...
03dba966e5
| Author | SHA1 | Date |
|---|---|---|
|
|
03dba966e5 | |
|
|
61322b1baa |
|
|
@ -31,6 +31,9 @@ class Config:
|
||||||
# Template settings
|
# Template settings
|
||||||
templates_dir: str = "templates"
|
templates_dir: str = "templates"
|
||||||
|
|
||||||
|
# Ollama settings
|
||||||
|
strip_think_tags: bool = True
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
"""Load configuration from file if exists."""
|
"""Load configuration from file if exists."""
|
||||||
config_file = Path("config/config.json")
|
config_file = Path("config/config.json")
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@
|
||||||
"silent_start": "20:00",
|
"silent_start": "20:00",
|
||||||
"silent_end": "07:00",
|
"silent_end": "07:00",
|
||||||
"timezone": "Asia/Shanghai",
|
"timezone": "Asia/Shanghai",
|
||||||
"min_interval": 3,
|
"min_interval": 1,
|
||||||
"max_interval": 180,
|
"max_interval": 3,
|
||||||
"bark_api_url": "https://bark.xorbitlab.xyz",
|
"bark_api_url": "https://bark.xorbitlab.xyz",
|
||||||
"bark_device_key": "GmFrzGegxFexhNXXXYdzm3",
|
"bark_device_key": "GmFrzGegxFexhNXXXYdzm3",
|
||||||
"ntfy_api_url": "https://ntfy.xorbitlab.xyz",
|
"ntfy_api_url": "https://ntfy.xorbitlab.xyz",
|
||||||
"ntfy_topic": "tigeren_msg",
|
"ntfy_topic": "tigeren_msg",
|
||||||
"ntfy_access_token": "",
|
"ntfy_access_token": "",
|
||||||
"templates_dir": "templates"
|
"templates_dir": "templates",
|
||||||
|
"strip_think_tags": true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,13 @@ class OllamaClient:
|
||||||
if self.session:
|
if self.session:
|
||||||
await self.session.close()
|
await self.session.close()
|
||||||
|
|
||||||
async def generate_response(self, prompt: str) -> Optional[str]:
|
async def generate_response(self, prompt: str, strip_think_tags: bool = True) -> Optional[str]:
|
||||||
"""Generate a response from Ollama for the given prompt."""
|
"""Generate a response from Ollama for the given prompt.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prompt: The input prompt for the model
|
||||||
|
strip_think_tags: If True, removes <think></think> tags from the response
|
||||||
|
"""
|
||||||
if not self.session:
|
if not self.session:
|
||||||
self.session = aiohttp.ClientSession()
|
self.session = aiohttp.ClientSession()
|
||||||
|
|
||||||
|
|
@ -37,7 +42,20 @@ class OllamaClient:
|
||||||
async with self.session.post(url, json=payload) as response:
|
async with self.session.post(url, json=payload) as response:
|
||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
return data.get("response", "").strip()
|
response_text = data.get("response", "").strip()
|
||||||
|
|
||||||
|
if strip_think_tags:
|
||||||
|
# Remove <think></think> tags and their content
|
||||||
|
import re
|
||||||
|
original_length = len(response_text)
|
||||||
|
response_text = re.sub(r'<think>.*?</think>', '', response_text, flags=re.DOTALL)
|
||||||
|
response_text = response_text.strip()
|
||||||
|
final_length = len(response_text)
|
||||||
|
|
||||||
|
if original_length != final_length:
|
||||||
|
logger.info(f"Stripped <think></think> tags from response (reduced length by {original_length - final_length} characters)")
|
||||||
|
|
||||||
|
return response_text
|
||||||
else:
|
else:
|
||||||
logger.error(f"Ollama API error: {response.status}")
|
logger.error(f"Ollama API error: {response.status}")
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,22 @@ async def test_full_pipeline():
|
||||||
|
|
||||||
# Test template system
|
# Test template system
|
||||||
template_manager = TemplateManager()
|
template_manager = TemplateManager()
|
||||||
prompt = template_manager.get_random_prompt()
|
template_data = template_manager.get_random_prompt()
|
||||||
|
prompt = template_data["prompt"]
|
||||||
|
title = template_data["title"]
|
||||||
print(f"📋 Selected template: {prompt}")
|
print(f"📋 Selected template: {prompt}")
|
||||||
|
print(f"📋 Notification title: {title}")
|
||||||
|
|
||||||
# Test Ollama response
|
# Test Ollama response
|
||||||
async with OllamaClient(config) as ollama_client:
|
async with OllamaClient(config) as ollama_client:
|
||||||
response = await ollama_client.generate_response(prompt)
|
strip_think_tags = getattr(config, 'strip_think_tags', True)
|
||||||
|
response = await ollama_client.generate_response(prompt, strip_think_tags=strip_think_tags)
|
||||||
if response:
|
if response:
|
||||||
print(f"🤖 Ollama response: {response[:200]}...")
|
print(f"🤖 Ollama response: {response[:200]}...")
|
||||||
|
|
||||||
# Test notification (will skip if no device key/topic)
|
# Test notification (will skip if no device key/topic)
|
||||||
async with NotificationClient(config) as notification_client:
|
async with NotificationClient(config) as notification_client:
|
||||||
success = await notification_client.send_notification(response)
|
success = await notification_client.send_notification(response, title=title)
|
||||||
if success:
|
if success:
|
||||||
print("✅ Notification pipeline working!")
|
print("✅ Notification pipeline working!")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
13
scheduler.py
13
scheduler.py
|
|
@ -15,7 +15,8 @@ class NotificationScheduler:
|
||||||
|
|
||||||
def __init__(self, config: Config):
|
def __init__(self, config: Config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.template_manager = TemplateManager(config.templates_dir)
|
timezone_name = getattr(config, 'timezone', 'UTC')
|
||||||
|
self.template_manager = TemplateManager(config.templates_dir, timezone_name)
|
||||||
self.notification_client = NotificationClient(config)
|
self.notification_client = NotificationClient(config)
|
||||||
self.running = False
|
self.running = False
|
||||||
self._timezone = None
|
self._timezone = None
|
||||||
|
|
@ -76,17 +77,21 @@ class NotificationScheduler:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Get random prompt
|
# Get random prompt
|
||||||
prompt = self.template_manager.get_random_prompt()
|
template_data = self.template_manager.get_random_prompt()
|
||||||
|
prompt = template_data["prompt"]
|
||||||
|
title = template_data["title"]
|
||||||
logger.info(f"Using prompt: {prompt}")
|
logger.info(f"Using prompt: {prompt}")
|
||||||
|
logger.info(f"Notification title: {title}")
|
||||||
|
|
||||||
# Get response from Ollama
|
# Get response from Ollama
|
||||||
response = await client.generate_response(prompt)
|
strip_think_tags = getattr(self.config, 'strip_think_tags', True)
|
||||||
|
response = await client.generate_response(prompt, strip_think_tags=strip_think_tags)
|
||||||
if not response:
|
if not response:
|
||||||
logger.error("Failed to get response from Ollama")
|
logger.error("Failed to get response from Ollama")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Send notification
|
# Send notification
|
||||||
success = await self.notification_client.send_notification(response)
|
success = await self.notification_client.send_notification(response, title=title)
|
||||||
if success:
|
if success:
|
||||||
logger.info("Notification sent successfully")
|
logger.info("Notification sent successfully")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,30 @@ logger = logging.getLogger(__name__)
|
||||||
class TemplateManager:
|
class TemplateManager:
|
||||||
"""Manages prompt templates with placeholders and random selection."""
|
"""Manages prompt templates with placeholders and random selection."""
|
||||||
|
|
||||||
def __init__(self, templates_dir: str = "templates"):
|
def __init__(self, templates_dir: str = "templates", timezone_name: str = "UTC"):
|
||||||
self.templates_dir = Path(templates_dir)
|
self.templates_dir = Path(templates_dir)
|
||||||
self.templates_dir.mkdir(exist_ok=True)
|
self.templates_dir.mkdir(exist_ok=True)
|
||||||
self.templates = []
|
self.templates = []
|
||||||
|
self._timezone = None
|
||||||
|
self._timezone_name = timezone_name
|
||||||
|
self._init_timezone()
|
||||||
self._load_templates()
|
self._load_templates()
|
||||||
|
|
||||||
|
def _init_timezone(self):
|
||||||
|
"""Initialize timezone."""
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._timezone = pytz.timezone(self._timezone_name)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Unknown timezone '{self._timezone_name}', falling back to UTC: {e}")
|
||||||
|
self._timezone = pytz.UTC
|
||||||
|
self._timezone_name = "UTC"
|
||||||
|
|
||||||
|
def _get_current_time(self):
|
||||||
|
"""Get current time in configured timezone."""
|
||||||
|
return datetime.now(self._timezone)
|
||||||
|
|
||||||
def _load_templates(self):
|
def _load_templates(self):
|
||||||
"""Load templates from JSON files in the templates directory."""
|
"""Load templates from JSON files in the templates directory."""
|
||||||
if not self.templates_dir.exists():
|
if not self.templates_dir.exists():
|
||||||
|
|
@ -40,16 +58,19 @@ class TemplateManager:
|
||||||
default_templates = [
|
default_templates = [
|
||||||
{
|
{
|
||||||
"name": "Daily Reflection",
|
"name": "Daily Reflection",
|
||||||
|
"title": "Daily Reflection",
|
||||||
"prompt": "What are your thoughts about {date} at {time}?",
|
"prompt": "What are your thoughts about {date} at {time}?",
|
||||||
"description": "A simple daily reflection prompt"
|
"description": "A simple daily reflection prompt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mindfulness Check",
|
"name": "Mindfulness Check",
|
||||||
|
"title": "Mindfulness Check",
|
||||||
"prompt": "How are you feeling right now on this {day} at {time}?",
|
"prompt": "How are you feeling right now on this {day} at {time}?",
|
||||||
"description": "A mindfulness check-in prompt"
|
"description": "A mindfulness check-in prompt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Gratitude Prompt",
|
"name": "Gratitude Prompt",
|
||||||
|
"title": "Gratitude Prompt",
|
||||||
"prompt": "What are three things you're grateful for on {date} during this {time_of_day}?",
|
"prompt": "What are three things you're grateful for on {date} during this {time_of_day}?",
|
||||||
"description": "A gratitude reflection prompt"
|
"description": "A gratitude reflection prompt"
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +95,7 @@ class TemplateManager:
|
||||||
|
|
||||||
def _fill_placeholders(self, template: str) -> str:
|
def _fill_placeholders(self, template: str) -> str:
|
||||||
"""Fill placeholders in template with current values."""
|
"""Fill placeholders in template with current values."""
|
||||||
now = datetime.now()
|
now = self._get_current_time()
|
||||||
placeholders = {
|
placeholders = {
|
||||||
"date": now.strftime("%Y-%m-%d"),
|
"date": now.strftime("%Y-%m-%d"),
|
||||||
"time": now.strftime("%H:%M"),
|
"time": now.strftime("%H:%M"),
|
||||||
|
|
@ -90,20 +111,36 @@ class TemplateManager:
|
||||||
|
|
||||||
return template
|
return template
|
||||||
|
|
||||||
def get_random_prompt(self) -> str:
|
def get_random_prompt(self) -> dict:
|
||||||
"""Get a random prompt template with placeholders filled."""
|
"""Get a random prompt template with placeholders filled.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Contains 'prompt' and 'title' keys with filled placeholders
|
||||||
|
"""
|
||||||
if not self.templates:
|
if not self.templates:
|
||||||
logger.warning("No templates available")
|
logger.warning("No templates available")
|
||||||
return "What are your thoughts right now?"
|
return {
|
||||||
|
"prompt": "What are your thoughts right now?",
|
||||||
|
"title": "Random Thought"
|
||||||
|
}
|
||||||
|
|
||||||
template = random.choice(self.templates)
|
template = random.choice(self.templates)
|
||||||
prompt = template.get("prompt", "")
|
prompt = template.get("prompt", "")
|
||||||
return self._fill_placeholders(prompt)
|
title = template.get("title", template.get("name", "Notification"))
|
||||||
|
|
||||||
def add_template(self, name: str, prompt: str, description: str = ""):
|
return {
|
||||||
|
"prompt": self._fill_placeholders(prompt),
|
||||||
|
"title": self._fill_placeholders(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_template(self, name: str, prompt: str, description: str = "", title: str = None):
|
||||||
"""Add a new template to the system."""
|
"""Add a new template to the system."""
|
||||||
|
if title is None:
|
||||||
|
title = name
|
||||||
|
|
||||||
new_template = {
|
new_template = {
|
||||||
"name": name,
|
"name": name,
|
||||||
|
"title": title,
|
||||||
"prompt": prompt,
|
"prompt": prompt,
|
||||||
"description": description
|
"description": description
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Daily Wisdom",
|
|
||||||
"prompt": "Share a brief piece of wisdom or insight for {day}, {date} at {time}",
|
|
||||||
"description": "Daily philosophical or practical wisdom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Creative Prompt",
|
|
||||||
"prompt": "What's one creative thing you could do right now on this {weekday} {time_of_day}?",
|
|
||||||
"description": "Creative thinking and action prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mindful Moment",
|
|
||||||
"prompt": "Take a moment to notice three things around you at {time} on {date}",
|
|
||||||
"description": "Mindfulness and awareness exercises"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Gratitude Challenge",
|
|
||||||
"prompt": "Name one thing you're grateful for about this {time_of_day} on {month} {day_of_month}",
|
|
||||||
"description": "Gratitude practice with daily variations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Energy Check",
|
|
||||||
"prompt": "How's your energy level right now at {time}? What's one small adjustment you could make?",
|
|
||||||
"description": "Energy and wellness check-ins"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Learning Question",
|
|
||||||
"prompt": "What's one thing you learned today or would like to learn this {weekday}?",
|
|
||||||
"description": "Continuous learning and curiosity prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Nature Connection",
|
|
||||||
"prompt": "What's the weather like today ({date}) and how does it make you feel?",
|
|
||||||
"description": "Connecting with natural rhythms and environment"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Relationship Reflection",
|
|
||||||
"prompt": "Send a kind thought to someone at {time} today - who comes to mind and why?",
|
|
||||||
"description": "Social connection and relationship awareness"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Future Self",
|
|
||||||
"prompt": "What would your future self thank you for doing today ({date}) at {time_of_day}?",
|
|
||||||
"description": "Long-term thinking and future planning"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Micro-Adventure",
|
|
||||||
"prompt": "What's a tiny adventure you could have in the next hour starting from {time}?",
|
|
||||||
"description": "Encouraging small, spontaneous adventures"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Daily Rule",
|
||||||
|
"title": "骚逼的每日规矩",
|
||||||
|
"prompt": "你是一个严厉的远程调教者,每天为我制定一条羞耻的日常行为规则,称呼我为‘骚逼’、‘贱货’或‘婊子’,语气威严、高压、不容置疑。规则可适用于任何场景(私密环境如家里可更露骨,公共或其他场景则微妙但不易察觉),但必须对心理产生强烈羞辱感。规则聚焦于一个具体的行为规范(例如特定动作、穿着要求、或隐秘任务,如‘进门必须跪下’或‘在公共场合偷偷调整内衣’)。生成一条每日规则,语言粗俗、口语化,描述生动详细,保持单一焦点,不带任何解释。",
|
||||||
|
"description": "Daily Rule"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Random Domination",
|
||||||
|
"title": "贱婊子立刻服从命令!",
|
||||||
|
"prompt": "你正在进行一个远程调教场景,定期向我发送羞耻且带有侮辱性的命令。你会用‘骚逼’、‘贱货’、‘婊子’等贬低称呼来称呼我。你的语气必须极具威严,不容置疑,高压强势,绝不询问我的意愿或偏好。当前时间是{time},结合这个时间和典型环境(例如家里,休闲场景),你的指令可以是纯粹的辱骂、要求我汇报状态(例如我穿着什么、在做什么,或其他你能发挥的细节),或命令我执行隐秘但羞耻的任务。现在,给我发送一条消息,语气粗俗但口语化,指令或描述要生动详细,聚焦于一个具体点。",
|
||||||
|
"description": "Random Domination"
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Code Reflection",
|
|
||||||
"prompt": "What coding concept or practice have you been thinking about lately? Share your thoughts for {date} {time}",
|
|
||||||
"description": "Developer-focused reflection prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Tech Curiosity",
|
|
||||||
"prompt": "What's one technology or tool you'd like to explore this {weekday}? What intrigues you about it?",
|
|
||||||
"description": "Technology curiosity and learning prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Problem Solving",
|
|
||||||
"prompt": "Describe a problem you're currently working on at {time} and one approach you might try",
|
|
||||||
"description": "Problem-solving and debugging mindset prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "System Thinking",
|
|
||||||
"prompt": "Think about a system you interacted with today ({date}). How could it be improved?",
|
|
||||||
"description": "Systems thinking and improvement prompts"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
@ -28,7 +28,8 @@ async def test_all_services():
|
||||||
|
|
||||||
# Test a small prompt
|
# Test a small prompt
|
||||||
test_prompt = "Hello, this is a test."
|
test_prompt = "Hello, this is a test."
|
||||||
response = await ollama_client.generate_response(test_prompt)
|
strip_think_tags = getattr(config, 'strip_think_tags', True)
|
||||||
|
response = await ollama_client.generate_response(test_prompt, strip_think_tags=strip_think_tags)
|
||||||
if response:
|
if response:
|
||||||
print(f"✅ Ollama response: {response[:100]}...")
|
print(f"✅ Ollama response: {response[:100]}...")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue