Metadata-Version: 2.5
Name: mistralai-workflows-plugins-triggers
Version: 0.1.0
Summary: Triggers plugin for Mistral Workflows
Project-URL: Homepage, https://mistral.ai
Project-URL: Documentation, https://docs.mistral.ai/workflows/getting-started/introduction
Author-email: Mistral AI <support@mistral.ai>
License: Apache-2.0
Keywords: ai,mistral,plugin,triggers,webhook,workflows
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <3.15,>=3.12
Requires-Dist: mistralai-workflows<4.0.0,>=3.0.0a1
Description-Content-Type: text/markdown

# Mistral Workflows - Triggers Plugin

Typed models for workflows started by triggers.

A trigger links an event source (for example a GitHub or Slack webhook) to a workflow. When the source receives an
event that matches the trigger's filter, the Workflows API starts the workflow asynchronously, passing the webhook
request as input. Signature verification happens before the workflow starts, and the workflow's return value is not
sent back to the webhook caller.

## Install

```bash
pip install mistralai-workflows-plugins-triggers
```

It requires `mistralai-workflows>=3.0.0,<4.0.0` and Python `>=3.12,<3.15`.

## Maturity and limits

- Alpha: models and module layout can change between releases without a deprecation period. Pin an exact version.
- The models describe the payload sent by the trigger system only. Workflows behind the legacy
  `/v1/workflows/webhooks/{name}` route keep using the webhook plugin.
- Provider models cover the events listed in [Provider events](#provider-events); other events validate as the
  provider's `EventReceived` with an untyped body.
- The workflow's return value is not sent back to the webhook caller: the plugin cannot answer a webhook
  synchronously (e.g. a Slack interaction response).

## Quick Start

```python
from pydantic import BaseModel

import mistralai.workflows as workflows
from mistralai.workflows.plugins.triggers import TriggerEvent


class PushBody(BaseModel):
    ref: str


@workflows.workflow.define(name="on-github-push")
class OnGithubPush:
    @workflows.workflow.entrypoint
    async def run(self, event: TriggerEvent) -> None:
        if event.header("x-github-event") != "push":
            return
        push = event.parse_body(PushBody)
        ...
```

Then create a trigger targeting the `on-github-push` workflow.

## `TriggerEvent`

| Field | Description |
|-------|-------------|
| `method` | HTTP method of the webhook request |
| `headers` | Request headers, with lowercase names and credential headers stripped |
| `query_params` | Query string parameters |
| `body` | Body parsed as JSON, or `None` if empty or not valid JSON |
| `raw_body` | Body decoded as UTF-8, or `None` if empty |

Unknown fields are ignored, so workers keep accepting payloads when the API adds new ones.

For custom webhooks, parse the body inside the workflow with `parse_body` rather than typing the entrypoint with a
strict model: the API validates the input against the workflow's input schema before starting it, so a payload that
does not match is rejected without any execution to inspect.

## Provider events

The `github`, `slack`, `linear`, `notion` and `lucca` modules each expose an `Event` class to use as the entrypoint type. It validates
into the subclass matching the payload, a `TriggerEvent` whose `body` is a provider model, so the request fields stay
available.

```python
from mistralai.workflows.plugins.triggers import github


@workflows.workflow.define(name="on-github-event")
class OnGithubEvent:
    @workflows.workflow.entrypoint
    async def run(self, event: github.Event) -> None:
        match event:
            case github.PullRequestOpened(body=body):
                ...
            case github.IssueCommentCreated(body=body):
                ...
            case _:
                return
```

- Every event class registers the permissive `TriggerEvent` input schema, so the API starts the workflow for every
  event and the dispatch happens on the worker.
- Events without a dedicated model validate as the provider's `EventReceived`. A known event with a malformed body
  raises a pydantic `ValidationError`: like any workflow-level error, it fails the workflow task, which Temporal
  retries, and the execution shows the error.
- Events keep their class when serialized and validated again as `Event`, e.g. when passed to an activity.
- A workflow taking a generic `TriggerEvent` can still get the typed event with `github.Event.model_validate(event)`.
- The entrypoint can take a single event class (e.g. `event: github.PullRequestOpened`) or a family class (e.g.
  `event: github.PullRequestEvent`, any pull request action). It is parsed like through `Event`, and a payload of
  another event raises a `ValidationError` (`Expected PullRequestOpened, got PullRequestClosed`): filter the trigger
  so that it only fires for that event.

| Module | Event type taken from | Events |
|--------|-----------------------|--------|
| `github` | `X-GitHub-Event` header and body `action` | `<Event><Action>` for every action of `pull_request`, `pull_request_review`, `pull_request_review_comment`, `issue_comment` and `check_run` (e.g. `PullRequestOpened`, `PullRequestReviewRequested`, `CheckRunCompleted`), each a subclass of its family's `<Event>Event` (e.g. `PullRequestEvent`); `Push` |
| `slack` | Events API `event.type` / `subtype`, the interaction `type`, or the slash command `command` | `MessagePosted`, `MessageEdited`, `MessageDeleted`, `MessageOther`, `ReactionAdded`, `ReactionRemoved`, `AppMentioned`, `MemberJoined`, `AgentSessionStopped`, `BlockActions`, `ViewSubmission`, `ViewClosed`, `SlashCommand`, `AppRateLimited` |
| `linear` | Body `type` and `action` | `IssueCreated`, `IssueUpdated`, `IssueDeleted`, `IssueCommentCreated`, `IssueCommentUpdated`, `IssueCommentDeleted`, `ProjectUpdated`, `AgentSessionCreated`, `AgentSessionPrompted` |
| `notion` | Body `type` | `Page{Created,PropertiesUpdated,ContentUpdated,Moved,Deleted,Undeleted,Locked,Unlocked}`, `PageTranscriptionBlockTranscriptDeleted`, `Database{Created,ContentUpdated,Moved,Deleted,Undeleted,SchemaUpdated}`, `DataSource{Created,ContentUpdated,Moved,Deleted,Undeleted,SchemaUpdated}`, `Comment{Created,Updated,Deleted}`, `FileUpload{Created,Completed,Expired,Failed}`, `View{Created,Updated,Deleted}` |
| `lucca` | Body `topic` | `<Resource>{Created,Updated,Deleted}` for `LegalEntity`, `BusinessEstablishment`, `Department`, `Employee`, `EmployeePersonalRecord`, `Employment`, `JobPosition`, `JobQualification`, `OccupationCategory`, `EmployeeAttribute` |

The `github` module is generated from GitHub's OpenAPI description by `scripts/generate_github_models.py`: don't
edit it by hand, change the generator and run `make generate-github-models`. CI fails if the module is stale. GitHub
spells out every nested object at each place it is used: the generator merges those copies into one class per object
(`User`, `Repository`, `PullRequest`, ...), with a field required only when GitHub always sends it.

Notion and Lucca send a thin envelope (the changed resource's id and URL): fetch the full resource from their API.

Slack interactions and slash commands are delivered form-encoded: for `BlockActions`, `ViewSubmission`, `ViewClosed`
and `SlashCommand`, `body` is the decoded payload and `raw_body` keeps the original form body.
