Skip to content

Commit 923ad54

Browse files
add text tokens utils
1 parent 949fb76 commit 923ad54

2 files changed

Lines changed: 412 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""Text token utilities for building prompts from tokenized content."""
2+
3+
import json
4+
from typing import Any
5+
6+
from uipath.agent.models.agent import TextToken, TextTokenType
7+
8+
9+
def build_string_from_tokens(
10+
tokens: list[TextToken],
11+
input_arguments: dict[str, Any],
12+
tool_names: list[str] | None = None,
13+
escalation_names: list[str] | None = None,
14+
context_names: list[str] | None = None,
15+
) -> str:
16+
"""Build a string from text tokens with variable replacement.
17+
18+
Args:
19+
tokens: List of text tokens to join
20+
input_arguments: Dictionary of input arguments for variable replacement
21+
tool_names: Optional list of tool names for tool.* variable resolution
22+
escalation_names: Optional list of escalation names for escalation.* variable resolution
23+
context_names: Optional list of context names for context.* variable resolution
24+
"""
25+
parts: list[str] = []
26+
27+
for token in tokens:
28+
if token.type == TextTokenType.SIMPLE_TEXT:
29+
parts.append(token.raw_string)
30+
elif token.type == TextTokenType.EXPRESSION:
31+
parts.append(token.raw_string)
32+
elif token.type == TextTokenType.VARIABLE:
33+
resolved_value = _process_variable_token(
34+
token.raw_string,
35+
input_arguments,
36+
tool_names,
37+
escalation_names,
38+
context_names,
39+
)
40+
parts.append(resolved_value)
41+
else:
42+
parts.append(token.raw_string)
43+
44+
return "".join(parts)
45+
46+
47+
def _process_variable_token(
48+
raw_string: str,
49+
input_arguments: dict[str, Any],
50+
tool_names: list[str] | None = None,
51+
escalation_names: list[str] | None = None,
52+
context_names: list[str] | None = None,
53+
) -> str:
54+
"""Process a variable token and return its resolved value.
55+
56+
Returns:
57+
The resolved variable value or original string if unresolved
58+
"""
59+
if not raw_string or not raw_string.strip():
60+
return raw_string
61+
62+
if raw_string.lower() == "input":
63+
return json.dumps(input_arguments, ensure_ascii=False)
64+
65+
dot_index = raw_string.find(".")
66+
if dot_index < 0:
67+
return raw_string
68+
69+
prefix = raw_string[:dot_index].lower()
70+
path = raw_string[dot_index + 1 :]
71+
72+
if prefix == "input":
73+
value = safe_get_nested(input_arguments, path)
74+
return serialize_argument(value) if value is not None else raw_string
75+
elif prefix == "output":
76+
return path
77+
elif prefix == "tools":
78+
found_name = _find_resource_name(path, tool_names)
79+
return found_name if found_name else raw_string
80+
elif prefix == "escalations":
81+
found_name = _find_resource_name(path, escalation_names)
82+
return found_name if found_name else raw_string
83+
elif prefix == "contexts":
84+
found_name = _find_resource_name(path, context_names)
85+
return found_name if found_name else raw_string
86+
87+
return raw_string
88+
89+
90+
def _find_resource_name(name: str, resource_names: list[str] | None) -> str | None:
91+
"""Find a resource name in the list.
92+
93+
Args:
94+
name: The name to search for
95+
resource_names: List of resource names to search in
96+
97+
Returns:
98+
The matching resource name, or None if not found
99+
"""
100+
if not resource_names:
101+
return None
102+
103+
name_lower = name.lower()
104+
return next(
105+
(
106+
resource_name
107+
for resource_name in resource_names
108+
if resource_name.lower() == name_lower
109+
),
110+
None,
111+
)
112+
113+
114+
def safe_get_nested(data: dict[str, Any], path: str) -> Any:
115+
"""Get nested dictionary value using dot notation (e.g., "user.email")."""
116+
keys = path.split(".")
117+
current = data
118+
119+
for key in keys:
120+
if isinstance(current, dict) and key in current:
121+
current = current[key]
122+
else:
123+
return None
124+
125+
return current
126+
127+
128+
def serialize_argument(
129+
value: str | int | float | bool | list[Any] | dict[str, Any] | None,
130+
) -> str:
131+
"""Serialize value for interpolation: primitives as-is, collections as JSON."""
132+
if value is None:
133+
return ""
134+
if isinstance(value, (list, dict, bool)):
135+
return json.dumps(value, ensure_ascii=False)
136+
return str(value)

0 commit comments

Comments
 (0)