Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,13 @@ uet_web/.env

# Archived non-research platform/prototype material, if temporarily restored locally.
_archive_non_research/

# Local service experiment artifacts
services_and_experiments/**/.env
services_and_experiments/**/target/
services_and_experiments/**/target_*/
services_and_experiments/**/debug/
services_and_experiments/**/.rustc_info.json
services_and_experiments/**/CACHEDIR.TAG
services_and_experiments/**/*.exe
services_and_experiments/**/*_linux
26 changes: 26 additions & 0 deletions services_and_experiments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Services and Experiments

This folder keeps prototype service code that supports UET tooling experiments.

It is source-only by default. Do not commit local runtime state, generated build
outputs, compiled binaries, credentials, `.env` files, or machine-specific caches.

## Current areas

| Area | Purpose |
| :-- | :-- |
| `uet_agents/` | prototype agent orchestration and document ingestion helpers |
| `uet_api/` | experimental Rust API service |
| `uet_chain/` | experimental ledger and chain primitives |
| `uet_core/` | reusable Rust core calculation primitives |
| `uet_kb/` | experimental knowledge-base and MCP-facing service code |
| `uet_miner/` | experimental mining and benchmarking code |
| `uet_security/` | experimental signing, hashing, and key-management primitives |
| `uet_under_development/` | unfinished market, governance, oracle, and economic modules |

## Commit discipline

- Keep this directory separate from research topic hardening commits.
- Treat these services as prototypes unless a later document says otherwise.
- Keep generated artifacts out of Git and publish binaries through releases only.
- Store real secrets outside the repository.
51 changes: 51 additions & 0 deletions services_and_experiments/uet_agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 🧠 UET Agents: The Executive Branch

> **"The Office" of the UET System.**
> While `uet_core` is the factory (Rust Engine) and `uet_kb` is the warehouse (Rust Database), this directory (`uet_agents`) is where the **Decisions** and **Logic** happen.

## 🌟 Why Python?
We use Python for Agents because:
1. **LLM Native:** AI models speak Python (PyTorch, SDKs).
2. **Flexibility:** Logic for conversation and reasoning changes often; Python is agile.
3. **Orchestration:** Python is excellent at gluing together high-performance Rust components.

## 🤖 The Workforce

| Agent | Role | File |
| :--- | :--- | :--- |
| **Orchestrator** | **The Boss.** Routes queries to the right specialist. | `orchestrator.py` |
| **ResearchAgent** | **The Librarian.** Searches `uet_kb` for verified knowledge. | `research_agent.py` |
| **MarketingAgent** | **The PR Officer.** Manages Social Media & Moltbook protocol. | `marketing_agent.py` |
| **EquationExpert** | **The Mathematician.** Verifies math & dimensions. | `base_agent.py` (specialized) |

## 🚀 How to Run

Run the main orchestrator loop from the project root:

```bash
# Windows
python uet_agents/main.py

# Linux/Mac
python3 uet_agents/main.py
```

## 🔗 Relationship to System

```mermaid
graph TD
User[User] --> ORC[Orchestrator Agent]

subgraph "Python Agents (The Office)"
ORC --> RES[Research Agent]
ORC --> MKT[Marketing Agent]
ORC --> EQ[Equation Expert]
end

subgraph "Rust Infrastructure (The Factory)"
RES --> KB[UET Knowledge Base]
EQ --> CORE[UET Core Engine]
end

KB --> DB[(Vector Database)]
```
19 changes: 19 additions & 0 deletions services_and_experiments/uet_agents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# UET Agents Package

# Standalone components (no external dependencies beyond requirements.txt)
from .semantic_engine import UETSemanticEngine
from .executive_router import ExecutiveRouter
from .memory_store import (
WorkingMemoryStore,
EpisodicMemoryStore,
SemanticMemoryStore,
ProceduralMemoryStore,
)

# Components that depend on docs.knowledge_base (optional)
try:
from .base_agent import BaseAgent
from .research_agent import ResearchAgent
from .orchestrator import OrchestratorAgent
except ImportError:
pass
183 changes: 183 additions & 0 deletions services_and_experiments/uet_agents/api_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import time
import uuid
import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional, Dict, Any, List
from .executive_router import ExecutiveRouter
from .semantic_engine import UETSemanticEngine

