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,9 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""atif-analytics: LLM analytics over the materialized ATIF corpus.
|
|
4
|
+
|
|
5
|
+
Deliberately import-lean: pipelines are imported from their modules
|
|
6
|
+
(``atif_analytics.application.analyze`` etc.), never re-exported here, so a
|
|
7
|
+
bare package import stays off the boto3 subtree (workspace lean-import
|
|
8
|
+
convention).
|
|
9
|
+
"""
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""The ``analyze`` orchestration composing the LLM pipelines.
|
|
4
|
+
|
|
5
|
+
Stage order (materializing is atif-corpus's job and embedding is
|
|
6
|
+
atif-embed's, so neither appears here):
|
|
7
|
+
|
|
8
|
+
1. classify (small tier; interactive sessions only)
|
|
9
|
+
2. conflicts (large tier; interactive sessions only)
|
|
10
|
+
3. friction (small tier; human turns only)
|
|
11
|
+
4. perceived (medium tier; >= 2 human-AI pairs)
|
|
12
|
+
|
|
13
|
+
Every stage honors ``dry_run``, which defaults to True (cost guard): the
|
|
14
|
+
stages then return plan dicts instead of running. ``skip_*`` flags opt out
|
|
15
|
+
per stage.
|
|
16
|
+
|
|
17
|
+
The trajectory pipeline and the three structural ones (cluster, terms,
|
|
18
|
+
community) were removed on 2026-09-27: no program read their output, the
|
|
19
|
+
trajectory labels were 91% neutral, clustering left 48.5% of messages as
|
|
20
|
+
noise, and the structural lane peaked near 17 GB every hour.
|
|
21
|
+
|
|
22
|
+
Heavy pipeline imports are DEFERRED into the body so importing this module
|
|
23
|
+
never drags boto3 onto the CLI's fast path.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from typing import TYPE_CHECKING, Any
|
|
29
|
+
|
|
30
|
+
from loguru import logger
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from atif_analytics.domain.ports import SessionSource
|
|
34
|
+
from atif_analytics.infrastructure.settings import AnalyticsSettings
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run_analyze(
|
|
38
|
+
settings: AnalyticsSettings,
|
|
39
|
+
*,
|
|
40
|
+
since_days: int | None = 30,
|
|
41
|
+
limit: int | None = None,
|
|
42
|
+
dry_run: bool = True,
|
|
43
|
+
skip_classify: bool = False,
|
|
44
|
+
skip_conflicts: bool = False,
|
|
45
|
+
skip_friction: bool = False,
|
|
46
|
+
skip_perceived: bool = False,
|
|
47
|
+
source: SessionSource | None = None,
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
"""Run the LLM analytics stages; return a per-stage summary dict.
|
|
50
|
+
|
|
51
|
+
``skip_*`` flags subtract individual stages. ``source`` is where session
|
|
52
|
+
data comes from (atif-cli passes the lake's when the lake holds the
|
|
53
|
+
corpus); the default reads the per-session files. The summary's
|
|
54
|
+
``session_source`` names which one ran.
|
|
55
|
+
"""
|
|
56
|
+
summary: dict[str, Any] = {"dry_run": dry_run}
|
|
57
|
+
|
|
58
|
+
# One shared corpus reader across every stage: the parsed-steps memo is
|
|
59
|
+
# the expensive part, and every LLM stage walks the same sessions.
|
|
60
|
+
from atif_analytics.infrastructure.corpus_reader import CorpusReader
|
|
61
|
+
|
|
62
|
+
reader = CorpusReader(settings.corpus_root, caps=settings.transcript_caps(), source=source)
|
|
63
|
+
summary["session_source"] = reader.source_name
|
|
64
|
+
|
|
65
|
+
from atif_analytics.application.use_cases._shared import RunBudget
|
|
66
|
+
from atif_analytics.application.use_cases.classify import classify_sessions
|
|
67
|
+
from atif_analytics.application.use_cases.conflicts import detect_conflicts
|
|
68
|
+
from atif_analytics.application.use_cases.friction import detect_user_friction
|
|
69
|
+
from atif_analytics.application.use_cases.perceived import detect_perceived_errors
|
|
70
|
+
|
|
71
|
+
# THE BUDGET CEILING (unattended-cron money guard). Two independent
|
|
72
|
+
# caps, both settings-driven (ATIF_SQL_LLM_MAX_SESSIONS_PER_RUN /
|
|
73
|
+
# ATIF_SQL_LLM_MAX_COST_USD_PER_RUN) and CLI-overridable:
|
|
74
|
+
# * llm_max_sessions_per_run — enforced DURING each pipeline's
|
|
75
|
+
# newest-first admission walk (fresh sessions win), so a deferred
|
|
76
|
+
# session is never rendered;
|
|
77
|
+
# * llm_max_cost_usd_per_run — one RunBudget shared by every stage,
|
|
78
|
+
# priced from the providers' UsageAccumulator RUNNING ACTUALS.
|
|
79
|
+
#
|
|
80
|
+
# The cost cap is a STOP-DISPATCH trigger, not a hard cap: actual
|
|
81
|
+
# spend only becomes visible after a call returns, so the run can end
|
|
82
|
+
# above the ceiling by whatever was in flight at the crossing — at
|
|
83
|
+
# most BUDGET_CHECK_BATCH units on the priciest watched model. It is
|
|
84
|
+
# re-read every batch (not once per write chunk, which is wider than
|
|
85
|
+
# the session ceiling), so the overshoot does not scale with the
|
|
86
|
+
# batch size. When crossed, remaining LLM work stops: the ceiling hit
|
|
87
|
+
# is logged, nothing is stamped (no checkpoint/cache row) for
|
|
88
|
+
# unstarted sessions, and the report flags budget_exhausted=true.
|
|
89
|
+
budget = RunBudget(settings.llm_max_cost_usd_per_run)
|
|
90
|
+
summary["llm_budget"] = {
|
|
91
|
+
"max_sessions_per_run": settings.llm_max_sessions_per_run,
|
|
92
|
+
"max_cost_usd_per_run": settings.llm_max_cost_usd_per_run,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
from atif_analytics.infrastructure.sqlite_state import checkpointer
|
|
96
|
+
|
|
97
|
+
#: Consecutive budget-skips before a stage's starvation escalates
|
|
98
|
+
#: to ERROR (the tail stages of a backlogged corpus can otherwise
|
|
99
|
+
#: starve silently behind WARNING noise).
|
|
100
|
+
max_consecutive_budget_skips = 3
|
|
101
|
+
|
|
102
|
+
state_db = settings.layout().state_db_path
|
|
103
|
+
stages = [
|
|
104
|
+
("classify", classify_sessions, skip_classify),
|
|
105
|
+
("conflicts", detect_conflicts, skip_conflicts),
|
|
106
|
+
("friction", detect_user_friction, skip_friction),
|
|
107
|
+
("perceived", detect_perceived_errors, skip_perceived),
|
|
108
|
+
]
|
|
109
|
+
for name, fn, skip in stages:
|
|
110
|
+
if skip:
|
|
111
|
+
continue
|
|
112
|
+
if not dry_run and budget.exhausted():
|
|
113
|
+
# Starvation visibility: persist the skip so consecutive
|
|
114
|
+
# streaks survive across runs, and escalate to ERROR when
|
|
115
|
+
# the SAME stage has been budget-starved 3+ runs in a row.
|
|
116
|
+
streak = checkpointer.record_budget_skip(state_db, name)
|
|
117
|
+
log = logger.error if streak >= max_consecutive_budget_skips else logger.warning
|
|
118
|
+
log(
|
|
119
|
+
"analyze/{}: SKIPPED — cost ceiling hit "
|
|
120
|
+
"(${:.2f} spent >= ${:.2f} ceiling); nothing stamped "
|
|
121
|
+
"(budget-skipped {} consecutive runs)",
|
|
122
|
+
name,
|
|
123
|
+
budget.spent_usd(),
|
|
124
|
+
budget.max_cost_usd,
|
|
125
|
+
streak,
|
|
126
|
+
)
|
|
127
|
+
summary[name] = {"skipped": "budget_exhausted", "consecutive_skips": streak}
|
|
128
|
+
continue
|
|
129
|
+
n = fn(
|
|
130
|
+
settings,
|
|
131
|
+
since_days=since_days,
|
|
132
|
+
limit=limit,
|
|
133
|
+
dry_run=dry_run,
|
|
134
|
+
reader=reader,
|
|
135
|
+
budget=budget,
|
|
136
|
+
)
|
|
137
|
+
if not dry_run and not budget.exhausted():
|
|
138
|
+
# Clear only when the stage finished INSIDE its budget. A stage
|
|
139
|
+
# that runs but aborts mid-chunk on the ceiling was starved just
|
|
140
|
+
# as surely as one skipped before it started, and clearing on
|
|
141
|
+
# "it ran at all" resets the streak every run, so the 3-strike
|
|
142
|
+
# escalation above can never fire for the stage that needs it
|
|
143
|
+
# most. A stage that spends its last cent doing all of its work
|
|
144
|
+
# also holds its streak; that errs toward the louder log, which
|
|
145
|
+
# is the right direction for a spend guard.
|
|
146
|
+
checkpointer.clear_budget_skips(state_db, name)
|
|
147
|
+
logger.info("analyze/{}: {} (dry_run={})", name, n, dry_run)
|
|
148
|
+
summary[name] = n
|
|
149
|
+
|
|
150
|
+
summary["budget_exhausted"] = False if dry_run else budget.exhausted()
|
|
151
|
+
if not dry_run:
|
|
152
|
+
summary["llm_spent_usd"] = round(budget.spent_usd(), 4)
|
|
153
|
+
|
|
154
|
+
logger.info("analyze: done")
|
|
155
|
+
return summary
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
__all__ = ["run_analyze"]
|