Spaces:
Sleeping
Sleeping
Update tools_additions.py
Browse files- tools_additions.py +25 -57
tools_additions.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 4 |
# GROUP 8: DBSCAN Clustering (title + abstract separate vectors)
|
| 5 |
# GROUP 9: Cluster Splitting and Min-Membership Enforcement
|
| 6 |
# GROUP 10: LLM Cluster Labeling (Anthropic API)
|
|
|
|
| 7 |
# =============================================================================
|
| 8 |
|
| 9 |
import os
|
|
@@ -366,7 +367,6 @@ def split_large_clusters(
|
|
| 366 |
next_cid += len(unique_sub)
|
| 367 |
|
| 368 |
indices = sub_df.index.tolist()
|
| 369 |
-
noise_mask = sub_labels == -1
|
| 370 |
for local_i, (orig_idx, sl) in enumerate(zip(indices, sub_labels)):
|
| 371 |
if sl == -1:
|
| 372 |
# Absorb noise into nearest non-noise neighbour's cluster
|
|
@@ -406,7 +406,7 @@ def split_large_clusters(
|
|
| 406 |
|
| 407 |
|
| 408 |
# =============================================================================
|
| 409 |
-
# GROUP 10: LLM CLUSTER LABELING (ANTHROPIC API
|
| 410 |
# =============================================================================
|
| 411 |
|
| 412 |
def _top3_sentences(group_df: pd.DataFrame) -> str:
|
|
@@ -510,35 +510,28 @@ def label_clusters_with_llm(
|
|
| 510 |
|
| 511 |
|
| 512 |
# =============================================================================
|
| 513 |
-
# GROUP 11: AGENTIC COUNCIL (MISTRAL + GEMINI
|
| 514 |
# =============================================================================
|
| 515 |
|
| 516 |
COUNCIL_PROMPT_TEMPLATE = """You are a senior Information Systems research analyst.
|
| 517 |
You have been given a PAJAIS research gap analysis report with the following findings:
|
| 518 |
-
|
| 519 |
{findings}
|
| 520 |
-
|
| 521 |
Based on this analysis, provide your expert assessment covering:
|
| 522 |
1. The 3 most strategically important research gaps for the field
|
| 523 |
2. Which novel topics have the highest publication impact potential
|
| 524 |
3. Recommended methodologies for addressing the top gap
|
| 525 |
4. Any risks or caveats in the analysis
|
| 526 |
-
|
| 527 |
Be specific, cite topic names from the report, and limit your response to 300 words."""
|
| 528 |
|
| 529 |
SYNTHESIS_PROMPT_TEMPLATE = """You are the Chief Research Officer synthesizing advice from two expert panels.
|
| 530 |
-
|
| 531 |
Panel A (Mistral) said:
|
| 532 |
{mistral_response}
|
| 533 |
-
|
| 534 |
Panel B (Gemini) said:
|
| 535 |
{gemini_response}
|
| 536 |
-
|
| 537 |
Your task:
|
| 538 |
1. Identify the 2-3 points both panels AGREE on (consensus insights)
|
| 539 |
2. Identify where they DIVERGE and explain which view is more defensible
|
| 540 |
3. Produce a final 200-word synthesis recommendation
|
| 541 |
-
|
| 542 |
Structure your response as:
|
| 543 |
### Consensus
|
| 544 |
<points>
|
|
@@ -606,56 +599,30 @@ def _call_gemini(
|
|
| 606 |
return f"[Gemini unavailable: {e}]"
|
| 607 |
|
| 608 |
|
| 609 |
-
def _call_anthropic_synthesis(
|
| 610 |
-
prompt: str,
|
| 611 |
-
api_key: str,
|
| 612 |
-
model: str = "claude-sonnet-4-20250514",
|
| 613 |
-
) -> str:
|
| 614 |
-
"""Call Anthropic for synthesis."""
|
| 615 |
-
try:
|
| 616 |
-
resp = httpx.post(
|
| 617 |
-
"https://api.anthropic.com/v1/messages",
|
| 618 |
-
headers={
|
| 619 |
-
"x-api-key": api_key,
|
| 620 |
-
"anthropic-version": "2023-06-01",
|
| 621 |
-
"content-type": "application/json",
|
| 622 |
-
},
|
| 623 |
-
json={
|
| 624 |
-
"model": model,
|
| 625 |
-
"max_tokens": 600,
|
| 626 |
-
"messages": [{"role": "user", "content": prompt}],
|
| 627 |
-
},
|
| 628 |
-
timeout=30.0,
|
| 629 |
-
)
|
| 630 |
-
resp.raise_for_status()
|
| 631 |
-
return resp.json()["content"][0]["text"].strip()
|
| 632 |
-
except Exception as e:
|
| 633 |
-
logger.error(f"Anthropic synthesis call failed: {e}")
|
| 634 |
-
return f"[Anthropic synthesis unavailable: {e}]"
|
| 635 |
-
|
| 636 |
-
|
| 637 |
def run_agentic_council(
|
| 638 |
taxonomy_map: Dict[str, Any],
|
| 639 |
topic_df: Optional[pd.DataFrame],
|
| 640 |
mistral_api_key: str = "",
|
| 641 |
gemini_api_key: str = "",
|
| 642 |
-
anthropic_api_key: str = "",
|
| 643 |
) -> Dict[str, str]:
|
| 644 |
"""
|
| 645 |
-
Run the three-
|
| 646 |
-
|
|
|
|
|
|
|
| 647 |
|
| 648 |
Parameters
|
| 649 |
----------
|
| 650 |
taxonomy_map : Output of generate_taxonomy_map.
|
| 651 |
topic_df : Topic DataFrame (used to build findings summary).
|
| 652 |
mistral_api_key : Mistral API key.
|
| 653 |
-
gemini_api_key : Google AI Studio API key.
|
| 654 |
-
anthropic_api_key :
|
| 655 |
|
| 656 |
Returns
|
| 657 |
-------
|
| 658 |
-
Dict with keys: '
|
| 659 |
"""
|
| 660 |
# Build findings summary
|
| 661 |
gap = taxonomy_map.get("gap_analysis", {})
|
|
@@ -681,44 +648,45 @@ def run_agentic_council(
|
|
| 681 |
PAJAIS Coverage: {gap.get('coverage_pct', 0):.1f}% ({gap.get('mapped_count', 0)} mapped, {gap.get('novel_count', 0)} novel)
|
| 682 |
Covered themes (sample): {covered_str}
|
| 683 |
Uncovered themes (sample): {uncovered_str}
|
| 684 |
-
|
| 685 |
Top discovered topics: {top_topics_str}
|
| 686 |
-
|
| 687 |
Novel research themes (top 5):
|
| 688 |
{novel_str}
|
| 689 |
-
|
| 690 |
Publishable gap candidates:
|
| 691 |
{pub_str}
|
| 692 |
""".strip()
|
| 693 |
|
| 694 |
council_prompt = COUNCIL_PROMPT_TEMPLATE.format(findings=findings)
|
| 695 |
|
| 696 |
-
|
|
|
|
| 697 |
mistral_resp = (
|
| 698 |
_call_mistral(council_prompt, mistral_api_key)
|
| 699 |
if mistral_api_key.strip()
|
| 700 |
else "[Mistral API key not provided]"
|
| 701 |
)
|
| 702 |
|
| 703 |
-
|
|
|
|
| 704 |
gemini_resp = (
|
| 705 |
_call_gemini(council_prompt, gemini_api_key)
|
| 706 |
if gemini_api_key.strip()
|
| 707 |
else "[Gemini API key not provided]"
|
| 708 |
)
|
| 709 |
|
|
|
|
| 710 |
synthesis_prompt = SYNTHESIS_PROMPT_TEMPLATE.format(
|
| 711 |
mistral_response=mistral_resp,
|
| 712 |
gemini_response=gemini_resp,
|
| 713 |
)
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
|
|
|
| 722 |
|
| 723 |
return {
|
| 724 |
"findings_summary": findings,
|
|
|
|
| 4 |
# GROUP 8: DBSCAN Clustering (title + abstract separate vectors)
|
| 5 |
# GROUP 9: Cluster Splitting and Min-Membership Enforcement
|
| 6 |
# GROUP 10: LLM Cluster Labeling (Anthropic API)
|
| 7 |
+
# GROUP 11: Agentic Council (Mistral + Gemini panels, Gemini synthesis judge)
|
| 8 |
# =============================================================================
|
| 9 |
|
| 10 |
import os
|
|
|
|
| 367 |
next_cid += len(unique_sub)
|
| 368 |
|
| 369 |
indices = sub_df.index.tolist()
|
|
|
|
| 370 |
for local_i, (orig_idx, sl) in enumerate(zip(indices, sub_labels)):
|
| 371 |
if sl == -1:
|
| 372 |
# Absorb noise into nearest non-noise neighbour's cluster
|
|
|
|
| 406 |
|
| 407 |
|
| 408 |
# =============================================================================
|
| 409 |
+
# GROUP 10: LLM CLUSTER LABELING (ANTHROPIC API)
|
| 410 |
# =============================================================================
|
| 411 |
|
| 412 |
def _top3_sentences(group_df: pd.DataFrame) -> str:
|
|
|
|
| 510 |
|
| 511 |
|
| 512 |
# =============================================================================
|
| 513 |
+
# GROUP 11: AGENTIC COUNCIL (MISTRAL + GEMINI PANELS, GEMINI SYNTHESIS JUDGE)
|
| 514 |
# =============================================================================
|
| 515 |
|
| 516 |
COUNCIL_PROMPT_TEMPLATE = """You are a senior Information Systems research analyst.
|
| 517 |
You have been given a PAJAIS research gap analysis report with the following findings:
|
|
|
|
| 518 |
{findings}
|
|
|
|
| 519 |
Based on this analysis, provide your expert assessment covering:
|
| 520 |
1. The 3 most strategically important research gaps for the field
|
| 521 |
2. Which novel topics have the highest publication impact potential
|
| 522 |
3. Recommended methodologies for addressing the top gap
|
| 523 |
4. Any risks or caveats in the analysis
|
|
|
|
| 524 |
Be specific, cite topic names from the report, and limit your response to 300 words."""
|
| 525 |
|
| 526 |
SYNTHESIS_PROMPT_TEMPLATE = """You are the Chief Research Officer synthesizing advice from two expert panels.
|
|
|
|
| 527 |
Panel A (Mistral) said:
|
| 528 |
{mistral_response}
|
|
|
|
| 529 |
Panel B (Gemini) said:
|
| 530 |
{gemini_response}
|
|
|
|
| 531 |
Your task:
|
| 532 |
1. Identify the 2-3 points both panels AGREE on (consensus insights)
|
| 533 |
2. Identify where they DIVERGE and explain which view is more defensible
|
| 534 |
3. Produce a final 200-word synthesis recommendation
|
|
|
|
| 535 |
Structure your response as:
|
| 536 |
### Consensus
|
| 537 |
<points>
|
|
|
|
| 599 |
return f"[Gemini unavailable: {e}]"
|
| 600 |
|
| 601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 602 |
def run_agentic_council(
|
| 603 |
taxonomy_map: Dict[str, Any],
|
| 604 |
topic_df: Optional[pd.DataFrame],
|
| 605 |
mistral_api_key: str = "",
|
| 606 |
gemini_api_key: str = "",
|
| 607 |
+
anthropic_api_key: str = "", # retained for signature compatibility
|
| 608 |
) -> Dict[str, str]:
|
| 609 |
"""
|
| 610 |
+
Run the three-stage agentic council:
|
| 611 |
+
Stage 1 — Mistral panel: independent analysis of findings.
|
| 612 |
+
Stage 2 — Gemini panel: independent analysis of findings.
|
| 613 |
+
Stage 3 — Gemini synthesis judge: reconciles both panels (gemini-1.5-pro).
|
| 614 |
|
| 615 |
Parameters
|
| 616 |
----------
|
| 617 |
taxonomy_map : Output of generate_taxonomy_map.
|
| 618 |
topic_df : Topic DataFrame (used to build findings summary).
|
| 619 |
mistral_api_key : Mistral API key.
|
| 620 |
+
gemini_api_key : Google AI Studio API key (used for both panel and judge).
|
| 621 |
+
anthropic_api_key : Ignored. Kept for backward-compatible call sites.
|
| 622 |
|
| 623 |
Returns
|
| 624 |
-------
|
| 625 |
+
Dict with keys: 'findings_summary', 'mistral', 'gemini', 'synthesis'
|
| 626 |
"""
|
| 627 |
# Build findings summary
|
| 628 |
gap = taxonomy_map.get("gap_analysis", {})
|
|
|
|
| 648 |
PAJAIS Coverage: {gap.get('coverage_pct', 0):.1f}% ({gap.get('mapped_count', 0)} mapped, {gap.get('novel_count', 0)} novel)
|
| 649 |
Covered themes (sample): {covered_str}
|
| 650 |
Uncovered themes (sample): {uncovered_str}
|
|
|
|
| 651 |
Top discovered topics: {top_topics_str}
|
|
|
|
| 652 |
Novel research themes (top 5):
|
| 653 |
{novel_str}
|
|
|
|
| 654 |
Publishable gap candidates:
|
| 655 |
{pub_str}
|
| 656 |
""".strip()
|
| 657 |
|
| 658 |
council_prompt = COUNCIL_PROMPT_TEMPLATE.format(findings=findings)
|
| 659 |
|
| 660 |
+
# Stage 1 — Mistral panel
|
| 661 |
+
logger.info("Council: calling Mistral (panel)…")
|
| 662 |
mistral_resp = (
|
| 663 |
_call_mistral(council_prompt, mistral_api_key)
|
| 664 |
if mistral_api_key.strip()
|
| 665 |
else "[Mistral API key not provided]"
|
| 666 |
)
|
| 667 |
|
| 668 |
+
# Stage 2 — Gemini panel
|
| 669 |
+
logger.info("Council: calling Gemini (panel)…")
|
| 670 |
gemini_resp = (
|
| 671 |
_call_gemini(council_prompt, gemini_api_key)
|
| 672 |
if gemini_api_key.strip()
|
| 673 |
else "[Gemini API key not provided]"
|
| 674 |
)
|
| 675 |
|
| 676 |
+
# Stage 3 — Gemini synthesis judge (upgraded model)
|
| 677 |
synthesis_prompt = SYNTHESIS_PROMPT_TEMPLATE.format(
|
| 678 |
mistral_response=mistral_resp,
|
| 679 |
gemini_response=gemini_resp,
|
| 680 |
)
|
| 681 |
+
logger.info("Council: calling Gemini (synthesis judge)…")
|
| 682 |
+
if gemini_api_key.strip():
|
| 683 |
+
synthesis_resp = _call_gemini(
|
| 684 |
+
synthesis_prompt,
|
| 685 |
+
gemini_api_key,
|
| 686 |
+
model="gemini-1.5-pro",
|
| 687 |
+
)
|
| 688 |
+
else:
|
| 689 |
+
synthesis_resp = "[Gemini API key not provided — synthesis skipped]"
|
| 690 |
|
| 691 |
return {
|
| 692 |
"findings_summary": findings,
|