feat(library-desk): add AI agent tools router
Tools Router: - Expose library-desk capabilities as AI tool endpoints - Support function calling for LLM agents - Query wiki pages, search knowledge base - Access graph entities and relationships Tools Models: - ToolDefinition for function schemas - ToolParameter specifications - ToolResponse format - OpenAI function calling compatible
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Tool catalog models for Library Desk.
|
||||
|
||||
Provides simplified tool definitions optimized for AI agent consumption.
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict, Any, Optional
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ParameterType(str, Enum):
|
||||
"""Parameter data types."""
|
||||
STRING = "string"
|
||||
INTEGER = "integer"
|
||||
BOOLEAN = "boolean"
|
||||
ARRAY = "array"
|
||||
OBJECT = "object"
|
||||
|
||||
|
||||
class ToolParameter(BaseModel):
|
||||
"""Tool parameter definition."""
|
||||
name: str = Field(..., description="Parameter name")
|
||||
type: ParameterType = Field(..., description="Parameter type")
|
||||
description: str = Field(..., description="Parameter description")
|
||||
required: bool = Field(default=False, description="Whether parameter is required")
|
||||
default: Optional[Any] = Field(default=None, description="Default value if not required")
|
||||
example: Optional[Any] = Field(default=None, description="Example value")
|
||||
|
||||
|
||||
class ToolDefinition(BaseModel):
|
||||
"""Individual tool definition."""
|
||||
name: str = Field(..., description="Tool identifier (e.g., 'wiki_create_page')")
|
||||
category: str = Field(..., description="Tool category (e.g., 'wiki', 'graph')")
|
||||
description: str = Field(..., description="What this tool does")
|
||||
method: str = Field(..., description="HTTP method (GET, POST, PUT, DELETE)")
|
||||
endpoint: str = Field(..., description="API endpoint path")
|
||||
parameters: List[ToolParameter] = Field(default_factory=list, description="Tool parameters")
|
||||
returns: str = Field(..., description="What the tool returns")
|
||||
example: Optional[Dict[str, Any]] = Field(default=None, description="Example request")
|
||||
fast: bool = Field(default=True, description="Whether operation completes quickly (<5s)")
|
||||
|
||||
|
||||
class CategoryInfo(BaseModel):
|
||||
"""Tool category information."""
|
||||
name: str = Field(..., description="Category name")
|
||||
description: str = Field(..., description="Category description")
|
||||
tool_count: int = Field(..., description="Number of tools in category")
|
||||
|
||||
|
||||
class ToolCatalog(BaseModel):
|
||||
"""Complete tool catalog response."""
|
||||
service: str = Field(default="library-desk", description="Service name")
|
||||
version: str = Field(default="1.0.0", description="API version")
|
||||
base_url: str = Field(..., description="Base URL for API")
|
||||
categories: List[CategoryInfo] = Field(..., description="Available categories")
|
||||
tools: List[ToolDefinition] = Field(..., description="All available tools")
|
||||
authentication: str = Field(
|
||||
default="Bearer token via Authorization header",
|
||||
description="Authentication method"
|
||||
)
|
||||
@@ -0,0 +1,334 @@
|
||||
"""
|
||||
Tools router for Library Desk API.
|
||||
|
||||
Provides tool discovery endpoint for AI agents.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from src.models.tools import (
|
||||
ToolCatalog, ToolDefinition, ToolParameter, CategoryInfo, ParameterType
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/tools", tags=["Tools"])
|
||||
|
||||
|
||||
def get_wiki_tools() -> list[ToolDefinition]:
|
||||
"""Get wiki tool definitions."""
|
||||
return [
|
||||
ToolDefinition(
|
||||
name="wiki_list_pages",
|
||||
category="wiki",
|
||||
description="List wiki pages for a user with optional tag filtering",
|
||||
method="GET",
|
||||
endpoint="/wiki/pages",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer",
|
||||
example="jpmschweitzer"
|
||||
),
|
||||
ToolParameter(
|
||||
name="tag",
|
||||
type=ParameterType.STRING,
|
||||
description="Filter by tag (dossier)",
|
||||
required=False,
|
||||
example="projects"
|
||||
),
|
||||
ToolParameter(
|
||||
name="limit",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Maximum pages to return",
|
||||
required=False,
|
||||
default=50,
|
||||
example=20
|
||||
),
|
||||
],
|
||||
returns="List of pages with summaries",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_get_page",
|
||||
category="wiki",
|
||||
description="Get a single wiki page by ID with full content",
|
||||
method="GET",
|
||||
endpoint="/wiki/pages/{page_id}",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="page_id",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Page ID to retrieve",
|
||||
required=True,
|
||||
example=123
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier for access control",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
],
|
||||
returns="Complete page object with content",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_create_page",
|
||||
category="wiki",
|
||||
description="Create a new wiki page in user's namespace",
|
||||
method="POST",
|
||||
endpoint="/wiki/pages",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="title",
|
||||
type=ParameterType.STRING,
|
||||
description="Page title",
|
||||
required=True,
|
||||
example="Project Documentation"
|
||||
),
|
||||
ToolParameter(
|
||||
name="path",
|
||||
type=ParameterType.STRING,
|
||||
description="Page path (will be prefixed with user namespace)",
|
||||
required=True,
|
||||
example="/projects/my-project"
|
||||
),
|
||||
ToolParameter(
|
||||
name="content",
|
||||
type=ParameterType.STRING,
|
||||
description="Page content in Markdown format",
|
||||
required=True,
|
||||
example="# Overview\n\nThis is the content."
|
||||
),
|
||||
ToolParameter(
|
||||
name="description",
|
||||
type=ParameterType.STRING,
|
||||
description="Short page description",
|
||||
required=False,
|
||||
example="Documentation for my project"
|
||||
),
|
||||
ToolParameter(
|
||||
name="tags",
|
||||
type=ParameterType.ARRAY,
|
||||
description="Tags for categorization (dossiers)",
|
||||
required=False,
|
||||
example=["projects", "documentation"]
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
],
|
||||
returns="Created page object",
|
||||
example={
|
||||
"title": "My Project",
|
||||
"path": "/projects/my-project",
|
||||
"content": "# My Project\n\nProject description here.",
|
||||
"tags": ["projects"],
|
||||
"user": "jpmschweitzer"
|
||||
},
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_update_page",
|
||||
category="wiki",
|
||||
description="Update an existing wiki page",
|
||||
method="PUT",
|
||||
endpoint="/wiki/pages/{page_id}",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="page_id",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Page ID to update",
|
||||
required=True,
|
||||
example=123
|
||||
),
|
||||
ToolParameter(
|
||||
name="title",
|
||||
type=ParameterType.STRING,
|
||||
description="New page title",
|
||||
required=False
|
||||
),
|
||||
ToolParameter(
|
||||
name="content",
|
||||
type=ParameterType.STRING,
|
||||
description="New page content",
|
||||
required=False
|
||||
),
|
||||
ToolParameter(
|
||||
name="description",
|
||||
type=ParameterType.STRING,
|
||||
description="New description",
|
||||
required=False
|
||||
),
|
||||
ToolParameter(
|
||||
name="tags",
|
||||
type=ParameterType.ARRAY,
|
||||
description="New tags",
|
||||
required=False
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
],
|
||||
returns="Updated page object",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_delete_page",
|
||||
category="wiki",
|
||||
description="Delete a wiki page",
|
||||
method="DELETE",
|
||||
endpoint="/wiki/pages/{page_id}",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="page_id",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Page ID to delete",
|
||||
required=True,
|
||||
example=123
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
],
|
||||
returns="Success confirmation",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_search_pages",
|
||||
category="wiki",
|
||||
description="Search wiki pages by content within user's namespace",
|
||||
method="GET",
|
||||
endpoint="/wiki/search",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="q",
|
||||
type=ParameterType.STRING,
|
||||
description="Search query",
|
||||
required=True,
|
||||
example="docker configuration"
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
ToolParameter(
|
||||
name="limit",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Maximum results",
|
||||
required=False,
|
||||
default=20
|
||||
),
|
||||
],
|
||||
returns="List of matching pages",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_list_dossiers",
|
||||
category="wiki",
|
||||
description="List all dossiers (unique tags) for a user with page counts",
|
||||
method="GET",
|
||||
endpoint="/wiki/dossiers",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
],
|
||||
returns="List of dossiers with page counts",
|
||||
fast=True
|
||||
),
|
||||
ToolDefinition(
|
||||
name="wiki_get_dossier_pages",
|
||||
category="wiki",
|
||||
description="Get all pages in a specific dossier",
|
||||
method="GET",
|
||||
endpoint="/wiki/dossiers/{dossier_name}/pages",
|
||||
parameters=[
|
||||
ToolParameter(
|
||||
name="dossier_name",
|
||||
type=ParameterType.STRING,
|
||||
description="Dossier name (tag)",
|
||||
required=True,
|
||||
example="projects"
|
||||
),
|
||||
ToolParameter(
|
||||
name="user",
|
||||
type=ParameterType.STRING,
|
||||
description="User identifier",
|
||||
required=False,
|
||||
default="jpmschweitzer"
|
||||
),
|
||||
ToolParameter(
|
||||
name="limit",
|
||||
type=ParameterType.INTEGER,
|
||||
description="Maximum pages",
|
||||
required=False,
|
||||
default=100
|
||||
),
|
||||
],
|
||||
returns="List of pages in dossier",
|
||||
fast=True
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@router.get("", response_model=ToolCatalog)
|
||||
async def get_tool_catalog() -> ToolCatalog:
|
||||
"""
|
||||
Get simplified tool catalog for AI agent consumption.
|
||||
|
||||
This endpoint provides a machine-readable catalog of all Library Desk tools,
|
||||
optimized for discovery and use by AI agents like The Librarian.
|
||||
|
||||
Returns tool definitions with:
|
||||
- Clear descriptions
|
||||
- Parameter specifications
|
||||
- Usage examples
|
||||
- Performance characteristics
|
||||
"""
|
||||
wiki_tools = get_wiki_tools()
|
||||
|
||||
# Calculate category stats
|
||||
categories = {}
|
||||
for tool in wiki_tools:
|
||||
if tool.category not in categories:
|
||||
categories[tool.category] = 0
|
||||
categories[tool.category] += 1
|
||||
|
||||
category_info = [
|
||||
CategoryInfo(
|
||||
name="wiki",
|
||||
description="Wiki.js page and dossier management operations",
|
||||
tool_count=categories.get("wiki", 0)
|
||||
)
|
||||
]
|
||||
|
||||
return ToolCatalog(
|
||||
service="library-desk",
|
||||
version="1.0.0",
|
||||
base_url="http://library-desk:8089",
|
||||
categories=category_info,
|
||||
tools=wiki_tools,
|
||||
authentication="Bearer token via Authorization header (LIBRARY_API_KEY)"
|
||||
)
|
||||
Reference in New Issue
Block a user