Step Search (:search)
Quick Answer
Use :search to query workspace resources and entities without external HTTP calls.
{:type :search :query "..." :mode :hybrid :keyword-mode :balanced :targets [:resources :flows :runs :connections] :limit 12}
For :resources, Breyta supports three retrieval modes:
:lexical— keyword/path/metadata search. Use this for deterministic strict keyword matching.:hybrid— lexical plus semantic ranking when the semantic index is enabled; falls back to lexical with diagnostics when semantic reads are unavailable.:semantic— semantic-only retrieval. Use this only when the semantic index is enabled; otherwise the request fails instead of silently returning lexical results.
The same :search step keeps exact path/URI pinning, storage filters, and hydration behavior.
For lexical matching, :keyword-mode controls term recall:
:all— every keyword must match one resource. Use this for strict lexical searches.:balanced— removes common query words and requires a strong subset of significant keywords. Use this for natural-language questions over small resource sets.:any— any keyword can match. Use this for broad discovery or when a later step will filter relevance.
When :targets includes :resources and the flow omits the retrieval knobs, the runtime defaults resource retrieval to :mode :hybrid and :keyword-mode :balanced. If the flow explicitly sets :mode :lexical and omits :keyword-mode, keyword matching defaults back to strict :all. Workspace entity-only searches still report the legacy lexical/all defaults.
When :targets includes :resources, the step hydrates the top resource hits by default so flows can send full file or blob content to downstream steps without an extra fetch path.
Use :persist {:search-index ...} on the producing step when you need persisted artifacts to be easier to find later by text, tags, or source label.
Canonical Shape
| Field | Type | Required | Notes |
|---|---|---|---|
:type | keyword | Yes | Must be :search |
:query | string | Yes | Non-blank search query |
:mode | keyword/string | No | Resource search mode: :lexical, :hybrid, or :semantic. Default :hybrid when :resources is targeted, otherwise :lexical. Applies only to :resources. |
:keyword-mode | keyword/string | No | Lexical keyword matching behavior: :all, :balanced, or :any. Default :balanced for resource-targeted hybrid retrieval, :all for explicit lexical mode and non-resource searches. Applies to the lexical branch of :resources, including hybrid fallback. |
:targets | vector | No | Any of :resources, :flows, :runs, :connections (default: all) |
:storage-backend | keyword/string | No | Resource-target filter such as :gcs. |
:storage-root | string | No | Resource-target filter such as "reports/acme". |
:path-prefix | string | No | Relative resource path filter under the effective storage root, such as "exports/2026". |
:limit | int | No | Per-target max results, 1 to 20 (default: 12) |
:hydrate | map | No | Resource hydration config. Defaults to enabled when :resources is targeted. |
Hydrate Config
| Field | Type | Required | Notes |
|---|---|---|---|
:enabled | boolean | No | Default true when :resources is targeted, otherwise false |
:top-k | int | No | Max resource hits to hydrate, 1 to 10 (default: 5) |
:max-chars | int | No | Max inline text chars per hydrated resource, 1 to 100000 (default: 20000) |
Runtime Behavior
- Search scope is always the current workflow runtime workspace.
workspace-idis taken from runtime context, not step input.- Search clients are injected by runtime wiring:
- resources search client
- workspace search client (flows/runs/connections)
- If a requested target has no configured search client, step execution fails with a validation error.
:modeapplies only to:resourceshits.:lexicalalways uses lexical resource search.:keyword-modeapplies only to resource lexical matching. It does not change:flows,:runs, or:connectionsworkspace search.:keyword-mode :balancedsupports recognized query-string syntax such as quoted phrases, wildcards, fuzzy suffixes, boosts, resource field filters, and uppercaseAND/OR/NOT. Ordinary punctuation in literal text or resource URIs stays on the keyword path.:hybriduses semantic retrieval only when the Elastic resource-search backend has semantic reads enabled and no explicit sort is requested; otherwise it returns lexical results with fallback diagnostics.:semanticrequires semantic reads and a non-blank query. If semantic reads are unavailable, the request fails loudly.:flows,:runs, and:connectionsstill use workspace search.- Local memory search and emulator fallback stay lexical.
- Explicit sorting disables hybrid semantic ranking. Sorted resource searches keep deterministic lexical/index ordering.
- Resource hydration uses the runtime workspace resource store and preserves workspace isolation.
- Hydration only applies to
:resourceshits. :storage-backend,:storage-root, and:path-prefixapply only to:resourceshits.- The resource index keeps both the full physical
pathand normalized storage fields (storage_backend,storage_root,path_under_root). :path-prefixis interpreted againstpath_under_root, so it stays relative to the effective storage root instead of the absolute workspace storage path.- Text-like resources return inline
:content. - Non-text resources return
:content-urlwith:hydration-status :linked-binary. - Oversized or failed reads keep the hit and mark
:hydration-statusinstead of failing the whole search step.
Public Docs Retrieval
:search does not search Breyta public docs directly. It only searches the current workflow runtime workspace.
When a flow needs Breyta docs at runtime, use an explicit :http step against the public docs API:
- search docs pages with
GET /api/docs/pages - fetch page content with
GET /api/docs/pages/:slug?format=md
Use a :http-api connection slot with :base-url "https://flows.breyta.ai" so the flow body only contains API paths. See Step HTTP for a complete copy-pasteable example.
Searchable Resource Lifecycle
Resource search is workspace-scoped and runs over indexed resource metadata plus indexed text when available.
The most common searchable resource sources are:
- workspace files
- persisted step artifacts (
res://...) - HTTP page responses auto-indexed by
:http - retained artifacts whose indexed text/metadata were customized with
:persist {:search-index ...}
Use :search-index inside the :persist map when the stored payload bytes are not the best search text on their own, or when the resource should show a more useful label in search results.
Successful :http GET responses for page-like content can be auto-persisted and indexed. This covers HTML, markdown, and article-like plain text pages. See Step HTTP for defaults and opt-out.
When semantic resource search is enabled, indexed article/page text is available to semantic ranking. When it is disabled, the same resources are still found through lexical keyword and metadata matching.
For fast operator proof outside the flow runtime, start with:
breyta resources search "refund support bundle" --mode hybrid --keyword-mode balanced
breyta steps run --type search --id kb-search \
--params '{"query":"refund support bundle","targets":["resources"],"mode":"hybrid","keyword-mode":"balanced","hydrate":{"enabled":true}}'
Result Shape
{:query "refund policy"
:mode :lexical
:keyword-mode :balanced
:requested-mode :hybrid
:semantic-fallback? true
:semantic-fallback-reason "semantic-index-unavailable"
:targets [:resources :flows]
:limit 12
:grouped {:resources [{:target :resources
:title "Refund policy"
:uri "res://..."
:content "Full file content..."
:content-type "text/markdown"
:size-bytes 1842
:content-truncated? false
:hydration-status :inline-text}]
:flows [...]
:runs []
:connections []}
:results [...]} ; flattened in requested target order
Hydrated resource hits may include:
:content:content-url:content-type:size-bytes:content-truncated?:hydration-statusas one of:inline-text,:linked-binary,:too-large,:read-error,:skipped:hydration-error
When :mode :hybrid cannot use semantic retrieval, the step keeps returning
lexical results and includes :requested-mode, :semantic-fallback?, and
:semantic-fallback-reason. Strict :mode :semantic does not fall back.
Canonical Example
'(let [hits (flow/step :search :knowledge
{:query (:question (flow/input))
:mode :hybrid
:keyword-mode :balanced
:targets [:resources :flows :runs]
:storage-backend :gcs
:storage-root "reports/acme"
:path-prefix "exports/2026"
:limit 10
:hydrate {:top-k 5
:max-chars 20000}})]
hits)
Persist, index, and retrieve later:
'(let [bundle (flow/step :function :persist-support-bundle
{:input {:rows (:rows (flow/input))}
:code '(fn [{:keys [rows]}] rows)
:persist {:type :blob
:path "support/bundles"
:filename "refund-bundle.json"
:content-type "application/json"
:search-index {:text "refund support bundle refunds chargebacks returns"
:tags ["refund" "support" "bundle"]
:source-label "Refund support bundle"
:include-raw-content? true}}})
hits (flow/step :search :find-support-bundle
{:query "refund support bundle"
:keyword-mode :balanced
:targets [:resources]
:limit 5
:hydrate {:enabled true
:top-k 1
:max-chars 20000}})]
{:bundle bundle
:hits hits})