Skip to content

Conversation

@nikhil-1e9
Copy link
Collaborator

@nikhil-1e9 nikhil-1e9 commented Jul 10, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a workflow to automate social media content creation from article URLs, including scraping articles, generating Twitter threads and LinkedIn posts, and scheduling drafts.
    • Added API endpoint for triggering content generation.
    • Implemented integration with Firecrawl, OpenAI, and Typefully APIs.
    • Provided prompt templates for Twitter and LinkedIn content generation.
  • Documentation

    • Added comprehensive README with setup, usage instructions, and project overview.
  • Chores

    • Added example environment configuration, dependency files, and .gitignore for project setup and environment management.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 10, 2025

Walkthrough

This change introduces a new "Social Media Automation" workflow project in the motia-content-creation directory. It adds environment configuration, dependencies, prompt templates, TypeScript types, and a multi-step event-driven pipeline for scraping articles, generating social media content with AI, and scheduling posts via Typefully. Documentation and setup files are included.

Changes

File(s) Change Summary
.env.example, .gitignore, README.md, requirements.txt, package.json Added environment variable template, Git ignore rules, project documentation, Python requirements, and Node.js package configuration.
config/index.js Introduced a configuration module to load and validate environment variables for API keys and settings.
prompts/linkedin-prompt.txt, prompts/twitter-prompt.txt Added prompt templates for LinkedIn and Twitter content generation, specifying structure, tone, and output format.
steps/api.step.py Added API step to trigger content generation from an article URL, defining request/response models and emitting scrape events.
steps/scrape.step.py Added scraping step using Firecrawl, validating input, extracting article content and title, and emitting generate-content events.
steps/generate.step.py Added content generation step using OpenAI, reading prompt templates, generating social media posts, and emitting schedule-content events.
steps/schedule.step.ts Added scheduling step to send generated content as drafts to Typefully via API, handling both Twitter and LinkedIn posts.
types.d.ts Added TypeScript type declarations for event handlers and API routes, defining the workflow's data contracts.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API Step
    participant Scrape Step
    participant Generate Step
    participant Schedule Step
    participant Typefully API

    Client->>API Step: POST /api/content-generation (url)
    API Step->>Scrape Step: emit scrape-article (requestId, url, timestamp)
    Scrape Step->>Generate Step: emit generate-content (requestId, url, title, content, timestamp)
    Generate Step->>Schedule Step: emit schedule-content (requestId, url, title, twitter, linkedin, metadata)
    Schedule Step->>Typefully API: POST draft (twitter thread)
    Schedule Step->>Typefully API: POST draft (linkedin post)
    Schedule Step-->>Client: (logs/notifications)
Loading

Poem

In burrows deep, the code did grow,
From scrape to tweet, the workflow flows.
Prompts and keys, all set with care,
AI-crafted posts fill digital air.
Typefully drafts, the threads align—
Social magic, by Motia’s design!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (14)
motia-content-creation/.env.example (2)

1-4: Add inline comments & safer placeholders to prevent accidental secret leaks

Consider adding a short # description beside each variable and wrap placeholders in angle-brackets (e.g. <your_openai_api_key>) so that newcomers immediately recognise that real secrets must never be committed.

-OPENAI_API_KEY=your_openai_api_key
+# OpenAI secret used by generate.step.py
+OPENAI_API_KEY=<your_openai_api_key>

Do the same for the other three keys.
This tiny change fosters security hygiene without affecting runtime.


5-5: Ensure file ends with a newline

Many linters flag missing trailing newlines; add one to avoid noisy diffs in future commits.

motia-content-creation/prompts/linkedin-prompt.txt (1)

60-64: Hard-coding characterCount to 1250 may mislead downstream logic

If your scheduling step later validates the field, the model will always output 1250, not the real length.
Either:

  1. Drop the field, or
  2. Instruct the model to compute it: "characterCount": "{{len(post)}}".

Clarify this before the pipeline relies on the value.

motia-content-creation/.gitignore (1)

27-30: Duplicate dist/ entry – keep list DRY

dist/ appears twice (lines 27 and 81). Remove one to avoid merge-conflict churn.

-27 dist/
+27 dist/
-81 dist

Also applies to: 81-82

motia-content-creation/package.json (1)

4-11: Consider cross-platform compatibility for scripts.

The scripts use Unix-specific commands (source, rm -rf) which won't work on Windows. Consider using cross-platform alternatives or providing Windows-specific scripts.

  "scripts": {
-    "prepare": "python3 -m venv python_modules && source python_modules/bin/activate && pip install -r requirements.txt",
-    "dev": "source python_modules/bin/activate && motia dev",
-    "dev:debug": "source python_modules/bin/activate && motia dev --debug",
-    "build": "source python_modules/bin/activate && motia build",
-    "clean": "rm -rf dist .motia .mermaid node_modules python_modules",
+    "prepare": "python3 -m venv python_modules && npm run activate && pip install -r requirements.txt",
+    "activate": "python_modules/bin/activate || python_modules\\Scripts\\activate.bat",
+    "dev": "npm run activate && motia dev",
+    "dev:debug": "npm run activate && motia dev --debug", 
+    "build": "npm run activate && motia build",
+    "clean": "npx rimraf dist .motia .mermaid node_modules python_modules",
    "generate:config": "motia get-config --output ./"
  },

You'll need to add rimraf as a dev dependency for cross-platform file deletion.

motia-content-creation/README.md (3)

12-12: Fix markdown heading structure.

Use proper heading syntax instead of emphasis for the "Workflow" section.

-**Workflow**
+### Workflow

16-18: Add language specification to code block.

Specify the language for better syntax highlighting and accessibility.

-```
+```mermaid
 API → Scrape → Generate → Schedule

---

`91-106`: **Add language specification to code block.**

Specify the language for the project structure tree.



```diff
-```
+```text
 social-media-automation/
 ├── steps/
 │   ├── api.step.ts          # API endpoint handler
 │   ├── scrape.step.ts       # Firecrawl integration
 │   ├── generate.step.ts     # Parallel OpenAI calls
 │   └── schedule.step.ts     # Typefully scheduling
 ├── prompts/
 │   ├── twitter-prompt.txt   # Twitter generation prompt
 │   └── linkedin-prompt.txt  # LinkedIn generation prompt
 ├── config/
 │   └── index.js            # Configuration management
 ├── package.json
 ├── tsconfig.json
 └── README.md

</blockquote></details>
<details>
<summary>motia-content-creation/steps/api.step.py (1)</summary><blockquote>

`2-2`: **Remove unused import.**

The `datetime` import is not used in this file and should be removed.



```diff
-from datetime import datetime
motia-content-creation/steps/scrape.step.py (1)

4-4: Remove unused import.

The datetime import is not used in this file and should be removed.

-from datetime import datetime
motia-content-creation/steps/generate.step.py (3)

5-5: Remove unused import.

The OpenAI import is not used in the code - only AsyncOpenAI is utilized.

-from openai import OpenAI
-from openai import AsyncOpenAI
+from openai import AsyncOpenAI

36-40: Consider using absolute paths for prompt templates.

The current relative paths may cause issues if the working directory changes. Consider using os.path.join or pathlib for more robust path handling.

-        with open("prompts/twitter-prompt.txt", "r", encoding='utf-8') as f:
-            twitterPromptTemplate = f.read()
-
-        with open("prompts/linkedin-prompt.txt", "r", encoding='utf-8') as f:
-            linkedinPromptTemplate = f.read()
+        base_dir = os.path.dirname(os.path.abspath(__file__))
+        with open(os.path.join(base_dir, "..", "prompts", "twitter-prompt.txt"), "r", encoding='utf-8') as f:
+            twitterPromptTemplate = f.read()
+
+        with open(os.path.join(base_dir, "..", "prompts", "linkedin-prompt.txt"), "r", encoding='utf-8') as f:
+            linkedinPromptTemplate = f.read()

69-69: Remove unnecessary f-string prefix.

The string doesn't contain any placeholders, so the f-string prefix is unnecessary.

-        context.logger.info(f"🎉 Content generated successfully!")
+        context.logger.info("🎉 Content generated successfully!")
motia-content-creation/steps/schedule.step.ts (1)

47-47: Add specific error handling for API failures.

