answer

package
v1.801.408 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package answer is a researched answer to a hard question, with its sources cited.

It is the native answer engine: the bounded plan → search → read → rank → synthesize → cite → follow-up loop behind POST /v1/ask when a `mode` (search|news|research|deep) selects web grounding. It is a clean-room Hanzo implementation (NOT derived from any AGPL reference).

ONE HOME, ONE DOOR. This package is the loop's only home; /v1/ask is its only door. `mode` is a VALUE handed to that door — "deep research" is a mode, never a second route. The package registers no routes of its own: clients/ask owns the door and delegates web modes here.

THE FIVE VALUES, one home each:

plan       → plan()        → []string            (≤ maxQueries, best-effort)
search     → websearch.Search → []websearch.Result (in-process, keyless)
rank       → rank()        → []Source             (dedupe URL+host, relevance, cap)
read       → read()        → []Source (enriched)  (the ONE crawl, ai/object)
synthesize → synthesize()  → string               (streamed through the Sink)

BOUNDED. ≤3 LLM calls (1 plan + 1 synthesis + 1 follow-up), ≤maxQueries search passes, ≤maxRead page fetches, a 90s wall clock, and a token ceiling past which the optional follow-up call is skipped. It is never an open agent loop.

METERED ONCE. Every answer debits the resolved payer through the per-org ResourceMeter (Base.Bill) — the ONE revenue debit, since the in-process AI path runs on the binary's balance-exempt M2M identity.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsMode

func IsMode(name string) bool

IsMode reports whether a request mode selects the answer engine. An empty or unknown mode is NOT an answer-engine request — /v1/ask's figure path handles it, so the advisor's existing behavior is untouched when no mode is set.

Types

type Engine

type Engine struct {
	cloud.Base
	AI    cloud.AIClient
	Model string
}

Engine is the answer engine value: the shared Base (logger + the ONE per-org meter) plus the AI plane it synthesizes with and the deployment's default model. The mounting package constructs it per request from what it already holds — the engine owns no state of its own.

func (Engine) Run

func (e Engine) Run(ctx context.Context, p Params, out Sink)

Run is the bounded loop, parameterized by mode — the ONE code path for search/news/research/deep. It emits the SearchEvent envelope through out, then meters the caller ONCE. A failed step degrades (fewer sources, snippets instead of pages, an honest note) rather than aborting the stream.

func (Engine) Serve

func (e Engine) Serve(c *zip.Ctx, in Request, q string) error

Serve answers one web-mode /v1/ask: resolve the billing subject → GATE the balance → run the bounded loop, streaming the SearchEvent envelope (SSE) or returning it as one JSON object. The caller is already gated as a validated principal by the door; here we additionally resolve the payer and gate spend BEFORE any work, so an out-of-funds caller gets a clean 402, never a half stream.

type Page

type Page struct {
	URL      string
	Markdown string
}

Page is the crawl seam's value: the URL a page was fetched for and its LLM-ready markdown. The answer engine depends on THIS, never on the crawler's own result shape — which is what lets a test inject a fake crawl.

type Params

type Params struct {
	// contains filtered or unexported fields
}

Params is the fully-owned per-request plan handed to Run(): safe to use after the Ctx is recycled (SSE) and retained by the async meter.

type Request

type Request struct {
	Mode       string   // search|news|research|deep
	Sources    []string // @hints appended to the web query: web,news,academic,github,reddit,x
	Model      string   // override the synthesis model
	Stream     *bool    // force SSE (else Accept: text/event-stream / ?stream=1)
	Language   string   // web-search language (BCP-47-ish)
	MaxSources int
	MaxQueries int
	FollowUps  *bool // default true
	System     string
}

Request is the answer engine's slice of the /v1/ask body. Every field is optional; mode selects the profile and the rest bound or override it.

type Sink

type Sink interface {
	// contains filtered or unexported methods
}

Sink receives the loop's envelope events — one method per SearchEvent variant, which keeps the loop declarative. Implemented by sseSink (streaming) and bufferSink (JSON).

There is no fail(): the loop never hard-fails. A down model degrades to an honest answer + a done frame (and is not billed), a down crawl degrades to snippets — so the client ALWAYS gets a terminal frame. The union's `error` variant is the CLIENT's (a transport failure it observes), never the server's.

type Source

type Source struct {
	URL     string `json:"url"`
	Title   string `json:"title"`
	Snippet string `json:"snippet"`
	Engine  string `json:"engine,omitempty"`
	Favicon string `json:"favicon"`
}

Source is one web source backing an answer — the @hanzo/ai SearchSource shape, field for field. It is the ONE source value in this package: search produces it, read() enriches only its Snippet (never its identity), synthesis grounds on it, and the wire emits it verbatim in the `sources` and `done` frames.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL