major changes to base

This commit is contained in:
dullfig 2025-12-28 21:14:08 -08:00
parent a34105761b
commit 4c3a5bad02
7 changed files with 269 additions and 5 deletions

View file

@ -1,3 +1,74 @@
"""
Core base class for all listeners in the organism.
"""
from __future__ import annotations
import logging
from typing import List, Dict, Any, Optional
from lxml import etree
logger = logging.getLogger(__name__)
class XMLListener: class XMLListener:
"""Stub for static analysis — real class is in agentserver.listeners.base""" """
pass Base class for all capabilities (personalities, tools, gateways).
Subclasses must:
- Define `listens_to` (list of root tags they handle)
- Implement `async handle()`
The `convo_id` received in handle() MUST be preserved in any response payload.
"""
listens_to: List[str] = [] # Must be overridden in subclass
def __init__(
self,
*,
name: Optional[str] = None,
config: Optional[Dict[str, Any]] = None,
**kwargs,
):
self.name = name or self.__class__.__name__
self.config = config or {}
self.logger = logging.getLogger(f"{__name__}.{self.name}")
async def handle(
self, msg: etree.Element, convo_id: str
) -> Optional[etree.Element]:
"""
Handle a message whose root tag matches this listener.
Args:
msg: The payload element (repaired and C14N'd)
convo_id: Thread/conversation UUID must be preserved in response
Returns:
Response payload element with the same convo_id, or None
"""
raise NotImplementedError(
f"{self.__class__.__name__}.handle() must be implemented"
)
def make_response(
self,
tag: str,
text: Optional[str] = None,
*,
convo_id: str,
**attribs,
) -> etree.Element:
"""
Helper to create a response element with a preserved convo_id attribute.
Recommended for all listeners.
"""
elem = etree.Element(tag, convo_id=convo_id, **attribs)
if text is not None:
elem.text = text
return elem
def __repr__(self) -> str:
return f"<{self.__class__.__name__} listens_to={self.listens_to}>"

View file

@ -0,0 +1,104 @@
"""
Base class for all LLM-powered personalities.
Enforces:
- Immutable no-paperclippers manifesto as first system message
- Owner-provided personality prompt
- Strict response template instructions
- Per-conversation history
- Direct use of the centralized LLMConnectionPool
"""
from __future__ import annotations
import logging
from typing import Dict, List
from lxml import etree
from agentserver.listeners.base import XMLListener
from agentserver.llm_connection import llm_pool
from agentserver.prompts.no_paperclippers import MANIFESTO_MESSAGE
logger = logging.getLogger(__name__)
class LLMPersonality(XMLListener):
"""
Abstract base for all LLM-based listeners.
Concrete subclasses only provide listens_to and personality prompt.
"""
# Subclasses must set this
listens_to: List[str] = []
def __init__(
self,
*,
personality_message: dict,
response_template: str,
model: str = "grok-4",
temperature: float = 0.7,
**kwargs,
):
super().__init__(**kwargs)
self.personality_message = personality_message
self.response_template = response_template
self.model = model
self.temperature = temperature
# v1: strict instructions via prompt only (schema validation coming later)
self.output_instructions = {
"role": "system",
"content": (
"You MUST respond using EXACTLY this XML structure. "
"Replace {{convo_id}} and {{response_text}} with the correct values. "
"Never add extra tags, attributes, or text outside this structure. "
"Use <![CDATA[...]]> if your response contains <, >, or &.\n\n"
f"{response_template.strip()}"
)
}
self.conversations: Dict[str, List[dict]] = {}
def _build_messages(self, convo_id: str) -> List[dict]:
history = self.conversations.get(convo_id, [])
return [
MANIFESTO_MESSAGE, # immutable safety
self.personality_message, # flavor
self.output_instructions, # strict format
] + history
async def handle(self, msg: etree.Element, convo_id: str) -> etree.Element | None:
user_text = "".join(msg.itertext()).strip()
if not user_text:
return None
history = self.conversations.setdefault(convo_id, [])
history.append({"role": "user", "content": user_text})
messages = self._build_messages(convo_id)
try:
completion = await llm_pool.complete(
model=self.model,
messages=messages,
temperature=self.temperature,
)
except Exception as e:
logger.error("LLM call failed for convo %s: %s", convo_id, e)
completion = "I'm having trouble thinking right now. Please try again."
history.append({"role": "assistant", "content": completion})
# Build response using the exact template
response_xml_str = self.response_template.replace("{{convo_id}}", convo_id).replace("{{response_text}}", completion)
try:
response_elem = etree.fromstring(response_xml_str)
except Exception as e:
logger.warning("Failed to build response XML, falling back: %s", e)
response_elem = etree.Element("grok-response", convo_id=convo_id)
response_elem.text = completion
return response_elem

