Merge pull request 'udpate to use bearer token correctly' (#21) from CREWAI-integration into master
Some checks failed
Create Blog Article if new notes exist / prepare_blog_drafts_and_push (push) Failing after 19m39s

Reviewed-on: #21
This commit is contained in:
armistace 2026-04-29 09:40:08 +10:00
commit d148b95534
2 changed files with 60 additions and 27 deletions

View File

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

View File

@ -4,17 +4,25 @@ 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:
- Ollama Python library: pip install ollama - httpx library: pip install httpx (already a transitive dependency of crewai)
- 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 ollama import httpx
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."""
@ -42,6 +50,10 @@ 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
@ -65,6 +77,9 @@ 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)
@ -72,16 +87,40 @@ 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:
# Ensure API key is set response = httpx.post(
if not os.environ.get("OLLAMA_API_KEY"): OLLAMA_WEB_SEARCH_URL,
return "Error: OLLAMA_API_KEY environment variable is not set. Please set your Ollama API key." json={"query": query, "max_results": max_results},
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
timeout=30.0,
)
# Perform the web search # Raise for HTTP errors so we can catch them with specific messages
response = ollama.web_search(query=query, max_results=max_results) if response.status_code == 401:
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()
# Extract and format results data = response.json()
results = response.get("results", []) results = data.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}'"
@ -98,27 +137,20 @@ class OllamaWebSearchTool(BaseTool):
return "\n".join(formatted_results) return "\n".join(formatted_results)
except Exception as exc: except httpx.TimeoutException:
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 (
"Authentication error: Your OLLAMA_API_KEY may be invalid or expired. " "Timeout error: The web search request timed out. "
"Please check your API key and ensure it's set correctly in the environment." "Please try again with a simpler query."
) )
elif "rate limit" in error_message.lower() or "429" in error_message: except httpx.ConnectError:
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."
) )
else: except httpx.HTTPStatusError as exc:
return f"Search failed: {error_message}" return (
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}"