Metadata-Version: 2.5
Name: localist
Version: 0.1.0
Summary: Local agentic framework on Ollama — filesystem, web, shell & OS tools with a sandboxed ReAct loop
License: MIT
License-File: LICENSE
Keywords: agent,agentic,autonomous,llm,local-ai,ollama,react,tool-calling
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: duckduckgo-search>=6.0
Requires-Dist: httpx>=0.27
Requires-Dist: ollama>=0.3
Requires-Dist: plyer>=2.1
Requires-Dist: psutil>=5.9
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyperclip>=1.8
Requires-Dist: python-dotenv>=1.0
Requires-Dist: rich>=13.0
Requires-Dist: trafilatura>=1.12
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Requires-Dist: types-psutil; extra == 'dev'
Provides-Extra: ui
Requires-Dist: fastapi>=0.111; extra == 'ui'
Requires-Dist: python-multipart>=0.0.9; extra == 'ui'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'ui'
Description-Content-Type: text/markdown

# localist

**Local agentic framework on Ollama** — give your local LLM sandboxed access to filesystem, web, shell, and OS tools via a ReAct reasoning loop.

[![PyPI version](https://badge.fury.io/py/localist.svg)](https://pypi.org/project/localist/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://python.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

---

## What is this?

`localist` is a Python library that wraps any [Ollama](https://ollama.ai)-served model and gives it the ability to autonomously:

- 📂 **Read / write / delete files** inside a sandboxed workspace
- 🌐 **Search the web** (DuckDuckGo, or Brave Search API)
- 🖥️ **Run shell commands** (behind a denylist + confirmation gate)
- 📋 **Use OS features** — clipboard, notifications, process management

It implements a **ReAct-style loop** (Reason → Act → Observe → repeat) and supports both models with native tool-calling (Qwen2.5, Llama 3.1+) and text-based tool-calling for any other model.

---

## Quick Start

```bash
# Install
pip install localist

# Make sure Ollama is running
ollama serve
ollama pull qwen2.5:7b

# Run a task
localist --task "Create a file called hello.txt with today's date, then list the workspace"
```

---

## Installation

```bash
# Core library + CLI
pip install localist

# With web UI (FastAPI + Uvicorn)
pip install "localist[ui]"

# Development
pip install "localist[dev]"
```

---

## Usage

### Python API

```python
from localist import Agent

agent = Agent(model="qwen2.5:7b", workspace_root="./workspace")
result = agent.run("Search for the latest Python release and save a summary to workspace/python_news.txt")
print(result)
```

### Streaming events

```python
from localist import Agent
from localist.agent import ToolCallEvent, FinalAnswerEvent

agent = Agent(model="qwen2.5:7b")

for event in agent.stream("Summarise all .txt files in workspace"):
    if isinstance(event, ToolCallEvent):
        print(f"  🔧 Calling: {event.tool}({event.args})")
    elif isinstance(event, FinalAnswerEvent):
        print(f"\n✅ Answer:\n{event.content}")
```

### Custom tools

```python
from localist import Agent, register_tool

@register_tool
def count_words(text: str) -> int:
    """Count the number of words in a text string.

    Args:
        text: The input text to count words in.
    """
    return len(text.split())

agent = Agent(model="qwen2.5:7b")
result = agent.run("Count the words in 'The quick brown fox jumps over the lazy dog'")
```

### CLI

```bash
# Single task
localist --task "Create a Python script that generates the Fibonacci sequence"

# Custom model and workspace
localist --task "..." --model llama3.1:8b --workspace ~/myproject

# Interactive REPL
localist --interactive

# Web UI (requires pip install localist[ui])
localist --ui

# List available Ollama models
localist --list-models

# Disable confirmation prompts (for scripts/automation)
localist --task "..." --no-confirm
```

---

## Tools

| Tool | Description | Confirmation required? |
|---|---|---|
| `file_read` | Read file contents | No |
| `file_write` | Create/overwrite a file | No (overwrite) |
| `file_append` | Append to a file | No |
| `file_delete` | Delete a file | **Yes** |
| `list_dir` | List directory contents | No |
| `web_search` | Search web (DuckDuckGo / Brave) | No |
| `web_fetch` | Fetch and extract text from URL | No |
| `shell_exec` | Run a shell command | **Yes** |
| `clipboard_get` | Read clipboard | No |
| `clipboard_set` | Write clipboard | No |
| `notify` | OS desktop notification | No |
| `process_list` | List running processes | No |
| `process_kill` | Terminate a process by PID | **Yes** |

---

## Configuration

Copy `.env.example` to `.env` and edit:

```env
LOCALIST_MODEL=qwen2.5:7b
LOCALIST_WORKSPACE_ROOT=./workspace
LOCALIST_MAX_ITERATIONS=20
LOCALIST_REQUIRE_CONFIRMATION=true
BRAVE_API_KEY=          # optional — enables Brave Search API
```

All settings can also be passed programmatically:

```python
from localist.config import LocalistConfig
from localist import Agent

cfg = LocalistConfig(model="llama3.1:8b", max_iterations=10, require_confirmation=False)
agent = Agent(config=cfg)
```

---

## Safety

- **Filesystem jail**: every path is resolved against `workspace_root`; `..` traversal, symlink escapes, and absolute paths outside the workspace are rejected with `SandboxViolationError`.
- **Shell denylist**: `shell_exec` blocks `rm`, `sudo`, `curl`, `python`, shell interpreters, and other high-risk binaries. No `shell=True` anywhere.
- **Confirmation gate**: `file_delete`, `shell_exec`, and `process_kill` require explicit human approval (configurable).
- **Hard iteration cap**: the loop stops after `max_iterations` (default: 20) to prevent infinite loops.
- **Audit logging**: every tool call, result, and confirmation is written to `logs/audit_<date>.jsonl`.

---

## Model Recommendations

| Model | Tool-calling | Notes |
|---|---|---|
| `qwen2.5:7b` / `qwen2.5:32b` | Native | Best balance of speed and reliability |
| `llama3.1:8b` / `llama3.3:70b` | Native | Strong general reasoning |
| `hermes3` | Native | Optimised for agentic tasks |
| `mistral-nemo` | Native | Fast, solid tool support |
| Any other model | Text protocol | Automatic fallback with JSON repair |

---

## Build and Publish

```bash
# Install build tools
pip install build twine

# Build
python -m build

# Validate
twine check dist/*

# Upload to TestPyPI
twine upload --repository testpypi dist/*

# Upload to PyPI
twine upload dist/*
```

---

## Development

```bash
git clone https://github.com/placeholder/localist
cd localist
pip install -e ".[dev]"
pytest tests/ -v
```

---

## License

MIT — see [LICENSE](LICENSE).