Consider adding more specific error handling for different types of API failures (network errors, authentication failures, etc.).

-    const twitterResponse = await axios.post(`${typefullyApiUrl}`, twitterPayload, { headers })
+    const twitterResponse = await axios.post(`${typefullyApiUrl}`, twitterPayload, { headers })
+      .catch(error => {
+        if (error.response?.status === 401) {
+          throw new Error('Typefully API authentication failed. Check your API key.')
+        }
+        throw new Error(`Typefully API error: ${error.message}`)
+      })

Also applies to: 60-60

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7228ba and c4d259a.

📒 Files selected for processing (13)
  • motia-content-creation/.env.example (1 hunks)
  • motia-content-creation/.gitignore (1 hunks)
  • motia-content-creation/README.md (1 hunks)
  • motia-content-creation/config/index.js (1 hunks)
  • motia-content-creation/package.json (1 hunks)
  • motia-content-creation/prompts/linkedin-prompt.txt (1 hunks)
  • motia-content-creation/prompts/twitter-prompt.txt (1 hunks)
  • motia-content-creation/requirements.txt (1 hunks)
  • motia-content-creation/steps/api.step.py (1 hunks)
  • motia-content-creation/steps/generate.step.py (1 hunks)
  • motia-content-creation/steps/schedule.step.ts (1 hunks)
  • motia-content-creation/steps/scrape.step.py (1 hunks)
  • motia-content-creation/types.d.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
motia-content-creation/steps/api.step.py (3)
motia-content-creation/config/index.js (1)
  • config (3-29)
motia-content-creation/steps/scrape.step.py (1)
  • handler (26-50)
motia-content-creation/steps/generate.step.py (1)
  • handler (31-92)
motia-content-creation/steps/scrape.step.py (3)
motia-content-creation/config/index.js (1)
  • config (3-29)
motia-content-creation/steps/api.step.py (1)
  • handler (32-56)
motia-content-creation/steps/generate.step.py (1)
  • handler (31-92)
motia-content-creation/steps/schedule.step.ts (4)
motia-content-creation/config/index.js (1)
  • config (3-29)
motia-content-creation/steps/api.step.py (1)
  • handler (32-56)
motia-content-creation/steps/scrape.step.py (1)
  • handler (26-50)
motia-content-creation/steps/generate.step.py (1)
  • handler (31-92)
🪛 markdownlint-cli2 (0.17.2)
motia-content-creation/README.md

12-12: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


91-91: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 LanguageTool
motia-content-creation/prompts/twitter-prompt.txt

[style] ~19-~19: Consider using the synonym “brief” (= concise, using a few words, not lasting long) to strengthen your wording.
Context: ...we delve into the details, let's take a quick look at its formula: (2/n) <add an il...

(QUICK_BRIEF)


[style] ~83-~83: Using many exclamation marks might seem excessive (in this case: 7 exclamation marks for a text that’s 2793 characters long)
Context: ...share tutorials on above topics! Cheers! 🥂 7/n ##############################...

(EN_EXCESSIVE_EXCLAMATION)

🪛 Ruff (0.11.9)
motia-content-creation/steps/api.step.py

2-2: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)

motia-content-creation/steps/generate.step.py

5-5: openai.OpenAI imported but unused

Remove unused import: openai.OpenAI

(F401)


69-69: f-string without any placeholders

Remove extraneous f prefix

(F541)

motia-content-creation/steps/scrape.step.py

4-4: datetime.datetime imported but unused

Remove unused import: datetime.datetime

(F401)

🔇 Additional comments (6)
motia-content-creation/config/index.js (1)

21-31: Excellent validation approach.

The validation function with immediate execution ensures required environment variables are present at startup, preventing runtime failures later in the workflow.

motia-content-creation/package.json (1)

20-33: Well-chosen dependencies for the workflow.

The dependency selection is appropriate for the social media automation use case, with proper version constraints and necessary TypeScript support.

motia-content-creation/README.md (1)

1-121: Excellent documentation structure.

The README provides comprehensive setup instructions, clear workflow explanation, and helpful usage examples. The project structure overview is particularly valuable for understanding the codebase.

motia-content-creation/steps/api.step.py (1)

