|
| 1 | +--- |
| 2 | +title: "Mirage" |
| 3 | +id: integrations-mirage |
| 4 | +description: "Mirage integration for Haystack" |
| 5 | +slug: "/integrations-mirage" |
| 6 | +--- |
| 7 | + |
| 8 | + |
| 9 | +## haystack_integrations.tools.mirage.shell_tool |
| 10 | + |
| 11 | +### MirageShellTool |
| 12 | + |
| 13 | +Bases: <code>Tool</code> |
| 14 | + |
| 15 | +A Haystack `Tool` that lets an `Agent` run bash commands across a Mirage virtual filesystem. |
| 16 | + |
| 17 | +Mirage mounts heterogeneous backends (object storage, databases, SaaS apps, local disk) as one |
| 18 | +filesystem; this tool exposes Mirage's single `execute` surface to an Agent as one well-described |
| 19 | +tool with a `command` parameter. Output is normalized to text and truncated before it reaches the |
| 20 | +model. |
| 21 | + |
| 22 | +### Security model |
| 23 | + |
| 24 | +Mirage never shells out to the host: every command runs inside Mirage's own virtual-filesystem |
| 25 | +interpreter, so the blast radius is confined to the mounts you attach. Two controls shape what an |
| 26 | +Agent can do: |
| 27 | + |
| 28 | +- **Per-mount read-only mode** (`MirageMount(..., read_only=True)`) is the authoritative write |
| 29 | + boundary. Mirage refuses any write to a read-only mount regardless of the command used, so this |
| 30 | + -- not the allowlist -- is how you prevent modification or deletion. Mount anything the Agent |
| 31 | + should not change as read-only. |
| 32 | +- **The command allowlist** (`allowed_commands`) restricts *which* commands may run. It is |
| 33 | + enforced against every command Mirage would execute, including commands nested inside |
| 34 | + `$(...)`, backticks, `<(...)` and subshells, so `ls "$(rm x)"` is rejected unless `rm` |
| 35 | + is also allowed. Treat it as a best-effort filter to steer the Agent, not a sandbox: allowing a |
| 36 | + command that itself runs other commands (`eval`, `bash`, `sh`, `source`, `xargs`, |
| 37 | + `timeout`) effectively allows anything, so do not list those for untrusted/hosted use. |
| 38 | +- **`denied_paths`** rejects any command whose text references one of the given path substrings. |
| 39 | + |
| 40 | +### Usage example |
| 41 | + |
| 42 | +```python |
| 43 | +from haystack.components.agents import Agent |
| 44 | +from haystack.components.generators.chat import OpenAIChatGenerator |
| 45 | +from haystack.dataclasses import ChatMessage |
| 46 | +from haystack_integrations.tools.mirage import MirageWorkspace, MirageMount, MirageShellTool |
| 47 | + |
| 48 | +workspace = MirageWorkspace([ |
| 49 | + MirageMount(path="/data", resource="ram"), |
| 50 | + MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True), |
| 51 | +]) |
| 52 | +tool = MirageShellTool(workspace, allowed_commands=["ls", "cat", "grep", "head", "wc", "cp"]) |
| 53 | + |
| 54 | +agent = Agent(chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=[tool]) |
| 55 | +result = agent.run(messages=[ChatMessage.from_user("How many lines in /s3/log.txt mention 'alert'?")]) |
| 56 | +print(result["messages"][-1].text) |
| 57 | +``` |
| 58 | + |
| 59 | +#### __init__ |
| 60 | + |
| 61 | +```python |
| 62 | +__init__( |
| 63 | + workspace: MirageWorkspace, |
| 64 | + *, |
| 65 | + name: str = "mirage_shell", |
| 66 | + description: str | None = None, |
| 67 | + invocation_timeout: float = 60.0, |
| 68 | + max_output_chars: int = 20000, |
| 69 | + allowed_commands: list[str] | None = None, |
| 70 | + denied_paths: list[str] | None = None |
| 71 | +) -> None |
| 72 | +``` |
| 73 | + |
| 74 | +Initialize the Mirage shell tool. |
| 75 | + |
| 76 | +**Parameters:** |
| 77 | + |
| 78 | +- **workspace** (<code>MirageWorkspace</code>) – The :class:`MirageWorkspace` describing the mount tree. |
| 79 | +- **name** (<code>str</code>) – Tool name exposed to the LLM. |
| 80 | +- **description** (<code>str | None</code>) – Custom description. If None, one is generated from the mount tree. |
| 81 | +- **invocation_timeout** (<code>float</code>) – Maximum seconds to wait for a command to finish. |
| 82 | +- **max_output_chars** (<code>int</code>) – Truncate command output to this many characters before returning it. |
| 83 | +- **allowed_commands** (<code>list\[str\] | None</code>) – If set, only these command names may run, e.g. |
| 84 | + `["ls", "cat", "grep", "head", "wc"]`. The allowlist is enforced against *every* command |
| 85 | + Mirage would execute -- including commands nested in substitutions/subshells -- so |
| 86 | + `ls "$(rm x)"` is rejected unless `rm` is also allowed. It is a filter over Mirage's |
| 87 | + virtual commands to steer the Agent, not a security sandbox; the write boundary is |
| 88 | + per-mount `read_only` (see the class "Security model" section). If None, any command is |
| 89 | + allowed (not recommended for untrusted/hosted use). |
| 90 | +- **denied_paths** (<code>list\[str\] | None</code>) – If set, any command referencing one of these path substrings is rejected. |
| 91 | + |
| 92 | +#### warm_up |
| 93 | + |
| 94 | +```python |
| 95 | +warm_up() -> None |
| 96 | +``` |
| 97 | + |
| 98 | +Build the underlying live workspace eagerly. Called by `Agent.warm_up()`/`Pipeline.warm_up()`. |
| 99 | + |
| 100 | +#### to_dict |
| 101 | + |
| 102 | +```python |
| 103 | +to_dict() -> dict[str, Any] |
| 104 | +``` |
| 105 | + |
| 106 | +Serialize the tool to a dictionary in the `{"type": ..., "data": ...}` format. |
| 107 | + |
| 108 | +#### from_dict |
| 109 | + |
| 110 | +```python |
| 111 | +from_dict(data: dict[str, Any]) -> MirageShellTool |
| 112 | +``` |
| 113 | + |
| 114 | +Deserialize the tool from a dictionary. |
| 115 | + |
| 116 | +#### close |
| 117 | + |
| 118 | +```python |
| 119 | +close() -> None |
| 120 | +``` |
| 121 | + |
| 122 | +Close the underlying workspace. |
| 123 | + |
| 124 | +## haystack_integrations.tools.mirage.workspace |
| 125 | + |
| 126 | +### MirageMount |
| 127 | + |
| 128 | +Declarative description of a single backend mounted into a :class:`MirageWorkspace`. |
| 129 | + |
| 130 | +A mount is the serializable unit of a Mirage workspace: it names *where* a backend is mounted |
| 131 | +(`path`), *which* backend it is (`resource`, a Mirage registry name such as `"s3"` or `"gdrive"`), |
| 132 | +and *how* to configure it (`config`). |
| 133 | + |
| 134 | +`config` values may be plain values, Haystack `Secret` objects for credentials, or an OAuth token |
| 135 | +source (e.g. `OAuthRefreshTokenSource`) for backends whose config accepts a token-provider callable |
| 136 | +(such as Mirage's OneDrive `access_token`). Secrets and token sources are resolved only when the |
| 137 | +live workspace is built. |
| 138 | + |
| 139 | +Every backend is created the same way. Use the Mirage registry name and the config keys that backend expects |
| 140 | +(discover names with `MirageMount.available_resources()`; config keys come from the backend's Mirage config class): |
| 141 | + |
| 142 | +```python |
| 143 | +from haystack.utils import Secret |
| 144 | + |
| 145 | +MirageMount(path="/data", resource="ram") # in-memory scratch |
| 146 | +MirageMount(path="/local", resource="disk", config={"root": "/srv/data"}) # local disk |
| 147 | +MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True) |
| 148 | +MirageMount( |
| 149 | + path="/drive", |
| 150 | + resource="gdrive", |
| 151 | + config={"client_id": "...", "refresh_token": Secret.from_env_var("GDRIVE_REFRESH_TOKEN")}, |
| 152 | + read_only=True, |
| 153 | +) |
| 154 | +``` |
| 155 | + |
| 156 | +**Parameters:** |
| 157 | + |
| 158 | +- **path** (<code>str</code>) – Mount point in the virtual filesystem, e.g. `"/s3"`. |
| 159 | +- **resource** (<code>str</code>) – Mirage registry name of the backend, e.g. `"ram"`, `"disk"`, `"s3"`, `"gdrive"`. |
| 160 | + See `mirage.resource.registry.REGISTRY` or `MirageMount.available_resources()` for the full list. |
| 161 | +- **config** (<code>dict\[str, Any\]</code>) – Keyword arguments passed to the backend's Mirage config. Values may be `Secret`s, or |
| 162 | + an OAuth token source that is turned into a token-provider callable when the workspace is built. |
| 163 | +- **read_only** (<code>bool</code>) – If True, the mount is mounted in Mirage's READ mode and writes are rejected by |
| 164 | + Mirage itself. |
| 165 | + |
| 166 | +#### available_resources |
| 167 | + |
| 168 | +```python |
| 169 | +available_resources() -> list[str] |
| 170 | +``` |
| 171 | + |
| 172 | +Return the Mirage registry names usable as `resource`. |
| 173 | + |
| 174 | +These are short backend names such as `"s3"`, `"gdrive"`, `"postgres"`. Pass one to |
| 175 | +`MirageMount(resource=...)`; the config keys each backend expects come from its Mirage |
| 176 | +config class. |
| 177 | + |
| 178 | +### MirageWorkspace |
| 179 | + |
| 180 | +A description of a Mirage mount tree that lazily builds a live `mirage.Workspace`. |
| 181 | + |
| 182 | +`MirageWorkspace` is the shared backend behind the Mirage tools and components: it holds the list of |
| 183 | +:class:`MirageMount`s and the cache configuration, serializes cleanly (resolving `Secret`s only at |
| 184 | +build time), and constructs the live workspace on first use via Mirage's resource registry. |
| 185 | + |
| 186 | +### Usage example |
| 187 | + |
| 188 | +```python |
| 189 | +from haystack.utils import Secret |
| 190 | +from haystack_integrations.tools.mirage import MirageWorkspace, MirageMount |
| 191 | + |
| 192 | +ws = MirageWorkspace( |
| 193 | + mounts=[ |
| 194 | + MirageMount(path="/data", resource="ram"), |
| 195 | + MirageMount(path="/s3", resource="s3", config={"bucket": "my-bucket"}, read_only=True), |
| 196 | + ] |
| 197 | +) |
| 198 | +print(ws.run("ls /s3")) |
| 199 | +``` |
| 200 | + |
| 201 | +#### __init__ |
| 202 | + |
| 203 | +```python |
| 204 | +__init__( |
| 205 | + mounts: list[MirageMount], *, cache_limit: str | int = "512MB" |
| 206 | +) -> None |
| 207 | +``` |
| 208 | + |
| 209 | +Initialize the workspace description. |
| 210 | + |
| 211 | +**Parameters:** |
| 212 | + |
| 213 | +- **mounts** (<code>list\[MirageMount\]</code>) – The backends to mount, as a list of :class:`MirageMount`. |
| 214 | +- **cache_limit** (<code>str | int</code>) – Mirage file-cache size limit (e.g. `"512MB"` or an int byte count). |
| 215 | + |
| 216 | +**Raises:** |
| 217 | + |
| 218 | +- <code>MirageConfigError</code> – If no mounts are provided or mount paths are not unique. |
| 219 | + |
| 220 | +#### warm_up |
| 221 | + |
| 222 | +```python |
| 223 | +warm_up() -> None |
| 224 | +``` |
| 225 | + |
| 226 | +Build the live `mirage.Workspace` eagerly. Idempotent. |
| 227 | + |
| 228 | +#### close |
| 229 | + |
| 230 | +```python |
| 231 | +close() -> None |
| 232 | +``` |
| 233 | + |
| 234 | +Close the live workspace and release its resources, if it was built. Thread-safe. |
| 235 | + |
| 236 | +#### run |
| 237 | + |
| 238 | +```python |
| 239 | +run( |
| 240 | + command: str, *, timeout: float = 60.0, max_chars: int | None = None |
| 241 | +) -> str |
| 242 | +``` |
| 243 | + |
| 244 | +Run a bash `command` against the mount tree from a synchronous context and return its output. |
| 245 | + |
| 246 | +**Parameters:** |
| 247 | + |
| 248 | +- **command** (<code>str</code>) – A bash command line, e.g. `"grep -r alert /s3/logs | wc -l"`. |
| 249 | +- **timeout** (<code>float</code>) – Maximum seconds to wait for the command. |
| 250 | +- **max_chars** (<code>int | None</code>) – If set, truncate the returned text to this many characters. |
| 251 | + |
| 252 | +**Returns:** |
| 253 | + |
| 254 | +- <code>str</code> – Combined stdout (plus a trailing error note on non-zero exit) as a string. |
| 255 | + |
| 256 | +#### run_async |
| 257 | + |
| 258 | +```python |
| 259 | +run_async( |
| 260 | + command: str, *, timeout: float = 60.0, max_chars: int | None = None |
| 261 | +) -> str |
| 262 | +``` |
| 263 | + |
| 264 | +Async counterpart of :meth:`run`. |
| 265 | + |
| 266 | +#### to_dict |
| 267 | + |
| 268 | +```python |
| 269 | +to_dict() -> dict[str, Any] |
| 270 | +``` |
| 271 | + |
| 272 | +Serialize the workspace description to a dictionary (Secret-safe). |
| 273 | + |
| 274 | +#### from_dict |
| 275 | + |
| 276 | +```python |
| 277 | +from_dict(data: dict[str, Any]) -> MirageWorkspace |
| 278 | +``` |
| 279 | + |
| 280 | +Deserialize a workspace description from a dictionary. |
| 281 | + |
| 282 | +#### describe |
| 283 | + |
| 284 | +```python |
| 285 | +describe() -> str |
| 286 | +``` |
| 287 | + |
| 288 | +Return a human/LLM-readable summary of the mount tree (used in tool descriptions). |
0 commit comments