Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- CHANGELOG.md +10 -4
- README.md +12 -11
- llm_backend.py +4 -3
- survey_generator.py +62 -57
CHANGELOG.md
CHANGED
|
@@ -10,7 +10,7 @@ All notable changes to ConversAI will be documented in this file.
|
|
| 10 |
- **No API endpoint issues** - everything runs on your Space
|
| 11 |
- **Faster after first load** - models cached in memory
|
| 12 |
- **100% private** - all processing happens locally
|
| 13 |
-
- Default model: **google/flan-t5-
|
| 14 |
- Supports all Flan-T5 variants (base, large, xl, xxl)
|
| 15 |
|
| 16 |
### Added
|
|
@@ -43,10 +43,16 @@ All notable changes to ConversAI will be documented in this file.
|
|
| 43 |
- Added model caching to keep models in memory
|
| 44 |
- Auto-detects CUDA/CPU and optimizes accordingly
|
| 45 |
|
| 46 |
-
- **Default model**: `google/flan-t5-
|
| 47 |
- Changed from API-based to local transformers
|
| 48 |
-
-
|
| 49 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
- **New dependencies added** to requirements.txt:
|
| 52 |
- transformers>=4.36.0
|
|
|
|
| 10 |
- **No API endpoint issues** - everything runs on your Space
|
| 11 |
- **Faster after first load** - models cached in memory
|
| 12 |
- **100% private** - all processing happens locally
|
| 13 |
+
- Default model: **google/flan-t5-large** (1.2GB, good quality)
|
| 14 |
- Supports all Flan-T5 variants (base, large, xl, xxl)
|
| 15 |
|
| 16 |
### Added
|
|
|
|
| 43 |
- Added model caching to keep models in memory
|
| 44 |
- Auto-detects CUDA/CPU and optimizes accordingly
|
| 45 |
|
| 46 |
+
- **Default model**: `google/flan-t5-large` (line 84)
|
| 47 |
- Changed from API-based to local transformers
|
| 48 |
+
- 1.2GB model provides good balance of quality and speed
|
| 49 |
+
- Better at JSON generation than smaller models
|
| 50 |
+
- User can upgrade to xl/xxl or downgrade to base via LLM_MODEL env var
|
| 51 |
+
|
| 52 |
+
- **Improved prompts** in `survey_generator.py`:
|
| 53 |
+
- Simplified prompts for better T5 model compatibility
|
| 54 |
+
- Added fallback survey generation if JSON parsing fails
|
| 55 |
+
- More direct instructions with concrete examples
|
| 56 |
|
| 57 |
- **New dependencies added** to requirements.txt:
|
| 58 |
- transformers>=4.36.0
|
README.md
CHANGED
|
@@ -57,9 +57,10 @@ Battle the blank page, reach global audiences, and uncover insights with AI assi
|
|
| 57 |
|
| 58 |
**✨ Zero configuration needed!** ConversAI works out-of-the-box on HuggingFace Spaces using local model loading.
|
| 59 |
|
| 60 |
-
**Default Model:** google/flan-t5-
|
| 61 |
- ✅ **100% Free** - No API keys, no costs, ever
|
| 62 |
-
- ✅ **
|
|
|
|
| 63 |
- ✅ **No API dependencies** - Runs entirely on your Space's compute
|
| 64 |
- ✅ **Private** - All processing happens locally, nothing sent to external APIs
|
| 65 |
- ✅ **Reliable** - Google's instruction-tuned model, battle-tested
|
|
@@ -77,9 +78,9 @@ You can try different free models by setting the `LLM_MODEL` environment variabl
|
|
| 77 |
|
| 78 |
| Model | Best For | Speed | Quality | Model Size |
|
| 79 |
|-------|----------|-------|---------|------------|
|
| 80 |
-
| **google/flan-t5-base**
|
| 81 |
-
| **google/flan-t5-large** |
|
| 82 |
-
| **google/flan-t5-xl** |
|
| 83 |
| **google/flan-t5-xxl** | Maximum quality | ⚡ Slower | ⭐⭐⭐⭐⭐ Best | 11GB |
|
| 84 |
|
| 85 |
**Note:** Flan-T5 models are Google's instruction-tuned models, specifically designed for following instructions. They run locally with transformers library.
|
|
@@ -102,12 +103,12 @@ LLM_MODEL=google/flan-t5-xl
|
|
| 102 |
|
| 103 |
### Tips for Best Performance with Local Models
|
| 104 |
|
| 105 |
-
1. **
|
| 106 |
-
2. **First load takes time** - Model downloads and loads (~
|
| 107 |
-
3. **Subsequent requests are fast** - Model stays in memory (
|
| 108 |
-
4. **
|
| 109 |
-
5. **
|
| 110 |
-
6. **
|
| 111 |
|
| 112 |
## 📦 Installation
|
| 113 |
|
|
|
|
| 57 |
|
| 58 |
**✨ Zero configuration needed!** ConversAI works out-of-the-box on HuggingFace Spaces using local model loading.
|
| 59 |
|
| 60 |
+
**Default Model:** google/flan-t5-large
|
| 61 |
- ✅ **100% Free** - No API keys, no costs, ever
|
| 62 |
+
- ✅ **Good quality** - 1.2GB model, excellent at following instructions
|
| 63 |
+
- ✅ **Fast after loading** - Typically 3-8 seconds per request after initial load
|
| 64 |
- ✅ **No API dependencies** - Runs entirely on your Space's compute
|
| 65 |
- ✅ **Private** - All processing happens locally, nothing sent to external APIs
|
| 66 |
- ✅ **Reliable** - Google's instruction-tuned model, battle-tested
|
|
|
|
| 78 |
|
| 79 |
| Model | Best For | Speed | Quality | Model Size |
|
| 80 |
|-------|----------|-------|---------|------------|
|
| 81 |
+
| **google/flan-t5-base** | Testing - fastest | ⚡⚡⚡ Very Fast | ⭐⭐ Basic | 250MB |
|
| 82 |
+
| **google/flan-t5-large** (default) | **Recommended** - balanced | ⚡⚡ Fast | ⭐⭐⭐ Good | 1.2GB |
|
| 83 |
+
| **google/flan-t5-xl** | Better quality | ⚡ Medium | ⭐⭐⭐⭐ Excellent | 3GB |
|
| 84 |
| **google/flan-t5-xxl** | Maximum quality | ⚡ Slower | ⭐⭐⭐⭐⭐ Best | 11GB |
|
| 85 |
|
| 86 |
**Note:** Flan-T5 models are Google's instruction-tuned models, specifically designed for following instructions. They run locally with transformers library.
|
|
|
|
| 103 |
|
| 104 |
### Tips for Best Performance with Local Models
|
| 105 |
|
| 106 |
+
1. **Default model (flan-t5-large) is recommended** - Good balance of quality and speed
|
| 107 |
+
2. **First load takes time** - Model downloads and loads (~2-3 minutes for large)
|
| 108 |
+
3. **Subsequent requests are fast** - Model stays in memory (3-8 seconds)
|
| 109 |
+
4. **For simple testing** - Use flan-t5-base (faster loading)
|
| 110 |
+
5. **For best quality** - Use flan-t5-xl or xxl (requires more memory)
|
| 111 |
+
6. **Keep prompts clear** - Simpler outlines work better with smaller models
|
| 112 |
|
| 113 |
## 📦 Installation
|
| 114 |
|
llm_backend.py
CHANGED
|
@@ -78,9 +78,10 @@ class LLMBackend:
|
|
| 78 |
defaults = {
|
| 79 |
LLMProvider.OPENAI: "gpt-4o-mini",
|
| 80 |
LLMProvider.ANTHROPIC: "claude-3-5-sonnet-20241022",
|
| 81 |
-
# Using Flan-T5-
|
| 82 |
-
# For
|
| 83 |
-
|
|
|
|
| 84 |
LLMProvider.LM_STUDIO: "google/gemma-3-27b"
|
| 85 |
}
|
| 86 |
return os.getenv("LLM_MODEL", defaults[self.provider])
|
|
|
|
| 78 |
defaults = {
|
| 79 |
LLMProvider.OPENAI: "gpt-4o-mini",
|
| 80 |
LLMProvider.ANTHROPIC: "claude-3-5-sonnet-20241022",
|
| 81 |
+
# Using Flan-T5-Large - good balance of size (1.2GB) and quality
|
| 82 |
+
# For smaller/faster: google/flan-t5-base (250MB)
|
| 83 |
+
# For better quality: google/flan-t5-xl (3GB) or google/flan-t5-xxl (11GB)
|
| 84 |
+
LLMProvider.HUGGINGFACE: "google/flan-t5-large",
|
| 85 |
LLMProvider.LM_STUDIO: "google/gemma-3-27b"
|
| 86 |
}
|
| 87 |
return os.getenv("LLM_MODEL", defaults[self.provider])
|
survey_generator.py
CHANGED
|
@@ -58,64 +58,22 @@ class SurveyGenerator:
|
|
| 58 |
|
| 59 |
def _get_system_prompt(self) -> str:
|
| 60 |
"""System prompt for survey generation"""
|
| 61 |
-
return """You are
|
| 62 |
-
- Industry best practices for survey design
|
| 63 |
-
- Question formulation techniques (open-ended, closed-ended, Likert scales)
|
| 64 |
-
- Avoiding bias and leading questions
|
| 65 |
-
- Survey flow and respondent experience
|
| 66 |
-
- Research methodologies (interviews, focus groups, ethnographic studies)
|
| 67 |
-
|
| 68 |
-
Your task is to generate professional, well-structured surveys that will yield high-quality research data.
|
| 69 |
-
Follow these principles:
|
| 70 |
-
1. Use clear, unambiguous language
|
| 71 |
-
2. Avoid double-barreled questions
|
| 72 |
-
3. Include a logical flow from general to specific
|
| 73 |
-
4. Balance open-ended and structured questions appropriately
|
| 74 |
-
5. Consider the respondent's cognitive load
|
| 75 |
-
6. Include screening questions when relevant
|
| 76 |
-
7. Add instructions and context where helpful
|
| 77 |
-
|
| 78 |
-
Always respond with valid JSON containing the survey structure."""
|
| 79 |
|
| 80 |
def _build_generation_prompt(self, outline, survey_type, num_questions, target_audience) -> str:
|
| 81 |
"""Build the user prompt for survey generation"""
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
{outline}
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
2. An introduction/welcome message
|
| 95 |
-
3. Well-crafted questions following best practices
|
| 96 |
-
4. Appropriate question types for the research goals
|
| 97 |
-
5. A thank you/closing message
|
| 98 |
-
|
| 99 |
-
Respond with a JSON object in this exact format:
|
| 100 |
-
{{
|
| 101 |
-
"title": "Survey Title",
|
| 102 |
-
"introduction": "Welcome message and instructions",
|
| 103 |
-
"questions": [
|
| 104 |
-
{{
|
| 105 |
-
"id": 1,
|
| 106 |
-
"question_text": "The question to ask",
|
| 107 |
-
"question_type": "open_ended|multiple_choice|likert_scale|yes_no|rating",
|
| 108 |
-
"options": ["option1", "option2"],
|
| 109 |
-
"required": true|false,
|
| 110 |
-
"help_text": "Optional clarification"
|
| 111 |
-
}}
|
| 112 |
-
],
|
| 113 |
-
"closing": "Thank you message"
|
| 114 |
-
}}
|
| 115 |
-
|
| 116 |
-
For open-ended questions, omit the "options" field.
|
| 117 |
-
For multiple choice and Likert questions, include appropriate options.
|
| 118 |
-
Ensure questions follow best practices and are unbiased."""
|
| 119 |
|
| 120 |
def _parse_survey_response(self, response: str) -> Dict:
|
| 121 |
"""Parse LLM response into survey structure"""
|
|
@@ -132,6 +90,12 @@ Ensure questions follow best practices and are unbiased."""
|
|
| 132 |
end = response.find("```", start)
|
| 133 |
response = response[start:end].strip()
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
try:
|
| 136 |
survey_data = json.loads(response)
|
| 137 |
|
|
@@ -147,8 +111,49 @@ Ensure questions follow best practices and are unbiased."""
|
|
| 147 |
|
| 148 |
return survey_data
|
| 149 |
|
| 150 |
-
except json.JSONDecodeError as e:
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
def refine_question(self, question: str, improvement_type: str = "clarity") -> str:
|
| 154 |
"""
|
|
|
|
| 58 |
|
| 59 |
def _get_system_prompt(self) -> str:
|
| 60 |
"""System prompt for survey generation"""
|
| 61 |
+
return """You are a professional survey designer. Create surveys in valid JSON format only."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
def _build_generation_prompt(self, outline, survey_type, num_questions, target_audience) -> str:
|
| 64 |
"""Build the user prompt for survey generation"""
|
| 65 |
+
# For T5 models, we need a very simple, direct instruction
|
| 66 |
+
return f"""Task: Generate a JSON survey.
|
| 67 |
+
|
| 68 |
+
Topic: {outline}
|
| 69 |
+
Questions needed: {num_questions}
|
| 70 |
+
Audience: {target_audience}
|
| 71 |
+
Type: {survey_type}
|
| 72 |
+
|
| 73 |
+
Required JSON format:
|
| 74 |
+
{{"title": "Survey Title Here", "introduction": "Welcome message here", "questions": [{{"id": 1, "question_text": "Your first question?", "question_type": "open_ended", "required": true}}, {{"id": 2, "question_text": "Your second question?", "question_type": "open_ended", "required": true}}], "closing": "Thank you message here"}}
|
| 75 |
+
|
| 76 |
+
Generate the complete survey JSON now:"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
def _parse_survey_response(self, response: str) -> Dict:
|
| 79 |
"""Parse LLM response into survey structure"""
|
|
|
|
| 90 |
end = response.find("```", start)
|
| 91 |
response = response[start:end].strip()
|
| 92 |
|
| 93 |
+
# Try to find JSON object in response
|
| 94 |
+
if "{" in response and "}" in response:
|
| 95 |
+
start = response.find("{")
|
| 96 |
+
end = response.rfind("}") + 1
|
| 97 |
+
response = response[start:end]
|
| 98 |
+
|
| 99 |
try:
|
| 100 |
survey_data = json.loads(response)
|
| 101 |
|
|
|
|
| 111 |
|
| 112 |
return survey_data
|
| 113 |
|
| 114 |
+
except (json.JSONDecodeError, ValueError) as e:
|
| 115 |
+
# Fallback: Try to create a simple survey from the response
|
| 116 |
+
print(f"Warning: JSON parsing failed, attempting fallback. Error: {e}")
|
| 117 |
+
return self._create_fallback_survey(response)
|
| 118 |
+
|
| 119 |
+
def _create_fallback_survey(self, response: str) -> Dict:
|
| 120 |
+
"""Create a basic survey structure from non-JSON response"""
|
| 121 |
+
# Extract potential questions from numbered list
|
| 122 |
+
lines = [line.strip() for line in response.split('\n') if line.strip()]
|
| 123 |
+
|
| 124 |
+
# Look for numbered items or lines with question marks
|
| 125 |
+
questions = []
|
| 126 |
+
question_id = 1
|
| 127 |
+
|
| 128 |
+
for line in lines:
|
| 129 |
+
# Remove leading numbers, bullets, etc.
|
| 130 |
+
clean_line = line.lstrip('0123456789.-) ')
|
| 131 |
+
|
| 132 |
+
# Check if it looks like a question
|
| 133 |
+
if len(clean_line) > 10 and (clean_line.endswith('?') or
|
| 134 |
+
any(word in clean_line.lower() for word in ['what', 'how', 'why', 'when', 'where', 'which', 'would', 'could', 'should', 'do you'])):
|
| 135 |
+
questions.append({
|
| 136 |
+
"id": question_id,
|
| 137 |
+
"question_text": clean_line,
|
| 138 |
+
"question_type": "open_ended",
|
| 139 |
+
"required": True
|
| 140 |
+
})
|
| 141 |
+
question_id += 1
|
| 142 |
+
|
| 143 |
+
# If we didn't find enough questions, create generic ones
|
| 144 |
+
if len(questions) < 3:
|
| 145 |
+
questions = [
|
| 146 |
+
{"id": 1, "question_text": "What are your thoughts on this topic?", "question_type": "open_ended", "required": True},
|
| 147 |
+
{"id": 2, "question_text": "Can you describe your experience?", "question_type": "open_ended", "required": True},
|
| 148 |
+
{"id": 3, "question_text": "What suggestions do you have for improvement?", "question_type": "open_ended", "required": True}
|
| 149 |
+
]
|
| 150 |
+
|
| 151 |
+
return {
|
| 152 |
+
"title": "Survey",
|
| 153 |
+
"introduction": "Thank you for participating in this survey. Please answer the following questions.",
|
| 154 |
+
"questions": questions[:10], # Limit to 10 questions
|
| 155 |
+
"closing": "Thank you for your time and feedback!"
|
| 156 |
+
}
|
| 157 |
|
| 158 |
def refine_question(self, question: str, improvement_type: str = "clarity") -> str:
|
| 159 |
"""
|