mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-15 18:15:33 +00:00
Add an endpoint to fix a connection tool's parameters
PUT /api/connections/<id>/tools/<tool_id>/parameters takes an action and a map of parameter to value (always use it) or null (let the model decide), checked against the action's schema; only the tool's owner can call it. Connection tools now list each action's parameters and whether they are fixed.
This commit is contained in:
1 parent
54bea5a1d8
commit
28b56e8b03
4 files changed
+171
-1
No files matched your search
@@ -518,6 +518,36 @@ class ConnectionToolPermissions(Resource):
|
||||
return make_response(jsonify({"success": True, "tool": payload}), 200)
|
||||
|
||||
|
||||
@connections_ns.route("/connections/<string:connection_id>/tools/<string:tool_id>/parameters")
|
||||
class ConnectionToolParameters(Resource):
|
||||
@api.doc(
|
||||
description=(
|
||||
"Fix or release an action's parameters: {action, parameters: {name: value | null}}. "
|
||||
"A value is sent on every call and hidden from the model; null lets the model decide. Owner only."
|
||||
)
|
||||
)
|
||||
def put(self, connection_id: str, tool_id: str):
|
||||
user_id = _user_id()
|
||||
if not user_id:
|
||||
return _unauthorized()
|
||||
body = _json_body()
|
||||
action, pins = body.get("action"), body.get("parameters")
|
||||
if not isinstance(action, str) or not isinstance(pins, dict) or not pins:
|
||||
return _error("Send the action and a map of parameters to values or null", 400)
|
||||
with db_session() as conn:
|
||||
row = _owned(conn, connection_id, user_id)
|
||||
if row is None:
|
||||
return _not_found()
|
||||
try:
|
||||
tool = service.set_tool_parameters(conn, user_id, connection_id, tool_id, action, pins)
|
||||
except ValueError as err:
|
||||
return _error(str(err), 400)
|
||||
if tool is None:
|
||||
return _not_found()
|
||||
payload = service.serialize_tool(tool)
|
||||
return make_response(jsonify({"success": True, "tool": payload}), 200)
|
||||
|
||||
|
||||
@connections_ns.route("/connections/<string:connection_id>/refresh-tools")
|
||||
class ConnectionRefreshTools(Resource):
|
||||
@api.doc(description="MCP: re-scan the server's actions and return what was added and removed")
|
||||
|
||||
@@ -151,8 +151,34 @@ def serialize_source(row: dict, connection_status: Optional[str] = None) -> dict
|
||||
}
|
||||
|
||||
|
||||
def serialize_parameters(action: dict) -> list[dict]:
|
||||
"""An action's parameters as the connection drawer shows them.
|
||||
|
||||
``fixed`` parameters are sent with ``value`` on every call and hidden
|
||||
from the model; the others are left to it.
|
||||
"""
|
||||
from docsgpt.agents.tool_pins import is_pinned, iter_parameters
|
||||
|
||||
schema = action.get("parameters") if isinstance(action.get("parameters"), dict) else {}
|
||||
required = set(schema.get("required") or []) if isinstance(schema.get("required"), list) else set()
|
||||
parameters = []
|
||||
for _section, name, details in iter_parameters(action):
|
||||
fixed = is_pinned(details)
|
||||
parameters.append(
|
||||
{
|
||||
"name": name,
|
||||
"description": details.get("description") or "",
|
||||
"type": details.get("type") if isinstance(details.get("type"), str) else "string",
|
||||
"required": bool(details.get("required")) or name in required,
|
||||
"fixed": fixed,
|
||||
"value": details.get("value") if fixed else None,
|
||||
}
|
||||
)
|
||||
return parameters
|
||||
|
||||
|
||||
def serialize_tool(row: dict) -> dict:
|
||||
"""A tool linked to a connection, with its actions and their permissions."""
|
||||
"""A tool linked to a connection, with its actions, permissions and parameters."""
|
||||
from docsgpt.connectors.permissions import action_access, action_permission
|
||||
|
||||
actions = []
|
||||
@@ -166,6 +192,7 @@ def serialize_tool(row: dict) -> dict:
|
||||
"description": action.get("description", ""),
|
||||
"access": access,
|
||||
"permission": action_permission(action),
|
||||
"parameters": serialize_parameters(action),
|
||||
}
|
||||
)
|
||||
return {
|
||||
@@ -1277,6 +1304,46 @@ def set_tool_permissions(
|
||||
return tools.get_any(str(tool["id"]), user_id)
|
||||
|
||||
|
||||
def set_tool_parameters(
|
||||
conn, user_id: str, connection_id: str, tool_id: str, action_name: str, pins: dict,
|
||||
) -> Optional[dict]:
|
||||
"""Fix or release parameters of one action of a tool the user owns.
|
||||
|
||||
Args:
|
||||
conn: Open database connection.
|
||||
user_id: The caller, who must own the tool.
|
||||
connection_id: The connection the tool must belong to.
|
||||
tool_id: The tool to update.
|
||||
action_name: The action whose parameters change.
|
||||
pins: Parameter name to the value to always use, or None to let the
|
||||
model decide.
|
||||
|
||||
Returns:
|
||||
The updated tool, or None (nothing written) when the tool is not the
|
||||
user's or belongs to another connection.
|
||||
|
||||
Raises:
|
||||
ValueError: The action or a parameter does not exist, or a value does
|
||||
not fit its parameter.
|
||||
"""
|
||||
from docsgpt.agents.tool_pins import set_pins
|
||||
from docsgpt.storage.db.repositories.user_tools import UserToolsRepository
|
||||
|
||||
tools = UserToolsRepository(conn)
|
||||
tool = tools.get_any(tool_id, user_id)
|
||||
if tool is None or tool.get("user_id") != user_id or str(tool.get("connection_id")) != connection_id:
|
||||
return None
|
||||
actions = _json(tool.get("actions")) or []
|
||||
if not any(isinstance(a, dict) and a.get("name") == action_name for a in actions):
|
||||
raise ValueError(f"Unknown action: {action_name}")
|
||||
actions = [
|
||||
set_pins(action, pins) if isinstance(action, dict) and action.get("name") == action_name else action
|
||||
for action in actions
|
||||
]
|
||||
tools.update(str(tool["id"]), user_id, {"actions": actions})
|
||||
return tools.get_any(str(tool["id"]), user_id)
|
||||
|
||||
|
||||
def reencrypt_all(batch_size: int = 500) -> dict:
|
||||
"""Rewrite every connection's credentials with the current key.
|
||||
|
||||
|
||||
Reference in new issue
Block a user