mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
d17a268d3f
Integration branch for the next docs release: a **sessions-first
documentation rewrite** — new and rewritten guides, example pages,
interactive components, and docs tooling — plus the supporting SDK
changes that the new docs describe.
The bulk of this PR is docs (~24k lines across ~150 commits); the SDK
changes (~5k lines) back the new guides.
## Documentation (the bulk)
- **Sessions-first restructure** — reorganized navigation and section
structure (incl. the "Sandbox (prev workbench)" section), with
v3-reorganization redirects so old URLs keep resolving.
- **Rewritten core guides** — quickstart, configuring sessions, triggers
(creating + subscribing to events), proxy-execute, toolkits
enable/disable, and common FAQ, rewritten in the house voice.
- **New example pages** — local-sandbox PR reviewer, daily standup bot,
and slack bot, with runnable build-ups.
- **New interactive components & diagrams** — triggers flow animation,
manage-connections visual, connection-refresh visual, and the
terminal-kit components.
- **Docs tooling** — a docs-graph link-graph connectivity checker,
search reprioritization (deprioritize legacy pages), and SDK-reference
regeneration.
## Supporting SDK changes
**`@composio/core` → 0.13.0 (minor)**
- `composio.sessions.create()` as the first-class sessions API
(`composio.create()` kept as an alias).
- **MCP is opt-in:** default `create()` / `use()` return native-tool
sessions (`SessionWithoutMcp`); pass `{ mcp: true }` to surface
`session.mcp`. _Migration: read `session.mcp` only after creating with
`{ mcp: true }`._
- `session.sandbox` is the canonical resolved config;
`session.workbench` kept as a deprecated alias. `sandbox` is the
preferred session-config key (`workbench` still accepted).
- `connectedAccounts.updateAcl()` graduated from experimental (alias
kept).
- `triggers.parse()` (parse + optionally verify an incoming webhook) and
`triggers.setWebhookSubscription()`.
**`@composio/experimental` → minor** — local-workbench helpers moved
onto the `@composio/experimental/workbench` subpath (out of
`@composio/core/experimental`), keeping the ~14 KB embedded Python
helper out of core. Plus the experimental Pi provider.
**`@composio/slim` → minor.**
**Python → 0.17.0** — mirrors the TS surface: `composio.sessions` mount
(`tool_router` deprecated), `triggers.parse()` /
`set_webhook_subscription()`, the `sandbox` config key, and
`connected_accounts.update_acl()`.
## Review response (#3664)
Addressed the `@composio/core` review:
- **Security:** `triggers.parse()` no longer fails open — a
present-but-empty `verifySecret` (e.g. unset `COMPOSIO_WEBHOOK_SECRET`)
now throws instead of silently skipping verification; omitting it stays
an explicit opt-out (both SDKs).
- Removed snake_case leakage from `transformWebhookSubscription` (+ the
index signature that allowed it).
- **Removed** the TS-only `connectedAccounts.link()` toolkit
auto-resolve (shipped with cancellability / orphaned-auth-config bugs
and was effectively undocumented; to be reintroduced properly later).
- Unified Python error types on `ValidationError`; added `mcp=True`
Python tests; fixed runtime-portability + error-type test assertions.
- Polished deprecation messages; fixed the backwards `/experimental`
`@deprecated` note and the `SessionWithMcp` JSDoc.
## Testing
- **TS:** `@composio/core` + `@composio/experimental` typecheck pass;
vitest green for the touched suites.
- **Python:** `test_tool_router.py` + `test_triggers.py` pass (161
tests).
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Kshitij Jhunjhunwala <kj@composio.dev>
Co-authored-by: Malay Vasa <malayvasa@gmail.com>
Co-authored-by: Sarah Simionescu <sarah@composio.dev>
Co-authored-by: Kshitij Jhunjhunwala <113939507+KJ-11@users.noreply.github.com>
188 lines
6.8 KiB
Python
188 lines
6.8 KiB
Python
"""The ``composio.experimental`` namespace.
|
|
|
|
Houses experimental SDK surfaces whose shape may change in future
|
|
releases. Two flavours live here today:
|
|
|
|
- Decorators for in-process custom tools and toolkits
|
|
(``composio.experimental.tool`` / ``composio.experimental.Toolkit``).
|
|
Implementation details for these still live in :mod:`custom_tool`;
|
|
this module just exposes them on the namespace.
|
|
- Experimental SDK methods that take a Composio client
|
|
(``composio.experimental.update_acl``).
|
|
|
|
Anything new on the ``composio.experimental`` namespace should land here,
|
|
not on the underlying model modules.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typing as t
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from composio.client import HttpClient
|
|
from composio.client.types import connected_account_patch_response
|
|
|
|
from .custom_tool import (
|
|
CustomTool,
|
|
ExperimentalToolkit,
|
|
_get_caller_locals,
|
|
_infer_tool_from_function,
|
|
)
|
|
|
|
# Server-side 400 message the API uses to reject ACL writes against a
|
|
# PRIVATE connection. Substring-matched in `update_acl` here and in the
|
|
# sibling `link()` / `authorize()` call sites — kept as a single constant
|
|
# so a server-side message tweak only requires one edit.
|
|
ACL_ONLY_FOR_SHARED_ERROR_FRAGMENT = "acl_config_for_shared is only valid on SHARED"
|
|
|
|
|
|
class ExperimentalAPI:
|
|
"""Experimental APIs accessed via ``composio.experimental``.
|
|
|
|
Provides decorators for creating custom tools and toolkits that run
|
|
in-process alongside remote Composio tools, plus experimental SDK
|
|
methods whose shape may change in future releases.
|
|
"""
|
|
|
|
Toolkit = ExperimentalToolkit
|
|
|
|
def __init__(self, client: t.Optional[HttpClient] = None) -> None:
|
|
self._client = client
|
|
|
|
def update_acl(
|
|
self,
|
|
nanoid: str,
|
|
*,
|
|
allow_all_users: t.Optional[bool] = None,
|
|
allowed_user_ids: t.Optional[t.List[str]] = None,
|
|
not_allowed_user_ids: t.Optional[t.List[str]] = None,
|
|
) -> connected_account_patch_response.ConnectedAccountPatchResponse:
|
|
"""
|
|
Update the per-user ACL on a SHARED connected account. Experimental —
|
|
shape may change in future releases.
|
|
|
|
Only valid on SHARED connections; raises
|
|
``ComposioAclOnlyForSharedError`` on a PRIVATE connection. Omit a
|
|
parameter to leave it unchanged; pass an empty list to clear an
|
|
allow/deny list. At least one parameter must be provided.
|
|
|
|
:param nanoid: The connected account ID (``ca_xxx``).
|
|
:param allow_all_users: When True, any ``user_id`` may use this
|
|
SHARED connection (subject to the deny list).
|
|
:param allowed_user_ids: Explicit list of allowed ``user_id`` strings.
|
|
Pass ``[]`` to clear.
|
|
:param not_allowed_user_ids: Explicit deny list (wins over allow on
|
|
conflict). Pass ``[]`` to clear — note that clearing the deny
|
|
list silently re-grants access to previously-blocked users.
|
|
:return: Response with ``id``, ``status``, and ``success``.
|
|
|
|
.. deprecated::
|
|
Use :meth:`composio.connected_accounts.update_acl` instead — ACL
|
|
updates graduated onto the ``connected_accounts`` model. This
|
|
experimental alias is kept only for backwards compatibility and
|
|
delegates to it. Prefer the ``connected_accounts`` model; do not
|
|
generate new code against this alias.
|
|
|
|
Example:
|
|
composio.connected_accounts.update_acl(
|
|
'ca_abc',
|
|
allow_all_users=True,
|
|
not_allowed_user_ids=['user_bob'],
|
|
)
|
|
"""
|
|
from composio import exceptions
|
|
|
|
from .connected_accounts import ConnectedAccounts
|
|
|
|
if self._client is None:
|
|
raise exceptions.ValidationError(
|
|
"update_acl requires a Composio client. Access it via "
|
|
"composio.connected_accounts.update_acl(...)."
|
|
)
|
|
|
|
return ConnectedAccounts(client=self._client).update_acl(
|
|
nanoid,
|
|
allow_all_users=allow_all_users,
|
|
allowed_user_ids=allowed_user_ids,
|
|
not_allowed_user_ids=not_allowed_user_ids,
|
|
)
|
|
|
|
@t.overload
|
|
def tool(self, fn: t.Callable[..., t.Any], /) -> CustomTool: ...
|
|
|
|
@t.overload
|
|
def tool(
|
|
self,
|
|
*,
|
|
slug: t.Optional[str] = None,
|
|
name: t.Optional[str] = None,
|
|
description: t.Optional[str] = None,
|
|
extends_toolkit: t.Optional[str] = None,
|
|
output_params: t.Optional[t.Type[BaseModel]] = None,
|
|
preload: t.Optional[bool] = None,
|
|
) -> t.Callable[[t.Callable[..., t.Any]], CustomTool]: ...
|
|
|
|
def tool(
|
|
self,
|
|
fn: t.Optional[t.Callable[..., t.Any]] = None,
|
|
*,
|
|
slug: t.Optional[str] = None,
|
|
name: t.Optional[str] = None,
|
|
description: t.Optional[str] = None,
|
|
extends_toolkit: t.Optional[str] = None,
|
|
output_params: t.Optional[t.Type[BaseModel]] = None,
|
|
preload: t.Optional[bool] = None,
|
|
) -> t.Union[CustomTool, t.Callable[[t.Callable[..., t.Any]], CustomTool]]:
|
|
"""Decorator to create a custom tool from a function.
|
|
|
|
Infers slug, name, description, and input_params from the function.
|
|
Override any with explicit keyword arguments.
|
|
|
|
Examples::
|
|
|
|
# Bare decorator — no parens
|
|
@composio.experimental.tool
|
|
def grep(input: GrepInput, ctx):
|
|
\"\"\"Search for a pattern.\"\"\"
|
|
return {"matches": []}
|
|
|
|
# With parens — no args
|
|
@composio.experimental.tool()
|
|
def grep(input: GrepInput, ctx):
|
|
\"\"\"Search for a pattern.\"\"\"
|
|
return {"matches": []}
|
|
|
|
# With extends_toolkit — inherits auth
|
|
@composio.experimental.tool(extends_toolkit="gmail")
|
|
def create_draft(input: DraftInput, ctx):
|
|
\"\"\"Create a Gmail draft.\"\"\"
|
|
return ctx.proxy_execute(toolkit="gmail", ...)
|
|
"""
|
|
|
|
def decorator(f: t.Callable[..., t.Any]) -> CustomTool:
|
|
annotation_locals = _get_caller_locals()
|
|
return _infer_tool_from_function(
|
|
f,
|
|
slug=slug,
|
|
name=name,
|
|
description=description,
|
|
extends_toolkit=extends_toolkit,
|
|
output_params=output_params,
|
|
preload=preload,
|
|
annotation_locals=annotation_locals,
|
|
)
|
|
|
|
if fn is not None:
|
|
return _infer_tool_from_function(
|
|
fn,
|
|
slug=slug,
|
|
name=name,
|
|
description=description,
|
|
extends_toolkit=extends_toolkit,
|
|
output_params=output_params,
|
|
preload=preload,
|
|
annotation_locals=_get_caller_locals(),
|
|
)
|
|
return decorator
|