Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,99 +2,98 @@ import os
|
|
| 2 |
import logging
|
| 3 |
import gradio as gr
|
| 4 |
from groq import Groq
|
| 5 |
-
from
|
| 6 |
-
import json
|
| 7 |
-
from typing import List, Dict, Tuple
|
| 8 |
|
| 9 |
# --- Basic Configuration ---
|
| 10 |
-
logging.basicConfig(
|
| 11 |
-
level=logging.INFO,
|
| 12 |
-
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 13 |
-
handlers=[logging.StreamHandler()]
|
| 14 |
-
)
|
| 15 |
|
| 16 |
-
# --- The Core Application Logic ---
|
| 17 |
class HumanTouchApp:
|
| 18 |
def __init__(self):
|
| 19 |
self.client = self._initialize_groq_client()
|
| 20 |
self.model = "llama-3.3-70b-versatile"
|
| 21 |
self.system_prompt_template = self._load_system_prompt()
|
| 22 |
-
|
| 23 |
def _initialize_groq_client(self) -> Groq:
|
| 24 |
api_key = os.environ.get("GROQ_API_KEY")
|
| 25 |
if not api_key:
|
| 26 |
-
logging.error("FATAL: GROQ_API_KEY secret not found
|
| 27 |
return None
|
| 28 |
logging.info("Groq client initialized successfully.")
|
| 29 |
return Groq(api_key=api_key)
|
| 30 |
|
| 31 |
def _load_system_prompt(self) -> str:
|
| 32 |
-
#
|
| 33 |
return """
|
| 34 |
You are HumanTouch, an AI specializing in transforming text into emotionally resonant communication.
|
| 35 |
-
Your
|
| 36 |
-
Your output style is guided by two scores:
|
| 37 |
-
1. **Style Score (0-100):** 0 is purely logical and precise. 100 is highly poetic, abstract, and metaphorical.
|
| 38 |
-
2. **Tone Score (0-100):** 0 is formal and stoic. 100 is casual, emotional, and passionate.
|
| 39 |
-
|
| 40 |
-
You will receive a user's text and a set of parameters. Your task is to rewrite the text according to these parameters, embodying the specified persona. Do not explain your process; only provide the transformed text.
|
| 41 |
-
|
| 42 |
-
Here are the Ink Resonance personalities:
|
| 43 |
-
- **Cosmic (Default):** Wise, expansive, slightly mysterious, and thought-provoking.
|
| 44 |
-
- **Ember (Premium):** Passionate, fiery, bold, and direct. Uses strong verbs and evocative imagery.
|
| 45 |
-
- **Crystal (Premium):** Sharp, analytical, precise, and elegant. Values clarity and structure above all.
|
| 46 |
-
- **Verdant (Premium):** Natural, organic, growing, and holistic. Uses metaphors from nature and life cycles.
|
| 47 |
-
"""
|
| 48 |
|
| 49 |
-
|
| 50 |
-
if not self.client:
|
| 51 |
-
chat_history.append([user_text, "## 🔴 Configuration Error\n\nThe `GROQ_API_KEY` is not set on the server. Please contact the Space administrator."])
|
| 52 |
-
return chat_history, ""
|
| 53 |
-
|
| 54 |
-
if not user_text.strip():
|
| 55 |
-
return chat_history, ""
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
upsell_message = f"""
|
| 60 |
-
### Unlock the '{ink_resonance}' Resonance ✨
|
| 61 |
-
The fiery, passionate voice of the **Ember** ink is a premium feature.
|
| 62 |
-
|
| 63 |
-
Upgrading gives you access to all unique AI personalities, allowing you to perfectly match your tone for any occasion.
|
| 64 |
-
|
| 65 |
-
*(For now, I'll respond with the default Cosmic ink.)*
|
| 66 |
-
"""
|
| 67 |
-
chat_history.append([user_text, upsell_message])
|
| 68 |
-
ink_resonance = "Cosmic"
|
| 69 |
-
|
| 70 |
-
dynamic_system_prompt = self.system_prompt_template.format(
|
| 71 |
-
ink_personality=ink_resonance,
|
| 72 |
-
style_score=style_level,
|
| 73 |
-
tone_score=tone_level
|
| 74 |
-
)
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
response = self.client.chat.completions.create(
|
| 83 |
model=self.model,
|
| 84 |
messages=messages,
|
| 85 |
temperature=0.6 + (style_level / 250) + (tone_level / 250),
|
| 86 |
max_tokens=4096,
|
| 87 |
)
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
chat_history.append([user_text, ai_response])
|
| 91 |
-
logging.info("Successfully generated response.")
|
| 92 |
-
|
| 93 |
except Exception as e:
|
| 94 |
logging.error(f"Error during Groq API call: {e}")
|
| 95 |
-
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
return chat_history, ""
|
| 99 |
|
| 100 |
# --- The Gradio User Interface ---
|
|
@@ -105,81 +104,89 @@ def create_interface():
|
|
| 105 |
#main_container { background: #111827; }
|
| 106 |
#header { text-align: center; margin: 20px auto; color: #E5E7EB; }
|
| 107 |
#header h1 { font-size: 2.5rem; font-weight: 600; }
|
| 108 |
-
#header p { font-size: 1.1rem; color: #9CA3AF; }
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
#symbiotic_canvas .user { background-color: transparent !important; color: #F9FAFB; border: 1px solid #4B5563; }
|
| 115 |
#symbiotic_canvas .bot { background-color: transparent !important; color: #A7F3D0; animation: bloom 0.7s ease-in-out; }
|
| 116 |
@keyframes bloom { 0% { opacity: 0; transform: translateY(10px) scale(0.98); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
|
| 117 |
#control_deck { background: #1F2937; padding: 20px; border-radius: 20px; border: 1px solid #374151; }
|
| 118 |
-
#control_deck .gradio-slider label span { color: #D1D5DB; }
|
| 119 |
-
#ink_selector { gap: 1rem; }
|
| 120 |
-
#ink_selector .gradio-radio label {
|
| 121 |
-
background: #374151 !important; color: #E5E7EB !important; padding: 10px 15px !important;
|
| 122 |
-
border-radius: 12px !important; border: 2px solid transparent !important; transition: all 0.2s ease-in-out;
|
| 123 |
-
}
|
| 124 |
-
#ink_selector .gradio-radio label:hover { border-color: #4B5563 !important; }
|
| 125 |
-
#ink_selector .gradio-radio input:checked + label {
|
| 126 |
-
border-color: #34D399 !important; background: #10B981 !important;
|
| 127 |
-
box-shadow: 0 0 15px rgba(16, 185, 129, 0.5);
|
| 128 |
-
}
|
| 129 |
-
#ink_selector .gradio-radio:nth-child(n+2) label:after { content: '🔒'; margin-left: 8px; font-size: 0.8em; }
|
| 130 |
-
#ink_selector .gradio-radio:nth-child(1) label:after { content: '✨'; margin-left: 8px; font-size: 0.8em; }
|
| 131 |
-
#chat_input_row { padding: 10px 0; }
|
| 132 |
#chat_input textarea { background-color: #374151; color: #F9FAFB; border: 1px solid #4B5563; border-radius: 15px !important; }
|
| 133 |
-
#submit_button { background: #10B981; color: white; font-weight: bold; }
|
| 134 |
-
#clear_button { background: #4B5563; }
|
| 135 |
"""
|
| 136 |
|
| 137 |
with gr.Blocks(css=custom_css, theme=gr.themes.Base(), title="HumanTouch") as interface:
|
| 138 |
with gr.Column(elem_id="main_container"):
|
| 139 |
with gr.Row(elem_id="header"):
|
| 140 |
-
gr.Markdown("<h1>✨ HumanTouch</h1><p>
|
| 141 |
-
|
| 142 |
-
with gr.
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
# Event Handling Logic
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
-
#
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
return interface
|
| 179 |
|
| 180 |
|
| 181 |
if __name__ == "__main__":
|
| 182 |
-
logging.info("Launching HumanTouch App with
|
| 183 |
try:
|
| 184 |
interface = create_interface()
|
| 185 |
interface.launch(debug=True)
|
|
|
|
| 2 |
import logging
|
| 3 |
import gradio as gr
|
| 4 |
from groq import Groq
|
| 5 |
+
from typing import List, Tuple
|
|
|
|
|
|
|
| 6 |
|
| 7 |
# --- Basic Configuration ---
|
| 8 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.StreamHandler()])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
+
# --- The Core Application Logic (Now serves both modes) ---
|
| 11 |
class HumanTouchApp:
|
| 12 |
def __init__(self):
|
| 13 |
self.client = self._initialize_groq_client()
|
| 14 |
self.model = "llama-3.3-70b-versatile"
|
| 15 |
self.system_prompt_template = self._load_system_prompt()
|
| 16 |
+
|
| 17 |
def _initialize_groq_client(self) -> Groq:
|
| 18 |
api_key = os.environ.get("GROQ_API_KEY")
|
| 19 |
if not api_key:
|
| 20 |
+
logging.error("FATAL: GROQ_API_KEY secret not found.")
|
| 21 |
return None
|
| 22 |
logging.info("Groq client initialized successfully.")
|
| 23 |
return Groq(api_key=api_key)
|
| 24 |
|
| 25 |
def _load_system_prompt(self) -> str:
|
| 26 |
+
# This one prompt now powers both modes, using context to differentiate tasks.
|
| 27 |
return """
|
| 28 |
You are HumanTouch, an AI specializing in transforming text into emotionally resonant communication.
|
| 29 |
+
Your output style is guided by two scores: Style (0=Logical, 100=Poetic) and Tone (0=Formal, 100=Passionate).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
You will be given a task: either "HUMANIZE" a block of text or "CO-CREATE" from a user's seed phrase.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
**Task: HUMANIZE**
|
| 34 |
+
When given a block of text, your goal is to rewrite it to sound more natural, engaging, and less robotic, according to the provided Style and Tone scores. Retain the core meaning but infuse it with a human touch.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
+
**Task: CO-CREATE**
|
| 37 |
+
When given a short phrase or idea, your goal is to expand and "bloom" it into a more complete, resonant thought, guided by the Style and Tone scores. This is a creative partnership.
|
| 38 |
+
|
| 39 |
+
In both tasks, do not explain your process. Only provide the transformed text.
|
| 40 |
+
"""
|
| 41 |
|
| 42 |
+
def _call_groq_api(self, messages: List[dict], style_level: float, tone_level: float) -> str:
|
| 43 |
+
if not self.client:
|
| 44 |
+
return "## 🔴 Configuration Error\n\nThe `GROQ_API_KEY` is not set on the server. Please contact the Space administrator."
|
| 45 |
+
try:
|
| 46 |
+
logging.info(f"Calling Groq API with Style: {style_level}, Tone: {tone_level}")
|
| 47 |
response = self.client.chat.completions.create(
|
| 48 |
model=self.model,
|
| 49 |
messages=messages,
|
| 50 |
temperature=0.6 + (style_level / 250) + (tone_level / 250),
|
| 51 |
max_tokens=4096,
|
| 52 |
)
|
| 53 |
+
return response.choices[0].message.content.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
except Exception as e:
|
| 55 |
logging.error(f"Error during Groq API call: {e}")
|
| 56 |
+
return f"### ⚠️ An Error Occurred\n\nThere was an issue connecting to the AI. Please try again shortly. \n\n*Details: {str(e)}*"
|
| 57 |
+
|
| 58 |
+
# Method for the "Humanizer" Tab
|
| 59 |
+
def humanize_block_text(self, text_to_humanize: str, style_level: float, tone_level: float) -> str:
|
| 60 |
+
if not text_to_humanize.strip():
|
| 61 |
+
return "Please paste some AI-generated text to get started."
|
| 62 |
+
|
| 63 |
+
user_content = f"""
|
| 64 |
+
TASK: HUMANIZE
|
| 65 |
+
Style Score: {style_level}
|
| 66 |
+
Tone Score: {tone_level}
|
| 67 |
+
---
|
| 68 |
+
TEXT TO HUMANIZE:
|
| 69 |
+
{text_to_humanize}
|
| 70 |
+
"""
|
| 71 |
+
messages = [
|
| 72 |
+
{"role": "system", "content": self.system_prompt_template},
|
| 73 |
+
{"role": "user", "content": user_content}
|
| 74 |
+
]
|
| 75 |
+
return self._call_groq_api(messages, style_level, tone_level)
|
| 76 |
+
|
| 77 |
+
# Method for the "Co-Creative Canvas" Tab
|
| 78 |
+
def generate_co_creation(self, user_text: str, chat_history: List[List[str]], style_level: float, tone_level: float) -> Tuple[List[List[str]], str]:
|
| 79 |
+
if not user_text.strip():
|
| 80 |
+
return chat_history, ""
|
| 81 |
|
| 82 |
+
user_content = f"""
|
| 83 |
+
TASK: CO-CREATE
|
| 84 |
+
Style Score: {style_level}
|
| 85 |
+
Tone Score: {tone_level}
|
| 86 |
+
---
|
| 87 |
+
SEED PHRASE:
|
| 88 |
+
{user_text}
|
| 89 |
+
"""
|
| 90 |
+
messages = [
|
| 91 |
+
{"role": "system", "content": self.system_prompt_template},
|
| 92 |
+
{"role": "user", "content": user_content}
|
| 93 |
+
]
|
| 94 |
+
|
| 95 |
+
ai_response = self._call_groq_api(messages, style_level, tone_level)
|
| 96 |
+
chat_history.append([user_text, ai_response])
|
| 97 |
return chat_history, ""
|
| 98 |
|
| 99 |
# --- The Gradio User Interface ---
|
|
|
|
| 104 |
#main_container { background: #111827; }
|
| 105 |
#header { text-align: center; margin: 20px auto; color: #E5E7EB; }
|
| 106 |
#header h1 { font-size: 2.5rem; font-weight: 600; }
|
| 107 |
+
#header p { font-size: 1.1rem; color: #9CA3AF; margin-bottom: 20px;}
|
| 108 |
+
.gradio-tabs { background-color: #1F2937; border-radius: 20px !important; border: 1px solid #374151; }
|
| 109 |
+
.tab-buttons button { background-color: #1F2937 !important; color: #9CA3AF !important; border-radius: 10px 10px 0 0 !important; }
|
| 110 |
+
.tab-buttons button.selected { background-color: #374151 !important; color: white !important; }
|
| 111 |
+
|
| 112 |
+
/* --- Humanizer Tab Styles --- */
|
| 113 |
+
#humanizer_input, #humanizer_output { background-color: #374151; color: #F9FAFB; border: 1px solid #4B5563; border-radius: 15px !important; min-height: 50vh;}
|
| 114 |
+
#humanize_btn { background: #10B981; color: white; font-weight: bold; padding: 15px 0; font-size: 1.2rem; }
|
| 115 |
+
|
| 116 |
+
/* --- Canvas Tab Styles --- */
|
| 117 |
+
#symbiotic_canvas { background: #1F2937; border-radius: 20px; box-shadow: inset 0 2px 4px 0 rgba(0,0,0,0.5); height: 65vh !important; border: 1px solid #374151; }
|
| 118 |
#symbiotic_canvas .user { background-color: transparent !important; color: #F9FAFB; border: 1px solid #4B5563; }
|
| 119 |
#symbiotic_canvas .bot { background-color: transparent !important; color: #A7F3D0; animation: bloom 0.7s ease-in-out; }
|
| 120 |
@keyframes bloom { 0% { opacity: 0; transform: translateY(10px) scale(0.98); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
|
| 121 |
#control_deck { background: #1F2937; padding: 20px; border-radius: 20px; border: 1px solid #374151; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
#chat_input textarea { background-color: #374151; color: #F9FAFB; border: 1px solid #4B5563; border-radius: 15px !important; }
|
|
|
|
|
|
|
| 123 |
"""
|
| 124 |
|
| 125 |
with gr.Blocks(css=custom_css, theme=gr.themes.Base(), title="HumanTouch") as interface:
|
| 126 |
with gr.Column(elem_id="main_container"):
|
| 127 |
with gr.Row(elem_id="header"):
|
| 128 |
+
gr.Markdown("<h1>✨ HumanTouch</h1><p>Transform robotic text or co-create with a symbiotic AI partner. Choose your mode below.</p>")
|
| 129 |
+
|
| 130 |
+
with gr.Tabs() as tabs:
|
| 131 |
+
# --- TAB 1: The Humanizer ---
|
| 132 |
+
with gr.Tab("Humanizer", id="humanizer_tab"):
|
| 133 |
+
with gr.Column():
|
| 134 |
+
gr.Markdown("### 🤖➡️🧑 Humanize AI Text\n<p>Paste your text, adjust the resonance, and see it transformed.</p>")
|
| 135 |
+
with gr.Row():
|
| 136 |
+
with gr.Column(scale=2):
|
| 137 |
+
input_text_humanizer = gr.Textbox(label="Paste AI Text Here", lines=20, elem_id="humanizer_input")
|
| 138 |
+
with gr.Column(scale=2):
|
| 139 |
+
output_text_humanizer = gr.Textbox(label="Humanized Result", lines=20, interactive=False, elem_id="humanizer_output")
|
| 140 |
+
with gr.Row():
|
| 141 |
+
with gr.Column(scale=1):
|
| 142 |
+
style_slider_h = gr.Slider(0, 100, 50, label="Style (Logical <-> Poetic)")
|
| 143 |
+
with gr.Column(scale=1):
|
| 144 |
+
tone_slider_h = gr.Slider(0, 100, 50, label="Tone (Formal <-> Passionate)")
|
| 145 |
+
humanize_button = gr.Button("Humanize ✨", variant="primary", elem_id="humanize_btn")
|
| 146 |
+
gr.Examples(
|
| 147 |
+
[["The system's analysis concluded that the optimal parameters were achieved through iterative processing."],
|
| 148 |
+
["Our team will action the deliverables post-haste to synergize our market position."]],
|
| 149 |
+
inputs=[input_text_humanizer], label="Try an Example"
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
# --- TAB 2: The Co-Creative Canvas ---
|
| 153 |
+
with gr.Tab("Co-Creative Canvas", id="canvas_tab"):
|
| 154 |
+
with gr.Row(equal_height=False):
|
| 155 |
+
with gr.Column(scale=3):
|
| 156 |
+
chatbot = gr.Chatbot([], label="Symbiotic Canvas", elem_id="symbiotic_canvas", bubble_full_width=True, avatar_images=(None, "https://i.imgur.com/Q6Zz3Jz.png"))
|
| 157 |
+
with gr.Row(elem_id="chat_input_row"):
|
| 158 |
+
chat_input = gr.Textbox(placeholder="Start a thought and press Enter...", show_label=False, container=False, scale=4)
|
| 159 |
+
clear_btn = gr.Button("New Canvas", scale=1)
|
| 160 |
+
with gr.Column(scale=1, elem_id="control_deck"):
|
| 161 |
+
gr.Markdown("### Resonance Controls")
|
| 162 |
+
style_slider_c = gr.Slider(0, 100, 50, label="Style", info="Logical <-> Poetic")
|
| 163 |
+
tone_slider_c = gr.Slider(0, 100, 50, label="Tone", info="Formal <-> Passionate")
|
| 164 |
+
gr.Examples(
|
| 165 |
+
["The city at night", "I'm not sure how to start this email.", "Let's brainstorm names for the new project."],
|
| 166 |
+
inputs=[chat_input], label="Try a Seed Phrase"
|
| 167 |
+
)
|
| 168 |
|
| 169 |
# Event Handling Logic
|
| 170 |
+
# Humanizer Tab
|
| 171 |
+
humanize_button.click(
|
| 172 |
+
app.humanize_block_text,
|
| 173 |
+
[input_text_humanizer, style_slider_h, tone_slider_h],
|
| 174 |
+
[output_text_humanizer]
|
| 175 |
+
)
|
| 176 |
|
| 177 |
+
# Canvas Tab
|
| 178 |
+
chat_input.submit(
|
| 179 |
+
app.generate_co_creation,
|
| 180 |
+
[chat_input, chatbot, style_slider_c, tone_slider_c],
|
| 181 |
+
[chatbot, chat_input]
|
| 182 |
+
)
|
| 183 |
+
clear_btn.click(lambda: ([], ""), [], [chatbot, chat_input])
|
| 184 |
|
| 185 |
return interface
|
| 186 |
|
| 187 |
|
| 188 |
if __name__ == "__main__":
|
| 189 |
+
logging.info("Launching HumanTouch App with Blended UI...")
|
| 190 |
try:
|
| 191 |
interface = create_interface()
|
| 192 |
interface.launch(debug=True)
|