motia 0.7.2-beta.135-338519 → 0.7.2-beta.135-994416

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.
Files changed (66) hide show
  1. package/dist/cjs/create/templates/nodejs/motia-workbench.json +5 -5
  2. package/dist/cjs/create/templates/nodejs/tutorial.tsx.txt +40 -8
  3. package/dist/cjs/create/templates/python/motia-workbench.json +4 -4
  4. package/dist/{esm/create/templates/python/steps → cjs/create/templates/python/steps/petstore}/api_step.py.txt +2 -2
  5. package/dist/cjs/create/templates/python/steps/{process_food_order_step.py.txt → petstore/process_food_order_step.py.txt} +1 -1
  6. package/dist/cjs/create/templates/python/tutorial.tsx.txt +41 -9
  7. package/dist/cjs/cursor-rules/dot-files/.cursor/index.mdc +16 -8
  8. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +112 -4
  9. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +30 -3
  10. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +67 -6
  11. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +103 -8
  12. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +151 -2
  13. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +63 -0
  14. package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +83 -4
  15. package/dist/cjs/dev.js +0 -2
  16. package/dist/cjs/generate-locked-data.js +1 -1
  17. package/dist/cjs/generate-types.js +2 -2
  18. package/dist/cjs/start.js +0 -2
  19. package/dist/cjs/watcher.js +4 -4
  20. package/dist/esm/create/templates/nodejs/motia-workbench.json +5 -5
  21. package/dist/esm/create/templates/nodejs/tutorial.tsx.txt +40 -8
  22. package/dist/esm/create/templates/python/motia-workbench.json +4 -4
  23. package/dist/esm/create/templates/python/src/__init__.py.txt +0 -0
  24. package/dist/esm/create/templates/python/src/services/__init__.py.txt +0 -0
  25. package/dist/{cjs/create/templates/python/steps → esm/create/templates/python/steps/petstore}/api_step.py.txt +2 -2
  26. package/dist/esm/create/templates/python/steps/{process_food_order_step.py.txt → petstore/process_food_order_step.py.txt} +1 -1
  27. package/dist/esm/create/templates/python/tutorial.tsx.txt +41 -9
  28. package/dist/esm/cursor-rules/dot-files/.cursor/index.mdc +16 -8
  29. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +112 -4
  30. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +30 -3
  31. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +67 -6
  32. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +103 -8
  33. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +151 -2
  34. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +63 -0
  35. package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +83 -4
  36. package/dist/esm/dev.js +0 -2
  37. package/dist/esm/generate-locked-data.js +1 -1
  38. package/dist/esm/generate-types.js +2 -2
  39. package/dist/esm/start.js +0 -2
  40. package/dist/esm/watcher.js +4 -4
  41. package/package.json +4 -4
  42. package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
  43. package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
  44. package/dist/cjs/dev/state-endpoints.d.ts +0 -2
  45. package/dist/cjs/dev/state-endpoints.js +0 -30
  46. package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
  47. package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
  48. package/dist/esm/dev/state-endpoints.d.ts +0 -2
  49. package/dist/esm/dev/state-endpoints.js +0 -26
  50. package/dist/types/dev/state-endpoints.d.ts +0 -2
  51. /package/dist/cjs/create/templates/python/{steps/services → src}/__init__.py.txt +0 -0
  52. /package/dist/{esm/create/templates/python/steps → cjs/create/templates/python/src}/services/__init__.py.txt +0 -0
  53. /package/dist/cjs/create/templates/python/{steps → src}/services/pet_store.py.txt +0 -0
  54. /package/dist/cjs/create/templates/python/{steps → src}/services/types.py.txt +0 -0
  55. /package/dist/cjs/create/templates/python/steps/{api_step.py-features.json.txt → petstore/api_step.py-features.json.txt} +0 -0
  56. /package/dist/cjs/create/templates/python/steps/{notification_step.py.txt → petstore/notification_step.py.txt} +0 -0
  57. /package/dist/cjs/create/templates/python/steps/{process_food_order_step.py-features.json.txt → petstore/process_food_order_step.py-features.json.txt} +0 -0
  58. /package/dist/cjs/create/templates/python/steps/{state_audit_cron_step.py-features.json.txt → petstore/state_audit_cron_step.py-features.json.txt} +0 -0
  59. /package/dist/cjs/create/templates/python/steps/{state_audit_cron_step.py.txt → petstore/state_audit_cron_step.py.txt} +0 -0
  60. /package/dist/esm/create/templates/python/{steps → src}/services/pet_store.py.txt +0 -0
  61. /package/dist/esm/create/templates/python/{steps → src}/services/types.py.txt +0 -0
  62. /package/dist/esm/create/templates/python/steps/{api_step.py-features.json.txt → petstore/api_step.py-features.json.txt} +0 -0
  63. /package/dist/esm/create/templates/python/steps/{notification_step.py.txt → petstore/notification_step.py.txt} +0 -0
  64. /package/dist/esm/create/templates/python/steps/{process_food_order_step.py-features.json.txt → petstore/process_food_order_step.py-features.json.txt} +0 -0
  65. /package/dist/esm/create/templates/python/steps/{state_audit_cron_step.py-features.json.txt → petstore/state_audit_cron_step.py-features.json.txt} +0 -0
  66. /package/dist/esm/create/templates/python/steps/{state_audit_cron_step.py.txt → petstore/state_audit_cron_step.py.txt} +0 -0
