search

package
v1.801.381 Latest Latest
Warning

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

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

Documentation

Overview

Package search is one ranked result set over everything your org has stored.

It answers "what is RELEVANT" over a tenant's own data: it owns no store and fuses the two retrieval stores the platform already runs — the lexical index (apps/index) and the vector index (apps/knowledge) — into one ranked result set.

IT IS NOT MOUNTED. There is no manifest row and no plugin/search binary, so Mount below is never called and its POST /v1/search never reaches the wire — /v1/search belongs to apps/provisioning (list/create a provisioned search index), which is a different product that happens to share the word. The one live caller is apps/team's fulltext RPC, which calls ForOrg in-process; and in the team BINARY neither leg is mounted, so index.Ready() is false there and the lexical leg reports `disabled` on every query. Until a door is decided, a caller still has to know which of /v1/kb/search, /v1/index/indexes/:uid/search and /v1/code/search holds the answer, and gets a different request shape and a different score scale from each — which is the problem this package was written to end.

WHAT BELONGS HERE. A query whose honest answer has a SCORE. A query whose honest answer has a TRUTH VALUE — the definition of a symbol, the callers of a function, a dependency edge — belongs to /v1/code (apps/code) and must not be forced through a relevance-ranked shape: a definition is not 0.87 relevant, it either is the definition or it is not.

NOT HERE: /v1/websearch/search. That searches the PUBLIC WEB, not the customer's data. It has a different tenancy model (no org-scoped corpus), a different cost model (per-call to an external provider) and a different failure mode. It stays separate — do not fold it in.

DEGRADATION IS THE CONTRACT. Every response names every backend it consulted and that backend's status. A leg that is down produces results from the surviving legs plus an explicit `degraded` entry carrying the error — never a silent empty. This is not a nicety: a silent empty is exactly how a vector-store credential drift went unnoticed for five days behind a fail-empty /v1/kb/search.

Index

Constants

View Source
const (
	BackendIndex  = "index"  // lexical, clients/index
	BackendVector = "vector" // semantic, clients/knowledge → hanzoai/vector
)

Backend names. One constant per leg so the wire value is declared once and the provenance a client reads always matches the status it reads.

View Source
const (
	StatusOK       = "ok"
	StatusDegraded = "degraded"
	StatusDisabled = "disabled"
	StatusSkipped  = "skipped"
)

Backend statuses — four DISTINCT operational facts, never collapsed:

  • ok the leg ran and answered.
  • degraded the leg is configured but FAILED. Carries the error.
  • disabled the leg is not provisioned in this deployment. Not a fault.
  • skipped the caller's mode excluded the leg. Not a fault.
View Source
const (
	ModeAuto     = "auto"
	ModeText     = "text"
	ModeSemantic = "semantic"
	ModeHybrid   = "hybrid"
)

Search modes. `auto` is the default and resolves to hybrid when both legs are available, else to whichever leg is.

Variables

This section is empty.

Functions

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the surface. Every route is a typed op, so it projects to OpenAPI, MCP tools and the generated CLI from the SAME registration — a Router without the op registry cannot carry it, and the mount fails loudly rather than registering routes no projection would know about.

Types

type BackendStatus

type BackendStatus struct {
	Name   string `json:"name"`
	Status string `json:"status"`
	Hits   int    `json:"hits"`
	TookMS int64  `json:"took_ms"`
	Error  string `json:"error,omitempty"`
}

BackendStatus reports one leg's outcome. It is present for EVERY leg on EVERY response, including the ones that were skipped, so a client never has to infer from absence.

type Match

type Match struct {
	Backend string  `json:"backend"`
	Rank    int     `json:"rank"`
	Score   float64 `json:"score"`
}

Match is PROVENANCE: one backend's contribution to one result. A fused ranking without this is undebuggable — you cannot distinguish a hit both legs agreed on from a hit only one leg saw, nor tell a healthy leg from one quietly returning nothing.

type Request

type Request struct {
	// Query is the natural-language or keyword query. Required.
	Query string `json:"query"`
	// Mode selects the legs: auto (default) | text | semantic | hybrid.
	Mode string `json:"mode,omitempty"`
	// Project narrows to one project scope within the org.
	Project string `json:"project,omitempty"`
	// DocTypes restricts the semantic leg to a subset of indexed knowledge types.
	DocTypes []string `json:"doctypes,omitempty"`
	// Index names the lexical index to query. Defaults to "kb".
	Index string `json:"index,omitempty"`
	// Limit bounds the FUSED result set (default 10, max 50).
	Limit int `json:"limit,omitempty"`
	// Offset pages the fused result set.
	Offset int `json:"offset,omitempty"`
}

Request is the ONE query shape. There is deliberately no `org` field: the tenant is the validated principal, so a caller can never search another org by asking.

type Response

type Response struct {
	// Status is the query's overall honesty signal:
	//   ok          every consulted leg answered.
	//   partial     at least one leg failed; Hits holds the survivors' results.
	//   unavailable every consulted leg failed; Hits is empty AND that is stated.
	Status string `json:"status"`
	// Mode is the mode actually used after `auto` resolution.
	Mode string `json:"mode"`
	// Hits is the fused, ranked result set.
	Hits []Result `json:"hits"`
	// Backends is the per-leg report. Always populated.
	Backends []BackendStatus `json:"backends"`
	TookMS   int64           `json:"took_ms"`
}

Response is the ONE result shape.

func ForOrg

func ForOrg(ctx context.Context, org string, in *Request) (*Response, error)

ForOrg is the composition itself, for callers that have ALREADY established the tenant by some other means than an HTTP principal — notably the Team transactor, which runs in this same binary and holds a session whose workspace is its org. Such a caller gets the identical fused answer with no HTTP hop and no second retrieval path.

org MUST be a tenant the caller has authenticated. This function does not and cannot check that; it is the caller's boundary, exactly as it is for every other in-process store API in the codebase.

func Query

func Query(ctx context.Context, in *Request) (*Response, error)

Query is the typed op behind POST /v1/search. It does exactly two things the in-process entry point must not do: resolve the tenant from the validated principal, and refuse when there is none. Everything else is ForOrg.

type Result

type Result struct {
	ID      string  `json:"id"`
	Corpus  string  `json:"corpus"`
	DocType string  `json:"doctype,omitempty"`
	Title   string  `json:"title,omitempty"`
	URL     string  `json:"url,omitempty"`
	Project string  `json:"project,omitempty"`
	Score   float64 `json:"score"`
	Matched []Match `json:"matched"`
}

Result is one fused hit. Score is the FUSED score (see fuse.go); each backend's native score stays in Matched, because the two are different things and flattening them loses the ability to explain a ranking.

Directories

Path Synopsis
Package rank is the ONE rank-fusion implementation in the codebase.
Package rank is the ONE rank-fusion implementation in the codebase.

Jump to

Keyboard shortcuts

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