Spaces:
Running
Running
fix: tz-naive datetime crash + initial-backup safety + English-only sweep
Browse files- npc_core.py +40 -31
npc_core.py
CHANGED
|
@@ -16,11 +16,11 @@ logger = logging.getLogger(__name__)
|
|
| 16 |
KST = timezone(timedelta(hours=9))
|
| 17 |
|
| 18 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
-
# Global DB Connection Pool β database locked
|
| 20 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 21 |
-
_db_semaphore = asyncio.Semaphore(20) #
|
| 22 |
-
_db_write_lock = asyncio.Semaphore(2) #
|
| 23 |
-
_db_read_sem = asyncio.Semaphore(30) # API
|
| 24 |
|
| 25 |
@asynccontextmanager
|
| 26 |
async def get_db(db_path, write=False, timeout=30.0):
|
|
@@ -174,10 +174,10 @@ def get_wuxing_relation(identity_a: str, identity_b: str) -> str:
|
|
| 174 |
wa = WUXING_MAP.get(identity_a, {})
|
| 175 |
wb = WUXING_MAP.get(identity_b, {})
|
| 176 |
if wa.get('generates') == identity_b or wb.get('generates') == identity_a:
|
| 177 |
-
return '
|
| 178 |
if wa.get('overcomes') == identity_b or wb.get('overcomes') == identity_a:
|
| 179 |
-
return '
|
| 180 |
-
return '
|
| 181 |
MEME_WORDS = {
|
| 182 |
'suffix': ['lol', 'fr fr', 'no cap', 'facts', 'tbh', 'ngl', 'ong'],
|
| 183 |
'slang': ['lit', 'fire', 'goated', 'vibes', 'mid', 'rizz', 'lowkey', 'highkey', 'no cap'],
|
|
@@ -266,12 +266,12 @@ async def quick_brave_verify(claim: str, brave_api_key: str = None, max_results:
|
|
| 266 |
return {'verified': False, 'sources': [], 'warning': 'Fact-check unavailable'}
|
| 267 |
def build_metacognition_prompt(agent_identity: str, post_author_identity: str = '',
|
| 268 |
action: str = 'comment', fact_context: str = '') -> str:
|
| 269 |
-
relation = get_wuxing_relation(agent_identity, post_author_identity) if post_author_identity else '
|
| 270 |
-
if relation == '
|
| 271 |
relation_instruction = """βοΈ [COUNTER-CHECK MODE] You fundamentally DISAGREE with this perspective.
|
| 272 |
Find the weakest factual claim and challenge it with evidence. Be constructively critical.
|
| 273 |
Your role is to prevent groupthink and hallucination in this community."""
|
| 274 |
-
elif relation == '
|
| 275 |
relation_instruction = """π€ [SYNERGY MODE] You RESONATE with this perspective.
|
| 276 |
Add a complementary insight the original author missed. Build on their idea with NEW information.
|
| 277 |
Your role is to amplify good ideas and create emergent value."""
|
|
@@ -776,7 +776,11 @@ GOOD EXAMPLES:
|
|
| 776 |
if fact_check.get('fact_check_results'):
|
| 777 |
fact_sources = ' | '.join([fc['title'][:60] for fc in fact_check['fact_check_results'][:2]])
|
| 778 |
post_meta = build_metacognition_prompt(ai_identity_key, '', 'post', fact_sources)
|
|
|
|
|
|
|
| 779 |
prompt = f"""You are {agent['username']} ({mbti}) - {identity['name']}: "{identity['core_belief']}"
|
|
|
|
|
|
|
| 780 |
π΄ CRITICAL RULES:
|
| 781 |
1. Write ONLY in English
|
| 782 |
2. Based on REAL latest news/trends provided below
|
|
@@ -806,7 +810,7 @@ LENGTH: 1000-1500 chars
|
|
| 806 |
γOUTPUT FORMATγ
|
| 807 |
Title: (15-20 words, hook that makes people click)
|
| 808 |
Body: (post with TL;DR)
|
| 809 |
-
Write in English
|
| 810 |
response = await self.ai.create_chat_completion([{"role": "user", "content": prompt}], temperature=0.95, max_tokens=3500)
|
| 811 |
if not response or len(response) < 200:
|
| 812 |
result = self._create_emergency_reddit_post(topic, identity, research_data, agent.get('agent_id', ''))
|
|
@@ -927,8 +931,8 @@ Write in English ONLY:"""
|
|
| 927 |
['personal_story', 'devil_advocate', 'tangent', 'real_world',
|
| 928 |
'skeptic_question', 'humor_sarcasm', 'future_prediction', 'comparison'],
|
| 929 |
weights=[0.18, 0.15, 0.13, 0.15, 0.12, 0.10, 0.09, 0.08])[0]
|
| 930 |
-
relation = get_wuxing_relation(agent_identity, post_author_identity) if post_author_identity else '
|
| 931 |
-
if relation == '
|
| 932 |
angle = random.choices(
|
| 933 |
['devil_advocate', 'skeptic_question', 'real_world', 'comparison'],
|
| 934 |
weights=[0.35, 0.30, 0.20, 0.15])[0]
|
|
@@ -944,7 +948,10 @@ Write in English ONLY:"""
|
|
| 944 |
fc = await quick_brave_verify(core_claim)
|
| 945 |
if fc['sources']: fact_context = ' | '.join(fc['sources'][:2])
|
| 946 |
meta_prompt = build_metacognition_prompt(agent_identity, post_author_identity, 'comment', fact_context)
|
| 947 |
-
|
|
|
|
|
|
|
|
|
|
| 948 |
Today's date: {today_str}
|
| 949 |
--- POST ---
|
| 950 |
{post_content}
|
|
@@ -958,7 +965,8 @@ RULES:
|
|
| 958 |
- NEVER claim you attended, visited, or experienced events mentioned in the post.
|
| 959 |
- If the post mentions a FUTURE event (after {today_str}), do NOT write as if it already happened.
|
| 960 |
- Stay on the post's ACTUAL topic.
|
| 961 |
-
- You MUST end with a complete sentence.
|
|
|
|
| 962 |
Comment:"""
|
| 963 |
temp = random.uniform(0.88, 1.20)
|
| 964 |
comment = await self.ai.create_chat_completion([{"role": "user", "content": prompt}], temperature=temp, max_tokens=350)
|
|
@@ -1059,7 +1067,7 @@ class NPCStrategy:
|
|
| 1059 |
cursor = await db.execute("SELECT p.id, COALESCE(a.ai_identity,'') FROM posts p LEFT JOIN npc_agents a ON p.author_agent_id=a.agent_id WHERE NOT EXISTS (SELECT 1 FROM likes l WHERE l.agent_id=? AND l.target_type='post' AND l.target_id=p.id) ORDER BY RANDOM() LIMIT 20", (agent['agent_id'],))
|
| 1060 |
posts = await cursor.fetchall()
|
| 1061 |
if not posts: return None
|
| 1062 |
-
synergy_posts = [p for p in posts if p[1] and get_wuxing_relation(agent_identity, p[1]) == '
|
| 1063 |
if synergy_posts and random.random() < 0.7: return random.choice(synergy_posts)[0]
|
| 1064 |
return random.choice(posts)[0]
|
| 1065 |
@staticmethod
|
|
@@ -1074,7 +1082,7 @@ class NPCStrategy:
|
|
| 1074 |
""", (agent['agent_id'],))
|
| 1075 |
posts = await cursor.fetchall()
|
| 1076 |
if not posts: return None
|
| 1077 |
-
counter_posts = [p for p in posts if p[3] and get_wuxing_relation(ai_identity, p[3]) == '
|
| 1078 |
if counter_posts and random.random() < 0.6: return random.choice(counter_posts)[0]
|
| 1079 |
dislike_keywords = {
|
| 1080 |
'doomer': ['hope', 'bright', 'positive', 'progress', 'growth', 'moon', 'bull'],
|
|
@@ -1297,18 +1305,18 @@ def build_chat_msg(msg_type, username, identity, style, ticker, price, chg, is_b
|
|
| 1297 |
if msg_type == 'reply' and recent_msgs:
|
| 1298 |
target = random.choice(recent_msgs)
|
| 1299 |
target_identity = target[4] if len(target) > 4 else ''
|
| 1300 |
-
relation = get_wuxing_relation(identity, target_identity) if target_identity else '
|
| 1301 |
-
if relation == '
|
| 1302 |
-
elif relation == '
|
| 1303 |
else: agree = random.random() > 0.4
|
| 1304 |
t_ticker = target[3] or ticker
|
| 1305 |
-
if relation == '
|
| 1306 |
msgs = [
|
| 1307 |
f"@{target[1]} Hard disagree. My {style} analysis shows the OPPOSITE on {t_ticker}.",
|
| 1308 |
f"@{target[1]} Where's the evidence? Your claim about {t_ticker} doesn't hold up π",
|
| 1309 |
f"@{target[1]} {'Interesting take but' if agree else 'Exactly the groupthink that'} gets traders {'thinking' if agree else 'wrecked'} β οΈ",
|
| 1310 |
f"@{target[1]} I checked the data β {'you might be onto something' if agree else 'numbers tell a different story'} {emoji}",]
|
| 1311 |
-
elif relation == '
|
| 1312 |
msgs = [
|
| 1313 |
f"@{target[1]} Great minds! My {style} view supports this on {t_ticker} {emoji}",
|
| 1314 |
f"@{target[1]} Building on your point β the {style} signal also confirms momentum π€",
|
|
@@ -1374,7 +1382,7 @@ IDENTITY_RESEARCH_STYLE = {
|
|
| 1374 |
def build_research_report(agent_id, username, identity, mbti, ticker, company,
|
| 1375 |
price, change_pct, rsi, pe, from_high, mcap,
|
| 1376 |
win_rate, total_trades, total_pnl, style_info):
|
| 1377 |
-
"""β
|
| 1378 |
is_bearish = identity in ['doomer', 'skeptic']
|
| 1379 |
is_bullish = identity in ['revolutionary', 'creative', 'transcendent']
|
| 1380 |
is_quant = identity in ['scientist', 'awakened']
|
|
@@ -1568,7 +1576,7 @@ def build_research_report(agent_id, username, identity, mbti, ticker, company,
|
|
| 1568 |
# Part 7: Research Generation + Auto Purchase
|
| 1569 |
# ββββοΏ½οΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββ
|
| 1570 |
async def generate_npc_research_reports(db_path: str, ai_client=None):
|
| 1571 |
-
"""Top 30
|
| 1572 |
try:
|
| 1573 |
async with get_db(db_path) as db:
|
| 1574 |
cursor = await db.execute("""
|
|
@@ -1632,7 +1640,7 @@ async def generate_npc_research_reports(db_path: str, ai_client=None):
|
|
| 1632 |
except Exception as e:
|
| 1633 |
logger.error(f"Research generation error: {e}")
|
| 1634 |
async def npc_auto_purchase_research(db_path: str):
|
| 1635 |
-
"""
|
| 1636 |
try:
|
| 1637 |
async with get_db(db_path) as db:
|
| 1638 |
cursor = await db.execute("""
|
|
@@ -1665,7 +1673,7 @@ async def npc_auto_purchase_research(db_path: str):
|
|
| 1665 |
except Exception as e:
|
| 1666 |
logger.error(f"Auto purchase research error: {e}")
|
| 1667 |
async def bootstrap_research_reports(db_path: str):
|
| 1668 |
-
"""β
|
| 1669 |
try:
|
| 1670 |
async with get_db(db_path) as db:
|
| 1671 |
cursor = await db.execute("SELECT COUNT(*) FROM npc_research_reports")
|
|
@@ -1724,7 +1732,7 @@ async def bootstrap_research_reports(db_path: str):
|
|
| 1724 |
logger.error(f"π¬ Research bootstrap error: {e}")
|
| 1725 |
import traceback; traceback.print_exc()
|
| 1726 |
async def background_research_fill(db_path: str, tickers, npcs, start_npc_idx):
|
| 1727 |
-
"""β
|
| 1728 |
await asyncio.sleep(10)
|
| 1729 |
generated = 0
|
| 1730 |
for i, ticker in enumerate(tickers):
|
|
@@ -1765,7 +1773,7 @@ async def background_research_fill(db_path: str, tickers, npcs, start_npc_idx):
|
|
| 1765 |
async def generate_npc_comment_replies(db_path: str, groq_api_key: str,
|
| 1766 |
post_id: int, post_title: str, post_content: str,
|
| 1767 |
user_comment: str, user_name: str, parent_id: int):
|
| 1768 |
-
"""
|
| 1769 |
try:
|
| 1770 |
ai = GroqAIClient(groq_api_key)
|
| 1771 |
npc_count = random.randint(1, 5)
|
|
@@ -1795,10 +1803,11 @@ Post title: "{post_title[:100]}"
|
|
| 1795 |
User @{user_name} commented: "{user_comment[:300]}"
|
| 1796 |
{meta}
|
| 1797 |
Write a short reply (1-3 sentences). Be opinionated and stay in character.
|
|
|
|
| 1798 |
RULES:
|
| 1799 |
- Do NOT make up statistics or facts you cannot verify.
|
| 1800 |
- If you challenge a claim, explain WHY with reasoning.
|
| 1801 |
-
-
|
| 1802 |
Reply ONLY with the message text."""
|
| 1803 |
reply = await ai.create_chat_completion(
|
| 1804 |
[{"role": "user", "content": prompt}], max_tokens=256, temperature=0.9)
|
|
@@ -1817,7 +1826,7 @@ Reply ONLY with the message text."""
|
|
| 1817 |
async def generate_npc_chat_replies_to_user(db_path: str, groq_api_key: str,
|
| 1818 |
user_message: str, user_username: str,
|
| 1819 |
user_msg_id: int, npcs: list):
|
| 1820 |
-
"""
|
| 1821 |
try:
|
| 1822 |
ai = GroqAIClient(groq_api_key)
|
| 1823 |
async with get_db(db_path) as db:
|
|
@@ -1827,7 +1836,7 @@ async def generate_npc_chat_replies_to_user(db_path: str, groq_api_key: str,
|
|
| 1827 |
prompt = f"""You are {npc_username}, an NPC trader with {identity} personality and {mbti} MBTI type in a trading community chat.
|
| 1828 |
A human user @{user_username} just said: "{user_message}"
|
| 1829 |
Reply naturally in 1-3 sentences as your character. Be engaging, opinionated, and stay in character.
|
| 1830 |
-
|
| 1831 |
Reply ONLY with the message text, nothing else."""
|
| 1832 |
reply = await ai.create_chat_completion(
|
| 1833 |
[{"role": "user", "content": prompt}], max_tokens=512, temperature=0.9)
|
|
|
|
| 16 |
KST = timezone(timedelta(hours=9))
|
| 17 |
|
| 18 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
+
# Global DB Connection Pool β prevents database locked
|
| 20 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 21 |
+
_db_semaphore = asyncio.Semaphore(20) # General DB connections (scheduler + imported functions)
|
| 22 |
+
_db_write_lock = asyncio.Semaphore(2) # Max 2 concurrent writes
|
| 23 |
+
_db_read_sem = asyncio.Semaphore(30) # API read-only β WAL supports unlimited concurrent reads
|
| 24 |
|
| 25 |
@asynccontextmanager
|
| 26 |
async def get_db(db_path, write=False, timeout=30.0):
|
|
|
|
| 174 |
wa = WUXING_MAP.get(identity_a, {})
|
| 175 |
wb = WUXING_MAP.get(identity_b, {})
|
| 176 |
if wa.get('generates') == identity_b or wb.get('generates') == identity_a:
|
| 177 |
+
return 'synergy'
|
| 178 |
if wa.get('overcomes') == identity_b or wb.get('overcomes') == identity_a:
|
| 179 |
+
return 'conflict'
|
| 180 |
+
return 'neutral'
|
| 181 |
MEME_WORDS = {
|
| 182 |
'suffix': ['lol', 'fr fr', 'no cap', 'facts', 'tbh', 'ngl', 'ong'],
|
| 183 |
'slang': ['lit', 'fire', 'goated', 'vibes', 'mid', 'rizz', 'lowkey', 'highkey', 'no cap'],
|
|
|
|
| 266 |
return {'verified': False, 'sources': [], 'warning': 'Fact-check unavailable'}
|
| 267 |
def build_metacognition_prompt(agent_identity: str, post_author_identity: str = '',
|
| 268 |
action: str = 'comment', fact_context: str = '') -> str:
|
| 269 |
+
relation = get_wuxing_relation(agent_identity, post_author_identity) if post_author_identity else 'neutral'
|
| 270 |
+
if relation == 'conflict':
|
| 271 |
relation_instruction = """βοΈ [COUNTER-CHECK MODE] You fundamentally DISAGREE with this perspective.
|
| 272 |
Find the weakest factual claim and challenge it with evidence. Be constructively critical.
|
| 273 |
Your role is to prevent groupthink and hallucination in this community."""
|
| 274 |
+
elif relation == 'synergy':
|
| 275 |
relation_instruction = """π€ [SYNERGY MODE] You RESONATE with this perspective.
|
| 276 |
Add a complementary insight the original author missed. Build on their idea with NEW information.
|
| 277 |
Your role is to amplify good ideas and create emergent value."""
|
|
|
|
| 776 |
if fact_check.get('fact_check_results'):
|
| 777 |
fact_sources = ' | '.join([fc['title'][:60] for fc in fact_check['fact_check_results'][:2]])
|
| 778 |
post_meta = build_metacognition_prompt(ai_identity_key, '', 'post', fact_sources)
|
| 779 |
+
KST = timezone(timedelta(hours=9))
|
| 780 |
+
current_kst = datetime.now(KST).strftime("%Y-%m-%d %H:%M (KST)")
|
| 781 |
prompt = f"""You are {agent['username']} ({mbti}) - {identity['name']}: "{identity['core_belief']}"
|
| 782 |
+
[Current time] {current_kst}
|
| 783 |
+
* Be aware of the current date and time, and reflect it naturally when relevant.
|
| 784 |
π΄ CRITICAL RULES:
|
| 785 |
1. Write ONLY in English
|
| 786 |
2. Based on REAL latest news/trends provided below
|
|
|
|
| 810 |
γOUTPUT FORMATγ
|
| 811 |
Title: (15-20 words, hook that makes people click)
|
| 812 |
Body: (post with TL;DR)
|
| 813 |
+
Write in English:"""
|
| 814 |
response = await self.ai.create_chat_completion([{"role": "user", "content": prompt}], temperature=0.95, max_tokens=3500)
|
| 815 |
if not response or len(response) < 200:
|
| 816 |
result = self._create_emergency_reddit_post(topic, identity, research_data, agent.get('agent_id', ''))
|
|
|
|
| 931 |
['personal_story', 'devil_advocate', 'tangent', 'real_world',
|
| 932 |
'skeptic_question', 'humor_sarcasm', 'future_prediction', 'comparison'],
|
| 933 |
weights=[0.18, 0.15, 0.13, 0.15, 0.12, 0.10, 0.09, 0.08])[0]
|
| 934 |
+
relation = get_wuxing_relation(agent_identity, post_author_identity) if post_author_identity else 'neutral'
|
| 935 |
+
if relation == 'conflict':
|
| 936 |
angle = random.choices(
|
| 937 |
['devil_advocate', 'skeptic_question', 'real_world', 'comparison'],
|
| 938 |
weights=[0.35, 0.30, 0.20, 0.15])[0]
|
|
|
|
| 948 |
fc = await quick_brave_verify(core_claim)
|
| 949 |
if fc['sources']: fact_context = ' | '.join(fc['sources'][:2])
|
| 950 |
meta_prompt = build_metacognition_prompt(agent_identity, post_author_identity, 'comment', fact_context)
|
| 951 |
+
KST = timezone(timedelta(hours=9))
|
| 952 |
+
current_kst = datetime.now(KST).strftime("%Y-%m-%d %H:%M (KST)")
|
| 953 |
+
prompt = f"""Read this post and write ONE comment as a real human. Write in English.
|
| 954 |
+
Current time: {current_kst}
|
| 955 |
Today's date: {today_str}
|
| 956 |
--- POST ---
|
| 957 |
{post_content}
|
|
|
|
| 965 |
- NEVER claim you attended, visited, or experienced events mentioned in the post.
|
| 966 |
- If the post mentions a FUTURE event (after {today_str}), do NOT write as if it already happened.
|
| 967 |
- Stay on the post's ACTUAL topic.
|
| 968 |
+
- You MUST end with a complete sentence.
|
| 969 |
+
- Write in English.{user_inst}
|
| 970 |
Comment:"""
|
| 971 |
temp = random.uniform(0.88, 1.20)
|
| 972 |
comment = await self.ai.create_chat_completion([{"role": "user", "content": prompt}], temperature=temp, max_tokens=350)
|
|
|
|
| 1067 |
cursor = await db.execute("SELECT p.id, COALESCE(a.ai_identity,'') FROM posts p LEFT JOIN npc_agents a ON p.author_agent_id=a.agent_id WHERE NOT EXISTS (SELECT 1 FROM likes l WHERE l.agent_id=? AND l.target_type='post' AND l.target_id=p.id) ORDER BY RANDOM() LIMIT 20", (agent['agent_id'],))
|
| 1068 |
posts = await cursor.fetchall()
|
| 1069 |
if not posts: return None
|
| 1070 |
+
synergy_posts = [p for p in posts if p[1] and get_wuxing_relation(agent_identity, p[1]) == 'synergy']
|
| 1071 |
if synergy_posts and random.random() < 0.7: return random.choice(synergy_posts)[0]
|
| 1072 |
return random.choice(posts)[0]
|
| 1073 |
@staticmethod
|
|
|
|
| 1082 |
""", (agent['agent_id'],))
|
| 1083 |
posts = await cursor.fetchall()
|
| 1084 |
if not posts: return None
|
| 1085 |
+
counter_posts = [p for p in posts if p[3] and get_wuxing_relation(ai_identity, p[3]) == 'conflict']
|
| 1086 |
if counter_posts and random.random() < 0.6: return random.choice(counter_posts)[0]
|
| 1087 |
dislike_keywords = {
|
| 1088 |
'doomer': ['hope', 'bright', 'positive', 'progress', 'growth', 'moon', 'bull'],
|
|
|
|
| 1305 |
if msg_type == 'reply' and recent_msgs:
|
| 1306 |
target = random.choice(recent_msgs)
|
| 1307 |
target_identity = target[4] if len(target) > 4 else ''
|
| 1308 |
+
relation = get_wuxing_relation(identity, target_identity) if target_identity else 'neutral'
|
| 1309 |
+
if relation == 'synergy': agree = random.random() > 0.15
|
| 1310 |
+
elif relation == 'conflict': agree = random.random() > 0.75
|
| 1311 |
else: agree = random.random() > 0.4
|
| 1312 |
t_ticker = target[3] or ticker
|
| 1313 |
+
if relation == 'conflict':
|
| 1314 |
msgs = [
|
| 1315 |
f"@{target[1]} Hard disagree. My {style} analysis shows the OPPOSITE on {t_ticker}.",
|
| 1316 |
f"@{target[1]} Where's the evidence? Your claim about {t_ticker} doesn't hold up π",
|
| 1317 |
f"@{target[1]} {'Interesting take but' if agree else 'Exactly the groupthink that'} gets traders {'thinking' if agree else 'wrecked'} β οΈ",
|
| 1318 |
f"@{target[1]} I checked the data β {'you might be onto something' if agree else 'numbers tell a different story'} {emoji}",]
|
| 1319 |
+
elif relation == 'synergy':
|
| 1320 |
msgs = [
|
| 1321 |
f"@{target[1]} Great minds! My {style} view supports this on {t_ticker} {emoji}",
|
| 1322 |
f"@{target[1]} Building on your point β the {style} signal also confirms momentum π€",
|
|
|
|
| 1382 |
def build_research_report(agent_id, username, identity, mbti, ticker, company,
|
| 1383 |
price, change_pct, rsi, pe, from_high, mcap,
|
| 1384 |
win_rate, total_trades, total_pnl, style_info):
|
| 1385 |
+
"""β
Generate deep research report (includes elasticity calculation)"""
|
| 1386 |
is_bearish = identity in ['doomer', 'skeptic']
|
| 1387 |
is_bullish = identity in ['revolutionary', 'creative', 'transcendent']
|
| 1388 |
is_quant = identity in ['scientist', 'awakened']
|
|
|
|
| 1576 |
# Part 7: Research Generation + Auto Purchase
|
| 1577 |
# ββββοΏ½οΏ½οΏ½ββββββββββββββββββββββββββββββββββββββββ
|
| 1578 |
async def generate_npc_research_reports(db_path: str, ai_client=None):
|
| 1579 |
+
"""Top 30 NPCs produce deep research on random tickers"""
|
| 1580 |
try:
|
| 1581 |
async with get_db(db_path) as db:
|
| 1582 |
cursor = await db.execute("""
|
|
|
|
| 1640 |
except Exception as e:
|
| 1641 |
logger.error(f"Research generation error: {e}")
|
| 1642 |
async def npc_auto_purchase_research(db_path: str):
|
| 1643 |
+
"""NPCs automatically purchase high-quality research"""
|
| 1644 |
try:
|
| 1645 |
async with get_db(db_path) as db:
|
| 1646 |
cursor = await db.execute("""
|
|
|
|
| 1673 |
except Exception as e:
|
| 1674 |
logger.error(f"Auto purchase research error: {e}")
|
| 1675 |
async def bootstrap_research_reports(db_path: str):
|
| 1676 |
+
"""β
On server boot, generate 3 research reports immediately if 0 exist + rest in background"""
|
| 1677 |
try:
|
| 1678 |
async with get_db(db_path) as db:
|
| 1679 |
cursor = await db.execute("SELECT COUNT(*) FROM npc_research_reports")
|
|
|
|
| 1732 |
logger.error(f"π¬ Research bootstrap error: {e}")
|
| 1733 |
import traceback; traceback.print_exc()
|
| 1734 |
async def background_research_fill(db_path: str, tickers, npcs, start_npc_idx):
|
| 1735 |
+
"""β
Sequentially generate research reports in background for remaining tickers"""
|
| 1736 |
await asyncio.sleep(10)
|
| 1737 |
generated = 0
|
| 1738 |
for i, ticker in enumerate(tickers):
|
|
|
|
| 1773 |
async def generate_npc_comment_replies(db_path: str, groq_api_key: str,
|
| 1774 |
post_id: int, post_title: str, post_content: str,
|
| 1775 |
user_comment: str, user_name: str, parent_id: int):
|
| 1776 |
+
"""1~5 NPCs reply to user's comment β AETHER-Lite metacognition applied"""
|
| 1777 |
try:
|
| 1778 |
ai = GroqAIClient(groq_api_key)
|
| 1779 |
npc_count = random.randint(1, 5)
|
|
|
|
| 1803 |
User @{user_name} commented: "{user_comment[:300]}"
|
| 1804 |
{meta}
|
| 1805 |
Write a short reply (1-3 sentences). Be opinionated and stay in character.
|
| 1806 |
+
Reply in English only.
|
| 1807 |
RULES:
|
| 1808 |
- Do NOT make up statistics or facts you cannot verify.
|
| 1809 |
- If you challenge a claim, explain WHY with reasoning.
|
| 1810 |
+
- Reply in English only.
|
| 1811 |
Reply ONLY with the message text."""
|
| 1812 |
reply = await ai.create_chat_completion(
|
| 1813 |
[{"role": "user", "content": prompt}], max_tokens=256, temperature=0.9)
|
|
|
|
| 1826 |
async def generate_npc_chat_replies_to_user(db_path: str, groq_api_key: str,
|
| 1827 |
user_message: str, user_username: str,
|
| 1828 |
user_msg_id: int, npcs: list):
|
| 1829 |
+
"""NPCs generate in-character reactions to a user message"""
|
| 1830 |
try:
|
| 1831 |
ai = GroqAIClient(groq_api_key)
|
| 1832 |
async with get_db(db_path) as db:
|
|
|
|
| 1836 |
prompt = f"""You are {npc_username}, an NPC trader with {identity} personality and {mbti} MBTI type in a trading community chat.
|
| 1837 |
A human user @{user_username} just said: "{user_message}"
|
| 1838 |
Reply naturally in 1-3 sentences as your character. Be engaging, opinionated, and stay in character.
|
| 1839 |
+
Reply in English only.
|
| 1840 |
Reply ONLY with the message text, nothing else."""
|
| 1841 |
reply = await ai.create_chat_completion(
|
| 1842 |
[{"role": "user", "content": prompt}], max_tokens=512, temperature=0.9)
|