Spaces:
Sleeping
Sleeping
Update tools.py
Browse files
tools.py
CHANGED
|
@@ -1697,21 +1697,21 @@ def _call_gemini_label(prompt: str, api_key: str) -> str:
|
|
| 1697 |
return f'[Gemini: {e}]'
|
| 1698 |
|
| 1699 |
|
| 1700 |
-
def
|
| 1701 |
-
|
| 1702 |
-
"""
|
| 1703 |
try:
|
|
|
|
| 1704 |
r = _httpx.post(
|
| 1705 |
-
|
| 1706 |
-
headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'},
|
| 1707 |
json={'model': model, 'messages': [{'role': 'user', 'content': prompt}],
|
| 1708 |
-
'
|
| 1709 |
-
timeout=
|
| 1710 |
)
|
| 1711 |
r.raise_for_status()
|
| 1712 |
-
return r.json()['
|
| 1713 |
except Exception as e:
|
| 1714 |
-
return f'[
|
| 1715 |
|
| 1716 |
|
| 1717 |
def _label_prompt(titles: List[str]) -> str:
|
|
@@ -1731,10 +1731,10 @@ def label_clusters_3llm(
|
|
| 1731 |
embeddings: np.ndarray,
|
| 1732 |
mistral_api_key: str = '',
|
| 1733 |
gemini_api_key: str = '',
|
| 1734 |
-
|
| 1735 |
max_clusters: int = 30,
|
| 1736 |
) -> pd.DataFrame:
|
| 1737 |
-
"""Label each cluster using Mistral + Gemini +
|
| 1738 |
|
| 1739 |
For each cluster:
|
| 1740 |
1. Selects 3 most representative paper titles (closest to centroid).
|
|
@@ -1749,7 +1749,7 @@ def label_clusters_3llm(
|
|
| 1749 |
embeddings : SPECTER2 embeddings aligned with cluster_df rows.
|
| 1750 |
mistral_api_key : Mistral API key (free tier).
|
| 1751 |
gemini_api_key : Google AI Studio API key (free tier).
|
| 1752 |
-
|
| 1753 |
max_clusters : Cap API calls to this many clusters.
|
| 1754 |
|
| 1755 |
Returns:
|
|
@@ -1761,10 +1761,10 @@ def label_clusters_3llm(
|
|
| 1761 |
summary['label_mistral'] = ''
|
| 1762 |
if 'label_gemini' not in summary.columns:
|
| 1763 |
summary['label_gemini'] = ''
|
| 1764 |
-
if '
|
| 1765 |
-
summary['
|
| 1766 |
|
| 1767 |
-
has_any_key = any([mistral_api_key.strip(), gemini_api_key.strip(),
|
| 1768 |
|
| 1769 |
for idx, row in summary.iterrows():
|
| 1770 |
if idx >= max_clusters:
|
|
@@ -1801,10 +1801,10 @@ def label_clusters_3llm(
|
|
| 1801 |
candidates.append(gl)
|
| 1802 |
_time.sleep(0.2)
|
| 1803 |
|
| 1804 |
-
if
|
| 1805 |
-
|
| 1806 |
-
summary.at[idx, '
|
| 1807 |
-
candidates.append(
|
| 1808 |
_time.sleep(0.2)
|
| 1809 |
|
| 1810 |
summary.at[idx, 'label'] = _majority_label(candidates) if candidates else 'Research Cluster'
|
|
@@ -1833,8 +1833,8 @@ Panel A (Mistral) said:
|
|
| 1833 |
{mistral_response}
|
| 1834 |
Panel B (Gemini) said:
|
| 1835 |
{gemini_response}
|
| 1836 |
-
Panel C (
|
| 1837 |
-
{
|
| 1838 |
Your task:
|
| 1839 |
1. Identify the 2-3 points ALL panels AGREE on (consensus insights)
|
| 1840 |
2. Identify where they DIVERGE and explain which view is most defensible
|
|
@@ -1864,7 +1864,7 @@ def _call_mistral(prompt: str, api_key: str, model: str = 'mistral-large-latest'
|
|
| 1864 |
return f'[Mistral unavailable: {e}]'
|
| 1865 |
|
| 1866 |
|
| 1867 |
-
def _call_gemini(prompt: str, api_key: str, model: str = 'gemini-
|
| 1868 |
try:
|
| 1869 |
url = (f'https://generativelanguage.googleapis.com/v1beta/models/'
|
| 1870 |
f'{model}:generateContent?key={api_key}')
|
|
@@ -1883,25 +1883,25 @@ def _call_gemini(prompt: str, api_key: str, model: str = 'gemini-2.0-flash') ->
|
|
| 1883 |
return f'[Gemini unavailable: {e}]'
|
| 1884 |
|
| 1885 |
|
| 1886 |
-
def
|
| 1887 |
prompt: str,
|
| 1888 |
-
|
| 1889 |
-
model: str = '
|
| 1890 |
) -> str:
|
| 1891 |
-
"""
|
| 1892 |
try:
|
|
|
|
| 1893 |
r = _httpx.post(
|
| 1894 |
-
|
| 1895 |
-
headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'},
|
| 1896 |
json={'model': model, 'messages': [{'role': 'user', 'content': prompt}],
|
| 1897 |
-
'
|
| 1898 |
-
timeout=
|
| 1899 |
)
|
| 1900 |
r.raise_for_status()
|
| 1901 |
-
return r.json()['
|
| 1902 |
except Exception as e:
|
| 1903 |
-
logger.error(f'
|
| 1904 |
-
return f'[
|
| 1905 |
|
| 1906 |
|
| 1907 |
def run_agentic_council(
|
|
@@ -1909,13 +1909,13 @@ def run_agentic_council(
|
|
| 1909 |
topic_df: Optional[pd.DataFrame],
|
| 1910 |
mistral_api_key: str = '',
|
| 1911 |
gemini_api_key: str = '',
|
| 1912 |
-
|
| 1913 |
anthropic_api_key: str = '', # kept for backward compat β not used
|
| 1914 |
) -> Dict[str, str]:
|
| 1915 |
-
"""Run 4-stage agentic council: Mistral β Gemini β
|
| 1916 |
|
| 1917 |
Returns dict with keys:
|
| 1918 |
-
'findings_summary', 'mistral', 'gemini', '
|
| 1919 |
"""
|
| 1920 |
gap = taxonomy_map.get('gap_analysis', {})
|
| 1921 |
novel_themes = taxonomy_map.get('novel_themes', [])[:5]
|
|
@@ -1963,31 +1963,23 @@ def run_agentic_council(
|
|
| 1963 |
else '[Gemini API key not provided]'
|
| 1964 |
)
|
| 1965 |
|
| 1966 |
-
# ββ Stage 3:
|
| 1967 |
-
logger.info('Council: calling
|
| 1968 |
-
|
| 1969 |
-
_call_deepseek(council_prompt, deepseek_api_key)
|
| 1970 |
-
if deepseek_api_key.strip()
|
| 1971 |
-
else '[DeepSeek API key not provided]'
|
| 1972 |
-
)
|
| 1973 |
|
| 1974 |
-
# ββ Stage 4:
|
| 1975 |
synthesis_prompt = SYNTHESIS_PROMPT_TEMPLATE.format(
|
| 1976 |
mistral_response=mistral_resp,
|
| 1977 |
gemini_response=gemini_resp,
|
| 1978 |
-
|
| 1979 |
-
)
|
| 1980 |
-
logger.info('Council: calling DeepSeek (synthesis judge)β¦')
|
| 1981 |
-
synthesis_resp = (
|
| 1982 |
-
_call_deepseek(synthesis_prompt, deepseek_api_key, model='deepseek-reasoner')
|
| 1983 |
-
if deepseek_api_key.strip()
|
| 1984 |
-
else '[DeepSeek API key not provided β synthesis skipped]'
|
| 1985 |
)
|
|
|
|
|
|
|
| 1986 |
|
| 1987 |
return {
|
| 1988 |
'findings_summary': findings,
|
| 1989 |
'mistral': mistral_resp,
|
| 1990 |
'gemini': gemini_resp,
|
| 1991 |
-
'
|
| 1992 |
'synthesis': synthesis_resp,
|
| 1993 |
}
|
|
|
|
| 1697 |
return f'[Gemini: {e}]'
|
| 1698 |
|
| 1699 |
|
| 1700 |
+
def _call_ollama_label(prompt: str, base_url: str = 'http://localhost:11434',
|
| 1701 |
+
model: str = 'llama3') -> str:
|
| 1702 |
+
"""Ollama local inference API."""
|
| 1703 |
try:
|
| 1704 |
+
url = f'{base_url.rstrip("/")}/api/chat'
|
| 1705 |
r = _httpx.post(
|
| 1706 |
+
url,
|
|
|
|
| 1707 |
json={'model': model, 'messages': [{'role': 'user', 'content': prompt}],
|
| 1708 |
+
'stream': False},
|
| 1709 |
+
timeout=30.0,
|
| 1710 |
)
|
| 1711 |
r.raise_for_status()
|
| 1712 |
+
return r.json()['message']['content'].strip()[:80]
|
| 1713 |
except Exception as e:
|
| 1714 |
+
return f'[Ollama: {e}]'
|
| 1715 |
|
| 1716 |
|
| 1717 |
def _label_prompt(titles: List[str]) -> str:
|
|
|
|
| 1731 |
embeddings: np.ndarray,
|
| 1732 |
mistral_api_key: str = '',
|
| 1733 |
gemini_api_key: str = '',
|
| 1734 |
+
ollama_url: str = 'http://localhost:11434',
|
| 1735 |
max_clusters: int = 30,
|
| 1736 |
) -> pd.DataFrame:
|
| 1737 |
+
"""Label each cluster using Mistral + Gemini + Ollama.
|
| 1738 |
|
| 1739 |
For each cluster:
|
| 1740 |
1. Selects 3 most representative paper titles (closest to centroid).
|
|
|
|
| 1749 |
embeddings : SPECTER2 embeddings aligned with cluster_df rows.
|
| 1750 |
mistral_api_key : Mistral API key (free tier).
|
| 1751 |
gemini_api_key : Google AI Studio API key (free tier).
|
| 1752 |
+
ollama_url : URL of the local Ollama instance.
|
| 1753 |
max_clusters : Cap API calls to this many clusters.
|
| 1754 |
|
| 1755 |
Returns:
|
|
|
|
| 1761 |
summary['label_mistral'] = ''
|
| 1762 |
if 'label_gemini' not in summary.columns:
|
| 1763 |
summary['label_gemini'] = ''
|
| 1764 |
+
if 'label_ollama' not in summary.columns:
|
| 1765 |
+
summary['label_ollama'] = ''
|
| 1766 |
|
| 1767 |
+
has_any_key = any([mistral_api_key.strip(), gemini_api_key.strip(), ollama_url.strip()])
|
| 1768 |
|
| 1769 |
for idx, row in summary.iterrows():
|
| 1770 |
if idx >= max_clusters:
|
|
|
|
| 1801 |
candidates.append(gl)
|
| 1802 |
_time.sleep(0.2)
|
| 1803 |
|
| 1804 |
+
if ollama_url.strip():
|
| 1805 |
+
ol = _call_ollama_label(prompt, ollama_url)
|
| 1806 |
+
summary.at[idx, 'label_ollama'] = ol
|
| 1807 |
+
candidates.append(ol)
|
| 1808 |
_time.sleep(0.2)
|
| 1809 |
|
| 1810 |
summary.at[idx, 'label'] = _majority_label(candidates) if candidates else 'Research Cluster'
|
|
|
|
| 1833 |
{mistral_response}
|
| 1834 |
Panel B (Gemini) said:
|
| 1835 |
{gemini_response}
|
| 1836 |
+
Panel C (Ollama) said:
|
| 1837 |
+
{ollama_response}
|
| 1838 |
Your task:
|
| 1839 |
1. Identify the 2-3 points ALL panels AGREE on (consensus insights)
|
| 1840 |
2. Identify where they DIVERGE and explain which view is most defensible
|
|
|
|
| 1864 |
return f'[Mistral unavailable: {e}]'
|
| 1865 |
|
| 1866 |
|
| 1867 |
+
def _call_gemini(prompt: str, api_key: str, model: str = 'gemini-1.5-flash') -> str:
|
| 1868 |
try:
|
| 1869 |
url = (f'https://generativelanguage.googleapis.com/v1beta/models/'
|
| 1870 |
f'{model}:generateContent?key={api_key}')
|
|
|
|
| 1883 |
return f'[Gemini unavailable: {e}]'
|
| 1884 |
|
| 1885 |
|
| 1886 |
+
def _call_ollama(
|
| 1887 |
prompt: str,
|
| 1888 |
+
base_url: str = 'http://localhost:11434',
|
| 1889 |
+
model: str = 'llama3',
|
| 1890 |
) -> str:
|
| 1891 |
+
"""Ollama API (Local). Uses llama3 by default."""
|
| 1892 |
try:
|
| 1893 |
+
url = f'{base_url.rstrip("/")}/api/chat'
|
| 1894 |
r = _httpx.post(
|
| 1895 |
+
url,
|
|
|
|
| 1896 |
json={'model': model, 'messages': [{'role': 'user', 'content': prompt}],
|
| 1897 |
+
'stream': False},
|
| 1898 |
+
timeout=120.0,
|
| 1899 |
)
|
| 1900 |
r.raise_for_status()
|
| 1901 |
+
return r.json()['message']['content'].strip()
|
| 1902 |
except Exception as e:
|
| 1903 |
+
logger.error(f'Ollama call failed: {e}')
|
| 1904 |
+
return f'[Ollama unavailable: {e}]'
|
| 1905 |
|
| 1906 |
|
| 1907 |
def run_agentic_council(
|
|
|
|
| 1909 |
topic_df: Optional[pd.DataFrame],
|
| 1910 |
mistral_api_key: str = '',
|
| 1911 |
gemini_api_key: str = '',
|
| 1912 |
+
ollama_url: str = 'http://localhost:11434',
|
| 1913 |
anthropic_api_key: str = '', # kept for backward compat β not used
|
| 1914 |
) -> Dict[str, str]:
|
| 1915 |
+
"""Run 4-stage agentic council: Mistral β Gemini β Ollama panels + Ollama synthesis.
|
| 1916 |
|
| 1917 |
Returns dict with keys:
|
| 1918 |
+
'findings_summary', 'mistral', 'gemini', 'ollama', 'synthesis'
|
| 1919 |
"""
|
| 1920 |
gap = taxonomy_map.get('gap_analysis', {})
|
| 1921 |
novel_themes = taxonomy_map.get('novel_themes', [])[:5]
|
|
|
|
| 1963 |
else '[Gemini API key not provided]'
|
| 1964 |
)
|
| 1965 |
|
| 1966 |
+
# ββ Stage 3: Ollama panel ββ
|
| 1967 |
+
logger.info('Council: calling Ollama (Panel C)β¦')
|
| 1968 |
+
ollama_resp = _call_ollama(council_prompt, ollama_url)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1969 |
|
| 1970 |
+
# ββ Stage 4: Ollama synthesis judge ββ
|
| 1971 |
synthesis_prompt = SYNTHESIS_PROMPT_TEMPLATE.format(
|
| 1972 |
mistral_response=mistral_resp,
|
| 1973 |
gemini_response=gemini_resp,
|
| 1974 |
+
ollama_response=ollama_resp,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1975 |
)
|
| 1976 |
+
logger.info('Council: calling Ollama (synthesis judge)β¦')
|
| 1977 |
+
synthesis_resp = _call_ollama(synthesis_prompt, ollama_url, model='llama3')
|
| 1978 |
|
| 1979 |
return {
|
| 1980 |
'findings_summary': findings,
|
| 1981 |
'mistral': mistral_resp,
|
| 1982 |
'gemini': gemini_resp,
|
| 1983 |
+
'ollama': ollama_resp,
|
| 1984 |
'synthesis': synthesis_resp,
|
| 1985 |
}
|