Vel commited on
Commit
17bf943
·
verified ·
1 Parent(s): ae7a494

Update app.py

Browse files

### Detailed Description of `my_custom_tool`

#### Purpose
`my_custom_tool` is a creative and analytical tool designed to generate a fictional psychological portrait of an X (Twitter) user based on their recent online activity. It combines real-world data analysis with imaginative storytelling to produce a poetic, personalized narrative that reflects the user’s inferred personality, emotions, and interests over a specified time period. While originally intended to leverage Grok's native X analysis tools, this version has been adapted for a Hugging Face environment, making it compatible with your existing setup using `smolagents`, `DuckDuckGoSearchTool`, and Hugging Face's NLP models.

The tool aims to:
- **Analyze**: Extract meaningful insights (tone and themes) from publicly available data about an X user.
- **Create**: Transform those insights into a unique, fictionalized psychological profile with a poetic flair.
- **Engage**: Provide an entertaining and thought-provoking output that blends data-driven analysis with artistic expression.

#### Functionality
The tool takes two inputs:
1. **x_username (str)**: The X username of the person to analyze (e.g., "elonmusk").
2. **days_in_past (int)**: The number of days in the past to consider for analysis (limited to 1–30 days).

It performs the following steps:
1. **Input Validation**: Ensures the `days_in_past` value is within the acceptable range (1 to 30). If not, it returns an error message.
2. **Time Range Calculation**: Determines the date range for analysis by calculating the start date (current date minus `days_in_past`) and formatting it as a string (e.g., "from 2025-02-21 to 2025-02-28").
3. **Data Collection**: Since direct access to X posts isn’t available in Hugging Face, it uses `DuckDuckGoSearchTool` to search for recent public snippets related to the user on X (e.g., tweets or mentions). This simulates the collection of activity data.
4. **Text Analysis**:
- **Tone Detection**: Uses a pre-trained Hugging Face sentiment analysis model (`distilbert-base-uncased-finetuned-sst-2-english`) to classify the tone as "positive," "negative," or "neutral" based on the collected text.
- **Theme Extraction**: Employs a zero-shot classification model (`facebook/bart-large-mnli`) to identify dominant themes (e.g., "tech," "humor," "science") from a predefined list of candidate labels.
- **Word Count**: Calculates the total number of words in the collected text to quantify the user’s activity level.
5. **Portrait Generation**: Crafts a poetic narrative based on the analyzed tone, themes, and word count, presenting it as a fictional psychological profile of the user.
6. **Output**: Returns the final portrait as a string, or an error message if no data is found.

#### Implementation Details
The tool is structured into three main functions:

1. **`my_custom_tool(x_username: str, days_in_past: int) -> str`**:
- The main entry point, decorated with `@tool` from `smolagents` to integrate it into your `CodeAgent`.
- Orchestrates the process: validates input, calculates the date range, calls the analysis function, and generates the portrait.
- Handles edge cases, such as no data being found, by returning informative messages.

2. **`analyze_x_activity_with_hf(username: str, days: int) -> dict`**:
- Simulates X activity analysis in a Hugging Face environment:
- Uses `DuckDuckGoSearchTool` to search for snippets with a query like `site:x.com {username} -inurl:(signup | login)`, collecting up to five results.
- Falls back to a generic string if the search fails (e.g., "Recent activity by {username}").
- Analyzes the collected text:
- **Tone**: Applies `sentiment_analyzer` to the first 512 tokens (due to model limits), mapping results to "positive," "negative," or "neutral."
- **Themes**: Uses `topic_classifier` to score a list of candidate labels (`tech`, `politics`, `humor`, etc.), selecting the top two themes with scores above 0.5, or the highest-scoring theme as a fallback.
- **Word Count**: Counts words in the aggregated content.
- Returns a dictionary with the raw content, tone, themes, and word count, or an empty dict if no content is retrieved.

