mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-11-01 21:35:25 +08:00
fix(api-tools): isolate processor audit deduplication and validate builder inputs
LANG-230 Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
multica-agent
parent
0f7ee5775e
commit
a7ef3104ec
@@ -265,12 +265,14 @@ class ApplicationAPI:
|
||||
resource_id = next(iter(identity.values()))
|
||||
detail = {'operation': spec.operation_id, 'version': spec.version}
|
||||
actor_name = None
|
||||
dedupe_actor = None
|
||||
if context.principal.principal_type == PrincipalType.PROCESSOR:
|
||||
actor_name = (
|
||||
f'{context.principal.processor_kind}:{context.principal.processor_uuid}'
|
||||
if context.principal.processor_kind
|
||||
else context.principal.processor_uuid
|
||||
)
|
||||
dedupe_actor = f'processor:{context.principal.processor_uuid}:{context.principal.run_id}'
|
||||
detail.update(
|
||||
{
|
||||
'processor_kind': context.principal.processor_kind,
|
||||
@@ -297,6 +299,7 @@ class ApplicationAPI:
|
||||
status_code=status,
|
||||
detail=detail,
|
||||
duration_ms=int((time.monotonic() - observed_at) * 1000),
|
||||
dedupe_actor=dedupe_actor,
|
||||
)
|
||||
except Exception:
|
||||
# Audit failures must preserve the original operation result or error.
|
||||
|
||||
@@ -8,6 +8,8 @@ from collections.abc import Iterable
|
||||
|
||||
from ..context import PrincipalContext, PrincipalType, RequestContext, WorkspaceContext
|
||||
|
||||
_VALID_PROCESSOR_KINDS: typing.Final = frozenset({'agent', 'pipeline', 'event_processor'})
|
||||
|
||||
|
||||
async def build_processor_request_context(
|
||||
ap: typing.Any,
|
||||
@@ -20,22 +22,20 @@ async def build_processor_request_context(
|
||||
trigger_actor: str | None = None,
|
||||
) -> RequestContext:
|
||||
"""Build a trusted RequestContext for internal processor invocations."""
|
||||
workspace_service = getattr(ap, 'workspace_service', None)
|
||||
if workspace_service is None and hasattr(ap, 'ap'):
|
||||
workspace_service = getattr(ap.ap, 'workspace_service', None)
|
||||
if workspace_service is None:
|
||||
raise RuntimeError('Workspace service is required to resolve execution binding')
|
||||
if not isinstance(workspace_uuid, str) or not workspace_uuid.strip():
|
||||
raise ValueError('workspace_uuid is required')
|
||||
if processor_kind not in _VALID_PROCESSOR_KINDS:
|
||||
raise ValueError(f'Invalid processor_kind {processor_kind!r}; must be one of {sorted(_VALID_PROCESSOR_KINDS)}')
|
||||
if not isinstance(processor_uuid, str) or not processor_uuid.strip():
|
||||
raise ValueError('processor_uuid is required')
|
||||
if not isinstance(run_id, str) or not run_id.strip():
|
||||
raise ValueError('run_id is required')
|
||||
|
||||
binding = await workspace_service.get_execution_binding(workspace_uuid)
|
||||
|
||||
app_api = getattr(ap, 'application_api', None)
|
||||
if app_api is None and hasattr(ap, '_operations'):
|
||||
app_api = ap
|
||||
if app_api is None and hasattr(ap, 'ap'):
|
||||
app_api = getattr(ap.ap, 'application_api', None)
|
||||
binding = await ap.workspace_service.get_execution_binding(workspace_uuid)
|
||||
|
||||
allowed_ids = frozenset(operation_ids)
|
||||
permissions: set[str] = set()
|
||||
app_api = getattr(ap, 'application_api', None)
|
||||
if app_api is not None:
|
||||
operations = getattr(app_api, '_operations', {})
|
||||
for op_id in allowed_ids:
|
||||
|
||||
@@ -1127,6 +1127,7 @@ def dedupe_material(
|
||||
action: str,
|
||||
route: str | None,
|
||||
resource_id: str | None,
|
||||
dedupe_actor: str | None = None,
|
||||
) -> str:
|
||||
"""Return the raw material the dedupe key hashes.
|
||||
|
||||
@@ -1134,9 +1135,10 @@ def dedupe_material(
|
||||
so enabling tracing never runs a hash on the request path.
|
||||
"""
|
||||
|
||||
actor_key = dedupe_actor if dedupe_actor is not None else str(actor_account_uuid or '')
|
||||
return '|'.join(
|
||||
[
|
||||
str(actor_account_uuid or ''),
|
||||
actor_key,
|
||||
str(action or ''),
|
||||
str(route or ''),
|
||||
str(resource_id or ''),
|
||||
@@ -1156,6 +1158,7 @@ def compute_dedupe_key(
|
||||
action: str,
|
||||
route: str | None,
|
||||
resource_id: str | None,
|
||||
dedupe_actor: str | None = None,
|
||||
) -> str:
|
||||
"""Build the key that collapses repeated identical observations."""
|
||||
|
||||
@@ -1165,6 +1168,7 @@ def compute_dedupe_key(
|
||||
action=action,
|
||||
route=route,
|
||||
resource_id=resource_id,
|
||||
dedupe_actor=dedupe_actor,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1578,6 +1582,7 @@ class WorkspaceSettingsService:
|
||||
client_ip: str | None = None,
|
||||
user_agent: str | None = None,
|
||||
duration_ms: int = 0,
|
||||
dedupe_actor: str | None = None,
|
||||
) -> bool:
|
||||
"""Queue one operation for traceability. Returns whether it was queued.
|
||||
|
||||
@@ -1630,6 +1635,7 @@ class WorkspaceSettingsService:
|
||||
action=rule.action,
|
||||
route=record_fields['route'],
|
||||
resource_id=record_fields['resource_id'],
|
||||
dedupe_actor=dedupe_actor,
|
||||
)
|
||||
|
||||
return self._enqueue(record_fields)
|
||||
|
||||
Reference in New Issue
Block a user