satif-ai 0.2.3__py3-none-any.whl → 0.2.5__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.
- satif_ai/code_builders/transformation.py +113 -27
- {satif_ai-0.2.3.dist-info → satif_ai-0.2.5.dist-info}/METADATA +1 -1
- {satif_ai-0.2.3.dist-info → satif_ai-0.2.5.dist-info}/RECORD +6 -6
- satif_ai-0.2.5.dist-info/entry_points.txt +3 -0
- satif_ai-0.2.3.dist-info/entry_points.txt +0 -3
- {satif_ai-0.2.3.dist-info → satif_ai-0.2.5.dist-info}/LICENSE +0 -0
- {satif_ai-0.2.3.dist-info → satif_ai-0.2.5.dist-info}/WHEEL +0 -0
@@ -1,10 +1,11 @@
|
|
1
|
+
import base64
|
1
2
|
import os
|
2
3
|
import re
|
3
4
|
from pathlib import Path
|
4
5
|
from typing import Dict, List, Optional, Union
|
5
6
|
|
6
7
|
from agents import Agent, Runner, function_tool
|
7
|
-
from agents.mcp.server import
|
8
|
+
from agents.mcp.server import MCPServer
|
8
9
|
from mcp import ClientSession
|
9
10
|
from satif_core import AsyncCodeBuilder, CodeBuilder, SDIFDatabase
|
10
11
|
from satif_sdk.comparators import get_comparator
|
@@ -14,6 +15,7 @@ from satif_sdk.transformers import CodeTransformer
|
|
14
15
|
# Global variables for transformation
|
15
16
|
INPUT_SDIF_PATH: Optional[Path] = None
|
16
17
|
OUTPUT_TARGET_FILES: Optional[Dict[Union[str, Path], str]] = None
|
18
|
+
SCHEMA_ONLY: Optional[bool] = None
|
17
19
|
|
18
20
|
|
19
21
|
@function_tool
|
@@ -31,6 +33,9 @@ async def execute_transformation(code: str) -> str:
|
|
31
33
|
generated_output_path = code_transformer.export(INPUT_SDIF_PATH)
|
32
34
|
|
33
35
|
comparisons = []
|
36
|
+
comparator_kwargs = {}
|
37
|
+
if SCHEMA_ONLY:
|
38
|
+
comparator_kwargs["check_structure_only"] = True
|
34
39
|
|
35
40
|
if os.path.isdir(generated_output_path):
|
36
41
|
# If it's a directory, compare each file with its corresponding target
|
@@ -45,7 +50,9 @@ async def execute_transformation(code: str) -> str:
|
|
45
50
|
generated_output_path, output_target_file_name
|
46
51
|
)
|
47
52
|
comparator = get_comparator(output_target_file_name.split(".")[-1])
|
48
|
-
comparison = comparator.compare(
|
53
|
+
comparison = comparator.compare(
|
54
|
+
generated_file_path, output_base_file, **comparator_kwargs
|
55
|
+
)
|
49
56
|
comparisons.append(
|
50
57
|
f"Comparison for {generated_file_path} [SOURCE] with {output_target_file_name} [TARGET]: {comparison}"
|
51
58
|
)
|
@@ -59,7 +66,9 @@ async def execute_transformation(code: str) -> str:
|
|
59
66
|
output_file = list(OUTPUT_TARGET_FILES.keys())[0]
|
60
67
|
output_target_file_name = list(OUTPUT_TARGET_FILES.values())[0]
|
61
68
|
comparator = get_comparator(output_file.split(".")[-1])
|
62
|
-
comparison = comparator.compare(
|
69
|
+
comparison = comparator.compare(
|
70
|
+
generated_output_path, output_file, **comparator_kwargs
|
71
|
+
)
|
63
72
|
comparisons.append(
|
64
73
|
f"Comparison for {generated_output_path} [SOURCE] with {output_target_file_name} [TARGET]: {comparison}"
|
65
74
|
)
|
@@ -88,9 +97,9 @@ class TransformationAsyncCodeBuilder(AsyncCodeBuilder):
|
|
88
97
|
|
89
98
|
def __init__(
|
90
99
|
self,
|
91
|
-
mcp_server:
|
100
|
+
mcp_server: MCPServer,
|
92
101
|
mcp_session: ClientSession,
|
93
|
-
llm_model: str = "
|
102
|
+
llm_model: str = "o4-mini",
|
94
103
|
):
|
95
104
|
self.mcp_server = mcp_server
|
96
105
|
self.mcp_session = mcp_session
|
@@ -98,39 +107,116 @@ class TransformationAsyncCodeBuilder(AsyncCodeBuilder):
|
|
98
107
|
|
99
108
|
async def build(
|
100
109
|
self,
|
101
|
-
sdif: Path,
|
110
|
+
sdif: Path, # This will now be relative to project root (MCP server CWD)
|
102
111
|
output_target_files: Dict[Union[str, Path], str] | List[Path],
|
103
|
-
output_sdif: Optional[Path] = None,
|
112
|
+
output_sdif: Optional[Path] = None, # This will now be relative or None
|
104
113
|
instructions: Optional[str] = None,
|
114
|
+
schema_only: bool = False,
|
105
115
|
) -> str:
|
106
|
-
global INPUT_SDIF_PATH, OUTPUT_TARGET_FILES
|
107
|
-
INPUT_SDIF_PATH
|
116
|
+
global INPUT_SDIF_PATH, OUTPUT_TARGET_FILES, SCHEMA_ONLY
|
117
|
+
# INPUT_SDIF_PATH is used by execute_transformation tool, needs to be accessible from where that tool runs.
|
118
|
+
# If execute_transformation runs in the same process as the builder, absolute path is fine.
|
119
|
+
# If it were a separate context, this might need adjustment.
|
120
|
+
# For now, assume execute_transformation can access absolute paths if needed for its *input SDIF*.
|
121
|
+
# However, the sdif for MCP URIs must be relative.
|
122
|
+
INPUT_SDIF_PATH = Path(sdif).resolve()
|
123
|
+
SCHEMA_ONLY = schema_only
|
124
|
+
# Paths for MCP URIs are now expected to be relative to MCP server CWD (project root)
|
125
|
+
# So, use them directly as strings.
|
126
|
+
input_sdif_mcp_uri_path = base64.b64encode(str(sdif).encode()).decode()
|
127
|
+
output_sdif_mcp_uri_path = (
|
128
|
+
base64.b64encode(str(output_sdif).encode()).decode()
|
129
|
+
if output_sdif
|
130
|
+
else None
|
131
|
+
)
|
108
132
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
133
|
+
input_schema = await self.mcp_session.read_resource(
|
134
|
+
f"schema://{input_sdif_mcp_uri_path}"
|
135
|
+
)
|
136
|
+
input_sample = await self.mcp_session.read_resource(
|
137
|
+
f"sample://{input_sdif_mcp_uri_path}"
|
138
|
+
)
|
113
139
|
|
114
|
-
|
115
|
-
|
140
|
+
output_schema_text = "N/A"
|
141
|
+
output_sample_text = "N/A"
|
142
|
+
if output_sdif_mcp_uri_path:
|
143
|
+
try:
|
144
|
+
output_schema_content = await self.mcp_session.read_resource(
|
145
|
+
f"schema://{output_sdif_mcp_uri_path}"
|
146
|
+
)
|
147
|
+
if output_schema_content.contents:
|
148
|
+
output_schema_text = output_schema_content.contents[0].text
|
149
|
+
except Exception as e:
|
150
|
+
print(
|
151
|
+
f"Warning: Could not read schema for output_sdif {output_sdif_mcp_uri_path}: {e}"
|
152
|
+
)
|
116
153
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
154
|
+
try:
|
155
|
+
output_sample_content = await self.mcp_session.read_resource(
|
156
|
+
f"sample://{output_sdif_mcp_uri_path}"
|
157
|
+
)
|
158
|
+
if output_sample_content.contents:
|
159
|
+
output_sample_text = output_sample_content.contents[0].text
|
160
|
+
except Exception as e:
|
161
|
+
print(
|
162
|
+
f"Warning: Could not read sample for output_sdif {output_sdif_mcp_uri_path}: {e}"
|
163
|
+
)
|
164
|
+
|
165
|
+
# OUTPUT_TARGET_FILES keys are absolute paths to original example files for local reading by representers/comparators.
|
166
|
+
# Values are agent-facing filenames.
|
167
|
+
if isinstance(output_target_files, list):
|
168
|
+
OUTPUT_TARGET_FILES = {
|
169
|
+
file_path.resolve(): file_path.name for file_path in output_target_files
|
170
|
+
}
|
171
|
+
elif isinstance(output_target_files, dict):
|
172
|
+
temp_map = {}
|
173
|
+
for k, v in output_target_files.items():
|
174
|
+
if isinstance(k, Path):
|
175
|
+
temp_map[k.resolve()] = v
|
176
|
+
else:
|
177
|
+
temp_map[k] = v
|
178
|
+
OUTPUT_TARGET_FILES = temp_map
|
179
|
+
else:
|
180
|
+
OUTPUT_TARGET_FILES = {}
|
181
|
+
|
182
|
+
output_representation = {}
|
183
|
+
if OUTPUT_TARGET_FILES:
|
184
|
+
for file_key_abs_path in list(OUTPUT_TARGET_FILES.keys()):
|
185
|
+
agent_facing_name = OUTPUT_TARGET_FILES[file_key_abs_path]
|
186
|
+
print(f"Representing {agent_facing_name} from {file_key_abs_path}!!")
|
187
|
+
try:
|
188
|
+
# Representer uses the absolute path (file_key_abs_path) to read the example file.
|
189
|
+
output_representation[agent_facing_name] = get_representer(
|
190
|
+
file_key_abs_path
|
191
|
+
).represent(file_key_abs_path)
|
192
|
+
except Exception as e:
|
193
|
+
print(
|
194
|
+
f"Warning: Could not get representation for {agent_facing_name} (path {file_key_abs_path}): {e}"
|
195
|
+
)
|
196
|
+
output_representation[agent_facing_name] = (
|
197
|
+
f"Error representing file: {e}"
|
198
|
+
)
|
123
199
|
|
124
200
|
prompt = await self.mcp_session.get_prompt(
|
125
201
|
"create_transformation",
|
126
202
|
arguments={
|
127
|
-
"input_file":
|
128
|
-
|
129
|
-
|
203
|
+
"input_file": Path(
|
204
|
+
input_sdif_mcp_uri_path
|
205
|
+
).name, # Display name for prompt (from relative path)
|
206
|
+
"input_schema": input_schema.contents[0].text
|
207
|
+
if input_schema.contents
|
208
|
+
else "Error reading input schema",
|
209
|
+
"input_sample": input_sample.contents[0].text
|
210
|
+
if input_sample.contents
|
211
|
+
else "Error reading input sample",
|
130
212
|
"output_files": str(list(OUTPUT_TARGET_FILES.values())),
|
131
|
-
"output_schema":
|
132
|
-
"output_sample":
|
133
|
-
|
213
|
+
"output_schema": output_schema_text,
|
214
|
+
"output_sample": output_sample_text
|
215
|
+
if not SCHEMA_ONLY
|
216
|
+
else "Sample not available. Transform according to the schema only. No data is present in the output example.",
|
217
|
+
"output_representation": str(
|
218
|
+
output_representation
|
219
|
+
), # Representation keyed by agent-facing name
|
134
220
|
},
|
135
221
|
)
|
136
222
|
agent = Agent(
|
@@ -3,15 +3,15 @@ satif_ai/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
3
3
|
satif_ai/adapters/tidy.py,sha256=2oYj7Gz3vOQtzcpoJI4JbftWlMKvOWL8rdwthjg-zUE,19884
|
4
4
|
satif_ai/code_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
satif_ai/code_builders/adaptation.py,sha256=E29YM0S6pMtAfB0uzSUexoeWKwXfF8iJVyYUCKWQz5k,188
|
6
|
-
satif_ai/code_builders/transformation.py,sha256=
|
6
|
+
satif_ai/code_builders/transformation.py,sha256=ZYpZpJb28PzN3suNG1lxeF2zB6mWn8gMoOLQov9YJUA,10043
|
7
7
|
satif_ai/plot_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
satif_ai/plot_builders/agent.py,sha256=Ncw7SL9qkpRN0hw76ezSo1K8vVQK6gcXFp8x8VFwqUI,8291
|
9
9
|
satif_ai/plot_builders/prompt.py,sha256=m0W1SsxnB9_FhIYRumkthImJbK-7KUm4dygN3kjAXGk,6877
|
10
10
|
satif_ai/plot_builders/tool.py,sha256=MeLnG_wFoITSVWZcNFsQLCi157O4L3wItQgolBa4fAw,5994
|
11
11
|
satif_ai/standardizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
satif_ai/standardizers/ai_csv.py,sha256=AAeTt7eqFAtayxF2b95Z_K_lnMdwBBnv2Cn-qTEpMp8,29499
|
13
|
-
satif_ai-0.2.
|
14
|
-
satif_ai-0.2.
|
15
|
-
satif_ai-0.2.
|
16
|
-
satif_ai-0.2.
|
17
|
-
satif_ai-0.2.
|
13
|
+
satif_ai-0.2.5.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
|
14
|
+
satif_ai-0.2.5.dist-info/METADATA,sha256=jIyyKOcveIz_98_JTyF6bsfVn5iOVvc3qLFVQZ0wUSo,670
|
15
|
+
satif_ai-0.2.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
16
|
+
satif_ai-0.2.5.dist-info/entry_points.txt,sha256=Mz2SwYALjktap1bF-Q3EWBgiZVNT6QJCVsCs_fCV33Y,43
|
17
|
+
satif_ai-0.2.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|