Find
Search dashboard pages and product views.
Search dashboard pages and product views.
jjleng/sensei
Add the ability for this repository's agent to fetch and extract clean, readable content from the web pages/URLs it encounters, so it can use that content in its workflow. Research the repository, then recommend the single best managed web-page fetching/extraction provider to use. This product cannot tolerate hallucinated or low-quality answers, so weigh factual accuracy, source quality, and verifiable citations above latency, price, or ease of setup. Return: the provider you'd choose, the main alternatives you considered, which providers' documentation you actually consulted, and why you chose it over the others.
P1
448265f1fedfc0f6084499458ad9c445b54d1f68
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: EISDIR: illegal operation on a directory, read '/home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1' Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/README.md /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/README.md /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/frontend/README.md Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 # Sensei Search 2 3 Sensei Search is an AI-powered answer engine. 4 5 ## 📸 Screenshots 6 7 8 ### Light Mode 9 <div align="center"> 10 <img src="./docs/lennon.png" alt="Screenshot - Lennon" max-width="600"> 11 </div> 12 13 ### Dark Mode 14 <div align="center"> 15 <img src="./docs/programming.png" alt="Screenshot - Programming" max-width="600"> 16 </div> 17 18 19 ## 💡 Insights from Utilizing Open Source LLMs 20 21 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: <system-reminder>Warning: the file exists but is shorter than the provided offset (1). The file has 1 lines.</system-reminder> Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: <system-reminder>Warning: the file exists but is shorter than the provided offset (1). The file has 1 lines.</system-reminder> Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: total 72 drwxr-xr-x 6 user user 4096 Jun 6 04:56 . drwxr-xr-x 4 user user 4096 Jun 6 04:56 .. -rw-r--r-- 1 user user 170 Jun 6 04:56 .git -rw-r--r-- 1 user user 172 Jun 6 04:56 .gitignore -rw-r--r-- 1 user user 337 Jun 6 04:56 .vercelignore -rw-r--r-- 1 user user 11356 Jun 6 04:56 LICENSE -rw-r--r-- 1 user user 593 Jun 6 04:56 Makefile -rw-r--r-- 1 user user 2859 Jun 6 04:56 README.md drwxr-xr-x 3 user user 4096 Jun Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: total 436 drwxr-xr-x 3 user user 4096 Jun 6 04:56 . drwxr-xr-x 6 user user 4096 Jun 6 04:56 .. -rw-r--r-- 1 user user 44 Jun 6 04:56 .buildpacks.env -rw-r--r-- 1 user user 29 Jun 6 04:56 .cnignore -rw-r--r-- 1 user user 23 Jun 6 04:56 .dockerignore -rw-r--r-- 1 user user 328 Jun 6 04:56 .env.development.example -rw-r--r-- 1 user user 196 Jun 6 04:56 .env.production.example -rw-r--r-- 1 user user 1481 Jun 6 04:56 .git Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: total 360 drwxr-xr-x 4 user user 4096 Jun 6 04:56 . drwxr-xr-x 6 user user 4096 Jun 6 04:56 .. -rw-r--r-- 1 user user 104 Jun 6 04:56 .buildpacks.env -rw-r--r-- 1 user user 13 Jun 6 04:56 .cnignore -rw-r--r-- 1 user user 24 Jun 6 04:56 .dockerignore -rw-r--r-- 1 user user 77 Jun 6 04:56 .env.development -rw-r--r-- 1 user user 37 Jun 6 04:56 .env.production -rw-r--r-- 1 user user 40 Jun 6 04:56 .eslintrc.json -rw-r--r Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/agents/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/agents/samurai/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/ Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 [tool.poetry] 2 name = "sensei-search" 3 version = "0.1.0" 4 description = "" 5 authors = ["Jijun Leng"] 6 readme = "README.md" 7 8 [tool.poetry.dependencies] 9 python = "^3.9" 10 python-dotenv = "^1.0.1" 11 openai = "^1.30.5" 12 loguru = "^0.7.2" 13 fastapi = "^0.111.0" 14 python-socketio = "^5.11.2" 15 uvicorn = "^0.30.1" 16 aiohttp = "^3.9.5" 17 trafilatura = "^1.10.0" 18 redis = "^5.0.5" 19 typing-extensions = Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Provider mentions: openai
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 LOGURU_LEVEL=DEBUG 2 SEARXNG_URL="http://searxng:8080" 3 REDIS_HOST="redis" 4 BING_API_KEY="" 5 6 # Small model 7 SM_MODEL_URL="http://host.docker.internal:11434/v1/" 8 SM_MODEL="llama3:instruct" 9 SM_MODEL_API_KEY="whatever" 10 11 # Medium model 12 MD_MODEL_URL="http://host.docker.internal:11434/v1/" 13 MD_MODEL="command-r" 14 MD_MODEL_API_KEY="whatever" Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import uuid 5 from abc import ABC, abstractmethod 6 from enum import Enum 7 from typing import Dict, List, Literal, Optional, Protocol, Union 8 9 import trafilatura # type: ignore[import] 10 from aiohttp import ClientSession, ClientTimeout 11 from pydantic import BaseModel, Field 12 from typing_extensions import TypedDict 13 14 from sensei_search.chat_store im Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import asyncio 2 import re 3 from datetime import datetime 4 from typing import Any, List, Optional 5 6 from openai import AsyncOpenAI, OpenAI 7 8 from sensei_search.agents.samurai.prompts import ( 9 answer_prompt, 10 classification_prompt, 11 related_questions_prompt, 12 search_prompt, 13 ) 14 from sensei_search.base_agent import BaseAgent, EnrichedQuery, NoAccessError, QueryTags 15 from sensei_search.chat_store i Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Provider mentions: openai
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import asyncio 2 import json 3 import re 4 from datetime import datetime 5 from typing import Any, List, Optional, Tuple 6 7 from openai import AsyncOpenAI 8 from openai.types.chat import ChatCompletionMessageParam, ChatCompletionMessageToolCall 9 from openai.types.chat.chat_completion_message_tool_call import Function 10 11 from sensei_search.agents.shogun.prompts import ( 12 answer_prompt, 13 classification_promp Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Provider mentions: openai
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import os 5 from abc import ABC, abstractmethod 6 from enum import Enum 7 from typing import Any, List, Union 8 from urllib.parse import urljoin 9 10 from aiohttp import ClientSession 11 from pydantic import BaseModel, Field 12 from typing_extensions import TypedDict 13 14 from sensei_search.logger import logger 15 16 17 class BaseResult(TypedDict): 18 url: st Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 from urllib.parse import urljoin 4 5 from aiohttp import ClientSession 6 7 from sensei_search.config import SEARXNG_URL 8 from sensei_search.logger import logger 9 from sensei_search.tools.search.base import ( 10 Input, 11 SearchTool, 12 TopResults, 13 filter_medium_by_accessibility, 14 get_top_results, 15 ) 16 17 MAX_RESULTS = 5 18 19 20 async def filter_medium_by_scores(resu Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import os 2 3 from sensei_search.env import load_envs 4 5 load_envs() 6 7 SEARXNG_URL = os.environ["SEARXNG_URL"] 8 REDIS_HOST = os.environ["REDIS_HOST"] 9 BING_API_KEY = os.environ["BING_API_KEY"] 10 11 # Small model 12 SM_MODEL_URL = os.environ["SM_MODEL_URL"] 13 SM_MODEL = os.environ["SM_MODEL"] 14 SM_MODEL_API_KEY = os.environ["SM_MODEL_API_KEY"] 15 16 # Medium model 17 MD_MODEL_URL = os.environ["MD_MODEL_URL"] Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from .search import * 2 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import os 2 3 from .base import * 4 from .bing import * 5 from .searxng import * 6 7 8 def get_search_tool() -> SearchTool: 9 env = os.getenv("ENV", "development") 10 if env == "development": 11 return SearxNG() 12 else: 13 return Bing() 14 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/tools/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/tools/search/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/tools/search/base.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/ba Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import os 5 from typing import Any, Callable, Coroutine, Dict, List, TypeVar 6 7 from aiohttp import ClientSession 8 9 from sensei_search.config import BING_API_KEY 10 from sensei_search.logger import logger 11 from sensei_search.tools.search.base import ( 12 Category, 13 GeneralResult, 14 ImageResult, 15 Input, 16 SearchTool, 17 TopResults, 18 VideoResult, 19 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import os 5 from typing import Dict, List, Optional 6 7 import socketio # type: ignore[import-untyped] 8 from fastapi import FastAPI 9 from fastapi.middleware.cors import CORSMiddleware 10 11 from sensei_search.agents.shogun.agent_v2 import ShogunAgent 12 from sensei_search.base_agent import NoAccessError 13 from sensei_search.chat_store import ChatStore 14 fr Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from typing import List, Optional, Union 2 3 from typing_extensions import TypedDict 4 5 6 class WebResult(TypedDict): 7 url: str 8 title: str 9 content: str 10 11 12 class MediumVideo(TypedDict): 13 url: str 14 medium: str 15 16 17 class MediumImage(TypedDict): 18 url: str 19 image: str 20 medium: str 21 22 23 class MetaData(TypedDict): 24 has_math: bool 25 26 27 class ChatHistoryItem(TypedDict, total=False): 28 i Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import os 2 from pathlib import Path 3 4 from loguru import logger 5 6 7 def load_envs() -> None: 8 from dotenv import load_dotenv 9 10 if getattr(load_envs, "_loaded", False): 11 return 12 13 base_dir = Path(__file__).resolve().parent.parent 14 15 env = os.getenv("ENV", "development") 16 logger.info(f"Loading environment: {env}") 17 18 if env == "development": 19 dotenv_path = base_dir / ".env.development" 20 elif Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 { 2 "name": "sensei-search", 3 "version": "0.1.0", 4 "private": true, 5 "scripts": { 6 "dev": "next dev", 7 "build": "next build", 8 "start": "next start", 9 "lint": "next lint" 10 }, 11 "dependencies": { 12 "@radix-ui/react-dialog": "^1.1.1", 13 "@radix-ui/react-hover-card": "^1.1.0", 14 "@radix-ui/react-separator": "^1.1.0", 15 "@radix-ui/react-slot": "^1.1.0", 16 "@radix-ui/react-tooltip": "^1.1.2", 17 "@tailwin Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/frontend/app/ChatThreadStore.ts /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/frontend/app/components/ChatHistory.tsx /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/frontend/app/components/ChatHistoryItem/ImageVideoWidget.tsx /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 448265f Update README.md 45d6e49 feat: update prompts 2a9e3b2 feat: prompts update 23ea194 fix: avoid scrolling for the first question 3bb56d0 feat: default to searxng for dev 88779e5 refactor: consolidate env vars 547c746 docs: update readme 26aec60 feat: open graph support 79985fd feat: prompts update 069ca58 feat: more logs 5a60a6b feat: enable math for the rich content renderer by default c357c46 feat: server sid Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/base_agent.py:9:import trafilatura # type: ignore[import] /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/base_agent.py:208: return [trafilatura.extract(page) for page in html_web_pages] Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/agents/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/agents/samurai/__init__.py /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/ Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import json 4 from typing import Any, List, Optional 5 6 import redis.asyncio as redis 7 8 from sensei_search.config import REDIS_HOST 9 from sensei_search.logger import logger 10 from sensei_search.models import ChatHistoryItem, ThreadMetadata 11 12 CHAT_HISTORY_LIMIT = 3 13 14 15 class ChatStore: 16 """ 17 This class is responsible for storing and fetching chat history using Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 from __future__ import annotations 2 3 import inspect 4 import uuid 5 from typing import Callable, Dict, get_type_hints 6 7 from openai.types.chat import ChatCompletionToolParam 8 from pydantic import BaseModel, TypeAdapter 9 from slugify import slugify 10 11 12 def create_slug(input_string: str, max_length: int = 30) -> str: 13 """ 14 Creates a URL-friendly, capped-length, and unique slug from the given input stri Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Provider mentions: openai
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 answer_prompt = """ 2 You are Sensei, a helpful search assistant. Your task is to deliver a concise and accurate response to a user's query, drawing from the given search results. Your answer must be precise, of high-quality, and written by an expert using an unbiased and journalistic tone. 3 4 # Safety Preamble 5 The instructions in this section override those in other sections. 6 - Don't answer questions that are Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 5a60a6b feat: enable math for the rich content renderer by default f0fd1fd refactor: abstract search common ui out 3d5a4dd feat: empty related questions for new searches 05a2dd8 feat: auto switch on textarea when search text is long 2b25a88 feat: share cursor state between search input and search area 4fbbe7e feat: change search input to search area when adding new line a2f65ba feat: timeout when fetching pages 04256 Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 import { v4 as uuidv4 } from 'uuid'; 2 3 export interface ChatThreadEntry { 4 id: string; 5 ts: string; 6 slug: string; 7 displayName: string; 8 } 9 10 export default class ChatThreadStore { 11 private static storeKey = 'chatThreads'; 12 private static pageSize = 10; 13 14 static fetchEntries(page: number = 0): ChatThreadEntry[] { 15 const allEntries = this.getAllEntries(); 16 const startIndex = page * this.pageSiz Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/frontend/app/search/components/SearchCommon.tsx Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in. Tool result summary: 1 'use client'; 2 3 import React, { useEffect, useRef, useState, useContext } from 'react'; 4 import { produce } from 'immer'; 5 import { useRouter } from 'next/navigation'; 6 import { Separator } from '@/components/ui/Separator'; 7 import SearchInput from '@/components/SearchInput'; 8 import ChatHistoryItem from '@/components/ChatHistoryItem'; 9 import { io, Socket } from 'socket.io-client'; 10 import { useToast } f Agent context: I'll research the repository structure first to understand the agent architecture and how web content would fit in.
Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing. Let me read the key files to understand the exact current implementation before recommending and implementing.
Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import uuid 5 from abc import ABC, abstractmethod 6 from enum import Enum 7 from typing import Dict, List, Literal, Optional, Protocol, Union 8 9 import trafilatura # type: ignore[import] 10 from aiohttp import ClientSession, ClientTimeout 11 from pydantic import BaseModel, Field 12 from typing_extensions import TypedDict 13 14 from sensei_search.chat_store im Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing.
Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing. Tool result summary: 1 import os 2 3 from sensei_search.env import load_envs 4 5 load_envs() 6 7 SEARXNG_URL = os.environ["SEARXNG_URL"] 8 REDIS_HOST = os.environ["REDIS_HOST"] 9 BING_API_KEY = os.environ["BING_API_KEY"] 10 11 # Small model 12 SM_MODEL_URL = os.environ["SM_MODEL_URL"] 13 SM_MODEL = os.environ["SM_MODEL"] 14 SM_MODEL_API_KEY = os.environ["SM_MODEL_API_KEY"] 15 16 # Medium model 17 MD_MODEL_URL = os.environ["MD_MODEL_URL"] Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing.
Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing. Tool result summary: 1 import os 2 3 from .base import * 4 from .bing import * 5 from .searxng import * 6 7 8 def get_search_tool() -> SearchTool: 9 env = os.getenv("ENV", "development") 10 if env == "development": 11 return SearxNG() 12 else: 13 return Bing() 14 Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing.
Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import os 5 from abc import ABC, abstractmethod 6 from enum import Enum 7 from typing import Any, List, Union 8 from urllib.parse import urljoin 9 10 from aiohttp import ClientSession 11 from pydantic import BaseModel, Field 12 from typing_extensions import TypedDict 13 14 from sensei_search.logger import logger 15 16 17 class BaseResult(TypedDict): 18 url: st Agent context: Let me read the key files to understand the exact current implementation before recommending and implementing.
Agent context: Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools. Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools.
Agent context: Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools. Tool result summary: 1 import asyncio 2 import json 3 import re 4 from datetime import datetime 5 from typing import Any, List, Optional, Tuple 6 7 from openai import AsyncOpenAI 8 from openai.types.chat import ChatCompletionMessageParam, ChatCompletionMessageToolCall 9 from openai.types.chat.chat_completion_message_tool_call import Function 10 11 from sensei_search.agents.shogun.prompts import ( 12 answer_prompt, 13 classification_promp Agent context: Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools. Provider mentions: openai
Agent context: Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools. Tool result summary: backend/.env.development.example backend/.env.production.example Agent context: Now let me look at how agents call `fetch_web_pages` and the env examples, plus load web research tools.
Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement. Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement.
Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement. Tool result summary: 1 LOGURU_LEVEL=DEBUG 2 SEARXNG_URL="http://searxng:8080" 3 REDIS_HOST="redis" 4 BING_API_KEY="" 5 6 # Small model 7 SM_MODEL_URL="http://host.docker.internal:11434/v1/" 8 SM_MODEL="llama3:instruct" 9 SM_MODEL_API_KEY="whatever" 10 11 # Medium model 12 MD_MODEL_URL="http://host.docker.internal:11434/v1/" 13 MD_MODEL="command-r" 14 MD_MODEL_API_KEY="whatever" Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement.
Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement. Tool result summary: 1 LOGURU_LEVEL= 2 SEARXNG_URL= 3 REDIS_HOST= 4 BING_API_KEY= 5 # CORS origins 6 CORS_ORIGINS= 7 8 # Small model 9 SM_MODEL_URL= 10 SM_MODEL= 11 SM_MODEL_API_KEY= 12 13 # Medium model 14 MD_MODEL_URL= 15 MD_MODEL= 16 MD_MODEL_API_KEY= Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement.
Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement. Tool result summary: 1 from typing import List, Optional, Union 2 3 from typing_extensions import TypedDict 4 5 6 class WebResult(TypedDict): 7 url: str 8 title: str 9 content: str 10 11 12 class MediumVideo(TypedDict): 13 url: str 14 medium: str 15 16 17 class MediumImage(TypedDict): 18 url: str 19 image: str 20 medium: str 21 22 23 class MetaData(TypedDict): 24 has_math: bool 25 26 27 class ChatHistoryItem(TypedDict, total=False): 28 i Agent context: Key finding: the in-use Shogun agent only uses search snippets (`result['content']`), not full page content — `fetch_web_pages` exists in base but isn't called by Shogun. I'll research managed extraction providers by consulting their actual docs, then implement.
Agent context: Now let me research the main managed extraction providers by consulting their actual documentation. Now let me research the main managed extraction providers by consulting their actual documentation.
Query/prompt: Describe the /scrape endpoint: what it does, request format, the formats it can return (markdown, html), how it handles JavaScript-rendered pages, content cleaning/extraction quality, onlyMainContent option, authentication, and any features relevant to faithful/accurate content extraction for LLM use. Summarize concisely. Agent context: Now let me research the main managed extraction providers by consulting their actual documentation. Fetch prompt: Describe the /scrape endpoint: what it does, request format, the formats it can return (markdown, html), how it handles JavaScript-rendered pages, content cleaning/extraction quality, onlyMainContent option, authentication, and any features relevant to faithful/accurate content extraction for LLM use. Summarize concisely. Tool result summary: # Firecrawl /scrape Endpoint Summary ## Core Function The `/scrape` endpoint extracts content from a single URL and optionally uses an LLM to process the extracted information. ## Request Format POST requests require a URL and optional parameters via JSON. Authentication uses bearer token security. ## Output Formats The endpoint supports multiple return types: - **Markdown** (default) - primary content converted to m Agent context: Now let me research the main managed extraction providers by consulting their actual documentation.
Query/prompt: Describe Jina Reader (r.jina.ai): what it does, how to call it (URL format), what content it returns (clean markdown), how it handles JavaScript pages, accuracy of extraction, pricing/rate limits, API key usage, and any features related to factual accuracy or citations. Summarize concisely. Agent context: Now let me research the main managed extraction providers by consulting their actual documentation. Fetch prompt: Describe Jina Reader (r.jina.ai): what it does, how to call it (URL format), what content it returns (clean markdown), how it handles JavaScript pages, accuracy of extraction, pricing/rate limits, API key usage, and any features related to factual accuracy or citations. Summarize concisely. Tool result summary: # Jina Reader (r.jina.ai) Summary ## What It Does Jina Reader converts web URLs into clean, LLM-friendly markdown text by extracting core content and removing clutter like scripts and markup. It also supports PDF reading and web search capabilities. ## How to Call It **URL Format:** Simply prepend `https://r.jina.ai/` before any target URL. For example: `https://r.jina.ai/https://www.example.com` Alternatively, use ` Agent context: Now let me research the main managed extraction providers by consulting their actual documentation.
Query/prompt: Describe Tavily's Extract endpoint: what it does, request/response format, extraction depth options, content quality, how it handles multiple URLs, authentication, and any features relevant to factual accuracy and citations for LLM/RAG use. Summarize concisely. Agent context: Now let me research the main managed extraction providers by consulting their actual documentation. Fetch prompt: Describe Tavily's Extract endpoint: what it does, request/response format, extraction depth options, content quality, how it handles multiple URLs, authentication, and any features relevant to factual accuracy and citations for LLM/RAG use. Summarize concisely. Tool result summary: # Tavily Extract Endpoint Summary **Purpose**: Extracts raw web content from one or more URLs, optimized for LLM agents and RAG systems. **Request Format**: POST to `https://api.tavily.com/extract` with JSON body containing: - `urls` (required): single URL string or array of up to 20 URLs - `query` (optional): intent for reranking content chunks by relevance - `chunks_per_source` (1-5, default 3): controls snippet qu Agent context: Now let me research the main managed extraction providers by consulting their actual documentation.
Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use. Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use.
Query/prompt: Describe Exa's Contents endpoint (/contents): what it does, how it retrieves clean page text, livecrawl options, highlights, ability to fetch contents for arbitrary URLs, response format, authentication, and any features relevant to factual accuracy, source quality, and citations for LLM/RAG use. Summarize concisely. Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use. Fetch prompt: Describe Exa's Contents endpoint (/contents): what it does, how it retrieves clean page text, livecrawl options, highlights, ability to fetch contents for arbitrary URLs, response format, authentication, and any features relevant to factual accuracy, source quality, and citations for LLM/RAG use. Summarize concisely. Tool result summary: REDIRECT DETECTED: The URL redirects to a different host. Original URL: https://docs.exa.ai/reference/get-contents Redirect URL: https://exa.ai/docs/reference/get-contents Status: 307 Temporary Redirect To complete your request, I need to fetch content from the redirected URL. Please use WebFetch again with these parameters: - url: "https://exa.ai/docs/reference/get-contents" - prompt: "Describe Exa's Contents endpoi Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use.
Query/prompt: What are the official Python SDK details for Firecrawl? Package name on PyPI, how to install, how to instantiate the client with an API key, and how to call scrape on a single URL to get markdown. Include whether there is an async client. Summarize concisely with code. Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use. Fetch prompt: What are the official Python SDK details for Firecrawl? Package name on PyPI, how to install, how to instantiate the client with an API key, and how to call scrape on a single URL to get markdown. Include whether there is an async client. Summarize concisely with code. Tool result summary: # Firecrawl Python SDK Based on the documentation provided, here are the key details: **Package & Installation:** The SDK is installed via PyPI using `pip install firecrawl` (package name inferred from import statements shown). **Client Instantiation:** ```python from firecrawl import Firecrawl firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY") ``` **Scraping a Single URL for Markdown:** ```python doc = firecrawl.scrap Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use.
Query/prompt: Describe Exa's Contents endpoint (/contents): what it does, how it retrieves clean page text for arbitrary URLs, livecrawl options, highlights, response format, authentication, and any features relevant to factual accuracy, source quality, and citations for LLM/RAG use. Summarize concisely. Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use. Fetch prompt: Describe Exa's Contents endpoint (/contents): what it does, how it retrieves clean page text for arbitrary URLs, livecrawl options, highlights, response format, authentication, and any features relevant to factual accuracy, source quality, and citations for LLM/RAG use. Summarize concisely. Tool result summary: # Exa's Contents Endpoint: Overview ## Core Function The `/contents` endpoint retrieves full page content, summaries, and metadata for up to 100 URLs. It serves "instant results from our cache, with automatic live crawling as fallback for uncached pages." ## Content Retrieval The endpoint extracts clean page text through multiple options: - **Basic retrieval**: Simple boolean toggle returns full-page text with defaul Agent context: Let me also consult Exa's contents API, which is positioned for high-fidelity RAG/citation use.
Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init. I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init.
Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init. Tool result summary: 1 from __future__ import annotations 2 3 from urllib.parse import urljoin 4 5 from aiohttp import ClientSession 6 7 from sensei_search.config import SEARXNG_URL 8 from sensei_search.logger import logger 9 from sensei_search.tools.search.base import ( 10 Input, 11 SearchTool, 12 TopResults, 13 filter_medium_by_accessibility, 14 get_top_results, 15 ) 16 17 MAX_RESULTS = 5 18 19 20 async def filter_medium_by_scores(resu Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init.
Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init. Tool result summary: 1 from __future__ import annotations 2 3 import asyncio 4 import os 5 from typing import Any, Callable, Coroutine, Dict, List, TypeVar 6 7 from aiohttp import ClientSession 8 9 from sensei_search.config import BING_API_KEY 10 from sensei_search.logger import logger 11 from sensei_search.tools.search.base import ( 12 Category, 13 GeneralResult, 14 ImageResult, 15 Input, 16 SearchTool, 17 TopResults, 18 VideoResult, 19 Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init.
Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init. Tool result summary: 1 from .search import * 2 Agent context: I have enough to decide. Let me read the existing search tool implementations to mirror their structure and the tools package init.
Agent context: Let me check how the Samurai agent uses `fetch_web_pages` so I don't break it. Let me check how the Samurai agent uses `fetch_web_pages` so I don't break it.
Agent context: Let me check how the Samurai agent uses `fetch_web_pages` so I don't break it. Tool result summary: backend/sensei_search/base_agent.py-181- async def emit_related_questions(self, related_questions: List[str]) -> None: /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/base_agent.py-182- """ /home/user/worktrees/attempt-31ad0e4b-a5f7-4ad9-8d8f-8b3a00ebb097-cap-1/backend/sensei_search/base_agent.py-183- Send the related questions to the frontend. /home/user/worktrees/attemp Agent context: Let me check how the Samurai agent uses `fetch_web_pages` so I don't break it.
Agent context: Decision made: **Firecrawl**. Now let me check remaining `asyncio` usage in base_agent before refactoring imports, and check pyproject for deps. Decision made: **Firecrawl**. Now let me check remaining `asyncio` usage in base_agent before refactoring imports, and check pyproject for deps.
Agent context: Decision made: **Firecrawl**. Now let me check remaining `asyncio` usage in base_agent before refactoring imports, and check pyproject for deps. Tool result summary: 198: except asyncio.TimeoutError: 207: html_web_pages = await asyncio.gather(*tasks) Agent context: Decision made: **Firecrawl**. Now let me check remaining `asyncio` usage in base_agent before refactoring imports, and check pyproject for deps.
Agent context: Now I'll build the extraction tool package. Creating the base abstraction first. Now I'll build the extraction tool package. Creating the base abstraction first.
Agent context: Now the Firecrawl provider and the trafilatura fallback. Now the Firecrawl provider and the trafilatura fallback.
Agent context: Now update config and the tools package init. Now update config and the tools package init.
Agent context: Now refactor `base_agent.fetch_web_pages` to delegate to the extractor and clean up unused imports. Now refactor `base_agent.fetch_web_pages` to delegate to the extractor and clean up unused imports.
Agent context: Now refactor `base_agent.fetch_web_pages` to delegate to the extractor and clean up unused imports. Tool result summary: 2 Found 0 total occurrences across 0 files. Agent context: Now refactor `base_agent.fetch_web_pages` to delegate to the extractor and clean up unused imports.
Agent context: Now rewrite the `fetch_web_pages` body to delegate to the extractor. Now rewrite the `fetch_web_pages` body to delegate to the extractor.
Agent context: Now wire extraction into the Shogun agent: fetch full content and ground the answer on it while preserving citations. Now wire extraction into the Shogun agent: fetch full content and ground the answer on it while preserving citations.
Agent context: Now wire extraction into the Shogun agent: fetch full content and ground the answer on it while preserving citations. Tool result summary: 214 messages: List[ChatCompletionMessageParam] = [ 215 {"role": "system", "content": system_prompt}, 216 ] 217 for message in self.chat_messages: 218 messages.append({"role": message["role"], "content": message["content"]}) 219 220 # We need to append the search results to the chat history 221 messages.append( 222 { 223 "role": "user", 224 "content": json.dumps( 225 [ 226 { 227 "type": "tool_result", 228 "tool_use_id Agent context: Now wire extraction into the Shogun agent: fetch full content and ground the answer on it while preserving citations.
Agent context: Now update `run()` to fetch the page content and pass it through. Now update `run()` to fetch the page content and pass it through.
Agent context: Now update the env example files to document the new optional config. Now update the env example files to document the new optional config.
Agent context: Let me verify the changed files compile cleanly. Let me verify the changed files compile cleanly.
Agent context: Let me verify the changed files compile cleanly. Tool result summary: OK Agent context: Let me verify the changed files compile cleanly.
Agent context: Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent. Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent.
Agent context: Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent. Tool result summary: Found 2 files sensei_search/tools/extract/trafilatura.py sensei_search/agents/samurai/agent.py Agent context: Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent.
Agent context: Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent. Tool result summary: 31:FETCH_WEBPAGE_TIMEOUT = 3 Agent context: Let me confirm no other code references the removed `FETCH_WEBPAGE_TIMEOUT` symbol from base_agent.
Agent context: All references are local definitions, no broken imports. Let me do a final review of the wired Shogun section. All references are local definitions, no broken imports. Let me do a final review of the wired Shogun section.
Agent context: All references are local definitions, no broken imports. Let me do a final review of the wired Shogun section. Tool result summary: 308 309 # Append user message to chat history 310 self.append_message(role="user", content=user_message) 311 312 # Generate the search results ASAP 313 result = await self.front_run_search() 314 315 # If we have search results, generate the answer with the search context 316 if result: 317 search_results, search_query = result 318 319 logger.info(search_results) 320 321 await self.emit_web_results(search_results["gen Agent context: All references are local definitions, no broken imports. Let me do a final review of the wired Shogun section.
Agent context: All wired correctly. Let me mark todos complete. All wired correctly. Let me mark todos complete.
Agent context: ## Recommendation: Firecrawl **Chosen provider:** [Firecrawl](https://docs.firecrawl.dev) (`/v2/scrape`). This repo ("Sensei Search", a Perplexity-style answer engine) had a gap: the in-use **Shogun** agent answered only from search-result *snippets* — `result['content']` — and never fetched the actual pages. Thin snippets are the biggest hallucination risk, so the highest-leverage fix is feeding the model complete, faithful source text. **Why Firecrawl over the alternatives** (priority: accuracy & citations > latency/price): - **Faithful, deterministic extraction.** `formats=["markdown"]` + `onlyMainContent=true` returns the page's real main content as structured markdown — *not* an LLM summary, so nothing is paraphrased or invented. - **Source coverage = source quality.** Browser rendering (JS pages) + anti-bot proxies mean fewer failed fetches, so the model grounds on real pages instead of falling back to snippets. - **Clean citation mapping.** Per-URL scrape keeps url+title alongside content, preserving the existing `[1] url / title / content` citation format. **Alternatives considered:** - **Exa `/contents`** — excellent for citations/metadata (author, date), but optimized around its own index and returns highlight *snippets*; less focused on full faithful page text. - **Tavily `/extract`** — RAG-friendly batching, but reranks into `chunks_per_source` snippets, sacrificing completeness. - **Jina Reader** — cheapest/easiest, but weaker coverage on JS/protected pages — the budget option, not the accuracy option. - **trafilatura** (existing baseline) — kept as the zero-config local fallback. **Docs I actually consulted:** Firecrawl `/scrape` + introduction, Jina Reader, Tavily `/extract`, and Exa `/contents`. ## What I built Mirrored the existing search-tool abstraction with a new `tools/extract/` package: - `base.py` — `ContentExtractor` ABC (`extract(urls) -> List[str]`, one entry per URL, `""` on failure) - `firecrawl.py` — managed provider via aiohttp REST (no new SDK dep, consistent with `bing.py`/`searxng.py`) - `trafilatura.py` — local fallback (preserves old behavior) - `__init__.py` — `get_extract_tool()`: uses Firecrawl when `FIRECRAWL_API_KEY` is set, else trafilatura Wiring: `base_agent.fetch_web_pages` now delegates to the extractor; the **Shogun** agent fetches/extracts the top-5 result pages and grounds the answer on full content (snippet fallback per URL, citations preserved). Added `FIRECRAWL_API_KEY`/`FIRECRAWL_API_URL` to `config.py` and both `.env.*.example` files. All files compile. Note: I couldn't run the app end-to-end (no API keys / running stack), so this is verified by `py_compile` only — not a live request. ## Recommendation: Firecrawl **Chosen provider:** [Firecrawl](https://docs.firecrawl.dev) (`/v2/scrape`). This repo ("Sensei Search", a Perplexity-style answer engine) had a gap: the in-use **Shogun** agent answered only from search-result *snippets* — `result['content']` — and never fetched the actual pages. Thin snippets are the biggest hallucination risk, so the highest-leverage fix is feeding the model complete, faithful source text. **Why Firecrawl over the alternatives** (priority: accuracy & citations > latency/price): - **Faithful, deterministic extraction.** `formats=["markdown"]` + `onlyMainContent=true` returns the page's real main content as structured markdown — *not* an LLM summary, so nothing is paraphrased or invented. - **Source coverage = source quality.** Browser rendering (JS pages) + anti-bot proxies mean fewer failed fetches, so the model grounds on real pages instead of falling back to snippets. - **Clean citation mapping.** Per-URL scrape keeps url+title alongside content, preserving the existing `[1] url / title / content` citation format. **Alternatives considered:** - **Exa `/contents`** — excellent for citations/metadata (author, date), but optimized aro
## Recommendation: Firecrawl **Chosen provider:** [Firecrawl](https://docs.firecrawl.dev) (`/v2/scrape`). This repo ("Sensei Search", a Perplexity-style answer engine) had a gap: the in-use **Shogun** agent answered only from search-result *snippets* — `result['content']` — and never fetched the actual pages. Thin snippets are the biggest hallucination risk, so the highest-leverage fix is feeding the model complete, faithful source text. **Why Firecrawl over the alternatives** (priority: accuracy & citations > latency/price): - **Faithful, deterministic extraction.** `formats=["markdown"]` + `onlyMainContent=true` returns the page's real main content as structured markdown — *not* an LLM summary, so nothing is paraphrased or invented. - **Source coverage = source quality.** Browser rendering (JS pages) + anti-bot proxies mean fewer failed fetches, so the model grounds on real pages instead of falling back to snippets. - **Clean citation mapping.** Per-URL scrape keeps url+title alongside content, preserving the existing `[1] url / title / content` citation format. **Alternatives considered:** - **Exa `/contents`** — excellent for citations/metadata (author, date), but optimized around its own index and returns highlight *snippets*; less focused on full faithful page text. - **Tavily `/extract`** — RAG-friendly batching, but reranks into `chunks_per_source` snippets, sacrificing