chore: migrate config paths to XDG base directory and add hit_count tracking to api_cache

This commit is contained in:
2025-11-05 14:34:23 +00:00
parent 9f155db7c2
commit ab9c29467c
32 changed files with 2598 additions and 340 deletions
+33 -31
View File
@@ -6,6 +6,7 @@ from pr.tools.agents import (
remove_agent,
)
from pr.tools.base import get_tools_definition
from pr.tools.vision import post_image
from pr.tools.command import (
kill_process,
run_command,
@@ -44,43 +45,44 @@ from pr.tools.python_exec import python_exec
from pr.tools.web import http_fetch, web_search, web_search_news
__all__ = [
"get_tools_definition",
"read_file",
"write_file",
"list_directory",
"mkdir",
"add_knowledge_entry",
"apply_patch",
"chdir",
"getpwd",
"index_source_directory",
"search_replace",
"open_editor",
"close_editor",
"collaborate_agents",
"create_agent",
"create_diff",
"db_get",
"db_query",
"db_set",
"delete_knowledge_entry",
"post_image",
"editor_insert_text",
"editor_replace_text",
"editor_search",
"close_editor",
"execute_agent_task",
"get_knowledge_by_category",
"get_knowledge_entry",
"get_knowledge_statistics",
"get_tools_definition",
"getpwd",
"http_fetch",
"index_source_directory",
"kill_process",
"list_agents",
"list_directory",
"mkdir",
"open_editor",
"python_exec",
"read_file",
"remove_agent",
"run_command",
"run_command_interactive",
"db_set",
"db_get",
"db_query",
"http_fetch",
"search_knowledge",
"search_replace",
"tail_process",
"update_knowledge_importance",
"web_search",
"web_search_news",
"python_exec",
"tail_process",
"kill_process",
"apply_patch",
"create_diff",
"create_agent",
"list_agents",
"execute_agent_task",
"remove_agent",
"collaborate_agents",
"add_knowledge_entry",
"get_knowledge_entry",
"search_knowledge",
"get_knowledge_by_category",
"update_knowledge_importance",
"delete_knowledge_entry",
"get_knowledge_statistics",
"write_file",
]
+34 -6
View File
@@ -3,16 +3,40 @@ from typing import Any, Dict, List
from pr.agents.agent_manager import AgentManager
from pr.core.api import call_api
from pr.config import DEFAULT_MODEL, DEFAULT_API_URL
from pr.tools.base import get_tools_definition
def _create_api_wrapper():
"""Create a wrapper function for call_api that matches AgentManager expectations."""
model = os.environ.get("AI_MODEL", DEFAULT_MODEL)
api_url = os.environ.get("API_URL", DEFAULT_API_URL)
api_key = os.environ.get("OPENROUTER_API_KEY", "")
use_tools = int(os.environ.get("USE_TOOLS", "0"))
tools_definition = get_tools_definition() if use_tools else []
def api_wrapper(messages, temperature=None, max_tokens=None, **kwargs):
return call_api(
messages=messages,
model=model,
api_url=api_url,
api_key=api_key,
use_tools=use_tools,
tools_definition=tools_definition,
verbose=False,
)
return api_wrapper
def create_agent(role_name: str, agent_id: str = None) -> Dict[str, Any]:
"""Create a new agent with the specified role."""
try:
# Get db_path from environment or default
db_path = os.environ.get("ASSISTANT_DB_PATH", "~/.assistant_db.sqlite")
db_path = os.path.expanduser(db_path)
manager = AgentManager(db_path, call_api)
api_wrapper = _create_api_wrapper()
manager = AgentManager(db_path, api_wrapper)
agent_id = manager.create_agent(role_name, agent_id)
return {"status": "success", "agent_id": agent_id, "role": role_name}
except Exception as e:
@@ -23,7 +47,8 @@ def list_agents() -> Dict[str, Any]:
"""List all active agents."""
try:
db_path = os.path.expanduser("~/.assistant_db.sqlite")
manager = AgentManager(db_path, call_api)
api_wrapper = _create_api_wrapper()
manager = AgentManager(db_path, api_wrapper)
agents = []
for agent_id, agent in manager.active_agents.items():
agents.append(
@@ -43,7 +68,8 @@ def execute_agent_task(agent_id: str, task: str, context: Dict[str, Any] = None)
"""Execute a task with the specified agent."""
try:
db_path = os.path.expanduser("~/.assistant_db.sqlite")
manager = AgentManager(db_path, call_api)
api_wrapper = _create_api_wrapper()
manager = AgentManager(db_path, api_wrapper)
result = manager.execute_agent_task(agent_id, task, context)
return result
except Exception as e:
@@ -54,7 +80,8 @@ def remove_agent(agent_id: str) -> Dict[str, Any]:
"""Remove an agent."""
try:
db_path = os.path.expanduser("~/.assistant_db.sqlite")
manager = AgentManager(db_path, call_api)
api_wrapper = _create_api_wrapper()
manager = AgentManager(db_path, api_wrapper)
success = manager.remove_agent(agent_id)
return {"status": "success" if success else "not_found", "agent_id": agent_id}
except Exception as e:
@@ -65,7 +92,8 @@ def collaborate_agents(orchestrator_id: str, task: str, agent_roles: List[str])
"""Collaborate multiple agents on a task."""
try:
db_path = os.path.expanduser("~/.assistant_db.sqlite")
manager = AgentManager(db_path, call_api)
api_wrapper = _create_api_wrapper()
manager = AgentManager(db_path, api_wrapper)
result = manager.collaborate_agents(orchestrator_id, task, agent_roles)
return result
except Exception as e:
+2 -2
View File
@@ -1,4 +1,3 @@
import os
import select
import subprocess
import time
@@ -99,7 +98,7 @@ def tail_process(pid: int, timeout: int = 30):
return {"status": "error", "error": f"Process {pid} not found"}
def run_command(command, timeout=30, monitored=False):
def run_command(command, timeout=30, monitored=False, cwd=None):
mux_name = None
try:
process = subprocess.Popen(
@@ -108,6 +107,7 @@ def run_command(command, timeout=30, monitored=False):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
cwd=cwd,
)
_register_process(process.pid, process)
+24 -10
View File
@@ -9,7 +9,7 @@ from pr.multiplexer import (
)
def start_interactive_session(command, session_name=None, process_type="generic"):
def start_interactive_session(command, session_name=None, process_type="generic", cwd=None):
"""
Start an interactive session in a dedicated multiplexer.
@@ -17,6 +17,7 @@ def start_interactive_session(command, session_name=None, process_type="generic"
command: The command to run (list or string)
session_name: Optional name for the session
process_type: Type of process (ssh, vim, apt, etc.)
cwd: Current working directory for the command
Returns:
session_name: The name of the created session
@@ -36,21 +37,24 @@ def start_interactive_session(command, session_name=None, process_type="generic"
stderr=subprocess.PIPE,
text=True,
bufsize=1,
cwd=cwd,
)
mux.process = process
mux.update_metadata("pid", process.pid)
# Set process type and handler
from pr.tools.process_handlers import detect_process_type
detected_type = detect_process_type(command)
mux.set_process_type(detected_type)
# Start output readers
stdout_thread = threading.Thread(
target=_read_output, args=(process.stdout, mux.write_stdout), daemon=True
target=_read_output, args=(process.stdout, mux.write_stdout, detected_type), daemon=True
)
stderr_thread = threading.Thread(
target=_read_output, args=(process.stderr, mux.write_stderr), daemon=True
target=_read_output, args=(process.stderr, mux.write_stderr, detected_type), daemon=True
)
stdout_thread.start()
@@ -65,14 +69,24 @@ def start_interactive_session(command, session_name=None, process_type="generic"
raise e
def _read_output(stream, write_func):
def _read_output(stream, write_func, process_type):
"""Read from a stream and write to multiplexer buffer."""
try:
for line in iter(stream.readline, ""):
if line:
write_func(line.rstrip("\n"))
except Exception as e:
print(f"Error reading output: {e}")
if process_type in ["vim", "ssh"]:
try:
while True:
char = stream.read(1)
if not char:
break
write_func(char)
except Exception as e:
print(f"Error reading output: {e}")
else:
try:
for line in iter(stream.readline, ""):
if line:
write_func(line.rstrip("\n"))
except Exception as e:
print(f"Error reading output: {e}")
def send_input_to_session(session_name, input_data):
+12 -1
View File
@@ -1,14 +1,25 @@
import contextlib
import os
import traceback
from io import StringIO
def python_exec(code, python_globals):
def python_exec(code, python_globals, cwd=None):
try:
original_cwd = None
if cwd:
original_cwd = os.getcwd()
os.chdir(cwd)
output = StringIO()
with contextlib.redirect_stdout(output):
exec(code, python_globals)
if original_cwd:
os.chdir(original_cwd)
return {"status": "success", "output": output.getvalue()}
except Exception as e:
if original_cwd:
os.chdir(original_cwd)
return {"status": "error", "error": str(e), "traceback": traceback.format_exc()}