17-30: Well-structured configuration and schema definitions.

The configuration properly defines the API endpoint with appropriate schemas and event flow specification. The Pydantic models provide good type safety.

motia-content-creation/steps/scrape.step.py (1)

26-50: Excellent error handling and workflow integration.

The scraping logic properly handles Firecrawl failures and integrates well with the workflow by emitting the correct event with all necessary data for the next step.

motia-content-creation/types.d.ts (1)

1-20: Auto-generated type definitions look correct.

The type definitions properly declare the handler interfaces and match the implementations in the step files. The warning about not editing manually is appropriate.

Comment on lines +1 to +5
pydantic>=2.11.7
openai>=1.93.1
firecrawl>=2.15.0
python-dotenv>=1.1.1
requests>=2.32.3 No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pin exact versions for reproducible builds

Using >= lets pip pull the latest minor/patch version, which can silently break the workflow months from now.
Unless you have an automated depend-on-latest policy, switch to strict pins:

-pydantic>=2.11.7
-openai>=1.93.1
-firecrawl>=2.15.0
-python-dotenv>=1.1.1
-requests>=2.32.3
+pydantic==2.11.7
+openai==1.93.1
+firecrawl==2.15.0      # verify still compatible with requests 2.32.3
+python-dotenv==1.1.1
+requests==2.32.3

Locking versions eliminates “works-on-my-machine” surprises and makes container builds deterministic.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pydantic>=2.11.7
openai>=1.93.1
firecrawl>=2.15.0
python-dotenv>=1.1.1
requests>=2.32.3
pydantic==2.11.7
openai==1.93.1
firecrawl==2.15.0 # verify still compatible with requests 2.32.3
python-dotenv==1.1.1
requests==2.32.3
🤖 Prompt for AI Agents
In motia-content-creation/requirements.txt lines 1 to 5, the dependencies use
minimum version specifiers (>=), which can lead to unpredictable builds. Change
each dependency to pin an exact version by replacing >= with == and specifying
the exact version number to ensure reproducible and deterministic builds.

Comment on lines +116 to +118
],
"totalTweets": 5
} No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fixed totalTweets contradicts dynamic 3-7 range – likely to break validation

You ask for 3-7 tweets, but the example JSON forces "totalTweets": 5.
The LLM will mimic the example and ignore the 3-7 instruction, causing an off-by-N error.

-  "totalTweets": 5
+  "totalTweets": "{{tweets|length}}"

Or let the backend derive the count instead of asking the model.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
],
"totalTweets": 5
}
],
"totalTweets": "{{tweets|length}}"
}
🤖 Prompt for AI Agents
In motia-content-creation/prompts/twitter-prompt.txt around lines 116 to 118,
the fixed "totalTweets" value of 5 contradicts the dynamic 3-7 tweet range
requested, which can cause validation errors. To fix this, remove the hardcoded
"totalTweets" value from the example JSON so the model does not mimic a fixed
count, or alternatively, modify the backend to calculate the total tweet count
dynamically instead of relying on the model's output.

Comment on lines +17 to +19
motia: {
port: parseInt(process.env.MOTIA_PORT) || 3000,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for MOTIA_PORT parsing.

The parseInt() function could return NaN if MOTIA_PORT is not a valid number, which would cause issues downstream.

  motia: {
-    port: parseInt(process.env.MOTIA_PORT) || 3000,
+    port: parseInt(process.env.MOTIA_PORT, 10) || 3000,
  },

Consider adding explicit validation:

  motia: {
-    port: parseInt(process.env.MOTIA_PORT) || 3000,
+    port: (() => {
+      const port = parseInt(process.env.MOTIA_PORT, 10);
+      return isNaN(port) ? 3000 : port;
+    })(),
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
motia: {
port: parseInt(process.env.MOTIA_PORT) || 3000,
},
motia: {
- port: parseInt(process.env.MOTIA_PORT) || 3000,
+ port: (() => {
+ const port = parseInt(process.env.MOTIA_PORT, 10);
+ return isNaN(port) ? 3000 : port;
+ })(),
},
🤖 Prompt for AI Agents
In motia-content-creation/config/index.js around lines 17 to 19, the port value
is set using parseInt on process.env.MOTIA_PORT without validating if the result
is a valid number. To fix this, add explicit validation after parsing MOTIA_PORT
to check if the result is NaN; if it is, fallback to the default port 3000. This
ensures the port configuration is always a valid number and prevents downstream
errors.

Comment on lines +32 to +35
async def handler(req, context):
# Extract request data
# req = input['req']
url = str(req['body']['url'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clean up commented code and add input validation.

Remove the commented legacy code and add proper input validation using the defined schema.

 async def handler(req, context):
     # Extract request data
-    # req = input['req']
+    # Validate request body against schema
+    try:
+        request_body = RequestBody(**req['body'])
+        url = str(request_body.url)
+    except Exception as e:
+        return {
+            'status': 400,
+            'body': {'error': f'Invalid request body: {str(e)}'}
+        }
+    
-    url = str(req['body']['url'])
-
     context.logger.info('🚀 Starting content generation workflow...')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def handler(req, context):
# Extract request data
# req = input['req']
url = str(req['body']['url'])
async def handler(req, context):
# Extract request data
# Validate request body against schema
try:
request_body = RequestBody(**req['body'])
url = str(request_body.url)
except Exception as e:
return {
'status': 400,
'body': {'error': f'Invalid request body: {str(e)}'}
}
context.logger.info('🚀 Starting content generation workflow...')
🤖 Prompt for AI Agents
In motia-content-creation/steps/api.step.py around lines 32 to 35, remove the
commented-out legacy code that extracts request data. Then, add input validation
by using the defined schema to validate the incoming request data before
processing it. This ensures the input is correctly structured and prevents
errors downstream.

Comment on lines +5 to +10
from dotenv import load_dotenv

load_dotenv()

FIRECRAWL_API_KEY = os.getenv('FIRECRAWL_API_KEY')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove redundant environment loading and add API key validation.

The load_dotenv() call is redundant since the configuration in config/index.js already handles environment variables. Also add validation for the API key.

-from dotenv import load_dotenv
-
-load_dotenv()
-
-FIRECRAWL_API_KEY = os.getenv('FIRECRAWL_API_KEY')
+FIRECRAWL_API_KEY = os.getenv('FIRECRAWL_API_KEY')
+
+if not FIRECRAWL_API_KEY:
+    raise ValueError("FIRECRAWL_API_KEY environment variable is required")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from dotenv import load_dotenv
load_dotenv()
FIRECRAWL_API_KEY = os.getenv('FIRECRAWL_API_KEY')
# (remove the `dotenv` import and load call)
FIRECRAWL_API_KEY = os.getenv('FIRECRAWL_API_KEY')
if not FIRECRAWL_API_KEY:
raise ValueError("FIRECRAWL_API_KEY environment variable is required")
🤖 Prompt for AI Agents
In motia-content-creation/steps/scrape.step.py around lines 5 to 10, remove the
redundant call to load_dotenv() since environment variables are already loaded
in config/index.js. Additionally, add validation to check if FIRECRAWL_API_KEY
is set after retrieving it with os.getenv, and raise an appropriate error or
handle the missing key scenario to ensure the API key is available before
proceeding.

Comment on lines +66 to +67
twitter_content = json.loads(twitter_response.choices[0].message.content)
linkedin_content = json.loads(linkedin_response.choices[0].message.content)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for JSON parsing.

The code assumes the OpenAI response will always contain valid JSON. Consider adding error handling for malformed JSON responses.

-        twitter_content = json.loads(twitter_response.choices[0].message.content)
-        linkedin_content = json.loads(linkedin_response.choices[0].message.content)
+        try:
+            twitter_content = json.loads(twitter_response.choices[0].message.content)
+            linkedin_content = json.loads(linkedin_response.choices[0].message.content)
+        except json.JSONDecodeError as e:
+            context.logger.error(f"Failed to parse OpenAI response as JSON: {e}")
+            raise ValueError("Invalid JSON response from OpenAI") from e
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
twitter_content = json.loads(twitter_response.choices[0].message.content)
linkedin_content = json.loads(linkedin_response.choices[0].message.content)
try:
twitter_content = json.loads(twitter_response.choices[0].message.content)
linkedin_content = json.loads(linkedin_response.choices[0].message.content)
except json.JSONDecodeError as e:
context.logger.error(f"Failed to parse OpenAI response as JSON: {e}")
raise ValueError("Invalid JSON response from OpenAI") from e
🤖 Prompt for AI Agents
In motia-content-creation/steps/generate.step.py around lines 66 to 67, the code
parses JSON from OpenAI responses without handling potential JSON decoding
errors. Add try-except blocks around the json.loads calls to catch
JSONDecodeError exceptions, and handle these errors gracefully, such as logging
the error and providing fallback behavior or raising a clear exception.

logger.info(`💼 Scheduling LinkedIn post...`)

const linkedinPayload = {
content: input.content.linkedin.post,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add null safety for LinkedIn post access.

Similar to the Twitter thread, ensure the LinkedIn post content exists before using it.

-    const linkedinPayload = {
-      content: input.content.linkedin.post,
-      schedule_date: null // Will be posted as draft
-    }
+    const linkedinPost = input.content.linkedin?.post
+    
+    if (!linkedinPost) {
+      throw new Error('LinkedIn post content is missing or invalid')
+    }
+    
+    const linkedinPayload = {
+      content: linkedinPost,
+      schedule_date: null // Will be posted as draft
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
content: input.content.linkedin.post,
const linkedinPost = input.content.linkedin?.post
if (!linkedinPost) {
throw new Error('LinkedIn post content is missing or invalid')
}
const linkedinPayload = {
content: linkedinPost,
schedule_date: null // Will be posted as draft
}
🤖 Prompt for AI Agents
In motia-content-creation/steps/schedule.step.ts at line 56, the code accesses
input.content.linkedin.post without checking if linkedin or post exists, which
can cause runtime errors. Add a null or undefined check for
input.content.linkedin and input.content.linkedin.post before accessing post,
using optional chaining or conditional checks to ensure safe access.

Comment on lines +24 to +26
twitter: z.any(),
linkedin: z.any()
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve schema validation for content structure.

Using z.any() for the content objects bypasses type safety. Consider defining more specific schemas based on the expected structure from the content generation step.

-    content: z.object({
-      twitter: z.any(),
-      linkedin: z.any()
-    }),
+    content: z.object({
+      twitter: z.object({
+        thread: z.array(z.object({
+          content: z.string()
+        }))
+      }),
+      linkedin: z.object({
+        post: z.string()
+      })
+    }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
twitter: z.any(),
linkedin: z.any()
}),
content: z.object({
twitter: z.object({
thread: z.array(z.object({
content: z.string()
}))
}),
linkedin: z.object({
post: z.string()
})
}),
🤖 Prompt for AI Agents
In motia-content-creation/steps/schedule.step.ts around lines 24 to 26, the
schema uses z.any() for twitter and linkedin fields, which bypasses type safety.
Replace z.any() with more specific zod schemas that reflect the expected
structure of the content objects from the content generation step, defining the
exact fields and their types to improve validation and type safety.

logger.info(`📱 Scheduling Twitter thread...`)

// Convert Twitter thread to Typefully format
const twitterThread = input.content.twitter.thread.map((tweet: any) => tweet.content).join('\n\n\n\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add null safety for nested property access.

The code assumes input.content.twitter.thread exists and is an array. This could cause runtime errors if the structure is different.

-    const twitterThread = input.content.twitter.thread.map((tweet: any) => tweet.content).join('\n\n\n\n')
+    const twitterThread = input.content.twitter?.thread?.map((tweet: any) => tweet.content).join('\n\n\n\n')
+    
+    if (!twitterThread) {
+      throw new Error('Twitter thread content is missing or invalid')
+    }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In motia-content-creation/steps/schedule.step.ts at line 40, the code accesses
input.content.twitter.thread without checking if these nested properties exist,
which can cause runtime errors. Add null or undefined checks for input.content,
input.content.twitter, and input.content.twitter.thread before mapping over the
array. Use optional chaining or conditional checks to safely access the thread
array and handle cases where it might be missing or not an array.

@patchy631 patchy631 merged commit eba4197 into patchy631:main Jul 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants