ArunCore / backend /app /core /agent.py
GitHub Actions Bot
deploy: automated sync from github main
985f3ee
Raw
History Blame Contribute Delete
4.47 kB
"""Agent composition root + backwards-compatible facade.
The heavy lifting previously in this file now lives in single-responsibility
services (notification, knowledge, memory, prompt, tools, tenant). This module
only: (1) applies the global IPv4-only DNS patch once, (2) exposes `init_agent`
as the small factory that wires an LLM + bound tools + chat prompt + memory,
and (3) re-exports every public symbol the API, Telegram bot, eval scripts,
and tests still import from `backend.app.core.agent`.
"""
import os
import socket
from typing import Any, Dict, Optional, Tuple
# Force IPv4-only DNS resolution (Hugging Face Spaces / dual-stack hosts can
# stall IPv6 lookups). Applied once at import time, mirroring the legacy boot.
try:
_orig_getaddrinfo = socket.getaddrinfo
def _ipv4_only_getaddrinfo(*args, **kwargs):
res = _orig_getaddrinfo(*args, **kwargs)
ipv4_res = [r for r in res if r[0] == socket.AF_INET]
return ipv4_res or res
socket.getaddrinfo = _ipv4_only_getaddrinfo
except Exception:
pass
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from backend.app.services.memory_manager import RollingMemory, MemoryManager
from backend.app.services.prompt_builder import PromptBuilder
from backend.app.services.tool_executor import (
ToolExecutor,
search_arun_knowledge,
get_github_live_data,
notify_arun,
)
from backend.app.services.tenant_service import tenant_service, TenantService
from backend.app.services.knowledge_service import knowledge_service
from backend.app.services.notification_service import (
queue_debug_event,
queue_maybe_notify_arun,
queue_chat_history_to_telegram,
queue_automated_chat_alert,
send_automated_chat_alert,
send_chat_history_to_telegram,
send_debug_event_to_telegram,
schedule_notify_arun,
TELEGRAM_DELIVERY_LOGS,
_deliver_notify_arun,
)
from backend.app.services.auth_service import generate_admin_token, verify_admin_token
from backend.app.services.agent_runner import AgentRunner, agent_runner, run_pre_escalation
load_dotenv()
__all__ = [
"init_agent",
"RollingMemory",
"MemoryManager",
"load_static_context",
"load_tutor_config",
"save_unknown_question_answer",
"queue_debug_event",
"queue_maybe_notify_arun",
"run_pre_escalation",
"queue_chat_history_to_telegram",
"queue_automated_chat_alert",
"send_automated_chat_alert",
"send_chat_history_to_telegram",
"send_debug_event_to_telegram",
"schedule_notify_arun",
"generate_admin_token",
"verify_admin_token",
"TELEGRAM_DELIVERY_LOGS",
"search_arun_knowledge",
"get_github_live_data",
"notify_arun",
"ToolExecutor",
"AgentRunner",
"agent_runner",
]
def init_agent(
temperature: float = 0.4,
model_name: Optional[str] = None,
tutor_id: Optional[str] = None,
):
"""Build a ready-to-run ArunCore agent.
Returns the same 4-tuple as the legacy factory:
(main_llm, chat_prompt, memory, tools).
"""
openai_key = os.getenv("OPENAI_API_KEY")
if not openai_key:
raise ValueError("OPENAI_API_KEY is not set in environment.")
resolved_model = model_name or os.getenv("OPENAI_MODEL", "gpt-4.1-nano")
tools = ToolExecutor.get_enabled_tools(ToolExecutor.DEFAULT_TOOLS)
main_llm = ChatOpenAI(
temperature=temperature,
model=resolved_model,
api_key=openai_key,
).bind_tools(tools)
system_prompt = PromptBuilder().build_system_prompt(tutor_id=tutor_id)
prompt = PromptBuilder.build_chat_prompt(system_prompt)
summary_llm = ChatOpenAI(
temperature=0.0,
model="gpt-4o-mini",
api_key=openai_key,
)
memory = RollingMemory(summary_llm=summary_llm)
return main_llm, prompt, memory, tools
def load_tutor_config(tutor_id: Optional[str]) -> Optional[Dict[str, Any]]:
"""Legacy demos-dictionary loader (kept for external consumers)."""
return tenant_service.load_legacy_tutor_config(tutor_id)
def save_unknown_question_answer(question: str, answer: str) -> str:
"""Persists a verified Q&A pair into the active-learning store."""
return knowledge_service.save_verified_answer(question, answer)
def load_static_context():
"""Canonical 5-tuple static context reader (system, guardrails, handoff, profile, rules)."""
from backend.app.services.prompt_builder import load_static_context as _load_5
return _load_5()