waldiez 0.4.7__py3-none-any.whl → 0.4.9__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.
Potentially problematic release.
This version of waldiez might be problematic. Click here for more details.
- waldiez/__init__.py +5 -5
- waldiez/_version.py +1 -1
- waldiez/cli.py +97 -102
- waldiez/exporter.py +61 -19
- waldiez/exporting/__init__.py +25 -6
- waldiez/exporting/agent/__init__.py +7 -3
- waldiez/exporting/agent/code_execution.py +114 -0
- waldiez/exporting/agent/exporter.py +354 -0
- waldiez/exporting/agent/extras/__init__.py +15 -0
- waldiez/exporting/agent/extras/captain_agent_extras.py +315 -0
- waldiez/exporting/agent/extras/group/target.py +178 -0
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +500 -0
- waldiez/exporting/agent/extras/group_member_extras.py +181 -0
- waldiez/exporting/agent/extras/handoffs/__init__.py +19 -0
- waldiez/exporting/agent/extras/handoffs/after_work.py +78 -0
- waldiez/exporting/agent/extras/handoffs/available.py +74 -0
- waldiez/exporting/agent/extras/handoffs/condition.py +158 -0
- waldiez/exporting/agent/extras/handoffs/handoff.py +171 -0
- waldiez/exporting/agent/extras/handoffs/target.py +189 -0
- waldiez/exporting/agent/extras/rag/__init__.py +10 -0
- waldiez/exporting/agent/{utils/rag_user/chroma_utils.py → extras/rag/chroma_extras.py} +37 -24
- waldiez/exporting/agent/{utils/rag_user/mongo_utils.py → extras/rag/mongo_extras.py} +10 -10
- waldiez/exporting/agent/{utils/rag_user/pgvector_utils.py → extras/rag/pgvector_extras.py} +13 -13
- waldiez/exporting/agent/{utils/rag_user/qdrant_utils.py → extras/rag/qdrant_extras.py} +13 -13
- waldiez/exporting/agent/{utils/rag_user/vector_db.py → extras/rag/vector_db_extras.py} +59 -46
- waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +245 -0
- waldiez/exporting/agent/extras/reasoning_agent_extras.py +88 -0
- waldiez/exporting/agent/factory.py +95 -0
- waldiez/exporting/agent/processor.py +150 -0
- waldiez/exporting/agent/system_message.py +36 -0
- waldiez/exporting/agent/termination.py +50 -0
- waldiez/exporting/chats/__init__.py +7 -3
- waldiez/exporting/chats/exporter.py +97 -0
- waldiez/exporting/chats/factory.py +65 -0
- waldiez/exporting/chats/processor.py +226 -0
- waldiez/exporting/chats/utils/__init__.py +6 -5
- waldiez/exporting/chats/utils/common.py +11 -45
- waldiez/exporting/chats/utils/group.py +55 -0
- waldiez/exporting/chats/utils/nested.py +37 -52
- waldiez/exporting/chats/utils/sequential.py +72 -61
- waldiez/exporting/chats/utils/{single_chat.py → single.py} +48 -50
- waldiez/exporting/core/__init__.py +196 -0
- waldiez/exporting/core/constants.py +17 -0
- waldiez/exporting/core/content.py +69 -0
- waldiez/exporting/core/context.py +244 -0
- waldiez/exporting/core/enums.py +89 -0
- waldiez/exporting/core/errors.py +19 -0
- waldiez/exporting/core/exporter.py +390 -0
- waldiez/exporting/core/exporters.py +67 -0
- waldiez/exporting/core/extras/__init__.py +39 -0
- waldiez/exporting/core/extras/agent_extras/__init__.py +27 -0
- waldiez/exporting/core/extras/agent_extras/captain_extras.py +57 -0
- waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +102 -0
- waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +53 -0
- waldiez/exporting/core/extras/agent_extras/reasoning_extras.py +68 -0
- waldiez/exporting/core/extras/agent_extras/standard_extras.py +263 -0
- waldiez/exporting/core/extras/base.py +241 -0
- waldiez/exporting/core/extras/chat_extras.py +118 -0
- waldiez/exporting/core/extras/flow_extras.py +70 -0
- waldiez/exporting/core/extras/model_extras.py +73 -0
- waldiez/exporting/core/extras/path_resolver.py +93 -0
- waldiez/exporting/core/extras/serializer.py +138 -0
- waldiez/exporting/core/extras/tool_extras.py +82 -0
- waldiez/exporting/core/protocols.py +259 -0
- waldiez/exporting/core/result.py +705 -0
- waldiez/exporting/core/types.py +329 -0
- waldiez/exporting/core/utils/__init__.py +11 -0
- waldiez/exporting/core/utils/comment.py +33 -0
- waldiez/exporting/core/utils/llm_config.py +117 -0
- waldiez/exporting/core/validation.py +96 -0
- waldiez/exporting/flow/__init__.py +6 -2
- waldiez/exporting/flow/execution_generator.py +193 -0
- waldiez/exporting/flow/exporter.py +107 -0
- waldiez/exporting/flow/factory.py +94 -0
- waldiez/exporting/flow/file_generator.py +214 -0
- waldiez/exporting/flow/merger.py +387 -0
- waldiez/exporting/flow/orchestrator.py +411 -0
- waldiez/exporting/flow/utils/__init__.py +9 -36
- waldiez/exporting/flow/utils/common.py +206 -0
- waldiez/exporting/flow/utils/importing.py +373 -0
- waldiez/exporting/flow/utils/linting.py +200 -0
- waldiez/exporting/flow/utils/{logging_utils.py → logging.py} +23 -9
- waldiez/exporting/models/__init__.py +3 -1
- waldiez/exporting/models/exporter.py +233 -0
- waldiez/exporting/models/factory.py +66 -0
- waldiez/exporting/models/processor.py +139 -0
- waldiez/exporting/tools/__init__.py +11 -0
- waldiez/exporting/tools/exporter.py +207 -0
- waldiez/exporting/tools/factory.py +57 -0
- waldiez/exporting/tools/processor.py +248 -0
- waldiez/exporting/tools/registration.py +133 -0
- waldiez/io/__init__.py +128 -0
- waldiez/io/_ws.py +199 -0
- waldiez/io/models/__init__.py +60 -0
- waldiez/io/models/base.py +66 -0
- waldiez/io/models/constants.py +78 -0
- waldiez/io/models/content/__init__.py +23 -0
- waldiez/io/models/content/audio.py +43 -0
- waldiez/io/models/content/base.py +45 -0
- waldiez/io/models/content/file.py +43 -0
- waldiez/io/models/content/image.py +96 -0
- waldiez/io/models/content/text.py +37 -0
- waldiez/io/models/content/video.py +43 -0
- waldiez/io/models/user_input.py +269 -0
- waldiez/io/models/user_response.py +215 -0
- waldiez/io/mqtt.py +681 -0
- waldiez/io/redis.py +782 -0
- waldiez/io/structured.py +439 -0
- waldiez/io/utils.py +184 -0
- waldiez/io/ws.py +298 -0
- waldiez/logger.py +481 -0
- waldiez/models/__init__.py +108 -51
- waldiez/models/agents/__init__.py +34 -70
- waldiez/models/agents/agent/__init__.py +10 -4
- waldiez/models/agents/agent/agent.py +466 -65
- waldiez/models/agents/agent/agent_data.py +119 -47
- waldiez/models/agents/agent/agent_type.py +13 -2
- waldiez/models/agents/agent/code_execution.py +12 -12
- waldiez/models/agents/agent/human_input_mode.py +8 -0
- waldiez/models/agents/agent/{linked_skill.py → linked_tool.py} +7 -7
- waldiez/models/agents/agent/nested_chat.py +35 -7
- waldiez/models/agents/agent/termination_message.py +30 -22
- waldiez/models/agents/{swarm_agent → agent}/update_system_message.py +22 -22
- waldiez/models/agents/agents.py +58 -63
- waldiez/models/agents/assistant/assistant.py +4 -4
- waldiez/models/agents/assistant/assistant_data.py +13 -1
- waldiez/models/agents/{captain_agent → captain}/captain_agent.py +5 -5
- waldiez/models/agents/{captain_agent → captain}/captain_agent_data.py +5 -5
- waldiez/models/agents/extra_requirements.py +11 -16
- waldiez/models/agents/group_manager/group_manager.py +103 -13
- waldiez/models/agents/group_manager/group_manager_data.py +36 -14
- waldiez/models/agents/group_manager/speakers.py +77 -24
- waldiez/models/agents/{rag_user → rag_user_proxy}/__init__.py +16 -16
- waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +64 -0
- waldiez/models/agents/{rag_user/rag_user_data.py → rag_user_proxy/rag_user_proxy_data.py} +6 -5
- waldiez/models/agents/{rag_user → rag_user_proxy}/retrieve_config.py +182 -114
- waldiez/models/agents/{rag_user → rag_user_proxy}/vector_db_config.py +13 -13
- waldiez/models/agents/reasoning/reasoning_agent.py +6 -6
- waldiez/models/agents/reasoning/reasoning_agent_data.py +110 -63
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +38 -10
- waldiez/models/agents/user_proxy/user_proxy.py +11 -7
- waldiez/models/agents/user_proxy/user_proxy_data.py +2 -2
- waldiez/models/chat/__init__.py +2 -1
- waldiez/models/chat/chat.py +166 -87
- waldiez/models/chat/chat_data.py +99 -136
- waldiez/models/chat/chat_message.py +33 -23
- waldiez/models/chat/chat_nested.py +31 -30
- waldiez/models/chat/chat_summary.py +10 -8
- waldiez/models/common/__init__.py +52 -2
- waldiez/models/common/ag2_version.py +1 -1
- waldiez/models/common/base.py +38 -7
- waldiez/models/common/dict_utils.py +42 -17
- waldiez/models/common/handoff.py +459 -0
- waldiez/models/common/id_generator.py +19 -0
- waldiez/models/common/method_utils.py +130 -68
- waldiez/{exporting/base/utils → models/common}/naming.py +38 -61
- waldiez/models/common/waldiez_version.py +37 -0
- waldiez/models/flow/__init__.py +9 -2
- waldiez/models/flow/connection.py +18 -0
- waldiez/models/flow/flow.py +311 -215
- waldiez/models/flow/flow_data.py +207 -40
- waldiez/models/flow/info.py +85 -0
- waldiez/models/flow/naming.py +131 -0
- waldiez/models/model/__init__.py +7 -1
- waldiez/models/model/extra_requirements.py +3 -12
- waldiez/models/model/model.py +76 -21
- waldiez/models/model/model_data.py +108 -20
- waldiez/models/tool/__init__.py +16 -0
- waldiez/models/tool/extra_requirements.py +36 -0
- waldiez/models/{skill/skill.py → tool/tool.py} +88 -88
- waldiez/models/tool/tool_data.py +51 -0
- waldiez/models/tool/tool_type.py +8 -0
- waldiez/models/waldiez.py +97 -80
- waldiez/runner.py +115 -61
- waldiez/running/__init__.py +13 -7
- waldiez/running/environment.py +49 -68
- waldiez/running/gen_seq_diagram.py +16 -14
- waldiez/running/post_run.py +119 -0
- waldiez/running/pre_run.py +149 -0
- waldiez/running/util.py +134 -0
- waldiez/utils/__init__.py +2 -4
- waldiez/utils/cli_extras/jupyter.py +5 -3
- waldiez/utils/cli_extras/runner.py +6 -4
- waldiez/utils/cli_extras/studio.py +6 -4
- waldiez/utils/conflict_checker.py +15 -9
- waldiez/utils/flaml_warnings.py +5 -5
- waldiez/utils/version.py +47 -0
- {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/METADATA +235 -91
- waldiez-0.4.9.dist-info/RECORD +203 -0
- waldiez/exporting/agent/agent_exporter.py +0 -297
- waldiez/exporting/agent/utils/__init__.py +0 -23
- waldiez/exporting/agent/utils/captain_agent.py +0 -263
- waldiez/exporting/agent/utils/code_execution.py +0 -65
- waldiez/exporting/agent/utils/group_manager.py +0 -220
- waldiez/exporting/agent/utils/rag_user/__init__.py +0 -7
- waldiez/exporting/agent/utils/rag_user/rag_user.py +0 -209
- waldiez/exporting/agent/utils/reasoning.py +0 -36
- waldiez/exporting/agent/utils/swarm_agent.py +0 -469
- waldiez/exporting/agent/utils/teachability.py +0 -41
- waldiez/exporting/agent/utils/termination_message.py +0 -44
- waldiez/exporting/base/__init__.py +0 -25
- waldiez/exporting/base/agent_position.py +0 -75
- waldiez/exporting/base/base_exporter.py +0 -118
- waldiez/exporting/base/export_position.py +0 -48
- waldiez/exporting/base/import_position.py +0 -23
- waldiez/exporting/base/mixin.py +0 -137
- waldiez/exporting/base/utils/__init__.py +0 -18
- waldiez/exporting/base/utils/comments.py +0 -96
- waldiez/exporting/base/utils/path_check.py +0 -68
- waldiez/exporting/base/utils/to_string.py +0 -84
- waldiez/exporting/chats/chats_exporter.py +0 -240
- waldiez/exporting/chats/utils/swarm.py +0 -210
- waldiez/exporting/flow/flow_exporter.py +0 -528
- waldiez/exporting/flow/utils/agent_utils.py +0 -204
- waldiez/exporting/flow/utils/chat_utils.py +0 -71
- waldiez/exporting/flow/utils/def_main.py +0 -77
- waldiez/exporting/flow/utils/flow_content.py +0 -202
- waldiez/exporting/flow/utils/flow_names.py +0 -116
- waldiez/exporting/flow/utils/importing_utils.py +0 -227
- waldiez/exporting/models/models_exporter.py +0 -199
- waldiez/exporting/models/utils.py +0 -174
- waldiez/exporting/skills/__init__.py +0 -9
- waldiez/exporting/skills/skills_exporter.py +0 -176
- waldiez/exporting/skills/utils.py +0 -369
- waldiez/models/agents/agent/teachability.py +0 -70
- waldiez/models/agents/rag_user/rag_user.py +0 -60
- waldiez/models/agents/swarm_agent/__init__.py +0 -50
- waldiez/models/agents/swarm_agent/after_work.py +0 -179
- waldiez/models/agents/swarm_agent/on_condition.py +0 -105
- waldiez/models/agents/swarm_agent/on_condition_available.py +0 -142
- waldiez/models/agents/swarm_agent/on_condition_target.py +0 -40
- waldiez/models/agents/swarm_agent/swarm_agent.py +0 -107
- waldiez/models/agents/swarm_agent/swarm_agent_data.py +0 -124
- waldiez/models/flow/utils.py +0 -232
- waldiez/models/skill/__init__.py +0 -16
- waldiez/models/skill/extra_requirements.py +0 -36
- waldiez/models/skill/skill_data.py +0 -53
- waldiez/models/skill/skill_type.py +0 -8
- waldiez/running/running.py +0 -369
- waldiez/utils/pysqlite3_checker.py +0 -308
- waldiez/utils/rdps_checker.py +0 -122
- waldiez-0.4.7.dist-info/RECORD +0 -149
- /waldiez/models/agents/{captain_agent → captain}/__init__.py +0 -0
- /waldiez/models/agents/{captain_agent → captain}/captain_agent_lib_entry.py +0 -0
- {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/WHEEL +0 -0
- {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/entry_points.txt +0 -0
- {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
|
|
4
|
+
"""Content merger for combining multiple export results."""
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
|
|
9
|
+
from ..core import (
|
|
10
|
+
ContentOrder,
|
|
11
|
+
EnvironmentVariable,
|
|
12
|
+
ExporterContext,
|
|
13
|
+
ExportResult,
|
|
14
|
+
ImportStatement,
|
|
15
|
+
PositionedContent,
|
|
16
|
+
ValidationError,
|
|
17
|
+
ValidationResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class MergeStatistics:
|
|
23
|
+
"""Statistics about the merge operation."""
|
|
24
|
+
|
|
25
|
+
total_results: int = 0
|
|
26
|
+
total_imports: int = 0
|
|
27
|
+
deduplicated_imports: int = 0
|
|
28
|
+
total_content_items: int = 0
|
|
29
|
+
total_env_vars: int = 0
|
|
30
|
+
deduplicated_env_vars: int = 0
|
|
31
|
+
conflicts_found: list[str] = field(default_factory=list[str])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ContentMerger:
|
|
35
|
+
"""Intelligently merges multiple ExportResult objects."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, context: Optional[ExporterContext] = None):
|
|
38
|
+
"""Initialize the content merger.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
context : Optional[ExporterContext], optional
|
|
43
|
+
The exporter context, by default None
|
|
44
|
+
"""
|
|
45
|
+
self.context = context or ExporterContext()
|
|
46
|
+
self.statistics = MergeStatistics()
|
|
47
|
+
|
|
48
|
+
def merge_results(self, results: list[ExportResult]) -> ExportResult:
|
|
49
|
+
"""Merge multiple export results into one.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
results : list[ExportResult]
|
|
54
|
+
The export results to merge
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
ExportResult
|
|
59
|
+
The merged export result
|
|
60
|
+
"""
|
|
61
|
+
if not results:
|
|
62
|
+
return ExportResult()
|
|
63
|
+
|
|
64
|
+
if len(results) == 1:
|
|
65
|
+
return results[0]
|
|
66
|
+
|
|
67
|
+
# Initialize statistics
|
|
68
|
+
self.statistics = MergeStatistics(total_results=len(results))
|
|
69
|
+
|
|
70
|
+
# Create merged result
|
|
71
|
+
merged = ExportResult()
|
|
72
|
+
|
|
73
|
+
# 1. Merge imports with intelligent deduplication
|
|
74
|
+
merged.imports = self._merge_imports(results)
|
|
75
|
+
|
|
76
|
+
# 2. Merge positioned content with proper ordering
|
|
77
|
+
merged.positioned_content = self._merge_positioned_content(results)
|
|
78
|
+
|
|
79
|
+
# 3. Merge environment variables with conflict detection
|
|
80
|
+
merged.environment_variables = self._merge_environment_variables(
|
|
81
|
+
results
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# 4. Merge validation results
|
|
85
|
+
merged.validation_result = self._merge_validation_results(results)
|
|
86
|
+
|
|
87
|
+
# 5. Handle main content (typically empty for merged results)
|
|
88
|
+
merged.main_content = self._merge_main_content(results)
|
|
89
|
+
|
|
90
|
+
return merged
|
|
91
|
+
|
|
92
|
+
def _merge_imports(
|
|
93
|
+
self, results: list[ExportResult]
|
|
94
|
+
) -> set[ImportStatement]:
|
|
95
|
+
"""Merge imports with intelligent deduplication and prioritization.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
results : list[ExportResult]
|
|
100
|
+
The export results containing imports
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
set[ImportStatement]
|
|
105
|
+
The merged and deduplicated imports
|
|
106
|
+
"""
|
|
107
|
+
all_imports: set[ImportStatement] = set()
|
|
108
|
+
|
|
109
|
+
# Collect all imports
|
|
110
|
+
for result in results:
|
|
111
|
+
all_imports.update(result.imports)
|
|
112
|
+
self.statistics.total_imports += len(result.imports)
|
|
113
|
+
|
|
114
|
+
# Deduplicate based on statement content, keeping best position
|
|
115
|
+
deduplicated = self._deduplicate_imports(all_imports)
|
|
116
|
+
|
|
117
|
+
self.statistics.deduplicated_imports = len(deduplicated)
|
|
118
|
+
|
|
119
|
+
return deduplicated
|
|
120
|
+
|
|
121
|
+
def _deduplicate_imports(
|
|
122
|
+
self, imports: set[ImportStatement]
|
|
123
|
+
) -> set[ImportStatement]:
|
|
124
|
+
"""Deduplicate imports intelligently.
|
|
125
|
+
|
|
126
|
+
Rules:
|
|
127
|
+
1. Keep import with highest priority (BUILTINS > THIRD_PARTY > LOCAL)
|
|
128
|
+
2. For same position, keep the one with more metadata
|
|
129
|
+
3. Detect conflicts and warn
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
imports : set[ImportStatement]
|
|
134
|
+
The imports to deduplicate
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
set[ImportStatement]
|
|
139
|
+
The deduplicated imports
|
|
140
|
+
"""
|
|
141
|
+
# Group by statement content
|
|
142
|
+
grouped: dict[str, ImportStatement] = {}
|
|
143
|
+
conflicts: list[str] = []
|
|
144
|
+
|
|
145
|
+
for imp in imports:
|
|
146
|
+
key = imp.statement.strip()
|
|
147
|
+
|
|
148
|
+
if key not in grouped:
|
|
149
|
+
grouped[key] = imp
|
|
150
|
+
else:
|
|
151
|
+
existing = grouped[key]
|
|
152
|
+
|
|
153
|
+
# Prioritize by position (lower enum value = higher priority)
|
|
154
|
+
if imp.position.value < existing.position.value:
|
|
155
|
+
# New import has higher priority position
|
|
156
|
+
grouped[key] = imp
|
|
157
|
+
elif imp.position.value == existing.position.value:
|
|
158
|
+
# Same position - check for conflicts
|
|
159
|
+
if imp.metadata != existing.metadata:
|
|
160
|
+
conflicts.append(
|
|
161
|
+
f"Import '{key}' has conflicting metadata: "
|
|
162
|
+
f"{existing.metadata} vs {imp.metadata}"
|
|
163
|
+
)
|
|
164
|
+
# Keep existing (first wins for same priority)
|
|
165
|
+
|
|
166
|
+
if conflicts:
|
|
167
|
+
self.statistics.conflicts_found.extend(conflicts)
|
|
168
|
+
# Log conflicts but continue
|
|
169
|
+
for conflict in conflicts:
|
|
170
|
+
self.context.get_logger().warning(
|
|
171
|
+
f"Import conflict: {conflict}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return set(grouped.values())
|
|
175
|
+
|
|
176
|
+
def _merge_positioned_content(
|
|
177
|
+
self, results: list[ExportResult]
|
|
178
|
+
) -> list[PositionedContent]:
|
|
179
|
+
"""Merge positioned content with proper ordering.
|
|
180
|
+
|
|
181
|
+
The challenge: Different exporters add content at different positions,
|
|
182
|
+
and we need to maintain the correct order while handling a
|
|
183
|
+
gent-specific positioning.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
results : list[ExportResult]
|
|
188
|
+
The export results containing positioned content
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
list[PositionedContent]
|
|
193
|
+
The merged and properly ordered content
|
|
194
|
+
"""
|
|
195
|
+
all_content: list[PositionedContent] = []
|
|
196
|
+
|
|
197
|
+
# Collect all positioned content
|
|
198
|
+
for result in results:
|
|
199
|
+
all_content.extend(result.positioned_content)
|
|
200
|
+
self.statistics.total_content_items += len(
|
|
201
|
+
result.positioned_content
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Sort by complex criteria for proper ordering
|
|
205
|
+
sorted_content = self._sort_positioned_content(all_content)
|
|
206
|
+
|
|
207
|
+
return sorted_content
|
|
208
|
+
|
|
209
|
+
def _sort_positioned_content(
|
|
210
|
+
self, content: list[PositionedContent]
|
|
211
|
+
) -> list[PositionedContent]:
|
|
212
|
+
"""Sort positioned content by multiple criteria.
|
|
213
|
+
|
|
214
|
+
Sorting Priority:
|
|
215
|
+
1. ExportPosition (TOP, IMPORTS, TOOLS, MODELS, AGENTS, CHATS, BOTTOM)
|
|
216
|
+
2. ContentOrder within position (EARLY_SETUP, SETUP, MAIN_CONTENT, etc.)
|
|
217
|
+
3. Agent ID (for agent-specific content)
|
|
218
|
+
4. AgentPosition (BEFORE_ALL, BEFORE, AS_ARGUMENT, AFTER, AFTER_ALL)
|
|
219
|
+
5. Original order (for stability)
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
content : list[PositionedContent]
|
|
224
|
+
The content to sort
|
|
225
|
+
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
list[PositionedContent]
|
|
229
|
+
The sorted content
|
|
230
|
+
"""
|
|
231
|
+
return sorted(
|
|
232
|
+
content,
|
|
233
|
+
key=lambda pc: (
|
|
234
|
+
pc.position.value, # 1. ExportPosition enum value
|
|
235
|
+
self._get_order_value(
|
|
236
|
+
pc.order
|
|
237
|
+
), # 2. ContentOrder numeric value
|
|
238
|
+
pc.agent_id
|
|
239
|
+
or "", # 3. Agent ID (empty string for non-agent content)
|
|
240
|
+
(
|
|
241
|
+
pc.agent_position.value if pc.agent_position else 0
|
|
242
|
+
), # 4. AgentPosition
|
|
243
|
+
id(pc), # 5. Original object ID for stability
|
|
244
|
+
),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# pylint: disable=no-self-use
|
|
248
|
+
def _get_order_value(self, order: Union[int, ContentOrder]) -> int:
|
|
249
|
+
"""Get numeric value from order for sorting.
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
order : Union[int, ContentOrder]
|
|
254
|
+
The order value
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
int
|
|
259
|
+
The numeric order value
|
|
260
|
+
"""
|
|
261
|
+
return order.value if isinstance(order, ContentOrder) else order
|
|
262
|
+
|
|
263
|
+
def _merge_environment_variables(
|
|
264
|
+
self, results: list[ExportResult]
|
|
265
|
+
) -> list[EnvironmentVariable]:
|
|
266
|
+
"""Merge environment variables with conflict detection.
|
|
267
|
+
|
|
268
|
+
Rules:
|
|
269
|
+
1. Deduplicate by variable name
|
|
270
|
+
2. Detect value conflicts and warn
|
|
271
|
+
3. Keep first occurrence for conflicts
|
|
272
|
+
4. Preserve metadata and descriptions
|
|
273
|
+
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
results : list[ExportResult]
|
|
277
|
+
The export results containing environment variables
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
list[EnvironmentVariable]
|
|
282
|
+
The merged environment variables
|
|
283
|
+
"""
|
|
284
|
+
env_vars: dict[str, EnvironmentVariable] = {}
|
|
285
|
+
conflicts: list[str] = []
|
|
286
|
+
for result in results:
|
|
287
|
+
for env_var in result.environment_variables:
|
|
288
|
+
self.statistics.total_env_vars += 1
|
|
289
|
+
key = env_var.name
|
|
290
|
+
if key in env_vars:
|
|
291
|
+
existing = env_vars[key]
|
|
292
|
+
|
|
293
|
+
# Check for value conflicts
|
|
294
|
+
if existing.value != env_var.value:
|
|
295
|
+
conflicts.append(
|
|
296
|
+
f"Environment variable '{key}' has conflicting "
|
|
297
|
+
f"values: '{existing.value}' vs '{env_var.value}'"
|
|
298
|
+
)
|
|
299
|
+
# Keep first occurrence
|
|
300
|
+
# (tools/models take precedence over agents)
|
|
301
|
+
|
|
302
|
+
else:
|
|
303
|
+
env_vars[key] = env_var
|
|
304
|
+
|
|
305
|
+
self.statistics.deduplicated_env_vars = len(env_vars)
|
|
306
|
+
|
|
307
|
+
if conflicts:
|
|
308
|
+
self.statistics.conflicts_found.extend(conflicts)
|
|
309
|
+
# Log conflicts but continue (non-fatal)
|
|
310
|
+
for conflict in conflicts:
|
|
311
|
+
self.context.get_logger().warning(
|
|
312
|
+
f"Environment variable conflict: {conflict}"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return list(env_vars.values())
|
|
316
|
+
|
|
317
|
+
def _merge_validation_results(
|
|
318
|
+
self, results: list[ExportResult]
|
|
319
|
+
) -> ValidationResult:
|
|
320
|
+
"""Merge validation results from all export results.
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
results : list[ExportResult]
|
|
325
|
+
The export results containing validation results
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
ValidationResult
|
|
330
|
+
The merged validation result
|
|
331
|
+
"""
|
|
332
|
+
all_errors: list[ValidationError] = []
|
|
333
|
+
all_warnings: list[ValidationError] = []
|
|
334
|
+
|
|
335
|
+
for result in results:
|
|
336
|
+
if result.validation_result:
|
|
337
|
+
all_errors.extend(result.validation_result.errors)
|
|
338
|
+
all_warnings.extend(result.validation_result.warnings)
|
|
339
|
+
|
|
340
|
+
# Add merger-specific warnings
|
|
341
|
+
if self.statistics.conflicts_found:
|
|
342
|
+
all_warnings.extend(
|
|
343
|
+
[
|
|
344
|
+
ValidationError(message=f"Merge conflict: {conflict}")
|
|
345
|
+
for conflict in self.statistics.conflicts_found
|
|
346
|
+
]
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
return ValidationResult(
|
|
350
|
+
is_valid=not all_errors,
|
|
351
|
+
errors=all_errors,
|
|
352
|
+
warnings=all_warnings,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
def _merge_main_content(self, results: list[ExportResult]) -> Optional[str]:
|
|
356
|
+
"""Merge main content from results.
|
|
357
|
+
|
|
358
|
+
For flow exports, main content is typically empty since all content
|
|
359
|
+
is positioned. But we handle it for completeness.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
results : list[ExportResult]
|
|
364
|
+
The export results
|
|
365
|
+
|
|
366
|
+
Returns
|
|
367
|
+
-------
|
|
368
|
+
Optional[str]
|
|
369
|
+
The merged main content, or None
|
|
370
|
+
"""
|
|
371
|
+
main_contents: list[str] = []
|
|
372
|
+
|
|
373
|
+
for result in results:
|
|
374
|
+
if result.main_content and result.main_content.strip():
|
|
375
|
+
main_contents.append(result.main_content.strip())
|
|
376
|
+
|
|
377
|
+
return "\n\n".join(main_contents) if main_contents else None
|
|
378
|
+
|
|
379
|
+
def get_merge_statistics(self) -> MergeStatistics:
|
|
380
|
+
"""Get statistics about the last merge operation.
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
MergeStatistics
|
|
385
|
+
Statistics about the merge operation
|
|
386
|
+
"""
|
|
387
|
+
return self.statistics
|