Spaces:
Sleeping
Sleeping
| """GAIA agent tools. Registry assembled by build_tools().""" | |
| from __future__ import annotations | |
| from typing import Optional | |
| from langchain_core.tools import BaseTool, tool | |
| from lilith_agent.config import Config | |
| from lilith_agent.tools.audio import transcribe_audio as _transcribe_audio | |
| from lilith_agent.tools.files import ( | |
| read_file as _read_file, | |
| ls as _ls, | |
| grep as _grep, | |
| glob_files as _glob_files, | |
| find_files as _find_files, | |
| write_file as _write_file | |
| ) | |
| from lilith_agent.tools.todos import todos as _todos | |
| from lilith_agent.tools.pdf import inspect_pdf as _inspect_pdf | |
| from lilith_agent.tools.python_exec import run_python as _run_python | |
| from lilith_agent.tools.search import web_search as _web_search | |
| from lilith_agent.tools.vision import inspect_visual_content as _inspect_visual_content | |
| from lilith_agent.tools.web import fetch_url as _fetch_url | |
| from lilith_agent.tools.youtube import ( | |
| youtube_transcript as _youtube_transcript, | |
| youtube_frame_at as _youtube_frame_at, | |
| ) | |
| from lilith_agent.tools.academic import ( | |
| arxiv_search as _arxiv_search, | |
| crossref_search as _crossref_search, | |
| count_journal_articles as _count_journal_articles | |
| ) | |
| from lilith_agent.tools.filters import filter_entities as _filter_entities | |
| def build_tools(cfg: Config) -> list[BaseTool]: | |
| """Build the list of LangChain tools, injecting config via closures.""" | |
| def web_search(query: str, max_results: int = 5) -> str: | |
| """Search the web for general information using DuckDuckGo (primary) and Tavily (fallback). | |
| Returns a list of title/url/snippet results. This is your primary web search tool.""" | |
| return _web_search(query, api_key=cfg.tavily_api_key, max_results=max_results) | |
| def fetch_url(url: str, max_chars: int = 8000) -> str: | |
| """Fetch a URL and return its extracted main text (trafilatura).""" | |
| return _fetch_url(url, max_chars=max_chars) | |
| def run_python(code: str, timeout: int = 30) -> str: | |
| """Run Python code in a sandboxed subprocess and return stdout + last expression. | |
| ISOLATED FROM THE HOST FILESYSTEM. This tool runs inside a container (or a | |
| scrubbed subprocess with cwd pinned to a throwaway tmpdir). It CANNOT open | |
| files from the user's workspace — paths like './notes.md', '.lilith/...', | |
| or any relative path resolve against the sandbox's own empty scratch dir, | |
| not the repo. Any file written here vanishes when the call returns. | |
| To move data across the boundary: | |
| - To READ a workspace file, call `read_file` first and pass the returned | |
| text into `run_python` as a string literal in `code`. | |
| - To PERSIST output the user can see, call `write_file` — do NOT try to | |
| write from inside `run_python`. | |
| STATELESS EXECUTION: The Python environment resets on every call! Functions, variables, or state created in one `run_python` call DO NOT persist to the next. If you need to reuse code or state, you must pass it back into the next script. | |
| AVAILABLE LIBRARIES: requests, beautifulsoup4 (bs4), pandas, trafilatura, openpyxl, faster-whisper, pypdf. | |
| CRITICAL: If you use this tool to scrape websites, you MUST include a 'User-Agent' header. | |
| Example: headers = {'User-Agent': 'Mozilla/5.0...'} | |
| """ | |
| return _run_python(code, timeout=timeout) | |
| def read_file(path: str, start_line: Optional[int] = None, end_line: Optional[int] = None, max_chars: int = 20000) -> str: | |
| """Read a local file with optional line-based chunking (start_line/end_line). Returns text. | |
| `path` accepts `~/...` for the user's home directory and absolute paths; reads the HOST filesystem (not the `run_python` sandbox).""" | |
| return _read_file(path, start_line=start_line, end_line=end_line, max_chars=max_chars) | |
| def ls(path: str = ".") -> str: | |
| """List contents of a directory. Use this to find available files. | |
| `path` accepts `~/...` for the user's home directory and absolute paths; lists the HOST filesystem (not the `run_python` sandbox).""" | |
| return _ls(path) | |
| def grep(pattern: str, path: str) -> str: | |
| """Search for a pattern in a file. Returns matching lines with line numbers. | |
| `path` accepts `~/...` for the user's home directory and absolute paths; searches the HOST filesystem (not the `run_python` sandbox).""" | |
| return _grep(pattern, path) | |
| def glob_files(pattern: str) -> str: | |
| """Find files matching a glob pattern (e.g., '**/*.csv'). | |
| `pattern` accepts `~/...` and absolute patterns (e.g. `/Users/me/code/**/*.py`); operates on the HOST filesystem (not the `run_python` sandbox).""" | |
| return _glob_files(pattern) | |
| def find_files(name: str, root: str = ".", max_results: int = 200) -> str: | |
| """Locate files by name under `root` using unix `find` (fast deep search). Use this instead of walking with `ls` when looking for a known filename in a large tree. | |
| `root` accepts `~/...` and absolute paths and operates on the HOST filesystem; NEVER pass `root='/'` (will time out scanning the whole disk) — start from a focused subtree like `~/code` or `~`. Default excludes already skip `node_modules`/`.git`/`.venv`/`__pycache__`/etc.""" | |
| return _find_files(name, root=root, max_results=max_results) | |
| def write_file(path: str, content: str) -> str: | |
| """Persist text to a file the user can open afterwards. | |
| This is the ONLY tool that writes to the user's workspace. `run_python` | |
| runs in an isolated sandbox with no host-filesystem access, so files it | |
| creates are lost when the call returns — always route persisted output | |
| through this tool instead. | |
| `path` is relative to the agent's write root (`.lilith/scratch` by | |
| default); absolute paths and `..`-escapes are rejected. | |
| """ | |
| return _write_file(path, content) | |
| def todos(action: str, items: Optional[list[str]] = None, index: Optional[int] = None) -> str: | |
| """Manage the planning todo list. | |
| action='write': overwrite the list. Requires `items` (list of strings). | |
| action='done': mark a todo complete. Requires `index` (0-based position). | |
| """ | |
| return _todos(action, items=items, index=index) | |
| def transcribe_audio(path: str) -> str: | |
| """Transcribe an audio file using faster-whisper. Returns the transcript text.""" | |
| return _transcribe_audio(path, model_size=cfg.whisper_model) | |
| def inspect_pdf(url_or_path: str, query: str) -> str: | |
| """Extract text from a PDF (local or remote). Recommended for reading academic papers or long documents.""" | |
| return _inspect_pdf(url_or_path, query=query) | |
| def inspect_visual_content(file_path_or_url: str, prompt: str) -> str: | |
| """Use Gemini Multimodal Vision to analyze an image (PNG, JPG) or video (MP4). Use this for counting objects on screen or identifying visual details.""" | |
| return _inspect_visual_content(file_path_or_url, prompt=prompt, cfg=cfg) | |
| def youtube_transcript(url: str) -> str: | |
| """Fetch the spoken-word transcript (captions) for a YouTube video URL. | |
| Does NOT capture on-screen text overlays — use `youtube_frame_at` + `inspect_visual_content` for those.""" | |
| return _youtube_transcript(url) | |
| def youtube_frame_at(url: str, timestamp_seconds: float) -> str: | |
| """Download a YouTube video and extract ONE frame at the given timestamp as a local PNG. | |
| Returns the local PNG path — pass it to `inspect_visual_content` to read on-screen text or visual details. | |
| REQUIRED for questions about what is visible on screen at a specific time in a YouTube video | |
| (e.g. a phrase shown as a title card, or an object visible at 0:30). | |
| """ | |
| return _youtube_frame_at(url, timestamp_seconds=timestamp_seconds) | |
| def arxiv_search(query: str, max_results: int = 5) -> str: | |
| """Search arXiv for papers. Returns a list of titles, authors, and summaries. Use this for modern academic pre-prints.""" | |
| return _arxiv_search(query, max_results=max_results) | |
| def crossref_search(filter_str: str, rows: int = 100, cursor: str = "*") -> str: | |
| """Search CrossRef API for bibliographic metadata. | |
| Use filter strings like 'issn:0028-0836,type:journal-article,from-pub-date:2020,until-pub-date:2020'. | |
| Returns a JSON list of works. Pair with filter_entities to prune broad results. | |
| """ | |
| return _crossref_search(filter_str, rows=rows, cursor=cursor) | |
| def count_journal_articles(journal_name: str, year: int, is_research_only: bool = True) -> str: | |
| """High-precision tool to count articles in a journal for a given year. | |
| Use this for definitive bibliographic metric counting (e.g., 'articles in Nature in 2020'). | |
| It handles complex filtering and official source scraping internally. | |
| """ | |
| return _count_journal_articles(journal_name, year, is_research_only=is_research_only) | |
| def filter_entities(entities: list[dict], keep_conditions: list[str] = None, remove_conditions: list[str] = None) -> str: | |
| """Filter a list of entities based on conditions. Returns a metadata-rich JSON. | |
| Use this when a prior tool (like crossref_search) returns a 'broad' list that needs pruning (e.g., removing book reviews). | |
| """ | |
| return _filter_entities(entities, keep_conditions=keep_conditions, remove_conditions=remove_conditions) | |
| def search_memory(query: str) -> str: | |
| """Search Lilith's long-term memory (semantic facts and episodic experiences) for information | |
| matching the given query. Use this when the system context mentions omitted facts, or when | |
| you need to recall specific past experiences, user preferences, or project details that may | |
| not be in the current context window.""" | |
| from lilith_agent.memory import search_memory_store | |
| return search_memory_store(query) | |
| return [ | |
| web_search, | |
| fetch_url, | |
| run_python, | |
| read_file, | |
| transcribe_audio, | |
| youtube_transcript, | |
| youtube_frame_at, | |
| inspect_pdf, | |
| inspect_visual_content, | |
| arxiv_search, | |
| crossref_search, | |
| count_journal_articles, | |
| filter_entities, | |
| ls, | |
| grep, | |
| glob_files, | |
| find_files, | |
| write_file, | |
| todos, | |
| search_memory, | |
| ] | |