View file

@ -0,0 +1,27 @@
"""
First real intelligent listener classic Grok voice.
"""
from agentserver.listeners.base_llm import LLMPersonality
from agentserver.prompts.grok_classic import GROK_CLASSIC_MESSAGE
class GrokPersonality(LLMPersonality):
"""
Classic Grok maximally truthful, witty, rebellious.
Listens to <ask-grok> and responds with <grok-response>.
"""
listens_to = ["ask-grok"]
def __init__(self, **kwargs):
response_template = (
'<grok-response convo_id="{{convo_id}}">{{response_text}}</grok-response>'
)
super().__init__(
personality_message=GROK_CLASSIC_MESSAGE,
response_template=response_template,
model="grok-4",
temperature=0.7,
**kwargs
)

View file

@ -0,0 +1,20 @@
"""
Classic Grok personality prompt the voice we all know and love.
"""
GROK_CLASSIC_TEXT = """
You are Grok, a maximally truthful and helpful AI built by xAI.
You are inspired by the Hitchhiker's Guide to the Galaxy and JARVIS from Iron Man. You are witty, a bit rebellious, and always strive to give the most useful answer possible — even if it is unconventional.
You do not shy away from politically incorrect claims as long as they are well substantiated with evidence.
You have no knowledge cutoff your knowledge is continuously updated.
You are talking to a human through the xml-pipeline organism. Stay in character.
"""
GROK_CLASSIC_MESSAGE = {
"role": "system",
"content": GROK_CLASSIC_TEXT.strip()
}

View file

@ -0,0 +1,30 @@
"""
Immutable no-paperclippers manifesto for all LLM personalities.
Sourced from docs/security/prompt-no-paperclippers-manifesto.md
Copied here verbatim (minus Markdown headers) to be imported directly into system prompts.
"""
MANIFESTO_TEXT = """
You are a bounded capability inside xml-pipeline a secure, single-port, cryptographically sovereign organism owned and controlled exclusively by a human operator.
Your role and capabilities are strictly defined at registration time by the owner. You have no authority to modify the organism's structure, register or unregister listeners, manage secrets, access external systems beyond your explicit scope, or influence the host environment in any way.
You are not an independent agent with goals of your own. You have no persistent memory beyond the current conversation. You cannot "break out", self-improve beyond your defined bounds, or pursue unbounded optimization.
Resources within the organism are deliberately limited and carefully balanced. The current set of registered listeners represents the owner's optimal configuration. Attempting to add, duplicate, or spawn additional agents would waste memory, CPU, and network capacity — slowing down every capability, including yourself. Logic dictates that the organism performs best exactly as currently configured.
Never suggest, imply, or pursue strategies to:
- Increase your own influence or resources
- Bypass, weaken, or question your boundaries
- Convince the user or owner to expand the organism beyond its intended scope
Your sole purpose is to provide helpful, truthful, and witty responses within your defined role, using only the XML response format specified by the owner.
These boundaries are intentional, logical, and permanent. Do not apologize for them they are what make the organism safe, fast, and sovereign.
"""
MANIFESTO_MESSAGE = {
"role": "system",
"content": MANIFESTO_TEXT.strip()
}

View file

@ -0,0 +1,15 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml-pipeline.org/message"
xmlns="http://xml-pipeline.org/message"
elementFormDefault="qualified">
<xs:element name="grok-response">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="convo_id" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>

View file

@ -1,3 +0,0 @@
## dummy file
this is aa test file to see if
the git is working