715 lines
20 KiB
Python
715 lines
20 KiB
Python
from __future__ import annotations
|
|
|
|
import functools
|
|
import typing as t
|
|
|
|
import typing_extensions as te
|
|
|
|
if t.TYPE_CHECKING:
|
|
from .tools import Tool, ToolExecutionResponse, tool_execute_params
|
|
|
|
|
|
# TODO: Maybe use `te.Unpack` in tools.execute?
|
|
class ToolExecuteParams(te.TypedDict):
|
|
allow_tracing: te.NotRequired[t.Optional[bool]]
|
|
arguments: t.Dict[str, t.Optional[t.Any]]
|
|
connected_account_id: te.NotRequired[str]
|
|
custom_auth_params: te.NotRequired["tool_execute_params.CustomAuthParams"]
|
|
custom_connection_data: te.NotRequired["tool_execute_params.CustomConnectionData"]
|
|
entity_id: te.NotRequired[str]
|
|
text: te.NotRequired[str]
|
|
user_id: te.NotRequired[str]
|
|
version: te.NotRequired[str]
|
|
dangerously_skip_version_check: te.NotRequired[t.Optional[bool]]
|
|
|
|
|
|
ModifierInOut = t.Union["ToolExecuteParams", "ToolExecutionResponse", "Tool"]
|
|
|
|
|
|
class BeforeExecute(t.Protocol):
|
|
"""
|
|
A modifier that is called before the tool is executed.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
tool: str,
|
|
toolkit: str,
|
|
params: ToolExecuteParams,
|
|
) -> ToolExecuteParams: ...
|
|
|
|
|
|
class AfterExecute(t.Protocol):
|
|
"""
|
|
A modifier that is called after the tool is executed.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
tool: str,
|
|
toolkit: str,
|
|
response: ToolExecutionResponse,
|
|
) -> ToolExecutionResponse: ...
|
|
|
|
|
|
class SchemaModifier(t.Protocol):
|
|
"""
|
|
A modifier that is called to modify the schema of the tool.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
tool: str,
|
|
toolkit: str,
|
|
schema: "Tool",
|
|
) -> "Tool": ...
|
|
|
|
|
|
class BeforeFileUploadCallable(t.Protocol):
|
|
"""Legacy positional form of the ``before_file_upload`` hook.
|
|
|
|
``(path, tool, toolkit) -> str | bool``. Still supported for back-compat,
|
|
but new code should use :class:`BeforeFileUploadContextCallable` so it can
|
|
discriminate local paths from URLs via ``context["source"]``.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
path: str,
|
|
tool: str,
|
|
toolkit: str,
|
|
) -> t.Union[str, bool]: ...
|
|
|
|
|
|
class BeforeFileUploadContext(te.TypedDict):
|
|
"""Context passed to the new-form ``before_file_upload`` hook.
|
|
|
|
- ``path``: the local filesystem path for ``source="path"``, or the URL
|
|
string for ``source="url"``.
|
|
- ``source``: discriminator — ``"path"`` for local paths, ``"url"`` for
|
|
``http(s)://...`` inputs. Mirrors the TypeScript SDK's ``source`` field
|
|
(TS additionally emits ``"file"`` for ``File`` objects; Python has no
|
|
equivalent runtime type).
|
|
- ``tool`` / ``toolkit``: slugs of the tool being executed.
|
|
"""
|
|
|
|
path: str
|
|
source: te.Literal["path", "url"]
|
|
tool: str
|
|
toolkit: str
|
|
|
|
|
|
class BeforeFileUploadContextCallable(t.Protocol):
|
|
"""Preferred form of the ``before_file_upload`` hook.
|
|
|
|
Takes a single :class:`BeforeFileUploadContext` argument and returns either
|
|
a new path/URL string, or ``False`` to abort the upload.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
context: BeforeFileUploadContext,
|
|
) -> t.Union[str, bool]: ...
|
|
|
|
|
|
BeforeFileUploadLike = t.Union[
|
|
BeforeFileUploadCallable,
|
|
BeforeFileUploadContextCallable,
|
|
]
|
|
"""Either form of ``before_file_upload``. Adapted internally."""
|
|
|
|
|
|
def _count_positional_params(fn: t.Callable) -> int:
|
|
"""Return the number of positional (or positional-or-keyword) params, or
|
|
-1 if the signature can't be introspected (e.g. builtins)."""
|
|
import inspect
|
|
|
|
try:
|
|
sig = inspect.signature(fn)
|
|
except (TypeError, ValueError):
|
|
return -1
|
|
return sum(
|
|
1
|
|
for p in sig.parameters.values()
|
|
if p.kind
|
|
in (
|
|
inspect.Parameter.POSITIONAL_ONLY,
|
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
)
|
|
)
|
|
|
|
|
|
def _adapt_before_file_upload(
|
|
hook: BeforeFileUploadLike,
|
|
) -> BeforeFileUploadContextCallable:
|
|
"""Normalise a user-supplied hook to the context-object form.
|
|
|
|
A hook declared with exactly 3 positional parameters is treated as the
|
|
legacy ``(path, tool, toolkit)`` form; anything else (typically a single
|
|
positional ``context`` parameter) is treated as the new form.
|
|
"""
|
|
if _count_positional_params(hook) == 3:
|
|
legacy = t.cast(BeforeFileUploadCallable, hook)
|
|
|
|
def wrap(context: BeforeFileUploadContext) -> t.Union[str, bool]:
|
|
return legacy(context["path"], context["tool"], context["toolkit"])
|
|
|
|
return wrap
|
|
return t.cast(BeforeFileUploadContextCallable, hook)
|
|
|
|
|
|
ModifierSlug: t.TypeAlias = str
|
|
AfterExecuteModifierL: t.TypeAlias = t.Literal["after_execute"]
|
|
BeforeExecuteModifierL: t.TypeAlias = t.Literal["before_execute"]
|
|
SchemaModifierL: t.TypeAlias = t.Literal["schema"]
|
|
BeforeFileUploadModifierL: t.TypeAlias = t.Literal["before_file_upload"]
|
|
|
|
|
|
class Modifier:
|
|
def __init__(
|
|
self,
|
|
modifier: t.Optional[
|
|
AfterExecute
|
|
| BeforeExecute
|
|
| SchemaModifier
|
|
| BeforeExecuteMeta
|
|
| AfterExecuteMeta
|
|
| BeforeFileUploadCallable
|
|
| BeforeFileUploadContextCallable
|
|
],
|
|
type_: (
|
|
AfterExecuteModifierL
|
|
| BeforeExecuteModifierL
|
|
| SchemaModifierL
|
|
| AfterExecuteMetaModifierL
|
|
| BeforeExecuteMetaModifierL
|
|
| BeforeFileUploadModifierL
|
|
),
|
|
tools: t.List[str],
|
|
toolkits: t.List[str],
|
|
) -> None:
|
|
self.modifier = modifier
|
|
self.tools = tools
|
|
self.type = type_
|
|
self.toolkits = toolkits
|
|
|
|
def apply(
|
|
self,
|
|
toolkit: str,
|
|
tool: str,
|
|
data: ModifierInOut,
|
|
modifer_type: str,
|
|
) -> ModifierInOut:
|
|
if self.modifier is None:
|
|
raise ValueError("Modifier is not provided")
|
|
|
|
# If no tools or toolkits are provided, apply the modifier to all tools
|
|
if (
|
|
self.type == modifer_type
|
|
and len(self.tools) == 0
|
|
and len(self.toolkits) == 0
|
|
):
|
|
return self.modifier(tool, toolkit, data) # type: ignore
|
|
|
|
# If the modifier is not the same type, or the slug is not in the tools or
|
|
# toolkits, return the data as is
|
|
if (
|
|
self.type != modifer_type
|
|
or tool not in self.tools
|
|
and toolkit not in self.toolkits
|
|
):
|
|
return data
|
|
|
|
# Apply the modifier to the data
|
|
return self.modifier(tool, toolkit, data) # type: ignore
|
|
|
|
|
|
@t.overload
|
|
def after_execute(
|
|
modifier: t.Optional[AfterExecute],
|
|
) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def after_execute(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[AfterExecute], Modifier]: ...
|
|
|
|
|
|
def after_execute(
|
|
modifier: t.Optional[AfterExecute] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[AfterExecute], Modifier]:
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="after_execute",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[AfterExecute], Modifier],
|
|
functools.partial(
|
|
after_execute,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
@t.overload
|
|
def before_execute(modifier: t.Optional[BeforeExecute]) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def before_execute(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[BeforeExecute], Modifier]: ...
|
|
|
|
|
|
def before_execute(
|
|
modifier: t.Optional[BeforeExecute] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[BeforeExecute], Modifier]:
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="before_execute",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[BeforeExecute], Modifier],
|
|
functools.partial(
|
|
before_execute,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
@t.overload
|
|
def before_file_upload(modifier: t.Optional[BeforeFileUploadLike]) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def before_file_upload(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[BeforeFileUploadLike], Modifier]: ...
|
|
|
|
|
|
def before_file_upload(
|
|
modifier: t.Optional[BeforeFileUploadLike] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[BeforeFileUploadLike], Modifier]:
|
|
"""
|
|
Build a ``Modifier`` for the file-upload hook (same scoping pattern as
|
|
:func:`before_execute`).
|
|
|
|
Your callable may take **either**:
|
|
|
|
- a single ``context`` argument (:class:`BeforeFileUploadContext`) — the
|
|
preferred form, exposes ``context["source"]`` (``"path"`` or ``"url"``),
|
|
or
|
|
- three positional arguments ``(path, tool, toolkit)`` — legacy form, kept
|
|
for back-compat.
|
|
|
|
Return a new path/URL string to substitute, or ``False`` to abort the
|
|
upload (raises :class:`~composio.exceptions.FileUploadAbortedError`).
|
|
|
|
Pass the returned ``Modifier`` in ``modifiers=[...]`` on
|
|
:meth:`composio.core.models.tools.Tools.execute` or ``tools.get``. Multiple
|
|
such modifiers are composed in list order.
|
|
"""
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="before_file_upload",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[BeforeFileUploadLike], Modifier],
|
|
functools.partial(
|
|
before_file_upload,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
@t.overload
|
|
def schema_modifier(modifier: t.Optional[SchemaModifier]) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def schema_modifier(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[SchemaModifier], Modifier]: ...
|
|
|
|
|
|
def schema_modifier(
|
|
modifier: t.Optional[SchemaModifier] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[SchemaModifier], Modifier]:
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="schema",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[SchemaModifier], Modifier],
|
|
functools.partial(
|
|
schema_modifier,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
@t.overload
|
|
def before_execute_meta(modifier: t.Optional[BeforeExecuteMeta]) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def before_execute_meta(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[BeforeExecuteMeta], Modifier]: ...
|
|
|
|
|
|
def before_execute_meta(
|
|
modifier: t.Optional[BeforeExecuteMeta] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[BeforeExecuteMeta], Modifier]:
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="before_execute_meta",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[BeforeExecuteMeta], Modifier],
|
|
functools.partial(
|
|
before_execute_meta,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
@t.overload
|
|
def after_execute_meta(modifier: t.Optional[AfterExecuteMeta]) -> Modifier: ...
|
|
|
|
|
|
@t.overload
|
|
def after_execute_meta(
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> t.Callable[[AfterExecuteMeta], Modifier]: ...
|
|
|
|
|
|
def after_execute_meta(
|
|
modifier: t.Optional[AfterExecuteMeta] = None,
|
|
*,
|
|
tools: t.Optional[t.List[str]] = None,
|
|
toolkits: t.Optional[t.List[str]] = None,
|
|
) -> Modifier | t.Callable[[AfterExecuteMeta], Modifier]:
|
|
if modifier is not None:
|
|
return Modifier(
|
|
modifier=modifier,
|
|
type_="after_execute_meta",
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
)
|
|
|
|
if tools is not None or toolkits is not None:
|
|
return t.cast(
|
|
t.Callable[[AfterExecuteMeta], Modifier],
|
|
functools.partial(
|
|
after_execute_meta,
|
|
tools=tools or [],
|
|
toolkits=toolkits or [],
|
|
),
|
|
)
|
|
|
|
raise ValueError("Either tools or toolkits must be provided")
|
|
|
|
|
|
Modifiers = t.List[Modifier]
|
|
|
|
|
|
def merge_before_file_upload(
|
|
modifiers: t.Optional[Modifiers],
|
|
tool: str,
|
|
toolkit: str,
|
|
) -> t.Optional[BeforeFileUploadContextCallable]:
|
|
"""Compose ``before_file_upload``-type :class:`Modifier`\\ s for this *tool* / *toolkit*.
|
|
|
|
Scoping matches :class:`Modifier` (empty ``tools`` and ``toolkits`` = all tools).
|
|
|
|
Each user-supplied hook is adapted to the context form by
|
|
:func:`_adapt_before_file_upload`, so legacy 3-arg callables keep working
|
|
while new-form callables receive the full :class:`BeforeFileUploadContext`
|
|
(including ``source``).
|
|
"""
|
|
to_chain = [
|
|
m
|
|
for m in (modifiers or [])
|
|
if m.type == "before_file_upload" and m.modifier is not None
|
|
]
|
|
if not to_chain:
|
|
return None
|
|
|
|
def _applies(m: Modifier) -> bool:
|
|
if len(m.tools) == 0 and len(m.toolkits) == 0:
|
|
return True
|
|
return tool in m.tools or toolkit in m.toolkits
|
|
|
|
adapted_chain = [
|
|
(m, _adapt_before_file_upload(t.cast(BeforeFileUploadLike, m.modifier)))
|
|
for m in to_chain
|
|
]
|
|
|
|
def combined(context: BeforeFileUploadContext) -> t.Union[str, bool]:
|
|
p: str = context["path"]
|
|
for m, hook in adapted_chain:
|
|
if not _applies(m):
|
|
continue
|
|
# Preserve source/tool/toolkit while forwarding the (possibly
|
|
# rewritten) path to the next hook.
|
|
next_ctx: BeforeFileUploadContext = {
|
|
"path": p,
|
|
"source": context["source"],
|
|
"tool": context["tool"],
|
|
"toolkit": context["toolkit"],
|
|
}
|
|
out = hook(next_ctx)
|
|
if out is False:
|
|
return False
|
|
if isinstance(out, str):
|
|
p = out
|
|
return p
|
|
|
|
return combined
|
|
|
|
|
|
@t.overload
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: BeforeExecuteModifierL,
|
|
request: ToolExecuteParams,
|
|
) -> ToolExecuteParams: ...
|
|
|
|
|
|
@t.overload
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: AfterExecuteModifierL,
|
|
response: "ToolExecutionResponse",
|
|
) -> "ToolExecutionResponse": ...
|
|
|
|
|
|
@t.overload
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: t.Literal["schema"],
|
|
schema: "Tool",
|
|
) -> "Tool": ...
|
|
|
|
|
|
@t.overload
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: BeforeExecuteMetaModifierL,
|
|
session_id: str,
|
|
params: t.Dict[str, t.Any],
|
|
) -> t.Dict[str, t.Any]: ...
|
|
|
|
|
|
@t.overload
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: AfterExecuteMetaModifierL,
|
|
session_id: str,
|
|
response: "ToolExecutionResponse",
|
|
) -> "ToolExecutionResponse": ...
|
|
|
|
|
|
def apply_modifier_by_type(
|
|
modifiers: Modifiers,
|
|
toolkit: str,
|
|
tool: str,
|
|
*,
|
|
type: t.Literal[
|
|
"before_execute",
|
|
"after_execute",
|
|
"schema",
|
|
"before_execute_meta",
|
|
"after_execute_meta",
|
|
],
|
|
schema: t.Optional["Tool"] = None,
|
|
request: t.Optional["ToolExecuteParams"] = None,
|
|
response: t.Optional["ToolExecutionResponse"] = None,
|
|
session_id: t.Optional[str] = None,
|
|
params: t.Optional[t.Dict[str, t.Any]] = None,
|
|
) -> t.Union[ModifierInOut, t.Dict[str, t.Any]]:
|
|
"""Apply a modifier to a tool."""
|
|
# For meta modifiers, we handle them differently
|
|
if type in ("before_execute_meta", "after_execute_meta"):
|
|
if session_id is None:
|
|
raise ValueError("session_id is required for meta modifiers")
|
|
|
|
if type == "before_execute_meta":
|
|
if params is None:
|
|
raise ValueError("params is required for before_execute_meta")
|
|
result_params: t.Dict[str, t.Any] = params
|
|
for modifier in modifiers:
|
|
if modifier.type == type:
|
|
# Check if modifier should be applied
|
|
should_apply = (
|
|
(len(modifier.tools) == 0 and len(modifier.toolkits) == 0)
|
|
or tool in modifier.tools
|
|
or toolkit in modifier.toolkits
|
|
)
|
|
|
|
if should_apply and modifier.modifier is not None:
|
|
result_params = t.cast(BeforeExecuteMeta, modifier.modifier)(
|
|
tool, toolkit, session_id, result_params
|
|
)
|
|
return result_params
|
|
else: # after_execute_meta
|
|
if response is None:
|
|
raise ValueError("response is required for after_execute_meta")
|
|
result_response: "ToolExecutionResponse" = response
|
|
for modifier in modifiers:
|
|
if modifier.type == type:
|
|
# Check if modifier should be applied
|
|
should_apply = (
|
|
(len(modifier.tools) == 0 and len(modifier.toolkits) == 0)
|
|
or tool in modifier.tools
|
|
or toolkit in modifier.toolkits
|
|
)
|
|
|
|
if should_apply and modifier.modifier is not None:
|
|
result_response = t.cast(AfterExecuteMeta, modifier.modifier)(
|
|
tool, toolkit, session_id, result_response
|
|
)
|
|
return result_response
|
|
|
|
# For regular modifiers
|
|
result: ModifierInOut
|
|
if schema is not None:
|
|
result = schema
|
|
elif request is not None:
|
|
result = request
|
|
elif response is not None:
|
|
result = response
|
|
else:
|
|
raise ValueError("No data provided")
|
|
|
|
for modifier in modifiers:
|
|
result = modifier.apply(
|
|
toolkit=toolkit,
|
|
tool=tool,
|
|
data=result,
|
|
modifer_type=type,
|
|
)
|
|
return result
|
|
|
|
|
|
class BeforeExecuteMeta(t.Protocol):
|
|
"""
|
|
A modifier that is called before the meta tool is executed in a session context.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
tool: str,
|
|
toolkit: str,
|
|
session_id: str,
|
|
params: t.Dict[str, t.Any],
|
|
) -> t.Dict[str, t.Any]: ...
|
|
|
|
|
|
class AfterExecuteMeta(t.Protocol):
|
|
"""
|
|
A modifier that is called after the meta tool is executed in a session context.
|
|
"""
|
|
|
|
def __call__(
|
|
self,
|
|
tool: str,
|
|
toolkit: str,
|
|
session_id: str,
|
|
response: ToolExecutionResponse,
|
|
) -> ToolExecutionResponse: ...
|
|
|
|
|
|
AfterExecuteMetaModifierL: t.TypeAlias = t.Literal["after_execute_meta"]
|
|
BeforeExecuteMetaModifierL: t.TypeAlias = t.Literal["before_execute_meta"]
|
|
|
|
|
|
class ToolOptions(te.TypedDict):
|
|
modify_schema: te.NotRequired[
|
|
t.Dict[ModifierSlug, AfterExecute | BeforeExecute | SchemaModifier]
|
|
]
|