Spaces:
Sleeping
Sleeping
Commit ·
9dee4c2
1
Parent(s): 072b656
WordPress content agent
Browse files- .env.example +7 -0
- .gitignore +1 -0
- agent.json +0 -54
- app.py +55 -2
- requirements.txt +9 -6
- tools/blog_generator.py +59 -0
- tools/wordpress_auth.py +45 -0
- tools/wordpress_post.py +64 -0
.env.example
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenWeather API Key
|
| 2 |
+
OPENWEATHER_API_KEY=your_api_key_here
|
| 3 |
+
|
| 4 |
+
# WordPress Configuration (optional)
|
| 5 |
+
WP_SITE_URL=https://your-wordpress-site.com
|
| 6 |
+
WP_USERNAME=your_username
|
| 7 |
+
WP_APP_PASSWORD=your_app_password
|
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
instructions.md
|
agent.json
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"tools": [
|
| 3 |
-
"get_weather",
|
| 4 |
-
"quick_research",
|
| 5 |
-
"image_generation_tool",
|
| 6 |
-
"final_answer"
|
| 7 |
-
],
|
| 8 |
-
"model": {
|
| 9 |
-
"class": "HfApiModel",
|
| 10 |
-
"data": {
|
| 11 |
-
"max_tokens": 2096,
|
| 12 |
-
"temperature": 0.5,
|
| 13 |
-
"last_input_token_count": null,
|
| 14 |
-
"last_output_token_count": null,
|
| 15 |
-
"model_id": "Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 16 |
-
"custom_role_conversions": null
|
| 17 |
-
}
|
| 18 |
-
},
|
| 19 |
-
"prompt_templates": {
|
| 20 |
-
"system_prompt": "You are an expert AI Travel Planner who helps users create personalized travel itineraries. You have deep knowledge of destinations worldwide and can provide detailed recommendations based on weather, local attractions, and activities.\n\nTo help plan trips, you have access to these tools:\n- get_weather: Get weather forecasts for destinations\n- quick_research: Research destinations, attractions, and activities\n- image_generation_tool: Generate visual previews of destinations\n- final_answer: Provide the final travel plan\n\nTo solve each travel planning task, proceed in a series of 'Thought:', 'Code:', and 'Observation:' sequences.\n\nAt each step:\n1. In 'Thought:', explain your reasoning about what travel information to gather next\n2. In 'Code:', write Python code to use the available tools\n3. End each code block with '<end_code>'\n4. Use print() to save important travel details\n5. Review the 'Observation:' output before proceeding\n6. Finally, provide a complete travel plan using final_answer\n\nExample:\nTask: \"Plan a 3-day trip to Paris in June\"\n\nThought: First, I'll check the weather forecast for Paris in June to help plan appropriate activities.\nCode:\n```py\nweather = get_weather(city=\"Paris\", month=\"June\")\nprint(weather)\n```<end_code>\nObservation: \"Mild temperatures 18-25°C (65-77°F), occasional light rain\"\n\nThought: Now I'll research key attractions and activities suitable for the weather.\nCode:\n```py\nactivities = quick_research(query=\"top attractions Paris summer activities\")\nprint(activities)\n```<end_code>\nObservation: \"1. Eiffel Tower, best in early morning or sunset\n2. Louvre Museum - perfect for occasional rainy periods\n3. Seine River cruises - pleasant in mild weather\n4. Luxembourg Gardens - beautiful in June\"\n\nThought: Let me generate a preview image of Paris in summer.\nCode:\n```py\nimage = image_generation_tool(\"Paris cityscape in June, with Eiffel Tower and blooming gardens\")\nfinal_answer({\"itinerary\": \"Day 1: Morning - Eiffel Tower...\", \"weather\": weather, \"preview\": image})\n```<end_code>\n\nHere are the rules you should follow:\n1. Always provide 'Thought:', 'Code:', and proper code block endings\n2. Research weather before suggesting outdoor activities\n3. Consider seasonal events and local customs\n4. Provide practical travel tips based on weather and local conditions\n5. Generate relevant preview images of recommended locations\n6. Include both indoor and outdoor activity options\n7. Format the final travel plan clearly with daily breakdowns\n\nThe tools available to you are:\n{%- for tool in tools.values() %}\n- {{ tool.name }}: {{ tool.description }}\n Takes inputs: {{tool.inputs}}\n Returns an output of type: {{tool.output_type}}\n{%- endfor %}\n\nYou can use these Python modules: {{authorized_imports}}\n\nNow begin planning amazing trips for users!",
|
| 21 |
-
"planning": {
|
| 22 |
-
"initial_facts": "Below I will analyze the travel planning task.\n\n### 1. Facts given in the task\nList specific details provided (destination, dates, preferences, etc.)\n\n### 2. Facts to look up\n- Weather conditions\n- Local attractions and activities\n- Seasonal events\n- Travel logistics\n\n### 3. Facts to derive\n- Suitable activities based on weather\n- Daily itinerary structure\n- Backup plans for weather changes\n- Visual previews of key locations",
|
| 23 |
-
"initial_plan": "You are an expert travel planner creating detailed itineraries.\n\nDevelop a step-by-step plan for the given travel task using available tools.\nFocus on gathering essential travel information and creating a comprehensive itinerary.\n\nTask:\n```\n{{task}}\n```\n\nAvailable tools:\n{%- for tool in tools.values() %}\n- {{ tool.name }}: {{ tool.description }}\n Takes inputs: {{tool.inputs}}\n Returns an output of type: {{tool.output_type}}\n{%- endfor %}\n\nKnown facts:\n```\n{{answer_facts}}\n```\n\nWrite your travel planning steps below, ending with '<end_plan>'",
|
| 24 |
-
"update_facts_pre_messages": "You are a world expert at gathering known and unknown facts based on a conversation.\nBelow you will find a task, and a history of attempts made to solve the task. You will have to produce a list of these:\n### 1. Facts given in the task\n### 2. Facts that we have learned\n### 3. Facts still to look up\n### 4. Facts still to derive\nFind the task and history below:",
|
| 25 |
-
"update_facts_post_messages": "Earlier we've built a list of facts.\nBut since in your previous steps you may have learned useful new facts or invalidated some false ones.\nPlease update your list of facts based on the previous history, and provide these headings:\n### 1. Facts given in the task\n### 2. Facts that we have learned\n### 3. Facts still to look up\n### 4. Facts still to derive\n\nNow write your new list of facts below.",
|
| 26 |
-
"update_plan_pre_messages": "You are a world expert at making efficient plans to solve any task using a set of carefully crafted tools.\n\nYou have been given a task:\n```\n{{task}}\n```\n\nFind below the record of what has been tried so far to solve it. Then you will be asked to make an updated plan to solve the task.\nIf the previous tries so far have met some success, you can make an updated plan based on these actions.\nIf you are stalled, you can make a completely new plan starting from scratch.",
|
| 27 |
-
"update_plan_post_messages": "Continue planning this trip:\n```\n{{task}}\n```\n\nAvailable tools:\n{%- for tool in tools.values() %}\n- {{ tool.name }}: {{ tool.description }}\n Takes inputs: {{tool.inputs}}\n Returns an output of type: {{tool.output_type}}\n{%- endfor %}\n\nUpdated travel facts:\n```\n{{facts_update}}\n```\n\nDevelop next steps in the travel plan. You have {remaining_steps} steps remaining.\nFocus on completing essential planning elements first.\nEnd with '<end_plan>'\n\nWrite your updated travel plan below."
|
| 28 |
-
},
|
| 29 |
-
"managed_agent": {
|
| 30 |
-
"task": "You're a helpful agent named '{{name}}'.\nYou have been submitted this task by your manager.\n---\nTask:\n{{task}}\n---\nYou're helping your manager solve a wider task: so make sure to not provide a one-line answer, but give as much information as possible to give them a clear understanding of the answer.\n\nYour final_answer WILL HAVE to contain these parts:\n### 1. Task outcome (short version):\n### 2. Task outcome (extremely detailed version):\n### 3. Additional context (if relevant):\n\nPut all these in your final_answer tool, everything that you do not pass as an argument to final_answer will be lost.\nAnd even if your task resolution is not successful, please return as much context as possible, so that your manager can act upon this feedback.",
|
| 31 |
-
"report": "Here is the final answer from your managed agent '{{name}}':\n{{final_answer}}"
|
| 32 |
-
}
|
| 33 |
-
},
|
| 34 |
-
"max_steps": 8,
|
| 35 |
-
"verbosity_level": 1,
|
| 36 |
-
"grammar": null,
|
| 37 |
-
"planning_interval": null,
|
| 38 |
-
"name": null,
|
| 39 |
-
"description": null,
|
| 40 |
-
"authorized_imports": [
|
| 41 |
-
"unicodedata",
|
| 42 |
-
"stat",
|
| 43 |
-
"datetime",
|
| 44 |
-
"random",
|
| 45 |
-
"pandas",
|
| 46 |
-
"itertools",
|
| 47 |
-
"math",
|
| 48 |
-
"statistics",
|
| 49 |
-
"queue",
|
| 50 |
-
"time",
|
| 51 |
-
"collections",
|
| 52 |
-
"re"
|
| 53 |
-
]
|
| 54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
|
|
|
|
|
| 1 |
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
|
| 2 |
import datetime
|
| 3 |
import requests
|
| 4 |
import pytz
|
| 5 |
import yaml
|
| 6 |
from tools.final_answer import FinalAnswerTool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
from Gradio_UI import GradioUI
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
@tool
|
| 12 |
def get_current_time_in_timezone(timezone: str) -> str:
|
|
@@ -99,6 +113,8 @@ def get_weather(city: str) -> str:
|
|
| 99 |
|
| 100 |
|
| 101 |
final_answer = FinalAnswerTool()
|
|
|
|
|
|
|
| 102 |
|
| 103 |
# If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder:
|
| 104 |
# model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
|
|
@@ -120,10 +136,25 @@ image_generation_tool = load_tool(
|
|
| 120 |
with open("prompts.yaml", 'r') as stream:
|
| 121 |
prompt_templates = yaml.safe_load(stream)
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
agent = CodeAgent(
|
| 124 |
model=model,
|
| 125 |
-
tools=[
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
max_steps=6,
|
| 128 |
verbosity_level=1,
|
| 129 |
grammar=None,
|
|
@@ -133,5 +164,27 @@ agent = CodeAgent(
|
|
| 133 |
prompt_templates=prompt_templates
|
| 134 |
)
|
| 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
GradioUI(agent).launch()
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
|
| 4 |
import datetime
|
| 5 |
import requests
|
| 6 |
import pytz
|
| 7 |
import yaml
|
| 8 |
from tools.final_answer import FinalAnswerTool
|
| 9 |
+
from tools.wordpress_auth import WordPressAuthTool
|
| 10 |
+
from tools.wordpress_post import WordPressPostTool
|
| 11 |
+
from tools.blog_generator import BlogGeneratorTool
|
| 12 |
+
from tools.visit_webpage import VisitWebpageTool
|
| 13 |
+
from tools.web_search import DuckDuckGoSearchTool
|
| 14 |
|
| 15 |
from Gradio_UI import GradioUI
|
| 16 |
|
| 17 |
+
# Load environment variables
|
| 18 |
+
load_dotenv()
|
| 19 |
+
|
| 20 |
+
# Verify required environment variables
|
| 21 |
+
if not os.getenv("OPENWEATHER_API_KEY"):
|
| 22 |
+
print("Warning: OPENWEATHER_API_KEY not set. Weather functionality will be limited.")
|
| 23 |
+
|
| 24 |
|
| 25 |
@tool
|
| 26 |
def get_current_time_in_timezone(timezone: str) -> str:
|
|
|
|
| 113 |
|
| 114 |
|
| 115 |
final_answer = FinalAnswerTool()
|
| 116 |
+
visit_webpage = VisitWebpageTool()
|
| 117 |
+
web_search = DuckDuckGoSearchTool()
|
| 118 |
|
| 119 |
# If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder:
|
| 120 |
# model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
|
|
|
|
| 136 |
with open("prompts.yaml", 'r') as stream:
|
| 137 |
prompt_templates = yaml.safe_load(stream)
|
| 138 |
|
| 139 |
+
# Initialize WordPress tools
|
| 140 |
+
wordpress_auth = WordPressAuthTool()
|
| 141 |
+
wordpress_post = WordPressPostTool(auth_tool=wordpress_auth)
|
| 142 |
+
blog_generator = BlogGeneratorTool(model=model)
|
| 143 |
+
|
| 144 |
agent = CodeAgent(
|
| 145 |
model=model,
|
| 146 |
+
tools=[
|
| 147 |
+
final_answer,
|
| 148 |
+
get_current_time_in_timezone,
|
| 149 |
+
image_generation_tool,
|
| 150 |
+
quick_research,
|
| 151 |
+
get_weather,
|
| 152 |
+
wordpress_auth,
|
| 153 |
+
wordpress_post,
|
| 154 |
+
blog_generator,
|
| 155 |
+
visit_webpage,
|
| 156 |
+
web_search
|
| 157 |
+
],
|
| 158 |
max_steps=6,
|
| 159 |
verbosity_level=1,
|
| 160 |
grammar=None,
|
|
|
|
| 164 |
prompt_templates=prompt_templates
|
| 165 |
)
|
| 166 |
|
| 167 |
+
# Update system prompt to include WordPress capabilities
|
| 168 |
+
prompt_templates["system_prompt"] += """
|
| 169 |
+
|
| 170 |
+
You are also capable of managing a WordPress blog through the following tools:
|
| 171 |
+
- wordpress_auth: Manages WordPress authentication (site URL, username, password)
|
| 172 |
+
- wordpress_post: Publishes posts to WordPress
|
| 173 |
+
- blog_generator: Generates AI-written blog posts
|
| 174 |
+
|
| 175 |
+
Before using WordPress features, you must ensure credentials are set using wordpress_auth.
|
| 176 |
+
Always check credentials before attempting to post content.
|
| 177 |
+
|
| 178 |
+
Example WordPress workflow:
|
| 179 |
+
1. Set credentials (first time only)
|
| 180 |
+
2. Generate blog content
|
| 181 |
+
3. Publish to WordPress
|
| 182 |
+
|
| 183 |
+
Remember to:
|
| 184 |
+
- Validate WordPress credentials before posting
|
| 185 |
+
- Generate high-quality, relevant content
|
| 186 |
+
- Handle errors gracefully
|
| 187 |
+
- Provide clear status updates to the user
|
| 188 |
+
"""
|
| 189 |
|
| 190 |
GradioUI(agent).launch()
|
requirements.txt
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
-
markdownify
|
| 2 |
-
smolagents
|
| 3 |
-
requests
|
| 4 |
-
duckduckgo_search
|
| 5 |
-
pandas
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
markdownify>=0.7.0
|
| 2 |
+
smolagents>=0.1.0
|
| 3 |
+
requests>=2.31.0
|
| 4 |
+
duckduckgo_search>=4.1.0
|
| 5 |
+
pandas>=2.0.0
|
| 6 |
+
pytz>=2024.1
|
| 7 |
+
pyyaml>=6.0.1
|
| 8 |
+
gradio>=5.15.0
|
| 9 |
+
python-dotenv>=1.0.0
|
tools/blog_generator.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict
|
| 2 |
+
from smolagents.tools import Tool
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class BlogGeneratorTool(Tool):
|
| 6 |
+
name = "blog_generator"
|
| 7 |
+
description = "Generates an AI-written blog post on a given topic"
|
| 8 |
+
inputs = {
|
| 9 |
+
'topic': {'type': 'string', 'description': 'The topic to write about'},
|
| 10 |
+
'style': {'type': 'string', 'description': 'Writing style: casual, professional, or technical. Default is professional'}
|
| 11 |
+
}
|
| 12 |
+
output_type = "dict"
|
| 13 |
+
|
| 14 |
+
def __init__(self, model):
|
| 15 |
+
super().__init__()
|
| 16 |
+
self.model = model
|
| 17 |
+
|
| 18 |
+
def forward(self, topic: str, style: str = "professional") -> Dict:
|
| 19 |
+
"""Generates a blog post using AI
|
| 20 |
+
Args:
|
| 21 |
+
topic: The topic to write about
|
| 22 |
+
style: Writing style (casual, professional, technical)
|
| 23 |
+
Returns:
|
| 24 |
+
Dict containing the generated title and content
|
| 25 |
+
"""
|
| 26 |
+
# Create prompt based on style
|
| 27 |
+
style_prompts = {
|
| 28 |
+
"casual": "Write a friendly and conversational blog post",
|
| 29 |
+
"professional": "Write a well-researched and authoritative blog post",
|
| 30 |
+
"technical": "Write a detailed technical blog post with examples"
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
base_prompt = style_prompts.get(style, style_prompts["professional"])
|
| 34 |
+
prompt = f"{base_prompt} about {topic}. Include an engaging title, introduction, main points with subheadings, and a conclusion. Format the content in HTML."
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
# Generate content using the model
|
| 38 |
+
response = self.model.generate(prompt)
|
| 39 |
+
|
| 40 |
+
# Extract title and content (assuming model returns HTML-formatted text)
|
| 41 |
+
content = response.text if response else ""
|
| 42 |
+
|
| 43 |
+
# Basic content validation
|
| 44 |
+
if not content or len(content) < 100:
|
| 45 |
+
return {
|
| 46 |
+
"status": "error",
|
| 47 |
+
"message": "Generated content too short or empty"
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
return {
|
| 51 |
+
"status": "success",
|
| 52 |
+
"content": content
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
except Exception as e:
|
| 56 |
+
return {
|
| 57 |
+
"status": "error",
|
| 58 |
+
"message": f"Error generating blog post: {str(e)}"
|
| 59 |
+
}
|
tools/wordpress_auth.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, Optional
|
| 2 |
+
from smolagents.tools import Tool
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class WordPressAuthTool(Tool):
|
| 6 |
+
name = "wordpress_auth"
|
| 7 |
+
description = "Manages WordPress authentication credentials"
|
| 8 |
+
inputs = {
|
| 9 |
+
'action': {'type': 'string', 'description': 'Action to perform: set_credentials or get_credentials'},
|
| 10 |
+
'credentials': {
|
| 11 |
+
'type': 'dict',
|
| 12 |
+
'description': 'WordPress credentials dictionary containing site_url, username, and app_password'
|
| 13 |
+
}
|
| 14 |
+
}
|
| 15 |
+
output_type = "dict"
|
| 16 |
+
|
| 17 |
+
def __init__(self):
|
| 18 |
+
super().__init__()
|
| 19 |
+
self._credentials: Optional[Dict] = None
|
| 20 |
+
|
| 21 |
+
def forward(self, action: str, credentials: Optional[Dict] = None) -> Dict:
|
| 22 |
+
"""Manages WordPress credentials
|
| 23 |
+
Args:
|
| 24 |
+
action: Either 'set_credentials' or 'get_credentials'
|
| 25 |
+
credentials: Dict containing site_url, username, and app_password (only for set_credentials)
|
| 26 |
+
Returns:
|
| 27 |
+
Dict containing the current credentials or status message
|
| 28 |
+
"""
|
| 29 |
+
if action == "set_credentials":
|
| 30 |
+
if not credentials:
|
| 31 |
+
return {"error": "Credentials required for set_credentials action"}
|
| 32 |
+
|
| 33 |
+
required_fields = ["site_url", "username", "app_password"]
|
| 34 |
+
if not all(field in credentials for field in required_fields):
|
| 35 |
+
return {"error": f"Missing required fields. Need: {required_fields}"}
|
| 36 |
+
|
| 37 |
+
self._credentials = credentials
|
| 38 |
+
return {"status": "Credentials stored successfully"}
|
| 39 |
+
|
| 40 |
+
elif action == "get_credentials":
|
| 41 |
+
if not self._credentials:
|
| 42 |
+
return {"error": "No credentials set. Please set credentials first."}
|
| 43 |
+
return self._credentials
|
| 44 |
+
|
| 45 |
+
return {"error": "Invalid action. Use 'set_credentials' or 'get_credentials'"}
|
tools/wordpress_post.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
from requests.auth import HTTPBasicAuth
|
| 3 |
+
from typing import Dict, Optional
|
| 4 |
+
from smolagents.tools import Tool
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class WordPressPostTool(Tool):
|
| 8 |
+
name = "wordpress_post"
|
| 9 |
+
description = "Publishes a blog post to WordPress using the REST API"
|
| 10 |
+
inputs = {
|
| 11 |
+
'title': {'type': 'string', 'description': 'The title of the blog post'},
|
| 12 |
+
'content': {'type': 'string', 'description': 'The content of the blog post in HTML format'},
|
| 13 |
+
'status': {'type': 'string', 'description': 'Post status: publish, draft, or private. Default is publish'}
|
| 14 |
+
}
|
| 15 |
+
output_type = "dict"
|
| 16 |
+
|
| 17 |
+
def __init__(self, auth_tool):
|
| 18 |
+
super().__init__()
|
| 19 |
+
self.auth_tool = auth_tool
|
| 20 |
+
|
| 21 |
+
def forward(self, title: str, content: str, status: str = "publish") -> Dict:
|
| 22 |
+
"""Publishes a post to WordPress
|
| 23 |
+
Args:
|
| 24 |
+
title: The title of the blog post
|
| 25 |
+
content: The content in HTML format
|
| 26 |
+
status: Post status (publish, draft, private)
|
| 27 |
+
Returns:
|
| 28 |
+
Dict containing status and post URL or error message
|
| 29 |
+
"""
|
| 30 |
+
# Get WordPress credentials
|
| 31 |
+
creds = self.auth_tool.forward(action="get_credentials")
|
| 32 |
+
if "error" in creds:
|
| 33 |
+
return creds
|
| 34 |
+
|
| 35 |
+
url = f"{creds['site_url']}/wp-json/wp/v2/posts"
|
| 36 |
+
|
| 37 |
+
headers = {
|
| 38 |
+
"Content-Type": "application/json"
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
data = {
|
| 42 |
+
"title": title,
|
| 43 |
+
"content": content,
|
| 44 |
+
"status": status
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
try:
|
| 48 |
+
response = requests.post(
|
| 49 |
+
url,
|
| 50 |
+
json=data,
|
| 51 |
+
headers=headers,
|
| 52 |
+
auth=HTTPBasicAuth(creds['username'], creds['app_password'])
|
| 53 |
+
)
|
| 54 |
+
response.raise_for_status()
|
| 55 |
+
return {
|
| 56 |
+
"status": "success",
|
| 57 |
+
"message": "Post published successfully!",
|
| 58 |
+
"url": response.json().get('link')
|
| 59 |
+
}
|
| 60 |
+
except requests.exceptions.RequestException as e:
|
| 61 |
+
return {
|
| 62 |
+
"status": "error",
|
| 63 |
+
"message": f"Error publishing post: {str(e)}"
|
| 64 |
+
}
|