mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 12:17:02 +00:00
Resolve issues pertaining to main module reloading mechanisms internal to Agno/agent-ui
Fixes #8
This commit is contained in:
1 parent
fbfb0cc7ff
commit
e88c1e6fac
3 files changed
+101
-67
No files matched your search
+6
-67
@@ -1,82 +1,21 @@
|
||||
import argparse
|
||||
import os
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
|
||||
from agno.agent.agent import Agent
|
||||
from agno.memory.agent import AgentMemory
|
||||
from agno.models.anthropic.claude import Claude
|
||||
from agno.models.google.gemini import Gemini
|
||||
from agno.playground.playground import Playground
|
||||
from agno.playground.serve import serve_playground_app
|
||||
from agno.storage.sqlite import SqliteStorage
|
||||
from dotenv import load_dotenv
|
||||
from sensai.util import logging
|
||||
from sensai.util.helper import mark_used
|
||||
|
||||
from serena.agent import SerenaAgent
|
||||
from serena.agno import SerenaAgnoToolkit
|
||||
from serena.agno import SerenaAgnoAgentProvider
|
||||
|
||||
mark_used(Gemini, Claude)
|
||||
|
||||
os.chdir(Path(__file__).parent)
|
||||
Logger.root.setLevel(logging.INFO)
|
||||
logging.configure(level=logging.INFO)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Serena coding assistant")
|
||||
parser.add_argument("--project-file", required=True, help="Path to the project file, either absolute or relative to the root directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
project_file = Path(args.project_file).resolve()
|
||||
# If project file path is relative, make it absolute by joining with project root
|
||||
if not project_file.is_absolute():
|
||||
# Get the project root directory (parent of scripts directory)
|
||||
project_root = Path(__file__).parent.parent
|
||||
project_file = project_root / args.project_file
|
||||
|
||||
# Ensure the path is normalized and absolute
|
||||
project_file = project_file.resolve()
|
||||
|
||||
|
||||
serena_agent = SerenaAgent(str(project_file), start_language_server=True)
|
||||
|
||||
# Even though we don't want to keep history between sessions,
|
||||
# for the agno ui to work as a conversation, we a persistent storage on disk
|
||||
# This storage should be deleted between sessions.
|
||||
# Note that this might collide with custom options for the agent, like adding vector-search based tools.
|
||||
# See here for an explanation: https://www.reddit.com/r/agno/comments/1jk6qea/regarding_the_built_in_memory/
|
||||
sql_db_path = Path("tmp") / "agent_storage.db"
|
||||
sql_db_path.parent.mkdir(exist_ok=True)
|
||||
# delete the db file if it exists
|
||||
if sql_db_path.exists():
|
||||
sql_db_path.unlink()
|
||||
|
||||
# model = Claude(id="claude-3-7-sonnet-20250219")
|
||||
# Or use any other model supported by agno, e.g. the newest Gemini:
|
||||
model = Gemini(id="gemini-2.5-pro-exp-03-25")
|
||||
|
||||
agno_agent = Agent(
|
||||
name="Serena",
|
||||
model=model,
|
||||
# See explanation above on why storage is needed
|
||||
storage=SqliteStorage(table_name="serena_agent_sessions", db_file=str(sql_db_path)),
|
||||
description="A fully-featured coding assistant",
|
||||
tools=[SerenaAgnoToolkit(serena_agent)], # type: ignore
|
||||
# The tool calls will be shown in the UI anyway since whether to show them is configurable per tool
|
||||
# To see detailed logs, you should use the serena logger (configure it in the project file path)
|
||||
show_tool_calls=False,
|
||||
markdown=True,
|
||||
system_message=serena_agent.prompt_factory.create_system_prompt(),
|
||||
telemetry=False,
|
||||
memory=AgentMemory(),
|
||||
add_history_to_messages=True,
|
||||
num_history_responses=100, # you might want to adjust this (expense vs. history awareness)
|
||||
)
|
||||
|
||||
app = Playground(agents=[agno_agent]).get_app()
|
||||
# Define the model to use (see Agno documentation for supported models; these are just examples)
|
||||
model = Claude(id="claude-3-7-sonnet-20250219")
|
||||
# model = Gemini(id="gemini-2.5-pro-exp-03-25")
|
||||
|
||||
app = Playground(agents=[SerenaAgnoAgentProvider.get_agent(model)]).get_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
serve_playground_app("agno_agent:app", reload=False)
|
||||
@@ -1 +1,7 @@
|
||||
__version__ = "0.1.0-dev1"
|
||||
|
||||
|
||||
def serena_root_path() -> str:
|
||||
from pathlib import Path
|
||||
|
||||
return str(Path(__file__).parent.parent.absolute())
|
||||
@@ -1,9 +1,20 @@
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from agno.agent import Agent
|
||||
from agno.memory import AgentMemory
|
||||
from agno.models.base import Model
|
||||
from agno.storage.sqlite import SqliteStorage
|
||||
from agno.tools.function import Function
|
||||
from agno.tools.toolkit import Toolkit
|
||||
from dotenv import load_dotenv
|
||||
from sensai.util.logging import LogTime
|
||||
|
||||
from serena import serena_root_path
|
||||
from serena.agent import SerenaAgent, Tool
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -89,3 +100,81 @@ class SerenaAgnoToolkit(Toolkit):
|
||||
function.entrypoint = entrypoint
|
||||
function.skip_entrypoint_processing = True
|
||||
return function
|
||||
|
||||
|
||||
class SerenaAgnoAgentProvider:
|
||||
_agent: Agent | None = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def get_agent(cls, model: Model) -> Agent:
|
||||
"""
|
||||
Returns the singleton instance of the Serena agent or creates it with the given parameters if it doesn't exist.
|
||||
|
||||
NOTE: This is very ugly with poor separation of concerns, but the way in which the Agno UI works (reloading the
|
||||
module that defines the `app` variable) essentially forces us to do something like this.
|
||||
|
||||
:param model: the large language model to use for the agent
|
||||
:return: the agent instance
|
||||
"""
|
||||
with cls._lock:
|
||||
if cls._agent is not None:
|
||||
return cls._agent
|
||||
|
||||
# change to Serena root
|
||||
os.chdir(serena_root_path())
|
||||
|
||||
load_dotenv()
|
||||
|
||||
parser = argparse.ArgumentParser(description="Serena coding assistant")
|
||||
parser.add_argument(
|
||||
"--project-file", required=True, help="Path to the project file, either absolute or relative to the root directory"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
project_file = Path(args.project_file).resolve()
|
||||
# If project file path is relative, make it absolute by joining with project root
|
||||
if not project_file.is_absolute():
|
||||
# Get the project root directory (parent of scripts directory)
|
||||
project_root = Path(serena_root_path())
|
||||
project_file = project_root / args.project_file
|
||||
|
||||
# Ensure the path is normalized and absolute
|
||||
project_file = project_file.resolve()
|
||||
|
||||
with LogTime("Loading Serena agent"):
|
||||
serena_agent = SerenaAgent(str(project_file), start_language_server=True)
|
||||
|
||||
# Even though we don't want to keep history between sessions,
|
||||
# for agno-ui to work as a conversation, we use a persistent storage on disk.
|
||||
# This storage should be deleted between sessions.
|
||||
# Note that this might collide with custom options for the agent, like adding vector-search based tools.
|
||||
# See here for an explanation: https://www.reddit.com/r/agno/comments/1jk6qea/regarding_the_built_in_memory/
|
||||
sql_db_path = (Path("temp") / "agno_agent_storage.db").absolute()
|
||||
sql_db_path.parent.mkdir(exist_ok=True)
|
||||
# delete the db file if it exists
|
||||
log.info(f"Deleting DB from PID {os.getpid()}")
|
||||
if sql_db_path.exists():
|
||||
sql_db_path.unlink()
|
||||
|
||||
agno_agent = Agent(
|
||||
name="Serena",
|
||||
model=model,
|
||||
# See explanation above on why storage is needed
|
||||
storage=SqliteStorage(table_name="serena_agent_sessions", db_file=str(sql_db_path)),
|
||||
description="A fully-featured coding assistant",
|
||||
tools=[SerenaAgnoToolkit(serena_agent)],
|
||||
# The tool calls will be shown in the UI anyway since whether to show them is configurable per tool
|
||||
# To see detailed logs, you should use the serena logger (configure it in the project file path)
|
||||
show_tool_calls=False,
|
||||
markdown=True,
|
||||
system_message=serena_agent.prompt_factory.create_system_prompt(),
|
||||
telemetry=False,
|
||||
memory=AgentMemory(),
|
||||
add_history_to_messages=True,
|
||||
num_history_responses=100, # you might want to adjust this (expense vs. history awareness)
|
||||
)
|
||||
cls._agent = agno_agent
|
||||
log.info(f"Agent instantiated: {agno_agent}")
|
||||
|
||||
return agno_agent
|
||||
Reference in new issue
Block a user