Documentation
¶
Overview ¶
Package searchwire is a zero-configuration Go metasearch and page-fetch runtime for agent tooling. Callers provide a search query and optional Config; built-in sources fan out concurrently, partial failures are reported in the response, and results are deduplicated and ranked. Built-in sources include Brave Search, Startpage, Wikipedia, GitHub, and the opt-in Serper.dev and Tavily API sources (registered only when their API keys resolve). Searcher.Fetch retrieves one http(s) URL as readable text, with per-call controls for byte budget, conditional GET, and Range requests; it also surfaces llmstxt.org v2 discovery hints (the covering llms.txt file and markdown alternates) when a page advertises them, and on 2xx HTML responses auto-attaches the parsed covering llms.txt (Searcher.LLMsTxt / Searcher.LLMsTxtURL). Truncated responses are also materialized to Config.TempDir (default os.TempDir()/searchwire) and surfaced as Searcher.TempFile so the agent can read the rest with file_read. Searcher.FetchLLMsTxt remains available as a standalone probe for callers that want the parsed llms.txt without fetching the page first. Searcher.SearchWithOptions runs a search with per-call options such as a different result limit than Config.Limit. When a supported provider API key is configured, Searcher.Answer returns web-grounded answers and CanAnswer reports that optional capability. The same library powers the searchwire-mcp stdio adapter under cmd/searchwire-mcp, where the answer tool is registered only when that capability is available at startup.
Index ¶
- Constants
- Variables
- type Answer
- type AnswerOptions
- type AnthropicConfig
- type BraveConfig
- type Config
- type CustomSearchConfig
- type FetchOptions
- type FetchResult
- type GitHubConfig
- type GoogleConfig
- type HTTPClient
- type HTTPError
- type LLMsLink
- type LLMsSection
- type LLMsTxt
- type LLMsTxtResult
- type Link
- type OpenAIConfig
- type OpenRouterConfig
- type PerplexityConfig
- type Response
- type Result
- type SearchError
- type SearchOptions
- type Searcher
- func (s *Searcher) Answer(ctx context.Context, question string) (*Answer, error)
- func (s *Searcher) AnswerWithOptions(ctx context.Context, question string, options AnswerOptions) (*Answer, error)
- func (s *Searcher) AvailableAnswerProviders() []string
- func (s *Searcher) CanAnswer() bool
- func (s *Searcher) Fetch(ctx context.Context, rawURL string) (*FetchResult, error)
- func (s *Searcher) FetchLLMsTxt(ctx context.Context, rawURL string) (*LLMsTxtResult, error)
- func (s *Searcher) FetchWithLimit(ctx context.Context, rawURL string, maxBytes int64) (*FetchResult, error)
- func (s *Searcher) FetchWithOptions(ctx context.Context, rawURL string, opts FetchOptions) (*FetchResult, error)
- func (s *Searcher) Search(ctx context.Context, query string) (*Response, error)
- func (s *Searcher) SearchWithOptions(ctx context.Context, query string, opts SearchOptions) (*Response, error)
- func (s *Searcher) Sources() []string
- type SerperConfig
- type SourceError
- type TavilyConfig
- type XAIConfig
Constants ¶
const ( AnswerProviderBrave = "brave" AnswerProviderOpenRouter = "openrouter" AnswerProviderOpenAI = "openai" AnswerProviderPerplexity = "perplexity" AnswerProviderAnthropic = "anthropic" AnswerProviderXAI = "xai" )
Variables ¶
var ( ErrEmptyQuery = errors.New("searchwire: query is required") ErrEmptyQuestion = errors.New("searchwire: question is required") ErrEmptyURL = errors.New("searchwire: url is required") ErrInvalidURL = errors.New("searchwire: invalid url") ErrUnsupportedScheme = errors.New("searchwire: unsupported url scheme") ErrNilSearcher = errors.New("searchwire: nil searcher") ErrResponseTooLarge = errors.New("searchwire: response exceeds configured limit") ErrUnexpectedContentType = errors.New("searchwire: unexpected content type") // ErrLLMsTxtNotFound is returned by FetchLLMsTxt when no llms.txt file // exists at the enclosing directory or the site root (all probes 404/410). ErrLLMsTxtNotFound = errors.New("searchwire: llms.txt not found") // ErrInvalidLLMsTxt is returned when llms.txt content exists but violates // the required structure (missing H1 heading). ErrInvalidLLMsTxt = errors.New("searchwire: invalid llms.txt") ErrInvalidProviderResponse = errors.New("searchwire: invalid provider response") )
Functions ¶
This section is empty.
Types ¶
type AnswerOptions ¶
type AnswerOptions struct {
Provider string
}
AnswerOptions selects an optional configured provider. An empty provider uses the first available provider in Searchwire's stable priority order.
type AnthropicConfig ¶
type AnthropicConfig struct {
// Enabled explicitly enables or disables the provider. When set to false,
// env var fallback is skipped. nil means default (enabled if key resolves).
Enabled *bool
// APIKey overrides APIKeyEnv (default ANTHROPIC_API_KEY).
APIKey string
APIKeyEnv string
// Model overrides ModelEnv (default ANTHROPIC_MODEL), then falls back to
// the cost-focused Claude Haiku 4.5 model.
Model string
ModelEnv string
}
AnthropicConfig controls web-grounded answers through the Messages API.
type BraveConfig ¶
type BraveConfig struct {
// Enabled registers the Brave source. Default true.
Enabled *bool
// APIKey is sent as X-Subscription-Token. When empty, APIKeyEnv is read.
APIKey string
// APIKeyEnv names the environment variable for APIKey
// (default BRAVE_SEARCH_API_KEY).
APIKeyEnv string
}
BraveConfig controls the built-in Brave source. Without an API key, Searchwire uses Brave's public HTML results. Supplying a key switches the source to the official Brave Search API while preserving the source name.
type Config ¶
type Config struct {
HTTPClient HTTPClient
UserAgent string
Limit int
MaxResponseBytes int64 // budget for extracted readable text (per fetch)
// MaxRawResponseBytes is the hard safety ceiling on the raw HTML/plaintext
// download, independent of the caller text budget.
MaxRawResponseBytes int64
Timeout time.Duration
Brave BraveConfig
Serper SerperConfig
Tavily TavilyConfig
GitHub GitHubConfig
// TempDir overrides the directory used to materialize truncated fetch
// artifacts. Empty falls back to os.TempDir() + "searchwire". The
// directory is created on demand. Each artifact is named
// "<UTC-timestamp>-<sha256-prefix>-<safe-name>.txt" so concurrent calls
// never collide.
TempDir string
// OpenRouter and OpenAI are optional answer providers. They do not add
// metasearch sources; a resolved API key enables the answer capability.
OpenRouter OpenRouterConfig
OpenAI OpenAIConfig
Perplexity PerplexityConfig
Anthropic AnthropicConfig
XAI XAIConfig
// Google and Custom are future-dev placeholders. They are not read by New()
// until their source adapters are implemented.
Google GoogleConfig
Custom CustomSearchConfig
}
Config configures a Searcher. The zero value uses built-in defaults and zero-configuration sources. Optional integrations read credentials from explicit fields first, then from named environment variables.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the zero-configuration defaults.
type CustomSearchConfig ¶
type CustomSearchConfig struct {
URL string
}
CustomSearchConfig is a future-dev placeholder for a caller-provided search endpoint. Setting URL has no effect on Search today.
type FetchOptions ¶
type FetchOptions struct {
// MaxBytes caps the response body. When <= 0, Config.MaxResponseBytes is
// used (or the library default).
MaxBytes int64
// IfNoneMatch is sent as If-None-Match for conditional GET. A 304
// response is returned as a FetchResult with StatusCode 304 (not an
// error).
IfNoneMatch string
// IfModifiedSince is sent as If-Modified-Since for conditional GET.
IfModifiedSince string
// Range is sent as the Range header (e.g. "bytes=10-20"). A 206 response
// is returned as a normal FetchResult with StatusCode 206.
Range string
}
FetchOptions controls one fetch call: byte budget, conditional GET headers, and a Range request. Zero values mean "do not send".
type FetchResult ¶
type FetchResult struct {
URL string
FinalURL string
StatusCode int
ContentType string
Title string
Text string
Links []Link
// Headers carries selected response headers useful to agents:
// ETag, Last-Modified, X-RateLimit-Remaining, X-RateLimit-Reset,
// Retry-After (on 429/503). Empty when absent.
Headers map[string]string
// Redirects is the number of redirects followed (0 = direct hit).
Redirects int
Truncated bool
Bytes int
// DescribedBy is the absolute URL of the llms.txt file covering this
// page, discovered via <link rel="describedby"> or an equivalent HTTP
// Link header (llmstxt.org v2). Empty when the site does not advertise
// one.
DescribedBy string
// MarkdownAlt is the absolute URL of a markdown alternate version of
// this page (<link rel="alternate" type="text/markdown"> or the HTTP
// Link header equivalent). Empty when absent.
MarkdownAlt string
// LLMsTxt is the parsed llms.txt file covering this page, populated
// automatically when the source fetch succeeds (2xx), the response
// content type is HTML, and a covering llms.txt file can be retrieved
// and parsed. The probe follows the same rules as Searcher.FetchLLMsTxt
// (most-specific-directory first, then the site root; 404/410 falls
// through; invalid/soft-404 content is skipped). Non-HTML responses
// (JSON, plain text, etc.) skip the probe entirely. Any probe failure
// is non-fatal: callers always get the source result they asked for.
LLMsTxt *LLMsTxt
// LLMsTxtURL is the absolute final URL of the llms.txt file that
// produced LLMsTxt (post-redirect). Empty when LLMsTxt is nil.
LLMsTxtURL string
// TempFile is the absolute path of a file under the configured temp
// directory (Config.TempDir, default os.TempDir()/searchwire) holding
// the full extracted text when Truncated is true. The file lets the
// agent read the rest of the response (e.g. via file_read) without
// having to refetch. Empty when the response was not truncated or when
// the write failed (the inline Text still carries the truncated
// payload with the [truncated] marker in that case).
TempFile string
}
FetchResult is the readable text extracted from one fetched page.
type GitHubConfig ¶
type GitHubConfig struct {
// Enabled registers the GitHub source. Default true.
Enabled *bool
// Token is sent as Bearer auth. When empty, TokenEnv is read.
Token string
// TokenEnv names the environment variable for Token (default GITHUB_TOKEN).
TokenEnv string
// SearchIssues enables repository+issues mode. When false, only repositories
// are searched. Default true. When true, repository search still serves as
// the fallback if issues search fails.
SearchIssues *bool
}
GitHubConfig controls the built-in GitHub Search API source.
type GoogleConfig ¶
type GoogleConfig struct {
// APIKey is the Google Custom Search JSON API key (future).
APIKey string
// APIKeyEnv names the env var for APIKey (planned default: GOOGLE_API_KEY).
APIKeyEnv string
// CX is the programmable search engine ID (future).
CX string
// CXEnv names the env var for CX (planned default: GOOGLE_CX).
CXEnv string
}
GoogleConfig is a future-dev placeholder for Google Programmable Search Engine. Setting these fields has no effect on Search today.
type HTTPClient ¶
HTTPClient is satisfied by *http.Client and lightweight test doubles.
type HTTPError ¶
type HTTPError struct {
StatusCode int
Status string
Body string
// RetryAfter is the parsed Retry-After header value in seconds when the
// server provided one (e.g. on 429/503). Zero when absent or unparseable.
RetryAfter int
// ErrorBody is the structured error envelope parsed from Body when it is
// valid JSON of the form {"error":...} or {"errors":[...]} or
// {"message":"..."} or {"detail":"..."}. Nil when Body is not JSON or
// does not match a known shape.
ErrorBody map[string]any
}
HTTPError reports a non-2xx HTTP response from a source.
type LLMsLink ¶
type LLMsLink struct {
Name string
URL string
// Notes is the optional free-text after the ": " separator.
Notes string
}
LLMsLink is one "- [name](url): notes" entry from a section's file list.
type LLMsSection ¶
LLMsSection is one H2-delimited group of links.
type LLMsTxt ¶
type LLMsTxt struct {
// Title is the required H1 project or site name.
Title string
// Summary is the blockquote summary following the H1. Empty when absent.
Summary string
// Description is free-form prose between the summary and the first H2
// section, one trimmed line per entry joined with newlines. Empty when
// absent.
Description string
// Sections are the H2-delimited groups in document order.
Sections []LLMsSection
}
LLMsTxt is a parsed /llms.txt file (llmstxt.org v2 format).
func ParseLLMsTxt ¶
ParseLLMsTxt parses llms.txt Markdown content per the llmstxt.org v2 format: an optional BOM, a required H1 title, an optional blockquote summary, optional free-form description, then H2-delimited sections whose lists contain "[name](url)" links with optional ": notes". Parsing is lenient: unrecognized lines are ignored rather than rejected. It returns an error wrapping ErrInvalidLLMsTxt only when the required H1 is missing.
type LLMsTxtResult ¶
type LLMsTxtResult struct {
// URL is the requested llms.txt URL and FinalURL the post-redirect URL.
URL string
FinalURL string
// StatusCode is the HTTP status of the successful fetch (2xx family).
StatusCode int
// Headers carries selected response headers (same set as Fetch) so
// callers can do conditional GETs on later refreshes.
Headers map[string]string
// LLMsTxt is the parsed file content.
LLMsTxt *LLMsTxt
}
LLMsTxtResult is a fetched and parsed llms.txt file.
type Link ¶
Link is one anchor collected from an HTML page (text + href). Only safe http/https hrefs are collected; javascript:, data:, and protocol-relative URLs are skipped.
type OpenAIConfig ¶
type OpenAIConfig struct {
// Enabled explicitly enables or disables the provider. When set to false,
// env var fallback is skipped. nil means default (enabled if key resolves).
Enabled *bool
// APIKey overrides APIKeyEnv (default OPENAI_API_KEY).
APIKey string
APIKeyEnv string
// Model overrides ModelEnv (default OPENAI_MODEL), then falls back to the
// cost-focused gpt-5.6-luna model.
Model string
ModelEnv string
}
OpenAIConfig controls web-grounded answers through the OpenAI Responses API.
type OpenRouterConfig ¶
type OpenRouterConfig struct {
// Enabled explicitly enables or disables the provider. When set to false,
// env var fallback is skipped. nil means default (enabled if key resolves).
Enabled *bool
// APIKey overrides APIKeyEnv (default OPENROUTER_API_KEY).
APIKey string
APIKeyEnv string
// Model overrides ModelEnv (default OPENROUTER_MODEL), then falls back to
// openrouter/auto.
Model string
ModelEnv string
}
OpenRouterConfig controls web-grounded answers through OpenRouter.
type PerplexityConfig ¶
type PerplexityConfig struct {
// Enabled explicitly enables or disables the provider. When set to false,
// env var fallback is skipped. nil means default (enabled if key resolves).
Enabled *bool
// APIKey overrides APIKeyEnv (default PERPLEXITY_API_KEY).
APIKey string
APIKeyEnv string
// Preset overrides PresetEnv (default PERPLEXITY_PRESET), then falls back
// to Perplexity's cost-focused low preset.
Preset string
PresetEnv string
}
PerplexityConfig controls web-grounded answers through the Agent API.
type Response ¶
type Response struct {
Query string
Results []Result
Errors []SourceError
}
Response is the merged output of a metasearch query.
type SearchError ¶
type SearchError struct {
Failures []SourceError
}
SearchError is returned when every built-in source fails.
func (*SearchError) Error ¶
func (e *SearchError) Error() string
type SearchOptions ¶
type SearchOptions struct {
// Limit overrides the configured result limit for this call: it caps the
// fused results returned and bounds each source's requested count.
// When <= 0, Config.Limit applies.
Limit int
// Sources restricts the query to the named registered sources. Names
// are matched case-insensitively after trimming whitespace; unknown
// names are ignored. When no registered source matches, the search
// fails like a fan-out with no sources. Empty means every registered
// source participates (merged metasearch). Callers can combine this
// with Searcher.Sources() to implement routing policies (round-robin,
// random, fixed provider) on top of the searcher.
Sources []string
}
SearchOptions controls one search call. Zero values keep the configured defaults.
type Searcher ¶
type Searcher struct {
// contains filtered or unexported fields
}
Searcher fans out to built-in sources, merges duplicates, and ranks results.
func New ¶
New returns a Searcher configured by cfg. Use DefaultConfig() or the zero value for zero-configuration behavior.
func (*Searcher) Answer ¶
Answer returns a web-grounded answer when an answer provider is configured.
func (*Searcher) AnswerWithOptions ¶
func (s *Searcher) AnswerWithOptions(ctx context.Context, question string, options AnswerOptions) (*Answer, error)
AnswerWithOptions returns a web-grounded answer from the selected provider.
func (*Searcher) AvailableAnswerProviders ¶
AvailableAnswerProviders returns configured provider identifiers in stable default-selection order.
func (*Searcher) CanAnswer ¶
CanAnswer reports whether this Searcher has a configured answer provider.
func (*Searcher) Fetch ¶
Fetch retrieves one http(s) URL and returns readable text. HTML responses are stripped to title + visible text. Plain text is returned as-is. Extracted text — not raw HTML — is capped at Config.MaxResponseBytes; Truncated reports when the returned text was cut to fit. Raw HTML downloads are additionally bounded by an internal hard ceiling (defaultMaxRawResponseBytes). This is a local agent helper, not a hardened proxy.
func (*Searcher) FetchLLMsTxt ¶
FetchLLMsTxt retrieves and parses the llms.txt file covering rawURL. Per the llmstxt.org v2 proposal a file covers every URL under its path, so the enclosing directory's llms.txt is probed first and the site root second; the most specific file found wins. Directories without their own file typically 404 and fall through to the root probe.
When every candidate 404s/410s, the error wraps ErrLLMsTxtNotFound. A file that exists but lacks the required H1 wraps ErrInvalidLLMsTxt. Other errors (network failures, non-404 HTTP errors, unsupported content types) surface as-is. Conditional GET support matches Fetch via LLMsTxtResult.Headers.
func (*Searcher) FetchWithLimit ¶
func (s *Searcher) FetchWithLimit(ctx context.Context, rawURL string, maxBytes int64) (*FetchResult, error)
FetchWithLimit is Fetch with an optional per-call byte budget for the extracted readable text. When maxBytes <= 0, Config.MaxResponseBytes is used. The budget applies to the returned text, not the raw HTML download, so HTML overhead (markup, scripts, styles) does not consume the caller's budget.
func (*Searcher) FetchWithOptions ¶
func (s *Searcher) FetchWithOptions(ctx context.Context, rawURL string, opts FetchOptions) (*FetchResult, error)
FetchWithOptions is Fetch with conditional-GET / Range / byte-budget controls.
func (*Searcher) Search ¶
Search runs the query across all built-in sources concurrently. When at least one source succeeds, partial failures are returned in Response.Errors.
func (*Searcher) SearchWithOptions ¶
func (s *Searcher) SearchWithOptions(ctx context.Context, query string, opts SearchOptions) (*Response, error)
SearchWithOptions is Search with per-call options such as a different result limit than the configured default.
type SerperConfig ¶
type SerperConfig struct {
// Enabled registers the Serper source. Default false.
Enabled *bool
// APIKey is sent as X-API-KEY. When empty, APIKeyEnv is read.
APIKey string
// APIKeyEnv names the environment variable for APIKey
// (default SERPER_API_KEY).
APIKeyEnv string
}
SerperConfig controls the optional Serper.dev (Google Search API) source. Unlike Brave, Serper has no HTML fallback, so the source is only registered when an API key resolves and Enabled is not explicitly false (default false).
type SourceError ¶
SourceError records one source failure without aborting the whole search.
type TavilyConfig ¶
type TavilyConfig struct {
// Enabled registers the Tavily source. Default false.
Enabled *bool
// APIKey is sent as Bearer auth. When empty, APIKeyEnv is read.
APIKey string
// APIKeyEnv names the environment variable for APIKey
// (default TAVILY_API_KEY).
APIKeyEnv string
}
TavilyConfig controls the optional Tavily (AI search) source. The source is only registered when an API key resolves and Enabled is not explicitly false (default false).
type XAIConfig ¶
type XAIConfig struct {
// Enabled explicitly enables or disables the provider. When set to false,
// env var fallback is skipped. nil means default (enabled if key resolves).
Enabled *bool
// APIKey overrides APIKeyEnv (default XAI_API_KEY).
APIKey string
APIKeyEnv string
// Model overrides ModelEnv (default XAI_MODEL), then falls back to
// grok-4.5.
Model string
ModelEnv string
}
XAIConfig controls web-grounded answers through the xAI Responses API.
Source Files
¶
- aggregate.go
- answer.go
- answer_anthropic.go
- answer_openai.go
- answer_openrouter.go
- answer_perplexity.go
- answer_responses.go
- answer_xai.go
- brave.go
- charset.go
- config.go
- doc.go
- errors.go
- extract.go
- fetch.go
- github.go
- html.go
- html_clean.go
- http_error.go
- http_error_vars.go
- llmstxt.go
- searchwire.go
- serper.go
- source.go
- startpage.go
- tavily.go
- temp_artifact.go
- wikipedia.go
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
searchwire-mcp
command
Command searchwire-mcp exposes Searchwire as an MCP stdio server.
|
Command searchwire-mcp exposes Searchwire as an MCP stdio server. |