mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-22 08:38:13 +02:00
Initial Commit 🚀 🚀
This commit is contained in:
commit
4f2a629340
444 changed files with 76863 additions and 0 deletions
0
api/services/workflow/tools/__init__.py
Normal file
0
api/services/workflow/tools/__init__.py
Normal file
51
api/services/workflow/tools/calculator.py
Normal file
51
api/services/workflow/tools/calculator.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import ast
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
def safe_calculator(expr: str) -> float:
|
||||
"""
|
||||
Parse arithmetic expressions using ast and support + - * / ** and parentheses.
|
||||
"""
|
||||
allowed_nodes = {
|
||||
ast.Expression,
|
||||
ast.BinOp,
|
||||
ast.UnaryOp,
|
||||
ast.Add,
|
||||
ast.Sub,
|
||||
ast.Mult,
|
||||
ast.Div,
|
||||
ast.Pow,
|
||||
ast.USub,
|
||||
ast.UAdd,
|
||||
ast.Constant,
|
||||
ast.Load,
|
||||
ast.Mod,
|
||||
}
|
||||
|
||||
node = ast.parse(expr, mode="eval")
|
||||
if not all(isinstance(n, tuple(allowed_nodes)) for n in ast.walk(node)):
|
||||
raise ValueError("Unsupported expression")
|
||||
return eval(compile(node, "<safe_calculator>", mode="eval"))
|
||||
|
||||
|
||||
def get_calculator_tools() -> list[Dict[str, Any]]:
|
||||
"""Get calculator tool definitions for LLM function calling."""
|
||||
return [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "safe_calculator",
|
||||
"description": "Perform simple arithmetic calculations",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
"type": "string",
|
||||
"description": "Arithmetic expression to evaluate (supports +, -, *, /, **, %, and parentheses). Example: 2000 + 5000",
|
||||
}
|
||||
},
|
||||
"required": ["expression"],
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
196
api/services/workflow/tools/timezone.py
Normal file
196
api/services/workflow/tools/timezone.py
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
"""Time tools for LLM function calling - timezone and time conversion utilities."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, Optional
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class TimeResult(BaseModel):
|
||||
"""Result model for time queries."""
|
||||
|
||||
timezone: str
|
||||
datetime: str
|
||||
is_dst: bool
|
||||
|
||||
|
||||
class TimeConversionResult(BaseModel):
|
||||
"""Result model for time conversions."""
|
||||
|
||||
source: TimeResult
|
||||
target: TimeResult
|
||||
time_difference: str
|
||||
|
||||
|
||||
def get_local_timezone(local_tz_override: Optional[str] = None) -> str:
|
||||
"""
|
||||
Get the local timezone name using system timezone.
|
||||
Falls back to UTC if cannot determine.
|
||||
"""
|
||||
if local_tz_override:
|
||||
return local_tz_override
|
||||
|
||||
try:
|
||||
# Try to get timezone from datetime
|
||||
local_tz = datetime.now().astimezone().tzinfo
|
||||
if hasattr(local_tz, "key"):
|
||||
return local_tz.key
|
||||
|
||||
# Try to parse from string representation
|
||||
tz_str = str(local_tz)
|
||||
if tz_str and not tz_str.startswith("UTC"):
|
||||
return tz_str
|
||||
|
||||
# Default to UTC
|
||||
return "UTC"
|
||||
except:
|
||||
return "UTC"
|
||||
|
||||
|
||||
def get_current_time(timezone: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current time in specified timezone.
|
||||
|
||||
Args:
|
||||
timezone: IANA timezone name (e.g., 'America/New_York', 'Europe/London')
|
||||
|
||||
Returns:
|
||||
Dict containing timezone, datetime, and DST status
|
||||
"""
|
||||
try:
|
||||
tz = ZoneInfo(timezone)
|
||||
current_time = datetime.now(tz)
|
||||
|
||||
result = TimeResult(
|
||||
timezone=timezone,
|
||||
datetime=current_time.isoformat(timespec="seconds"),
|
||||
is_dst=bool(current_time.dst()),
|
||||
)
|
||||
return result.model_dump()
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid timezone '{timezone}': {str(e)}")
|
||||
|
||||
|
||||
def convert_time(
|
||||
source_timezone: str, time: str, target_timezone: str
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Convert time between timezones.
|
||||
|
||||
Args:
|
||||
source_timezone: Source IANA timezone name
|
||||
time: Time to convert in 24-hour format (HH:MM)
|
||||
target_timezone: Target IANA timezone name
|
||||
|
||||
Returns:
|
||||
Dict containing source time, target time, and time difference
|
||||
"""
|
||||
try:
|
||||
source_tz = ZoneInfo(source_timezone)
|
||||
target_tz = ZoneInfo(target_timezone)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid timezone: {str(e)}")
|
||||
|
||||
# Parse time
|
||||
try:
|
||||
parsed_time = datetime.strptime(time, "%H:%M").time()
|
||||
except ValueError:
|
||||
raise ValueError("Invalid time format. Expected HH:MM in 24-hour format")
|
||||
|
||||
# Create datetime objects
|
||||
now = datetime.now(source_tz)
|
||||
source_time = datetime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
parsed_time.hour,
|
||||
parsed_time.minute,
|
||||
tzinfo=source_tz,
|
||||
)
|
||||
|
||||
# Convert to target timezone
|
||||
target_time = source_time.astimezone(target_tz)
|
||||
|
||||
# Calculate time difference
|
||||
source_offset = source_time.utcoffset() or timedelta()
|
||||
target_offset = target_time.utcoffset() or timedelta()
|
||||
hours_difference = (target_offset - source_offset).total_seconds() / 3600
|
||||
|
||||
# Format time difference
|
||||
if hours_difference.is_integer():
|
||||
time_diff_str = f"{int(hours_difference):+d}h"
|
||||
else:
|
||||
# For fractional hours like Nepal's UTC+5:45
|
||||
hours = int(hours_difference)
|
||||
minutes = int(abs(hours_difference - hours) * 60)
|
||||
if hours_difference >= 0:
|
||||
time_diff_str = f"+{hours}h{minutes:02d}m"
|
||||
else:
|
||||
time_diff_str = f"{hours}h{minutes:02d}m"
|
||||
|
||||
result = TimeConversionResult(
|
||||
source=TimeResult(
|
||||
timezone=source_timezone,
|
||||
datetime=source_time.isoformat(timespec="seconds"),
|
||||
is_dst=bool(source_time.dst()),
|
||||
),
|
||||
target=TimeResult(
|
||||
timezone=target_timezone,
|
||||
datetime=target_time.isoformat(timespec="seconds"),
|
||||
is_dst=bool(target_time.dst()),
|
||||
),
|
||||
time_difference=time_diff_str,
|
||||
)
|
||||
return result.model_dump()
|
||||
|
||||
|
||||
# Tool definitions for LLM function calling
|
||||
def get_time_tools(local_tz_override: Optional[str] = None) -> list[Dict[str, Any]]:
|
||||
"""Get tool definitions with dynamic local timezone."""
|
||||
local_tz = local_tz_override or get_local_timezone()
|
||||
|
||||
return [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_time",
|
||||
"description": "Get current time in a specific timezone",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": f"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use '{local_tz}' as local timezone if no timezone provided by the user.",
|
||||
}
|
||||
},
|
||||
"required": ["timezone"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "convert_time",
|
||||
"description": "Convert time between timezones",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source_timezone": {
|
||||
"type": "string",
|
||||
"description": f"Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use '{local_tz}' as local timezone if no source timezone provided by the user.",
|
||||
},
|
||||
"time": {
|
||||
"type": "string",
|
||||
"description": "Time to convert in 24-hour format (HH:MM)",
|
||||
},
|
||||
"target_timezone": {
|
||||
"type": "string",
|
||||
"description": f"Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use '{local_tz}' as local timezone if no target timezone provided by the user.",
|
||||
},
|
||||
},
|
||||
"required": ["source_timezone", "time", "target_timezone"],
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue