Compare commits

..

No commits in common. "d148b95534585b3c0c45fe5c13e50c5d377b34ec" and "15359e2ae3a6f502e8aa84adb3e456ad7ebd321b" have entirely different histories.

2 changed files with 27 additions and 60 deletions

View File

@ -1,5 +1,4 @@
ollama ollama
httpx
trilium-py trilium-py
gitpython gitpython
PyGithub PyGithub

View File

@ -4,25 +4,17 @@ Custom CrewAI tool that wraps Ollama's native web search API.
This tool allows CrewAI agents to perform web searches using an Ollama This tool allows CrewAI agents to perform web searches using an Ollama
subscription instead of third-party services like Serper or EXA. subscription instead of third-party services like Serper or EXA.
Uses direct HTTP requests via httpx with explicit Authorization: Bearer
header to ensure the OLLAMA_API_KEY is properly passed to the Ollama cloud
API endpoint (https://ollama.com/api/web_search).
Requires: Requires:
- httpx library: pip install httpx (already a transitive dependency of crewai) - Ollama Python library: pip install ollama
- OLLAMA_API_KEY environment variable set with your Ollama API key - OLLAMA_API_KEY environment variable set with your Ollama API key
Reference: https://docs.ollama.com/capabilities/web-search
""" """
import os import os
import httpx import ollama
from crewai.tools import BaseTool from crewai.tools import BaseTool
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
OLLAMA_WEB_SEARCH_URL = "https://ollama.com/api/web_search"
class OllamaWebSearchInput(BaseModel): class OllamaWebSearchInput(BaseModel):
"""Input schema for OllamaWebSearchTool.""" """Input schema for OllamaWebSearchTool."""
@ -50,10 +42,6 @@ class OllamaWebSearchTool(BaseTool):
The tool requires an Ollama subscription and the OLLAMA_API_KEY environment The tool requires an Ollama subscription and the OLLAMA_API_KEY environment
variable to be set. variable to be set.
Authentication is handled by sending the OLLAMA_API_KEY as a Bearer token
in the Authorization header, as documented at:
https://docs.ollama.com/capabilities/web-search
Example usage: Example usage:
from ai_generators.tools.ollama_web_search_tool import OllamaWebSearchTool from ai_generators.tools.ollama_web_search_tool import OllamaWebSearchTool
@ -77,9 +65,6 @@ class OllamaWebSearchTool(BaseTool):
""" """
Execute a web search and return formatted results. Execute a web search and return formatted results.
Makes a POST request to https://ollama.com/api/web_search with the
OLLAMA_API_KEY as a Bearer token in the Authorization header.
Args: Args:
query: The search query string query: The search query string
max_results: Maximum number of results to return (1-10) max_results: Maximum number of results to return (1-10)
@ -87,40 +72,16 @@ class OllamaWebSearchTool(BaseTool):
Returns: Returns:
Formatted string with search results, each containing title, URL, and content Formatted string with search results, each containing title, URL, and content
""" """
api_key = os.environ.get("OLLAMA_API_KEY")
if not api_key:
return (
"Error: OLLAMA_API_KEY environment variable is not set. "
"Please set your Ollama API key."
)
try: try:
response = httpx.post( # Ensure API key is set
OLLAMA_WEB_SEARCH_URL, if not os.environ.get("OLLAMA_API_KEY"):
json={"query": query, "max_results": max_results}, return "Error: OLLAMA_API_KEY environment variable is not set. Please set your Ollama API key."
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
timeout=30.0,
)
# Raise for HTTP errors so we can catch them with specific messages # Perform the web search
if response.status_code == 401: response = ollama.web_search(query=query, max_results=max_results)
return (
"Authentication error: OLLAMA_API_KEY was rejected. "
"Your key may be invalid or expired. Please verify it at "
"https://ollama.com/settings/keys"
)
if response.status_code == 429:
return (
"Rate limit exceeded: Too many search requests. "
"Please wait a moment and try again."
)
response.raise_for_status()
data = response.json() # Extract and format results
results = data.get("results", []) results = response.get("results", [])
if not results: if not results:
return f"No search results found for query: '{query}'" return f"No search results found for query: '{query}'"
@ -137,20 +98,27 @@ class OllamaWebSearchTool(BaseTool):
return "\n".join(formatted_results) return "\n".join(formatted_results)
except httpx.TimeoutException: except Exception as exc:
return f"Error performing web search: {exc}"
def _handle_exception(self, exc: Exception) -> str:
"""Handle exceptions gracefully and return a user-friendly error message."""
error_message = str(exc)
# Check for common error types
if "authentication" in error_message.lower() or "401" in error_message:
return ( return (
"Timeout error: The web search request timed out. " "Authentication error: Your OLLAMA_API_KEY may be invalid or expired. "
"Please try again with a simpler query." "Please check your API key and ensure it's set correctly in the environment."
) )
except httpx.ConnectError: elif "rate limit" in error_message.lower() or "429" in error_message:
return "Rate limit exceeded: Too many search requests. Please wait a moment and try again."
elif (
"network" in error_message.lower() or "connection" in error_message.lower()
):
return ( return (
"Network error: Unable to connect to Ollama's web search service. " "Network error: Unable to connect to Ollama's web search service. "
"Please check your internet connection and try again." "Please check your internet connection and try again."
) )
except httpx.HTTPStatusError as exc: else:
return ( return f"Search failed: {error_message}"
f"HTTP error {exc.response.status_code} from Ollama web search API: "
f"{exc.response.text}"
)
except Exception as exc:
return f"Error performing web search: {exc}"