Spaces:
Sleeping
Sleeping
Commit
·
83c32ef
1
Parent(s):
d879ef4
Upd analytics
Browse files- .DS_Store +0 -0
- helpers/coder.py +1 -1
- helpers/diagram.py +2 -2
- routes/chats.py +6 -2
- routes/reports.py +8 -8
- static/analytics.js +7 -5
- static/styles.css +22 -6
- utils/api/router.py +17 -1
.DS_Store
CHANGED
|
Binary files a/.DS_Store and b/.DS_Store differ
|
|
|
helpers/coder.py
CHANGED
|
@@ -73,7 +73,7 @@ async def generate_code_artifacts(
|
|
| 73 |
)
|
| 74 |
except Exception:
|
| 75 |
pass
|
| 76 |
-
code_md = await generate_answer_with_model(selection, system_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 77 |
code_md = (code_md or "").strip()
|
| 78 |
|
| 79 |
if not code_md:
|
|
|
|
| 73 |
)
|
| 74 |
except Exception:
|
| 75 |
pass
|
| 76 |
+
code_md = await generate_answer_with_model(selection, system_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "coding")
|
| 77 |
code_md = (code_md or "").strip()
|
| 78 |
|
| 79 |
if not code_md:
|
helpers/diagram.py
CHANGED
|
@@ -92,7 +92,7 @@ async def generate_mermaid_diagram(
|
|
| 92 |
)
|
| 93 |
except Exception:
|
| 94 |
pass
|
| 95 |
-
diagram = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 96 |
diagram = (diagram or "").strip()
|
| 97 |
|
| 98 |
# Strip accidental code fences
|
|
@@ -186,7 +186,7 @@ Please provide the corrected Mermaid code that will render successfully."""
|
|
| 186 |
|
| 187 |
# Use NVIDIA_LARGE for better error correction
|
| 188 |
selection = {"provider": "nvidia_large", "model": "openai/gpt-oss-120b"}
|
| 189 |
-
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, None, None)
|
| 190 |
|
| 191 |
if response:
|
| 192 |
# Clean up the response
|
|
|
|
| 92 |
)
|
| 93 |
except Exception:
|
| 94 |
pass
|
| 95 |
+
diagram = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "diagram")
|
| 96 |
diagram = (diagram or "").strip()
|
| 97 |
|
| 98 |
# Strip accidental code fences
|
|
|
|
| 186 |
|
| 187 |
# Use NVIDIA_LARGE for better error correction
|
| 188 |
selection = {"provider": "nvidia_large", "model": "openai/gpt-oss-120b"}
|
| 189 |
+
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, None, None, user_id, "diagram_fix")
|
| 190 |
|
| 191 |
if response:
|
| 192 |
# Clean up the response
|
routes/chats.py
CHANGED
|
@@ -732,7 +732,9 @@ async def _chat_impl(
|
|
| 732 |
system_prompt=system_prompt,
|
| 733 |
user_prompt=user_prompt,
|
| 734 |
gemini_rotator=gemini_rotator,
|
| 735 |
-
nvidia_rotator=nvidia_rotator
|
|
|
|
|
|
|
| 736 |
)
|
| 737 |
logger.info(f"[CHAT] Answer generated successfully, length: {len(answer)}")
|
| 738 |
except Exception as e:
|
|
@@ -930,7 +932,9 @@ async def chat_with_search(
|
|
| 930 |
system_prompt=system_prompt,
|
| 931 |
user_prompt=user_prompt,
|
| 932 |
gemini_rotator=gemini_rotator,
|
| 933 |
-
nvidia_rotator=nvidia_rotator
|
|
|
|
|
|
|
| 934 |
)
|
| 935 |
except Exception as e:
|
| 936 |
logger.warning(f"[CHAT] Web-augmented LLM error: {e}")
|
|
|
|
| 732 |
system_prompt=system_prompt,
|
| 733 |
user_prompt=user_prompt,
|
| 734 |
gemini_rotator=gemini_rotator,
|
| 735 |
+
nvidia_rotator=nvidia_rotator,
|
| 736 |
+
user_id=user_id,
|
| 737 |
+
context="chat"
|
| 738 |
)
|
| 739 |
logger.info(f"[CHAT] Answer generated successfully, length: {len(answer)}")
|
| 740 |
except Exception as e:
|
|
|
|
| 932 |
system_prompt=system_prompt,
|
| 933 |
user_prompt=user_prompt,
|
| 934 |
gemini_rotator=gemini_rotator,
|
| 935 |
+
nvidia_rotator=nvidia_rotator,
|
| 936 |
+
user_id=user_id,
|
| 937 |
+
context="chat"
|
| 938 |
)
|
| 939 |
except Exception as e:
|
| 940 |
logger.warning(f"[CHAT] Web-augmented LLM error: {e}")
|
routes/reports.py
CHANGED
|
@@ -296,7 +296,7 @@ Create a detailed plan for this report."""
|
|
| 296 |
logger.info(f"[REPORT] System prompt length: {len(sys_prompt)}")
|
| 297 |
logger.info(f"[REPORT] User prompt length: {len(user_prompt)}")
|
| 298 |
|
| 299 |
-
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 300 |
|
| 301 |
# Parse JSON response
|
| 302 |
import json
|
|
@@ -406,7 +406,7 @@ FILE SUMMARY: {file_summary[:500]}
|
|
| 406 |
Create a simple plan for this report."""
|
| 407 |
|
| 408 |
simple_selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 409 |
-
simple_response = await generate_answer_with_model(simple_selection, simple_sys_prompt, simple_user_prompt, gemini_rotator, nvidia_rotator)
|
| 410 |
simple_json_text = simple_response.strip()
|
| 411 |
|
| 412 |
if simple_json_text.startswith('```json'):
|
|
@@ -662,7 +662,7 @@ Perform the comprehensive analysis as specified, following all sub-actions, meet
|
|
| 662 |
|
| 663 |
try:
|
| 664 |
selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 665 |
-
analysis = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 666 |
return analysis.strip()
|
| 667 |
|
| 668 |
except Exception as e:
|
|
@@ -746,7 +746,7 @@ Synthesize these analyses into a comprehensive, coherent section with proper hie
|
|
| 746 |
|
| 747 |
try:
|
| 748 |
selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 749 |
-
synthesis = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 750 |
return synthesis.strip()
|
| 751 |
|
| 752 |
except Exception as e:
|
|
@@ -884,7 +884,7 @@ Create a comprehensive, authoritative report with proper hierarchical structure
|
|
| 884 |
try:
|
| 885 |
# Use Gemini Pro for final synthesis (better for long-form content)
|
| 886 |
selection = {"provider": "gemini", "model": "gemini-2.5-pro"}
|
| 887 |
-
report = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 888 |
|
| 889 |
# Post-process to remove any remaining meta-commentary and ensure proper formatting
|
| 890 |
report = remove_meta_commentary(report)
|
|
@@ -935,7 +935,7 @@ async def generate_code_artifacts(subsection_id: str, task: str, reasoning: str,
|
|
| 935 |
"Produce the code files and explanations as specified."
|
| 936 |
)
|
| 937 |
selection = {"provider": "gemini", "model": "gemini-2.5-pro"}
|
| 938 |
-
code_md = await generate_answer_with_model(selection, system_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 939 |
return code_md.strip()
|
| 940 |
|
| 941 |
def should_generate_mermaid(instructions: str, report_text: str) -> bool:
|
|
@@ -970,7 +970,7 @@ async def generate_mermaid_diagram(instructions: str, detailed_analysis: Dict[st
|
|
| 970 |
|
| 971 |
# Use NVIDIA_LARGE for diagram synthesis
|
| 972 |
selection = {"provider": "nvidia_large", "model": os.getenv("NVIDIA_LARGE", "openai/gpt-oss-120b")}
|
| 973 |
-
diagram = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
|
| 974 |
# Strip accidental code fences
|
| 975 |
diagram = diagram.strip()
|
| 976 |
if diagram.startswith("```"):
|
|
@@ -1272,7 +1272,7 @@ Return the renumbered headings in the format: "level: new_number: heading_text"
|
|
| 1272 |
|
| 1273 |
# Use NVIDIA model for heading re-numbering
|
| 1274 |
selection = {"provider": "nvidia", "model": "meta/llama-3.1-8b-instruct"}
|
| 1275 |
-
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, None, nvidia_rotator)
|
| 1276 |
|
| 1277 |
# Parse the AI response
|
| 1278 |
renumbered_headings = []
|
|
|
|
| 296 |
logger.info(f"[REPORT] System prompt length: {len(sys_prompt)}")
|
| 297 |
logger.info(f"[REPORT] User prompt length: {len(user_prompt)}")
|
| 298 |
|
| 299 |
+
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_planning")
|
| 300 |
|
| 301 |
# Parse JSON response
|
| 302 |
import json
|
|
|
|
| 406 |
Create a simple plan for this report."""
|
| 407 |
|
| 408 |
simple_selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 409 |
+
simple_response = await generate_answer_with_model(simple_selection, simple_sys_prompt, simple_user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_planning_simple")
|
| 410 |
simple_json_text = simple_response.strip()
|
| 411 |
|
| 412 |
if simple_json_text.startswith('```json'):
|
|
|
|
| 662 |
|
| 663 |
try:
|
| 664 |
selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 665 |
+
analysis = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_analysis")
|
| 666 |
return analysis.strip()
|
| 667 |
|
| 668 |
except Exception as e:
|
|
|
|
| 746 |
|
| 747 |
try:
|
| 748 |
selection = {"provider": "gemini", "model": "gemini-2.5-flash"}
|
| 749 |
+
synthesis = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_synthesis")
|
| 750 |
return synthesis.strip()
|
| 751 |
|
| 752 |
except Exception as e:
|
|
|
|
| 884 |
try:
|
| 885 |
# Use Gemini Pro for final synthesis (better for long-form content)
|
| 886 |
selection = {"provider": "gemini", "model": "gemini-2.5-pro"}
|
| 887 |
+
report = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_final")
|
| 888 |
|
| 889 |
# Post-process to remove any remaining meta-commentary and ensure proper formatting
|
| 890 |
report = remove_meta_commentary(report)
|
|
|
|
| 935 |
"Produce the code files and explanations as specified."
|
| 936 |
)
|
| 937 |
selection = {"provider": "gemini", "model": "gemini-2.5-pro"}
|
| 938 |
+
code_md = await generate_answer_with_model(selection, system_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_coding")
|
| 939 |
return code_md.strip()
|
| 940 |
|
| 941 |
def should_generate_mermaid(instructions: str, report_text: str) -> bool:
|
|
|
|
| 970 |
|
| 971 |
# Use NVIDIA_LARGE for diagram synthesis
|
| 972 |
selection = {"provider": "nvidia_large", "model": os.getenv("NVIDIA_LARGE", "openai/gpt-oss-120b")}
|
| 973 |
+
diagram = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator, user_id, "report_diagram")
|
| 974 |
# Strip accidental code fences
|
| 975 |
diagram = diagram.strip()
|
| 976 |
if diagram.startswith("```"):
|
|
|
|
| 1272 |
|
| 1273 |
# Use NVIDIA model for heading re-numbering
|
| 1274 |
selection = {"provider": "nvidia", "model": "meta/llama-3.1-8b-instruct"}
|
| 1275 |
+
response = await generate_answer_with_model(selection, sys_prompt, user_prompt, None, nvidia_rotator, user_id, "report_heading_fix")
|
| 1276 |
|
| 1277 |
# Parse the AI response
|
| 1278 |
renumbered_headings = []
|
static/analytics.js
CHANGED
|
@@ -208,12 +208,14 @@
|
|
| 208 |
sortedModels.forEach(model => {
|
| 209 |
const percentage = totalUsage > 0 ? Math.round((model.count / totalUsage) * 100) : 0;
|
| 210 |
const lastUsed = new Date(model.last_used * 1000).toLocaleDateString();
|
|
|
|
|
|
|
| 211 |
|
| 212 |
html += `
|
| 213 |
<div class="model-usage-item">
|
| 214 |
<div class="model-info">
|
| 215 |
-
<div class="model-name">${
|
| 216 |
-
<div class="model-provider">${
|
| 217 |
</div>
|
| 218 |
<div class="model-stats">
|
| 219 |
<div class="model-count">${model.count} requests</div>
|
|
@@ -291,11 +293,11 @@
|
|
| 291 |
let html = '<div class="daily-trends-chart">';
|
| 292 |
sortedDaily.forEach(day => {
|
| 293 |
const date = new Date(day._id.year, day._id.month - 1, day._id.day);
|
| 294 |
-
const dateStr = date.toLocaleDateString();
|
| 295 |
-
const height = maxUsage > 0 ? (day.total_requests / maxUsage) * 100 :
|
| 296 |
|
| 297 |
html += `
|
| 298 |
-
<div class="daily-bar">
|
| 299 |
<div class="daily-bar-fill" style="height: ${height}%"></div>
|
| 300 |
<div class="daily-label">${dateStr}</div>
|
| 301 |
<div class="daily-count">${day.total_requests}</div>
|
|
|
|
| 208 |
sortedModels.forEach(model => {
|
| 209 |
const percentage = totalUsage > 0 ? Math.round((model.count / totalUsage) * 100) : 0;
|
| 210 |
const lastUsed = new Date(model.last_used * 1000).toLocaleDateString();
|
| 211 |
+
const modelName = model.model_name || model._id;
|
| 212 |
+
const provider = model.provider || 'unknown';
|
| 213 |
|
| 214 |
html += `
|
| 215 |
<div class="model-usage-item">
|
| 216 |
<div class="model-info">
|
| 217 |
+
<div class="model-name">${modelName}</div>
|
| 218 |
+
<div class="model-provider">${provider}</div>
|
| 219 |
</div>
|
| 220 |
<div class="model-stats">
|
| 221 |
<div class="model-count">${model.count} requests</div>
|
|
|
|
| 293 |
let html = '<div class="daily-trends-chart">';
|
| 294 |
sortedDaily.forEach(day => {
|
| 295 |
const date = new Date(day._id.year, day._id.month - 1, day._id.day);
|
| 296 |
+
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
| 297 |
+
const height = maxUsage > 0 ? Math.max(10, (day.total_requests / maxUsage) * 100) : 10;
|
| 298 |
|
| 299 |
html += `
|
| 300 |
+
<div class="daily-bar" title="${day.total_requests} requests on ${date.toLocaleDateString()}">
|
| 301 |
<div class="daily-bar-fill" style="height: ${height}%"></div>
|
| 302 |
<div class="daily-label">${dateStr}</div>
|
| 303 |
<div class="daily-count">${day.total_requests}</div>
|
static/styles.css
CHANGED
|
@@ -2300,6 +2300,7 @@
|
|
| 2300 |
gap: 0.5rem;
|
| 2301 |
height: 200px;
|
| 2302 |
padding: 1rem 0;
|
|
|
|
| 2303 |
}
|
| 2304 |
|
| 2305 |
.daily-bar {
|
|
@@ -2309,28 +2310,43 @@
|
|
| 2309 |
align-items: center;
|
| 2310 |
gap: 0.5rem;
|
| 2311 |
min-height: 100px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2312 |
}
|
| 2313 |
|
| 2314 |
.daily-bar-fill {
|
| 2315 |
width: 100%;
|
| 2316 |
background: var(--gradient-accent);
|
| 2317 |
-
border-radius:
|
| 2318 |
-
min-height:
|
| 2319 |
-
transition:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2320 |
}
|
| 2321 |
|
| 2322 |
.daily-label {
|
| 2323 |
font-size: 0.75rem;
|
| 2324 |
color: var(--muted);
|
| 2325 |
text-align: center;
|
| 2326 |
-
|
| 2327 |
-
|
|
|
|
|
|
|
| 2328 |
}
|
| 2329 |
|
| 2330 |
.daily-count {
|
| 2331 |
font-size: 0.75rem;
|
| 2332 |
color: var(--text-secondary);
|
| 2333 |
-
font-weight:
|
|
|
|
| 2334 |
}
|
| 2335 |
|
| 2336 |
/* Usage Summary Styles */
|
|
|
|
| 2300 |
gap: 0.5rem;
|
| 2301 |
height: 200px;
|
| 2302 |
padding: 1rem 0;
|
| 2303 |
+
border-bottom: 1px solid var(--border);
|
| 2304 |
}
|
| 2305 |
|
| 2306 |
.daily-bar {
|
|
|
|
| 2310 |
align-items: center;
|
| 2311 |
gap: 0.5rem;
|
| 2312 |
min-height: 100px;
|
| 2313 |
+
cursor: pointer;
|
| 2314 |
+
transition: transform 0.2s ease;
|
| 2315 |
+
}
|
| 2316 |
+
|
| 2317 |
+
.daily-bar:hover {
|
| 2318 |
+
transform: translateY(-2px);
|
| 2319 |
}
|
| 2320 |
|
| 2321 |
.daily-bar-fill {
|
| 2322 |
width: 100%;
|
| 2323 |
background: var(--gradient-accent);
|
| 2324 |
+
border-radius: 4px 4px 0 0;
|
| 2325 |
+
min-height: 8px;
|
| 2326 |
+
transition: all 0.3s ease;
|
| 2327 |
+
position: relative;
|
| 2328 |
+
}
|
| 2329 |
+
|
| 2330 |
+
.daily-bar:hover .daily-bar-fill {
|
| 2331 |
+
background: var(--accent);
|
| 2332 |
+
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
|
| 2333 |
}
|
| 2334 |
|
| 2335 |
.daily-label {
|
| 2336 |
font-size: 0.75rem;
|
| 2337 |
color: var(--muted);
|
| 2338 |
text-align: center;
|
| 2339 |
+
white-space: nowrap;
|
| 2340 |
+
transform: rotate(-45deg);
|
| 2341 |
+
transform-origin: center;
|
| 2342 |
+
margin-top: 0.5rem;
|
| 2343 |
}
|
| 2344 |
|
| 2345 |
.daily-count {
|
| 2346 |
font-size: 0.75rem;
|
| 2347 |
color: var(--text-secondary);
|
| 2348 |
+
font-weight: 600;
|
| 2349 |
+
margin-top: 0.25rem;
|
| 2350 |
}
|
| 2351 |
|
| 2352 |
/* Usage Summary Styles */
|
utils/api/router.py
CHANGED
|
@@ -89,9 +89,25 @@ def select_model(question: str, context: str) -> Dict[str, Any]:
|
|
| 89 |
|
| 90 |
|
| 91 |
async def generate_answer_with_model(selection: Dict[str, Any], system_prompt: str, user_prompt: str,
|
| 92 |
-
gemini_rotator: APIKeyRotator, nvidia_rotator: APIKeyRotator
|
|
|
|
| 93 |
provider = selection["provider"]
|
| 94 |
model = selection["model"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
if provider == "gemini":
|
| 97 |
# Try Gemini first
|
|
|
|
| 89 |
|
| 90 |
|
| 91 |
async def generate_answer_with_model(selection: Dict[str, Any], system_prompt: str, user_prompt: str,
|
| 92 |
+
gemini_rotator: APIKeyRotator, nvidia_rotator: APIKeyRotator,
|
| 93 |
+
user_id: str = None, context: str = "") -> str:
|
| 94 |
provider = selection["provider"]
|
| 95 |
model = selection["model"]
|
| 96 |
+
|
| 97 |
+
# Track model usage for analytics
|
| 98 |
+
try:
|
| 99 |
+
from utils.analytics import get_analytics_tracker
|
| 100 |
+
tracker = get_analytics_tracker()
|
| 101 |
+
if tracker and user_id:
|
| 102 |
+
await tracker.track_model_usage(
|
| 103 |
+
user_id=user_id,
|
| 104 |
+
model_name=model,
|
| 105 |
+
provider=provider,
|
| 106 |
+
context=context or "api_call",
|
| 107 |
+
metadata={"system_prompt_length": len(system_prompt), "user_prompt_length": len(user_prompt)}
|
| 108 |
+
)
|
| 109 |
+
except Exception as e:
|
| 110 |
+
logger.debug(f"[ROUTER] Analytics tracking failed: {e}")
|
| 111 |
|
| 112 |
if provider == "gemini":
|
| 113 |
# Try Gemini first
|