mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-22 08:38:13 +02:00
feat: add openai realtime models
This commit is contained in:
parent
53f1959edf
commit
4d7b681928
33 changed files with 1518 additions and 75 deletions
|
|
@ -1,5 +1,6 @@
|
|||
"""Custom tool execution for user-defined HTTP API tools."""
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ from loguru import logger
|
|||
|
||||
from api.db import db_client
|
||||
from api.utils.credential_auth import build_auth_header
|
||||
from api.utils.template_renderer import render_template
|
||||
|
||||
# Map tool parameter types to JSON schema types
|
||||
TYPE_MAP = {
|
||||
|
|
@ -84,10 +86,94 @@ def tool_to_function_schema(tool: Any) -> Dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
def _coerce_parameter_value(value: Any, param_type: str) -> Any:
|
||||
"""Coerce a rendered preset parameter into the configured JSON type."""
|
||||
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
if param_type == "string":
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, (dict, list)):
|
||||
return json.dumps(value)
|
||||
return str(value)
|
||||
|
||||
if param_type == "number":
|
||||
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
||||
return value
|
||||
|
||||
rendered = str(value).strip()
|
||||
if rendered == "":
|
||||
return None
|
||||
|
||||
if re.fullmatch(r"[-+]?\d+", rendered):
|
||||
return int(rendered)
|
||||
|
||||
return float(rendered)
|
||||
|
||||
if param_type == "boolean":
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
if isinstance(value, (int, float)):
|
||||
return bool(value)
|
||||
|
||||
rendered = str(value).strip().lower()
|
||||
if rendered in {"true", "1", "yes", "y", "on"}:
|
||||
return True
|
||||
if rendered in {"false", "0", "no", "n", "off"}:
|
||||
return False
|
||||
|
||||
raise ValueError(f"Cannot convert '{value}' to boolean")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _resolve_preset_parameters(
|
||||
config: Dict[str, Any],
|
||||
call_context_vars: Optional[Dict[str, Any]],
|
||||
gathered_context_vars: Optional[Dict[str, Any]],
|
||||
) -> Dict[str, Any]:
|
||||
"""Resolve fixed/template-backed parameters before executing the HTTP request."""
|
||||
|
||||
preset_parameters = config.get("preset_parameters", []) or []
|
||||
if not preset_parameters:
|
||||
return {}
|
||||
|
||||
initial_context = dict(call_context_vars or {})
|
||||
render_context: Dict[str, Any] = {
|
||||
**initial_context,
|
||||
"initial_context": initial_context,
|
||||
"gathered_context": dict(gathered_context_vars or {}),
|
||||
}
|
||||
|
||||
resolved: Dict[str, Any] = {}
|
||||
for param in preset_parameters:
|
||||
param_name = (param.get("name") or "").strip()
|
||||
if not param_name:
|
||||
continue
|
||||
|
||||
rendered = render_template(param.get("value_template", ""), render_context)
|
||||
if rendered in (None, ""):
|
||||
if param.get("required", True):
|
||||
raise ValueError(
|
||||
f"Preset parameter '{param_name}' resolved to an empty value"
|
||||
)
|
||||
continue
|
||||
|
||||
resolved[param_name] = _coerce_parameter_value(
|
||||
rendered, param.get("type", "string")
|
||||
)
|
||||
|
||||
return resolved
|
||||
|
||||
|
||||
async def execute_http_tool(
|
||||
tool: Any,
|
||||
arguments: Dict[str, Any],
|
||||
call_context_vars: Optional[Dict[str, Any]] = None,
|
||||
gathered_context_vars: Optional[Dict[str, Any]] = None,
|
||||
organization_id: Optional[int] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Execute an HTTP API tool.
|
||||
|
|
@ -95,7 +181,8 @@ async def execute_http_tool(
|
|||
Args:
|
||||
tool: ToolModel instance
|
||||
arguments: Arguments passed by the LLM (parameter name -> value)
|
||||
call_context_vars: Additional context variables from the call (unused for now)
|
||||
call_context_vars: Initial context variables available at runtime
|
||||
gathered_context_vars: Variables extracted during the conversation
|
||||
organization_id: Organization ID for credential lookup
|
||||
|
||||
Returns:
|
||||
|
|
@ -133,17 +220,31 @@ async def execute_http_tool(
|
|||
timeout_ms = config.get("timeout_ms", 5000)
|
||||
timeout_seconds = timeout_ms / 1000
|
||||
|
||||
try:
|
||||
preset_arguments = _resolve_preset_parameters(
|
||||
config, call_context_vars, gathered_context_vars
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error(f"Custom tool '{tool.name}' preset parameter error: {e}")
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
resolved_arguments = {**(arguments or {}), **preset_arguments}
|
||||
|
||||
# Build request: JSON body for POST/PUT/PATCH, query params for GET/DELETE
|
||||
body = None
|
||||
params = None
|
||||
if method in ("POST", "PUT", "PATCH"):
|
||||
body = arguments
|
||||
elif method in ("GET", "DELETE") and arguments:
|
||||
params = arguments
|
||||
body = resolved_arguments
|
||||
elif method in ("GET", "DELETE") and resolved_arguments:
|
||||
params = resolved_arguments
|
||||
|
||||
logger.info(
|
||||
f"Executing custom tool '{tool.name}' ({tool.tool_uuid}): {method} {url}"
|
||||
)
|
||||
if preset_arguments:
|
||||
logger.debug(
|
||||
f"Resolved preset parameters for '{tool.name}': {list(preset_arguments.keys())}"
|
||||
)
|
||||
logger.debug(f"Request body: {body}, params: {params}")
|
||||
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue