Make server tool http based

This commit is contained in:
Xin Wang
2026-02-11 11:39:45 +08:00
parent 80e1d24443
commit 4c46793169
9 changed files with 281 additions and 17 deletions

View File

@@ -92,6 +92,10 @@ class ToolResource(Base):
description: Mapped[str] = mapped_column(String(512), nullable=False, default="")
category: Mapped[str] = mapped_column(String(32), nullable=False, default="system") # system/query
icon: Mapped[str] = mapped_column(String(64), nullable=False, default="Wrench")
http_method: Mapped[str] = mapped_column(String(16), nullable=False, default="GET")
http_url: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
http_headers: Mapped[dict] = mapped_column(JSON, default=dict)
http_timeout_ms: Mapped[int] = mapped_column(Integer, default=10000)
enabled: Mapped[bool] = mapped_column(default=True)
is_system: Mapped[bool] = mapped_column(default=False)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)

View File

@@ -108,11 +108,37 @@ TOOL_ICON_MAP = {
"decrease_volume": "Volume2",
}
TOOL_HTTP_DEFAULTS = {
"current_time": {
"http_method": "GET",
"http_url": "https://worldtimeapi.org/api/ip",
"http_headers": {},
"http_timeout_ms": 10000,
},
}
def _normalize_http_method(method: Optional[str]) -> str:
normalized = str(method or "GET").strip().upper()
return normalized if normalized in {"GET", "POST", "PUT", "PATCH", "DELETE"} else "GET"
def _requires_http_request(category: str, tool_id: Optional[str]) -> bool:
if category != "query":
return False
return str(tool_id or "").strip() not in {"calculator", "code_interpreter"}
def _validate_query_http_config(*, category: str, tool_id: Optional[str], http_url: Optional[str]) -> None:
if _requires_http_request(category, tool_id) and not str(http_url or "").strip():
raise HTTPException(status_code=400, detail="http_url is required for query tools (except calculator/code_interpreter)")
def _seed_default_tools_if_empty(db: Session) -> None:
"""Seed built-in tools only when tool_resources is empty."""
if db.query(ToolResource).count() > 0:
return
for tool_id, payload in TOOL_REGISTRY.items():
http_defaults = TOOL_HTTP_DEFAULTS.get(tool_id, {})
db.add(ToolResource(
id=tool_id,
user_id=1,
@@ -120,6 +146,10 @@ def _seed_default_tools_if_empty(db: Session) -> None:
description=payload.get("description", ""),
category=TOOL_CATEGORY_MAP.get(tool_id, "system"),
icon=TOOL_ICON_MAP.get(tool_id, "Wrench"),
http_method=_normalize_http_method(http_defaults.get("http_method")),
http_url=http_defaults.get("http_url"),
http_headers=http_defaults.get("http_headers") or {},
http_timeout_ms=int(http_defaults.get("http_timeout_ms") or 10000),
enabled=True,
is_system=True,
))
@@ -128,8 +158,9 @@ def _seed_default_tools_if_empty(db: Session) -> None:
def recreate_tool_resources(db: Session) -> None:
"""Recreate tool resources table content with current built-in defaults."""
db.query(ToolResource).delete()
db.commit()
bind = db.get_bind()
ToolResource.__table__.drop(bind=bind, checkfirst=True)
ToolResource.__table__.create(bind=bind, checkfirst=True)
_seed_default_tools_if_empty(db)
@@ -189,6 +220,8 @@ def create_tool_resource(data: ToolResourceCreate, db: Session = Depends(get_db)
if candidate_id and db.query(ToolResource).filter(ToolResource.id == candidate_id).first():
raise HTTPException(status_code=400, detail="Tool ID already exists")
_validate_query_http_config(category=data.category, tool_id=candidate_id, http_url=data.http_url)
item = ToolResource(
id=candidate_id or f"tool_{str(uuid.uuid4())[:8]}",
user_id=1,
@@ -196,6 +229,10 @@ def create_tool_resource(data: ToolResourceCreate, db: Session = Depends(get_db)
description=data.description,
category=data.category,
icon=data.icon,
http_method=_normalize_http_method(data.http_method),
http_url=(data.http_url or "").strip() or None,
http_headers=data.http_headers or {},
http_timeout_ms=max(1000, int(data.http_timeout_ms or 10000)),
enabled=data.enabled,
is_system=False,
)
@@ -214,6 +251,16 @@ def update_tool_resource(id: str, data: ToolResourceUpdate, db: Session = Depend
raise HTTPException(status_code=404, detail="Tool resource not found")
update_data = data.model_dump(exclude_unset=True)
new_category = update_data.get("category", item.category)
new_http_url = update_data.get("http_url", item.http_url)
_validate_query_http_config(category=new_category, tool_id=id, http_url=new_http_url)
if "http_method" in update_data:
update_data["http_method"] = _normalize_http_method(update_data.get("http_method"))
if "http_timeout_ms" in update_data and update_data.get("http_timeout_ms") is not None:
update_data["http_timeout_ms"] = max(1000, int(update_data["http_timeout_ms"]))
for field, value in update_data.items():
setattr(item, field, value)
item.updated_at = datetime.utcnow()

View File

@@ -235,6 +235,10 @@ class ToolResourceBase(BaseModel):
description: str = ""
category: str = "system" # system/query
icon: str = "Wrench"
http_method: str = "GET"
http_url: Optional[str] = None
http_headers: Dict[str, str] = Field(default_factory=dict)
http_timeout_ms: int = 10000
enabled: bool = True
@@ -247,6 +251,10 @@ class ToolResourceUpdate(BaseModel):
description: Optional[str] = None
category: Optional[str] = None
icon: Optional[str] = None
http_method: Optional[str] = None
http_url: Optional[str] = None
http_headers: Optional[Dict[str, str]] = None
http_timeout_ms: Optional[int] = None
enabled: Optional[bool] = None