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.
@@ -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 MCPServerStdio
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(generated_file_path, output_base_file)
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(generated_output_path, output_file)
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: MCPServerStdio,
100
+ mcp_server: MCPServer,
92
101
  mcp_session: ClientSession,
93
- llm_model: str = "o3-mini",
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 = Path(sdif)
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
- if isinstance(output_target_files, list):
110
- OUTPUT_TARGET_FILES = {file: file.name for file in output_target_files}
111
- else:
112
- OUTPUT_TARGET_FILES = output_target_files
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
- input_schema = await self.mcp_session.read_resource(f"schema://{sdif}")
115
- input_sample = await self.mcp_session.read_resource(f"sample://{sdif}")
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
- output_schema = await self.mcp_session.read_resource(f"schema://{output_sdif}")
118
- output_sample = await self.mcp_session.read_resource(f"sample://{output_sdif}")
119
- output_representation = {
120
- file: get_representer(file).represent(file)
121
- for file in list(OUTPUT_TARGET_FILES.keys())
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": INPUT_SDIF_PATH.name,
128
- "input_schema": input_schema.contents[0].text,
129
- "input_sample": input_sample.contents[0].text,
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": output_schema.contents[0].text,
132
- "output_sample": output_sample.contents[0].text,
133
- "output_representation": str(output_representation),
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(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: satif-ai
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: AI Agents for Satif
5
5
  License: MIT
6
6
  Author: Bryan Djafer
@@ -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=fdegXe2jNYxbPWOoDKhpKws8ltO5we-aJQQAhBBYVWQ,5970
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.3.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
14
- satif_ai-0.2.3.dist-info/METADATA,sha256=lPxjO5D5g8U-kePulS6-15OsTMkosvt49xn6KX1oiWI,670
15
- satif_ai-0.2.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
16
- satif_ai-0.2.3.dist-info/entry_points.txt,sha256=_8RP7sSos2cwqYblhwHOK3hhRkLbP6XkbAju3vHWft8,40
17
- satif_ai-0.2.3.dist-info/RECORD,,
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,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ satif-ai=satif.cli:main
3
+
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- satif=satif.cli:main
3
-