app = FastAPI(title="UET Semantic Engine API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
engine = UETSemanticEngine()
router = ExecutiveRouter(engine)

# Expose models
class ChatRequest(BaseModel):
prompt: str
doc_context: Optional[str] = None
session_id: Optional[str] = None
project_scope: Optional[str] = None
source_tags: Optional[list[str]] = None

class IngestRequest(BaseModel):
doc_id: str
text: str
source_type: Optional[str] = "text"
project_scope: Optional[str] = None
doc_version: Optional[str] = None
tags: Optional[list[str]] = None
ingest_mode: Optional[str] = "manual"

class ComputeResult(BaseModel):
response: str
equilibrium_data: Dict[str, Any]
work_computed: float
task_type: Optional[str] = None
session_id: Optional[str] = None

@app.post("/ingest")
async def ingest_document(req: IngestRequest):
"""Ingest a document into the UET Semantic Manifold"""
router.ingest_document(
doc_id=req.doc_id,
text=req.text,
source_type=req.source_type or "text",
project_scope=req.project_scope,
doc_version=req.doc_version,
tags=req.tags,
ingest_mode=req.ingest_mode or "manual",
)
return {"status": "success", "message": f"Ingested {len(req.text)} chars for {req.doc_id}"}

@app.post("/chat", response_model=ComputeResult)
async def chat(req: ChatRequest):
"""
1. Calculate Equilibrium Path (Work)
2. Generate Response based on Path
"""
if len(engine.knowledge_chunks) == 0 and req.doc_context:
router.ingest_document("temp_context", req.doc_context)

result = router.handle_chat(
prompt=req.prompt,
doc_context=req.doc_context,
session_id=req.session_id,
project_scope=req.project_scope,
source_tags=req.source_tags,
)

return ComputeResult(
response=result.response,
equilibrium_data=result.equilibrium_data,
work_computed=result.work_computed,
task_type=result.task_type,
session_id=result.session_id,
)

@app.get("/debug/status")
async def debug_status():
return {
"status": "ok",
"knowledge_chunk_count": len(engine.knowledge_chunks),
"vocab_size": len(engine.vocab),
}

@app.get("/debug/session/{session_id}")
async def debug_session(session_id: str):
return router.debug_session(session_id)


# ─────────────────────────────────────────────────────────────
# OpenAI-Compatible API (so LobeChat / any OpenAI client works)
# ─────────────────────────────────────────────────────────────

class OAIMessage(BaseModel):
role: str
content: str

class OAIChatRequest(BaseModel):
model: Optional[str] = "uet-agent"
messages: List[OAIMessage]
stream: Optional[bool] = False
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = None

@app.get("/v1/models")
async def list_models():
"""OpenAI-compatible model list — returns UET agent models."""
return {
"object": "list",
"data": [
{"id": "uet-agent", "object": "model", "created": 1700000000, "owned_by": "uet"},
{"id": "uet-agent-fast", "object": "model", "created": 1700000000, "owned_by": "uet"},
{"id": "glm-4.7-flash", "object": "model", "created": 1700000000, "owned_by": "uet"},
],
}

@app.post("/v1/chat/completions")
async def oai_chat_completions(req: OAIChatRequest):
"""OpenAI-compatible chat completions — proxies through UET Semantic Engine."""
# Extract the last user message as the prompt
user_messages = [m for m in req.messages if m.role == "user"]
system_messages = [m for m in req.messages if m.role == "system"]
if not user_messages:
raise HTTPException(status_code=400, detail="No user message provided")

prompt = user_messages[-1].content
system_ctx = system_messages[-1].content if system_messages else None

# Include system prompt in doc_context if present
doc_context = system_ctx if system_ctx else None

result = router.handle_chat(
prompt=prompt,
doc_context=doc_context,
session_id=None,
project_scope="lobechat",
source_tags=["lobechat"],
)

completion_id = f"chatcmpl-{uuid.uuid4().hex[:16]}"
created_ts = int(time.time())

return {
"id": completion_id,
"object": "chat.completion",
"created": created_ts,
"model": req.model or "uet-agent",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": result.response,
},
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": len(prompt.split()),
"completion_tokens": len(result.response.split()),
"total_tokens": len(prompt.split()) + len(result.response.split()),
},
"uet_metadata": {
"work_computed": result.work_computed,
"task_type": result.task_type,
"session_id": result.session_id,
},
}


def start_server(port: int = 8001):
uvicorn.run(app, host="0.0.0.0", port=port)

if __name__ == "__main__":
start_server()
53 changes: 53 additions & 0 deletions services_and_experiments/uet_agents/base_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from typing import List, Dict, Optional, Any
import logging
from docs.knowledge_base.api_client import OpenRouterClient, CostTracker


class BaseAgent:
"""
Base class for all UET Agents.
Handles configuration, API client connection, and common utilities.
"""

def __init__(
self,
name: str,
client: OpenRouterClient,
system_prompt: str = "",
model_override: Optional[str] = None,
):
self.name = name
self.client = client
self.system_prompt = system_prompt
self.model_override = model_override

# Load agent config
self.agent_config = self.client.agents.get(name, {})
self.model = model_override or self.agent_config.get("model")

if not self.model:
# Fallback if config is missing or incomplete
self.model = "qwen/qwen3-coder-next"
logging.warning(f"Agent '{name}' has no model in config. Defaulting to {self.model}")

def chat(self, messages: List[Dict[str, str]], temperature: float = 0.7) -> str:
"""
Send a chat request to the LLM.
Automatically prepends system prompt if not present in history (optional strategy).
For now, we assume 'messages' includes what is needed, or we prepend system prompt here.
"""
# Prepend system message if provided and not already there
msgs_to_send = []
if self.system_prompt:
msgs_to_send.append({"role": "system", "content": self.system_prompt})

msgs_to_send.extend(messages)

return self.client.chat(agent_id=self.name, messages=msgs_to_send, temperature=temperature)

def run(self, user_query: str) -> str:
"""
Simple synchronous run: User Query -> Answer.
Override this for more complex loops (e.g. ResearchAgent).
"""
return self.chat([{"role": "user", "content": user_query}])
Loading
Loading