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.
Files changed (164) hide show
  1. atif_analytics/__init__.py +9 -0
  2. atif_analytics/application/__init__.py +3 -0
  3. atif_analytics/application/analyze.py +158 -0
  4. atif_analytics/application/prompts.py +750 -0
  5. atif_analytics/application/use_cases/__init__.py +3 -0
  6. atif_analytics/application/use_cases/_shared.py +264 -0
  7. atif_analytics/application/use_cases/classify.py +430 -0
  8. atif_analytics/application/use_cases/conflicts.py +448 -0
  9. atif_analytics/application/use_cases/friction.py +634 -0
  10. atif_analytics/application/use_cases/perceived.py +451 -0
  11. atif_analytics/domain/__init__.py +3 -0
  12. atif_analytics/domain/authorship.py +166 -0
  13. atif_analytics/domain/config.py +30 -0
  14. atif_analytics/domain/costs.py +50 -0
  15. atif_analytics/domain/friction.py +117 -0
  16. atif_analytics/domain/layout.py +94 -0
  17. atif_analytics/domain/models.py +385 -0
  18. atif_analytics/domain/ports.py +100 -0
  19. atif_analytics/domain/transcript.py +359 -0
  20. atif_analytics/infrastructure/__init__.py +3 -0
  21. atif_analytics/infrastructure/corpus_reader.py +546 -0
  22. atif_analytics/infrastructure/parquet_cache.py +186 -0
  23. atif_analytics/infrastructure/settings.py +114 -0
  24. atif_analytics/infrastructure/sqlite_state/__init__.py +3 -0
  25. atif_analytics/infrastructure/sqlite_state/checkpointer.py +326 -0
  26. atif_analytics/infrastructure/sqlite_state/retry_queue.py +296 -0
  27. atif_analytics/py.typed +0 -0
  28. atif_cli/__init__.py +3 -0
  29. atif_cli/__main__.py +8 -0
  30. atif_cli/app.py +2459 -0
  31. atif_cli/converter_adapter.py +151 -0
  32. atif_cli/corpus.py +409 -0
  33. atif_cli/cron.py +311 -0
  34. atif_cli/duck_errors.py +97 -0
  35. atif_cli/embed_lake.py +101 -0
  36. atif_cli/errors.py +89 -0
  37. atif_cli/lake.py +373 -0
  38. atif_cli/lake_sessions.py +270 -0
  39. atif_cli/output.py +297 -0
  40. atif_cli/py.typed +0 -0
  41. atif_converter/__init__.py +3 -0
  42. atif_converter/application/__init__.py +3 -0
  43. atif_converter/application/convert_and_audit.py +294 -0
  44. atif_converter/application/convert_codex.py +210 -0
  45. atif_converter/domain/__init__.py +9 -0
  46. atif_converter/domain/agents.py +40 -0
  47. atif_converter/domain/atif/__init__.py +55 -0
  48. atif_converter/domain/atif/agent.py +37 -0
  49. atif_converter/domain/atif/content.py +161 -0
  50. atif_converter/domain/atif/final_metrics.py +45 -0
  51. atif_converter/domain/atif/metrics.py +49 -0
  52. atif_converter/domain/atif/observation.py +21 -0
  53. atif_converter/domain/atif/observation_result.py +47 -0
  54. atif_converter/domain/atif/step.py +144 -0
  55. atif_converter/domain/atif/subagent_trajectory_ref.py +90 -0
  56. atif_converter/domain/atif/tool_call.py +36 -0
  57. atif_converter/domain/atif/trajectory.py +211 -0
  58. atif_converter/domain/atif/trajectory_validator.py +299 -0
  59. atif_converter/domain/blobs.py +510 -0
  60. atif_converter/domain/claude_code_conversion.py +1077 -0
  61. atif_converter/domain/codex_conversion.py +1036 -0
  62. atif_converter/domain/codex_edges.py +192 -0
  63. atif_converter/domain/codex_enrichment.py +419 -0
  64. atif_converter/domain/codex_fidelity.py +124 -0
  65. atif_converter/domain/codex_nested_calls.py +165 -0
  66. atif_converter/domain/edges.py +113 -0
  67. atif_converter/domain/enrichment.py +412 -0
  68. atif_converter/domain/errors.py +60 -0
  69. atif_converter/domain/fidelity.py +171 -0
  70. atif_converter/domain/model_prices.json +8538 -0
  71. atif_converter/domain/pricing.py +790 -0
  72. atif_converter/domain/result_signals.py +774 -0
  73. atif_converter/domain/schema_version.py +90 -0
  74. atif_converter/domain/session_events.py +408 -0
  75. atif_converter/infrastructure/__init__.py +3 -0
  76. atif_converter/infrastructure/census.py +89 -0
  77. atif_converter/infrastructure/claude_code_converter.py +127 -0
  78. atif_converter/infrastructure/codex_adapter.py +132 -0
  79. atif_converter/infrastructure/codex_census.py +100 -0
  80. atif_converter/infrastructure/codex_converter.py +82 -0
  81. atif_converter/infrastructure/harbor_adapter.py +187 -0
  82. atif_converter/infrastructure/raw_records.py +426 -0
  83. atif_converter/infrastructure/source_archive.py +154 -0
  84. atif_converter/py.typed +0 -0
  85. atif_corpus/__init__.py +13 -0
  86. atif_corpus/application/__init__.py +7 -0
  87. atif_corpus/application/materialize.py +1826 -0
  88. atif_corpus/domain/__init__.py +9 -0
  89. atif_corpus/domain/agents.py +33 -0
  90. atif_corpus/domain/generation.py +34 -0
  91. atif_corpus/domain/layout.py +168 -0
  92. atif_corpus/domain/ports.py +175 -0
  93. atif_corpus/domain/session_id.py +68 -0
  94. atif_corpus/domain/sessions.py +228 -0
  95. atif_corpus/domain/slug.py +72 -0
  96. atif_corpus/domain/source_layout.py +143 -0
  97. atif_corpus/domain/watermark.py +76 -0
  98. atif_corpus/infrastructure/__init__.py +7 -0
  99. atif_corpus/infrastructure/atomic.py +266 -0
  100. atif_corpus/infrastructure/compress_artifacts.py +161 -0
  101. atif_corpus/infrastructure/fake_converter.py +174 -0
  102. atif_corpus/infrastructure/scanner.py +316 -0
  103. atif_corpus/infrastructure/settings.py +130 -0
  104. atif_corpus/infrastructure/source_archive.py +202 -0
  105. atif_corpus/py.typed +0 -0
  106. atif_duck/__init__.py +43 -0
  107. atif_duck/domain/__init__.py +32 -0
  108. atif_duck/domain/artifacts.py +66 -0
  109. atif_duck/domain/authorship.py +165 -0
  110. atif_duck/domain/catalog.py +626 -0
  111. atif_duck/domain/columnar.py +149 -0
  112. atif_duck/domain/embedding_guard.py +88 -0
  113. atif_duck/domain/examples.py +265 -0
  114. atif_duck/domain/lake.py +427 -0
  115. atif_duck/domain/lake_sessions.py +97 -0
  116. atif_duck/domain/raw_readers.py +140 -0
  117. atif_duck/domain/session_id.py +69 -0
  118. atif_duck/domain/sql_literal.py +50 -0
  119. atif_duck/infrastructure/__init__.py +34 -0
  120. atif_duck/infrastructure/analytics.py +469 -0
  121. atif_duck/infrastructure/authorship.py +139 -0
  122. atif_duck/infrastructure/columnar.py +641 -0
  123. atif_duck/infrastructure/lake.py +1383 -0
  124. atif_duck/infrastructure/lake_sessions.py +194 -0
  125. atif_duck/infrastructure/lake_settings.py +63 -0
  126. atif_duck/infrastructure/lake_steps.py +225 -0
  127. atif_duck/infrastructure/projections.py +221 -0
  128. atif_duck/infrastructure/registry.py +2194 -0
  129. atif_duck/infrastructure/stored_artifacts.py +115 -0
  130. atif_duck/py.typed +0 -0
  131. atif_embed/__init__.py +12 -0
  132. atif_embed/application/__init__.py +3 -0
  133. atif_embed/application/embed.py +293 -0
  134. atif_embed/application/prune.py +80 -0
  135. atif_embed/domain/__init__.py +3 -0
  136. atif_embed/domain/discovery.py +70 -0
  137. atif_embed/domain/embedding_guard.py +83 -0
  138. atif_embed/domain/errors.py +94 -0
  139. atif_embed/domain/ports.py +223 -0
  140. atif_embed/domain/sql_literal.py +49 -0
  141. atif_embed/domain/text_stamp.py +71 -0
  142. atif_embed/infrastructure/__init__.py +3 -0
  143. atif_embed/infrastructure/cohere_bedrock.py +433 -0
  144. atif_embed/infrastructure/corpus_text_rows.py +273 -0
  145. atif_embed/infrastructure/gemma_local.py +355 -0
  146. atif_embed/infrastructure/lake_text_rows.py +203 -0
  147. atif_embed/infrastructure/lance_store.py +440 -0
  148. atif_embed/infrastructure/providers.py +30 -0
  149. atif_embed/infrastructure/settings.py +154 -0
  150. atif_embed/py.typed +0 -0
  151. atif_models/__init__.py +14 -0
  152. atif_models/domain/__init__.py +9 -0
  153. atif_models/domain/ports.py +143 -0
  154. atif_models/domain/registry.py +151 -0
  155. atif_models/domain/schema.py +109 -0
  156. atif_models/infrastructure/__init__.py +8 -0
  157. atif_models/infrastructure/openai_bedrock.py +314 -0
  158. atif_models/infrastructure/settings.py +85 -0
  159. atif_models/py.typed +0 -0
  160. atif_sql-0.2.0.dist-info/METADATA +272 -0
  161. atif_sql-0.2.0.dist-info/RECORD +164 -0
  162. atif_sql-0.2.0.dist-info/WHEEL +4 -0
  163. atif_sql-0.2.0.dist-info/entry_points.txt +2 -0
  164. 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()