From 96c90376c32dd8be90fd375adb5422f9a7ea5c3d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 2 Apr 2026 14:32:43 +0530 Subject: [PATCH] feat: add default initial context variables --- api/services/workflow/pipecat_engine.py | 10 --- api/utils/template_renderer.py | 82 +++++++++++++++++++- docs/core-concepts/context-and-variables.mdx | 21 +++++ ui/src/components/flow/nodes/AgentNode.tsx | 4 +- ui/src/components/flow/nodes/EndCall.tsx | 4 +- ui/src/components/flow/nodes/GlobalNode.tsx | 4 +- ui/src/components/flow/nodes/StartCall.tsx | 4 +- ui/src/constants/documentation.ts | 2 + 8 files changed, 112 insertions(+), 19 deletions(-) diff --git a/api/services/workflow/pipecat_engine.py b/api/services/workflow/pipecat_engine.py index 7b3801e..c8a012e 100644 --- a/api/services/workflow/pipecat_engine.py +++ b/api/services/workflow/pipecat_engine.py @@ -47,7 +47,6 @@ from api.services.workflow.pipecat_engine_variable_extractor import ( from api.services.workflow.tools.knowledge_base import ( retrieve_from_knowledge_base, ) -from api.services.workflow.tools.timezone import get_current_time from api.utils.template_renderer import render_template @@ -149,15 +148,6 @@ class PipecatEngine: # Helper that encapsulates custom tool management self._custom_tool_manager = CustomToolManager(self) - # Add current time in EST (America/New_York) to gathered context - try: - est_time_result = get_current_time("America/New_York") - # The get_current_time utility returns a dict with 'datetime' field - # Store the ISO formatted datetime string under the key 'time' - self._gathered_context["time"] = est_time_result.get("datetime") - except Exception as e: - logger.error(f"Failed to fetch current EST time: {e}") - await self.set_node(self.workflow.start_node_id) logger.debug(f"{self.__class__.__name__} initialized") diff --git a/api/utils/template_renderer.py b/api/utils/template_renderer.py index 982eca9..858f9e3 100644 --- a/api/utils/template_renderer.py +++ b/api/utils/template_renderer.py @@ -2,10 +2,17 @@ import json import re -from typing import Any, Dict, Union +from datetime import datetime +from typing import Any, Dict, Optional, Union +from zoneinfo import ZoneInfo + +from loguru import logger from api.services.workflow.workflow import TEMPLATE_VAR_PATTERN +_CURRENT_TIME_PREFIX = "current_time" +_CURRENT_WEEKDAY_PREFIX = "current_weekday" + def get_nested_value(obj: Any, path: str) -> Any: """ @@ -85,6 +92,70 @@ def render_template( return _render_string(template, context) +def _extract_timezone_from_template(template_str: str) -> Optional[str]: + """Extract the timezone from a ``current_time_`` or ``current_weekday_`` variable. + + Returns the first IANA timezone found, or None. + """ + pattern = ( + r"\{\{\s*(?:" + + re.escape(_CURRENT_TIME_PREFIX) + + r"|" + + re.escape(_CURRENT_WEEKDAY_PREFIX) + + r")_([^|\s}]+)" + ) + match = re.search(pattern, template_str) + return match.group(1).strip() if match else None + + +def _resolve_builtin_variable( + variable_path: str, default_tz: Optional[str] = None +) -> Optional[str]: + """Resolve built-in template variables that are available in all contexts. + + Supported variables: + - ``current_time`` – current time in UTC + - ``current_time_`` – current time in the given IANA timezone + - ``current_weekday`` – current weekday name (uses *default_tz* if set, else UTC) + - ``current_weekday_`` – current weekday name in the given timezone + + Args: + variable_path: The template variable name to resolve. + default_tz: Fallback timezone for ``current_weekday`` when no explicit + timezone suffix is provided (typically inferred from a + ``current_time_`` variable in the same template). + + Returns: + The resolved string value, or None if *variable_path* is not a + recognised built-in. + """ + if variable_path == _CURRENT_TIME_PREFIX: + tz = ZoneInfo(default_tz) if default_tz else ZoneInfo("UTC") + return datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S %Z") + + if variable_path.startswith(_CURRENT_TIME_PREFIX + "_"): + timezone = variable_path[len(_CURRENT_TIME_PREFIX) + 1 :] + try: + return datetime.now(ZoneInfo(timezone)).strftime("%Y-%m-%d %H:%M:%S %Z") + except Exception: + logger.warning(f"Invalid timezone in template variable: {timezone}") + return None + + if variable_path == _CURRENT_WEEKDAY_PREFIX: + tz = ZoneInfo(default_tz) if default_tz else ZoneInfo("UTC") + return datetime.now(tz).strftime("%A") + + if variable_path.startswith(_CURRENT_WEEKDAY_PREFIX + "_"): + timezone = variable_path[len(_CURRENT_WEEKDAY_PREFIX) + 1 :] + try: + return datetime.now(ZoneInfo(timezone)).strftime("%A") + except Exception: + logger.warning(f"Invalid timezone in template variable: {timezone}") + return None + + return None + + def _render_string(template_str: str, context: Dict[str, Any]) -> str: """ Render a string template with variable substitution. @@ -99,11 +170,20 @@ def _render_string(template_str: str, context: Dict[str, Any]) -> str: if not template_str: return template_str + # Pre-scan for a current_time_ variable so that {{current_weekday}} + # can inherit the same timezone instead of defaulting to UTC. + default_tz = _extract_timezone_from_template(template_str) + def _replace(match: re.Match[str]) -> str: # type: ignore[type-arg] variable_path = match.group(1).strip() filter_name = match.group(2).strip() if match.group(2) else None filter_value = match.group(3).strip() if match.group(3) else None + # Check for built-in variables first (current_time, current_weekday) + builtin_value = _resolve_builtin_variable(variable_path, default_tz) + if builtin_value is not None: + return builtin_value + # Get value using nested path lookup value = get_nested_value(context, variable_path) diff --git a/docs/core-concepts/context-and-variables.mdx b/docs/core-concepts/context-and-variables.mdx index ff9851e..a3f3174 100644 --- a/docs/core-concepts/context-and-variables.mdx +++ b/docs/core-concepts/context-and-variables.mdx @@ -45,6 +45,27 @@ whether they'd like to continue. When the call starts, Dograh substitutes the values before sending the prompt to the LLM — so the agent speaks naturally as if it already knows the contact. +### Built-in template variables + +Dograh provides built-in variables for current time and weekday that you can use in any prompt without setting up `initial_context`. + +| Variable | Description | Example output | +|---|---|---| +| `{{current_time}}` | Current time in UTC (or inferred timezone) | `2026-04-02 14:30:45 UTC` | +| `{{current_time_}}` | Current time in the specified timezone | `2026-04-02 20:00:45 IST` | +| `{{current_weekday}}` | Current weekday name in UTC (or inferred timezone) | `Thursday` | +| `{{current_weekday_}}` | Current weekday name in the specified timezone | `Thursday` | + +Replace `` with an [IANA timezone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) such as `Asia/Kolkata`, `America/New_York`, or `Europe/London`. + +``` +Today is {{current_weekday}} and the current time is {{current_time_America/New_York}}. +``` + + +When you use a timezone suffix on **either** `current_time` or `current_weekday`, the other variable without a suffix will automatically use the same timezone instead of UTC. For example, if your prompt contains both `{{current_time_Asia/Kolkata}}` and `{{current_weekday}}`, the weekday will also be resolved in `Asia/Kolkata`. + + ### gathered_context Data the agent collects *during* the call. You configure what to extract in the agent node's extraction settings — each variable has a name, type, and a prompt that tells the LLM what to look for. diff --git a/ui/src/components/flow/nodes/AgentNode.tsx b/ui/src/components/flow/nodes/AgentNode.tsx index 87cbe24..adaaedd 100644 --- a/ui/src/components/flow/nodes/AgentNode.tsx +++ b/ui/src/components/flow/nodes/AgentNode.tsx @@ -15,7 +15,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; -import { NODE_DOCUMENTATION_URLS } from "@/constants/documentation"; +import { CONTEXT_VARIABLES_DOC_URL, NODE_DOCUMENTATION_URLS } from "@/constants/documentation"; import { NodeContent } from "./common/NodeContent"; import { NodeEditDialog } from "./common/NodeEditDialog"; @@ -313,7 +313,7 @@ const AgentNodeEditForm = ({
Prompt Prompt Prompt = { qaAnalysis: `${DOCS_BASE}/getting-started`, }; +export const CONTEXT_VARIABLES_DOC_URL = `${DOCS_BASE}/core-concepts/context-and-variables`; + export const TOOL_DOCUMENTATION_URLS: Record = { http_api: `${DOCS_BASE}/voice-agent/tools/http-api`, end_call: `${DOCS_BASE}/voice-agent/tools/end-call`,