3. **`craft_psychological_portrait(username: str, posts_data: dict, date_range: str) -> str`**:
- Takes the analysis results and constructs a creative narrative.
- Maps the detected tone to one of three poetic styles:
- **Positive**: Describes the user as hopeful and radiant, with words as "sparks of light."
- **Negative**: Portrays a melancholic figure, with words "murmuring like rain."
- **Neutral (or other)**: Presents the user as a curious explorer, with words "unfurling toward the infinite."
- Incorporates the username, date range, themes, and word count into the narrative for personalization.
- Joins the introduction and trait description into a cohesive paragraph.


#### Example Output
For `my_custom_tool("elonmusk", 7)`:
- **Search**: Finds snippets like "Elon Musk tweets about Starship progress."
- **Analysis**: Tone = "positive," themes = ["tech", "science"], word count = 50.
- **Result**:
"@elonmusk, over from 2025-02-21 to 2025-02-28, emerges as a radiant soul, gazing at the world with unyielding hope. Your words weave tech and science into a tapestry of possibility, each of your 50 words a spark of light."

#### Limitations
- **Data Source**: Relies on DuckDuckGo search results rather than direct X API access, which may miss real-time posts or include noise (e.g., unrelated mentions).
- **Text Length**: NLP models are limited to 512 tokens per analysis, truncating longer content.
- **Accuracy**: Sentiment and theme detection depend on the quality of the pre-trained models and the relevance of search results.

#### Why It’s Creative
- **Data-Driven Storytelling**: Merges objective analysis (tone, themes) with subjective, poetic interpretation.
- **Personalization**: Tailors the output to the specific user and time period, making each portrait unique.
- **Hugging Face Fit**: Adapts Grok-like functionality to an open-source environment, leveraging accessible NLP tools creatively.

This tool provides a balance of technical analysis and artistic output, making it both functional and engaging in your Hugging Face-based project.

Files changed (1) hide show
  1. app.py +85 -6
app.py CHANGED
@@ -4,19 +4,98 @@ import requests
4
  import pytz
5
  import yaml
6
  from tools.final_answer import FinalAnswerTool
 
7
 
8
  from Gradio_UI import GradioUI
9
 
10
  # Below is an example of a tool that does nothing. Amaze us with your creativity !
 
 
 
 
 
 
 
 
11
  @tool
12
- def my_custom_tool(arg1:str, arg2:int)-> str: #it's import to specify the return type
13
  #Keep this format for the description / args / args description but feel free to modify the tool
14
- """A tool that does nothing yet
15
  Args:
16
- arg1: the first argument
17
- arg2: the second argument
18
  """
19
- return "What magic will you build ?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  @tool
22
  def get_current_time_in_timezone(timezone: str) -> str:
@@ -55,7 +134,7 @@ with open("prompts.yaml", 'r') as stream:
55
 
