Compare commits
8 Commits
ec6b6e4545
...
c8847b0dbb
| Author | SHA1 | Date | |
|---|---|---|---|
| c8847b0dbb | |||
| c4ad6433cb | |||
| bd4dfaad2a | |||
| c3d748c08f | |||
| 8558e60ee6 | |||
| abb78ad70e | |||
| 262d7dd51b | |||
| c64df2f48a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
.vscode/
|
||||
logs/
|
||||
*.egg-info/
|
||||
workspace/
|
||||
|
||||
*.pyc
|
||||
*.zip
|
||||
|
||||
@@ -2,9 +2,13 @@ import tyro
|
||||
|
||||
from lang_agent.fs_bkends.base import BaseFilesystemBackend
|
||||
from lang_agent.fs_bkends.statebk import StateBk, StateBkConfig
|
||||
from lang_agent.fs_bkends.localshell import LocalShell, LocalShellConfig
|
||||
from lang_agent.fs_bkends.daytona_sandbox import DaytonaSandboxBk, DaytonaSandboxConfig
|
||||
|
||||
statebk_dict = {
|
||||
"statebk": StateBkConfig(),
|
||||
"localshell": LocalShellConfig(),
|
||||
"daytonasandbox": DaytonaSandboxConfig(),
|
||||
}
|
||||
|
||||
statebk_union = tyro.extras.subcommand_type_from_defaults(statebk_dict, prefix_names=False)
|
||||
|
||||
@@ -7,6 +7,7 @@ from abc import ABC, abstractmethod
|
||||
|
||||
class BaseFilesystemBackend(ABC):
|
||||
backend: Any
|
||||
config: Any
|
||||
|
||||
@abstractmethod
|
||||
def _build_backend(self):
|
||||
@@ -21,4 +22,7 @@ class BaseFilesystemBackend(ABC):
|
||||
|
||||
def get_deepagent_params(self):
|
||||
"""extra params to pass into the creation of deepagents"""
|
||||
return {}
|
||||
if hasattr(self.config, "rt_skills_dir"):
|
||||
return {"skills" : [self.config.rt_skills_dir]}
|
||||
else:
|
||||
return {}
|
||||
92
lang_agent/fs_bkends/daytona_sandbox.py
Normal file
92
lang_agent/fs_bkends/daytona_sandbox.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Type, Optional
|
||||
from pathlib import Path
|
||||
import os
|
||||
import tyro
|
||||
from loguru import logger
|
||||
|
||||
from daytona import Daytona, DaytonaConfig, FileUpload
|
||||
from langchain_daytona import DaytonaSandbox
|
||||
|
||||
from lang_agent.config import InstantiateConfig
|
||||
from lang_agent.fs_bkends import BaseFilesystemBackend
|
||||
|
||||
|
||||
@tyro.conf.configure(tyro.conf.SuppressFixed)
|
||||
@dataclass
|
||||
class DaytonaSandboxConfig(InstantiateConfig):
|
||||
_target: Type = field(default_factory=lambda: DaytonaSandboxBk)
|
||||
|
||||
api_key: Optional[str] = None
|
||||
"""Daytona API key. Falls back to DAYTONA_API_KEY env var."""
|
||||
|
||||
skills_dir: str = "./workspace/skills"
|
||||
"""local path to directory containing skill files to upload"""
|
||||
|
||||
rt_skills_dir: str = ""
|
||||
"""runtime skills path inside the sandbox (auto-set from sandbox workdir)"""
|
||||
|
||||
def __post_init__(self):
|
||||
if self.api_key is None:
|
||||
self.api_key = os.environ.get("DAYTONA_API_KEY")
|
||||
if self.api_key is None:
|
||||
logger.error("no DAYTONA_API_KEY provided")
|
||||
else:
|
||||
logger.info("DAYTONA_API_KEY loaded from environ")
|
||||
|
||||
|
||||
class DaytonaSandboxBk(BaseFilesystemBackend):
|
||||
def __init__(self, config: DaytonaSandboxConfig):
|
||||
self.config = config
|
||||
self.sandbox = None
|
||||
self._build_backend()
|
||||
|
||||
def _build_backend(self):
|
||||
daytona = Daytona(DaytonaConfig(api_key=self.config.api_key))
|
||||
self.sandbox = daytona.create()
|
||||
workdir = self.sandbox.get_work_dir()
|
||||
logger.info(f"Daytona sandbox created: {self.sandbox.id}, workdir: {workdir}")
|
||||
|
||||
if not self.config.rt_skills_dir:
|
||||
self.config.rt_skills_dir = f"{workdir}/skills"
|
||||
|
||||
self._upload_skills(workdir)
|
||||
self.backend = DaytonaSandbox(sandbox=self.sandbox)
|
||||
|
||||
def _upload_skills(self, workdir: str):
|
||||
skills_dir = Path(self.config.skills_dir)
|
||||
if not skills_dir.exists():
|
||||
logger.warning(f"Skills directory not found: {skills_dir}")
|
||||
return
|
||||
|
||||
files_to_upload = []
|
||||
for skill_path in skills_dir.rglob("*"):
|
||||
if not skill_path.is_file():
|
||||
continue
|
||||
relative_path = skill_path.relative_to(skills_dir)
|
||||
remote_path = f"{workdir}/skills/{relative_path.as_posix()}"
|
||||
with open(skill_path, "rb") as f:
|
||||
files_to_upload.append(FileUpload(source=f.read(), destination=remote_path))
|
||||
|
||||
if not files_to_upload:
|
||||
logger.warning("No skill files found to upload")
|
||||
return
|
||||
|
||||
unique_dirs = {str(Path(u.destination).parent) for u in files_to_upload}
|
||||
for dir_path in sorted(unique_dirs):
|
||||
try:
|
||||
self.sandbox.fs.create_folder(dir_path, "755")
|
||||
except Exception as e:
|
||||
if "permission denied" not in str(e).lower():
|
||||
logger.debug(f"Creating dir {dir_path}: {e}")
|
||||
|
||||
self.sandbox.fs.upload_files(files_to_upload)
|
||||
logger.info(f"Uploaded {len(files_to_upload)} skill files to {workdir}/skills/")
|
||||
|
||||
def get_deepagent_params(self):
|
||||
return {"skills": [self.config.rt_skills_dir]}
|
||||
|
||||
def stop(self):
|
||||
if self.sandbox is not None:
|
||||
self.sandbox.stop()
|
||||
logger.info("Daytona sandbox stopped")
|
||||
39
lang_agent/fs_bkends/localshell.py
Normal file
39
lang_agent/fs_bkends/localshell.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from dataclasses import dataclass, field, is_dataclass
|
||||
from typing import Type, TypedDict, Literal, Dict, List, Tuple, Optional
|
||||
import tyro
|
||||
import os.path as osp
|
||||
from abc import ABC, abstractmethod
|
||||
import glob
|
||||
from loguru import logger
|
||||
|
||||
from deepagents.backends.utils import create_file_data
|
||||
from deepagents.backends import LocalShellBackend
|
||||
|
||||
from lang_agent.config import InstantiateConfig
|
||||
from lang_agent.fs_bkends import BaseFilesystemBackend
|
||||
|
||||
|
||||
@tyro.conf.configure(tyro.conf.SuppressFixed)
|
||||
@dataclass
|
||||
class LocalShellConfig(InstantiateConfig):
|
||||
_target:Type = field(default_factory=lambda:LocalShell)
|
||||
|
||||
workspace_dir:str = "./workspace"
|
||||
"""path to workspace directory"""
|
||||
|
||||
skills_dir:str = "./workspace/skills"
|
||||
"""path to directory containing skill files"""
|
||||
|
||||
rt_skills_dir:str = "/skills"
|
||||
"""path to directory with skills in runtime directory"""
|
||||
|
||||
|
||||
class LocalShell(BaseFilesystemBackend):
|
||||
def __init__(self, config:LocalShellConfig):
|
||||
self.config = config
|
||||
self._build_backend()
|
||||
|
||||
def _build_backend(self):
|
||||
self.backend = LocalShellBackend(root_dir=self.config.workspace_dir,
|
||||
virtual_mode=True,
|
||||
env={"PATH": "/usr/bin:/bin"})
|
||||
@@ -55,16 +55,6 @@ class StateBk(BaseFilesystemBackend):
|
||||
self.skills_dict = build_skill_fs_dict(self.config.skills_dir)
|
||||
self.backend = lambda rt : StateBackend(rt)
|
||||
|
||||
def get_backend(self):
|
||||
return self.backend
|
||||
|
||||
def _get_rt_skill_dir(self)->List[str]:
|
||||
"""get runtime skill dir"""
|
||||
return [self.config.rt_skills_dir]
|
||||
|
||||
def get_inf_inp(self):
|
||||
"""get inference input for deepagent"""
|
||||
return {"files":self.skills_dict}
|
||||
|
||||
def get_deepagent_params(self):
|
||||
return {"skills" : self._get_rt_skill_dir()}
|
||||
@@ -5,7 +5,7 @@ from lang_agent.graphs.routing import RoutingConfig, RoutingGraph
|
||||
from lang_agent.graphs.dual_path import DualConfig, Dual
|
||||
from lang_agent.graphs.vision_routing import VisionRoutingConfig, VisionRoutingGraph
|
||||
# from lang_agent.graphs.child_demo import ChildDemoGraphConfig, ChildDemoGraph
|
||||
from lang_agent.graphs.qt_deepagents import DeepAgentConfig
|
||||
from lang_agent.graphs.deepagents_qt import DeepAgentConfig
|
||||
|
||||
graph_dict = {
|
||||
"react": ReactGraphConfig(),
|
||||
|
||||
@@ -12,12 +12,14 @@ from deepagents import create_deep_agent
|
||||
|
||||
from lang_agent.utils import make_llm
|
||||
from lang_agent.components.tool_manager import ToolManager, ToolManagerConfig
|
||||
from lang_agent.fs_bkends import StateBk, StateBkConfig
|
||||
from lang_agent.components.prompt_store import build_prompt_store
|
||||
from lang_agent.graphs.graph_states import State
|
||||
from lang_agent.config import LLMNodeConfig
|
||||
from lang_agent.base import GraphBase
|
||||
|
||||
# from lang_agent.fs_bkends import StateBk, StateBkConfig, LocalShell, LocalShellConfig, DaytonaSandboxBk, DaytonaSandboxConfig
|
||||
from lang_agent.fs_bkends import BaseFilesystemBackend, StateBkConfig, AnnotatedStateBk
|
||||
|
||||
@tyro.conf.configure(tyro.conf.SuppressFixed)
|
||||
@dataclass
|
||||
class DeepAgentConfig(LLMNodeConfig):
|
||||
@@ -28,7 +30,9 @@ class DeepAgentConfig(LLMNodeConfig):
|
||||
|
||||
tool_manager_config: ToolManagerConfig = field(default_factory=ToolManagerConfig)
|
||||
|
||||
file_backend_config: StateBkConfig = field(default_factory=StateBkConfig)
|
||||
# file_backend_config: StateBkConfig = field(default_factory=StateBkConfig)
|
||||
# file_backend_config: LocalShellConfig = field(default_factory=LocalShellConfig)
|
||||
file_backend_config: AnnotatedStateBk = field(default_factory=StateBkConfig)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
@@ -47,7 +51,7 @@ class DeepAgent(GraphBase):
|
||||
tags=["main_llm"])
|
||||
|
||||
self.tool_man: ToolManager = self.config.tool_manager_config.setup()
|
||||
self.file_backend: StateBk = self.config.file_backend_config.setup()
|
||||
self.file_backend: BaseFilesystemBackend = self.config.file_backend_config.setup()
|
||||
bkend_agent_params = self.file_backend.get_deepagent_params()
|
||||
|
||||
self.mem = MemorySaver()
|
||||
Reference in New Issue
Block a user