cite-agent 1.3.9__py3-none-any.whl → 1.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cite_agent/__init__.py +13 -13
- cite_agent/__version__.py +1 -1
- cite_agent/action_first_mode.py +150 -0
- cite_agent/adaptive_providers.py +413 -0
- cite_agent/archive_api_client.py +186 -0
- cite_agent/auth.py +0 -1
- cite_agent/auto_expander.py +70 -0
- cite_agent/cache.py +379 -0
- cite_agent/circuit_breaker.py +370 -0
- cite_agent/citation_network.py +377 -0
- cite_agent/cli.py +8 -16
- cite_agent/cli_conversational.py +113 -3
- cite_agent/confidence_calibration.py +381 -0
- cite_agent/deduplication.py +325 -0
- cite_agent/enhanced_ai_agent.py +689 -371
- cite_agent/error_handler.py +228 -0
- cite_agent/execution_safety.py +329 -0
- cite_agent/full_paper_reader.py +239 -0
- cite_agent/observability.py +398 -0
- cite_agent/offline_mode.py +348 -0
- cite_agent/paper_comparator.py +368 -0
- cite_agent/paper_summarizer.py +420 -0
- cite_agent/pdf_extractor.py +350 -0
- cite_agent/proactive_boundaries.py +266 -0
- cite_agent/quality_gate.py +442 -0
- cite_agent/request_queue.py +390 -0
- cite_agent/response_enhancer.py +257 -0
- cite_agent/response_formatter.py +458 -0
- cite_agent/response_pipeline.py +295 -0
- cite_agent/response_style_enhancer.py +259 -0
- cite_agent/self_healing.py +418 -0
- cite_agent/similarity_finder.py +524 -0
- cite_agent/streaming_ui.py +13 -9
- cite_agent/thinking_blocks.py +308 -0
- cite_agent/tool_orchestrator.py +416 -0
- cite_agent/trend_analyzer.py +540 -0
- cite_agent/unpaywall_client.py +226 -0
- {cite_agent-1.3.9.dist-info → cite_agent-1.4.3.dist-info}/METADATA +15 -1
- cite_agent-1.4.3.dist-info/RECORD +62 -0
- cite_agent-1.3.9.dist-info/RECORD +0 -32
- {cite_agent-1.3.9.dist-info → cite_agent-1.4.3.dist-info}/WHEEL +0 -0
- {cite_agent-1.3.9.dist-info → cite_agent-1.4.3.dist-info}/entry_points.txt +0 -0
- {cite_agent-1.3.9.dist-info → cite_agent-1.4.3.dist-info}/licenses/LICENSE +0 -0
- {cite_agent-1.3.9.dist-info → cite_agent-1.4.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Response Pipeline - Comprehensive Quality Processing
|
|
3
|
+
Integrates all quality improvements into a single pipeline
|
|
4
|
+
|
|
5
|
+
This is the "intelligence layer" that was missing
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from .error_handler import GracefulErrorHandler
|
|
13
|
+
from .response_formatter import ResponseFormatter
|
|
14
|
+
from .quality_gate import ResponseQualityGate, QualityAssessment
|
|
15
|
+
from .response_enhancer import ResponseEnhancer
|
|
16
|
+
from .response_style_enhancer import ResponseStyleEnhancer
|
|
17
|
+
from .action_first_mode import ActionFirstMode
|
|
18
|
+
from .auto_expander import AutoExpander
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ProcessedResponse:
|
|
25
|
+
"""Result of pipeline processing"""
|
|
26
|
+
final_response: str
|
|
27
|
+
quality_score: float
|
|
28
|
+
improvements_applied: list
|
|
29
|
+
processing_notes: list
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ResponsePipeline:
|
|
33
|
+
"""
|
|
34
|
+
End-to-end response processing pipeline
|
|
35
|
+
|
|
36
|
+
Steps:
|
|
37
|
+
1. Clean any technical errors
|
|
38
|
+
2. Apply appropriate formatting
|
|
39
|
+
3. Assess quality
|
|
40
|
+
4. Improve if needed
|
|
41
|
+
5. Final safety check
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
async def process(
|
|
46
|
+
cls,
|
|
47
|
+
raw_response: str,
|
|
48
|
+
query: str,
|
|
49
|
+
context: Dict[str, Any],
|
|
50
|
+
response_type: str = "generic"
|
|
51
|
+
) -> ProcessedResponse:
|
|
52
|
+
"""
|
|
53
|
+
Process a response through the quality pipeline
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
raw_response: The initial response from LLM
|
|
57
|
+
query: The original user query
|
|
58
|
+
context: Additional context (tools used, API results, etc.)
|
|
59
|
+
response_type: Type of response (greeting, file_list, data, code, etc.)
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
ProcessedResponse with final polished response and metadata
|
|
63
|
+
"""
|
|
64
|
+
improvements = []
|
|
65
|
+
notes = []
|
|
66
|
+
|
|
67
|
+
# Step 1: Clean technical errors (safety first!)
|
|
68
|
+
cleaned_response = GracefulErrorHandler.wrap_response_with_error_handling(raw_response)
|
|
69
|
+
|
|
70
|
+
if cleaned_response != raw_response:
|
|
71
|
+
improvements.append("Removed technical error messages")
|
|
72
|
+
notes.append(f"Cleaned {len(raw_response) - len(cleaned_response)} chars of technical details")
|
|
73
|
+
|
|
74
|
+
# Step 2: Apply appropriate formatting based on response type
|
|
75
|
+
formatted_response = cls._apply_smart_formatting(
|
|
76
|
+
cleaned_response,
|
|
77
|
+
query,
|
|
78
|
+
context,
|
|
79
|
+
response_type
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if formatted_response != cleaned_response:
|
|
83
|
+
improvements.append("Applied smart formatting")
|
|
84
|
+
|
|
85
|
+
# Step 3: Assess quality
|
|
86
|
+
assessment = ResponseQualityGate.assess(formatted_response, query, context)
|
|
87
|
+
|
|
88
|
+
notes.append(f"Quality score: {assessment.overall_score:.2f}")
|
|
89
|
+
if assessment.issues:
|
|
90
|
+
notes.append(f"Issues: {', '.join(assessment.issues[:3])}")
|
|
91
|
+
|
|
92
|
+
# Step 4: Apply automatic improvements
|
|
93
|
+
if assessment.should_retry or assessment.overall_score < 0.75:
|
|
94
|
+
improved_response = ResponseQualityGate.improve_response(
|
|
95
|
+
formatted_response,
|
|
96
|
+
assessment,
|
|
97
|
+
query
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if improved_response != formatted_response:
|
|
101
|
+
improvements.append("Applied automatic quality improvements")
|
|
102
|
+
formatted_response = improved_response
|
|
103
|
+
|
|
104
|
+
# Re-assess after improvements
|
|
105
|
+
new_assessment = ResponseQualityGate.assess(improved_response, query, context)
|
|
106
|
+
notes.append(f"Improved quality: {new_assessment.overall_score:.2f}")
|
|
107
|
+
assessment = new_assessment
|
|
108
|
+
|
|
109
|
+
# Step 4.5: ENHANCE to push quality even higher
|
|
110
|
+
if assessment.overall_score < 0.80:
|
|
111
|
+
enhanced_response = ResponseEnhancer.enhance(
|
|
112
|
+
formatted_response,
|
|
113
|
+
query,
|
|
114
|
+
context
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if enhanced_response != formatted_response:
|
|
118
|
+
improvements.append("Enhanced for higher quality")
|
|
119
|
+
formatted_response = enhanced_response
|
|
120
|
+
|
|
121
|
+
# Re-assess after enhancement
|
|
122
|
+
final_assessment = ResponseQualityGate.assess(enhanced_response, query, context)
|
|
123
|
+
notes.append(f"Enhanced quality: {final_assessment.overall_score:.2f}")
|
|
124
|
+
assessment = final_assessment
|
|
125
|
+
|
|
126
|
+
# Step 4.7: STYLE ENHANCEMENT - Make responses pleasant and stylish
|
|
127
|
+
# This is what makes responses ACTUALLY GOOD, not just functional
|
|
128
|
+
styled_response = ResponseStyleEnhancer.enhance(
|
|
129
|
+
formatted_response,
|
|
130
|
+
query,
|
|
131
|
+
context
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if styled_response != formatted_response:
|
|
135
|
+
improvements.append("Enhanced style to be pleasant and friendly")
|
|
136
|
+
notes.append("Applied style enhancements: warm, natural")
|
|
137
|
+
formatted_response = styled_response
|
|
138
|
+
|
|
139
|
+
# Step 4.8: ACTION-FIRST MODE - Remove asking phrases, prioritize action over talk
|
|
140
|
+
# User wants agent to DO things, not ask permission
|
|
141
|
+
action_first_response = ActionFirstMode.make_action_first(
|
|
142
|
+
formatted_response,
|
|
143
|
+
query,
|
|
144
|
+
context
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if action_first_response != formatted_response:
|
|
148
|
+
improvements.append("Transformed to action-first mode")
|
|
149
|
+
notes.append("Removed asking phrases - agent shows results proactively")
|
|
150
|
+
formatted_response = action_first_response
|
|
151
|
+
|
|
152
|
+
# Step 4.9: AUTO-EXPANSION CHECK - Detect if response needs more detail
|
|
153
|
+
# This is a quality check - if it detects expansion needed, it means
|
|
154
|
+
# the LLM didn't follow action-first guidelines properly
|
|
155
|
+
checked_response = AutoExpander.expand(formatted_response, query, context)
|
|
156
|
+
# (This currently just logs warnings if expansion is detected as needed)
|
|
157
|
+
|
|
158
|
+
# Step 5: Final safety check
|
|
159
|
+
final_response = GracefulErrorHandler.wrap_response_with_error_handling(checked_response)
|
|
160
|
+
|
|
161
|
+
return ProcessedResponse(
|
|
162
|
+
final_response=final_response,
|
|
163
|
+
quality_score=assessment.overall_score,
|
|
164
|
+
improvements_applied=improvements,
|
|
165
|
+
processing_notes=notes
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
@classmethod
|
|
169
|
+
def _apply_smart_formatting(
|
|
170
|
+
cls,
|
|
171
|
+
response: str,
|
|
172
|
+
query: str,
|
|
173
|
+
context: Dict[str, Any],
|
|
174
|
+
response_type: str
|
|
175
|
+
) -> str:
|
|
176
|
+
"""
|
|
177
|
+
Apply intelligent formatting based on response type and context
|
|
178
|
+
"""
|
|
179
|
+
# Detect response type if not specified
|
|
180
|
+
if response_type == "generic":
|
|
181
|
+
response_type = cls._detect_response_type(response, query, context)
|
|
182
|
+
|
|
183
|
+
# Apply appropriate formatter
|
|
184
|
+
if response_type == "greeting":
|
|
185
|
+
# Greetings should be brief and friendly
|
|
186
|
+
if len(response.split()) > 30:
|
|
187
|
+
return ResponseFormatter.format_greeting(query)
|
|
188
|
+
return response
|
|
189
|
+
|
|
190
|
+
elif response_type == "acknowledgment":
|
|
191
|
+
# Thanks/acknowledgments should be brief
|
|
192
|
+
if len(response.split()) > 20:
|
|
193
|
+
return ResponseFormatter.format_acknowledgment(query)
|
|
194
|
+
return response
|
|
195
|
+
|
|
196
|
+
elif response_type == "file_list":
|
|
197
|
+
# File listings should be structured
|
|
198
|
+
# Extract file paths from response
|
|
199
|
+
import re
|
|
200
|
+
file_pattern = r'[\/\w\-\.]+\.[\w]+'
|
|
201
|
+
files = re.findall(file_pattern, response)
|
|
202
|
+
|
|
203
|
+
if files:
|
|
204
|
+
return ResponseFormatter.format_file_listing(files, query)
|
|
205
|
+
return response
|
|
206
|
+
|
|
207
|
+
elif response_type == "clarification":
|
|
208
|
+
# Clarifications should have bullets
|
|
209
|
+
# Extract options if present
|
|
210
|
+
lines = response.split('\n')
|
|
211
|
+
options = [line.strip('- •*').strip() for line in lines if line.strip().startswith(('-', '•', '*'))]
|
|
212
|
+
|
|
213
|
+
if not options:
|
|
214
|
+
# No bullets found - check for sentence-based options
|
|
215
|
+
sentences = response.split('.')
|
|
216
|
+
if len(sentences) >= 2:
|
|
217
|
+
# Try to extract options from sentences
|
|
218
|
+
options = [s.strip() + '.' for s in sentences[1:4] if len(s.strip()) > 10]
|
|
219
|
+
|
|
220
|
+
if options:
|
|
221
|
+
question = lines[0] if lines else "What would you like to focus on?"
|
|
222
|
+
return ResponseFormatter.format_clarification(question, options)
|
|
223
|
+
return response
|
|
224
|
+
|
|
225
|
+
elif response_type == "code":
|
|
226
|
+
# Code should have structure (summary + code + offer)
|
|
227
|
+
return response # Already should be formatted
|
|
228
|
+
|
|
229
|
+
elif response_type == "shell_output":
|
|
230
|
+
# Shell output should be clean and focused
|
|
231
|
+
output_type = context.get('shell_output_type', 'generic')
|
|
232
|
+
command = context.get('command', '')
|
|
233
|
+
output = context.get('output', response)
|
|
234
|
+
|
|
235
|
+
return ResponseFormatter.format_shell_output(command, output, output_type)
|
|
236
|
+
|
|
237
|
+
# Default: apply progressive disclosure if too long
|
|
238
|
+
elif len(response) > 600:
|
|
239
|
+
return ResponseFormatter.apply_progressive_disclosure(response, "generic", 500)
|
|
240
|
+
|
|
241
|
+
return response
|
|
242
|
+
|
|
243
|
+
@classmethod
|
|
244
|
+
def _detect_response_type(cls, response: str, query: str, context: Dict[str, Any]) -> str:
|
|
245
|
+
"""
|
|
246
|
+
Detect what type of response this is
|
|
247
|
+
"""
|
|
248
|
+
query_lower = query.lower()
|
|
249
|
+
response_lower = response.lower()
|
|
250
|
+
|
|
251
|
+
# Greeting
|
|
252
|
+
if any(word in query_lower for word in ['hi', 'hey', 'hello']) and len(query.split()) <= 3:
|
|
253
|
+
return "greeting"
|
|
254
|
+
|
|
255
|
+
# Acknowledgment
|
|
256
|
+
if any(word in query_lower for word in ['thanks', 'thank you', 'thx']):
|
|
257
|
+
return "acknowledgment"
|
|
258
|
+
|
|
259
|
+
# File listing
|
|
260
|
+
if ('file' in query_lower or 'directory' in query_lower or 'folder' in query_lower):
|
|
261
|
+
if any(ext in response for ext in ['.py', '.js', '.md', '.txt', '.json', '.csv']):
|
|
262
|
+
return "file_list"
|
|
263
|
+
|
|
264
|
+
# Clarification
|
|
265
|
+
if '?' in response and any(word in response_lower for word in ['which', 'what kind', 'tell me more']):
|
|
266
|
+
return "clarification"
|
|
267
|
+
|
|
268
|
+
# Code
|
|
269
|
+
if '```' in response or 'def ' in response or 'class ' in response:
|
|
270
|
+
return "code"
|
|
271
|
+
|
|
272
|
+
# Shell output
|
|
273
|
+
if context.get('tools_used') and 'shell_execution' in context.get('tools_used', []):
|
|
274
|
+
return "shell_output"
|
|
275
|
+
|
|
276
|
+
return "generic"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# Convenience function
|
|
280
|
+
async def process_response(
|
|
281
|
+
response: str,
|
|
282
|
+
query: str,
|
|
283
|
+
context: Dict[str, Any] = None,
|
|
284
|
+
response_type: str = "generic"
|
|
285
|
+
) -> str:
|
|
286
|
+
"""
|
|
287
|
+
Quick response processing - returns just the final response string
|
|
288
|
+
"""
|
|
289
|
+
result = await ResponsePipeline.process(
|
|
290
|
+
response,
|
|
291
|
+
query,
|
|
292
|
+
context or {},
|
|
293
|
+
response_type
|
|
294
|
+
)
|
|
295
|
+
return result.final_response
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Response Style Enhancer - Make responses PLEASANT and STYLISH
|
|
3
|
+
|
|
4
|
+
Transforms robotic, formal responses into warm, friendly, conversational ones
|
|
5
|
+
Target: 0.80+ style score on all responses
|
|
6
|
+
|
|
7
|
+
Key transformations:
|
|
8
|
+
1. Warm greetings instead of formal ones
|
|
9
|
+
2. Natural language instead of robotic phrasing
|
|
10
|
+
3. Elegant formatting with bullets and structure
|
|
11
|
+
4. Anticipatory offers to help more
|
|
12
|
+
5. Personality and friendliness
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
from typing import Dict, Any, List, Tuple
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResponseStyleEnhancer:
|
|
23
|
+
"""
|
|
24
|
+
Enhances response style to be pleasant and stylish
|
|
25
|
+
|
|
26
|
+
Focus areas:
|
|
27
|
+
1. WARMTH - Replace cold/formal with friendly/warm
|
|
28
|
+
2. NATURAL - Replace robotic with conversational
|
|
29
|
+
3. ELEGANT - Add beautiful formatting
|
|
30
|
+
4. ANTICIPATORY - Offer to help with next steps
|
|
31
|
+
5. PERSONALITY - Make it feel like a smart friend
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Formal → Warm transformations
|
|
35
|
+
GREETING_TRANSFORMS = {
|
|
36
|
+
r'^Hello\.\s*How can I assist you today\?': 'Hi there! Ready to help - what can I dig into for you?',
|
|
37
|
+
r'^Hello\.\s*': 'Hi! ',
|
|
38
|
+
r'^Greetings\.\s*': 'Hey there! ',
|
|
39
|
+
r'^Good (morning|afternoon|evening)\.\s*': 'Hi! ',
|
|
40
|
+
r'How may I help you\?': 'What can I help you with?',
|
|
41
|
+
r'How can I assist you\?': 'What would you like to know?',
|
|
42
|
+
r'Is there anything else\?': 'Need anything else?',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Robotic → Natural transformations
|
|
46
|
+
NATURAL_TRANSFORMS = {
|
|
47
|
+
r'I have analyzed': "I've looked at",
|
|
48
|
+
r'I have determined': "I found",
|
|
49
|
+
r'I have located': "I found",
|
|
50
|
+
r'I have identified': "I see",
|
|
51
|
+
r'I have processed': "I've checked",
|
|
52
|
+
r'I have executed': "I ran",
|
|
53
|
+
r'Processing complete': "All done",
|
|
54
|
+
r'Execution successful': "Got it",
|
|
55
|
+
r'Operation completed': "Done",
|
|
56
|
+
r'Please note that': "Note:",
|
|
57
|
+
r'It should be noted': "Keep in mind",
|
|
58
|
+
r'Additionally,': "Also,",
|
|
59
|
+
r'Furthermore,': "Plus,",
|
|
60
|
+
r'However,': "But",
|
|
61
|
+
r'Therefore,': "So",
|
|
62
|
+
r'Subsequently,': "Then",
|
|
63
|
+
r'You are welcome': "Happy to help! Let me know if you need anything else",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Cold → Warm phrases
|
|
67
|
+
WARM_TRANSFORMS = {
|
|
68
|
+
r'You must': 'You should',
|
|
69
|
+
r'You need to': "You'll want to",
|
|
70
|
+
r'It is required': "You'll need to",
|
|
71
|
+
r'It is necessary': "You'll want to",
|
|
72
|
+
r'Error:': 'Hmm,',
|
|
73
|
+
r'Failed to': "Couldn't",
|
|
74
|
+
r'Unable to': "Can't",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Add anticipatory phrases based on context
|
|
78
|
+
ANTICIPATORY_PATTERNS = [
|
|
79
|
+
(r'(I found|Here are|Here\'s)\s+(\d+)\s+(files?|items?|results?)',
|
|
80
|
+
lambda m: f"{m.group(0)}\n\nWant me to show you what's in any of these?"),
|
|
81
|
+
(r'(The|This)\s+(file|code|function)\s+(\w+)',
|
|
82
|
+
lambda m: f"{m.group(0)}. Want me to walk through how it works?"),
|
|
83
|
+
(r'(Here\'s|Here is)\s+(what|how|the)',
|
|
84
|
+
lambda m: f"{m.group(0)}"), # Keep as is, will add offer at end
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def enhance(cls, response: str, query: str, context: Dict[str, Any]) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Enhance response style to be pleasant and stylish
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
response: Original response
|
|
94
|
+
query: User's query
|
|
95
|
+
context: Context including tools, data, etc.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Enhanced stylish response
|
|
99
|
+
"""
|
|
100
|
+
if not response or len(response) < 5:
|
|
101
|
+
return response
|
|
102
|
+
|
|
103
|
+
enhanced = response
|
|
104
|
+
|
|
105
|
+
# Step 1: Make greetings warm and friendly
|
|
106
|
+
enhanced = cls._enhance_greetings(enhanced, query)
|
|
107
|
+
|
|
108
|
+
# Step 2: Make language natural instead of robotic
|
|
109
|
+
enhanced = cls._make_natural(enhanced)
|
|
110
|
+
|
|
111
|
+
# Step 3: Add warmth
|
|
112
|
+
enhanced = cls._add_warmth(enhanced)
|
|
113
|
+
|
|
114
|
+
# Step 4: Improve formatting to be elegant
|
|
115
|
+
enhanced = cls._make_elegant(enhanced, context)
|
|
116
|
+
|
|
117
|
+
# Step 5: Add anticipatory offers
|
|
118
|
+
enhanced = cls._add_anticipatory_offers(enhanced, query, context)
|
|
119
|
+
|
|
120
|
+
# Step 6: Add personality touches
|
|
121
|
+
enhanced = cls._add_personality(enhanced, query)
|
|
122
|
+
|
|
123
|
+
return enhanced
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def _enhance_greetings(cls, response: str, query: str) -> str:
|
|
127
|
+
"""Replace formal greetings with warm friendly ones"""
|
|
128
|
+
# Check if query is a greeting
|
|
129
|
+
query_lower = query.lower().strip()
|
|
130
|
+
is_greeting = any(g in query_lower for g in ['hi', 'hello', 'hey', 'greetings'])
|
|
131
|
+
|
|
132
|
+
if is_greeting and len(query_lower) < 20:
|
|
133
|
+
# Replace formal greeting responses
|
|
134
|
+
for pattern, replacement in cls.GREETING_TRANSFORMS.items():
|
|
135
|
+
response = re.sub(pattern, replacement, response, flags=re.IGNORECASE)
|
|
136
|
+
|
|
137
|
+
return response
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def _make_natural(cls, response: str) -> str:
|
|
141
|
+
"""Replace robotic phrasing with natural conversation"""
|
|
142
|
+
enhanced = response
|
|
143
|
+
|
|
144
|
+
for pattern, replacement in cls.NATURAL_TRANSFORMS.items():
|
|
145
|
+
enhanced = re.sub(pattern, replacement, enhanced, flags=re.IGNORECASE)
|
|
146
|
+
|
|
147
|
+
return enhanced
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def _add_warmth(cls, response: str) -> str:
|
|
151
|
+
"""Replace cold/formal phrases with warm ones"""
|
|
152
|
+
enhanced = response
|
|
153
|
+
|
|
154
|
+
for pattern, replacement in cls.WARM_TRANSFORMS.items():
|
|
155
|
+
enhanced = re.sub(pattern, replacement, enhanced, flags=re.IGNORECASE)
|
|
156
|
+
|
|
157
|
+
return enhanced
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def _make_elegant(cls, response: str, context: Dict[str, Any]) -> str:
|
|
161
|
+
"""
|
|
162
|
+
Add elegant formatting
|
|
163
|
+
|
|
164
|
+
- Convert plain lists to bulleted lists
|
|
165
|
+
- Add proper spacing
|
|
166
|
+
- Use emphasis where appropriate
|
|
167
|
+
"""
|
|
168
|
+
# Check if response has lists that aren't bulleted
|
|
169
|
+
lines = response.split('\n')
|
|
170
|
+
|
|
171
|
+
# Detect list patterns like "file1, file2, file3"
|
|
172
|
+
for i, line in enumerate(lines):
|
|
173
|
+
# If line has comma-separated items (3+)
|
|
174
|
+
if line.count(',') >= 2 and len(line) < 200:
|
|
175
|
+
parts = [p.strip() for p in line.split(',')]
|
|
176
|
+
|
|
177
|
+
if len(parts) >= 3:
|
|
178
|
+
# Check if these look like filenames or items
|
|
179
|
+
if any(ext in line for ext in ['.py', '.js', '.md', '.txt', '.json']) or \
|
|
180
|
+
all(len(p) < 50 for p in parts):
|
|
181
|
+
# Convert to bulleted list
|
|
182
|
+
intro = "I found these:" if i == 0 else ""
|
|
183
|
+
bulleted = '\n'.join([f"• {p}" for p in parts])
|
|
184
|
+
lines[i] = f"{intro}\n\n{bulleted}" if intro else bulleted
|
|
185
|
+
|
|
186
|
+
enhanced = '\n'.join(lines)
|
|
187
|
+
|
|
188
|
+
# Add proper paragraph spacing if missing
|
|
189
|
+
if enhanced.count('\n\n') == 0 and len(enhanced) > 200:
|
|
190
|
+
# Split into paragraphs at sentence boundaries
|
|
191
|
+
sentences = re.split(r'(?<=[.!?])\s+', enhanced)
|
|
192
|
+
if len(sentences) >= 3:
|
|
193
|
+
# Group into 2-3 sentence paragraphs
|
|
194
|
+
paragraphs = []
|
|
195
|
+
current = []
|
|
196
|
+
for sent in sentences:
|
|
197
|
+
current.append(sent)
|
|
198
|
+
if len(current) >= 2:
|
|
199
|
+
paragraphs.append(' '.join(current))
|
|
200
|
+
current = []
|
|
201
|
+
if current:
|
|
202
|
+
paragraphs.append(' '.join(current))
|
|
203
|
+
|
|
204
|
+
enhanced = '\n\n'.join(paragraphs)
|
|
205
|
+
|
|
206
|
+
return enhanced
|
|
207
|
+
|
|
208
|
+
@classmethod
|
|
209
|
+
def _add_anticipatory_offers(cls, response: str, query: str, context: Dict[str, Any]) -> str:
|
|
210
|
+
"""
|
|
211
|
+
Add anticipatory offers - BUT IN ACTION-FIRST MODE
|
|
212
|
+
|
|
213
|
+
Instead of "Want me to X?", the agent should have ALREADY DONE X
|
|
214
|
+
So we SKIP adding asking phrases in action-first mode
|
|
215
|
+
|
|
216
|
+
DISABLED: User wants action-first, not conversation-first
|
|
217
|
+
"""
|
|
218
|
+
# ACTION-FIRST MODE: Don't add asking phrases
|
|
219
|
+
# The agent should have already done the obvious next step
|
|
220
|
+
# If it didn't, that's a different problem to fix
|
|
221
|
+
|
|
222
|
+
return response # Return unchanged - no asking phrases added
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
def _add_personality(cls, response: str, query: str) -> str:
|
|
226
|
+
"""Add personality touches to make it feel like a smart friend"""
|
|
227
|
+
# Check query sentiment
|
|
228
|
+
query_lower = query.lower()
|
|
229
|
+
|
|
230
|
+
# If user said thanks
|
|
231
|
+
if any(word in query_lower for word in ['thanks', 'thank you', 'appreciate']):
|
|
232
|
+
# Make response warmer
|
|
233
|
+
if 'you are welcome' in response.lower() or 'you\'re welcome' in response.lower():
|
|
234
|
+
response = re.sub(
|
|
235
|
+
r"you'?re? welcome\.?",
|
|
236
|
+
"Happy to help! Let me know if you need anything else.",
|
|
237
|
+
response,
|
|
238
|
+
flags=re.IGNORECASE
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# If user seems excited (exclamation marks)
|
|
242
|
+
if '!' in query and len(query) < 50:
|
|
243
|
+
# Match energy slightly
|
|
244
|
+
if not '!' in response:
|
|
245
|
+
# Add one exclamation at the end if appropriate
|
|
246
|
+
response = response.rstrip('.') + '!'
|
|
247
|
+
|
|
248
|
+
# If response is very short and abrupt, make it friendlier
|
|
249
|
+
if len(response) < 20 and '?' not in response:
|
|
250
|
+
# Add friendly suffix
|
|
251
|
+
if not any(word in response.lower() for word in ['happy', 'glad', 'sure']):
|
|
252
|
+
response += " Let me know if you need anything else!"
|
|
253
|
+
|
|
254
|
+
return response
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def enhance_style(response: str, query: str, context: Dict[str, Any] = None) -> str:
|
|
258
|
+
"""Convenience function to enhance response style"""
|
|
259
|
+
return ResponseStyleEnhancer.enhance(response, query, context or {})
|