Files

207 lines
11 KiB
Bash

#!/usr/bin/env bash
# Nia Search — query, web, deep, universal
# Usage: search.sh <command> [args...]
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib.sh"
# ─── query — AI-powered search across specific repos, docs, or local folders
cmd_query() {
if [ -z "$1" ]; then
echo "Usage: search.sh query <query> <repos_csv> [docs_csv]"
echo " Env: LOCAL_FOLDERS, SLACK_WORKSPACES, CATEGORY, MAX_TOKENS,"
echo " STREAM, INCLUDE_SOURCES, FAST_MODE, SKIP_LLM,"
echo " REASONING_STRATEGY, MODEL, SEARCH_MODE,"
echo " BYPASS_CACHE, SEMANTIC_CACHE_THRESHOLD, INCLUDE_FOLLOW_UPS,"
echo " TRUST_MINIMUM_TIER, TRUST_VERIFIED_ONLY, TRUST_REQUIRE_OVERLAY,"
echo " E2E_SESSION_ID"
echo " Slack filter env: SLACK_CHANNELS, SLACK_USERS, SLACK_DATE_FROM,"
echo " SLACK_DATE_TO, SLACK_INCLUDE_THREADS"
echo " Local source filter env: SOURCE_SUBTYPE, DB_TYPE, CONNECTOR_TYPE,"
echo " CONVERSATION_ID, CONTACT_ID, SENDER_ROLE, TIME_AFTER, TIME_BEFORE"
return 1
fi
local query="$1" repos="${2:-}" docs="${3:-}"
if [ -n "$repos" ]; then
REPOS_JSON=$(echo "$repos" | tr ',' '\n' | jq -R '.' | jq -s 'map({repository: .})')
else REPOS_JSON="[]"; fi
if [ -n "$docs" ]; then
DOCS_JSON=$(echo "$docs" | tr ',' '\n' | jq -R '.' | jq -s '.')
else DOCS_JSON="[]"; fi
if [ -n "${LOCAL_FOLDERS:-}" ]; then
FOLDERS_JSON=$(echo "$LOCAL_FOLDERS" | tr ',' '\n' | jq -R '.' | jq -s '.')
else FOLDERS_JSON="[]"; fi
if [ -n "${SLACK_WORKSPACES:-}" ]; then
SLACK_JSON=$(echo "$SLACK_WORKSPACES" | tr ',' '\n' | jq -R '.' | jq -s '.')
else SLACK_JSON="[]"; fi
LOCAL_SOURCE_FILTERS="null"
if [ -n "${SOURCE_SUBTYPE:-}${DB_TYPE:-}${CONNECTOR_TYPE:-}${CONVERSATION_ID:-}${CONTACT_ID:-}${SENDER_ROLE:-}${TIME_AFTER:-}${TIME_BEFORE:-}" ]; then
LOCAL_SOURCE_FILTERS=$(jq -n \
--arg subtype "${SOURCE_SUBTYPE:-}" --arg dbtype "${DB_TYPE:-}" \
--arg connector "${CONNECTOR_TYPE:-}" --arg conversation "${CONVERSATION_ID:-}" \
--arg contact "${CONTACT_ID:-}" --arg sender "${SENDER_ROLE:-}" \
--arg after "${TIME_AFTER:-}" --arg before "${TIME_BEFORE:-}" \
'{}
+ (if $subtype != "" then {source_subtype: $subtype} else {} end)
+ (if $dbtype != "" then {db_type: $dbtype} else {} end)
+ (if $connector != "" then {connector_type: $connector} else {} end)
+ (if $conversation != "" then {conversation_id: $conversation} else {} end)
+ (if $contact != "" then {contact_id: $contact} else {} end)
+ (if $sender != "" then {sender_role: $sender} else {} end)
+ (if $after != "" then {time_after: $after} else {} end)
+ (if $before != "" then {time_before: $before} else {} end)')
fi
# Build slack_filters if any slack filter env is set
SLACK_FILTERS="null"
if [ -n "${SLACK_CHANNELS:-}${SLACK_USERS:-}${SLACK_DATE_FROM:-}${SLACK_DATE_TO:-}${SLACK_INCLUDE_THREADS:-}" ]; then
SLACK_FILTERS=$(jq -n \
--arg ch "${SLACK_CHANNELS:-}" --arg us "${SLACK_USERS:-}" \
--arg df "${SLACK_DATE_FROM:-}" --arg dt "${SLACK_DATE_TO:-}" \
--arg it "${SLACK_INCLUDE_THREADS:-}" \
'{}
+ (if $ch != "" then {channels: ($ch | split(","))} else {} end)
+ (if $us != "" then {users: ($us | split(","))} else {} end)
+ (if $df != "" then {date_from: $df} else {} end)
+ (if $dt != "" then {date_to: $dt} else {} end)
+ (if $it != "" then {include_threads: ($it == "true")} else {} end)')
fi
SOURCE_TRUST_FILTER="null"
if [ -n "${TRUST_MINIMUM_TIER:-}${TRUST_VERIFIED_ONLY:-}${TRUST_REQUIRE_OVERLAY:-}" ]; then
SOURCE_TRUST_FILTER=$(jq -n \
--arg tier "${TRUST_MINIMUM_TIER:-}" \
--arg verified "${TRUST_VERIFIED_ONLY:-}" \
--arg overlay "${TRUST_REQUIRE_OVERLAY:-}" \
'{}
+ (if $tier != "" then {minimum_trust_tier: $tier} else {} end)
+ (if $verified != "" then {verified_only: ($verified == "true")} else {} end)
+ (if $overlay != "" then {require_overlay: ($overlay == "true")} else {} end)')
fi
# Auto-detect search mode
if [ -n "$repos" ] && [ -z "$docs" ]; then MODE="repositories"
elif [ -z "$repos" ] && [ -n "$docs" ]; then MODE="sources"
else MODE="unified"; fi
if [ -n "${SEARCH_MODE:-}" ]; then MODE="$SEARCH_MODE"; fi
DATA=$(jq -n \
--arg q "$query" --arg mode "$MODE" \
--argjson repos "$REPOS_JSON" --argjson docs "$DOCS_JSON" \
--argjson folders "$FOLDERS_JSON" --argjson slack "$SLACK_JSON" \
--argjson slack_filters "$SLACK_FILTERS" --argjson local_filters "$LOCAL_SOURCE_FILTERS" \
--argjson trust_filter "$SOURCE_TRUST_FILTER" \
--arg cat "${CATEGORY:-}" --arg mt "${MAX_TOKENS:-}" \
--arg stream "${STREAM:-}" --arg include_sources "${INCLUDE_SOURCES:-}" \
--arg fast "${FAST_MODE:-}" --arg skip "${SKIP_LLM:-}" \
--arg rs "${REASONING_STRATEGY:-}" --arg model "${MODEL:-}" \
--arg bc "${BYPASS_CACHE:-}" --arg sct "${SEMANTIC_CACHE_THRESHOLD:-}" \
--arg ifu "${INCLUDE_FOLLOW_UPS:-}" --arg e2e "${E2E_SESSION_ID:-}" \
'{mode: "query", messages: [{role: "user", content: $q}], repositories: $repos,
data_sources: $docs, search_mode: $mode, stream: false, include_sources: true}
+ (if ($folders | length) > 0 then {local_folders: $folders} else {} end)
+ (if ($slack | length) > 0 then {slack_workspaces: $slack} else {} end)
+ (if $slack_filters != null then {slack_filters: $slack_filters} else {} end)
+ (if $local_filters != null then {local_source_filters: $local_filters} else {} end)
+ (if $trust_filter != null then {source_trust_filter: $trust_filter} else {} end)
+ (if $cat != "" then {category: $cat} else {} end)
+ (if $mt != "" then {max_tokens: ($mt | tonumber)} else {} end)
+ (if $stream != "" then {stream: ($stream == "true")} else {} end)
+ (if $include_sources != "" then {include_sources: ($include_sources == "true")} else {} end)
+ (if $fast != "" then {fast_mode: ($fast == "true")} else {} end)
+ (if $skip != "" then {skip_llm: ($skip == "true")} else {} end)
+ (if $rs != "" then {reasoning_strategy: $rs} else {} end)
+ (if $model != "" then {model: $model} else {} end)
+ (if $bc != "" then {bypass_semantic_cache: ($bc == "true")} else {} end)
+ (if $sct != "" then {semantic_cache_threshold: ($sct | tonumber)} else {} end)
+ (if $ifu != "" then {include_follow_ups: ($ifu == "true")} else {} end)
+ (if $e2e != "" then {e2e_session_id: $e2e} else {} end)')
nia_post "$BASE_URL/search" "$DATA"
}
# ─── web — search the public web, filterable by category and recency
cmd_web() {
if [ -z "$1" ]; then
echo "Usage: search.sh web <query> [num_results]"
echo " Env: CATEGORY (github|company|research|news|tweet|pdf|blog), DAYS_BACK, FIND_SIMILAR_TO"
return 1
fi
echo "⚠ Reminder: Did you check indexed sources first? Run repos.sh list / sources.sh list before using web search." >&2
DATA=$(jq -n \
--arg q "$1" --argjson n "${2:-5}" \
--arg cat "${CATEGORY:-}" --arg days "${DAYS_BACK:-}" --arg sim "${FIND_SIMILAR_TO:-}" \
'{mode: "web", query: $q, num_results: $n}
+ (if $cat != "" then {category: $cat} else {} end)
+ (if $days != "" then {days_back: ($days | tonumber)} else {} end)
+ (if $sim != "" then {find_similar_to: $sim} else {} end)')
nia_post "$BASE_URL/search" "$DATA"
}
# ─── deep — deep AI research that synthesizes multiple web sources (Pro)
cmd_deep() {
if [ -z "$1" ]; then
echo "Usage: search.sh deep <query> [output_format]"
echo " Env: VERBOSE=true for trace output"
return 1
fi
DATA=$(jq -n \
--arg q "$1" --arg fmt "${2:-}" --arg verbose "${VERBOSE:-}" \
'{mode: "deep", query: $q}
+ (if $fmt != "" then {output_format: $fmt} else {} end)
+ (if $verbose == "true" then {verbose: true} else {} end)')
nia_post "$BASE_URL/search" "$DATA"
}
# ─── universal — hybrid semantic+keyword search across all your indexed sources
cmd_universal() {
if [ -z "$1" ]; then
echo "Usage: search.sh universal <query> [top_k]"
echo " Env: INCLUDE_REPOS, INCLUDE_DOCS, INCLUDE_HF, ALPHA, COMPRESS,"
echo " MAX_TOKENS, MAX_SOURCES, SOURCES_FOR_ANSWER, BYPASS_CACHE,"
echo " BYPASS_SEMANTIC_CACHE, SEMANTIC_CACHE_THRESHOLD, BOOST_LANGUAGES,"
echo " LANGUAGE_BOOST, EXPAND_SYMBOLS, NATIVE_BOOSTING"
return 1
fi
DATA=$(jq -n \
--arg q "$1" --argjson k "${2:-20}" \
--arg ir "${INCLUDE_REPOS:-true}" --arg id "${INCLUDE_DOCS:-true}" \
--arg ihf "${INCLUDE_HF:-}" --arg alpha "${ALPHA:-}" \
--arg compress "${COMPRESS:-false}" --arg mt "${MAX_TOKENS:-}" \
--arg ms "${MAX_SOURCES:-}" --arg sfa "${SOURCES_FOR_ANSWER:-}" \
--arg bc "${BYPASS_CACHE:-}" --arg sct "${SEMANTIC_CACHE_THRESHOLD:-}" \
--arg bl "${BOOST_LANGUAGES:-}" --arg lbf "${LANGUAGE_BOOST:-}" \
--arg bsc "${BYPASS_SEMANTIC_CACHE:-}" \
--arg es "${EXPAND_SYMBOLS:-}" --arg nb "${NATIVE_BOOSTING:-${USE_NATIVE_BOOSTING:-}}" \
'{mode: "universal", query: $q, top_k: $k,
include_repos: ($ir == "true"), include_docs: ($id == "true"),
compress_output: ($compress == "true")}
+ (if $ihf != "" then {include_huggingface_datasets: ($ihf == "true")} else {} end)
+ (if $alpha != "" then {alpha: ($alpha | tonumber)} else {} end)
+ (if $mt != "" then {max_tokens: ($mt | tonumber)} else {} end)
+ (if $ms != "" then {max_sources: ($ms | tonumber)} else {} end)
+ (if $sfa != "" then {sources_for_answer: ($sfa | tonumber)} else {} end)
+ (if $bc != "" then {bypass_cache: ($bc == "true")} else {} end)
+ (if $sct != "" then {semantic_cache_threshold: ($sct | tonumber)} else {} end)
+ (if $bl != "" then {boost_languages: ($bl | split(","))} else {} end)
+ (if $lbf != "" then {language_boost_factor: ($lbf | tonumber)} else {} end)
+ (if $bsc != "" then {bypass_semantic_cache: ($bsc == "true")} else {} end)
+ (if $es != "" then {expand_symbols: ($es == "true")} else {} end)
+ (if $nb != "" then {use_native_boosting: ($nb == "true")} else {} end)')
nia_post "$BASE_URL/search" "$DATA"
}
# ─── dispatch ─────────────────────────────────────────────────────────────────
case "${1:-}" in
query) shift; cmd_query "$@" ;;
web) shift; cmd_web "$@" ;;
deep) shift; cmd_deep "$@" ;;
universal) shift; cmd_universal "$@" ;;
*)
echo "Usage: $(basename "$0") <command> [args...]"
echo ""
echo "Commands:"
echo " query Query specific repos/sources with AI"
echo " web Web search"
echo " deep Deep research (Pro only)"
echo " universal Search across all public indexed sources"
exit 1
;;
esac