Newer
Older
navi-1 / navi / tools / content_publish.py
"""content_publish tool — publish a file for inline viewing in the chat client.

Copies the file into the content store (navi/content/<uuid>/) and returns a URL
served via /content/<uuid>/filename. The client renders it as an interactive card.
"""

from pathlib import Path

from navi.content_store import publish

from .base import Tool, ToolResult, current_session_id


class ContentPublishTool(Tool):
    name = "content_publish"
    description = (
        "Publish a file for inline viewing in the chat client. "
        "Use this when you generate or produce content the user will want to see interactively "
        "(3D models, HTML pages, SVG graphics, etc.). "
        "The file will be copied to a public URL and displayed as an embeddable card. "
        "IMPORTANT — path must be an ABSOLUTE path (e.g. /home/user/model.stl)."
    )
    parameters = {
        "type": "object",
        "properties": {
            "path": {
                "type": "string",
                "description": "Absolute path to the file to publish",
            },
            "filename": {
                "type": "string",
                "description": (
                    "Published filename shown to the user. "
                    "Defaults to the original filename."
                ),
            },
            "title": {
                "type": "string",
                "description": "Human-readable title for the content card",
            },
            "content_type": {
                "type": "string",
                "enum": ["stl", "html", "svg", "pdf", "image", "video", "unknown"],
                "description": "Content type for viewer selection. Auto-detected from extension if omitted.",
            },
        },
        "required": ["path"],
    }

    async def execute(self, params: dict) -> ToolResult:
        session_id = current_session_id.get()
        if not session_id:
            return ToolResult(success=False, output="No active session context.", error="no_session")

        src = Path(params["path"]).expanduser().resolve()
        if not src.exists():
            return ToolResult(success=False, output=f"File not found: {src}", error="not_found")
        if not src.is_file():
            return ToolResult(success=False, output=f"Path is not a file: {src}", error="not_a_file")

        try:
            info = await publish(
                session_id=session_id,
                src_path=src,
                filename=params.get("filename"),
                title=params.get("title"),
                content_type=params.get("content_type"),
            )
        except Exception as e:
            return ToolResult(success=False, output=f"Publish failed: {e}", error="publish_failed")

        return ToolResult(
            success=True,
            output=(
                f"Published: {info['title']} ({info['content_type']})\n"
                f"URL: {info['url']}\n"
                f"ID: {info['id']}"
            ),
            metadata=info,
        )