juanmaguitar commited on
Commit
9dee4c2
·
1 Parent(s): 072b656

WordPress content agent

Browse files
.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=[final_answer, get_current_time_in_timezone, image_generation_tool,
126
- quick_research, get_weather], # added get_weather tool
 
 
 
 
 
 
 
 
 
 
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
- requests
 
 
 
 
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
+ }