mistralai-workflows-cli 1.2.1__tar.gz → 1.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/.gitignore +3 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/Makefile +6 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/PKG-INFO +1 -1
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/pyproject.toml +1 -2
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/SKILL.md +15 -3
- mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/connectors.mdx +178 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/limitations.mdx +11 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/rate-limiting.mdx +3 -2
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/workflows-plugins.mdx +66 -0
- mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/linting/rules/determinism.yaml +217 -0
- mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/linting/rules/imports.yaml +114 -0
- mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/linting/rules/io.yaml +116 -0
- mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/linting/rules/structure.yaml +120 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/test_workflow.py +88 -73
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/Makefile +22 -1
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/pyproject.toml.jinja +1 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/start.py +1 -3
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/worker.py +3 -5
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/activities.py +5 -15
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/models.py +3 -9
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/workflow.py +4 -11
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/activities.py +7 -23
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/models.py +4 -12
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/workflow.py +2 -6
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/activities.py +1 -3
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/models.py +0 -1
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/workflow.py +1 -4
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/__init__.py +1 -1
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/linear_activities.py +5 -20
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/summary_activity.py +2 -8
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/workflow.py +2 -7
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/worker.py +2 -3
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/workflows/hello.py +1 -2
- {mistralai_workflows_cli-1.2.1/src/mistral_workflows_cli/_template/template/src → mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template}/worker.py +1 -1
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/README.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/copier.yml +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/execution_ids.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/core-concepts.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/installation.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/introduction.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/python-sdk.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/value-proposition.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/getting-started/your-first-workflow.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/_deployment-patterns.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/activities.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/assist-workflows.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/concurrency.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/dependency-injection.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/diagnostics.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/durable-agents.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/error-codes.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/handling-large-data.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/local-execution.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/migration-v2-to-v3.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/observability.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/payload-encoding.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/scheduling.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/signals-queries-updates.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/streaming-consumption.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/streaming.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/testing.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/workflows-exception.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/workflows.mdx +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/pipeline_pattern.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/workflow_testing.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.env.jinja +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.gitignore +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/README.md.jinja +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/dev.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/README.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/sample_data/shipping_doc_anomaly.png +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/sample_data/shipping_doc_normal.png +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/README.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sample_data/legacy_repo/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sample_data/legacy_repo/api_client.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sample_data/legacy_repo/processor.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sample_data/legacy_repo/utils.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sub_workflow.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/README.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/claim_high_severity.json +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/claim_low_severity.json +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/claim_medium_severity.json +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/photos/CREDITS.md +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/photos/claim_high_totaled_front.jpg +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/photos/claim_high_totaled_side.jpg +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/photos/claim_low_scratch_door.jpg +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/sample_data/photos/claim_medium_dent_rear.jpg +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/README.md +0 -0
- {mistralai_workflows_cli-1.2.1/src/mistral_workflows_cli/_template/template → mistralai_workflows_cli-1.2.3/src/mistral_workflows_cli/_template/template/src}/worker.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/workflows/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_version.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/main.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/setup.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/tests/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/tests/test_setup.py +0 -0
- {mistralai_workflows_cli-1.2.1 → mistralai_workflows_cli-1.2.3}/uv.lock +0 -0
|
@@ -42,6 +42,7 @@ integration-test:
|
|
|
42
42
|
$(MAKE) _test-start-examples
|
|
43
43
|
$(MAKE) _test-execute-help
|
|
44
44
|
$(MAKE) _test-execute-bad-json
|
|
45
|
+
$(MAKE) _test-make-check
|
|
45
46
|
$(MAKE) _test-wheel-contents
|
|
46
47
|
@echo ""
|
|
47
48
|
@echo "All integration tests passed."
|
|
@@ -124,3 +125,8 @@ _test-wheel-contents:
|
|
|
124
125
|
| grep -q "entrypoints/worker.py" \
|
|
125
126
|
&& echo "OK: wheel contains entrypoints package" \
|
|
126
127
|
|| (echo "FAIL: wheel is missing the entrypoints package" && exit 1)
|
|
128
|
+
|
|
129
|
+
_test-make-check:
|
|
130
|
+
@echo "--- test: 'make check' (ruff, mypy, workflow lint) passes on the generated project"
|
|
131
|
+
cd $(GENERATED_PROJECT) && $(MAKE) check
|
|
132
|
+
@echo "OK: make check passes on the generated project"
|
|
@@ -32,8 +32,7 @@ packages = ["src/mistral_workflows_cli"]
|
|
|
32
32
|
|
|
33
33
|
[tool.ruff]
|
|
34
34
|
line-length = 120
|
|
35
|
-
|
|
36
|
-
extend-exclude = ["src/mistral_workflows_cli/_template"]
|
|
35
|
+
extend-exclude = ["src/mistral_workflows_cli/_template/template/src/examples/code_modernization/sample_data/legacy_repo"]
|
|
37
36
|
|
|
38
37
|
[tool.ruff.lint]
|
|
39
38
|
select = ["E", "F", "I", "UP"]
|
|
@@ -73,10 +73,11 @@ The documentation is organized into several categories:
|
|
|
73
73
|
- **[Payload Encoding](references/guides/payload-encoding.mdx)**: Payload offloading, AES-GCM encryption, key rotation
|
|
74
74
|
- **[Observability](references/guides/observability.mdx)**: OpenTelemetry traces, trace sampling
|
|
75
75
|
- **[Durable Agents](references/guides/durable-agents.mdx)**: Agent, Runner, RemoteSession/LocalSession, MCP, multi-agent handoffs
|
|
76
|
+
- **[Connectors](references/guides/connectors.mdx)**: Call third-party tools (GitHub, Notion, Slack, ...) from a workflow or give them to an agent, without holding the service's credentials
|
|
76
77
|
- **[Conversational Workflows](references/guides/assist-workflows.mdx)**: InteractiveWorkflow, HITL, ChatInput/FormInput, Canvas editing, Rich UI components, Tool UI states
|
|
77
78
|
- **[Local Execution](references/guides/local-execution.mdx)**: No-infra dev mode with Pydantic model params
|
|
78
|
-
- **[Limitations](references/guides/limitations.mdx)**: System constraints and limits
|
|
79
|
-
- **[Workflows Plugins](references/guides/workflows-plugins.mdx)**: Mistral AI plugin, Webhook plugin, Nuage plugin, custom plugins
|
|
79
|
+
- **[Limitations](references/guides/limitations.mdx)**: System constraints, determinism rules, sandbox import restrictions (module-level non-deterministic imports + `imports_passed_through`), I/O and heavy work belongs in activities, execution-history limits
|
|
80
|
+
- **[Workflows Plugins](references/guides/workflows-plugins.mdx)**: Mistral AI plugin (calling chat / structured-output / embeddings / OCR models), Webhook plugin, Nuage plugin, custom plugins
|
|
80
81
|
- **[Deployment Patterns](references/guides/_deployment-patterns.mdx)**: Best practices for deploying workflows
|
|
81
82
|
- **[Migration v2 to v3](references/guides/migration-v2-to-v3.mdx)**: Breaking changes and upgrade steps from SDK v2 through v3.4.0
|
|
82
83
|
|
|
@@ -100,6 +101,16 @@ python .agents/skills/workflows/scripts/test_workflow.py <workflow_file> --input
|
|
|
100
101
|
|
|
101
102
|
If a workflow is known to be long-running (e.g. multi-step agent, large data processing), increase timeouts proportionally — but start short and only raise them when you see legitimate timeout failures, not preemptively.
|
|
102
103
|
|
|
104
|
+
**Lint & check** — validate your code before running it. Three checks are available:
|
|
105
|
+
- `make lint` — Ruff: style, imports, and common errors (`make format` auto-fixes most)
|
|
106
|
+
- `make typecheck` — mypy static type checking
|
|
107
|
+
- `make lint-workflows` — Semgrep workflow rules: determinism violations, I/O outside activities, missing return-type annotations, unsafe imports (advisory — reports but does not block)
|
|
108
|
+
|
|
109
|
+
Run all three at once after writing or editing a workflow, and address any findings:
|
|
110
|
+
```bash
|
|
111
|
+
make check
|
|
112
|
+
```
|
|
113
|
+
|
|
103
114
|
### Internal References
|
|
104
115
|
|
|
105
116
|
These are additional patterns and utilities not covered in the official docs:
|
|
@@ -121,6 +132,7 @@ Use this skill when you need to:
|
|
|
121
132
|
7. **Build conversational workflows**: Create interactive workflows with HITL, forms, canvas, and rich UI
|
|
122
133
|
8. **Ensure fault tolerance**: Implement systems that can recover from failures automatically
|
|
123
134
|
9. **Stream events**: Real-time token streaming and progress updates via NATS
|
|
135
|
+
10. **Integrate external services**: Call GitHub, Notion, Slack, and other connectors with platform-managed credentials
|
|
124
136
|
|
|
125
137
|
## Key Features
|
|
126
138
|
|
|
@@ -135,5 +147,5 @@ Use this skill when you need to:
|
|
|
135
147
|
- **Large payload handling**: OffloadableField with S3/Azure/GCS blob storage
|
|
136
148
|
- **Conversational workflows**: Interactive workflows with Vibe integration, forms, canvas, and rich UI components
|
|
137
149
|
- **Durable agents**: AI agents with MCP support, multi-agent handoffs, and persistent state
|
|
150
|
+
- **Connectors**: Call external services with platform-managed credentials avoiding secrets in the workflow code
|
|
138
151
|
- **Scalability**: Designed to handle complex, distributed applications
|
|
139
|
-
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: connectors
|
|
3
|
+
title: Connectors
|
|
4
|
+
sidebar_position: 8
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Connectors in Workflows
|
|
8
|
+
|
|
9
|
+
Connectors let workflows call external services (GitHub, Notion, Slack, ...) using credentials managed by the Mistral Connectors platform. The workflow **declares** which connectors it needs; the platform resolves credentials and drives any OAuth flow automatically. The same code works whether the connector uses a bearer/PAT token or OAuth2. **You never put secrets in workflow code**.
|
|
10
|
+
|
|
11
|
+
Connectors ship in the Mistral plugin:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv add 'mistralai-workflows[mistralai]'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Discovering configured connectors
|
|
18
|
+
|
|
19
|
+
The `name` you pass to `connector(...)` must match a connector already configured for the workspace, don't guess them ever! To see the configured connectors and their exact names, run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run python -c "from dotenv import load_dotenv; load_dotenv(); import os; from mistralai.client import Mistral; print(*[c.name for c in Mistral(api_key=os.environ['MISTRAL_API_KEY']).beta.connectors.list().items], sep='\n')"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For a connector's tools, use `list_tools` (returns a plain list, not `.items`) and don't guess them even if you think you know how they must be called!:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run python -c "from dotenv import load_dotenv; load_dotenv(); import os; from mistralai.client import Mistral; print(*[t.name for t in Mistral(api_key=os.environ['MISTRAL_API_KEY']).beta.connectors.list_tools(connector_id_or_name='github_app')], sep='\n')"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Each tool also has `.jsonschema` for its `arguments` (add `t.jsonschema` to the print when you need it).
|
|
32
|
+
(You only need these calls to figure out how to write the code. Don't put these snippets in the workflow itself.)
|
|
33
|
+
|
|
34
|
+
## Declaring connectors in a workflow
|
|
35
|
+
|
|
36
|
+
Three pieces wire a connector in:
|
|
37
|
+
|
|
38
|
+
1. `connector("name")` — declares a **connector slot**.
|
|
39
|
+
2. `@uses_connectors(...)` — attaches slots to the workflow class; apply it **after** `@workflow.define` (on the line below).
|
|
40
|
+
3. `Depends(slot)` — injects a `ToolCallClient` into an activity.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import pydantic
|
|
44
|
+
import mistralai.workflows as workflows
|
|
45
|
+
from mistralai.workflows import Depends
|
|
46
|
+
from mistralai.workflows.plugins.mistralai.connectors import (
|
|
47
|
+
ToolCallClient,
|
|
48
|
+
connector,
|
|
49
|
+
uses_connectors,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
github = connector("github_app")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CreateIssueInput(pydantic.BaseModel):
|
|
56
|
+
owner: str
|
|
57
|
+
repo: str
|
|
58
|
+
title: str
|
|
59
|
+
body: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@workflows.activity(name="create-github-issue")
|
|
63
|
+
async def create_github_issue(
|
|
64
|
+
owner: str,
|
|
65
|
+
repo: str,
|
|
66
|
+
title: str,
|
|
67
|
+
body: str,
|
|
68
|
+
client: ToolCallClient = Depends(github),
|
|
69
|
+
) -> None:
|
|
70
|
+
await client.call_tool(
|
|
71
|
+
tool_name="issue_write",
|
|
72
|
+
arguments={
|
|
73
|
+
"method": "create",
|
|
74
|
+
"owner": owner,
|
|
75
|
+
"repo": repo,
|
|
76
|
+
"title": title,
|
|
77
|
+
"body": body,
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@workflows.workflow.define(name="github-issue-creator")
|
|
83
|
+
@uses_connectors(github)
|
|
84
|
+
class GitHubIssueCreatorWorkflow:
|
|
85
|
+
@workflows.workflow.entrypoint
|
|
86
|
+
async def run(self, prompt: CreateIssueInput) -> None:
|
|
87
|
+
await create_github_issue(prompt.owner, prompt.repo, prompt.title, prompt.body)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Watch out for:
|
|
91
|
+
|
|
92
|
+
- **Import path** is `mistralai.workflows.plugins.mistralai.connectors` (repeated `mistralai`), not `...plugins.connectors`.
|
|
93
|
+
- **`@uses_connectors` is required** and sits **below** `@workflow.define`. Without it, credentials are never resolved and `call_tool` fails.
|
|
94
|
+
- A `ToolCallClient` only works **inside an activity** (it resolves credentials from the workflow context at call time). A failed call raises `ConnectorToolCallError` (non-retryable).
|
|
95
|
+
- **Tool names and argument schemas are connector-specific** (e.g. GitHub's `issue_write`, Notion's `notion-create-pages`) and aren't inferable from the SDK types. Discover them with `client.beta.connectors.list_tools_async(connector_id_or_name=...)` or take them from the task — don't guess.
|
|
96
|
+
- **No special worker setup** — the Mistral plugin auto-registers the connector auth interceptor; run on a normal worker.
|
|
97
|
+
|
|
98
|
+
### `connector(...)` parameters
|
|
99
|
+
|
|
100
|
+
| Parameter | Default | Purpose |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `name` (positional) | — | Must match a connector configured for the workspace; discover via `client.beta.connectors.list_async()`. |
|
|
103
|
+
| `auto_auth` | `True` | Run the OAuth preflight before the workflow starts. |
|
|
104
|
+
| `credentials_name` | `None` | Pin a specific named credential set; otherwise the user's default is used. |
|
|
105
|
+
| `allow_mcp_ui` | `False` | Surface an MCP tool's `_meta.ui.resourceUri` as a side app in Vibe after a matching tool call (fire-and-forget — the workflow doesn't wait for or consume the result). |
|
|
106
|
+
|
|
107
|
+
## Acting as the user (on-behalf-of)
|
|
108
|
+
|
|
109
|
+
A connector call runs as one of two identities:
|
|
110
|
+
|
|
111
|
+
- **The triggering user** — set `on_behalf_of=True` on `@workflow.define`, and the connector uses the credentials *that user* authorized (their GitHub, their Notion, ...).
|
|
112
|
+
- **The worker** (default) — the connector uses the worker's own stored credentials: one shared service identity for every run.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
@workflows.workflow.define(name="github-issue-creator", on_behalf_of=True)
|
|
116
|
+
@uses_connectors(github)
|
|
117
|
+
class GitHubIssueCreatorWorkflow:
|
|
118
|
+
...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Fall back to worker credentials (omit `on_behalf_of`) when there's no triggering user or you deliberately want one shared identity. Caveats (platform-enforced):
|
|
122
|
+
|
|
123
|
+
- **Needs a triggering user.** Starting an `on_behalf_of=True` workflow without a user session is rejected (`403 "On-behalf-of workflows require user identity"`). For the same reason it **can't be combined with `schedules`** — the SDK raises at definition time, since a scheduled run has no triggering user.
|
|
124
|
+
- **Restricted to Mistral-internal orgs.** Registering an `on_behalf_of=True` workflow returns `403 "on_behalf workflows are currently restricted to Mistral-internal organizations"` unless the org is internal or the `ON_BEHALF_WORKFLOWS_ENABLED` feature flag is enabled.
|
|
125
|
+
|
|
126
|
+
## Connectors in durable agents
|
|
127
|
+
|
|
128
|
+
Pass slots to an `Agent` via `connectors=[...]` so it can call connector tools autonomously. Keep `@uses_connectors` on the workflow. See the [Durable Agents](durable-agents.mdx) guide for agent/runner output handling.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import mistralai.workflows as workflows
|
|
132
|
+
from mistralai.workflows.plugins.mistralai import Agent, Runner
|
|
133
|
+
from mistralai.workflows.plugins.mistralai.connectors import connector, uses_connectors
|
|
134
|
+
|
|
135
|
+
github = connector("github_app")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@workflows.workflow.define(name="github-agent")
|
|
139
|
+
@uses_connectors(github)
|
|
140
|
+
class GitHubAgentWorkflow:
|
|
141
|
+
@workflows.workflow.entrypoint
|
|
142
|
+
async def run(self, repo: str) -> str:
|
|
143
|
+
agent = Agent(
|
|
144
|
+
name="github-pr-lister",
|
|
145
|
+
model="mistral-medium-latest",
|
|
146
|
+
instructions=f"List recent pull requests on {repo}.",
|
|
147
|
+
connectors=[github], # connector tools available to the agent
|
|
148
|
+
)
|
|
149
|
+
outputs = await Runner.run(agent=agent, inputs=f"Summarize PRs on {repo}.")
|
|
150
|
+
return await extract_text(outputs)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
OAuth and credential resolution still happen automatically.
|
|
154
|
+
|
|
155
|
+
## Running a connector workflow
|
|
156
|
+
|
|
157
|
+
You usually don't run these by hand, they're triggered from Studio/the UI (which shows the OAuth prompt) or by whatever starts the workflow. To trigger one **from Python** when it may need OAuth, use `execute_with_connector_auth_async` (`mistralai.extra.workflows`): it surfaces the auth URL via an `on_auth_required(state)` callback and resumes automatically once the user authorizes. For plain execution without OAuth, see the [Python SDK](../getting-started/python-sdk.mdx) guide.
|
|
158
|
+
|
|
159
|
+
## Common errors
|
|
160
|
+
|
|
161
|
+
| Symptom | Cause / fix |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| `RuntimeError: No workflow context available` | `ToolCallClient` used outside an activity. Inject via `Depends` and call tools inside an `@activity`. |
|
|
164
|
+
| Credentials never resolve / `call_tool` fails | Missing `@uses_connectors`, or the connector `name` doesn't match a configured connector (check `client.beta.connectors.list_async()`). |
|
|
165
|
+
| `ConnectorAuthTimeout` | OAuth not completed within 10 minutes. Re-run and authorize promptly. |
|
|
166
|
+
| `ConnectorToolCallError` | The connector tool rejected the call — check `tool_name` and the `arguments` schema. |
|
|
167
|
+
| `ImportError` for connectors | Install the extra: `uv add 'mistralai-workflows[mistralai]'`; import from `mistralai.workflows.plugins.mistralai.connectors`. |
|
|
168
|
+
|
|
169
|
+
## Quick reference
|
|
170
|
+
|
|
171
|
+
| Symbol | Import from | Use |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| `connector(name, *, auto_auth, credentials_name, allow_mcp_ui)` | `mistralai.workflows.plugins.mistralai.connectors` | Declare a connector slot. |
|
|
174
|
+
| `uses_connectors(*slots)` | same | Class decorator; attach slots (after `@workflow.define`). |
|
|
175
|
+
| `ToolCallClient` / `.call_tool(tool_name, arguments)` | same | Call connector tools from an activity (inject via `Depends`). |
|
|
176
|
+
| `Agent(..., connectors=[...])` | `mistralai.workflows.plugins.mistralai` | Give a durable agent connector tools. |
|
|
177
|
+
| `execute_with_connector_auth_async(...)` | `mistralai.extra.workflows` | Run a connector workflow from Python, handling OAuth (trigger-side, not needed to build a workflow). |
|
|
178
|
+
| `client.beta.connectors.list_async()` / `list_tools_async(connector_id_or_name=)` | `mistralai` client | Discover configured connectors and their tool names/schemas. |
|
|
@@ -144,6 +144,17 @@ if os.getenv("ENV") == "prod":
|
|
|
144
144
|
|
|
145
145
|
must be performed in activities. While activities themselves can produce different outputs for the same inputs (as they might contain non-deterministic operations), the workflow code must behave deterministically based on the activity results it receives.
|
|
146
146
|
|
|
147
|
+
### Imports must be deterministic too
|
|
148
|
+
|
|
149
|
+
The sandbox also validates **imports**. A module-level `import` of a non-deterministic library (`httpx`, `requests`, `aiohttp`, the raw `mistralai` client, etc.) in a workflow file won't work in the sandbox — the worker rejects it while validating the workflow at startup, so the workflow never becomes runnable. Keep such imports **inside the activity** that uses them, or — when a top-level import is unavoidable (e.g. shared type hints) — wrap it so the sandbox lets it through:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
with workflows.workflow.unsafe.imports_passed_through():
|
|
153
|
+
import httpx
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Only pass through imports you know are side-effect-free (no import-time I/O or global-state mutation).
|
|
157
|
+
|
|
147
158
|
### Maximum Execution History
|
|
148
159
|
|
|
149
160
|
The execution history for each workflow is capped at 51,200 events or 50MB per workflow execution. This is a hard limit of the durable execution engine. In order to avoid hitting this limit, we recommend using `workflows.execute_activities_in_parallel`, that allows you to execute large amounts of activities in parallel, while having the lowest footprint on the execution history. [Learn more about concurrency patterns](concurrency).
|
|
@@ -28,7 +28,8 @@ When **a key is provided**, multiple different activities that use the same key
|
|
|
28
28
|
|
|
29
29
|
```python
|
|
30
30
|
import mistralai.workflows as workflows
|
|
31
|
-
from mistralai import Mistral
|
|
31
|
+
from mistralai.client import Mistral
|
|
32
|
+
from mistralai.client.models import ChatCompletionRequestMessage
|
|
32
33
|
from mistralai.workflows import Depends
|
|
33
34
|
from pydantic import BaseModel
|
|
34
35
|
|
|
@@ -41,7 +42,7 @@ def get_mistral_client() -> Mistral:
|
|
|
41
42
|
|
|
42
43
|
class CompletionParams(BaseModel):
|
|
43
44
|
model: str
|
|
44
|
-
messages:
|
|
45
|
+
messages: list[ChatCompletionRequestMessage]
|
|
45
46
|
|
|
46
47
|
@workflows.activity(rate_limit=workflows.RateLimit(time_window_in_sec=1, max_calls=100))
|
|
47
48
|
async def generate_chat_response(
|
|
@@ -78,6 +78,72 @@ class SummarizeWorkflow:
|
|
|
78
78
|
return result.choices[0].message.content
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
**Example — structured output:** to get a validated Pydantic object back instead of a string, define a model and build the same `ChatCompletionRequest` as above, then swap the final call for `chat_parse_to_model`:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from pydantic import BaseModel
|
|
85
|
+
from mistralai.workflows.plugins.mistralai import chat_parse_to_model
|
|
86
|
+
|
|
87
|
+
class Sentiment(BaseModel):
|
|
88
|
+
label: str
|
|
89
|
+
score: float
|
|
90
|
+
|
|
91
|
+
# inside the entrypoint, with `request` built as in the chat example above:
|
|
92
|
+
sentiment = await chat_parse_to_model(Sentiment, request)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For the raw response instead of a parsed model, call `mistralai_chat_parse(request, response_format)` directly.
|
|
96
|
+
|
|
97
|
+
**Example — embeddings** (call from inside an entrypoint or activity):
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from mistralai.workflows.plugins.mistralai import (
|
|
101
|
+
MistralEmbeddingsParams,
|
|
102
|
+
mistralai_embeddings,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
response = await mistralai_embeddings(
|
|
106
|
+
MistralEmbeddingsParams(model="mistral-embed", inputs=["first text", "second text"]),
|
|
107
|
+
)
|
|
108
|
+
vectors = [item.embedding for item in response.data]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Example — OCR** extracts text from a document or image URL:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from mistralai.client.models import DocumentURLChunk
|
|
115
|
+
from mistralai.workflows.plugins.mistralai import OCRRequest, mistralai_ocr
|
|
116
|
+
|
|
117
|
+
result = await mistralai_ocr(
|
|
118
|
+
OCRRequest(
|
|
119
|
+
model="mistral-ocr-latest",
|
|
120
|
+
document=DocumentURLChunk(document_url="https://example.com/invoice.pdf"),
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
pages_markdown = [page.markdown for page in result.pages]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Raw client (when a plugin activity isn't enough):** call the Mistral API directly from inside an activity. Import the client from `mistralai.client` — `mistralai` is a namespace package, so `from mistralai import Mistral` does **not** work — and keep that import inside the activity function so the workflow sandbox doesn't reject it:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import os
|
|
130
|
+
from mistralai.workflows import activity
|
|
131
|
+
|
|
132
|
+
@activity()
|
|
133
|
+
async def summarize(text: str) -> str:
|
|
134
|
+
# Import inside the activity so the workflow sandbox doesn't reject it.
|
|
135
|
+
from mistralai.client import Mistral # NOT `from mistralai import Mistral`
|
|
136
|
+
|
|
137
|
+
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
|
|
138
|
+
resp = await client.chat.complete_async(
|
|
139
|
+
model="mistral-small-latest",
|
|
140
|
+
messages=[{"role": "user", "content": text}],
|
|
141
|
+
)
|
|
142
|
+
return resp.choices[0].message.content
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Model calls and other I/O must run inside an activity, never in the workflow body; see [Limitations](./limitations) for the determinism and import rules.
|
|
146
|
+
|
|
81
147
|
For full documentation on `Agent`, `Runner`, sessions, MCP, and multi-agent handoffs, see the [Durable Agents guide](./durable-agents).
|
|
82
148
|
|
|
83
149
|
### Webhook Plugin
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
# Workflow code runs inside a determinism sandbox and is replayed on
|
|
3
|
+
# recovery. Non-deterministic operations break replay. These rules
|
|
4
|
+
# flag non-deterministic stdlib calls inside `@workflow.define` classes
|
|
5
|
+
# (the workflow classes). Activities and plain helper classes are exempt.
|
|
6
|
+
|
|
7
|
+
- id: no-datetime-now-in-workflow
|
|
8
|
+
patterns:
|
|
9
|
+
- pattern-either:
|
|
10
|
+
- pattern: datetime.now(...)
|
|
11
|
+
- pattern: datetime.datetime.now(...)
|
|
12
|
+
- pattern: datetime.utcnow(...)
|
|
13
|
+
- pattern: datetime.datetime.utcnow(...)
|
|
14
|
+
- pattern-either:
|
|
15
|
+
- pattern-inside: |
|
|
16
|
+
@workflow.define(...)
|
|
17
|
+
class $CLASS:
|
|
18
|
+
...
|
|
19
|
+
- pattern-inside: |
|
|
20
|
+
@workflows.workflow.define(...)
|
|
21
|
+
class $CLASS:
|
|
22
|
+
...
|
|
23
|
+
message: >-
|
|
24
|
+
Use `workflow.now()` instead of `datetime.now()` in workflow code.
|
|
25
|
+
Workflows must be deterministic — `datetime.now()` returns
|
|
26
|
+
different values on replay, breaking the execution history.
|
|
27
|
+
severity: ERROR
|
|
28
|
+
languages: [python]
|
|
29
|
+
metadata:
|
|
30
|
+
category: determinism
|
|
31
|
+
fix: Replace with `workflow.now()` from `mistralai.workflows`
|
|
32
|
+
|
|
33
|
+
- id: no-uuid4-in-workflow
|
|
34
|
+
patterns:
|
|
35
|
+
- pattern-either:
|
|
36
|
+
- pattern: uuid.uuid4(...)
|
|
37
|
+
- pattern: uuid4(...)
|
|
38
|
+
- pattern-either:
|
|
39
|
+
- pattern-inside: |
|
|
40
|
+
@workflow.define(...)
|
|
41
|
+
class $CLASS:
|
|
42
|
+
...
|
|
43
|
+
- pattern-inside: |
|
|
44
|
+
@workflows.workflow.define(...)
|
|
45
|
+
class $CLASS:
|
|
46
|
+
...
|
|
47
|
+
message: >-
|
|
48
|
+
Use `workflow.uuid4()` instead of `uuid.uuid4()` in workflow code.
|
|
49
|
+
Standard UUID generation is non-deterministic and breaks replay.
|
|
50
|
+
severity: ERROR
|
|
51
|
+
languages: [python]
|
|
52
|
+
metadata:
|
|
53
|
+
category: determinism
|
|
54
|
+
fix: Replace with `workflow.uuid4()` from `mistralai.workflows`
|
|
55
|
+
|
|
56
|
+
- id: no-random-in-workflow
|
|
57
|
+
patterns:
|
|
58
|
+
- pattern-either:
|
|
59
|
+
- pattern: random.random(...)
|
|
60
|
+
- pattern: random.randint(...)
|
|
61
|
+
- pattern: random.choice(...)
|
|
62
|
+
- pattern: random.shuffle(...)
|
|
63
|
+
- pattern: random.sample(...)
|
|
64
|
+
- pattern: random.uniform(...)
|
|
65
|
+
- pattern: random.randrange(...)
|
|
66
|
+
- pattern: random.gauss(...)
|
|
67
|
+
- pattern-either:
|
|
68
|
+
- pattern-inside: |
|
|
69
|
+
@workflow.define(...)
|
|
70
|
+
class $CLASS:
|
|
71
|
+
...
|
|
72
|
+
- pattern-inside: |
|
|
73
|
+
@workflows.workflow.define(...)
|
|
74
|
+
class $CLASS:
|
|
75
|
+
...
|
|
76
|
+
message: >-
|
|
77
|
+
Use `workflow.random()` instead of the `random` module in workflow
|
|
78
|
+
code. Standard random functions are non-deterministic and break
|
|
79
|
+
replay.
|
|
80
|
+
severity: ERROR
|
|
81
|
+
languages: [python]
|
|
82
|
+
metadata:
|
|
83
|
+
category: determinism
|
|
84
|
+
fix: Replace with `workflow.random()` from `mistralai.workflows`
|
|
85
|
+
|
|
86
|
+
- id: no-os-env-in-workflow
|
|
87
|
+
patterns:
|
|
88
|
+
- pattern-either:
|
|
89
|
+
- pattern: os.environ
|
|
90
|
+
- pattern: os.environ[...]
|
|
91
|
+
- pattern: os.environ.get(...)
|
|
92
|
+
- pattern: os.getenv(...)
|
|
93
|
+
- pattern: os.getcwd(...)
|
|
94
|
+
- pattern: os.listdir(...)
|
|
95
|
+
- pattern: os.path.exists(...)
|
|
96
|
+
- pattern-either:
|
|
97
|
+
- pattern-inside: |
|
|
98
|
+
@workflow.define(...)
|
|
99
|
+
class $CLASS:
|
|
100
|
+
...
|
|
101
|
+
- pattern-inside: |
|
|
102
|
+
@workflows.workflow.define(...)
|
|
103
|
+
class $CLASS:
|
|
104
|
+
...
|
|
105
|
+
message: >-
|
|
106
|
+
Do not access `os.environ`, `os.getenv()`, `os.getcwd()`, or
|
|
107
|
+
filesystem functions in workflow code. Environment variables and
|
|
108
|
+
filesystem state can differ between replays. Read config in an
|
|
109
|
+
activity and pass it as workflow input, or use dependency injection.
|
|
110
|
+
severity: ERROR
|
|
111
|
+
languages: [python]
|
|
112
|
+
metadata:
|
|
113
|
+
category: determinism
|
|
114
|
+
fix: Move to an activity or use dependency injection (Depends)
|
|
115
|
+
|
|
116
|
+
- id: no-time-in-workflow
|
|
117
|
+
patterns:
|
|
118
|
+
- pattern-either:
|
|
119
|
+
- pattern: time.time(...)
|
|
120
|
+
- pattern: time.monotonic(...)
|
|
121
|
+
- pattern: time.sleep(...)
|
|
122
|
+
- pattern-either:
|
|
123
|
+
- pattern-inside: |
|
|
124
|
+
@workflow.define(...)
|
|
125
|
+
class $CLASS:
|
|
126
|
+
...
|
|
127
|
+
- pattern-inside: |
|
|
128
|
+
@workflows.workflow.define(...)
|
|
129
|
+
class $CLASS:
|
|
130
|
+
...
|
|
131
|
+
message: >-
|
|
132
|
+
Do not use `time.time()`, `time.monotonic()`, or `time.sleep()`
|
|
133
|
+
in workflow code. Use `workflow.now()` for timestamps and
|
|
134
|
+
`asyncio.sleep()` for delays (which is durable and survives
|
|
135
|
+
replays).
|
|
136
|
+
severity: ERROR
|
|
137
|
+
languages: [python]
|
|
138
|
+
metadata:
|
|
139
|
+
category: determinism
|
|
140
|
+
fix: Use `workflow.now()` for time, `asyncio.sleep()` for delays
|
|
141
|
+
|
|
142
|
+
- id: no-hashlib-in-workflow
|
|
143
|
+
patterns:
|
|
144
|
+
- pattern-either:
|
|
145
|
+
- pattern: hashlib.sha256(...)
|
|
146
|
+
- pattern: hashlib.sha512(...)
|
|
147
|
+
- pattern: hashlib.md5(...)
|
|
148
|
+
- pattern: hashlib.sha1(...)
|
|
149
|
+
- pattern: hashlib.new(...)
|
|
150
|
+
- pattern: hashlib.pbkdf2_hmac(...)
|
|
151
|
+
- pattern-either:
|
|
152
|
+
- pattern-inside: |
|
|
153
|
+
@workflow.define(...)
|
|
154
|
+
class $CLASS:
|
|
155
|
+
...
|
|
156
|
+
- pattern-inside: |
|
|
157
|
+
@workflows.workflow.define(...)
|
|
158
|
+
class $CLASS:
|
|
159
|
+
...
|
|
160
|
+
message: >-
|
|
161
|
+
Cryptographic operations are CPU-intensive and can exceed the
|
|
162
|
+
2-second limit between activities. Move hashing to an activity:
|
|
163
|
+
@activity()
|
|
164
|
+
async def hash_data(data: bytes) -> str:
|
|
165
|
+
return hashlib.sha256(data).hexdigest()
|
|
166
|
+
severity: WARNING
|
|
167
|
+
languages: [python]
|
|
168
|
+
metadata:
|
|
169
|
+
category: determinism
|
|
170
|
+
fix: Move cryptographic operations to an activity
|
|
171
|
+
|
|
172
|
+
- id: no-global-in-workflow
|
|
173
|
+
patterns:
|
|
174
|
+
- pattern: global $VAR
|
|
175
|
+
- pattern-either:
|
|
176
|
+
- pattern-inside: |
|
|
177
|
+
@workflow.define(...)
|
|
178
|
+
class $CLASS:
|
|
179
|
+
...
|
|
180
|
+
- pattern-inside: |
|
|
181
|
+
@workflows.workflow.define(...)
|
|
182
|
+
class $CLASS:
|
|
183
|
+
...
|
|
184
|
+
message: >-
|
|
185
|
+
Do not use `global` in workflow code. Modifying global variables
|
|
186
|
+
is non-deterministic — the global state may differ between
|
|
187
|
+
replays. Store state on `self` or pass it through activities.
|
|
188
|
+
severity: ERROR
|
|
189
|
+
languages: [python]
|
|
190
|
+
metadata:
|
|
191
|
+
category: determinism
|
|
192
|
+
fix: Use `self.field` for workflow state, or pass data via activities
|
|
193
|
+
|
|
194
|
+
- id: no-model-validate-in-workflow
|
|
195
|
+
patterns:
|
|
196
|
+
- pattern: $MODEL.model_validate(...)
|
|
197
|
+
- pattern-either:
|
|
198
|
+
- pattern-inside: |
|
|
199
|
+
@workflow.define(...)
|
|
200
|
+
class $CLASS:
|
|
201
|
+
...
|
|
202
|
+
- pattern-inside: |
|
|
203
|
+
@workflows.workflow.define(...)
|
|
204
|
+
class $CLASS:
|
|
205
|
+
...
|
|
206
|
+
message: >-
|
|
207
|
+
Pydantic `.model_validate()` can be CPU-intensive on large
|
|
208
|
+
payloads and may exceed the 2-second limit between activities.
|
|
209
|
+
Move heavy validation to an activity:
|
|
210
|
+
@activity()
|
|
211
|
+
async def validate(data: dict) -> MyModel:
|
|
212
|
+
return MyModel.model_validate(data)
|
|
213
|
+
severity: WARNING
|
|
214
|
+
languages: [python]
|
|
215
|
+
metadata:
|
|
216
|
+
category: determinism
|
|
217
|
+
fix: Move validation to an activity
|