@@ -2,27 +2,27 @@
2
2
  {
3
3
  "id": "basic-tutorial",
4
4
  "config": {
5
- "steps/state-audit-cron.step.ts": {
5
+ "steps/petstore/state-audit-cron.step.ts": {
6
6
  "x": -165,
7
7
  "y": 217,
8
8
  "sourceHandlePosition": "right"
9
9
  },
10
- "steps/process-food-order.step.ts": {
10
+ "steps/petstore/process-food-order.step.ts": {
11
11
  "x": 211,
12
12
  "y": 17,
13
13
  "sourceHandlePosition": "bottom",
14
14
  "targetHandlePosition": "left"
15
15
  },
16
- "steps/api.step.ts": {
16
+ "steps/petstore/api.step.ts": {
17
17
  "x": -100,
18
18
  "y": 3,
19
19
  "sourceHandlePosition": "right",
20
20
  "targetHandlePosition": "left"
21
21
  },
22
- "steps/notification.step.ts": {
22
+ "steps/petstore/notification.step.ts": {
23
23
  "x": 300,
24
24
  "y": 264
25
25
  }
26
26
  }
27
27
  }
28
- ]
28
+ ]
@@ -371,25 +371,35 @@ export const steps: TutorialStep[] = [
371
371
  before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
372
372
  },
373
373
  {
374
- elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
374
+ elementXpath: workbenchXPath.endpoints.endpointsList,
375
375
  title: 'Endpoints Tool',
376
376
  description: () => (
377
377
  <p>
378
378
  This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL
379
379
  path, and the description declared in the Step configuration.
380
- <br />
381
- <br />
382
- 💡 Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to
383
- use the endpoint and a tool to test the endpoint.
384
380
  </p>
385
381
  ),
386
382
  before: [{ type: 'click', selector: workbenchXPath.links.endpoints }],
387
383
  },
388
384
  {
389
- elementXpath: workbenchXPath.sidebarContainer,
390
- title: 'API Endpoint Docs',
385
+ elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
386
+ title: 'Endpoints Tool',
391
387
  description: () => (
392
388
  <p>
389
+ Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use
390
+ the endpoint and a tool to test the endpoint.
391
+ </p>
392
+ ),
393
+ },
394
+ {
395
+ elementXpath: workbenchXPath.endpoints.callPanel,
396
+ title: 'API Endpoint Playground',
397
+ description: () => (
398
+ <p>
399
+ Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
400
+ clicking on the <b>Play</b> button.
401
+ <br />
402
+ <br />
393
403
  This section will provide an overview of your API endpoint.
394
404
  <br />
395
405
  <br />
@@ -399,6 +409,25 @@ export const steps: TutorialStep[] = [
399
409
  ),
400
410
  before: [{ type: 'click', selector: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial') }],
401
411
  },
412
+ {
413
+ elementXpath: workbenchXPath.endpoints.specButton,
414
+ title: 'API Endpoint Specification',
415
+ description: () => (
416
+ <p>
417
+ Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
418
+ </p>
419
+ ),
420
+ },
421
+ {
422
+ elementXpath: workbenchXPath.sidebarContainer,
423
+ title: 'API Endpoint Specification',
424
+ description: () => (
425
+ <p>
426
+ This is what you see when clicking on the <b>Details</b> button.
427
+ </p>
428
+ ),
429
+ before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }],
430
+ },
402
431
  {
403
432
  elementXpath: workbenchXPath.endpoints.callPanel,
404
433
  title: 'API Endpoint Test',
@@ -428,7 +457,10 @@ export const steps: TutorialStep[] = [
428
457
  </pre>
429
458
  </p>
430
459
  ),
431
- before: [{ type: 'click', selector: workbenchXPath.endpoints.callTab }],
460
+ before: [
461
+ { type: 'click', selector: workbenchXPath.closePanelButton },
462
+ { type: 'click', selector: workbenchXPath.endpoints.bodyTab },
463
+ ],
432
464
  },
433
465
  {
434
466
  elementXpath: workbenchXPath.endpoints.playButton,
@@ -2,22 +2,22 @@
2
2
  {
3
3
  "id": "basic-tutorial",
4
4
  "config": {
5
- "steps/state_audit_cron_step.py": {
5
+ "steps/petstore/state_audit_cron_step.py": {
6
6
  "x": -38,
7
7
  "y": 683,
8
8
  "sourceHandlePosition": "right"
9
9
  },
10
- "steps/process_food_order_step.py": {
10
+ "steps/petstore/process_food_order_step.py": {
11
11
  "x": 384,
12
12
  "y": 476,
13
13
  "targetHandlePosition": "left"
14
14
  },
15
- "steps/notification_step.py": {
15
+ "steps/petstore/notification_step.py": {
16
16
  "x": 601,
17
17
  "y": 724,
18
18
  "targetHandlePosition": "left"
19
19
  },
20
- "steps/api_step.py": {
20
+ "steps/petstore/api_step.py": {
21
21
  "x": 15,
22
22
  "y": 461,
23
23
  "sourceHandlePosition": "right"
@@ -1,7 +1,7 @@
1
1
  from pydantic import BaseModel
2
2
  from typing import Optional
3
- from .services.pet_store import pet_store_service
4
- from .services.types import Pet
3
+ from src.services.pet_store import pet_store_service
4
+ from src.services.types import Pet
5
5
 
6
6
  class PetRequest(BaseModel):
7
7
  name: str
@@ -1,6 +1,6 @@
1
1
  from pydantic import BaseModel
2
2
  from datetime import datetime
3
- from .services.pet_store import pet_store_service
3
+ from src.services.pet_store import pet_store_service
4
4
 
5
5
  class InputSchema(BaseModel):
6
6
  id: str
@@ -360,7 +360,7 @@ export const steps: TutorialStep[] = [
360
360
  title: 'Endpoints',
361
361
  description: () => (
362
362
  <p>
363
- Now that we've looked at Motia's Step types, let's trigger the API Step from the <b>endpoints</b> section in
363
+ Now that we've looked at Motia primitives, let's trigger the API Step from the <b>endpoints</b> section in
364
364
  Workbench.
365
365
  <br />
366
366
  <br />
@@ -371,25 +371,35 @@ export const steps: TutorialStep[] = [
371
371
  before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
372
372
  },
373
373
  {
374
- elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
374
+ elementXpath: workbenchXPath.endpoints.endpointsList,
375
375
  title: 'Endpoints Tool',
376
376
  description: () => (
377
377
  <p>
378
378
  This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL
379
379
  path, and the description declared in the Step configuration.
380
- <br />
381
- <br />
382
- 💡 Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to
383
- use the endpoint and a tool to test the endpoint.
384
380
  </p>
385
381
  ),
386
382
  before: [{ type: 'click', selector: workbenchXPath.links.endpoints }],
387
383
  },
388
384
  {
389
- elementXpath: workbenchXPath.sidebarContainer,
390
- title: 'API Endpoint Docs',
385
+ elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
386
+ title: 'Endpoints Tool',
391
387
  description: () => (
392
388
  <p>
389
+ Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use
390
+ the endpoint and a tool to test the endpoint.
391
+ </p>
392
+ ),
393
+ },
394
+ {
395
+ elementXpath: workbenchXPath.endpoints.callPanel,
396
+ title: 'API Endpoint Playground',
397
+ description: () => (
398
+ <p>
399
+ Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
400
+ clicking on the <b>Play</b> button.
401
+ <br />
402
+ <br />
393
403
  This section will provide an overview of your API endpoint.
394
404
  <br />
395
405
  <br />
@@ -399,6 +409,25 @@ export const steps: TutorialStep[] = [
399
409
  ),
400
410
  before: [{ type: 'click', selector: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial') }],
401
411
  },
412
+ {
413
+ elementXpath: workbenchXPath.endpoints.specButton,
414
+ title: 'API Endpoint Specification',
415
+ description: () => (
416
+ <p>
417
+ Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
418
+ </p>
419
+ ),
420
+ },
421
+ {
422
+ elementXpath: workbenchXPath.sidebarContainer,
423
+ title: 'API Endpoint Specification',
424
+ description: () => (
425
+ <p>
426
+ This is what you see when clicking on the <b>Details</b> button.
427
+ </p>
428
+ ),
429
+ before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }],
430
+ },
402
431
  {
403
432
  elementXpath: workbenchXPath.endpoints.callPanel,
404
433
  title: 'API Endpoint Test',
@@ -428,7 +457,10 @@ export const steps: TutorialStep[] = [
428
457
  </pre>
429
458
  </p>
430
459
  ),
431
- before: [{ type: 'click', selector: workbenchXPath.endpoints.callTab }],
460
+ before: [
461
+ { type: 'click', selector: workbenchXPath.closePanelButton },
462
+ { type: 'click', selector: workbenchXPath.endpoints.bodyTab },
463
+ ],
432
464
  },
433
465
  {
434
466
  elementXpath: workbenchXPath.endpoints.playButton,
@@ -4,14 +4,6 @@ globs:
4
4
  alwaysApply: true
5
5
  ---
6
6
 
7
- ## Adding migration
8
-
9
- Make sure to use [migration guide](./architecture/database/database-migration.mdc) to create a new migration.
10
-
11
- ## Database integration
12
-
13
- Whenever database is used, please use [database guide](./architecture/database/database.mdc) to create a database connection and methods.
14
-
15
7
  ## Real time events
16
8
 
17
9
  Make sure to use [real time events guide](./rules/motia/realtime-streaming.mdc) to create a new real time event.
@@ -20,6 +12,22 @@ Make sure to use [real time events guide](./rules/motia/realtime-streaming.mdc)
20
12
 
21
13
  Make sure to use [state management guide](./rules/motia/state-management.mdc) to create a new state management.
22
14
 
15
+ ## Creating HTTP Endpoints
16
+
17
+ Make sure to use [API steps guide](./rules/motia/api-steps.mdc) to create new HTTP endpoints.
18
+
19
+ ## Background Tasks
20
+
21
+ Make sure to use [event steps guide](./rules/motia/event-steps.mdc) to create new background tasks.
22
+
23
+ ## Scheduled Tasks
24
+
25
+ Make sure to use [cron steps guide](./rules/motia/cron-steps.mdc) to create new scheduled tasks.
26
+
27
+ ## Virtual Steps & Flow Visualization
28
+
29
+ Make sure to use [virtual steps guide](./rules/motia/virtual-steps.mdc) when connecting nodes virtually and creating smooth flows in Workbench.
30
+
23
31
  ## Authentication
24
32
 
25
33
  If ever need to add authentication, make sure to use middleware to authenticate the request.
@@ -18,13 +18,16 @@ Steps need to be created in the `steps` folder, it can be in subfolders.
18
18
 
19
19
  Defining an API Step is done by two elements. Configuration and Handler.
20
20
 
21
- ### Zod Usage
21
+ ### Schema Definition
22
22
 
23
- Motia relies on Zod to define body and response schemas. In Python and other languages, it uses JSON Schema.
23
+ - **TypeScript/JavaScript**: Motia uses Zod schemas for automatic validation of request/response data
24
+ - **Python**: Motia uses JSON Schema format. You can optionally use Pydantic models to generate JSON Schemas and handle manual validation in your handlers
24
25
 
25
26
  ### Configuration
26
27
 
27
- You need to export a config constant via `export const config` that is a `ApiRouteConfig` type.
28
+ **TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `ApiRouteConfig` type.
29
+
30
+ **Python**: You need to define a `config` dictionary with the same properties as the TypeScript `ApiRouteConfig`.
28
31
 
29
32
  ```typescript
30
33
  export type Emit = string | {
@@ -189,13 +192,24 @@ export type ApiRouteHandler<
189
192
 
190
193
  ### Handler definition
191
194
 
195
+ **TypeScript/JavaScript:**
192
196
  ```typescript
193
197
  export const handler: Handlers['CreateResource'] = async (req, { emit, logger, state, streams }) => {
194
198
  // Implementation
195
199
  }
196
200
  ```
197
201
 
198
- ### Example
202
+ **Python:**
203
+ ```python
204
+ async def handler(req, context):
205
+ # req: dictionary containing pathParams, queryParams, body, headers
206
+ # context: object containing emit, logger, state, streams, trace_id
207
+ pass
208
+ ```
209
+
210
+ ### Examples
211
+
212
+ #### TypeScript Example
199
213
 
200
214
  ```typescript
201
215
  import { ApiRouteConfig, Handlers } from 'motia';
@@ -314,4 +328,98 @@ export const handler: Handlers['CreateResource'] = async (req, { emit, logger })
314
328
  };
315
329
  }
316
330
  };
331
+ ```
332
+
333
+ #### Python Example
334
+
335
+ ```python
336
+ from pydantic import BaseModel, Field
337
+ from typing import Optional, Dict, Any
338
+
339
+ class ResourceData(BaseModel):
340
+ title: str = Field(..., min_length=1, description="Title cannot be empty")
341
+ description: Optional[str] = None
342
+ category: str = Field(..., min_length=1, description="Category is required")
343
+ metadata: Optional[Dict[str, Any]] = None
344
+
345
+ class ResourceResponse(BaseModel):
346
+ id: str
347
+ title: str
348
+ category: str
349
+ status: str
350
+
351
+ class ErrorResponse(BaseModel):
352
+ error: str
353
+
354
+ config = {
355
+ "type": "api",
356
+ "name": "CreateResource",
357
+ "path": "/resources",
358
+ "method": "POST",
359
+ "emits": ["send-email"],
360
+ "flows": ["resource-management"],
361
+ "bodySchema": ResourceData.model_json_schema(),
362
+ "responseSchema": {
363
+ 201: ResourceResponse.model_json_schema(),
364
+ 400: ErrorResponse.model_json_schema()
365
+ }
366
+ }
367
+
368
+ async def handler(req, context):
369
+ try:
370
+ body = req.get("body", {})
371
+
372
+ # Optional: Validate input manually using Pydantic (Motia doesn't do this automatically)
373
+ resource_data = ResourceData(**body)
374
+
375
+ context.logger.info("Attempting to create resource", {
376
+ "title": resource_data.title,
377
+ "category": resource_data.category
378
+ })
379
+
380
+ # Process the resource creation
381
+ result = await service.create_resource({
382
+ "title": resource_data.title,
383
+ "description": resource_data.description,
384
+ "category": resource_data.category,
385
+ "metadata": resource_data.metadata
386
+ })
387
+
388
+ # Emit event to trigger other steps
389
+ await context.emit({
390
+ "topic": "send-email",
391
+ "data": {
392
+ "resource": result,
393
+ "user_id": "example-user"
394
+ }
395
+ })
396
+
397
+ context.logger.info("Resource created successfully", {
398
+ "resource_id": result.get("id"),
399
+ "title": result.get("title"),
400
+ "category": result.get("category")
401
+ })
402
+
403
+ return {
404
+ "status": 201,
405
+ "body": {
406
+ "id": result.get("id"),
407
+ "title": result.get("title"),
408
+ "category": result.get("category"),
409
+ "status": "active"
410
+ }
411
+ }
412
+
413
+ except ValidationError as e:
414
+ context.logger.error("Resource creation failed - Pydantic validation error", {"error": str(e)})
415
+ return {
416
+ "status": 400,
417
+ "body": {"error": "Validation failed"}
418
+ }
419
+ except Exception as e:
420
+ context.logger.error("Resource creation failed", {"error": str(e)})
421
+ return {
422
+ "status": 500,
423
+ "body": {"error": "Creation failed"}
424
+ }
317
425
  ```
@@ -26,7 +26,9 @@ Defining a CRON Step is done by two elements. Configuration and Handler.
26
26
 
27
27
  ### Configuration
28
28
 
29
- You need to export a config constant via `export const config` that is a `CronConfig` type.
29
+ **TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `CronConfig` type.
30
+
31
+ **Python**: You need to define a `config` dictionary with the same properties as the TypeScript `CronConfig`.
30
32
 
31
33
  ```typescript
32
34
  export type Emit = string | {
@@ -98,6 +100,7 @@ export type CronConfig = {
98
100
 
99
101
  The handler is a function that is exported via `export const handler` that is a `CronHandler` type.
100
102
 
103
+ **TypeScript/JavaScript:**
101
104
  ```typescript
102
105
  /**
103
106
  * CRON handler accepts only one argument, the FlowContext.
@@ -109,6 +112,13 @@ export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger, emit,
109
112
  }
110
113
  ```
111
114
 
115
+ **Python:**
116
+ ```python
117
+ async def handler(context):
118
+ # context: object containing emit, logger, state, streams, trace_id
119
+ context.logger.info("CRON Job Every 5 Minutes started")
120
+ ```
121
+
112
122
  ### Examples of Cron expressions
113
123
 
114
124
  - `0 0 * * *`: Runs daily at midnight
@@ -127,7 +137,9 @@ export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger, emit,
127
137
  - Collecting metrics from third-party services
128
138
  - Reconciling data from different sources
129
139
 
130
- ## Example
140
+ ## Examples
141
+
142
+ ### TypeScript Example
131
143
 
132
144
  ```typescript
133
145
  export const config: CronConfig = {
@@ -138,7 +150,22 @@ export const config: CronConfig = {
138
150
  flows: ['example-flow']
139
151
  };
140
152
 
141
- export const handler: Handlers['CronJobEvery5Minutes'] = async (_, { logger }) => {
153
+ export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger }) => {
142
154
  logger.info('Cron job started')
143
155
  }
156
+ ```
157
+
158
+ ### Python Example
159
+
160
+ ```python
161
+ config = {
162
+ "type": "cron",
163
+ "name": "CronJobEvery5Minutes",
164
+ "cron": "*/5 * * * *", # Run every 5 minutes
165
+ "emits": [], # No emits in this example
166
+ "flows": ["example-flow"]
167
+ }
168
+
169
+ async def handler(context):
170
+ context.logger.info("Cron job started")
144
171
  ```
@@ -28,13 +28,16 @@ Steps need to be created in the `steps` folder, it can be in subfolders.
28
28
 
29
29
  Defining an API Step is done by two elements. Configuration and Handler.
30
30
 
31
- ### Zod Usage
31
+ ### Schema Definition
32
32
 
33
- Motia relies on Zod to define body and response schemas. In Python and other languages, it uses JSON Schema.
33
+ - **TypeScript/JavaScript**: Motia uses Zod schemas for automatic validation of input data
34
+ - **Python**: Motia uses JSON Schema format. You can optionally use Pydantic models to generate JSON Schemas and handle manual validation in your handlers
34
35
 
35
36
  ### Configuration
36
37
 
37
- You need to export a config constant via `export const config` that is a `EventConfig` type.
38
+ **TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `EventConfig` type.
39
+
40
+ **Python**: You need to define a `config` dictionary with the same properties as the TypeScript `EventConfig`.
38
41
 
39
42
  ```typescript
40
43
  export type Emit = string | {
@@ -119,17 +122,28 @@ The handler is a function that is exported via `export const handler` that is a
119
122
 
120
123
  ### Handler definition
121
124
 
125
+ **TypeScript/JavaScript:**
122
126
  ```typescript
123
127
  /**
124
128
  * Input is inferred from the Event Step config['input']
125
129
  * Context is the FlowContext
126
130
  */
127
- export const handler: Handlers['CreateResource'] = async (input, { emit, logger, state, streams }) => {
131
+ export const handler: Handlers['SendEmail'] = async (input, { emit, logger, state, streams }) => {
128
132
  // Implementation
129
133
  }
130
134
  ```
131
135
 
132
- ### Example
136
+ **Python:**
137
+ ```python
138
+ async def handler(input_data, context):
139
+ # input_data: dictionary with the event data (matches the input schema)
140
+ # context: object containing emit, logger, state, streams, trace_id
141
+ pass
142
+ ```
143
+
144
+ ### Examples
145
+
146
+ #### TypeScript Example
133
147
 
134
148
  ```typescript
135
149
  import { EventConfig, Handlers } from 'motia';
@@ -152,6 +166,53 @@ export const config: EventConfig = {
152
166
  };
153
167
 
154
168
  export const handler: Handlers['SendEmail'] = async (input, { emit, logger }) => {
155
- logger.info('Email sent successfully', { email, templateId, templateData });
169
+ const { email, templateId, templateData } = input;
170
+
171
+ // Process email sending logic here
172
+ await emailService.send({
173
+ to: email,
174
+ templateId,
175
+ data: templateData
176
+ });
177
+
178
+ logger.info('Email sent successfully', { email, templateId });
156
179
  };
180
+ ```
181
+
182
+ #### Python Example
183
+
184
+ ```python
185
+ from pydantic import BaseModel
186
+ from typing import Dict, Any
187
+
188
+ class EmailData(BaseModel):
189
+ email: str
190
+ template_id: str
191
+ template_data: Dict[str, Any]
192
+
193
+ config = {
194
+ "type": "event",
195
+ "name": "SendEmail",
196
+ "description": "Sends email notification to the user",
197
+ "subscribes": ["send-email"],
198
+ "emits": [],
199
+ "input": EmailData.model_json_schema(),
200
+ "flows": ["resource-management"]
201
+ }
202
+
203
+ async def handler(input_data, context):
204
+ # Optional: Validate input manually using Pydantic (Motia doesn't do this automatically)
205
+ email_data = EmailData(**input_data)
206
+
207
+ # Process email sending logic here
208
+ await email_service.send({
209
+ "to": email_data.email,
210
+ "template_id": email_data.template_id,
211
+ "data": email_data.template_data
212
+ })
213
+
214
+ context.logger.info("Email sent successfully", {
215
+ "email": email_data.email,
216
+ "template_id": email_data.template_id
217
+ })
157
218
  ```