mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-12 11:46:54 +08:00
feat(operation-trace): lead with the change, fix pipeline extension tracing
The panel answered "who, when, what" poorly: the actual before → after diff
was hidden behind a click, the actor was a muted line, and a whole row went to
the route while duration and hash sat in a permanent right column. The data
already arrives with the page, so the panel now reads top-down in the order an
operator actually looks:
* who and when first, then the action and resource, then the diff spelled out
inline (long values collapse to their size so a pipeline config does not
render as two near-identical blobs), with route / method / status / duration
/ evidence hash moved into the expanded diagnostics line;
* the whole card is the expand toggle with a rotating chevron, which removes
the ambiguity of a small text link whose expanded state looked identical;
* records are bucketed into today / yesterday / dated groups so a long history
reads as a timeline;
* a "changes only" toggle (reusing the level filter) answers "who changed
something" in one click -- in the live log 489 of 499 rows are pure views,
so this is the difference between a haystack and the ten rows that matter.
Tracing fixes found while reading the live log:
* PUT /pipelines/<uuid>/extensions was classified as a plugin config change
because the generic ('/extensions') rule ran first; it now maps to a
pipeline-specific action;
* that handler recorded no diff at all, so "modified extension config" could
never say what changed. It now captures the previous bindings and reports the
before → after.
Cache: the integrity result is now keyed by (Workspace, listing filters) so
each view reuses its own verification, and a delete invalidates all views for
the Workspace. Fourteen tests cover the projection, the verifier, the cache
lifecycle and the failure modes.
This commit is contained in:
@@ -174,7 +174,41 @@ class PipelinesRouterGroup(group.RouterGroup):
|
||||
permission=Permission.RESOURCE_MANAGE,
|
||||
)
|
||||
async def _(pipeline_uuid: str, request_context: RequestContext) -> str:
|
||||
quart.g.operation_log_resource_id = pipeline_uuid
|
||||
json_data = await quart.request.json
|
||||
# Capture "what was bound before" so the trace answers "what changed
|
||||
# into what": without this the log could only say an extension was
|
||||
# configured, never which binding was added or removed.
|
||||
try:
|
||||
previous = await self.ap.pipeline_service.get_pipeline(request_context, pipeline_uuid)
|
||||
except Exception:
|
||||
previous = None
|
||||
# The request uses ``bound_*`` keys while the stored preferences use
|
||||
# the canonical names, so both sides are normalized before diffing.
|
||||
requested = {
|
||||
canonical: json_data[field]
|
||||
for field, canonical in {
|
||||
'bound_plugins': 'plugins',
|
||||
'bound_mcp_servers': 'mcp_servers',
|
||||
'bound_skills': 'skills',
|
||||
'bound_mcp_resources': 'mcp_resources',
|
||||
'enable_all_plugins': 'enable_all_plugins',
|
||||
'enable_all_mcp_servers': 'enable_all_mcp_servers',
|
||||
'enable_all_skills': 'enable_all_skills',
|
||||
'mcp_resource_agent_read_enabled': 'mcp_resource_agent_read_enabled',
|
||||
}.items()
|
||||
if field in json_data
|
||||
}
|
||||
if isinstance(previous, dict):
|
||||
current = {
|
||||
**normalize_extension_preferences(previous.get('extensions_preferences')),
|
||||
**requested,
|
||||
}
|
||||
else:
|
||||
current = requested
|
||||
changes = settings_service.changed_fields(current, requested)
|
||||
if changes:
|
||||
quart.g.operation_log_changes = changes
|
||||
try:
|
||||
validate_extension_preferences(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user