Providers
AI providers and the chat model
The backend has no model access by default. Each [[providers]] entry defines a named API endpoint. base-url is the API root; the embedding client appends /embeddings and the judge client /systemone. api-key-env names the environment variable holding the bearer token, so all keys come from the backend's environment (for example through services.pointy-backend.agentEnvFile). timeout-seconds (default 10) bounds the embedding and judge HTTP calls.
[models.chat] selects the model used by the agent panel:
providermust name an entry in[[providers]], or startup fails withFailed to parse config: [models.chat] names provider "…", which is not in [providers].protocolis one ofopenai-completions,openai-responses,anthropic-messages, orgoogle-generative-ai.modelis the provider's model identifier.thinkingis optional and becomes the level suffix of--model <provider>/<model>:<level>.[models.chat.pi]supplies Pi metadata:name,contextWindow,maxTokens,input,reasoning,thinkingLevelMap,disabledThinkingLevels,cost(all four prices required), andcompatflags such assupportsStore,supportsDeveloperRole,supportsReasoningEffort, andrequiresReasoningContentOnAssistantMessages.
The backend rewrites Pi's models.json and settings.json from these tables before a turn, and passes --model itself. Without [models.chat], the agent starts with no chat model. A provider referenced by [models.chat], [search.embedding], or [search.judge] whose key is not set at startup produces Warning: … reads its key from …, which is not set; turns or searches that use it then fail.
Model selection and chat titles
The chat model comes from [models.chat], not from runner-args. The backend writes Pi's models.json and settings.json from [[providers]] and [models.chat] before a turn and passes --model <provider>/<model>[:<thinking>]. A --model, --thinking, or --provider in runner-args is discarded. Without [models.chat] the agent has no chat model.
title-prompt controls automatic naming of unnamed chats. A blank value disables it. Title generation is a separate short Pi request with --no-session --no-tools and a 60-second timeout; it accepts at most 60 characters and 10 words from the last non-empty output line, and does not overwrite a user-chosen name.
Step search
Step search is lexical by default. Adding [search.embedding] enables the Related section in the search box; [search.judge] then filters the embedding candidates.
[search]:limit(default10) is the number of results;judge-candidates(default24, at most64) is how many similarity hits reach the judge.[search.embedding]:providernames a[[providers]]entry;protocolmust beopenai-embeddings;modelis the embedding model.dimensionsis optional and sent to the provider.query-template(default{query}) anddocument-template(default{document}) wrap the query and step text.batch-size(default128) sets embedding request size, andmin-similarity(default0.4) is the cosine cutoff.[search.judge]:providernames a[[providers]]entry;protocolmust besystemone;modelis the judge model.instructionsis the per-step question and may use{id}and{query}.min-relevance(default0.5) is the cutoff.
A provider named by any of these sections must exist in [[providers]], and judge-candidates above 64 fails startup. Embeddings are cached under /home/backend/.local/state/pointy/embeddings/, keyed by model, dimensions, and document template; the in-memory index is per repository commit. The cache is safe to delete; it is rebuilt by calling the embedding provider.
