Dagdaf commited on
Commit
7f48a2b
·
1 Parent(s): bda8b36

Update LLM sequence comments and enhance tool imports for enum attributes

Browse files

Commented out unused LLM types in the DEFAULT_LLM_SEQUENCE for clarity. Additionally, imported new enum attribute tools to support enhanced functionality in the agent's toolset.

Files changed (3) hide show
  1. agent.py +6 -6
  2. attributes_tools/tools_enum_attribute.py +345 -0
  3. tools.py +1 -0
agent.py CHANGED
@@ -418,11 +418,11 @@ class GaiaAgent:
418
  # Default LLM sequence order - references LLM_CONFIG keys
419
  DEFAULT_LLM_SEQUENCE = [
420
  "openrouter",
421
- "gigachat",
422
- "mistral",
423
- "gemini",
424
- "groq",
425
- "huggingface"
426
  ]
427
  # Print truncation length for debug output
428
  MAX_PRINT_LEN = 1000
@@ -2581,7 +2581,7 @@ class GaiaAgent:
2581
  'web_search_deep_research_exa_ai',
2582
  'wiki_search', 'arxiv_search', 'web_search',
2583
  # Comindware Platform tools
2584
- 'edit_or_create_text_attribute', 'get_text_attribute', 'delete_attribute', 'archive_or_unarchive_attribute', 'list_attributes', 'list_templates', 'list_applications'
2585
  ]
2586
 
2587
  # Build a set of tool names for deduplication (handle both __name__ and .name attributes)
 
418
  # Default LLM sequence order - references LLM_CONFIG keys
419
  DEFAULT_LLM_SEQUENCE = [
420
  "openrouter",
421
+ # "gigachat",
422
+ # "mistral",
423
+ # "gemini",
424
+ # "groq",
425
+ # "huggingface"
426
  ]
427
  # Print truncation length for debug output
428
  MAX_PRINT_LEN = 1000
 
2581
  'web_search_deep_research_exa_ai',
2582
  'wiki_search', 'arxiv_search', 'web_search',
2583
  # Comindware Platform tools
2584
+ 'edit_or_create_text_attribute', 'get_text_attribute', 'edit_or_create_enum_attribute', 'get_enum_attribute', 'delete_attribute', 'archive_or_unarchive_attribute', 'list_attributes', 'list_templates', 'list_applications'
2585
  ]
2586
 
2587
  # Build a set of tool names for deduplication (handle both __name__ and .name attributes)
