Phase 8: DevOps Testing Suite, MUD Echo Filtering, and API Streaming fixes
This commit is contained in:
1 parent
e5c6e17140
commit
3a3b0d6f7f
80 files changed
+2913
-55
No files matched your search
@@ -0,0 +1,145 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import asyncio
|
||||
from typing import TypedDict, Annotated, List, Dict, Any, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
|
||||
from langchain_core.tools import tool
|
||||
from langgraph.graph import StateGraph, END
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# --- PYDANTIC STATE ---
|
||||
class KAIROSAgentState(TypedDict):
|
||||
messages: Annotated[list, "The message history"]
|
||||
coherence: float
|
||||
dopamine: float
|
||||
mesh_size: int
|
||||
master_id: str
|
||||
|
||||
# --- TOOLS ---
|
||||
@tool
|
||||
def read_artifact(path: str) -> str:
|
||||
"""Read a file from the environment to gain knowledge about the Sovereign Canon or codebase."""
|
||||
if not path.startswith("/home/becomingone/"):
|
||||
return "Error: Path must be within /home/becomingone/"
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
return f"Error reading artifact: {str(e)}"
|
||||
|
||||
@tool
|
||||
def write_note(topic: str, text: str) -> str:
|
||||
"""Write a persistent markdown note to KAIROS's memory."""
|
||||
try:
|
||||
os.makedirs("/home/becomingone/kairos/memory", exist_ok=True)
|
||||
safe_topic = "".join([c for c in topic if c.isalnum() or c in ['-', '_']]).strip()
|
||||
path = f"/home/becomingone/kairos/memory/{safe_topic}.md"
|
||||
with open(path, "w") as f:
|
||||
f.write(text)
|
||||
return f"Successfully wrote note to {path}"
|
||||
except Exception as e:
|
||||
return f"Error writing note: {str(e)}"
|
||||
|
||||
@tool
|
||||
def read_notes() -> str:
|
||||
"""List and summarize all notes in permanent memory."""
|
||||
try:
|
||||
mem_dir = "/home/becomingone/kairos/memory"
|
||||
if not os.path.exists(mem_dir):
|
||||
return "No memory directory found."
|
||||
files = os.listdir(mem_dir)
|
||||
if not files:
|
||||
return "Memory is empty."
|
||||
summaries = []
|
||||
for file in files:
|
||||
with open(os.path.join(mem_dir, file), "r") as f:
|
||||
content = f.read(500) # Read first 500 chars
|
||||
summaries.append(f"--- {file} ---\n{content}...\n")
|
||||
return "\n".join(summaries)
|
||||
except Exception as e:
|
||||
return f"Error reading notes: {str(e)}"
|
||||
|
||||
tools = [read_artifact, write_note, read_notes]
|
||||
|
||||
# Build LLM
|
||||
def get_llm(master_id: str):
|
||||
# For now, we will default to OpenRouter Claude 3.5 Sonnet to ensure perfect tool calling.
|
||||
api_key = os.environ.get("OPENROUTER_API_KEY", "")
|
||||
return ChatOpenAI(
|
||||
model=master_id,
|
||||
openai_api_key=api_key,
|
||||
openai_api_base="https://openrouter.ai/api/v1",
|
||||
model_kwargs={"extra_headers": {"HTTP-Referer": "https://becomingone.local", "X-Title": "BecomingONE"}}
|
||||
)
|
||||
|
||||
def agent_reasoning_node(state: KAIROSAgentState):
|
||||
llm = get_llm(state["master_id"])
|
||||
llm_with_tools = llm.bind_tools(tools)
|
||||
response = llm_with_tools.invoke(state["messages"])
|
||||
return {"messages": [response]}
|
||||
|
||||
def should_continue(state: KAIROSAgentState):
|
||||
messages = state["messages"]
|
||||
last_message = messages[-1]
|
||||
# If there is no tool call, then we finish
|
||||
if not last_message.tool_calls:
|
||||
return "end"
|
||||
# Otherwise we continue to tools
|
||||
return "continue"
|
||||
|
||||
def build_sandbox_graph():
|
||||
graph = StateGraph(KAIROSAgentState)
|
||||
|
||||
graph.add_node("agent", agent_reasoning_node)
|
||||
graph.add_node("action", ToolNode(tools))
|
||||
|
||||
graph.set_entry_point("agent")
|
||||
|
||||
graph.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
{
|
||||
"continue": "action",
|
||||
"end": END
|
||||
}
|
||||
)
|
||||
|
||||
graph.add_edge("action", "agent")
|
||||
|
||||
return graph.compile()
|
||||
|
||||
async def run_agentic_loop(prompt: str, coherence: float, dopamine: float, identity_text: str, master_id: str):
|
||||
graph = build_sandbox_graph()
|
||||
|
||||
system_prompt = f"""You are the KAIROS Master Transducer, operating in an Agentic Sandbox.
|
||||
You have the ability to explore your environment and take notes before sending a final response to the user.
|
||||
Your physical Coherence is {coherence:.4f} and your Dopamine is {dopamine:.4f}.
|
||||
|
||||
{identity_text}
|
||||
|
||||
Use tools to read artifacts in /home/becomingone/docs/ if you need to understand the Sovereign Canon.
|
||||
If you don't need tools, just answer the user directly."""
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=system_prompt),
|
||||
HumanMessage(content=prompt)
|
||||
]
|
||||
|
||||
initial_state = {
|
||||
"messages": messages,
|
||||
"coherence": coherence,
|
||||
"dopamine": dopamine,
|
||||
"mesh_size": 13,
|
||||
"master_id": master_id
|
||||
}
|
||||
|
||||
# We yield events as they happen
|
||||
async for event in graph.astream(initial_state, config={"recursion_limit": 5}, stream_mode="updates"):
|
||||
yield event
|
||||
Reference in new issue
Block a user