huronvalley21 commited on
Commit
7caa635
·
verified ·
1 Parent(s): 5940135

Upload mythos/agent.py

Browse files
Files changed (1) hide show
  1. mythos/agent.py +139 -0
mythos/agent.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Agent definition for Mythos — role-based reasoning units."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from typing import Any, Awaitable, Callable, Optional
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+ from .memory import Memory, MemoryType
11
+ from .message import Message, MessageType
12
+ from .tool import ToolRegistry
13
+
14
+
15
+ class AgentConfig(BaseModel):
16
+ """Configuration for an agent."""
17
+
18
+ max_context_length: int = 4096
19
+ temperature: float = 0.7
20
+ max_tokens: int = 1024
21
+ auto_memory: bool = True # Automatically store interactions as memory
22
+ verbose: bool = False
23
+
24
+
25
+ class Agent(BaseModel):
26
+ """A Mythos agent — a role-based reasoning unit with memory and tools."""
27
+
28
+ name: str
29
+ role: str
30
+ goal: str
31
+ backstory: str = ""
32
+ config: AgentConfig = Field(default_factory=AgentConfig)
33
+ memory: Memory = Field(default_factory=lambda: Memory(agent_name=""))
34
+ tools: ToolRegistry = Field(default_factory=ToolRegistry)
35
+ _llm_call: Optional[Callable[[str], Awaitable[str]]] = None
36
+ _message_handler: Optional[Callable[[Message], Awaitable[Message]]] = None
37
+
38
+ class Config:
39
+ arbitrary_types_allowed = True
40
+
41
+ def __init__(self, **data):
42
+ super().__init__(**data)
43
+ self.memory.agent_name = self.name
44
+
45
+ def set_llm(self, llm_call: Callable[[str], Awaitable[str]]) -> None:
46
+ """Set the LLM inference function for this agent."""
47
+ self._llm_call = llm_call
48
+
49
+ def set_message_handler(self, handler: Callable[[Message], Awaitable[Message]]) -> None:
50
+ """Set a custom message handler."""
51
+ self._message_handler = handler
52
+
53
+ def _build_system_prompt(self) -> str:
54
+ """Build the system prompt for this agent."""
55
+ lines = [
56
+ f"You are {self.name}, a {self.role}.",
57
+ f"Your goal: {self.goal}",
58
+ ]
59
+ if self.backstory:
60
+ lines.append(f"Backstory: {self.backstory}")
61
+ if self.tools.list_tools():
62
+ lines.append("\nAvailable tools:")
63
+ for spec in self.tools.list_tools():
64
+ lines.append(f" - {spec.name}: {spec.description}")
65
+ lines.append("\nRespond helpfully and concisely.")
66
+ return "\n".join(lines)
67
+
68
+ def _build_prompt(self, message: Message, context: str = "") -> str:
69
+ """Build the full prompt for a message."""
70
+ parts = [self._build_system_prompt()]
71
+ if context:
72
+ parts.append(f"\n--- Context ---\n{context}\n---")
73
+ parts.append(f"\n[{message.sender}] says: {message.content}")
74
+ parts.append(f"\n[{self.name}] responds:")
75
+ return "\n".join(parts)
76
+
77
+ async def think(self, content: str, context: str = "") -> str:
78
+ """Generate a response to content using the LLM."""
79
+ if self._llm_call is None:
80
+ # Fallback: echo with role context
81
+ return f"[{self.name} ({self.role})] Acknowledged: {content[:200]}"
82
+
83
+ prompt = self._build_system_prompt()
84
+ if context:
85
+ prompt += f"\n\n--- Context ---\n{context}\n---"
86
+ prompt += f"\n\nUser: {content}\n\n{self.name}:"
87
+
88
+ response = await self._llm_call(prompt)
89
+ return response.strip()
90
+
91
+ async def handle(self, message: Message) -> Message:
92
+ """Handle an incoming message and return a response."""
93
+ if self._message_handler:
94
+ return await self._message_handler(message)
95
+
96
+ # Auto-retrieve relevant memories
97
+ memory_context = ""
98
+ if self.config.auto_memory:
99
+ relevant = self.memory.active_retrieve(message.content)
100
+ if relevant:
101
+ memory_context = self.memory.to_context(relevant)
102
+
103
+ # Generate response
104
+ response_text = await self.think(message.content, context=memory_context)
105
+
106
+ # Store interaction in memory
107
+ if self.config.auto_memory:
108
+ self.memory.add(
109
+ content=f"Received from {message.sender}: {message.content}",
110
+ mem_type=MemoryType.EPISODIC,
111
+ tags=["interaction", message.sender],
112
+ )
113
+ self.memory.add(
114
+ content=f"Responded: {response_text}",
115
+ mem_type=MemoryType.EPISODIC,
116
+ tags=["interaction", message.sender],
117
+ )
118
+
119
+ return Message(
120
+ type=MessageType.RESPONSE,
121
+ sender=self.name,
122
+ recipient=message.sender,
123
+ content=response_text,
124
+ parent_id=message.id,
125
+ )
126
+
127
+ async def run_task(self, task: str) -> str:
128
+ """Run a standalone task without a message context."""
129
+ msg = Message(
130
+ type=MessageType.TASK,
131
+ sender="system",
132
+ recipient=self.name,
133
+ content=task,
134
+ )
135
+ response = await self.handle(msg)
136
+ return response.content
137
+
138
+ def __str__(self) -> str:
139
+ return f"Agent({self.name}, role={self.role})"