attributes_tools/tools_enum_attribute.py ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from multiprocessing import Value
2
+ from typing import Any, Dict, List, Optional, Literal
3
+ from langchain.tools import tool
4
+ from pydantic import BaseModel, Field, field_validator, model_validator
5
+ import requests_
6
+ from models import AttributeResult
7
+ import re
8
+
9
+ ATTRIBUTE_ENDPOINT = "webapi/Attribute"
10
+
11
+ def _remove_nones(obj: Any) -> Any:
12
+ """
13
+ Recursively remove None values from dicts/lists to keep payload minimal and consistent with Platform expectations.
14
+ """
15
+ if isinstance(obj, dict):
16
+ return {k: _remove_nones(v) for k, v in obj.items() if v is not None}
17
+ if isinstance(obj, list):
18
+ return [ _remove_nones(v) for v in obj if v is not None]
19
+ return obj
20
+
21
+ class VariantAliasModel(BaseModel):
22
+ variant_type: Literal["Variant"] = Field(
23
+ description="Variant type. RU: Тип значения",
24
+ alias="type"
25
+ )
26
+ attribute_system_name: str = Field(
27
+ description="Attribute system name. RU: Системное имя атрибута",
28
+ alias="owner"
29
+ )
30
+ system_name: str = Field(
31
+ description="Variant system name. Ru: Системное имя значения",
32
+ alias="alias"
33
+ )
34
+
35
+ @model_validator(mode='before')
36
+ def set_attribute_system_name_later(cls, values):
37
+ return values
38
+
39
+ class VariantNameModel(BaseModel):
40
+ english_name: Optional[str] = Field(
41
+ default=None,
42
+ description="Variant English name. RU: Английское название значения",
43
+ alias="en"
44
+ )
45
+ russian_name: str = Field(
46
+ description="Variant Russian name. RU: Русское название значения",
47
+ alias="ru"
48
+ )
49
+ deutsche_name: Optional[str] = Field(
50
+ default=None,
51
+ description="Variant Deutsche name. RU: Немецкое название значения",
52
+ alias="de"
53
+ )
54
+
55
+ class VariantModel(BaseModel):
56
+ sytem_name: VariantAliasModel = Field(
57
+ alias="alias"
58
+ )
59
+ name: VariantNameModel
60
+ color: Optional[str] = Field(
61
+ default=None,
62
+ description="Variant display color via hex code. RU: Цвет отображения значения"
63
+ )
64
+
65
+ @field_validator('color')
66
+ def validate_hex_color(cls, v):
67
+ if not re.match(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', v):
68
+ raise ValueError('Color must be a valid hex color code, e.g. #RRGGBB')
69
+
70
+ class EditOrCreateEnumAttributeSchema(BaseModel):
71
+ operation: Literal["create", "edit"] = Field(
72
+ description="Choose operation: Create or Edit the attribute. RU: Создать, Редактировать"
73
+ )
74
+ name: str = Field(
75
+ description="Human-readable name of the attribute. RU: Название"
76
+ )
77
+ system_name: str = Field(
78
+ description="System name of the attribute. RU: Системное имя"
79
+ )
80
+ application_system_name: str = Field(
81
+ description="System name of the application with the template where the attribute is created or edited. RU: Системное имя приложения"
82
+ )
83
+ template_system_name: str = Field(
84
+ description="System name of the template where the attribute is created or edited. RU: Системное имя шаблона"
85
+ )
86
+ display_format: Literal[
87
+ "Text",
88
+ "Indicator",
89
+ "Badge"
90
+ ] = Field(
91
+ description="Attribute display format. RU: Формат отображения. When `display_format=CustomMask` provide `custom_mask`."
92
+ )
93
+ description: Optional[str] = Field(
94
+ default=None,
95
+ description="Human-readable business-oriented description of the attribute (auto-generate if empty). RU: Описание",
96
+ )
97
+ write_changes_to_the_log: bool = Field(
98
+ default=False,
99
+ description="Set to `True` to log attribute value changes. RU: Записывать изменения в журнал",
100
+ )
101
+ calculate_value: bool = Field(
102
+ default=False,
103
+ description="Set to `True` to calculate the attribute value automatically. Relevant only when `expression_for_calculation` is provided. RU: Вычислять автоматически",
104
+ )
105
+ expression_for_calculation: Optional[str] = Field(
106
+ default=None,
107
+ description="Expression to calculate the attribute value automatically. User-provided. RU: Выражение для вычисления",
108
+ )
109
+ variants: List['VariantModel'] = Field(
110
+ description="Attribute value variants. Ru: Варианты значений атрибута"
111
+ )
112
+
113
+ @model_validator(mode='after')
114
+ def inject_attribute_system_name_into_aliases(self) -> 'EditOrCreateEnumAttributeSchema':
115
+ for variant in self.variants:
116
+ variant.sytem_name.attribute_system_name = self.system_name
117
+ return self
118
+
119
+ @field_validator("operation", mode="before")
120
+ @classmethod
121
+ def normalize_operation(cls, v: str) -> str:
122
+ if v is None:
123
+ return v
124
+ value = str(v).strip().lower()
125
+ mapping = {
126
+ "создать": "create",
127
+ "редактировать": "edit",
128
+ }
129
+ return mapping.get(value, value)
130
+
131
+ @field_validator("name", "system_name", "application_system_name", "template_system_name", mode="before")
132
+ @classmethod
133
+ def non_empty_str(cls, v: Any) -> Any:
134
+ if isinstance(v, str) and v.strip() == "":
135
+ raise ValueError("must be a non-empty string")
136
+ return v
137
+
138
+ @model_validator(mode="after")
139
+ def validate_masks_and_calc(self) -> "EditOrCreateTextAttributeSchema":
140
+ if self.display_format == "CustomMask":
141
+ if not self.custom_mask or not str(self.custom_mask).strip():
142
+ raise ValueError("custom_mask is required when display_format is 'CustomMask'")
143
+ else:
144
+ # Ensure custom_mask is not accidentally provided for non-CustomMask
145
+ if self.custom_mask is not None and str(self.custom_mask).strip() != "":
146
+ raise ValueError("custom_mask must be omitted unless display_format is 'CustomMask'")
147
+
148
+ if self.expression_for_calculation is None:
149
+ # Calculation must be off when expression is not provided
150
+ object.__setattr__(self, "calculate_value", False)
151
+ else:
152
+ # Turn on calculation if expression is provided
153
+ object.__setattr__(self, "calculate_value", True)
154
+
155
+ return self
156
+
157
+ @tool("edit_or_create_enum_attribute", return_direct=False, args_schema=EditOrCreateEnumAttributeSchema)
158
+ def edit_or_create_enum_attribute(
159
+ operation: str,
160
+ name: str,
161
+ system_name: str,
162
+ application_system_name: str,
163
+ template_system_name: str,
164
+ display_format: str,
165
+ variants: List[VariantModel],
166
+ description: Optional[str] = None,
167
+ write_changes_to_the_log: Optional[bool] = False,
168
+ calculate_value: Optional[bool] = False,
169
+ expression_for_calculation: Optional[str] = None,
170
+ ) -> Dict[str, Any]:
171
+ r"""
172
+ Edit or Create a enum attribute.
173
+
174
+ IMPORTANT: When providing `variants`, you MUST follow this exact structure (with aliases!):
175
+
176
+ Example `variants`:
177
+ [
178
+ {
179
+ "alias": {
180
+ "type": "Variant",
181
+ "owner": "<system_name of the attribute>", # ← will be auto-filled, but you can omit or set to placeholder
182
+ "alias": "variant_system_name_1"
183
+ },
184
+ "name": {
185
+ "ru": "Название на русском",
186
+ "en": "English name",
187
+ "de": "Deutscher Name"
188
+ },
189
+ "color": "#FF5733"
190
+ }
191
+ ]
192
+
193
+ - `owner` in `alias` will be automatically set to the attribute's `system_name` — you may omit it or set to any placeholder.
194
+ - `color` must be a valid hex color, e.g. "#RRGGBB" or "#RGB".
195
+ - At least one variant is required.
196
+ - `ru` field in `name` is REQUIRED. `en` and `de` are optional.
197
+
198
+ Returns:
199
+ dict: {
200
+ "success": bool - True if the attribute was created or edited successfully
201
+ "status_code": int - HTTP response status code
202
+ "raw_response": dict|str|None - Raw response for auditing or payload body (sanitized)
203
+ "error": str|None - Error message if operation failed
204
+ }
205
+ """
206
+
207
+ request_body: Dict[str, Any] = {
208
+ "globalAlias": {
209
+ "owner": template_system_name,
210
+ "type": "Undefined",
211
+ "alias": system_name
212
+ },
213
+ "type": "Enum",
214
+ "format": display_format,
215
+ "name": name,
216
+ "description": description,
217
+ "isTracked": write_changes_to_the_log,
218
+ "isCalculated": calculate_value if expression_for_calculation != None else False,
219
+ "expression": expression_for_calculation,
220
+ "variants": [
221
+ {
222
+ "alias": variant.system_name.model_dump(),
223
+ "name": variant.name.model_dump(),
224
+ "color": variant.color
225
+ }
226
+ for variant in variants
227
+ ]
228
+ }
229
+
230
+ # Remove None values
231
+ request_body = _remove_nones(request_body)
232
+
233
+ try:
234
+ if operation == "create":
235
+ result = requests_._post_request(request_body, f"{ATTRIBUTE_ENDPOINT}/{application_system_name}")
236
+ if operation == "edit" or operation == "create":
237
+ result = requests_._put_request(request_body, f"{ATTRIBUTE_ENDPOINT}/{application_system_name}")
238
+ print("edit is complited")
239
+ else:
240
+ result = {
241
+ "success": False,
242
+ "error": f"No such operation for attribute: {operation}. Available operations: create, edit",
243
+ "status_code": 400
244
+ }
245
+ except Exception as e:
246
+ result = {
247
+ "success": False,
248
+ "error": f"Tool execution failed: {str(e)}",
249
+ "status_code": 500
250
+ }
251
+
252
+ # Ensure result is always a dict with proper structure
253
+ if not isinstance(result, dict):
254
+ result = {
255
+ "success": False,
256
+ "error": f"Unexpected result type: {type(result)}",
257
+ "status_code": 500
258
+ }
259
+
260
+ # Add additional error information if the API call failed
261
+ if not result.get("success", False) and result.get("error"):
262
+ error_info = result.get("error", "")
263
+ result["error"] = f"API operation failed: {error_info}"
264
+
265
+ validated = AttributeResult(**result)
266
+ return validated.model_dump()
267
+
268
+ class GetEnumAttributeSchema(BaseModel):
269
+ application_system_name: str = Field(
270
+ description="System name of the application with the template where the attribute is located. RU: Системное имя приложения"
271
+ )
272
+ template_system_name: str = Field(
273
+ description="System name of the template where the attribute is located. RU: Системное имя шаблона"
274
+ )
275
+ system_name: str = Field(
276
+ description="Unique system name of the attribute to fetch. RU: Системное имя атрибута"
277
+ )
278
+
279
+ @field_validator("application_system_name", "template_system_name", "system_name", mode="before")
280
+ @classmethod
281
+ def non_empty(cls, v: Any) -> Any:
282
+ if isinstance(v, str) and v.strip() == "":
283
+ raise ValueError("must be a non-empty string")
284
+ return v
285
+
286
+
287
+ @tool("get_enum_attribute", return_direct=False, args_schema=GetEnumAttributeSchema)
288
+ def get_enum_attribute(
289
+ application_system_name: str,
290
+ template_system_name: str,
291
+ system_name: str
292
+ ) -> Dict[str, Any]:
293
+ """
294
+ Get a enum attribute in a given template and application.
295
+
296
+ Returns:
297
+ dict: {
298
+ "success": bool - True if operation completed successfully
299
+ "status_code": int - HTTP response status code
300
+ "raw_response": dict|str|None - Raw response payload for auditing or payload body (sanitized)
301
+ "error": str|None - Error message if operation failed
302
+ }
303
+ """
304
+
305
+ attribute_global_alias = f"Attribute@{template_system_name}.{system_name}"
306
+
307
+ result = requests_._get_request(f"{ATTRIBUTE_ENDPOINT}/{application_system_name}/{attribute_global_alias}")
308
+
309
+ # Check if the request was successful and has the expected structure
310
+ if not result.get('success', False):
311
+ return result
312
+
313
+ result_body = result.get('raw_response')
314
+ if result_body is None:
315
+ result.update({"error": "No response data received from server"})
316
+ return result
317
+
318
+ # Check if result_body has the expected 'response' key
319
+ if not isinstance(result_body, dict) or 'response' not in result_body:
320
+ result.update({"error": "Unexpected response structure from server"})
321
+ return result
322
+
323
+ keys_to_remove = ['isUnique', 'isTitle', 'isIndexed', 'isMultiValue', 'isMandatory', 'isOwnership', 'instanceGlobalAlias', 'imageColorType', 'imagePreserveAspectRatio']
324
+
325
+ for key in keys_to_remove:
326
+ if key in result_body['response']:
327
+ result_body['response'].pop(key, None)
328
+
329
+ result.update({"raw_response": result_body['response']})
330
+ validated = AttributeResult(**result)
331
+ return validated.model_dump()
332
+
333
+ if __name__ == "__main__":
334
+ results = edit_or_create_text_attribute.invoke({
335
+ "operation": "create",
336
+ "name": "US Phone Number",
337
+ "system_name": "USPhoneNumber",
338
+ "application_system_name": "AItestAndApi",
339
+ "template_system_name": "Test",
340
+ "display_format": "CustomMask",
341
+ "custom_mask": r"^+1-?\d{3}-?\d{3}-?\d{4}$",
342
+ "control_uniqueness": False,
343
+ "use_as_record_title": False
344
+ })
345
+ print(results)
tools.py CHANGED
@@ -42,6 +42,7 @@ except ImportError:
42
  from langchain_core.tools import tool
43
  # Expose Comindware Platform tool(s)
44
  from attributes_tools.tools_text_attribute import edit_or_create_text_attribute, get_text_attribute # noqa: F401
 
45
  from attributes_tools.tool_delete_attribute import delete_attribute
46
  from attributes_tools.tool_archive_or_unarchive_attribute import archive_or_unarchive_attribute
47
  from templates_tools.tool_list_attributes import list_attributes
 
42
  from langchain_core.tools import tool
43
  # Expose Comindware Platform tool(s)
44
  from attributes_tools.tools_text_attribute import edit_or_create_text_attribute, get_text_attribute # noqa: F401
45
+ from attributes_tools.tools_enum_attribute import edit_or_create_enum_attribute, get_enum_attribute
46
  from attributes_tools.tool_delete_attribute import delete_attribute
47
  from attributes_tools.tool_archive_or_unarchive_attribute import archive_or_unarchive_attribute
48
  from templates_tools.tool_list_attributes import list_attributes