atif-sql 0.2.0__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.
- atif_analytics/__init__.py +9 -0
- atif_analytics/application/__init__.py +3 -0
- atif_analytics/application/analyze.py +158 -0
- atif_analytics/application/prompts.py +750 -0
- atif_analytics/application/use_cases/__init__.py +3 -0
- atif_analytics/application/use_cases/_shared.py +264 -0
- atif_analytics/application/use_cases/classify.py +430 -0
- atif_analytics/application/use_cases/conflicts.py +448 -0
- atif_analytics/application/use_cases/friction.py +634 -0
- atif_analytics/application/use_cases/perceived.py +451 -0
- atif_analytics/domain/__init__.py +3 -0
- atif_analytics/domain/authorship.py +166 -0
- atif_analytics/domain/config.py +30 -0
- atif_analytics/domain/costs.py +50 -0
- atif_analytics/domain/friction.py +117 -0
- atif_analytics/domain/layout.py +94 -0
- atif_analytics/domain/models.py +385 -0
- atif_analytics/domain/ports.py +100 -0
- atif_analytics/domain/transcript.py +359 -0
- atif_analytics/infrastructure/__init__.py +3 -0
- atif_analytics/infrastructure/corpus_reader.py +546 -0
- atif_analytics/infrastructure/parquet_cache.py +186 -0
- atif_analytics/infrastructure/settings.py +114 -0
- atif_analytics/infrastructure/sqlite_state/__init__.py +3 -0
- atif_analytics/infrastructure/sqlite_state/checkpointer.py +326 -0
- atif_analytics/infrastructure/sqlite_state/retry_queue.py +296 -0
- atif_analytics/py.typed +0 -0
- atif_cli/__init__.py +3 -0
- atif_cli/__main__.py +8 -0
- atif_cli/app.py +2459 -0
- atif_cli/converter_adapter.py +151 -0
- atif_cli/corpus.py +409 -0
- atif_cli/cron.py +311 -0
- atif_cli/duck_errors.py +97 -0
- atif_cli/embed_lake.py +101 -0
- atif_cli/errors.py +89 -0
- atif_cli/lake.py +373 -0
- atif_cli/lake_sessions.py +270 -0
- atif_cli/output.py +297 -0
- atif_cli/py.typed +0 -0
- atif_converter/__init__.py +3 -0
- atif_converter/application/__init__.py +3 -0
- atif_converter/application/convert_and_audit.py +294 -0
- atif_converter/application/convert_codex.py +210 -0
- atif_converter/domain/__init__.py +9 -0
- atif_converter/domain/agents.py +40 -0
- atif_converter/domain/atif/__init__.py +55 -0
- atif_converter/domain/atif/agent.py +37 -0
- atif_converter/domain/atif/content.py +161 -0
- atif_converter/domain/atif/final_metrics.py +45 -0
- atif_converter/domain/atif/metrics.py +49 -0
- atif_converter/domain/atif/observation.py +21 -0
- atif_converter/domain/atif/observation_result.py +47 -0
- atif_converter/domain/atif/step.py +144 -0
- atif_converter/domain/atif/subagent_trajectory_ref.py +90 -0
- atif_converter/domain/atif/tool_call.py +36 -0
- atif_converter/domain/atif/trajectory.py +211 -0
- atif_converter/domain/atif/trajectory_validator.py +299 -0
- atif_converter/domain/blobs.py +510 -0
- atif_converter/domain/claude_code_conversion.py +1077 -0
- atif_converter/domain/codex_conversion.py +1036 -0
- atif_converter/domain/codex_edges.py +192 -0
- atif_converter/domain/codex_enrichment.py +419 -0
- atif_converter/domain/codex_fidelity.py +124 -0
- atif_converter/domain/codex_nested_calls.py +165 -0
- atif_converter/domain/edges.py +113 -0
- atif_converter/domain/enrichment.py +412 -0
- atif_converter/domain/errors.py +60 -0
- atif_converter/domain/fidelity.py +171 -0
- atif_converter/domain/model_prices.json +8538 -0
- atif_converter/domain/pricing.py +790 -0
- atif_converter/domain/result_signals.py +774 -0
- atif_converter/domain/schema_version.py +90 -0
- atif_converter/domain/session_events.py +408 -0
- atif_converter/infrastructure/__init__.py +3 -0
- atif_converter/infrastructure/census.py +89 -0
- atif_converter/infrastructure/claude_code_converter.py +127 -0
- atif_converter/infrastructure/codex_adapter.py +132 -0
- atif_converter/infrastructure/codex_census.py +100 -0
- atif_converter/infrastructure/codex_converter.py +82 -0
- atif_converter/infrastructure/harbor_adapter.py +187 -0
- atif_converter/infrastructure/raw_records.py +426 -0
- atif_converter/infrastructure/source_archive.py +154 -0
- atif_converter/py.typed +0 -0
- atif_corpus/__init__.py +13 -0
- atif_corpus/application/__init__.py +7 -0
- atif_corpus/application/materialize.py +1826 -0
- atif_corpus/domain/__init__.py +9 -0
- atif_corpus/domain/agents.py +33 -0
- atif_corpus/domain/generation.py +34 -0
- atif_corpus/domain/layout.py +168 -0
- atif_corpus/domain/ports.py +175 -0
- atif_corpus/domain/session_id.py +68 -0
- atif_corpus/domain/sessions.py +228 -0
- atif_corpus/domain/slug.py +72 -0
- atif_corpus/domain/source_layout.py +143 -0
- atif_corpus/domain/watermark.py +76 -0
- atif_corpus/infrastructure/__init__.py +7 -0
- atif_corpus/infrastructure/atomic.py +266 -0
- atif_corpus/infrastructure/compress_artifacts.py +161 -0
- atif_corpus/infrastructure/fake_converter.py +174 -0
- atif_corpus/infrastructure/scanner.py +316 -0
- atif_corpus/infrastructure/settings.py +130 -0
- atif_corpus/infrastructure/source_archive.py +202 -0
- atif_corpus/py.typed +0 -0
- atif_duck/__init__.py +43 -0
- atif_duck/domain/__init__.py +32 -0
- atif_duck/domain/artifacts.py +66 -0
- atif_duck/domain/authorship.py +165 -0
- atif_duck/domain/catalog.py +626 -0
- atif_duck/domain/columnar.py +149 -0
- atif_duck/domain/embedding_guard.py +88 -0
- atif_duck/domain/examples.py +265 -0
- atif_duck/domain/lake.py +427 -0
- atif_duck/domain/lake_sessions.py +97 -0
- atif_duck/domain/raw_readers.py +140 -0
- atif_duck/domain/session_id.py +69 -0
- atif_duck/domain/sql_literal.py +50 -0
- atif_duck/infrastructure/__init__.py +34 -0
- atif_duck/infrastructure/analytics.py +469 -0
- atif_duck/infrastructure/authorship.py +139 -0
- atif_duck/infrastructure/columnar.py +641 -0
- atif_duck/infrastructure/lake.py +1383 -0
- atif_duck/infrastructure/lake_sessions.py +194 -0
- atif_duck/infrastructure/lake_settings.py +63 -0
- atif_duck/infrastructure/lake_steps.py +225 -0
- atif_duck/infrastructure/projections.py +221 -0
- atif_duck/infrastructure/registry.py +2194 -0
- atif_duck/infrastructure/stored_artifacts.py +115 -0
- atif_duck/py.typed +0 -0
- atif_embed/__init__.py +12 -0
- atif_embed/application/__init__.py +3 -0
- atif_embed/application/embed.py +293 -0
- atif_embed/application/prune.py +80 -0
- atif_embed/domain/__init__.py +3 -0
- atif_embed/domain/discovery.py +70 -0
- atif_embed/domain/embedding_guard.py +83 -0
- atif_embed/domain/errors.py +94 -0
- atif_embed/domain/ports.py +223 -0
- atif_embed/domain/sql_literal.py +49 -0
- atif_embed/domain/text_stamp.py +71 -0
- atif_embed/infrastructure/__init__.py +3 -0
- atif_embed/infrastructure/cohere_bedrock.py +433 -0
- atif_embed/infrastructure/corpus_text_rows.py +273 -0
- atif_embed/infrastructure/gemma_local.py +355 -0
- atif_embed/infrastructure/lake_text_rows.py +203 -0
- atif_embed/infrastructure/lance_store.py +440 -0
- atif_embed/infrastructure/providers.py +30 -0
- atif_embed/infrastructure/settings.py +154 -0
- atif_embed/py.typed +0 -0
- atif_models/__init__.py +14 -0
- atif_models/domain/__init__.py +9 -0
- atif_models/domain/ports.py +143 -0
- atif_models/domain/registry.py +151 -0
- atif_models/domain/schema.py +109 -0
- atif_models/infrastructure/__init__.py +8 -0
- atif_models/infrastructure/openai_bedrock.py +314 -0
- atif_models/infrastructure/settings.py +85 -0
- atif_models/py.typed +0 -0
- atif_sql-0.2.0.dist-info/METADATA +272 -0
- atif_sql-0.2.0.dist-info/RECORD +164 -0
- atif_sql-0.2.0.dist-info/WHEEL +4 -0
- atif_sql-0.2.0.dist-info/entry_points.txt +2 -0
- atif_sql-0.2.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Vendored from harbor 0.24.0, src/harbor/utils/trajectory_validator.py
|
|
3
|
+
# (Apache-2.0, Copyright the Harbor authors). Byte for byte upstream apart
|
|
4
|
+
# from these header lines and the import path, which reads
|
|
5
|
+
# atif_converter.domain.atif where upstream reads harbor.models.trajectories.
|
|
6
|
+
"""Validator for Agent Trajectory Interchange Format (ATIF) trajectories.
|
|
7
|
+
|
|
8
|
+
This module provides validation functionality for trajectory files following
|
|
9
|
+
the ATIF specification (RFC 0001).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from pydantic import ValidationError
|
|
17
|
+
|
|
18
|
+
from atif_converter.domain.atif import Trajectory
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TrajectoryValidator:
|
|
22
|
+
"""Validator for ATIF trajectory format.
|
|
23
|
+
|
|
24
|
+
Validates that trajectory JSON follows the schema defined in RFC 0001,
|
|
25
|
+
using Pydantic models for validation.
|
|
26
|
+
|
|
27
|
+
Always collects all validation errors before returning.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
"""Initialize the validator."""
|
|
32
|
+
self.errors: list[str] = []
|
|
33
|
+
self._trajectory_dir: Path | None = None
|
|
34
|
+
|
|
35
|
+
def _add_error(self, error: str) -> None:
|
|
36
|
+
"""Add an error to the error list.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
error: Error message to add.
|
|
40
|
+
"""
|
|
41
|
+
self.errors.append(error)
|
|
42
|
+
|
|
43
|
+
def _is_url(self, path: str) -> bool:
|
|
44
|
+
"""Check if a path is a URL rather than a local file path.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
path: The path to check.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True if the path appears to be a URL, False otherwise.
|
|
51
|
+
"""
|
|
52
|
+
# Check for scheme:// pattern (e.g., https://, s3://, gs://)
|
|
53
|
+
return "://" in path
|
|
54
|
+
|
|
55
|
+
def _validate_image_paths(self, trajectory_data: dict[str, Any]) -> None:
|
|
56
|
+
"""Validate that all referenced local media paths exist.
|
|
57
|
+
|
|
58
|
+
Covers image and (since ATIF-v1.8) audio content parts. URLs are skipped
|
|
59
|
+
since they cannot be validated locally.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
trajectory_data: The parsed trajectory dictionary.
|
|
63
|
+
"""
|
|
64
|
+
if self._trajectory_dir is None:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
def check_content_for_images(content: Any, location: str) -> None:
|
|
68
|
+
"""Check content field for media references."""
|
|
69
|
+
if not isinstance(content, list):
|
|
70
|
+
return
|
|
71
|
+
for idx, part in enumerate(content):
|
|
72
|
+
if not isinstance(part, dict):
|
|
73
|
+
continue
|
|
74
|
+
part_type = part.get("type")
|
|
75
|
+
if part_type in ("image", "audio"):
|
|
76
|
+
source = part.get("source", {})
|
|
77
|
+
if isinstance(source, dict):
|
|
78
|
+
image_path = source.get("path")
|
|
79
|
+
if image_path:
|
|
80
|
+
# Skip URLs - they can't be validated locally
|
|
81
|
+
if self._is_url(image_path): # ty: ignore[invalid-argument-type]
|
|
82
|
+
continue
|
|
83
|
+
# Handle both absolute and relative paths
|
|
84
|
+
path_obj = Path(image_path) # ty: ignore[invalid-argument-type]
|
|
85
|
+
if path_obj.is_absolute():
|
|
86
|
+
full_path = path_obj
|
|
87
|
+
else:
|
|
88
|
+
full_path = self._trajectory_dir / image_path # ty: ignore[unsupported-operator]
|
|
89
|
+
if not full_path.exists():
|
|
90
|
+
self._add_error(
|
|
91
|
+
f"{location}[{idx}].source.path: "
|
|
92
|
+
f"referenced {part_type} file does not exist: "
|
|
93
|
+
f"{image_path}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Check all steps for image references
|
|
97
|
+
for step_idx, step in enumerate(trajectory_data.get("steps", [])):
|
|
98
|
+
step_loc = f"trajectory.steps[{step_idx}]"
|
|
99
|
+
|
|
100
|
+
# Check message field
|
|
101
|
+
message = step.get("message")
|
|
102
|
+
if isinstance(message, list):
|
|
103
|
+
check_content_for_images(message, f"{step_loc}.message")
|
|
104
|
+
|
|
105
|
+
# Check observation results
|
|
106
|
+
observation = step.get("observation")
|
|
107
|
+
if observation:
|
|
108
|
+
for res_idx, result in enumerate(observation.get("results", [])):
|
|
109
|
+
content = result.get("content")
|
|
110
|
+
if isinstance(content, list):
|
|
111
|
+
check_content_for_images(
|
|
112
|
+
content,
|
|
113
|
+
f"{step_loc}.observation.results[{res_idx}].content",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def validate(
|
|
117
|
+
self, trajectory: dict[str, Any] | str | Path, validate_images: bool = True
|
|
118
|
+
) -> bool:
|
|
119
|
+
"""Validate a complete trajectory.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
trajectory: Trajectory to validate. Can be a dict, JSON string,
|
|
123
|
+
or path to a JSON file.
|
|
124
|
+
validate_images: Whether to validate that referenced image paths exist.
|
|
125
|
+
Only applicable when trajectory is a file path.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if valid, False otherwise. All errors are collected in self.errors.
|
|
129
|
+
"""
|
|
130
|
+
self.errors = []
|
|
131
|
+
self._trajectory_dir = None
|
|
132
|
+
|
|
133
|
+
# Load trajectory if it's a string or path
|
|
134
|
+
if isinstance(trajectory, (str, Path)):
|
|
135
|
+
path = Path(trajectory)
|
|
136
|
+
if path.exists():
|
|
137
|
+
self._trajectory_dir = path.parent
|
|
138
|
+
with open(path, "r") as f:
|
|
139
|
+
try:
|
|
140
|
+
trajectory = json.load(f)
|
|
141
|
+
except json.JSONDecodeError as e:
|
|
142
|
+
self._add_error(f"Invalid JSON: {e}")
|
|
143
|
+
return False
|
|
144
|
+
else:
|
|
145
|
+
try:
|
|
146
|
+
trajectory = json.loads(str(trajectory))
|
|
147
|
+
except json.JSONDecodeError as e:
|
|
148
|
+
if isinstance(trajectory, Path):
|
|
149
|
+
self._add_error(f"File not found: {trajectory}")
|
|
150
|
+
else:
|
|
151
|
+
self._add_error(
|
|
152
|
+
f"Input string is not a valid file path and not valid JSON: {e}"
|
|
153
|
+
)
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
if not isinstance(trajectory, dict):
|
|
157
|
+
self._add_error("Trajectory must be a JSON object/dict")
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
# Use Pydantic for schema validation
|
|
161
|
+
try:
|
|
162
|
+
Trajectory(**trajectory)
|
|
163
|
+
except ValidationError as e:
|
|
164
|
+
# Convert Pydantic errors to our error format
|
|
165
|
+
for error in e.errors():
|
|
166
|
+
loc_str = ".".join(str(x) for x in error["loc"])
|
|
167
|
+
msg = error["msg"]
|
|
168
|
+
error_type = error["type"]
|
|
169
|
+
error_input = error.get("input")
|
|
170
|
+
|
|
171
|
+
# Format the error message in a user-friendly way
|
|
172
|
+
if error_type == "missing":
|
|
173
|
+
self._add_error(f"trajectory.{loc_str}: required field is missing")
|
|
174
|
+
elif error_type == "extra_forbidden":
|
|
175
|
+
self._add_error(
|
|
176
|
+
f"trajectory.{loc_str}: unexpected field (not part of ATIF schema)"
|
|
177
|
+
)
|
|
178
|
+
elif error_type.startswith("value_error"):
|
|
179
|
+
# Custom validation error from our validators
|
|
180
|
+
self._add_error(f"trajectory.{loc_str}: {msg}")
|
|
181
|
+
elif error_type.startswith("type_error") or error_type in [
|
|
182
|
+
"string_type",
|
|
183
|
+
"int_type",
|
|
184
|
+
"float_type",
|
|
185
|
+
"dict_type",
|
|
186
|
+
"list_type",
|
|
187
|
+
]:
|
|
188
|
+
# Type mismatch error
|
|
189
|
+
# Include the actual value in the error message for better debugging
|
|
190
|
+
if error_input is not None:
|
|
191
|
+
self._add_error(
|
|
192
|
+
f"trajectory.{loc_str}: expected {error_type.replace('_', ' ')}, got {type(error_input).__name__}"
|
|
193
|
+
)
|
|
194
|
+
else:
|
|
195
|
+
self._add_error(f"trajectory.{loc_str}: {msg}")
|
|
196
|
+
elif error_type == "literal_error":
|
|
197
|
+
# Literal/enum validation failed - include the actual invalid value
|
|
198
|
+
if error_input is not None:
|
|
199
|
+
self._add_error(
|
|
200
|
+
f"trajectory.{loc_str}: {msg}, got '{error_input}'"
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
self._add_error(f"trajectory.{loc_str}: {msg}")
|
|
204
|
+
else:
|
|
205
|
+
# Generic error
|
|
206
|
+
self._add_error(f"trajectory.{loc_str}: {msg}")
|
|
207
|
+
|
|
208
|
+
# Validate image paths if requested and we have a trajectory directory
|
|
209
|
+
if validate_images and self._trajectory_dir is not None:
|
|
210
|
+
self._validate_image_paths(trajectory)
|
|
211
|
+
|
|
212
|
+
return len(self.errors) == 0
|
|
213
|
+
|
|
214
|
+
def get_errors(self) -> list[str]:
|
|
215
|
+
"""Get all validation errors.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
List of error messages.
|
|
219
|
+
"""
|
|
220
|
+
return self.errors
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def validate_trajectory(trajectory: dict[str, Any] | str | Path) -> bool:
|
|
224
|
+
"""Validate a trajectory against the ATIF schema.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
trajectory: Trajectory to validate (dict, JSON string, or file path).
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
True if valid, False otherwise.
|
|
231
|
+
"""
|
|
232
|
+
validator = TrajectoryValidator()
|
|
233
|
+
return validator.validate(trajectory)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def main():
|
|
237
|
+
"""CLI entrypoint for trajectory validation."""
|
|
238
|
+
import argparse
|
|
239
|
+
import sys
|
|
240
|
+
|
|
241
|
+
parser = argparse.ArgumentParser(
|
|
242
|
+
description="Validate Agent Trajectory Interchange Format (ATIF) trajectory files"
|
|
243
|
+
)
|
|
244
|
+
parser.add_argument(
|
|
245
|
+
"trajectory_file",
|
|
246
|
+
type=str,
|
|
247
|
+
help="Path to the trajectory JSON file to validate",
|
|
248
|
+
)
|
|
249
|
+
parser.add_argument(
|
|
250
|
+
"--no-validate-images",
|
|
251
|
+
action="store_true",
|
|
252
|
+
help="Skip validation of referenced image file paths",
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
args = parser.parse_args()
|
|
256
|
+
|
|
257
|
+
trajectory_path = Path(args.trajectory_file)
|
|
258
|
+
|
|
259
|
+
if not trajectory_path.exists():
|
|
260
|
+
print(f"Error: File not found: {trajectory_path}", file=sys.stderr)
|
|
261
|
+
sys.exit(1)
|
|
262
|
+
|
|
263
|
+
validator = TrajectoryValidator()
|
|
264
|
+
|
|
265
|
+
# Use ASCII fallbacks for checkmark/cross on Windows with limited encodings
|
|
266
|
+
try:
|
|
267
|
+
# Test if we can encode these characters
|
|
268
|
+
"✓✗".encode(sys.stdout.encoding or "utf-8")
|
|
269
|
+
check_mark = "✓"
|
|
270
|
+
cross_mark = "✗"
|
|
271
|
+
except (UnicodeEncodeError, LookupError):
|
|
272
|
+
check_mark = "[OK]"
|
|
273
|
+
cross_mark = "[FAILED]"
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
is_valid = validator.validate(
|
|
277
|
+
trajectory_path, validate_images=not args.no_validate_images
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if is_valid:
|
|
281
|
+
print(f"{check_mark} Trajectory is valid: {trajectory_path}")
|
|
282
|
+
sys.exit(0)
|
|
283
|
+
else:
|
|
284
|
+
print(
|
|
285
|
+
f"{cross_mark} Trajectory validation failed: {trajectory_path}",
|
|
286
|
+
file=sys.stderr,
|
|
287
|
+
)
|
|
288
|
+
print(f"\nFound {len(validator.errors)} error(s):", file=sys.stderr)
|
|
289
|
+
for error in validator.errors:
|
|
290
|
+
print(f" - {error}", file=sys.stderr)
|
|
291
|
+
sys.exit(1)
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
print(f"{cross_mark} Validation error: {e}", file=sys.stderr)
|
|
295
|
+
sys.exit(1)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
main()
|