134 lines
3.8 KiB
Python
134 lines
3.8 KiB
Python
"""Project agent — full lifecycle management (list, get, create, update, archive, delete)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from langchain_core.tools import tool
|
|
|
|
from app.core.ws_context import execute_on_client
|
|
|
|
|
|
@tool
|
|
async def list_projects(
|
|
client_id: str = "",
|
|
include_archived: int = 0,
|
|
) -> str:
|
|
"""List projects, optionally filtered by client_id.
|
|
include_archived: 1 to include archived projects, 0 for active only (default).
|
|
"""
|
|
result = await execute_on_client(
|
|
action="select",
|
|
table="projects",
|
|
filters={
|
|
"clientId": client_id or None,
|
|
"includeArchived": bool(include_archived),
|
|
},
|
|
)
|
|
rows = result.get("rows", [])
|
|
if not rows:
|
|
return "No projects found."
|
|
lines = [f"- {r['name']} (status: {r['status']}, id: {r['id']})" for r in rows]
|
|
return f"Found {len(rows)} project(s):\n" + "\n".join(lines)
|
|
|
|
|
|
@tool
|
|
async def list_all_projects() -> str:
|
|
"""List every project regardless of client or status.
|
|
Use only when the user wants a complete cross-client overview.
|
|
"""
|
|
result = await execute_on_client(action="select", table="projects")
|
|
rows = result.get("rows", [])
|
|
if not rows:
|
|
return "No projects found."
|
|
lines = [f"- {r['name']} (status: {r['status']}, id: {r['id']})" for r in rows]
|
|
return f"All projects ({len(rows)}):\n" + "\n".join(lines)
|
|
|
|
|
|
@tool
|
|
async def get_project(project_id: str) -> str:
|
|
"""Fetch a single project by its UUID."""
|
|
result = await execute_on_client(action="get", table="projects", data={"id": project_id})
|
|
row = result.get("row")
|
|
if not row:
|
|
return f"Project {project_id} not found."
|
|
return (
|
|
f"Project: '{row['name']}' (id: {row['id']}, status: {row['status']}, "
|
|
f"clientId: {row.get('clientId', 'none')})"
|
|
)
|
|
|
|
|
|
@tool
|
|
async def create_project(
|
|
name: str,
|
|
client_id: str = "",
|
|
) -> str:
|
|
"""Create a new project.
|
|
name: human-readable project name (required)
|
|
client_id: optional UUID of the owning client
|
|
"""
|
|
result = await execute_on_client(
|
|
action="insert",
|
|
table="projects",
|
|
data={"name": name, "clientId": client_id or None},
|
|
)
|
|
row = result["row"]
|
|
return f"Project created: '{row['name']}' (id: {row['id']})"
|
|
|
|
|
|
@tool
|
|
async def update_project(
|
|
project_id: str,
|
|
name: str = "",
|
|
client_id: str = "",
|
|
status: str = "",
|
|
ai_summary: str = "",
|
|
) -> str:
|
|
"""Update a project. Only pass fields that should change.
|
|
project_id: UUID of the project (required)
|
|
status: active | archived
|
|
ai_summary: AI-generated summary text (populate only when explicitly requested)
|
|
"""
|
|
updates: dict[str, Any] = {}
|
|
if name:
|
|
updates["name"] = name
|
|
if client_id:
|
|
updates["clientId"] = client_id
|
|
if status:
|
|
updates["status"] = status
|
|
if ai_summary:
|
|
updates["aiSummary"] = ai_summary
|
|
result = await execute_on_client(
|
|
action="update",
|
|
table="projects",
|
|
data={"id": project_id, "updates": updates},
|
|
)
|
|
row = result["row"]
|
|
return f"Project updated: '{row['name']}' (id: {row['id']}, status: {row['status']})"
|
|
|
|
|
|
@tool
|
|
async def delete_project(project_id: str) -> str:
|
|
"""Permanently delete a project and orphan its tasks.
|
|
IMPORTANT: prefer update_project(status='archived') unless the user
|
|
has explicitly confirmed they want permanent deletion.
|
|
"""
|
|
await execute_on_client(action="delete", table="projects", data={"id": project_id})
|
|
return f"Project {project_id} permanently deleted."
|
|
|
|
|
|
PROJECT_TOOLS: list[Any] = [
|
|
list_projects,
|
|
list_all_projects,
|
|
get_project,
|
|
create_project,
|
|
update_project,
|
|
delete_project,
|
|
]
|
|
|
|
PROJECT_READ_TOOLS: list[Any] = [
|
|
list_projects,
|
|
list_all_projects,
|
|
get_project,
|
|
]
|