unique_toolkit 1.42.9__py3-none-any.whl → 1.43.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. unique_toolkit/_common/experimental/write_up_agent/README.md +848 -0
  2. unique_toolkit/_common/experimental/write_up_agent/__init__.py +22 -0
  3. unique_toolkit/_common/experimental/write_up_agent/agent.py +170 -0
  4. unique_toolkit/_common/experimental/write_up_agent/config.py +42 -0
  5. unique_toolkit/_common/experimental/write_up_agent/examples/data.csv +13 -0
  6. unique_toolkit/_common/experimental/write_up_agent/examples/example_usage.py +78 -0
  7. unique_toolkit/_common/experimental/write_up_agent/schemas.py +36 -0
  8. unique_toolkit/_common/experimental/write_up_agent/services/__init__.py +13 -0
  9. unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/__init__.py +19 -0
  10. unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/exceptions.py +29 -0
  11. unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/service.py +150 -0
  12. unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/utils.py +130 -0
  13. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/__init__.py +27 -0
  14. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/config.py +56 -0
  15. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/exceptions.py +79 -0
  16. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/config.py +34 -0
  17. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/system_prompt.j2 +15 -0
  18. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/user_prompt.j2 +21 -0
  19. unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/service.py +369 -0
  20. unique_toolkit/_common/experimental/write_up_agent/services/template_handler/__init__.py +29 -0
  21. unique_toolkit/_common/experimental/write_up_agent/services/template_handler/default_template.j2 +37 -0
  22. unique_toolkit/_common/experimental/write_up_agent/services/template_handler/exceptions.py +39 -0
  23. unique_toolkit/_common/experimental/write_up_agent/services/template_handler/service.py +191 -0
  24. unique_toolkit/_common/experimental/write_up_agent/services/template_handler/utils.py +182 -0
  25. unique_toolkit/_common/experimental/write_up_agent/utils.py +24 -0
  26. {unique_toolkit-1.42.9.dist-info → unique_toolkit-1.43.1.dist-info}/METADATA +7 -1
  27. {unique_toolkit-1.42.9.dist-info → unique_toolkit-1.43.1.dist-info}/RECORD +29 -4
  28. {unique_toolkit-1.42.9.dist-info → unique_toolkit-1.43.1.dist-info}/LICENSE +0 -0
  29. {unique_toolkit-1.42.9.dist-info → unique_toolkit-1.43.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,182 @@
1
+ """Template utilities."""
2
+
3
+ from jinja2 import Environment
4
+ from jinja2.nodes import For, Getattr, Name
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class TemplateStructureInfo(BaseModel):
9
+ """Information about the structure expected by a Jinja template.
10
+
11
+ Attributes:
12
+ grouping_columns: List of column names detected from {{ group.column }} patterns
13
+ row_columns: List of column names detected from {{ row.column }} patterns
14
+ expects_groups: True if template iterates over 'groups' variable
15
+ expects_rows: True if template iterates over 'rows' variable
16
+ """
17
+
18
+ grouping_columns: list[str]
19
+ row_columns: list[str]
20
+ expects_groups: bool
21
+ expects_rows: bool
22
+
23
+
24
+ # TODO [UN-16142]: Simplify template logic
25
+ def parse_template(template_str: str) -> TemplateStructureInfo:
26
+ """
27
+ Parse a Jinja template to extract structure information.
28
+
29
+ The parser detects:
30
+ - {% for X in groups %} → expects_groups = True, X becomes the group variable
31
+ - {{ X.column_name }} → grouping_columns contains 'column_name' (excluding 'rows', 'instructions')
32
+ - {% for Y in rows %} or {% for Y in X.rows %} → expects_rows = True
33
+ - {{ row.column_name }} → row_columns contains 'column_name'
34
+
35
+ Args:
36
+ template_str: Jinja template string to parse
37
+
38
+ Returns:
39
+ TemplateStructureInfo with detected structure
40
+
41
+ Raises:
42
+ Exception: If template parsing fails
43
+
44
+ Example:
45
+ >>> template = '''
46
+ ... {% for g in groups %}
47
+ ... Region: {{ g.region }}
48
+ ... {% for row in g.rows %}
49
+ ... - {{ row.product }}: ${{ row.price }}
50
+ ... {% endfor %}
51
+ ... {% endfor %}
52
+ ... '''
53
+ >>> info = parse_template(template)
54
+ >>> info.grouping_columns
55
+ ['region']
56
+ >>> info.row_columns
57
+ ['product', 'price']
58
+ >>> info.expects_groups
59
+ True
60
+ """
61
+ env = Environment()
62
+
63
+ try:
64
+ ast = env.parse(template_str)
65
+ except Exception as e:
66
+ raise ValueError(f"Failed to parse Jinja template: {e}") from e
67
+
68
+ # First, find what variable name is used for groups iteration
69
+ # e.g., {% for g in groups %} -> group_var = 'g'
70
+ group_var = None
71
+ for node in ast.find_all(For):
72
+ if isinstance(node.iter, Name) and node.iter.name == "groups":
73
+ if isinstance(node.target, Name):
74
+ group_var = node.target.name
75
+ break
76
+
77
+ # Detect if template expects 'groups' and 'rows' loops
78
+ expects_groups = _check_for_loop_variable(ast, "groups")
79
+ expects_rows = _check_for_loop_variable(ast, "rows")
80
+
81
+ # Extract column references
82
+ # For grouping columns, use the group variable name (e.g., 'g', 'group', etc.)
83
+ if group_var:
84
+ grouping_columns_raw = _extract_attribute_references(ast, group_var)
85
+ # Filter out special attributes that are not DataFrame grouping columns:
86
+ # - 'rows': structural template variable for row data
87
+ # - 'llm_response': reserved for LLM-generated summaries
88
+ # - 'instructions': structural template variable for group-specific instructions
89
+ # - anything starting with '_': internal/computed variables
90
+ grouping_columns = sorted(
91
+ [
92
+ col
93
+ for col in grouping_columns_raw
94
+ if col not in ["rows", "llm_response", "instructions"]
95
+ and not col.startswith("_")
96
+ ]
97
+ )
98
+ else:
99
+ # Fallback to 'group' if no explicit group var found
100
+ grouping_columns = sorted(list(_extract_attribute_references(ast, "group")))
101
+ grouping_columns = sorted(
102
+ [
103
+ col
104
+ for col in grouping_columns
105
+ if col not in ["rows", "llm_response", "instructions"]
106
+ and not col.startswith("_")
107
+ ]
108
+ )
109
+
110
+ row_columns = sorted(list(_extract_attribute_references(ast, "row")))
111
+
112
+ return TemplateStructureInfo(
113
+ grouping_columns=grouping_columns,
114
+ row_columns=row_columns,
115
+ expects_groups=expects_groups,
116
+ expects_rows=expects_rows,
117
+ )
118
+
119
+
120
+ def _extract_attribute_references(node, target_var: str) -> set[str]:
121
+ """
122
+ Recursively extract attribute references for a specific variable.
123
+
124
+ For example, if target_var='group', this extracts:
125
+ - 'region' from {{ group.region }}
126
+ - 'region' from {{ group.group.region }} (nested access)
127
+
128
+ Args:
129
+ node: Jinja2 AST node to traverse
130
+ target_var: Variable name to look for (e.g., 'group', 'row')
131
+
132
+ Returns:
133
+ Set of attribute names referenced on the target variable
134
+ """
135
+ attributes = set()
136
+
137
+ if isinstance(node, Getattr):
138
+ # Check if this is an attribute access on our target variable
139
+ if isinstance(node.node, Name) and node.node.name == target_var:
140
+ attributes.add(node.attr)
141
+ # Handle nested attributes like group.group.section
142
+ # If node.node is also Getattr, check if it eventually resolves to target_var
143
+ elif isinstance(node.node, Getattr):
144
+ # Recursively extract from nested getattr
145
+ nested_attrs = _extract_attribute_references(node.node, target_var)
146
+ if nested_attrs:
147
+ # If the nested part references our target, add this attr too
148
+ attributes.add(node.attr)
149
+ # Recursively check the node being accessed
150
+ attributes.update(_extract_attribute_references(node.node, target_var))
151
+
152
+ # Process all child nodes
153
+ for child in node.iter_child_nodes():
154
+ attributes.update(_extract_attribute_references(child, target_var))
155
+
156
+ return attributes
157
+
158
+
159
+ def _check_for_loop_variable(node, loop_var: str) -> bool:
160
+ """
161
+ Check if a for-loop iterates over a specific variable.
162
+
163
+ For example, checks if template contains {% for X in groups %}.
164
+
165
+ Args:
166
+ node: Jinja2 AST node to traverse
167
+ loop_var: Variable name to look for (e.g., 'groups', 'rows')
168
+
169
+ Returns:
170
+ True if a for-loop over the variable is found
171
+ """
172
+ if isinstance(node, For):
173
+ # Check if this for-loop iterates over our target variable
174
+ if isinstance(node.iter, Name) and node.iter.name == loop_var:
175
+ return True
176
+
177
+ # Recursively check child nodes
178
+ for child in node.iter_child_nodes():
179
+ if _check_for_loop_variable(child, loop_var):
180
+ return True
181
+
182
+ return False
@@ -0,0 +1,24 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def template_loader(parent_dir: Path, template_name: str) -> str:
5
+ """
6
+ Load a Jinja2 template file from the filesystem.
7
+
8
+ Args:
9
+ parent_dir: Path object pointing to the directory containing the template
10
+ template_name: Name of the template file to load (e.g., 'template.j2')
11
+
12
+ Returns:
13
+ Template content as a string
14
+
15
+ Raises:
16
+ FileNotFoundError: If the template file does not exist
17
+ IOError: If there's an error reading the template file
18
+
19
+ Example:
20
+ >>> from pathlib import Path
21
+ >>> template = template_loader(Path(__file__).parent, "my_template.j2")
22
+ """
23
+ template_path = parent_dir / template_name
24
+ return template_path.read_text()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.42.9
3
+ Version: 1.43.1
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -124,6 +124,12 @@ All notable changes to this project will be documented in this file.
124
124
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
125
125
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
126
126
 
127
+ ## [1.43.1] - 2026-01-12
128
+ - Remove accidental example report.md from repo
129
+
130
+ ## [1.43.0] - 2026-01-11
131
+ - Add `WriteUpAgent` as an experimental service
132
+
127
133
  ## [1.42.9] - 2026-01-11
128
134
  - Include feature flag to have message logs compatible with new ChatUI
129
135
 
@@ -20,6 +20,31 @@ unique_toolkit/_common/exception.py,sha256=ho0uBcPeZXU2w15IrSBhO5w7KUgxp1HcKAQrf
20
20
  unique_toolkit/_common/execution.py,sha256=ocPGGfUwa851207HNTLYiBJ1pNzJp4VhMZ49OPP33gU,8022
21
21
  unique_toolkit/_common/experimental/endpoint_builder.py,sha256=pEDwgeDzt67qbyaM98u8X7UAy29mQIw9Qufjz2bxgEA,11410
22
22
  unique_toolkit/_common/experimental/endpoint_requestor.py,sha256=YnDr8wASAEjZjLAeBmOWuFn4wUIZslTHBN_aApWeJBA,16079
23
+ unique_toolkit/_common/experimental/write_up_agent/README.md,sha256=ygmSpks3to5yye_XddLTpBXTiW61bW8w0hg2T33UP7U,28701
24
+ unique_toolkit/_common/experimental/write_up_agent/__init__.py,sha256=ka0eMrZiIAZVaoAbM-SPIT7heMd7rsuqVHDNIaNOQBQ,534
25
+ unique_toolkit/_common/experimental/write_up_agent/agent.py,sha256=bn-ftCx2GBRK2-yw9YspAob8zzHFX-X8Bk9Nx95Uczw,5835
26
+ unique_toolkit/_common/experimental/write_up_agent/config.py,sha256=5nGku-_feFWhM6RBI-Y4QRv9CG4Bgi7RKGOk65LsybY,1640
27
+ unique_toolkit/_common/experimental/write_up_agent/examples/data.csv,sha256=JHaYFBvmsSYOP7Ay1IKdMjBApsU-AJ902IDWp_85Lho,1569
28
+ unique_toolkit/_common/experimental/write_up_agent/examples/example_usage.py,sha256=--4rnjQ9_MY9FzHHl6LBAaqF6HM1TeqoZbouxssZxXY,2484
29
+ unique_toolkit/_common/experimental/write_up_agent/schemas.py,sha256=DUZ_RrjtqrtTUkqcecs-lW8eCOEAwB1Qxw_hrOXm3HE,861
30
+ unique_toolkit/_common/experimental/write_up_agent/services/__init__.py,sha256=w2SR53rQl1yM9dYKxP9dJWF6qiovN2oyyGis0-aCslc,341
31
+ unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/__init__.py,sha256=hd2IktiTyCiwE_JJTC43zmu-67LL2vDhaLoi1tgZH_U,539
32
+ unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/exceptions.py,sha256=6F9VmWD5j3GI7JbHzakoYUM83y8rFWTTMVjTD6YQni8,855
33
+ unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/service.py,sha256=gaXXjIpTBNMPD_8KD94i6eSl4q1Em7iqIcmLFLhwHAM,5981
34
+ unique_toolkit/_common/experimental/write_up_agent/services/dataframe_handler/utils.py,sha256=E-Yupm2d-Xo6wXha669L4B7g4cKa3qL03iJe7nIF5Ts,3382
35
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/__init__.py,sha256=u5Z2-XhEXbmMoH2X2PmonkMe5rr1VGc-XYp3gdCXOig,750
36
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/config.py,sha256=944Wn47t3MrC2f85zRA7vzrtuVKV22Um9olHLsv81B4,1927
37
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/exceptions.py,sha256=6hbP4ZbbvCGQVOVzUav2sfIkU-sVbst1K2grNCvo2Rk,1996
38
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/config.py,sha256=ep8NJNyBEbKIN7FudkeAfFaHzl1JygJxfNR2Ab0MHU0,1153
39
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/system_prompt.j2,sha256=U66WhKnA-_AHM-APCzHVRn2bu5rZMqRwyZ2kvAmF2xA,674
40
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/prompts/user_prompt.j2,sha256=9OMngS80URT4g96c0pYWjIcZn2Jn-C2aSFUdE7uIfCs,709
41
+ unique_toolkit/_common/experimental/write_up_agent/services/generation_handler/service.py,sha256=aIUc7HUbqBkBZGxXhwdhwQPYEcXoLy1NLCfAKOMav9E,12825
42
+ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/__init__.py,sha256=dyUAoV0uL-eEtSQQQ7XV8YMnNvwcyowiquZrgbhzNII,800
43
+ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/default_template.j2,sha256=mL2l4rvrQFEHvjZ8uf2uQFujoKsnT1CoYHbEC45zHDY,1036
44
+ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/exceptions.py,sha256=FGkJsbEeLwGDtFROH0TMxMFeXo4zfF40pXbLk6peYAo,1256
45
+ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/service.py,sha256=lg9rIbQKTzPhQTSWMt66z1X3hHnu8v7uIylV7Jn9vyY,6494
46
+ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/utils.py,sha256=FeJhTAU7AUlSFCfFwmEzzI22PtAdFJluzsGeiEPK49Q,6393
47
+ unique_toolkit/_common/experimental/write_up_agent/utils.py,sha256=I_OCDXCxY6uZk_fNQg_bIXHwjnN7ND_ngcisThnGGIc,739
23
48
  unique_toolkit/_common/feature_flags/schema.py,sha256=X32VqH4VMK7bhEfSd8Wbddl8FVs7Gh7ucuIEbmqc4Kw,268
24
49
  unique_toolkit/_common/pydantic/rjsf_tags.py,sha256=T3AZIF8wny3fFov66s258nEl1GqfKevFouTtG6k9PqU,31219
25
50
  unique_toolkit/_common/pydantic_helpers.py,sha256=Yg1CHD603wVrqvinHiyh3stjIK3MjuexUe9aQQUfmXs,5406
@@ -217,7 +242,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
217
242
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
218
243
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
219
244
  unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
220
- unique_toolkit-1.42.9.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
221
- unique_toolkit-1.42.9.dist-info/METADATA,sha256=UlLw100oSlKFi6ycPSH6swuqJZwbzMQebOdbfbookk0,47453
222
- unique_toolkit-1.42.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
223
- unique_toolkit-1.42.9.dist-info/RECORD,,
245
+ unique_toolkit-1.43.1.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
246
+ unique_toolkit-1.43.1.dist-info/METADATA,sha256=esEOCD00HzfGppkkGkf53KU-JWbQBcKJbOq1sw8uDRA,47602
247
+ unique_toolkit-1.43.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
248
+ unique_toolkit-1.43.1.dist-info/RECORD,,