buildai-cli 0.3.73__tar.gz → 0.3.74__tar.gz
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.
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/.gitignore +23 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/PKG-INFO +1 -1
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/gigcamera.py +236 -1
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/pyproject.toml +1 -1
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/AGENTS.md +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/CLAUDE.md +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/buildai_bootstrap.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/__init__.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/_has_core.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/auth_local.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/__init__.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/api_proxy.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/auth.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/__init__.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/broker.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/common.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/migrate.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/query.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/schema.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/status.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/db/tunnel.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/dev.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/doctor.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/commands/processing.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/config.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/console.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/context.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/db_broker.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/guard.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/internal_api.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/main.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/nl_query/__init__.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/nl_query/dataset_tools.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/ops_init.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/output.py +0 -0
- {buildai_cli-0.3.73 → buildai_cli-0.3.74}/cli/pagination.py +0 -0
|
@@ -17,6 +17,10 @@ ENV/
|
|
|
17
17
|
.eggs/
|
|
18
18
|
*.egg-info/
|
|
19
19
|
dist/
|
|
20
|
+
!apps/buildai-openai-ingest/frontend/
|
|
21
|
+
!apps/buildai-openai-ingest/frontend/dist/
|
|
22
|
+
!apps/buildai-openai-ingest/frontend/dist/index.html
|
|
23
|
+
!apps/buildai-openai-ingest/frontend/dist/mac-mini.png
|
|
20
24
|
build/
|
|
21
25
|
.pytest_cache/
|
|
22
26
|
.mypy_cache/
|
|
@@ -55,6 +59,10 @@ Thumbs.db
|
|
|
55
59
|
|
|
56
60
|
# Local tool state
|
|
57
61
|
.superpowers/
|
|
62
|
+
|
|
63
|
+
# EgoExo sync trial script spill (default output is under experiments/egoexo-pipeline/out/)
|
|
64
|
+
/*.sync_trial.json
|
|
65
|
+
/run_config.json
|
|
58
66
|
.entire/*
|
|
59
67
|
!.entire/
|
|
60
68
|
!.entire/settings.json
|
|
@@ -68,6 +76,13 @@ Thumbs.db
|
|
|
68
76
|
buildai-workspace/
|
|
69
77
|
apps/buildai-sd-card-reader-android/
|
|
70
78
|
|
|
79
|
+
# Local DDS verification refresh artifacts. The reviewed dry-run manifest is
|
|
80
|
+
# tracked separately so tests and handoffs use one stable repair plan.
|
|
81
|
+
.codex/dds-gcs-inventory*.txt
|
|
82
|
+
.codex/dds-gcs-repair-dry-run.current.json
|
|
83
|
+
.codex/dds-gcs-repair-verify*.json
|
|
84
|
+
.codex/dds-*-smoke-current.png
|
|
85
|
+
|
|
71
86
|
# Generated artifacts
|
|
72
87
|
outputs/
|
|
73
88
|
logs/
|
|
@@ -75,8 +90,14 @@ reports/
|
|
|
75
90
|
scratchpad/
|
|
76
91
|
local-archive/
|
|
77
92
|
reference/
|
|
93
|
+
recording-cache/
|
|
78
94
|
/datasets/1m-hour-dataset/*
|
|
79
95
|
!/datasets/1m-hour-dataset/README.md
|
|
96
|
+
!/datasets/1m-hour-dataset/anonymized-remap-summary.json
|
|
97
|
+
!/datasets/1m-hour-dataset/clip_mapping.parquet
|
|
98
|
+
!/datasets/1m-hour-dataset/factory_mapping.csv
|
|
99
|
+
!/datasets/1m-hour-dataset/object_rename_mapping.parquet
|
|
100
|
+
!/datasets/1m-hour-dataset/worker_mapping.csv
|
|
80
101
|
/datasets/250k-hour-dataset/*
|
|
81
102
|
!/datasets/250k-hour-dataset/README.md
|
|
82
103
|
!/datasets/250k-hour-dataset/build/
|
|
@@ -93,6 +114,8 @@ reference/
|
|
|
93
114
|
/datasets/**/*.mp4
|
|
94
115
|
/datasets/**/*.mov
|
|
95
116
|
/datasets/**/*.parquet
|
|
117
|
+
!/datasets/1m-hour-dataset/clip_mapping.parquet
|
|
118
|
+
!/datasets/1m-hour-dataset/object_rename_mapping.parquet
|
|
96
119
|
/datasets/**/manifest.csv
|
|
97
120
|
/datasets/**/scored_device_on_head_candidates.jsonl
|
|
98
121
|
/datasets/label-viz-clips/candidates/*.decisions.jsonl
|
|
@@ -4,9 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import hashlib
|
|
7
|
+
from dataclasses import asdict, is_dataclass
|
|
7
8
|
from datetime import date
|
|
8
9
|
from decimal import Decimal
|
|
9
|
-
from
|
|
10
|
+
from time import monotonic
|
|
11
|
+
from typing import Literal, cast
|
|
10
12
|
from uuid import UUID
|
|
11
13
|
|
|
12
14
|
import typer
|
|
@@ -40,6 +42,7 @@ _SUBMITTED_BY = "00000000-0000-0000-0000-000000000000"
|
|
|
40
42
|
# `last_error` on quarantined rows; reset to `pending` and zero `attempt_count`
|
|
41
43
|
# manually once the underlying problem is fixed.
|
|
42
44
|
_RECAP_QUEUE_MAX_ATTEMPTS = 5
|
|
45
|
+
_READ_MODEL_VALIDATION_PROFILES = frozenset({"internal_admin", "internal_viewer"})
|
|
43
46
|
|
|
44
47
|
|
|
45
48
|
@app.callback()
|
|
@@ -59,6 +62,11 @@ def gigcamera_callback(
|
|
|
59
62
|
"-p",
|
|
60
63
|
help="Auth workflow profile. Use internal_admin for writes.",
|
|
61
64
|
),
|
|
65
|
+
all_proxy: str | None = typer.Option(
|
|
66
|
+
None,
|
|
67
|
+
"--all-proxy",
|
|
68
|
+
help="Proxy URL passed to alloydb-auth-proxy for private-only DB lanes.",
|
|
69
|
+
),
|
|
62
70
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Verbose logging."),
|
|
63
71
|
) -> None:
|
|
64
72
|
"""Capture DB-targeting flags; subcommands initialize the heavy ops context."""
|
|
@@ -68,6 +76,7 @@ def gigcamera_callback(
|
|
|
68
76
|
ctx.obj["_env"] = env
|
|
69
77
|
ctx.obj["_auth"] = auth
|
|
70
78
|
ctx.obj["_user"] = user
|
|
79
|
+
ctx.obj["_all_proxy"] = all_proxy
|
|
71
80
|
ctx.obj["_verbose"] = verbose
|
|
72
81
|
ctx.obj.setdefault("allow_write", False)
|
|
73
82
|
ctx.obj.setdefault("_ops_ready", False)
|
|
@@ -93,6 +102,61 @@ def _require_internal_admin_for_write(ctx: typer.Context, *, write: bool) -> Non
|
|
|
93
102
|
raise typer.BadParameter("writes require --profile internal_admin")
|
|
94
103
|
|
|
95
104
|
|
|
105
|
+
def _require_read_model_validation_profile(ctx: typer.Context) -> None:
|
|
106
|
+
"""Prevent misleading read-model parity runs under scoped developer profiles."""
|
|
107
|
+
|
|
108
|
+
profile = (ctx.obj or {}).get("cli_profile") or "engineers-dev"
|
|
109
|
+
if profile not in _READ_MODEL_VALIDATION_PROFILES:
|
|
110
|
+
raise typer.BadParameter(
|
|
111
|
+
"read-model validation requires --profile internal_admin or --profile internal_viewer"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _context_with_org_id_constraints(
|
|
116
|
+
dal_ctx: object,
|
|
117
|
+
*,
|
|
118
|
+
scope_org_ids: list[UUID] | None,
|
|
119
|
+
) -> object:
|
|
120
|
+
"""Stamp a DAL context with explicit org visibility for scoped read-model rows."""
|
|
121
|
+
|
|
122
|
+
if not scope_org_ids:
|
|
123
|
+
return dal_ctx
|
|
124
|
+
from dal.constraints import DataConstraints, ResourceType
|
|
125
|
+
|
|
126
|
+
requested = frozenset(str(value) for value in scope_org_ids)
|
|
127
|
+
existing = getattr(dal_ctx, "constraints", None)
|
|
128
|
+
if existing is not None:
|
|
129
|
+
allowed_ids = getattr(existing, "allowed_ids", None)
|
|
130
|
+
if isinstance(allowed_ids, dict) and any(
|
|
131
|
+
str(resource_type) != "organization" for resource_type in allowed_ids
|
|
132
|
+
):
|
|
133
|
+
raise typer.BadParameter(
|
|
134
|
+
"--scope-org-id cannot be combined with non-organization constraints"
|
|
135
|
+
)
|
|
136
|
+
if existing.has_constraint("organization"):
|
|
137
|
+
existing_ids = frozenset(str(value) for value in existing.uuid_ids_for("organization"))
|
|
138
|
+
if not requested.issubset(existing_ids):
|
|
139
|
+
raise typer.BadParameter(
|
|
140
|
+
"--scope-org-id must stay within the active profile's organization constraints"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
constraints = DataConstraints(allowed_ids={cast(ResourceType, "organization"): requested})
|
|
144
|
+
if is_dataclass(dal_ctx) and not isinstance(dal_ctx, type):
|
|
145
|
+
from dataclasses import replace
|
|
146
|
+
|
|
147
|
+
return replace(dal_ctx, constraints=constraints)
|
|
148
|
+
setattr(dal_ctx, "constraints", constraints)
|
|
149
|
+
return dal_ctx
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _renderable_result(value: object) -> object:
|
|
153
|
+
"""Convert dataclass DAL summaries to normal JSON-friendly dictionaries."""
|
|
154
|
+
|
|
155
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
156
|
+
return asdict(value)
|
|
157
|
+
return value
|
|
158
|
+
|
|
159
|
+
|
|
96
160
|
def _parse_ist_day(value: str, *, field_name: str) -> date:
|
|
97
161
|
"""Parse one ISO calendar day supplied as a GigCamera IST boundary."""
|
|
98
162
|
|
|
@@ -109,6 +173,177 @@ def _idempotency_key(base: str, *, run_suffix: str | None) -> str:
|
|
|
109
173
|
return f"{base}:{suffix}" if suffix else base
|
|
110
174
|
|
|
111
175
|
|
|
176
|
+
@app.command("admin-analytics-read-model-refresh")
|
|
177
|
+
def admin_analytics_read_model_refresh(
|
|
178
|
+
ctx: typer.Context,
|
|
179
|
+
range_name: Literal["7d", "30d", "all"] = typer.Option(
|
|
180
|
+
"30d",
|
|
181
|
+
"--range",
|
|
182
|
+
help="Canonical analytics range to refresh when not using --recent.",
|
|
183
|
+
),
|
|
184
|
+
org_slug: str | None = typer.Option(
|
|
185
|
+
None,
|
|
186
|
+
"--org-slug",
|
|
187
|
+
help="Optional organization slug matching the admin analytics API filter.",
|
|
188
|
+
),
|
|
189
|
+
scope_org_ids: list[UUID] | None = typer.Option(
|
|
190
|
+
None,
|
|
191
|
+
"--scope-org-id",
|
|
192
|
+
"--org-id",
|
|
193
|
+
help=(
|
|
194
|
+
"Organization id to apply as DAL visibility constraints. Repeat for "
|
|
195
|
+
"multi-org scoped admins."
|
|
196
|
+
),
|
|
197
|
+
),
|
|
198
|
+
recent: bool = typer.Option(
|
|
199
|
+
False,
|
|
200
|
+
"--recent",
|
|
201
|
+
help="Refresh today plus the configured late-update window instead of the full range.",
|
|
202
|
+
),
|
|
203
|
+
late_window_days: int = typer.Option(
|
|
204
|
+
3,
|
|
205
|
+
"--late-window-days",
|
|
206
|
+
min=0,
|
|
207
|
+
max=29,
|
|
208
|
+
help="Number of prior IST days to refresh with --recent.",
|
|
209
|
+
),
|
|
210
|
+
write: bool = typer.Option(False, "--write", help="Actually upsert read-model rows."),
|
|
211
|
+
format: Format = format_option(),
|
|
212
|
+
) -> None:
|
|
213
|
+
"""Refresh the admin analytics shadow read model through the canonical DAL path."""
|
|
214
|
+
|
|
215
|
+
settings = _settings_for_command(ctx, write=write)
|
|
216
|
+
scope_org_id_values = [str(value) for value in scope_org_ids or []]
|
|
217
|
+
if not write:
|
|
218
|
+
render(
|
|
219
|
+
{
|
|
220
|
+
"dry_run": True,
|
|
221
|
+
"mode": "recent" if recent else "range",
|
|
222
|
+
"range": range_name,
|
|
223
|
+
"org_slug": org_slug,
|
|
224
|
+
"scope_org_ids": scope_org_id_values,
|
|
225
|
+
"late_window_days": late_window_days if recent else None,
|
|
226
|
+
"message": "pass --write with --profile internal_admin to upsert read-model rows",
|
|
227
|
+
},
|
|
228
|
+
format=format,
|
|
229
|
+
)
|
|
230
|
+
return
|
|
231
|
+
_require_internal_admin_for_write(ctx, write=True)
|
|
232
|
+
|
|
233
|
+
async def run() -> None:
|
|
234
|
+
from dal.product import dashboard as dashboard_dal
|
|
235
|
+
|
|
236
|
+
started_at = monotonic()
|
|
237
|
+
async with get_cli_context(settings, profile=(ctx.obj or {}).get("cli_profile")) as (
|
|
238
|
+
_db,
|
|
239
|
+
dal_ctx,
|
|
240
|
+
):
|
|
241
|
+
scoped_dal_ctx = _context_with_org_id_constraints(
|
|
242
|
+
dal_ctx,
|
|
243
|
+
scope_org_ids=scope_org_ids,
|
|
244
|
+
)
|
|
245
|
+
if recent:
|
|
246
|
+
result = (
|
|
247
|
+
await dashboard_dal.refresh_recent_gigcamera_admin_daily_analytics_read_model(
|
|
248
|
+
scoped_dal_ctx,
|
|
249
|
+
org_slug=org_slug,
|
|
250
|
+
late_window_days=late_window_days,
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
else:
|
|
254
|
+
result = await dashboard_dal.refresh_gigcamera_admin_daily_analytics_read_model(
|
|
255
|
+
scoped_dal_ctx,
|
|
256
|
+
range_name=range_name,
|
|
257
|
+
org_slug=org_slug,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
render(
|
|
261
|
+
{
|
|
262
|
+
"dry_run": False,
|
|
263
|
+
"mode": "recent" if recent else "range",
|
|
264
|
+
"range": range_name,
|
|
265
|
+
"org_slug": org_slug,
|
|
266
|
+
"scope_org_ids": scope_org_id_values,
|
|
267
|
+
"late_window_days": late_window_days if recent else None,
|
|
268
|
+
"duration_ms": round((monotonic() - started_at) * 1000, 2),
|
|
269
|
+
"result": _renderable_result(result),
|
|
270
|
+
},
|
|
271
|
+
format=format,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
asyncio.run(run())
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@app.command("admin-analytics-read-model-parity")
|
|
278
|
+
def admin_analytics_read_model_parity(
|
|
279
|
+
ctx: typer.Context,
|
|
280
|
+
range_name: Literal["7d", "30d", "all"] = typer.Option(
|
|
281
|
+
"30d",
|
|
282
|
+
"--range",
|
|
283
|
+
help="Canonical analytics range to compare against read-model rows.",
|
|
284
|
+
),
|
|
285
|
+
org_slug: str | None = typer.Option(
|
|
286
|
+
None,
|
|
287
|
+
"--org-slug",
|
|
288
|
+
help="Optional organization slug matching the admin analytics API filter.",
|
|
289
|
+
),
|
|
290
|
+
scope_org_ids: list[UUID] | None = typer.Option(
|
|
291
|
+
None,
|
|
292
|
+
"--scope-org-id",
|
|
293
|
+
"--org-id",
|
|
294
|
+
help=(
|
|
295
|
+
"Organization id to apply as DAL visibility constraints. Repeat for "
|
|
296
|
+
"multi-org scoped admins."
|
|
297
|
+
),
|
|
298
|
+
),
|
|
299
|
+
max_mismatches: int = typer.Option(
|
|
300
|
+
20,
|
|
301
|
+
"--max-mismatches",
|
|
302
|
+
min=1,
|
|
303
|
+
max=200,
|
|
304
|
+
help="Maximum mismatch examples to include in the parity payload.",
|
|
305
|
+
),
|
|
306
|
+
format: Format = format_option(),
|
|
307
|
+
) -> None:
|
|
308
|
+
"""Compare read-model rows with canonical admin analytics without mutating data."""
|
|
309
|
+
|
|
310
|
+
settings = _settings_for_command(ctx, write=False)
|
|
311
|
+
_require_read_model_validation_profile(ctx)
|
|
312
|
+
|
|
313
|
+
async def run() -> None:
|
|
314
|
+
from dal.product import dashboard as dashboard_dal
|
|
315
|
+
|
|
316
|
+
started_at = monotonic()
|
|
317
|
+
async with get_cli_context(settings, profile=(ctx.obj or {}).get("cli_profile")) as (
|
|
318
|
+
_db,
|
|
319
|
+
dal_ctx,
|
|
320
|
+
):
|
|
321
|
+
scoped_dal_ctx = _context_with_org_id_constraints(
|
|
322
|
+
dal_ctx,
|
|
323
|
+
scope_org_ids=scope_org_ids,
|
|
324
|
+
)
|
|
325
|
+
result = await dashboard_dal.get_gigcamera_admin_daily_analytics_read_model_parity(
|
|
326
|
+
scoped_dal_ctx,
|
|
327
|
+
range_name=range_name,
|
|
328
|
+
org_slug=org_slug,
|
|
329
|
+
max_mismatches=max_mismatches,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
render(
|
|
333
|
+
{
|
|
334
|
+
"range": range_name,
|
|
335
|
+
"org_slug": org_slug,
|
|
336
|
+
"scope_org_ids": [str(value) for value in scope_org_ids or []],
|
|
337
|
+
"max_mismatches": max_mismatches,
|
|
338
|
+
"duration_ms": round((monotonic() - started_at) * 1000, 2),
|
|
339
|
+
"parity": result,
|
|
340
|
+
},
|
|
341
|
+
format=format,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
asyncio.run(run())
|
|
345
|
+
|
|
346
|
+
|
|
112
347
|
async def _stale_consolidated_source_rows(
|
|
113
348
|
db: object,
|
|
114
349
|
*,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|