ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
# coding: utf-8
|
||||
# Copyright (c) 2025 inclusionAI.
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import logging
|
||||
from typing import Dict, Any, Callable, Optional, Union, Awaitable
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger("callback_registry")
|
||||
|
||||
|
||||
class CallbackRegistry:
|
||||
"""Callback function registry, used to manage and execute callback functions"""
|
||||
|
||||
# Registry for storing decorated callback functions
|
||||
_registry: Dict[str, Callable] = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, key_name: str, func: Callable) -> Callable:
|
||||
"""Register callback function to the registry
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
func: Callback function to register
|
||||
|
||||
Returns:
|
||||
Registered callback function
|
||||
"""
|
||||
# Check if a callback function with the same key_name already exists
|
||||
if key_name in cls._registry:
|
||||
existing_func = cls._registry[key_name]
|
||||
logger.warning(
|
||||
f"Callback function '{key_name}' already exists and will be overwritten! "
|
||||
f"Original function: {existing_func.__name__ if hasattr(existing_func, '__name__') else str(existing_func)}, "
|
||||
f"New function: {func.__name__ if hasattr(func, '__name__') else str(func)}"
|
||||
)
|
||||
|
||||
cls._registry[key_name] = func
|
||||
return func
|
||||
|
||||
@classmethod
|
||||
def get(cls, key_name: str) -> Optional[Callable]:
|
||||
"""Get registered callback function by key_name
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
|
||||
Returns:
|
||||
Registered callback function, or None if not found
|
||||
"""
|
||||
return cls._registry.get(key_name)
|
||||
|
||||
@classmethod
|
||||
async def execute(
|
||||
cls,
|
||||
key_name: str,
|
||||
tool: Any,
|
||||
args: Dict[str, Any],
|
||||
tool_context: Any,
|
||||
tool_response: Optional[Dict[str, Any]] = None
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Execute registered callback function
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
tool: Tool object
|
||||
args: Tool arguments
|
||||
tool_context: Tool context
|
||||
tool_response: Tool response (for post-callbacks)
|
||||
|
||||
Returns:
|
||||
Return value of the callback function, or None if the callback function doesn't exist
|
||||
"""
|
||||
callback = cls.get(key_name)
|
||||
if not callback:
|
||||
return None
|
||||
|
||||
# Determine parameters based on callback type
|
||||
if tool_response is not None:
|
||||
# Post-callback
|
||||
result = callback(tool, args, tool_context, tool_response)
|
||||
else:
|
||||
# Pre-callback
|
||||
result = callback(tool, args, tool_context)
|
||||
|
||||
# Handle asynchronous callbacks
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def list(cls) -> Dict[str, str]:
|
||||
"""List all registered callback functions
|
||||
|
||||
Returns:
|
||||
Dictionary containing callback function names and descriptions
|
||||
"""
|
||||
return {
|
||||
key: func.__name__ if hasattr(func, '__name__') else str(func)
|
||||
for key, func in cls._registry.items()
|
||||
}
|
||||
|
||||
|
||||
def reg_callback(key_name: str):
|
||||
"""Decorator for registering callback functions
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
|
||||
Returns:
|
||||
Decorator function
|
||||
"""
|
||||
def decorator(func):
|
||||
# Register function to the global registry
|
||||
CallbackRegistry.register(key_name, func)
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
# For backward compatibility, keep these functions
|
||||
def get_callback(key_name: str) -> Optional[Callable]:
|
||||
"""Get registered callback function by key_name
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
|
||||
Returns:
|
||||
Registered callback function, or None if not found
|
||||
"""
|
||||
return CallbackRegistry.get(key_name)
|
||||
|
||||
|
||||
async def execute_callback(
|
||||
key_name: str,
|
||||
tool: Any,
|
||||
args: Dict[str, Any],
|
||||
tool_context: Any,
|
||||
tool_response: Optional[Dict[str, Any]] = None
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Execute registered callback function
|
||||
|
||||
Args:
|
||||
key_name: Unique identifier for the callback function
|
||||
tool: Tool object
|
||||
args: Tool arguments
|
||||
tool_context: Tool context
|
||||
tool_response: Tool response (for post-callbacks)
|
||||
|
||||
Returns:
|
||||
Return value of the callback function, or None if the callback function doesn't exist
|
||||
"""
|
||||
return await CallbackRegistry.execute(key_name, tool, args, tool_context, tool_response)
|
||||
|
||||
|
||||
def list_callbacks() -> Dict[str, str]:
|
||||
"""List all registered callback functions
|
||||
|
||||
Returns:
|
||||
Dictionary containing callback function names and descriptions
|
||||
"""
|
||||
return CallbackRegistry.list()
|
||||
@@ -0,0 +1,84 @@
|
||||
from typing import Tuple
|
||||
|
||||
from aworld.runners.callback.decorator import CallbackRegistry
|
||||
from aworld.runners.handler.base import DefaultHandler
|
||||
from aworld.core.common import Observation, CallbackItem
|
||||
from aworld.core.event.base import Message, Constants
|
||||
from aworld.logs.util import logger
|
||||
from aworld.runners.state_manager import RuntimeStateManager, HandleResult, RunNodeStatus
|
||||
|
||||
|
||||
class ToolCallbackHandler(DefaultHandler):
|
||||
def __init__(self, runner):
|
||||
self.runner = runner
|
||||
|
||||
async def handle(self, message):
|
||||
if message.category != Constants.TOOL_CALLBACK:
|
||||
return
|
||||
logger.info(f"-------ToolCallbackHandler start handle message----: {message}")
|
||||
self.context = message.context
|
||||
observation = None
|
||||
state_mng = RuntimeStateManager.instance()
|
||||
if not state_mng:
|
||||
logger.eror("-------ToolCallbackHandler state_mng is None----")
|
||||
return
|
||||
try:
|
||||
payload = message.payload
|
||||
if not payload:
|
||||
state_mng.run_failed(message.id, "callback failed", [])
|
||||
return
|
||||
if isinstance(payload, CallbackItem):
|
||||
observation = payload.data[0] if isinstance(payload.data, Tuple) else payload.data
|
||||
elif isinstance(payload, Tuple) and isinstance(payload[0], Observation):
|
||||
observation=payload[0]
|
||||
if not isinstance(observation, Observation):
|
||||
state_mng.run_failed(message.id, "callback failed", [])
|
||||
return
|
||||
if not observation.action_result:
|
||||
state_mng.run_failed(message.id, "callback failed", [])
|
||||
return
|
||||
|
||||
results = []
|
||||
for res in observation.action_result:
|
||||
success = False
|
||||
result = HandleResult(
|
||||
result=Message(payload=None,
|
||||
category=Constants.TOOL_CALLBACK,
|
||||
sender=self.name(),
|
||||
session_id=message.context.session_id,
|
||||
headers={"context": message.context}),
|
||||
status=RunNodeStatus.FAILED
|
||||
)
|
||||
if not res or not res.content or not res.tool_name or not res.action_name:
|
||||
results.append(result)
|
||||
continue
|
||||
callback_func = CallbackRegistry.get(res.tool_name + "__" + res.action_name)
|
||||
if not callback_func:
|
||||
result.status = RunNodeStatus.SUCCESS
|
||||
results.append(result)
|
||||
continue
|
||||
callback_res = callback_func(res)
|
||||
if not callback_res or callback_res.success is False:
|
||||
results.append(result)
|
||||
continue
|
||||
result.status = RunNodeStatus.SUCCESS
|
||||
result.result.payload = callback_res
|
||||
results.append(result)
|
||||
|
||||
state_mng.run_succeed(message.id, "test callback succ", results)
|
||||
except Exception as e:
|
||||
# todo
|
||||
logger.warning(f"ToolCallbackHandler Failed to parse payload: {e}")
|
||||
state_mng.run_failed(message.id, "callback failed", [])
|
||||
finally:
|
||||
yield Message(
|
||||
category=Constants.OUTPUT,
|
||||
payload=None,
|
||||
sender=self.name(),
|
||||
session_id=message.session_id,
|
||||
headers={"context": self.context}
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user