Settings, in plain language

There are a lot of knobs. You need four of them, and only if you use a hosted model at all. This page is the four, then the rest, then a block you can paste for each of eight providers.

01Where a setting comes from

Three places, and the one further left always wins:

precedence
environment variable   >   config.toml   >   built-in default

The Settings screen shows you which of the three each value came from, and if an environment variable is winning, the field goes read-only and says so rather than letting you write something that will have no effect. Editing in the browser writes the file; it never touches your environment.

where is the file
facetmark config path

It is created the first time something writes to it. There is no requirement to have one — a run with no file and no variables is a valid run with all defaults.

Three settings need a restart

embed_backend, embed_dim and local_embed_path decide the shape of the vector store. The screen saves them and then tells you plainly that they take effect next start.

02The four that matter

SettingIn plain language
api_keyYour key. Stored in the file, shown back to you masked, and never re-sent when you save an unrelated field.
base_urlWhere the requests go. Anything speaking the OpenAI API works, including something on your own machine.
chat_modelUsed to read pages and to answer on the Ask screen. Cheap and fast beats clever here.
embed_modelTurns text into vectors. This is the one that decides search quality.
Changing the embedding model means reindexing

Vectors from two different models are not comparable. Change it and run a rebuild, or search gets quietly worse in a way no error message will tell you about.

The Test button on the Settings screen calls chat and embeddings separately and reports them separately, because in practice exactly one of them is usually the broken one — an account with chat access and no embedding access is a very common shape.

03Eight providers, ready to paste

Put these in the file from facetmark config path, or type the same values into the Settings screen. Model names move; if one is rejected, check the provider's current list rather than trusting this page.

OpenAI
api_key = "sk-..."
base_url = "https://api.openai.com/v1"
chat_model = "gpt-4o-mini"
embed_model = "text-embedding-3-small"
embed_dim = 1536
DeepSeek (chat only — pair it with embeddings from elsewhere)
api_key = "sk-..."
base_url = "https://api.deepseek.com/v1"
chat_model = "deepseek-chat"
Moonshot / Kimi
api_key = "sk-..."
base_url = "https://api.moonshot.cn/v1"
chat_model = "moonshot-v1-8k"
Zhipu / GLM
api_key = "..."
base_url = "https://open.bigmodel.cn/api/paas/v4"
chat_model = "glm-4-flash"
embed_model = "embedding-3"
embed_dim = 2048
SiliconFlow
api_key = "sk-..."
base_url = "https://api.siliconflow.cn/v1"
chat_model = "Qwen/Qwen2.5-7B-Instruct"
embed_model = "BAAI/bge-m3"
embed_dim = 1024
Aliyun Bailian (OpenAI-compatible endpoint)
api_key = "sk-..."
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
chat_model = "qwen-plus"
embed_model = "text-embedding-v3"
embed_dim = 1024
Ollama (on your machine, no key)
base_url = "http://127.0.0.1:11434/v1"
api_key = "ollama"
chat_model = "qwen2.5:7b"
embed_model = "bge-m3"
embed_dim = 1024
vLLM (your own server)
base_url = "http://127.0.0.1:8000/v1"
api_key = "not-used"
chat_model = "Qwen/Qwen2.5-7B-Instruct"
Mixing providers is normal

facetmark makes one kind of request for chat and one for embeddings. Plenty of people run chat on whatever is cheapest and embeddings on whatever is best. Set base_url to the embedding provider and give the chat model a fully-qualified name, or run the embeddings locally and leave the API for chat.

04With no API at all

Embeddings can run on your own machine. No key, no network, nothing leaves the laptop:

local embeddings
embed_backend = "local"
local_embed_path = "BAAI/bge-m3"
embed_dim = 1024

It is slower to build and it needs the model downloaded once. Search quality is good: on a 1,024-token window bge-m3 reproduces its own vector to a cosine of 0.999976 run-to-run, which is the property that matters for an index you keep rather than rebuild.

what you give up

Two facets are built on a language model reading your pages: the questions a page could answer, and the topic labels. With no chat model those stay empty and you are searching on body text and full-text — still the two strongest paths, and still better than what your browser gives you.

You can also start here and add a key later. Nothing has to be thrown away; the index fills in the parts it could not build before.

05Everything else

Vectors

SettingIn plain language
embed_backendapi or local. Restart to take effect.
embed_dimHow long each vector is. Must match what the model actually returns. Restart to take effect.
local_embed_pathModel id or folder for the local backend. Restart to take effect.

How hard it pushes

SettingIn plain language
request_timeoutSeconds before a call is given up on. Raise it on a slow link; lower it if a provider hangs.
fetch_concurrencyHow many pages are downloaded at once. Lower it if your network complains.
enrich_concurrencyHow many pages are sent to the model at once. This is the one to lower when you get rate-limited.

What it is not allowed to look at

SettingIn plain language
privacy_excluded_domainsDomains never fetched and never sent anywhere. Bank, health, work intranet. The bookmark stays; only the title is indexed.
chat_model_fallbacksModels to try, in order, when the first one refuses.
Set the exclusions before the first index

Once a page's text is in the database, adding its domain to the list stops future fetches but does not unremember it. Rebuild after changing the list if that matters to you.

06The errors you will actually get

MessageWhat to do
401 / invalid_api_keyKey wrong, or wrong provider's key for this base_url. Test on the Settings screen — it tells you which half failed.
404 on a model nameThat name is not on that endpoint. Check the provider's model list.
429Rate limit. Lower enrich_concurrency and run again; finished stages are not redone.
dim mismatchembed_dim disagrees with the model. Fix it, restart, rebuild.
Chat works, embeddings 403Common. The account has one entitlement and not the other. Switch embed_backend to local, or point embeddings at another provider.
unknown setting on saveA typo in a key name. The writer refuses unknown keys rather than storing something that will be silently ignored forever.