Compare commits

2 Commits
Author SHA1 Message Date
haylanandClaude-Bot a3ecbc0e02 docs(research): investigate LiteLLM knowledgebase/vector_store feature for #23
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 21:26:39 +02:00
haylanandClaude-Bot 8c42f2518b research: LiteLLM web-search + SearXNG wiring (issue #22)
Answers whether SearXNG is natively supported by LiteLLM's /v1/search
feature, whether it's a model-tool or automatic retrieval, what
docker-compose.yml networking change is needed for the litellm container
to reach the LAN's search.home host, and how it interacts with this
project's known-flaky Qwen3.8-27B tool-calling.

Research only — litellm-config.yaml and docker-compose.yml are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 21:25:30 +02:00
2 changed files with 565 additions and 0 deletions
+305
View File
@@ -0,0 +1,305 @@
# Research: LiteLLM vector store / knowledgebase feature (for Gitea issue #23)
**Question:** Can LiteLLM's built-in "knowledgebase" / vector store feature be
used to load two Markdown fact files (`data/memory.md`,
`data/claude-legacy-memory.md`) into a queryable knowledge base, and if so,
what does that require from this stack (config shape, backend, embedding
model, ingestion mechanism)?
**Bottom line: no, not against this stack's existing Qdrant instance, and not
without adding a dedicated embedding model.** LiteLLM's vector store feature
is a *routing/registry* layer over a small set of natively-integrated
backends (Bedrock Knowledge Bases, OpenAI Vector Stores, Azure
Vector/AI-Search, Vertex AI RAG/Search, Gemini File Search, RAGFlow) plus one
self-hosted option — Postgres+pgvector, via a **separate companion service**
(`BerriAI/litellm-pgvector`), not Qdrant. There is no Qdrant provider at all.
Ingesting the two fact files would also require running a dedicated
embedding-capable model, which this stack doesn't currently have (the one
llama.cpp instance serves a chat model, not started with `--embeddings`).
## 1. Config shape in `config.yaml`
Top-level block is **`vector_store_registry`** (this is the current key name;
an earlier PR that introduced the feature used `vector_stores` — see
Provenance note below), a list of entries:
```yaml
vector_store_registry:
- vector_store_name: "my-knowledgebase" # optional friendly name
litellm_params:
vector_store_id: "T37J8R4WTM" # required, provider's own ID
custom_llm_provider: "bedrock" # required — selects backend
vector_store_description: "..." # optional
vector_store_metadata: {} # optional
litellm_credential_name: "..." # optional, reuse a named credential
embedding_model: "..." # backend-dependent — see §3
```
Source: [docs.litellm.ai/docs/completion/knowledgebase](https://docs.litellm.ai/docs/completion/knowledgebase)
(fetched directly). Earlier shape (same idea, older field names) documented
in [BerriAI/litellm PR #10448](https://github.com/BerriAI/litellm/pull/10448)
("[Feat] Vector Stores/KnowledgeBases - Allow defining Vector Store Configs"):
```yaml
vector_stores:
- vector_store_name: "bedrock-litellm-website-knowledgebase"
litellm_params:
custom_llm_provider: "bedrock"
id: "T37J8R4WTM"
```
This is a **registry of pointers to knowledge bases that already exist on
the backend provider** — it is not itself a vector database. `model_list`
entries are not where a store is attached; `vector_store_registry` is its
own top-level sibling of `model_list`.
### Referencing a vector store from a request
Not via a `model_list` entry — via the **`tools`** array of a
`/chat/completions` (or `/v1/responses`) request, OpenAI Assistants-style
`file_search` tool:
```json
{
"model": "gpt-4o",
"messages": [...],
"tools": [
{
"type": "file_search",
"vector_store_ids": ["T37J8R4WTM"]
}
]
}
```
LiteLLM intercepts the tool call, looks up the referenced ID in
`vector_store_registry`, calls that backend's native search, and injects the
retrieved chunks into the prompt before calling the target model. Source:
[docs.litellm.ai/docs/completion/knowledgebase](https://docs.litellm.ai/docs/completion/knowledgebase).
There are also direct, OpenAI-compatible proxy endpoints for managing/using a
store outside of a chat completion — see §4.
## 2. Backend providers — Qdrant is NOT one of them
Per the official docs page and the PR that introduced the feature, the
natively-supported `custom_llm_provider` values are:
| Provider | `custom_llm_provider` value | Notes |
|---|---|---|
| AWS Bedrock Knowledge Bases | `bedrock` | Original/reference implementation ([PR #10448](https://github.com/BerriAI/litellm/pull/10448)) |
| OpenAI Vector Stores | `openai` | Wraps OpenAI's own vector store API |
| Azure Vector Stores | `azure` | Assistants-API-only per docs |
| Azure AI Search | `azure_ai_search` (vector search capability) | |
| Vertex AI RAG Engine | `vertex_ai` | Added in [PR #15781](https://github.com/BerriAI/litellm/pull/15781) |
| Vertex AI Search API | `vertex_ai/search_api` | |
| Gemini File Search | — | |
| RAGFlow Datasets | — | Dataset mgmt only; search unsupported per docs |
| Postgres + pgvector | `pg_vector` (via a **separate** connector service) | See below — not built into the litellm proxy image |
Source: [docs.litellm.ai/docs/completion/knowledgebase](https://docs.litellm.ai/docs/completion/knowledgebase),
cross-checked against [PR #12595](https://github.com/BerriAI/litellm/pull/12595)
("[Feat] Vector Stores - Add Vertex RAG Engine API as a provider") and
[PR #15781](https://github.com/BerriAI/litellm/pull/15781) ("(feat) Vector
Stores: support Vertex AI Search API").
**Qdrant is not listed anywhere** in the knowledgebase docs, the vector
store provider PRs, or the pgvector connector's own README. LiteLLM does use
Qdrant in one unrelated feature — **semantic response caching**
(`docs.litellm.ai/docs/caching/all_caches`, `qdrant_api_base` /
`qdrant_api_key` / `qdrant_collection_name` config) — but that is a cache for
LLM *responses*, not the vector-store/knowledgebase (RAG) feature, and shares
no config or code path with `vector_store_registry`. It would not let
LiteLLM search Qdrant-held documents as a knowledge base.
### The pgvector option is a separate microservice, not a built-in backend
[`BerriAI/litellm-pgvector`](https://github.com/BerriAI/litellm-pgvector) is
its own repo/container: a FastAPI app that exposes OpenAI-compatible vector
store endpoints backed by Postgres with the `pgvector` extension, calling
back out to a LiteLLM proxy's `/embeddings` endpoint to generate embeddings.
It is registered into `vector_store_registry` like any other backend (with
`custom_llm_provider: pg_vector` pointed at this companion service's URL),
but it is **not Qdrant** and **not part of the main `litellm` proxy image**
already running in this stack — it would mean deploying and operating a
fourth service (on top of `litellm`, `litellm-db`, and `llama-server`), with
its own Postgres database using the pgvector extension (the existing
`litellm-db` Postgres image, `postgres:16-alpine`, does not have pgvector
installed).
### Verdict on the existing Qdrant instance
**LiteLLM's knowledgebase/vector_store feature cannot point at this repo's
existing standalone `qdrant` service.** There is no Qdrant provider type for
`vector_store_registry`. To use LiteLLM's native feature at all, this stack
would need to either integrate with a cloud-native backend (Bedrock/Vertex/
Azure/OpenAI — none of which apply, this stack is local-only) or stand up
the separate `litellm-pgvector` + pgvector-enabled Postgres stack — an
entirely different vector store technology from the Qdrant already running
for Open WebUI. The existing Qdrant collection Open WebUI uses for its own
RAG/Memory feature is unrelated to and unreachable from LiteLLM's
knowledgebase feature.
## 3. Embedding model requirement
LiteLLM's vector store feature **does call an embedding endpoint itself**
when a backend needs one (pgvector explicitly; the managed cloud backends
handle embedding server-side). The field is `embedding_model` inside a
`vector_store_registry` entry's `litellm_params`, confirmed in
[BerriAI/litellm issue #23980](https://github.com/BerriAI/litellm/issues/23980)
("[Bug]: Vector store creation fails when using model mapping public model
name for embedding_model"), which shows:
```json
"litellm_params": {
"vector_bucket_name": "my-embeddings",
"index_name": "test-index",
"aws_region_name": "us-east-1",
"embedding_model": "test-vector-store/bedrock/amazon.nova-2-multimodal-embeddings-v1:0"
}
```
For the pgvector connector specifically, the embedding model is configured
via its own env vars (not `vector_store_registry`, since it's a separate
service): `EMBEDDING__MODEL`, `EMBEDDING__BASE_URL` (pointed at a LiteLLM
proxy), `EMBEDDING__API_KEY`, `EMBEDDING__DIMENSIONS` — e.g.
`EMBEDDING__MODEL=text-embedding-ada-002`,
`EMBEDDING__BASE_URL=http://litellm:4000`. Source:
[BerriAI/litellm-pgvector README](https://github.com/BerriAI/litellm-pgvector/blob/main/README.md).
This confirms: **yes, the embedding model must be reachable as a model
LiteLLM's proxy can call** — i.e. it needs its own `model_list` entry with
`mode: embedding` (LiteLLM's standard way of declaring an embedding-capable
model — see [docs.litellm.ai/docs/embedding/supported_embedding](https://github.com/BerriAI/litellm/blob/main/docs/my-website/docs/embedding/supported_embedding.md)),
so the pgvector service (or LiteLLM itself for backends that embed
internally) can call `POST /embeddings` against it through the proxy.
### Does llama.cpp (this repo's model server) support `/embeddings`?
Yes, but not enabled as currently configured, and not well-suited to the
model already loaded. llama.cpp's server (`tools/server`, the same
`ghcr.io/ggml-org/llama.cpp:server-rocm` image this repo uses per
`docker-compose.yml`) exposes an OpenAI-compatible `POST /v1/embeddings`
route (and a native `/embedding` route), **but only when started with the
`--embeddings` flag** — source:
[ggml-org/llama.cpp tools/server/README.md](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md).
This repo's `llama-server` command in `docker-compose.yml` (lines 1622) does
not pass `--embeddings`, so the currently-running instance does not serve
embeddings at all.
Even if the flag were added, llama.cpp loads **one model per server
process** — the flag would make the already-loaded Qwen3.8-27B **chat**
model (per `litellm-config.yaml`, `qwen3.8-27b-local` /
`Qwen3.8-27B-UD-Q4_K_XL.gguf`) emit pooled hidden-state vectors as
"embeddings," but a generalist instruction-tuned chat model is not what
that's trained for — embedding quality from a non-embedding-trained model is
materially worse than a purpose-trained embedding model (e.g. BGE, Nomic
Embed, mxbai-embed, gte). **A separate, dedicated embedding model/server
would be needed** — either a second `llama-server` container loaded with a
small GGUF embedding model (`--embeddings` flag on), or a different
embedding-serving stack — and it would need its own `model_list` entry in
`litellm-config.yaml` with `mode: embedding` for LiteLLM/litellm-pgvector to
call it.
## 4. How documents actually get ingested
Two ingestion paths exist depending on backend, both are HTTP APIs — there
is no admin-UI "add document" flow beyond store *creation*, and no CLI:
**a) OpenAI-compatible vector-store file API** (used for the `openai`
backend, and shown as the general pattern in the docs):
- `POST /v1/vector_stores` — create a store. Body:
```json
{
"name": "My Document Store",
"file_ids": ["file-abc123"],
"chunking_strategy": {
"type": "static",
"static": {"max_chunk_size_tokens": 800, "chunk_overlap_tokens": 400}
},
"metadata": {"key": "value"}
}
```
Requires files already uploaded through a separate Files API to obtain
`file_id`s first — the docs page does not show a files-upload endpoint
under the vector-store docs directly (this is OpenAI's own two-step
upload-then-attach flow, proxied through). Source:
[docs.litellm.ai/docs/vector_stores/create](https://docs.litellm.ai/docs/vector_stores/create).
- `POST /v1/vector_stores/{vector_store_id}/search` — query it:
```json
{
"query": "What is the capital of France?",
"filters": {"file_ids": ["file-abc123"]},
"max_num_results": 5,
"ranking_options": {"score_threshold": 0.7},
"rewrite_query": true
}
```
Source: [docs.litellm.ai/docs/vector_stores/search](https://docs.litellm.ai/docs/vector_stores/search).
- `GET /vector_store/list` — list registered stores. Source:
[docs.litellm.ai/docs/completion/knowledgebase](https://docs.litellm.ai/docs/completion/knowledgebase).
- LiteLLM Admin UI: **Experimental → Vector Stores → Create Vector Store**
exists for registering/creating stores, and the **Logs** page shows
vector-store search queries/scores after use — but this is store
management and observability, not a bulk document-ingestion UI. Source:
[docs.litellm.ai/docs/completion/knowledgebase](https://docs.litellm.ai/docs/completion/knowledgebase).
**b) `litellm-pgvector` connector's own embeddings API** (only path relevant
if this repo went the self-hosted pgvector route, since Qdrant isn't
supported at all): direct chunk-level ingestion, no file upload step —
- `POST /v1/vector_stores/{id}/embeddings` — single chunk:
```json
{"content": "...", "embedding": [/* optional, else computed server-side */], "metadata": {}}
```
- `POST /v1/vector_stores/{id}/embeddings/batch` — array of the same shape,
for bulk loading.
Source: [BerriAI/litellm-pgvector README](https://github.com/BerriAI/litellm-pgvector/blob/main/README.md).
For a follow-up ticket that wants to programmatically load
`data/memory.md` / `data/claude-legacy-memory.md` (dated fact lists) into a
knowledge base, the realistic path through LiteLLM's native feature would be
the pgvector connector's batch-embeddings endpoint (chunk the Markdown into
facts/sections client-side, POST each as a batch) — **but that requires
first standing up**: (1) a pgvector-enabled Postgres, (2) the
`litellm-pgvector` service, and (3) a dedicated embedding model server
registered in `litellm-config.yaml`. None of that reuses the Qdrant instance
already running in this stack.
## 5. Repo context read for this research
- `litellm-config.yaml` — current config has one `model_list` entry
(`qwen3.8-27b-local`, chat-only, via llama.cpp), `router_settings`
(priority scheduling), `general_settings.master_key`. No
`vector_store_registry` block exists yet.
- `docker-compose.yml` — confirms `qdrant` service (image `qdrant/qdrant`,
network `ai-stack`, no host port, only `open-webui` currently consumes it
via `VECTOR_DB=qdrant` / `QDRANT_URI=http://qdrant:6333`); confirms
`llama-server` command has no `--embeddings` flag and loads a single GGUF
(`Qwen3.8-27B-UD-Q4_K_XL.gguf`); confirms `litellm-db` is plain
`postgres:16-alpine` (no pgvector extension installed).
- `CLAUDE.md` — points to `docs/agents/issue-tracker.md` (Gitea issues via
`tea`) and `docs/agents/domain.md` (domain docs convention).
- `docs/agents/domain.md` — says to check `CONTEXT.md` and `docs/adr/` at
repo root before exploring, but "if any of these files don't exist,
proceed silently." Neither `CONTEXT.md` nor `docs/adr/` exist in this repo
yet, so there's no glossary/ADR conflict to flag.
- `docs/research/` — repeated existing precedent (e.g.
`docs/research/proxy-shadow-pricing.md`, `docs/research/voidllm-evaluation.md`,
`docs/research/qwen3.8-27b-tool-calling.md`) confirms this is the
established location and Markdown format for this kind of investigation;
this file follows that convention.
## Provenance note on the top-level config key name
The docs page fetched live (`docs.litellm.ai/docs/completion/knowledgebase`)
shows `vector_store_registry` as the current top-level key. The original
feature PR ([#10448](https://github.com/BerriAI/litellm/pull/10448)) used
`vector_stores` in its example YAML. If implementing against a specific
pinned LiteLLM version, verify the exact key against that version's docs/
source rather than assuming either name — this repo's `docker-compose.yml`
pins `ghcr.io/berriai/litellm:main-stable`, a rolling tag, so the schema in
whatever image is actually pulled should be spot-checked (e.g. `GET /openapi.json`
against the running proxy, or grepping the image's installed
`litellm/types/router.py` / proxy schema) before writing config against it.
+260
View File
@@ -0,0 +1,260 @@
# Research: Wiring the local SearXNG instance into LiteLLM's web-search feature
**Question:** How does LiteLLM's web-search integration
(https://docs.litellm.ai/docs/search) actually work, and what does wiring the
local SearXNG instance (`http://search.home/`) into this repo's
`litellm-config.yaml` require?
**Answer, short version:** SearXNG **is** a natively supported provider for
LiteLLM's `/v1/search` feature — no custom-endpoint workaround needed. But the
feature is **not** a model-callable tool and **not** automatic
context-injection into chat completions either — it's a **separate REST API**
(`/v1/search/{search_tool_name}`) that a caller (Open WebUI, a script, a
future MCP wrapper) must invoke directly, independent of any LLM call. That
sidesteps this project's known-flaky Qwen3.8-27B tool-calling entirely, as
long as nothing wraps the endpoint back into model-driven tool-calling.
Reachability is the real blocker: `search.home` is a LAN mDNS/local-DNS name
that the `litellm` container cannot resolve by default — needs an
`extra_hosts` entry in `docker-compose.yml`.
## 1. What LiteLLM's search feature actually is
LiteLLM ships a **unified search API** (`/v1/search` and
`/v1/search/{search_tool_name}`) that wraps multiple search-provider backends
behind one Perplexity-compatible request/response shape.
Source: https://docs.litellm.ai/docs/search
Config shape in `config.yaml` (LiteLLM's own documented example, Perplexity
shown, same shape for every provider):
```yaml
search_tools:
- search_tool_name: perplexity-search
litellm_params:
search_provider: perplexity
api_key: os.environ/PERPLEXITYAI_API_KEY
```
Call shape:
```bash
curl http://0.0.0.0:4000/v1/search/searxng-search \
-H "Authorization: Bearer sk-1234" \
-H "Content-Type: application/json" \
-d '{"query": "latest AI developments", "max_results": 5}'
```
Source: https://docs.litellm.ai/docs/search
**18 providers are listed as supported**, including Perplexity, Tavily, Exa
AI, Brave, Parallel AI, Google PSE, DataForSEO, Firecrawl, **SearXNG**,
Linkup, Serper, DuckDuckGo, SearchAPI.io, You.com, APISerpent, Bedrock
AgentCore, Nimble, and Bing Grounding.
Source: https://docs.litellm.ai/docs/search
## 2. Is SearXNG natively supported? — Yes
SearXNG is one of the 18 built-in `search_provider` values, added by
BerriAI/litellm PR #16259 ("[Feat] add serxng search API provider").
Source: https://github.com/BerriAI/litellm/pull/16259
Config shape for SearXNG specifically:
```yaml
search_tools:
- search_tool_name: searxng-search
litellm_params:
search_provider: searxng
api_base: https://your-searxng-instance.com
```
Equivalently, the base URL can be supplied via the `SEARXNG_API_BASE`
environment variable instead of an inline `api_base` key — SearXNG has no API
key of its own (it's an unauthenticated local meta-search engine), so this is
the one provider in the list where `litellm_params` doesn't need a secret.
Sources: https://github.com/BerriAI/litellm/pull/16259,
https://docs.litellm.ai/docs/search
**For this repo**, the addition to `litellm-config.yaml` (research only — not
applied here) would look like:
```yaml
search_tools:
- search_tool_name: searxng-search
litellm_params:
search_provider: searxng
api_base: http://search.home/
```
No custom-endpoint or "generic OpenAI-compatible /v1/web_search" fallback is
needed — the concern in the ticket that LiteLLM's docs "may assume a hosted
provider like Tavily/Serper" turned out not to apply; SearXNG is a first-class
`search_provider` value, same shape as every hosted one.
## 3. Tool-call vs. automatic injection vs. a third thing
The ticket asked to determine whether this is (a) a tool the model must
explicitly call, or (b) automatic pre-retrieval/context-injection like
Perplexity's own search-augmented answers. **It's neither** — it's a
**standalone REST endpoint** that sits alongside `/v1/chat/completions`, not
wired into it:
> The documentation indicates this is a separate REST endpoint the
> application calls directly. The page presents `/search` as a standalone API
> endpoint alongside chat completions, not as an automatic injection feature.
> Users explicitly invoke the search endpoint; LiteLLM does not automatically
> inject search results into completions.
Source: https://docs.litellm.ai/docs/search (fetched content, describing the
`/v1/search` and `/v1/search/{search_tool_name}` endpoints as siblings of
`/v1/chat/completions`, not a chat-completion parameter or automatic
retrieval step)
Practical effect: whatever calls this endpoint — Open WebUI's own web-search
feature, a shell script, a future MCP server — does so with a plain HTTP call.
**LiteLLM's model routing and Qwen3.8-27B's tool-calling reliability are not
in that path at all**, unless something downstream chooses to expose this
endpoint back to the model *as* a function-calling tool (e.g. an MCP wrapper
that hands the model a `web_search` tool definition backed by this endpoint —
that would reintroduce the model-must-emit-a-correct-tool-call problem, but
that's a choice made one layer up, not something LiteLLM's `/v1/search`
feature forces).
## 4. Network reachability: `search.home` from inside the `litellm` container
`docker-compose.yml`'s `litellm` service joins only the `ai-stack` bridge
network (`networks: [ai-stack]`, line 116) and gets DNS resolution from
Docker's embedded DNS server for that network — which resolves other
containers by service/container name (`llama-server`, `qdrant`, etc., as
already used at `api_base: http://llama-server:8080/v1` in
`litellm-config.yaml` line 8) but has **no visibility into the LAN's mDNS/
local-DNS namespace** that resolves `search.home` on the host machine or on
LAN clients. So `http://search.home/` will not resolve from inside the
`litellm` container as configured today — this matches the ticket's
suspicion, and is standard Docker bridge-networking behavior, not specific to
this repo.
Source: `g:\_DEV\repos\LLM-Server\docker-compose.yml` (litellm service, lines
94124; `networks:` block, lines 154156)
No `extra_hosts`, `host.docker.internal`, or `network_mode: host` pattern
exists yet anywhere in this compose file to crib from — this would be the
first. (One service, `lazytainer`, already uses `network_mode: host`, but for
an unrelated reason — Docker-socket/host-port introspection — and switching
`litellm` to host networking would be a much bigger blast-radius change than
this ticket needs, dropping the `ai-stack` network isolation for every other
port `litellm` exposes.)
Source: `g:\_DEV\repos\LLM-Server\docker-compose.yml` lines 144152
**Recommendation: `extra_hosts` on the `litellm` service**, mapping
`search.home` to its LAN IP, e.g.:
```yaml
litellm:
...
extra_hosts:
- "search.home:192.0.2.10" # replace with SearXNG's actual LAN IP
```
This is the smallest, most local fix: one line, scoped to the one service
that needs it, no change to network topology or isolation, and it keeps
`litellm-config.yaml`'s `api_base: http://search.home/` value human-readable
(matching this repo's existing preference for symbolic hostnames like
`ai.home` / `proxy.ai.home` documented in `docs/network-access.md`) rather
than hardcoding the LAN IP directly into the YAML config. The IP needs to stay
in sync if SearXNG's host ever gets a new DHCP lease — same caveat that would
apply to any hardcoded-IP alternative, just isolated to one `extra_hosts`
line instead of buried in the search config.
`host.docker.internal` is not applicable here: that special hostname
resolves to the Docker **host's** own IP (useful for reaching a service
running directly on the host machine's loopback), not to arbitrary LAN mDNS
names — it wouldn't help resolve `search.home` unless SearXNG happens to run
on the same physical host as this compose stack.
## 5. Interaction with this project's known-flaky Qwen3.8-27B tool-calling
`docs/research/qwen3.8-27b-tool-calling.md` (2026-08-24) found, with medium-
to-high confidence, that Qwen3.8-27B's tool-calling through llama.cpp
inherits open/partially-fixed upstream parser bugs from the Qwen3.5 lineage
(issues #21158, #20837 in `ggml-org/llama.cpp`) — tool calls can be emitted
but not recognized, or land as inert XML inside a reasoning block, especially
with thinking enabled.
Source: `g:\_DEV\repos\LLM-Server\docs\research\qwen3.8-27b-tool-calling.md`
(section 3, "Bottom line" section)
Given section 3 above (`/v1/search` is a standalone endpoint, not a
model-tool), **that flakiness has no bearing on the recommended integration
path**: nothing about calling `POST /v1/search/searxng-search` from Open
WebUI or a script asks Qwen3.8-27B to emit a tool call at all. The search
happens (or doesn't) independent of the model's tool-calling grammar/parser
entirely.
The risk **would** resurface only if a *different* design choice is made
later — e.g. wrapping this same SearXNG-backed endpoint as an MCP tool or a
`tools=[...]` function definition handed to Qwen3.8-27B in a chat-completion
request, so the model itself decides when to search. That path would inherit
every bug documented in `qwen3.8-27b-tool-calling.md` (calls silently dropped,
calls trapped inside `<think>` blocks, etc.) and would need the live smoke
test that doc recommends before being trusted unattended. **That is not what
LiteLLM's `/v1/search` feature itself requires** — it's a choice a caller
could additionally make on top of it.
## Recommendation
1. Add a `search_tools` block to `litellm-config.yaml` using
`search_provider: searxng` and `api_base: http://search.home/` (see
section 2) — no custom/generic-endpoint workaround needed, this is a
first-class supported provider.
2. Add `extra_hosts: ["search.home:<LAN IP>"]` to the `litellm` service in
`docker-compose.yml` (see section 4) so the container can resolve the
hostname; confirm the IP is stable (static DHCP reservation) since
`extra_hosts` is a static mapping baked in at container start.
3. Treat `/v1/search` as a plain HTTP integration point, not a model tool —
whatever calls it (Open WebUI, a script) should call the REST endpoint
directly rather than exposing it to Qwen3.8-27B as a function-calling
tool, to avoid inheriting this project's documented tool-calling
flakiness (section 5). If model-driven search-tool-calling is wanted
later, that's a separate decision that should be smoke-tested against the
caveats in `qwen3.8-27b-tool-calling.md` first.
This is research only — `litellm-config.yaml` and `docker-compose.yml` are
not modified by this doc.
## Sources
- https://docs.litellm.ai/docs/search — LiteLLM search feature docs:
endpoints, `search_tools` config shape, provider list, standalone-endpoint
behavior.
- https://github.com/BerriAI/litellm/pull/16259 — SearXNG provider
implementation: `search_provider: searxng`, `api_base` /
`SEARXNG_API_BASE` config.
- `g:\_DEV\repos\LLM-Server\docker-compose.yml``litellm` service
definition (lines 94124), `ai-stack` network block (lines 154156),
`lazytainer`'s `network_mode: host` precedent (lines 144152).
- `g:\_DEV\repos\LLM-Server\litellm-config.yaml` — current proxy config
conventions (`model_list`, `litellm_params`, `api_base` usage at line 8).
- `g:\_DEV\repos\LLM-Server\docs\network-access.md` — this repo's existing
`*.home` / NPM hostname conventions.
- `g:\_DEV\repos\LLM-Server\docs\research\qwen3.8-27b-tool-calling.md`
prior findings on Qwen3.8-27B tool-calling reliability via llama.cpp.
## Confidence/uncertainty summary
- **High confidence:** SearXNG is a native, first-class `search_provider` in
LiteLLM's `/v1/search` feature (directly documented and confirmed via the
implementing PR); the feature is a standalone REST endpoint separate from
chat completions, not automatic context-injection and not itself a
model-callable tool.
- **Medium confidence:** the exact field name (`api_base` vs. relying solely
on `SEARXNG_API_BASE`) — two independent fetch passes against
docs.litellm.ai returned slightly different renderings of the same example
(one showed `api_key: os.environ/SEARXNG_API_BASE`, the other and the PR
fetch showed `api_base: <url>`); the PR-sourced `api_base` form is treated
as authoritative here since it comes from the implementing code change, but
this should be smoke-tested against the actual deployed LiteLLM image
(`ghcr.io/berriai/litellm:main-stable`) before being relied on verbatim.
- **Not independently verified:** SearXNG's actual LAN IP/hostname stability
on this network, and whether the deployed LiteLLM version
(`main-stable`, per `docker-compose.yml` line 95) already includes PR
#16259 — worth a quick `docker exec litellm pip show litellm` / changelog
check before wiring this in for real.