Terion7/Trojanity / brain.py
Terion7's picture
download
raw
4.96 kB
import asyncio, inspect, re
from typing import Optional
import signals
from config import CFG
from vlog import log, exception
SPOKEN_DISCIPLINE = """
Spoken discipline:
Answer for the ear, not the screen. Use short conversational sentences.
Do not use markdown, code blocks, bullets, numbered lists, tables, or long
formatted passages when speaking. If code or structured content is necessary,
explain it naturally and briefly. TTS performs punctuation, so make punctuation
sound natural. After the first sentence, prefer two-sentence breaths instead
of isolated one-sentence fragments. For tool use, say a short natural filler
such as "On it, checking now." before a potentially long tool operation.
"""
class Brain:
def __init__(self, deep=False):
self.deep = deep
self.client = None
self.last_interrupted = False
async def start(self):
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
model = CFG.claude_deep_model if self.deep else CFG.claude_model
opts = ClaudeAgentOptions(
model=model,
cwd=str(CFG.cwd),
system_prompt={"type": "preset", "preset": "claude_code",
"append": SPOKEN_DISCIPLINE},
)
self.client = ClaudeSDKClient(options=opts)
await self.client.connect()
log("brain_started", requested_model=model, cwd=str(CFG.cwd))
async def warmup(self):
# A harmless startup query pays the cache/warm-session cost behind a greeting.
try:
await self.client.query(
"Warm up this voice session. Reply with exactly: Voice line ready."
)
async for _ in self.client.receive_response():
pass
log("brain_warmup_complete")
except Exception as e:
exception("brain_warmup_error", e)
@staticmethod
def _text_of(msg):
if hasattr(msg, "text"):
return msg.text or ""
if isinstance(msg, dict):
return msg.get("text", "")
return ""
async def ask(self, text, mouth):
await self.client.query(text)
buf = ""
try:
async for msg in self.client.receive_response():
# SDK message objects vary by release; content blocks expose text.
chunks = []
content = getattr(msg, "content", None)
if isinstance(content, list):
for block in content:
t = getattr(block, "text", None)
if t:
chunks.append(t)
else:
t = self._text_of(msg)
if t:
chunks.append(t)
for t in chunks:
buf += t
# Sentence chunking; retain punctuation in spoken text.
while True:
m = re.search(r"(.+?[.!?](?:[\"')\]]*)\s+)", buf, re.S)
if not m:
break
sentence = m.group(1).strip()
buf = buf[m.end():]
await mouth.enqueue(sentence)
# Flush content block fragments so pre-tool filler is not silent.
if content and not buf.strip():
pass
if buf.strip():
await mouth.enqueue(buf.strip())
except asyncio.CancelledError:
self.last_interrupted = True
raise
except Exception as e:
exception("brain_turn_error", e)
raise
async def realign_after_interrupt(self):
"""
The Agent SDK has one shared stream. After an interrupted reader, stale
messages can remain buffered. Interrupt the client, then drain until a
ResultMessage boundary or timeout. If that cannot be established, rebuild.
"""
try:
if hasattr(self.client, "interrupt"):
result = self.client.interrupt()
if inspect.isawaitable(result):
await result
deadline = asyncio.get_running_loop().time() + 3.0
async for msg in self.client.receive_response():
name = type(msg).__name__
if name == "ResultMessage":
log("brain_realign_ok")
return True
if asyncio.get_running_loop().time() > deadline:
break
except Exception as e:
exception("brain_realign_error", e)
log("brain_realign_rebuild")
try:
if self.client and hasattr(self.client, "disconnect"):
r = self.client.disconnect()
if inspect.isawaitable(r):
await r
except Exception:
pass
self.client = None
await self.start()
return False

Xet Storage Details

Size:
4.96 kB
·
Xet hash:
0b6ecca2d0553a6b562e81ad3ee039e812bf530ef0d7170fdf5931b1dcac2a6a

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.