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,221 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""The step-level column expressions, written once and used from two places.
|
|
4
|
+
|
|
5
|
+
The ``steps`` / ``tool_calls`` / ``tool_results`` views turn ATIF step JSON
|
|
6
|
+
into typed columns. The same columns are written to the columnar artifacts at
|
|
7
|
+
materialize time. Both readers must agree byte for byte, so the expressions
|
|
8
|
+
live here and each caller supplies only how it reaches a step's members:
|
|
9
|
+
|
|
10
|
+
* The registry's JSON path has one ``step`` JSON column holding the whole
|
|
11
|
+
step, so a member is ``json_extract(step, '$.<member>')``.
|
|
12
|
+
* The columnar producer receives one JSON column per top-level step member
|
|
13
|
+
(``step_id``, ``timestamp``, ``message``, ...), so the same member is
|
|
14
|
+
``json_extract(<member>, '$')``.
|
|
15
|
+
|
|
16
|
+
Everything below the member boundary is identical text: the message
|
|
17
|
+
flattener, the coalesced booleans, the token casts, and the per-call and
|
|
18
|
+
per-result projections (a ``call`` or ``res`` column always holds one whole
|
|
19
|
+
tool call or observation result as JSON on both paths).
|
|
20
|
+
|
|
21
|
+
Pure string building. No duckdb import; the callers own the connection.
|
|
22
|
+
Every expression comes back typed :data:`~atif_duck.domain.sql_literal.SqlFragment`:
|
|
23
|
+
this module is one of the three producers of SQL text (with the catalog and
|
|
24
|
+
``sql_literal``), and nothing here reads a value from outside the process.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from dataclasses import dataclass
|
|
30
|
+
|
|
31
|
+
from atif_duck.domain.sql_literal import SqlFragment
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, slots=True)
|
|
35
|
+
class StepMemberAccess:
|
|
36
|
+
"""How to spell "the JSON value of top-level step member ``name``" in SQL.
|
|
37
|
+
|
|
38
|
+
``column`` names the SQL column that holds the JSON; ``path_prefix`` is
|
|
39
|
+
the JSONPath that reaches the member inside it (``'$.'`` when the column
|
|
40
|
+
holds a whole step, ``'$'`` when the column already is the member, in
|
|
41
|
+
which case ``name`` is the column itself).
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
#: Column holding the whole step, or ``None`` when each member is its own column.
|
|
45
|
+
whole_step_column: str | None
|
|
46
|
+
|
|
47
|
+
def json(self, member: str, path: str = "") -> SqlFragment:
|
|
48
|
+
"""SQL for ``json_extract`` of ``member`` (optionally a sub-path below it)."""
|
|
49
|
+
column, root = self._locate(member)
|
|
50
|
+
return SqlFragment(f"json_extract({column}, '{root}{path}')")
|
|
51
|
+
|
|
52
|
+
def string(self, member: str, path: str = "") -> SqlFragment:
|
|
53
|
+
"""SQL for ``json_extract_string`` of ``member`` (optionally a sub-path)."""
|
|
54
|
+
column, root = self._locate(member)
|
|
55
|
+
return SqlFragment(f"json_extract_string({column}, '{root}{path}')")
|
|
56
|
+
|
|
57
|
+
def type_of(self, member: str) -> SqlFragment:
|
|
58
|
+
"""SQL for ``json_type`` of ``member``."""
|
|
59
|
+
column, root = self._locate(member)
|
|
60
|
+
return SqlFragment(f"json_type({column}, '{root}')")
|
|
61
|
+
|
|
62
|
+
def _locate(self, member: str) -> tuple[str, str]:
|
|
63
|
+
if self.whole_step_column is not None:
|
|
64
|
+
return self.whole_step_column, f"$.{member}"
|
|
65
|
+
return member, "$"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
#: The registry's JSON path: ``UNNEST(t.steps) AS s(step)`` yields ``step``.
|
|
69
|
+
WHOLE_STEP: StepMemberAccess = StepMemberAccess(whole_step_column="step")
|
|
70
|
+
|
|
71
|
+
#: The producer's path: one JSON column per member, named after the member.
|
|
72
|
+
MEMBER_COLUMNS: StepMemberAccess = StepMemberAccess(whole_step_column=None)
|
|
73
|
+
|
|
74
|
+
#: Top-level step members the ``steps`` view reads. ``tool_calls`` and
|
|
75
|
+
#: ``observation`` are deliberately absent: they feed the other two views.
|
|
76
|
+
STEP_MEMBERS: tuple[str, ...] = (
|
|
77
|
+
"step_id",
|
|
78
|
+
"timestamp",
|
|
79
|
+
"source",
|
|
80
|
+
"model_name",
|
|
81
|
+
"message",
|
|
82
|
+
"metrics",
|
|
83
|
+
"llm_call_count",
|
|
84
|
+
"extra",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def step_columns(access: StepMemberAccess) -> tuple[tuple[str, str], ...]:
|
|
89
|
+
"""``(expression, alias)`` pairs for every ``steps`` column after ``session_id``.
|
|
90
|
+
|
|
91
|
+
Column semantics a transcript-shaped reader would misread:
|
|
92
|
+
|
|
93
|
+
* ``message`` is flattened text: ATIF ``Step.message`` is
|
|
94
|
+
``str | ContentPart[]``; the ARRAY branch joins the parts' ``text``
|
|
95
|
+
fields with blank lines (mirrors harbor's own text bundling).
|
|
96
|
+
* ``prompt_tokens`` is the ATIF TOTAL (input + cache_read +
|
|
97
|
+
cache_creation); ``cached_tokens`` is the cache-read subset;
|
|
98
|
+
``cache_creation`` is dug out of ``metrics.extra`` because harbor only
|
|
99
|
+
preserves the creation split there (fidelity gap 6). The two adapters
|
|
100
|
+
spell that key differently (``cache_creation_input_tokens`` for Claude
|
|
101
|
+
Code, ``cache_write_input_tokens`` for Codex), so the column coalesces
|
|
102
|
+
them: one name reaches SQL, and a cost query does not branch on which
|
|
103
|
+
agent wrote the session.
|
|
104
|
+
* ``is_sidechain`` / ``is_compact_summary`` / ``source_uuids`` come from
|
|
105
|
+
``step.extra`` per the CONTRACT enrichment pass.
|
|
106
|
+
* ``agent_id`` names the subagent a sidechain step belongs to (NULL on the
|
|
107
|
+
main chain); ``images`` lists the attachments pasted into a user
|
|
108
|
+
message, each already moved to the corpus blob store.
|
|
109
|
+
"""
|
|
110
|
+
message_parts = access.json("message", "[*].text")
|
|
111
|
+
return (
|
|
112
|
+
(f"{access.json('step_id')}::BIGINT", "step_id"),
|
|
113
|
+
(f"{access.string('timestamp')}::TIMESTAMP", "ts"),
|
|
114
|
+
(access.string("source"), "source"),
|
|
115
|
+
(access.string("model_name"), "model_name"),
|
|
116
|
+
(
|
|
117
|
+
(
|
|
118
|
+
f"CASE WHEN {access.type_of('message')} = 'ARRAY' "
|
|
119
|
+
f"THEN array_to_string(list_transform({message_parts}, "
|
|
120
|
+
"part -> json_extract_string(part, '$')), '\n\n') "
|
|
121
|
+
f"ELSE {access.string('message')} END"
|
|
122
|
+
),
|
|
123
|
+
"message",
|
|
124
|
+
),
|
|
125
|
+
(f"coalesce({access.json('extra', '.is_sidechain')}::BOOLEAN, false)", "is_sidechain"),
|
|
126
|
+
(
|
|
127
|
+
f"coalesce({access.json('extra', '.is_compact_summary')}::BOOLEAN, false)",
|
|
128
|
+
"is_compact_summary",
|
|
129
|
+
),
|
|
130
|
+
(f"{access.json('metrics', '.prompt_tokens')}::BIGINT", "prompt_tokens"),
|
|
131
|
+
(f"{access.json('metrics', '.completion_tokens')}::BIGINT", "completion_tokens"),
|
|
132
|
+
(f"{access.json('metrics', '.cached_tokens')}::BIGINT", "cached_tokens"),
|
|
133
|
+
(
|
|
134
|
+
(
|
|
135
|
+
f"coalesce({access.json('metrics', '.extra.cache_creation_input_tokens')}, "
|
|
136
|
+
f"{access.json('metrics', '.extra.cache_write_input_tokens')})::BIGINT"
|
|
137
|
+
),
|
|
138
|
+
"cache_creation",
|
|
139
|
+
),
|
|
140
|
+
(f"{access.json('llm_call_count')}::BIGINT", "llm_call_count"),
|
|
141
|
+
(access.json("extra", ".source_uuids"), "source_uuids"),
|
|
142
|
+
(access.string("extra", ".agent_id"), "agent_id"),
|
|
143
|
+
(access.json("extra", ".images"), "images"),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def step_key_columns(access: StepMemberAccess) -> tuple[tuple[str, str], ...]:
|
|
148
|
+
"""The ``(step_id, ts)`` pair every tool call and result row carries."""
|
|
149
|
+
return (
|
|
150
|
+
(f"{access.json('step_id')}::BIGINT", "step_id"),
|
|
151
|
+
(f"{access.string('timestamp')}::TIMESTAMP", "ts"),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
#: Per-call columns over a ``call`` JSON column holding one ATIF ``ToolCall``.
|
|
156
|
+
#: Renamed off the ATIF field names: function_name -> tool_name,
|
|
157
|
+
#: tool_call_id -> tool_use_id, arguments -> tool_input. ``tool_input`` is an
|
|
158
|
+
#: already parsed JSON value, NOT a JSON string, so a query must not
|
|
159
|
+
#: json_extract it twice.
|
|
160
|
+
CALL_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
161
|
+
("json_extract_string(call, '$.function_name')", "tool_name"),
|
|
162
|
+
("json_extract_string(call, '$.tool_call_id')", "tool_use_id"),
|
|
163
|
+
("json_extract(call, '$.arguments')", "tool_input"),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
#: Per-result columns over a ``res`` JSON column holding one ATIF
|
|
167
|
+
#: ``ObservationResult``. ``source_call_id`` is ATIF's join key back to the
|
|
168
|
+
#: tool_calls array, surfaced under the SAME name ``tool_use_id`` that
|
|
169
|
+
#: ``tool_calls`` exposes, so the two views join with ``USING (tool_use_id)``.
|
|
170
|
+
#: The four typed columns after ``content`` come from ``extra``, where the
|
|
171
|
+
#: converter's result-signals pass writes them: NULL means the transcript did
|
|
172
|
+
#: not say, never "false" or "0" (see ``atif_converter.domain.result_signals``).
|
|
173
|
+
RESULT_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
174
|
+
("json_extract_string(res, '$.source_call_id')", "tool_use_id"),
|
|
175
|
+
("json_extract(res, '$.content')", "content"),
|
|
176
|
+
("json_extract(res, '$.extra.is_error')::BOOLEAN", "is_error"),
|
|
177
|
+
("json_extract(res, '$.extra.exit_code')::BIGINT", "exit_code"),
|
|
178
|
+
("json_extract(res, '$.extra.interrupted')::BOOLEAN", "interrupted"),
|
|
179
|
+
("json_extract(res, '$.extra.images')", "images"),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
#: ``session_events`` columns after ``session_id``, over an ``ev`` JSON column
|
|
184
|
+
#: holding one whole ``session_events.jsonl`` line. The registry's JSON path
|
|
185
|
+
#: reads the lines with ``read_json_objects`` and the producer hands each line
|
|
186
|
+
#: in as text, so both spell every column through these same expressions.
|
|
187
|
+
#: ``payload`` is the bounded record body (a parsed JSON value); ``ts`` is NULL
|
|
188
|
+
#: for the record types that carry no timestamp (``cost-state``, ``mode``).
|
|
189
|
+
EVENT_COLUMNS: tuple[tuple[str, str], ...] = (
|
|
190
|
+
("json_extract(ev, '$.seq')::BIGINT", "seq"),
|
|
191
|
+
("json_extract_string(ev, '$.ts')::TIMESTAMP", "ts"),
|
|
192
|
+
("json_extract_string(ev, '$.event_type')", "event_type"),
|
|
193
|
+
("json_extract_string(ev, '$.subtype')", "subtype"),
|
|
194
|
+
("json_extract_string(ev, '$.uuid')", "uuid"),
|
|
195
|
+
("json_extract_string(ev, '$.parent_uuid')", "parent_uuid"),
|
|
196
|
+
("json_extract_string(ev, '$.tool_use_id')", "tool_use_id"),
|
|
197
|
+
("coalesce(json_extract(ev, '$.is_sidechain')::BOOLEAN, false)", "is_sidechain"),
|
|
198
|
+
("json_extract_string(ev, '$.source_file')", "source_file"),
|
|
199
|
+
("json_extract(ev, '$.payload')", "payload"),
|
|
200
|
+
("json_extract(ev, '$.payload_bytes')::BIGINT", "payload_bytes"),
|
|
201
|
+
("coalesce(json_extract(ev, '$.payload_truncated')::BOOLEAN, false)", "payload_truncated"),
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def render(columns: tuple[tuple[str, str], ...]) -> SqlFragment:
|
|
206
|
+
"""Join ``(expression, alias)`` pairs into a SELECT list body."""
|
|
207
|
+
return SqlFragment(",\n ".join(f"{expression} AS {alias}" for expression, alias in columns))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
__all__ = [
|
|
211
|
+
"CALL_COLUMNS",
|
|
212
|
+
"EVENT_COLUMNS",
|
|
213
|
+
"MEMBER_COLUMNS",
|
|
214
|
+
"RESULT_COLUMNS",
|
|
215
|
+
"STEP_MEMBERS",
|
|
216
|
+
"WHOLE_STEP",
|
|
217
|
+
"StepMemberAccess",
|
|
218
|
+
"render",
|
|
219
|
+
"step_columns",
|
|
220
|
+
"step_key_columns",
|
|
221
|
+
]
|