Skip to content

Comments

feat: add auto-event emission to CapiscioMiddleware#33

Open
beonde wants to merge 2 commits intomainfrom
feature/middleware-auto-events
Open

feat: add auto-event emission to CapiscioMiddleware#33
beonde wants to merge 2 commits intomainfrom
feature/middleware-auto-events

Conversation

@beonde
Copy link
Member

@beonde beonde commented Feb 24, 2026

Summary

Adds opt-in automatic event emission to the FastAPI/Starlette middleware, closing the gap where all observability events had to be manually emitted.

Problem

The middleware handled badge verification but emitted zero events. Developers had to manually instrument every request lifecycle event, which meant most deployments had no observability into request patterns, verification outcomes, or latency.

Solution

When an EventEmitter is provided to CapiscioMiddleware, it automatically emits standardized events at key middleware lifecycle points:

Event Type When Key Fields
request.received Every inbound request method, path
verification.success Badge verified successfully method, path, caller_did, duration_ms
verification.failed Badge missing or invalid method, path, reason, duration_ms
request.completed After response sent method, path, status_code, duration_ms, caller_did

Design Principles

  • Fully opt-in: No emitter parameter = no events. Zero breaking changes.
  • Safe: All event emission is wrapped in try/except — emitter errors never break request handling.
  • Standardized: Consistent field names across all event types for easy querying.
  • Excluded paths respected: Paths in exclude_paths emit nothing (no noise from health checks).

Usage

from capiscio_sdk.events import EventEmitter
from capiscio_sdk.integrations.fastapi import CapiscioMiddleware

emitter = EventEmitter(agent_id="...", api_key="...", registry_url="...")
app.add_middleware(CapiscioMiddleware, guard=guard, emitter=emitter)
# That's it — events flow automatically

New Event Type Constants

Added to capiscio_sdk/events.py:

  • EVENT_REQUEST_RECEIVED
  • EVENT_REQUEST_COMPLETED
  • EVENT_REQUEST_FAILED
  • EVENT_VERIFICATION_SUCCESS
  • EVENT_VERIFICATION_FAILED

Tests

