mistralai-workflows-cli 1.2.2__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.2 → mistralai_workflows_cli-1.2.3}/Makefile +6 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/PKG-INFO +1 -1
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/SKILL.md +12 -3
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/scripts/test_workflow.py +62 -19
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/Makefile +22 -1
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/pyproject.toml.jinja +1 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/.gitignore +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/README.md +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/pyproject.toml +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/copier.yml +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.agents/skills/workflows/references/guides/connectors.mdx +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.env.jinja +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/.gitignore +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/README.md.jinja +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/dev.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/start.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/entrypoints/worker.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/activities.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/models.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/cargo_release/workflow.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/activities.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/models.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/code_modernization/workflow.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/activities.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/models.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/insurance_claims/workflow.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/linear_activities.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/summary_activity.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/linear_summarization/workflow.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/examples/worker.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/worker.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/workflows/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/src/workflows/hello.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_template/template/worker.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/_version.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/main.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/src/mistral_workflows_cli/setup.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/tests/__init__.py +0 -0
- {mistralai_workflows_cli-1.2.2 → mistralai_workflows_cli-1.2.3}/tests/test_setup.py +0 -0
- {mistralai_workflows_cli-1.2.2 → 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"
|
|
@@ -76,8 +76,8 @@ The documentation is organized into several categories:
|
|
|
76
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
|
|
77
77
|
- **[Conversational Workflows](references/guides/assist-workflows.mdx)**: InteractiveWorkflow, HITL, ChatInput/FormInput, Canvas editing, Rich UI components, Tool UI states
|
|
78
78
|
- **[Local Execution](references/guides/local-execution.mdx)**: No-infra dev mode with Pydantic model params
|
|
79
|
-
- **[Limitations](references/guides/limitations.mdx)**: System constraints and limits
|
|
80
|
-
- **[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
|
|
81
81
|
- **[Deployment Patterns](references/guides/_deployment-patterns.mdx)**: Best practices for deploying workflows
|
|
82
82
|
- **[Migration v2 to v3](references/guides/migration-v2-to-v3.mdx)**: Breaking changes and upgrade steps from SDK v2 through v3.4.0
|
|
83
83
|
|
|
@@ -101,6 +101,16 @@ python .agents/skills/workflows/scripts/test_workflow.py <workflow_file> --input
|
|
|
101
101
|
|
|
102
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.
|
|
103
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
|
+
|
|
104
114
|
### Internal References
|
|
105
115
|
|
|
106
116
|
These are additional patterns and utilities not covered in the official docs:
|
|
@@ -139,4 +149,3 @@ Use this skill when you need to:
|
|
|
139
149
|
- **Durable agents**: AI agents with MCP support, multi-agent handoffs, and persistent state
|
|
140
150
|
- **Connectors**: Call external services with platform-managed credentials avoiding secrets in the workflow code
|
|
141
151
|
- **Scalability**: Designed to handle complex, distributed applications
|
|
142
|
-
|
|
@@ -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
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: no-temporalio-import
|
|
3
|
+
pattern-either:
|
|
4
|
+
- pattern: import temporalio
|
|
5
|
+
- pattern: import temporalio.$MOD
|
|
6
|
+
- pattern: from temporalio import $X
|
|
7
|
+
- pattern: from temporalio.$MOD import $X
|
|
8
|
+
message: >-
|
|
9
|
+
Do not import `temporalio` directly. The workflows SDK re-exports
|
|
10
|
+
everything you need: `workflow.now()`, `workflow.uuid4()`,
|
|
11
|
+
`workflow.random()`, `workflow.wait_condition()`,
|
|
12
|
+
`workflow.continue_as_new()`, `WorkflowError`, `ActivityError`,
|
|
13
|
+
`ParentClosePolicy`, etc.
|
|
14
|
+
Use `from mistralai.workflows import ...` instead.
|
|
15
|
+
severity: ERROR
|
|
16
|
+
languages: [python]
|
|
17
|
+
metadata:
|
|
18
|
+
category: import
|
|
19
|
+
fix: Replace with the equivalent import from `mistralai.workflows`
|
|
20
|
+
|
|
21
|
+
- id: top-level-httpx-import
|
|
22
|
+
patterns:
|
|
23
|
+
- pattern: import httpx
|
|
24
|
+
- pattern-not-inside: |
|
|
25
|
+
def $F(...):
|
|
26
|
+
...
|
|
27
|
+
- pattern-not-inside: |
|
|
28
|
+
async def $F(...):
|
|
29
|
+
...
|
|
30
|
+
message: >-
|
|
31
|
+
Avoid top-level `import httpx`. The determinism sandbox re-imports
|
|
32
|
+
modules, and top-level imports with side effects can cause issues.
|
|
33
|
+
Import httpx inside your activity function instead:
|
|
34
|
+
@activity()
|
|
35
|
+
async def call_api(url: str) -> dict:
|
|
36
|
+
import httpx
|
|
37
|
+
async with httpx.AsyncClient() as client: ...
|
|
38
|
+
severity: WARNING
|
|
39
|
+
languages: [python]
|
|
40
|
+
metadata:
|
|
41
|
+
category: import
|
|
42
|
+
fix: Move `import httpx` inside the activity function body
|
|
43
|
+
|
|
44
|
+
- id: top-level-requests-import
|
|
45
|
+
patterns:
|
|
46
|
+
- pattern-either:
|
|
47
|
+
- pattern: import requests
|
|
48
|
+
- pattern: from requests import $X
|
|
49
|
+
- pattern-not-inside: |
|
|
50
|
+
def $F(...):
|
|
51
|
+
...
|
|
52
|
+
- pattern-not-inside: |
|
|
53
|
+
async def $F(...):
|
|
54
|
+
...
|
|
55
|
+
message: >-
|
|
56
|
+
Do not use the `requests` library. It is synchronous and blocks
|
|
57
|
+
the event loop. Use `httpx.AsyncClient` instead, imported inside
|
|
58
|
+
your activity function.
|
|
59
|
+
severity: WARNING
|
|
60
|
+
languages: [python]
|
|
61
|
+
metadata:
|
|
62
|
+
category: import
|
|
63
|
+
fix: Remove `requests` and use `httpx.AsyncClient` in activities
|
|
64
|
+
|
|
65
|
+
- id: top-level-aiohttp-import
|
|
66
|
+
patterns:
|
|
67
|
+
- pattern-either:
|
|
68
|
+
- pattern: import aiohttp
|
|
69
|
+
- pattern: from aiohttp import $X
|
|
70
|
+
- pattern-not-inside: |
|
|
71
|
+
def $F(...):
|
|
72
|
+
...
|
|
73
|
+
- pattern-not-inside: |
|
|
74
|
+
async def $F(...):
|
|
75
|
+
...
|
|
76
|
+
message: >-
|
|
77
|
+
Avoid top-level `import aiohttp`. Import it inside your activity
|
|
78
|
+
function to avoid sandbox re-import issues.
|
|
79
|
+
severity: WARNING
|
|
80
|
+
languages: [python]
|
|
81
|
+
metadata:
|
|
82
|
+
category: import
|
|
83
|
+
fix: Move the import inside the activity function body
|
|
84
|
+
|
|
85
|
+
- id: top-level-mistral-client-import
|
|
86
|
+
patterns:
|
|
87
|
+
- pattern-either:
|
|
88
|
+
- pattern: import mistralai.client
|
|
89
|
+
- pattern: import mistralai.client.$SUB
|
|
90
|
+
- pattern: from mistralai.client import $X
|
|
91
|
+
- pattern: from mistralai.client.$SUB import $X
|
|
92
|
+
- pattern-not-inside: |
|
|
93
|
+
def $F(...):
|
|
94
|
+
...
|
|
95
|
+
- pattern-not-inside: |
|
|
96
|
+
async def $F(...):
|
|
97
|
+
...
|
|
98
|
+
- pattern-not-inside: |
|
|
99
|
+
with workflows.workflow.unsafe.imports_passed_through():
|
|
100
|
+
...
|
|
101
|
+
- pattern-not-inside: |
|
|
102
|
+
with workflow.unsafe.imports_passed_through():
|
|
103
|
+
...
|
|
104
|
+
message: >-
|
|
105
|
+
Avoid a top-level import from `mistralai.client` (the raw SDK). It pulls in
|
|
106
|
+
`httpx`, which the determinism sandbox rejects when it re-imports the workflow
|
|
107
|
+
module — the worker then fails to validate the workflow at startup. Prefer the
|
|
108
|
+
Mistral AI plugin activities (`mistralai_chat_complete`, `mistralai_ocr`, ...);
|
|
109
|
+
if you genuinely need the raw client, import it inside the activity that uses it.
|
|
110
|
+
severity: ERROR
|
|
111
|
+
languages: [python]
|
|
112
|
+
metadata:
|
|
113
|
+
category: import
|
|
114
|
+
fix: Use a plugin activity, or import `mistralai.client` inside the activity body
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
# All I/O must be performed in activities. Workflow code may be
|
|
3
|
+
# replayed, and I/O operations would violate determinism or cause
|
|
4
|
+
# duplicate side effects.
|
|
5
|
+
|
|
6
|
+
- id: no-file-io-in-workflow
|
|
7
|
+
patterns:
|
|
8
|
+
- pattern-either:
|
|
9
|
+
- pattern: open(...)
|
|
10
|
+
- pattern: aiofiles.open(...)
|
|
11
|
+
- pattern-either:
|
|
12
|
+
- pattern-inside: |
|
|
13
|
+
@workflow.define(...)
|
|
14
|
+
class $CLASS:
|
|
15
|
+
...
|
|
16
|
+
- pattern-inside: |
|
|
17
|
+
@workflows.workflow.define(...)
|
|
18
|
+
class $CLASS:
|
|
19
|
+
...
|
|
20
|
+
message: >-
|
|
21
|
+
Do not perform file I/O in workflow code. Move file operations
|
|
22
|
+
to an activity:
|
|
23
|
+
@activity()
|
|
24
|
+
async def read_file(path: str) -> str:
|
|
25
|
+
async with aiofiles.open(path) as f:
|
|
26
|
+
return await f.read()
|
|
27
|
+
severity: ERROR
|
|
28
|
+
languages: [python]
|
|
29
|
+
metadata:
|
|
30
|
+
category: io
|
|
31
|
+
fix: Move file I/O to an activity function
|
|
32
|
+
|
|
33
|
+
- id: no-http-client-in-workflow
|
|
34
|
+
patterns:
|
|
35
|
+
- pattern-either:
|
|
36
|
+
- pattern: httpx.AsyncClient(...)
|
|
37
|
+
- pattern: httpx.Client(...)
|
|
38
|
+
- pattern: aiohttp.ClientSession(...)
|
|
39
|
+
- pattern-either:
|
|
40
|
+
- pattern-inside: |
|
|
41
|
+
@workflow.define(...)
|
|
42
|
+
class $CLASS:
|
|
43
|
+
...
|
|
44
|
+
- pattern-inside: |
|
|
45
|
+
@workflows.workflow.define(...)
|
|
46
|
+
class $CLASS:
|
|
47
|
+
...
|
|
48
|
+
message: >-
|
|
49
|
+
Do not make HTTP calls in workflow code. Move all HTTP/API calls
|
|
50
|
+
to an activity:
|
|
51
|
+
@activity()
|
|
52
|
+
async def call_api(url: str) -> dict:
|
|
53
|
+
async with httpx.AsyncClient() as client:
|
|
54
|
+
resp = await client.get(url)
|
|
55
|
+
return resp.json()
|
|
56
|
+
severity: ERROR
|
|
57
|
+
languages: [python]
|
|
58
|
+
metadata:
|
|
59
|
+
category: io
|
|
60
|
+
fix: Move HTTP calls to an activity function
|
|
61
|
+
|
|
62
|
+
- id: no-db-in-workflow
|
|
63
|
+
patterns:
|
|
64
|
+
- pattern-either:
|
|
65
|
+
- pattern: await $CONN.execute(...)
|
|
66
|
+
- pattern: await $CONN.fetch(...)
|
|
67
|
+
- pattern: await $CONN.fetchone(...)
|
|
68
|
+
- pattern: await $CONN.fetchall(...)
|
|
69
|
+
- pattern: await $CONN.fetchrow(...)
|
|
70
|
+
- pattern: await $CONN.query(...)
|
|
71
|
+
- pattern: await $CONN.commit(...)
|
|
72
|
+
- pattern: $CONN.commit(...)
|
|
73
|
+
- pattern-either:
|
|
74
|
+
- pattern-inside: |
|
|
75
|
+
@workflow.define(...)
|
|
76
|
+
class $CLASS:
|
|
77
|
+
...
|
|
78
|
+
- pattern-inside: |
|
|
79
|
+
@workflows.workflow.define(...)
|
|
80
|
+
class $CLASS:
|
|
81
|
+
...
|
|
82
|
+
message: >-
|
|
83
|
+
Do not perform database operations in workflow code. Move all
|
|
84
|
+
database queries to an activity. Use dependency injection
|
|
85
|
+
(Depends) to provide the connection:
|
|
86
|
+
@activity()
|
|
87
|
+
async def query_db(
|
|
88
|
+
q: str,
|
|
89
|
+
conn: Connection = Depends(get_connection),
|
|
90
|
+
) -> list[dict]:
|
|
91
|
+
return await conn.fetch(q)
|
|
92
|
+
severity: ERROR
|
|
93
|
+
languages: [python]
|
|
94
|
+
metadata:
|
|
95
|
+
category: io
|
|
96
|
+
fix: Move database operations to an activity function
|
|
97
|
+
|
|
98
|
+
- id: no-sync-requests-library
|
|
99
|
+
pattern-either:
|
|
100
|
+
- pattern: requests.get(...)
|
|
101
|
+
- pattern: requests.post(...)
|
|
102
|
+
- pattern: requests.put(...)
|
|
103
|
+
- pattern: requests.delete(...)
|
|
104
|
+
- pattern: requests.patch(...)
|
|
105
|
+
- pattern: requests.head(...)
|
|
106
|
+
- pattern: requests.request(...)
|
|
107
|
+
message: >-
|
|
108
|
+
The `requests` library is synchronous and blocks the event loop.
|
|
109
|
+
Use `httpx.AsyncClient` instead:
|
|
110
|
+
async with httpx.AsyncClient() as client:
|
|
111
|
+
response = await client.get(url)
|
|
112
|
+
severity: ERROR
|
|
113
|
+
languages: [python]
|
|
114
|
+
metadata:
|
|
115
|
+
category: async
|
|
116
|
+
fix: Replace `requests` with `httpx.AsyncClient`
|