716 files rewritten, 30 renamed, same family mapping as the frontend pass.
Held back, and each for a reason rather than by oversight:
db_table values insights_hogfunction, insights_hogflow,
insights_hogfunctiontemplate, hogflow_templates.
Renaming a table is ALTER TABLE, so it ships as a
migration with the raw SQL in nodejs that names it.
hog_invocation_results a Datastore table with Kafka engine tables on it
products/review_hog the module path is the Django app path; moving it
moves app_label and every migration is recorded under
(app_label, name). That is the same history break as
renaming a migration file, one level up.
*/migrations/* five files, untouched
whogho an integration vendor
Checked: 395 of 397 changed Python files compile. The two that do not —
insightsql/database/database.py and temporal/schedule.py — fail identically at
origin/main under the same interpreter, because they use `type X = ...` and
`except*`, which need the 3.13 this repo pins and not the 3.9 doing the check.
Running the check against the original is what told those apart from a real
break.
Co-authored-by: Hanzo Dev <[email protected]>
3.3 KiB
AI observability agent guide
Repo-wide conventions live in ../../AGENTS.md — kea over hooks, generated
API over api.get, ph_scoped_capture in Celery, etc. Read that first.
This file is only for rules that are specific to products/ai_observability/
(and to the query runners in insights/insightsql_queries/ai/).
If you don't know the folder yet, read README.md first for the directory map. Skip it if you already know where things live — it's orientation, not a rulebook.
Update both query runners together
LLMTrace and LLMTraceEvent are built by two parallel query runners:
insights/insightsql_queries/ai/trace_query_runner.py(single trace read)insights/insightsql_queries/ai/traces_query_runner.py(list of traces)
Both define their own _map_trace and _map_event. Neither imports
from the other. Any time you add, remove, or rename a field on
LLMTrace or LLMTraceEvent, or change how an event is shaped into
either, change both runners in the same PR and cover the change in
insights/insightsql_queries/ai/test/. The type checker will not catch drift
here.
The same rule applies to routing via ai_events vs events. If one
runner adopts a new code path, the other needs the matching change so
single-trace and list reads don't diverge.
Product docs go under docs/
Product ADRs and engineering decision records belong in
products/ai_observability/docs/, not at the product root.
Custom @action methods need explicit scope action lists
ScopeBasePermission (in insights/permissions.py) only resolves
<scope_object>:read / <scope_object>:write from its built-in defaults
— read_actions = ["list", "retrieve"] and
write_actions = ["create", "update", "partial_update", "patch", "destroy"].
Any custom @action(detail=..., methods=[...]) falls through to None,
which causes APIScopePermission.has_permission to return False with
"This action does not support Personal API Key access". Every personal
API key, OAuth token, and MCP call to that endpoint 403s — even though
the ViewSet has scope_object set.
Two ways to fix it on a ViewSet with custom actions:
-
Per-class action lists. Add
scope_object_read_actionsand / orscope_object_write_actionson the ViewSet, listing the custom action method names. Best when the actions are stable and share the ViewSet's scope.class EvaluationConfigViewSet(TeamAndOrgViewSetMixin, viewsets.ViewSet): scope_object = "evaluation" scope_object_read_actions = ["list"] scope_object_write_actions = ["set_active_key"] -
Per-action override. Pass
required_scopes=[...]to@action(...). Best when the scope for one action diverges from the rest of the ViewSet (e.g. a write-shaped endpoint that's safe enough to expose with:read, likeEvaluationViewSet.test_script).@action(detail=False, methods=["post"], required_scopes=["evaluation:read"]) def test_script(self, request, **kwargs): ...
When you add or rename a custom action, update one of the two — if you
forget, the new endpoint silently 403s every API-key caller. The default
test client uses force_login and skips the scope check, so unit tests
pass even when the production path is broken. Verify with a personal
API key (or via the MCP layer) before merging.