7 new tests in TestAutoEvents covering:

  • Successful request lifecycle (received → success → completed)
  • Blocked request (received → failed, no completed)
  • Log mode verification failure (all events emitted, request proceeds)
  • Backward compatibility (no emitter = no events)
  • Excluded paths (no events emitted)
  • Emitter error resilience (exceptions don't break requests)

All 50 unit tests pass (22 fastapi + 28 events).

Add opt-in automatic event emission to the FastAPI/Starlette middleware.
When an EventEmitter is provided, the middleware automatically emits:
- request.received: on every inbound request
- verification.success/failed: after badge verification
- request.completed: after response with status_code and duration_ms

Design:
- Fully opt-in: no emitter = no events (backward compatible)
- Safe: emitter errors never break request handling
- Standardized fields: method, path, caller_did, duration_ms, status_code
Copilot AI review requested due to automatic review settings February 24, 2026 19:26
@github-actions
Copy link

✅ Documentation validation passed!

Unified docs will be deployed from capiscio-docs repo.

@github-actions
Copy link

✅ All checks passed! Ready for review.

@github-actions
Copy link

✅ SDK server contract tests passed (test_server_integration.py). Cross-product scenarios are validated in capiscio-e2e-tests.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in automatic observability event emission to the FastAPI/Starlette CapiscioMiddleware so request lifecycle and verification outcomes can be tracked without manual instrumentation.

Changes:

  • Extend CapiscioMiddleware to accept an optional EventEmitter and emit standardized middleware lifecycle events.
  • Add new middleware-related event type constants to EventEmitter.
  • Add unit tests validating auto-event emission across success/failure/excluded-path scenarios.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
capiscio_sdk/integrations/fastapi.py Adds emitter support and emits request.received, verification.*, and request.completed events around middleware execution.
capiscio_sdk/events.py Introduces middleware auto-event type constants on EventEmitter.
tests/unit/test_fastapi_integration.py Adds TestAutoEvents coverage for auto-emission behavior using a mocked EventEmitter.

Comment on lines +493 to +503
def test_auto_events_on_missing_badge_block(self):
"""Emitter receives request.received and verification.failed when badge missing."""
guard = MagicMock()
guard.agent_id = "test-agent"

emitter = MagicMock(spec=EventEmitter)

app = self._make_app(guard, emitter)
client = TestClient(app)

response = client.post("/tasks/send", json={"msg": "hi"})
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-events are tested for missing-badge in block mode and for verification failure in log mode, but there’s no test covering missing-badge behavior in fail_mode="log"/"monitor" with an emitter. Add a test to lock in whether verification.failed should be emitted (per PR description) while still allowing the request.

Copilot uses AI. Check for mistakes.
import json
import pytest
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, patch, call
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call is imported from unittest.mock but never used in this test module; this will be flagged by ruff (F401) and should be removed (or actually used if you intended to assert call ordering).

Suggested change
from unittest.mock import MagicMock, patch, call
from unittest.mock import MagicMock, patch

Copilot uses AI. Check for mistakes.
Comment on lines 100 to +104
if self.fail_mode in ("log", "monitor"):
logger.warning(f"Missing X-Capiscio-Badge header for {request.url.path} ({self.fail_mode} mode)")
request.state.agent = None
request.state.agent_id = None
return await call_next(request)
response = await call_next(request)
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the missing-badge branch for fail_mode = "log"/"monitor", the middleware currently emits request.received and request.completed but does not emit verification.failed. This conflicts with the PR description that verification.failed should be emitted when the badge is missing; consider emitting it here as well (without blocking).

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +112
self._auto_emit(EventEmitter.EVENT_VERIFICATION_FAILED, {
"method": request.method,
"path": path,
"reason": "missing_badge",
})
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verification.failed auto-event emitted for a blocked missing-badge request does not include duration_ms, but the PR description lists duration_ms as a key field for verification failures. Consider including a duration (e.g., from request_start).

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +33
the middleware automatically emits request.received, request.completed,
verification.success, and verification.failed events.
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring says the middleware "automatically emits request.completed" when an emitter is provided, but blocked requests (401/403 paths) return before _auto_emit_completed is called. Update the docstring to clarify when request.completed is (and is not) emitted, or emit it for blocked responses too.

Suggested change
the middleware automatically emits request.received, request.completed,
verification.success, and verification.failed events.
the middleware automatically emits request.received, verification.success,
and verification.failed events, and emits request.completed for requests
that reach the downstream ASGI app (blocked 4xx responses may not emit
request.completed).

Copilot uses AI. Check for mistakes.
# Middleware auto-event types (emitted automatically by CapiscioMiddleware)
EVENT_REQUEST_RECEIVED = "request.received"
EVENT_REQUEST_COMPLETED = "request.completed"
EVENT_REQUEST_FAILED = "request.failed"
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EVENT_REQUEST_FAILED is introduced but not referenced anywhere in the repository. If it’s not part of the current auto-event contract, consider removing it to avoid confusing API consumers, or wire it into the middleware and add coverage.

Suggested change
EVENT_REQUEST_FAILED = "request.failed"

Copilot uses AI. Check for mistakes.
- CHANGELOG.md: document middleware auto-events feature
- README.md: add middleware observability section
- docs/guides/configuration.md: add auto-events configuration guide
- docs/api-reference.md: add EventEmitter and event constants
@github-actions
Copy link

✅ Documentation validation passed!

Unified docs will be deployed from capiscio-docs repo.

@github-actions
Copy link

✅ All checks passed! Ready for review.

@github-actions
Copy link

✅ SDK server contract tests passed (test_server_integration.py). Cross-product scenarios are validated in capiscio-e2e-tests.

@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant