Diomedes Git
commited on
Commit
·
e4effb4
1
Parent(s):
8d614a3
some cleanup, tweakery
Browse files- pyproject.toml +1 -0
- src/characters/check_char_init.py +55 -0
- src/characters/magpie.py +7 -6
- src/characters/src/data/memory.json +1 -0
- src/characters/src/data/observations.json +1 -0
- src/cluas_mcp/academic/thing.py +0 -2
- src/cluas_mcp/news/news_search_entrypoint.py +1 -0
- src/cluas_mcp/observation/observation_entrypoint.py +1 -0
- src/cluas_mcp/observation/sunrise_sunset.py +1 -0
- src/cluas_mcp/server.py +6 -5
- src/cluas_mcp/tool_router.py +0 -0
- src/cluas_mcp/tools.py +0 -20
- src/cluas_mcp/web/web_search.py +0 -68
- uv.lock +14 -0
pyproject.toml
CHANGED
|
@@ -14,6 +14,7 @@ dependencies = [
|
|
| 14 |
"groq>=0.36.0",
|
| 15 |
"mcp>=1.20.0",
|
| 16 |
"newsapi>=0.1.1",
|
|
|
|
| 17 |
"openaq>=0.5.0",
|
| 18 |
"python-dotenv>=1.2.1",
|
| 19 |
"pytrends>=4.9.2",
|
|
|
|
| 14 |
"groq>=0.36.0",
|
| 15 |
"mcp>=1.20.0",
|
| 16 |
"newsapi>=0.1.1",
|
| 17 |
+
"newsapi-python>=0.2.7",
|
| 18 |
"openaq>=0.5.0",
|
| 19 |
"python-dotenv>=1.2.1",
|
| 20 |
"pytrends>=4.9.2",
|
src/characters/check_char_init.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
# Add the parent directory of 'src' to sys.path to resolve imports
|
| 6 |
+
sys.path.append('/Users/gboa/cluas/')
|
| 7 |
+
|
| 8 |
+
# Disable logging for cleaner output
|
| 9 |
+
logging.getLogger().setLevel(logging.CRITICAL)
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
from src.characters.corvus import Corvus
|
| 13 |
+
from src.characters.crow import Crow
|
| 14 |
+
from src.characters.magpie import Magpie
|
| 15 |
+
from src.characters.raven import Raven
|
| 16 |
+
except ImportError as e:
|
| 17 |
+
print(f"Error importing character classes: {e}")
|
| 18 |
+
sys.exit(1)
|
| 19 |
+
|
| 20 |
+
characters = {
|
| 21 |
+
"Corvus": Corvus,
|
| 22 |
+
"Crow": Crow,
|
| 23 |
+
"Magpie": Magpie,
|
| 24 |
+
"Raven": Raven,
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
print("--- Initializing Characters ---")
|
| 28 |
+
|
| 29 |
+
# Set a dummy GROQ_API_KEY for testing purposes if it's not already set
|
| 30 |
+
# This allows the use_groq=True path to be tested without an actual key,
|
| 31 |
+
# but it will still raise an error if the key is explicitly checked for existence
|
| 32 |
+
# in the character's __init__ method (which is good, as it tests that check).
|
| 33 |
+
if "GROQ_API_KEY" not in os.environ:
|
| 34 |
+
os.environ["GROQ_API_KEY"] = "dummy_key_for_testing"
|
| 35 |
+
|
| 36 |
+
for name, char_class in characters.items():
|
| 37 |
+
print(f"Testing {name}...")
|
| 38 |
+
|
| 39 |
+
# Test with use_groq=True
|
| 40 |
+
try:
|
| 41 |
+
instance = char_class(use_groq=True)
|
| 42 |
+
print(f" ✅ {name} (use_groq=True) initialized successfully.")
|
| 43 |
+
except ValueError as e:
|
| 44 |
+
print(f" ❌ {name} (use_groq=True) failed to initialize: {e} (Expected if GROQ_API_KEY is missing or invalid)")
|
| 45 |
+
except Exception as e:
|
| 46 |
+
print(f" ❌ {name} (use_groq=True) failed to initialize with unexpected error: {e}")
|
| 47 |
+
|
| 48 |
+
# Test with use_groq=False (Ollama path)
|
| 49 |
+
try:
|
| 50 |
+
instance = char_class(use_groq=False)
|
| 51 |
+
print(f" ✅ {name} (use_groq=False) initialized successfully.")
|
| 52 |
+
except Exception as e:
|
| 53 |
+
print(f" ❌ {name} (use_groq=False) failed to initialize: {e}")
|
| 54 |
+
|
| 55 |
+
print("--- Character Initialization Test Complete ---")
|
src/characters/magpie.py
CHANGED
|
@@ -4,7 +4,8 @@ import asyncio
|
|
| 4 |
from typing import Optional, List, Dict
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
from groq import Groq
|
| 7 |
-
from src.cluas_mcp.web.web_search import search_web
|
|
|
|
| 8 |
|
| 9 |
from src.cluas_mcp.common.paper_memory import PaperMemory
|
| 10 |
from src.cluas_mcp.common.observation_memory import ObservationMemory
|
|
@@ -17,7 +18,7 @@ class Magpie:
|
|
| 17 |
def __init__(self, use_groq=True, location="Brooklyn, NY"):
|
| 18 |
self.name = "Magpie"
|
| 19 |
self.use_groq = use_groq
|
| 20 |
-
self.tools = ["search_web", "
|
| 21 |
self.trend_memory = TrendMemory()
|
| 22 |
self.paper_memory = PaperMemory()
|
| 23 |
self.observation_memory = ObservationMemory(location=location)
|
|
@@ -51,7 +52,7 @@ You're in a group chat, so keep it fun and engaging!
|
|
| 51 |
|
| 52 |
TOOLS AVAILABLE:
|
| 53 |
- search_web: Search the web for current information
|
| 54 |
-
-
|
| 55 |
- get_quick_facts: Get quick facts about any topic
|
| 56 |
|
| 57 |
When you need current information or want to share something interesting, use your tools!"""
|
|
@@ -135,7 +136,7 @@ When you need current information or want to share something interesting, use yo
|
|
| 135 |
{
|
| 136 |
"type": "function",
|
| 137 |
"function": {
|
| 138 |
-
"name": "
|
| 139 |
"description": "Find trending topics in a given category",
|
| 140 |
"parameters": {
|
| 141 |
"type": "object",
|
|
@@ -197,9 +198,9 @@ When you need current information or want to share something interesting, use yo
|
|
| 197 |
search_results = await loop.run_in_executor(None, search_web, query)
|
| 198 |
tool_result = self._format_web_search_for_llm(search_results)
|
| 199 |
|
| 200 |
-
elif tool_name == "
|
| 201 |
category = args.get("category", "general")
|
| 202 |
-
trending_results = await loop.run_in_executor(None,
|
| 203 |
tool_result = self._format_trending_topics_for_llm(trending_results)
|
| 204 |
|
| 205 |
elif tool_name == "get_quick_facts":
|
|
|
|
| 4 |
from typing import Optional, List, Dict
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
from groq import Groq
|
| 7 |
+
from src.cluas_mcp.web.web_search import search_web
|
| 8 |
+
from src.cluas_mcp.web.trending import fetch_trends
|
| 9 |
|
| 10 |
from src.cluas_mcp.common.paper_memory import PaperMemory
|
| 11 |
from src.cluas_mcp.common.observation_memory import ObservationMemory
|
|
|
|
| 18 |
def __init__(self, use_groq=True, location="Brooklyn, NY"):
|
| 19 |
self.name = "Magpie"
|
| 20 |
self.use_groq = use_groq
|
| 21 |
+
self.tools = ["search_web", "fetch_trends"]
|
| 22 |
self.trend_memory = TrendMemory()
|
| 23 |
self.paper_memory = PaperMemory()
|
| 24 |
self.observation_memory = ObservationMemory(location=location)
|
|
|
|
| 52 |
|
| 53 |
TOOLS AVAILABLE:
|
| 54 |
- search_web: Search the web for current information
|
| 55 |
+
- fetch_trend_topics: Find what's trending right now
|
| 56 |
- get_quick_facts: Get quick facts about any topic
|
| 57 |
|
| 58 |
When you need current information or want to share something interesting, use your tools!"""
|
|
|
|
| 136 |
{
|
| 137 |
"type": "function",
|
| 138 |
"function": {
|
| 139 |
+
"name": "fetch_trends",
|
| 140 |
"description": "Find trending topics in a given category",
|
| 141 |
"parameters": {
|
| 142 |
"type": "object",
|
|
|
|
| 198 |
search_results = await loop.run_in_executor(None, search_web, query)
|
| 199 |
tool_result = self._format_web_search_for_llm(search_results)
|
| 200 |
|
| 201 |
+
elif tool_name == "fetch_trends":
|
| 202 |
category = args.get("category", "general")
|
| 203 |
+
trending_results = await loop.run_in_executor(None, fetch_trends, category)
|
| 204 |
tool_result = self._format_trending_topics_for_llm(trending_results)
|
| 205 |
|
| 206 |
elif tool_name == "get_quick_facts":
|
src/characters/src/data/memory.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{}
|
src/characters/src/data/observations.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{}
|
src/cluas_mcp/academic/thing.py
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
from src.cluas_mcp.academic.pubmed import PubMedClient
|
| 2 |
-
print(hasattr(PubMedClient, "fetch_articles"))
|
|
|
|
|
|
|
|
|
src/cluas_mcp/news/news_search_entrypoint.py
CHANGED
|
@@ -9,3 +9,4 @@ def search_news_entrypoint(query: str, max_results: int = 5) -> dict:
|
|
| 9 |
Uses cascading fallbacks: NewsAPI -> DuckDuckGo -> Google -> Bing -> Mock.
|
| 10 |
"""
|
| 11 |
return search_news(query, max_results)
|
|
|
|
|
|
| 9 |
Uses cascading fallbacks: NewsAPI -> DuckDuckGo -> Google -> Bing -> Mock.
|
| 10 |
"""
|
| 11 |
return search_news(query, max_results)
|
| 12 |
+
|
src/cluas_mcp/observation/observation_entrypoint.py
CHANGED
|
@@ -127,3 +127,4 @@ def analyze_temporal_patterns(obs_type: str, location: Optional[str] = None, day
|
|
| 127 |
# }
|
| 128 |
|
| 129 |
|
|
|
|
|
|
| 127 |
# }
|
| 128 |
|
| 129 |
|
| 130 |
+
|
src/cluas_mcp/observation/sunrise_sunset.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from astral import LocationInfo
|
| 2 |
from astral.sun import sun
|
| 3 |
from datetime import datetime, date
|
|
|
|
| 4 |
|
| 5 |
LOCATIONS = {
|
| 6 |
"brooklyn": LocationInfo("Brooklyn", "USA", "America/New_York", 40.6782, -73.9442),
|
|
|
|
| 1 |
from astral import LocationInfo
|
| 2 |
from astral.sun import sun
|
| 3 |
from datetime import datetime, date
|
| 4 |
+
from typing import Optional
|
| 5 |
|
| 6 |
LOCATIONS = {
|
| 7 |
"brooklyn": LocationInfo("Brooklyn", "USA", "America/New_York", 40.6782, -73.9442),
|
src/cluas_mcp/server.py
CHANGED
|
@@ -6,8 +6,9 @@ from mcp.server.stdio import stdio_server
|
|
| 6 |
from mcp.types import Tool, TextContent
|
| 7 |
|
| 8 |
from src.cluas_mcp.academic.academic_search_entrypoint import academic_search
|
| 9 |
-
from src.cluas_mcp.web.web_search import search_web
|
| 10 |
-
from src.cluas_mcp.
|
|
|
|
| 11 |
from src.cluas_mcp.observation.observation_entrypoint import get_bird_sightings, get_weather_patterns, analyze_temporal_patterns
|
| 12 |
|
| 13 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -50,7 +51,7 @@ async def list_tools() -> list[Tool]:
|
|
| 50 |
}
|
| 51 |
),
|
| 52 |
Tool(
|
| 53 |
-
name="
|
| 54 |
description="Find trending topics in a given category",
|
| 55 |
inputSchema={
|
| 56 |
"type": "object",
|
|
@@ -216,9 +217,9 @@ async def call_tool(tool_name: str, arguments: dict) -> list[TextContent]:
|
|
| 216 |
formatted = format_web_search_results(results)
|
| 217 |
return [TextContent(type="text", text=formatted)]
|
| 218 |
|
| 219 |
-
elif tool_name == "
|
| 220 |
category = arguments.get("category", "general")
|
| 221 |
-
results = await loop.run_in_executor(None,
|
| 222 |
formatted = format_trending_topics(results)
|
| 223 |
return [TextContent(type="text", text=formatted)]
|
| 224 |
|
|
|
|
| 6 |
from mcp.types import Tool, TextContent
|
| 7 |
|
| 8 |
from src.cluas_mcp.academic.academic_search_entrypoint import academic_search
|
| 9 |
+
from src.cluas_mcp.web.web_search import search_web
|
| 10 |
+
from src.cluas_mcp.web.trending import fetch_trends
|
| 11 |
+
from src.cluas_mcp.news.news_search_entrypoint import search_news
|
| 12 |
from src.cluas_mcp.observation.observation_entrypoint import get_bird_sightings, get_weather_patterns, analyze_temporal_patterns
|
| 13 |
|
| 14 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 51 |
}
|
| 52 |
),
|
| 53 |
Tool(
|
| 54 |
+
name="fetch_trends",
|
| 55 |
description="Find trending topics in a given category",
|
| 56 |
inputSchema={
|
| 57 |
"type": "object",
|
|
|
|
| 217 |
formatted = format_web_search_results(results)
|
| 218 |
return [TextContent(type="text", text=formatted)]
|
| 219 |
|
| 220 |
+
elif tool_name == "fetch_trends":
|
| 221 |
category = arguments.get("category", "general")
|
| 222 |
+
results = await loop.run_in_executor(None, fetch_trends, category)
|
| 223 |
formatted = format_trending_topics(results)
|
| 224 |
return [TextContent(type="text", text=formatted)]
|
| 225 |
|
src/cluas_mcp/tool_router.py
DELETED
|
File without changes
|
src/cluas_mcp/tools.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
def search_academic_papers(
|
| 2 |
-
query: str,
|
| 3 |
-
topic: str = "corvid",
|
| 4 |
-
year_range: tuple[int, int] | None = None,
|
| 5 |
-
sort_by: str = "relevance",
|
| 6 |
-
max_results: int = 5
|
| 7 |
-
) -> list[dict] | None:
|
| 8 |
-
"""
|
| 9 |
-
Search academic papers on a given topic using arXiv and fallback APIs.
|
| 10 |
-
|
| 11 |
-
Args:
|
| 12 |
-
query: Search terms (e.g., "tool use in crows").
|
| 13 |
-
topic: Domain of interest (e.g., "corvid", "conservation").
|
| 14 |
-
year_range: Optional (start_year, end_year) filter.
|
| 15 |
-
sort_by: Sort results by "relevance" or "date".
|
| 16 |
-
max_results: Maximum number of results to return.
|
| 17 |
-
|
| 18 |
-
Returns:
|
| 19 |
-
List of paper metadata (dicts) or None if no results.
|
| 20 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/cluas_mcp/web/web_search.py
CHANGED
|
@@ -74,72 +74,4 @@ def _mock_search_web(query: str) -> dict:
|
|
| 74 |
"total_results": 2
|
| 75 |
}
|
| 76 |
|
| 77 |
-
def fetch_trending(category: str = "general") -> dict:
|
| 78 |
-
"""
|
| 79 |
-
Find trending topics in a given category.
|
| 80 |
-
|
| 81 |
-
TODO: Implement full trending topics functionality using a trends API.
|
| 82 |
-
|
| 83 |
-
Args:
|
| 84 |
-
category: Category to search for trends (e.g., "general", "technology", "science")
|
| 85 |
-
|
| 86 |
-
Returns:
|
| 87 |
-
Dictionary with trending topics
|
| 88 |
-
"""
|
| 89 |
-
logger.info("Finding trending topics for category: %s", category)
|
| 90 |
-
|
| 91 |
-
# Mock structured data
|
| 92 |
-
return {
|
| 93 |
-
"trending_topics": [
|
| 94 |
-
{
|
| 95 |
-
"topic": "Mock Trending Topic 1",
|
| 96 |
-
"category": category,
|
| 97 |
-
"trend_score": 95,
|
| 98 |
-
"description": "This is a mock trending topic. Real implementation would fetch actual trending data."
|
| 99 |
-
},
|
| 100 |
-
{
|
| 101 |
-
"topic": "Mock Trending Topic 2",
|
| 102 |
-
"category": category,
|
| 103 |
-
"trend_score": 87,
|
| 104 |
-
"description": "Another mock trending topic for demonstration purposes."
|
| 105 |
-
},
|
| 106 |
-
{
|
| 107 |
-
"topic": "Mock Trending Topic 3",
|
| 108 |
-
"category": category,
|
| 109 |
-
"trend_score": 82,
|
| 110 |
-
"description": "Third mock trending topic to show structure."
|
| 111 |
-
}
|
| 112 |
-
],
|
| 113 |
-
"category": category
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
def get_quick_facts(topic: str) -> dict:
|
| 117 |
-
"""
|
| 118 |
-
Get quick facts about a topic.
|
| 119 |
-
|
| 120 |
-
TODO: Implement full quick facts functionality using a knowledge API.
|
| 121 |
-
|
| 122 |
-
Args:
|
| 123 |
-
topic: Topic to get facts about
|
| 124 |
-
|
| 125 |
-
Returns:
|
| 126 |
-
Dictionary with quick facts
|
| 127 |
-
"""
|
| 128 |
-
logger.info("Getting quick facts for topic: %s", topic)
|
| 129 |
-
|
| 130 |
-
# Mock structured data
|
| 131 |
-
return {
|
| 132 |
-
"topic": topic,
|
| 133 |
-
"facts": [
|
| 134 |
-
f"Mock fact 1 about {topic}: This is a placeholder fact that would be replaced with real data in a full implementation.",
|
| 135 |
-
f"Mock fact 2 about {topic}: Another placeholder fact demonstrating the expected structure.",
|
| 136 |
-
f"Mock fact 3 about {topic}: A third placeholder fact to show the format."
|
| 137 |
-
],
|
| 138 |
-
"source": "mock_data"
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
|
|
|
|
| 74 |
"total_results": 2
|
| 75 |
}
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
uv.lock
CHANGED
|
@@ -341,6 +341,7 @@ dependencies = [
|
|
| 341 |
{ name = "groq" },
|
| 342 |
{ name = "mcp" },
|
| 343 |
{ name = "newsapi" },
|
|
|
|
| 344 |
{ name = "openaq" },
|
| 345 |
{ name = "python-dotenv" },
|
| 346 |
{ name = "pytrends" },
|
|
@@ -365,6 +366,7 @@ requires-dist = [
|
|
| 365 |
{ name = "groq", specifier = ">=0.36.0" },
|
| 366 |
{ name = "mcp", specifier = ">=1.20.0" },
|
| 367 |
{ name = "newsapi", specifier = ">=0.1.1" },
|
|
|
|
| 368 |
{ name = "openaq", specifier = ">=0.5.0" },
|
| 369 |
{ name = "python-dotenv", specifier = ">=1.2.1" },
|
| 370 |
{ name = "pytrends", specifier = ">=4.9.2" },
|
|
@@ -1156,6 +1158,18 @@ wheels = [
|
|
| 1156 |
{ url = "https://files.pythonhosted.org/packages/56/70/df0dd31067f03703d06f66b6c3f324915bd43bc8a597ee1a7eef27c9e622/newsapi-0.1.1-py2.py3-none-any.whl", hash = "sha256:baef702f6e29fc2736932858528d94f18b2baaef9d330018ba78369e005e1df4", size = 4127, upload-time = "2017-03-28T22:10:12.306Z" },
|
| 1157 |
]
|
| 1158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1159 |
[[package]]
|
| 1160 |
name = "numpy"
|
| 1161 |
version = "2.3.5"
|
|
|
|
| 341 |
{ name = "groq" },
|
| 342 |
{ name = "mcp" },
|
| 343 |
{ name = "newsapi" },
|
| 344 |
+
{ name = "newsapi-python" },
|
| 345 |
{ name = "openaq" },
|
| 346 |
{ name = "python-dotenv" },
|
| 347 |
{ name = "pytrends" },
|
|
|
|
| 366 |
{ name = "groq", specifier = ">=0.36.0" },
|
| 367 |
{ name = "mcp", specifier = ">=1.20.0" },
|
| 368 |
{ name = "newsapi", specifier = ">=0.1.1" },
|
| 369 |
+
{ name = "newsapi-python", specifier = ">=0.2.7" },
|
| 370 |
{ name = "openaq", specifier = ">=0.5.0" },
|
| 371 |
{ name = "python-dotenv", specifier = ">=1.2.1" },
|
| 372 |
{ name = "pytrends", specifier = ">=4.9.2" },
|
|
|
|
| 1158 |
{ url = "https://files.pythonhosted.org/packages/56/70/df0dd31067f03703d06f66b6c3f324915bd43bc8a597ee1a7eef27c9e622/newsapi-0.1.1-py2.py3-none-any.whl", hash = "sha256:baef702f6e29fc2736932858528d94f18b2baaef9d330018ba78369e005e1df4", size = 4127, upload-time = "2017-03-28T22:10:12.306Z" },
|
| 1159 |
]
|
| 1160 |
|
| 1161 |
+
[[package]]
|
| 1162 |
+
name = "newsapi-python"
|
| 1163 |
+
version = "0.2.7"
|
| 1164 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1165 |
+
dependencies = [
|
| 1166 |
+
{ name = "requests" },
|
| 1167 |
+
]
|
| 1168 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/4b/12fb9495211fc5a6d3a96968759c1a48444124a1654aaf65d0de80b46794/newsapi-python-0.2.7.tar.gz", hash = "sha256:a4b66d5dd9892198cdaa476f7542f2625cdd218e5e3121c8f880b2ace717a3c2", size = 7485, upload-time = "2023-03-02T13:15:35.89Z" }
|
| 1169 |
+
wheels = [
|
| 1170 |
+
{ url = "https://files.pythonhosted.org/packages/74/47/e3b099102f0c826d37841d2266e19f1568dcf58ba86e4c6948e2a124f91d/newsapi_python-0.2.7-py2.py3-none-any.whl", hash = "sha256:11d34013a24d92ca7b7cbdac84ed2d504862b1e22467bc2a9a6913a70962318e", size = 7942, upload-time = "2023-03-02T13:15:34.475Z" },
|
| 1171 |
+
]
|
| 1172 |
+
|
| 1173 |
[[package]]
|
| 1174 |
name = "numpy"
|
| 1175 |
version = "2.3.5"
|