feat: add default initial context variables

This commit is contained in:
Abhishek Kumar 2026-04-02 14:32:43 +05:30
parent f368fe5134
commit 96c90376c3
8 changed files with 112 additions and 19 deletions

View file

@ -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")

View file

@ -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_<TZ>`` or ``current_weekday_<TZ>`` 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_<TIMEZONE>`` current time in the given IANA timezone
- ``current_weekday`` current weekday name (uses *default_tz* if set, else UTC)
- ``current_weekday_<TIMEZONE>`` 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_<TZ>`` 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_<TZ> 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)

View file

@ -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_<TIMEZONE>}}` | 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_<TIMEZONE>}}` | Current weekday name in the specified timezone | `Thursday` |
Replace `<TIMEZONE>` 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}}.
```
<Note>
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`.
</Note>
### 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.

View file

@ -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 = ({
<div className="pt-2 space-y-2">
<Label>Prompt</Label>
<Label className="text-xs text-muted-foreground">
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Prompt engineering&apos;s best practices apply.
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Supports <a href={CONTEXT_VARIABLES_DOC_URL} target="_blank" rel="noopener noreferrer" className="underline">template variables</a>
</Label>
<MentionTextarea
value={prompt}

View file

@ -11,7 +11,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";
@ -216,7 +216,7 @@ const EndCallEditForm = ({
<Label>Prompt</Label>
<Label className="text-xs text-muted-foreground">
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Prompt engineering&apos;s best practices apply.
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Supports <a href={CONTEXT_VARIABLES_DOC_URL} target="_blank" rel="noopener noreferrer" className="underline">template variables</a>
</Label>
<MentionTextarea
value={prompt}

View file

@ -9,7 +9,7 @@ import { FlowNodeData } from "@/components/flow/types";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
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";
@ -145,7 +145,7 @@ const GlobalNodeEditForm = ({
<Label>Prompt</Label>
<Label className="text-xs text-muted-foreground">
This is the global prompt. This will be added to the system prompt of all the agents.
This is the global prompt. This will be added to the system prompt of all the agents. Supports <a href={CONTEXT_VARIABLES_DOC_URL} target="_blank" rel="noopener noreferrer" className="underline">template variables</a>
</Label>
<MentionTextarea
value={prompt}

View file

@ -16,7 +16,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";
@ -334,7 +334,7 @@ const StartCallEditForm = ({
<Label>Prompt</Label>
<Label className="text-xs text-muted-foreground">
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Prompt engineering&apos;s best practices apply.
Enter the prompt for the agent. This will be used to generate the agent&apos;s response. Supports <a href={CONTEXT_VARIABLES_DOC_URL} target="_blank" rel="noopener noreferrer" className="underline">template variables</a>
</Label>
<MentionTextarea
value={prompt}

View file

@ -10,6 +10,8 @@ export const NODE_DOCUMENTATION_URLS: Record<string, string> = {
qaAnalysis: `${DOCS_BASE}/getting-started`,
};
export const CONTEXT_VARIABLES_DOC_URL = `${DOCS_BASE}/core-concepts/context-and-variables`;
export const TOOL_DOCUMENTATION_URLS: Record<string, string> = {
http_api: `${DOCS_BASE}/voice-agent/tools/http-api`,
end_call: `${DOCS_BASE}/voice-agent/tools/end-call`,