LiamKhoaLe commited on
Commit
83c32ef
·
1 Parent(s): d879ef4

Upd analytics

Browse files
.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">${model._id}</div>
216
- <div class="model-provider">${model.provider}</div>
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 : 0;
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: 2px 2px 0 0;
2318
- min-height: 4px;
2319
- transition: height 0.3s ease;
 
 
 
 
 
 
2320
  }
2321
 
2322
  .daily-label {
2323
  font-size: 0.75rem;
2324
  color: var(--muted);
2325
  text-align: center;
2326
- writing-mode: vertical-rl;
2327
- text-orientation: mixed;
 
 
2328
  }
2329
 
2330
  .daily-count {
2331
  font-size: 0.75rem;
2332
  color: var(--text-secondary);
2333
- font-weight: 500;
 
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) -> str:
 
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