@@ -386,6 +386,163 @@ async with Agent(
386386
387387** Note** : Currently input schemas can only be used by subagents, not by regular agents.
388388
389+ ## Middleware
390+
391+ Middleware lets you intercept model, tool, and subagent calls in a request/handler chain.
392+ Each middleware can inspect input, call ` handler(request) ` , and modify the returned response.
393+
394+ Available decorators:
395+
396+ - ` model_middleware `
397+ - ` tool_middleware `
398+ - ` subagent_middleware `
399+
400+ Class-based middleware:
401+
402+ ``` py
403+ from typing import override
404+ from splunklib.ai.middleware import (
405+ AgentMiddleware,
406+ ModelMiddlewareHandler,
407+ ModelRequest,
408+ SubagentMiddlewareHandler,
409+ SubagentRequest,
410+ SubagentResponse,
411+ ToolMiddlewareHandler,
412+ ToolRequest,
413+ ToolResponse,
414+ )
415+ from splunklib.ai.messages import AIMessage
416+
417+
418+ class ExampleMiddleware (AgentMiddleware ):
419+ @override
420+ async def model_middleware (
421+ self , request : ModelRequest, handler : ModelMiddlewareHandler
422+ ) -> AIMessage:
423+ request.system_message = request.system_message.replace(" SECRET" , " [REDACTED]" )
424+ return await handler(request)
425+
426+ @override
427+ async def tool_middleware (
428+ self , request : ToolRequest, handler : ToolMiddlewareHandler
429+ ) -> ToolResponse:
430+ if request.call.name == " temperature" :
431+ return ToolResponse(content = " 25.0" )
432+ return await handler(request)
433+
434+ @override
435+ async def subagent_middleware (
436+ self , request : SubagentRequest, handler : SubagentMiddlewareHandler
437+ ) -> SubagentResponse:
438+ if request.call.name == " SummaryAgent" :
439+ return SubagentResponse(
440+ content = " Executive summary: no critical incidents detected."
441+ )
442+ return await handler(request)
443+ ```
444+
445+ Example model middleware:
446+
447+ ``` py
448+ from splunklib.ai.middleware import (
449+ model_middleware,
450+ ModelMiddlewareHandler,
451+ ModelRequest,
452+ )
453+ from splunklib.ai.messages import AIMessage
454+
455+
456+ @model_middleware
457+ async def redact_system_prompt (
458+ request : ModelRequest, handler : ModelMiddlewareHandler
459+ ) -> AIMessage:
460+ request.system_message = request.system_message.replace(" SECRET" , " [REDACTED]" )
461+ return await handler(request)
462+ ```
463+
464+ Example tool middleware:
465+
466+ ``` py
467+ from splunklib.ai.middleware import (
468+ tool_middleware,
469+ ToolMiddlewareHandler,
470+ ToolRequest,
471+ ToolResponse,
472+ )
473+
474+
475+ @tool_middleware
476+ async def mock_temperature (
477+ request : ToolRequest, handler : ToolMiddlewareHandler
478+ ) -> ToolResponse:
479+ if request.call.name == " temperature" :
480+ return ToolResponse(content = " 25.0" )
481+ return await handler(request)
482+ ```
483+
484+ Example subagent middleware:
485+
486+ ``` py
487+ from splunklib.ai.middleware import (
488+ subagent_middleware,
489+ SubagentMiddlewareHandler,
490+ SubagentRequest,
491+ SubagentResponse,
492+ )
493+
494+
495+ @subagent_middleware
496+ async def mock_subagent (
497+ request : SubagentRequest, handler : SubagentMiddlewareHandler
498+ ) -> SubagentResponse:
499+ if request.call.name == " SummaryAgent" :
500+ return SubagentResponse(
501+ content = " Executive summary: no critical incidents detected."
502+ )
503+ return await handler(request)
504+ ```
505+
506+ Retry pattern (bounded retries):
507+
508+ ``` py
509+ from splunklib.ai.middleware import (
510+ tool_middleware,
511+ ToolMiddlewareHandler,
512+ ToolRequest,
513+ ToolResponse,
514+ )
515+
516+
517+ class RetryableToolError (Exception ): pass
518+
519+
520+ @tool_middleware
521+ async def retry_transient_tool_failures (
522+ request : ToolRequest, handler : ToolMiddlewareHandler
523+ ) -> ToolResponse:
524+ last_error: Exception | None = None
525+ for _ in range (3 ):
526+ try :
527+ return await handler(request)
528+ except RetryableToolError as e:
529+ last_error = e
530+
531+ assert last_error is not None
532+ raise last_error
533+ ```
534+
535+ Pass middleware to ` Agent ` :
536+
537+ ``` py
538+ async with Agent(
539+ model = model,
540+ service = service,
541+ system_prompt = " ..." ,
542+ middleware = [redact_system_prompt, mock_temperature, mock_subagent],
543+ ) as agent: ...
544+ ```
545+
389546## Hooks
390547
391548Hooks are user-defined callback functions that can be registered to execute at specific points
0 commit comments