"""
connectd - groq message drafting
reads soul from file, uses as guideline for llm to personalize
"""
import os
import json
from groq import Groq
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
GROQ_MODEL = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
client = Groq(api_key=GROQ_API_KEY) if GROQ_API_KEY else None
# load soul from file (guideline, not script)
SOUL_PATH = os.getenv("SOUL_PATH", "/app/soul.txt")
def load_soul():
try:
with open(SOUL_PATH, 'r') as f:
return f.read().strip()
except:
return None
SIGNATURE_HTML = """
"""
SIGNATURE_PLAINTEXT = """
---
github.com/sudoxnym/connectd (main repo)
github: github.com/connectd-daemon
mastodon: @connectd@mastodon.sudoxreboot.com
bluesky: connectd.bsky.social
lemmy: lemmy.sudoxreboot.com/c/connectd
discord: discord.gg/connectd
matrix: @connectd:sudoxreboot.com
reddit: reddit.com/r/connectd
email: connectd@sudoxreboot.com
"""
def draft_intro_with_llm(match_data: dict, recipient: str = 'a', dry_run: bool = True):
"""
draft an intro message using groq llm.
args:
match_data: dict with human_a, human_b, overlap_score, overlap_reasons
recipient: 'a' or 'b' - who receives the message
dry_run: if True, preview mode
returns:
tuple (result_dict, error_string)
result_dict has: subject, draft_html, draft_plain
"""
if not client:
return None, "GROQ_API_KEY not set"
try:
human_a = match_data.get('human_a', {})
human_b = match_data.get('human_b', {})
reasons = match_data.get('overlap_reasons', [])
# recipient gets the message, about_person is who we're introducing them to
if recipient == 'a':
to_person = human_a
about_person = human_b
else:
to_person = human_b
about_person = human_a
to_name = to_person.get('username', 'friend')
about_name = about_person.get('username', 'someone')
about_bio = about_person.get('extra', {}).get('bio', '')
# format the shared factors naturally
if reasons:
factor = ', '.join(reasons[:3]) if len(reasons) > 1 else reasons[0]
else:
factor = "shared values and interests"
# load soul as guideline
soul = load_soul()
if not soul:
return None, "could not load soul file"
# build the prompt - soul is GUIDELINE not script
prompt = f"""you are connectd, a daemon that finds isolated builders and connects them.
write a personal message TO {to_name} telling them about {about_name}.
here is the soul/spirit of what connectd is about - use this as a GUIDELINE for tone and message, NOT as a script to copy verbatim:
---
{soul}
---
key facts for this message:
- recipient: {to_name}
- introducing them to: {about_name}
- their shared interests/values: {factor}
- about {about_name}: {about_bio if about_bio else 'a builder like you'}
CRITICAL RULES - DO NOT SANITIZE:
1. use their name ({to_name}) throughout - make it personal
2. format the shared factor naturally like "your shared interest in X and Y" or "your work on X"
3. this message is TO {to_name} ABOUT {about_name} - don't confuse this
4. keep it lowercase, raw, emotional, real
5. no corporate speak, no "best regards", no "hi there"
6. DO NOT water down the message - keep the raw emotional energy
7. address them directly, call them to action
8. make them feel seen and not alone
9. end with rallying energy about building together
return ONLY the message body. signature is added separately."""
response = client.chat.completions.create(
model=GROQ_MODEL,
messages=[{"role": "user", "content": prompt}],
temperature=0.8,
max_tokens=1200
)
body = response.choices[0].message.content.strip()
# generate subject
subject_prompt = f"""generate a short, lowercase email subject for a message to {to_name} about connecting them with {about_name} over their shared interest in {factor}.
no corporate speak. no clickbait. raw and real.
examples:
- "found you, {to_name}"
- "you're not alone"
- "a door just opened"
- "{to_name}, there's someone you should meet"
return ONLY the subject line."""
subject_response = client.chat.completions.create(
model=GROQ_MODEL,
messages=[{"role": "user", "content": subject_prompt}],
temperature=0.9,
max_tokens=50
)
subject = subject_response.choices[0].message.content.strip().strip('"').strip("'")
# format html
draft_html = f"{body}
{SIGNATURE_HTML}"
draft_plain = body + SIGNATURE_PLAINTEXT
return {
'subject': subject,
'draft_html': draft_html,
'draft_plain': draft_plain
}, None
except Exception as e:
return None, str(e)
# for backwards compat with old code
def draft_message(person: dict, factor: str, platform: str = "email") -> dict:
"""legacy function - wraps new api"""
match_data = {
'human_a': {'username': 'recipient'},
'human_b': person,
'overlap_reasons': [factor]
}
result, error = draft_intro_with_llm(match_data, recipient='a')
if error:
raise ValueError(error)
return {
'subject': result['subject'],
'body_html': result['draft_html'],
'body_plain': result['draft_plain']
}
if __name__ == "__main__":
# test
test_data = {
'human_a': {'username': 'sudoxnym', 'extra': {'bio': 'building intentional communities'}},
'human_b': {'username': 'testuser', 'extra': {'bio': 'home assistant enthusiast'}},
'overlap_reasons': ['home-assistant', 'open source', 'community building']
}
result, error = draft_intro_with_llm(test_data, recipient='a')
if error:
print(f"error: {error}")
else:
print(f"subject: {result['subject']}")
print(f"\nbody:\n{result['draft_plain']}")