56
  agent = CodeAgent(
57
  model=model,
58
- tools=[final_answer], ## add your tools here (don't remove final answer)
59
  max_steps=6,
60
  verbosity_level=1,
61
  grammar=None,
 
4
  import pytz
5
  import yaml
6
  from tools.final_answer import FinalAnswerTool
7
+ from transformers import pipeline # For local NLP analysis
8
 
9
  from Gradio_UI import GradioUI
10
 
11
  # Below is an example of a tool that does nothing. Amaze us with your creativity !
12
+
13
+ # Tools and model already present in the environment
14
+ search_tool = DuckDuckGoSearchTool()
15
+
16
+ # Set up a local NLP pipeline with Hugging Face for text analysis
17
+ sentiment_analyzer = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english")
18
+ topic_classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
19
+
20
  @tool
21
+ def my_custom_tool(x_username: str, days_in_the past:int)-> str: #it's import to specify the return type
22
  #Keep this format for the description / args / args description but feel free to modify the tool
23
+ """A tool that creates a fictional psychological portrait based on an X user's recent activity using Hugging Face tools.
24
  Args:
25
+ x_username: The X username to analyze (e.g., 'elonmusk')
26
+ days_in_past: Number of days in the past to analyze (max 30)
27
  """
28
+ # Check if the number of days is within acceptable range
29
+ if days_in_past < 1 or days_in_past > 30:
30
+ return "Please choose a number of days between 1 and 30."
31
+
32
+ # Calculate the time range
33
+ current_date = datetime.datetime.now()
34
+ start_date = current_date - datetime.timedelta(days=days_in_past)
35
+ date_range = f"from {start_date.strftime('%Y-%m-%d')} to {current_date.strftime('%Y-%m-%d')}"
36
+
37
+ # Analyze available data using adapted tools
38
+ posts_data = analyze_x_activity_with_hf(x_username, days_in_past)
39
+ if not posts_data or not posts_data.get("content"):
40
+ return f"No recent activity data found for @{x_username} in the last {days_in_past} days."
41
+
42
+ # Generate the psychological portrait
43
+ portrait = craft_psychological_portrait(x_username, posts_data, date_range)
44
+ return portrait
45
+
46
+ def analyze_x_activity_with_hf(username: str, days: int) -> dict:
47
+ """Use Hugging Face-compatible tools to analyze X activity."""
48
+ # Search via DuckDuckGo to simulate posts (no direct X access)
49
+ query = f"site:x.com {username} -inurl:(signup | login)"
50
+ try:
51
+ search_results = search_tool(query)
52
+ content = " ".join([result["snippet"] for result in search_results[:5] if result.get("snippet")])
53
+ except Exception:
54
+ content = f"Recent activity by {username}" # Fallback if search fails
55
+
56
+ # Return empty dict if no content is found
57
+ if not content:
58
+ return {}
59
+
60
+ # Analyze tone with DistilBERT
61
+ sentiment = sentiment_analyzer(content[:512])[0] # Limit to 512 tokens
62
+ tone = "positive" if sentiment["label"] == "POSITIVE" else "negative" if sentiment["label"] == "NEGATIVE" else "neutral"
63
+
64
+ # Extract themes with zero-shot classification
65
+ candidate_labels = ["tech", "politics", "humor", "science", "personal", "nature","philosophy"]
66
+ theme_result = topic_classifier(content[:512], candidate_labels, multi_label=False)
67
+ top_themes = [label for label, score in zip(theme_result["labels"], theme_result["scores"]) if score > 0.5][:2]
68
+ if not top_themes:
69
+ top_themes = [theme_result["labels"][0]] # Take the most probable if nothing above 0.5
70
+
71
+ # Count words
72
+ word_count = len(content.split())
73
+
74
+ return {
75
+ "content": content,
76
+ "tone": tone,
77
+ "themes": top_themes,
78
+ "word_count": word_count
79
+ }
80
+
81
+ def craft_psychological_portrait(username: str, posts_data: dict, date_range: str) -> str:
82
+ """Helper function to craft a fictional psychological portrait."""
83
+ tone = posts_data["tone"]
84
+ themes = " and ".join(posts_data["themes"])
85
+ word_count = posts_data["word_count"]
86
+
87
+ # Generate a creative description based on tone
88
+ if tone == "positive":
89
+ intro = f"@{username}, over {date_range}, emerges as a radiant soul, gazing at the world with unyielding hope."
90
+ trait = f"Your words weave {themes} into a tapestry of possibility, each of your {word_count} words a spark of light."
91
+ elif tone == "negative":
92
+ intro = f"@{username}, across {date_range}, walks a quiet path, shadowed by gentle sorrow."
93
+ trait = f"In {themes}, your {word_count} words murmur like rain, painting a world both tender and lost."
94
+ else: # neutral or other
95
+ intro = f"@{username}, within {date_range}, stands as an explorer of the unknown, eyes wide with wonder."
96
+ trait = f"Your {word_count} words chase {themes}, each a question unfurling toward the infinite."
97
+
98
+ return f"{intro} {trait}"
99
 
100
  @tool
101
  def get_current_time_in_timezone(timezone: str) -> str:
 
134
 
135
  agent = CodeAgent(
136
  model=model,
137
+ tools=[final_answer,my_custom_tool, search_tool], # Add compatible HF tools
138
  max_steps=6,
139
  verbosity_level=1,
140
  grammar=None,