SolonCode ships with no model. That is a deliberate product choice, not an omission: no bundled provider, no default key, no telemetry-backed endpoint you did not ask for. The upside is you can point it at DeepSeek, a local Ollama box, or an air-gapped corporate gateway with equal ease. The downside is that installing the binary does not give you a working assistant. You have to wire a model up first, and that is where most first-time users stall.
This is the follow-up to getting SolonCode installed. Here we get one model connected and stable — meaning it survives long tool-calling sessions, weak networks, and multi-file refactors without mysterious mid-task failures.
You can hand-edit settings.json
. You should not, at least not on day one. The settings page validates your input and gives you a Test connection button, which is the single most useful debugging affordance in the whole product.
soloncode web # default port 4808
soloncode web 0 # pick any free port
soloncode web 1212 # explicit port
Then go to Settings → LLM, add a model, and hit test. Do not skip the test. A green test is the boundary between "config problem" and "everything else."
If the port is taken, soloncode web 0
sidesteps it. That is the official answer too.
This is the part that saves the most time. SolonCode sits on Solon AI, and Solon AI does not have a list of supported vendors. It has a list of supported chat dialects — request/response shapes. Any service that speaks one of those shapes works.
The built-in dialects and how they are detected:
| Dialect | Detected by |
|---|---|
openai |
|
default, or apiUrl ending in .../chat/completions |
|
openai-responses |
|
standard=openai-responses , or apiUrl ending in .../v1/responses |
|
ollama |
|
standard=ollama |
|
gemini |
|
standard=gemini , or apiUrl matching .../v1beta/models/* (usable since v3.8.1) |
|
gemini-interactions |
|
standard=gemini-interactions , or .../v1beta/interactions/* (usable since v4.0.3) |
|
anthropic |
|
standard=anthropic , or apiUrl ending in .../v1/messages (usable since v3.9.1) |
|
dashscope |
|
standard=dashscope , or apiUrl matching .../v1/services/* |
|
ChatConfig
picks the dialect from either standard
or the shape of apiUrl
. So the question is never "does SolonCode support vendor X" — it is "which of these seven shapes does vendor X's endpoint speak."
Two useful notes straight from the docs: Claude also offers an OpenAI-compatible mode, so you can run it through the openai
dialect instead of anthropic
. Alibaba's Bailian exposes both its native DashScope protocol and an OpenAI-compatible one. When a provider gives you both, the OpenAI-compatible endpoint is almost always the faster path to a green test.
apiUrl
Underneath, a model entry maps to Solon AI's ChatConfig
:
| Field | Required | Notes |
|---|---|---|
apiUrl |
||
| yes | the endpoint address | |
apiKey |
||
| depends | token; may be empty for unauthenticated local services | |
model |
||
| yes | the API model id, not the console display name | |
standard |
||
| no | interface spec; defaults to openai |
|
timeout |
||
| no | ||
Duration , defaults to 60s |
||
headers |
||
| no | extra headers — other options are all passed as headers | |
proxy |
||
| no | network proxy | |
contextLength |
||
| no | model context length; the docs flag this one as important | |
defaultAutoToolCall |
||
| no | defaults to true , since v3.8.4 |
There is also provider
, which served the role of standard
before v4.0. If you are reading an old config, that is what it is.
Now the apiUrl
rules, which are where the real failures live:
/chat/completions
or /api/chat
) and the dialect is inferred from the tail.standard
#
#
onward is stripped.One honest wrinkle: the ChatConfig
reference describes apiUrl
as "the full address, not a baseUrl," while the dialect page explicitly supports baseUrl + standard
. Both statements are in the official docs. My practical read: paste the full address. It removes a whole class of ambiguity, and the #
rule exists precisely for the cases where appending would break you.
Field shapes are stable. Hostnames and model ids are not — providers rename models and retire old ones. Always confirm the current id in the vendor console, and never put a real key in a doc, a screenshot, or a commit.
A cloud provider over an OpenAI-compatible endpoint — the common case, and the one to start with:
apiUrl: "https://api.example.com/v1/chat/completions"
apiKey: "sk-xxxxxx"
model: "<copy the exact id from the console>"
timeout: "180s"
Find the "OpenAI compatible" base URL in your provider's console, leave standard
at its default, and copy the model id rather than typing it from memory. The console's display name is frequently not the API id.
A local Ollama instance, from the official example:
ChatModel chatModel = ChatModel.of("http://127.0.0.1:11434/api/chat")
.standard("ollama")
.model("llama3.2")
.build();
The model
value is whatever you pulled — if you ran ollama run deepseek-r1:7b
, you write deepseek-r1:7b
. Note that the dialect table lists only standard=ollama
as the detection rule; the /api/chat
tail-matching behavior appears in the dialect's source, not the table. Declaring standard
explicitly is the safe move.
Also worth setting expectations: a small local model that connects fine may still be unreliable at multi-file refactors and long tool chains. That is a capability ceiling, not a config bug.
A corporate gateway. Fill apiUrl
per the gateway's own docs, put tenant or custom auth headers in headers
, use the model names from the gateway's allow-list rather than the public cloud names, and if the gateway path is unusual, apply the #
suffix. Keep this one in the workspace scope so a client-specific gateway does not leak into every other project on your machine.
Settings persist to settings.json
in one of two places:
| Scope | Path | Good for |
|---|---|---|
| user | ~/.soloncode/settings.json |
|
| models, skill pool, and MCP servers shared across projects | ||
| workspace | .soloncode/settings.json |
|
| models, APIs, LSP, and mounts for this project only |
The merge order is documented plainly: the user-level file is read first, then the workspace file, and workspace config overrides or supplements user config. Saving from the settings page tries to write into the running engine, so a restart is often unnecessary — the docs say "not necessarily," so if your model list does not refresh, restarting the web process is the cheap fix.
Practical split: your personal default model goes global; anything client- or gateway-specific goes workspace.
If .soloncode/settings.json
holds a real key, it does not belong in version control:
.soloncode/settings.json
.soloncode/logs/
Ship a settings.example.json
with placeholder values instead, and let people copy it locally.
One exposure path deserves explicit attention: if you bind the web UI to anything beyond localhost, set webAuthUser and webAuthPass. Leave them blank and authentication is off. An unauthenticated SolonCode web endpoint on your LAN hands over your conversations, your tool permissions, and — since the keys live in the settings file it manages — effectively your API credentials. Sandboxing (
sandboxMode
, sandboxAllowUserHome
, both default true
) and human-in-the-loop review are a separate line of defense. Getting a model connected is not a reason to switch either of them off.A coding agent makes many round trips per task, each carrying context and tool descriptions. So connectivity is necessary but not sufficient. The general settings that govern stability, with their documented defaults:
| Setting | Default | What it controls |
|---|---|---|
apiRetries |
||
| 3 | API retry count | |
modelRetries |
||
| 3 | model retry count | |
mcpRetries |
||
| 3 | MCP retry count | |
maxTurns |
||
| 20 | max rounds in a single task | |
autoRethink |
||
| true | allow self-reflection retries | |
logLevel |
||
| INFO | TRACE / DEBUG / INFO / WARN / ERROR | |
logMaxHistory |
||
| 7 | days of log archives kept |
Retries help with flaky networks and nothing else. A 401 or a wrong model id will fail identically three times and just triple your log volume.
That 60-second timeout
default deserves a second look. For plain chat it is fine. For a reasoning model chewing on a long context behind a slow proxy, it is easy to hit. Raising it is reasonable; raising it to effectively-infinite is not, because an invisible hang is harder to diagnose than a fast failure.
Long tasks accumulate conversation turns and tool results. Compression summarizes older content to relieve context pressure. Two settings and a naming caveat:
compressionThresholdPercent
defaults to 75 — compression triggers when 75% of the context window is used. Both doc pages agree on this one.
The message-count trigger defaults to 100, but appears under two different names in the docs: the settings reference calls it summaryWindowSize
, while the compression guide calls it compressionThresholdMessages
. Similarly, sessionWindowSize
(how many recent messages a new instruction carries) is documented as 8 on one page and 12 on the other. I am not going to guess which is authoritative — check what your settings page actually shows and treat that as truth for your version.
The official tuning ranges, by model context window:
| Context | Message threshold | Percent threshold | Rationale |
|---|---|---|---|
| 128k | 30–50 | 65–70 | balanced, fits most coding tasks |
| 256k | 60–100 | 70–75 | keeps more tool chain, suits complex refactors |
| 1m | 100–200 | 75–80 | compress less; watch cost and latency |
And the symptom-to-knob mapping, which is the genuinely useful part:
| Symptom | Adjustment |
|---|---|
| agent keeps re-reading a file it just read | raise the message threshold |
| model reports context overflow | lower the percent threshold |
| summarization is slow or expensive | raise both thresholds |
| small model flaky on complex tasks | lower sessionWindowSize |
If a task consistently gets cut off mid-way, maxTurns
may be too low. But if the model is spinning in circles, tighten the task description instead. More turns will not fix an underspecified request.
The official checklist is short: verify apiUrl
, apiKey
, the model name, and your network proxy. Expanded into something you can actually run through:
apiUrl
the chat endpoint, not the vendor's homepage? Did you drop the /v1
or /chat/completions
segment?standard=ollama
set, or the full /api/chat
address used?#
suffix.Still stuck? Set logLevel
to DEBUG or TRACE, reproduce the failure once, and read .soloncode/logs/
. Grep for timeout
, 401
, 403
, dialect
, model
. On dialect mismatch specifically, the docs name two causes: a wrong standard
, or a missing dependency package.
There is one more failure mode worth naming, because it looks like a config problem and is not: the connection tests green but the coding output is poor. Check whether you launched from the project root (wrong working directory means no project context), whether the project has an AGENTS.md
describing build and test commands, and whether the session is simply too polluted — a fresh session often fixes it. If none of that helps, you are looking at a model capability limit, and the fix is a different model, not a different config value.
Run these in your project root before trusting the setup with real work:
| Step | Input | Pass condition |
|---|---|---|
| 1 | hello |
|
| clean response, no auth error | ||
| 2 | "list the top-level files in this workspace, change nothing" | output matches ls |
| 3 | same question on a second model, if configured | both answer |
| 4 | temporarily break the key, hit Test connection | it fails — proving the button is honest |
| 5 | restore the key, test again | green |
Step 4 is the one people skip and the one that matters most. A test button you have never seen fail is a test button you cannot trust.
Once all five pass, add a second model and split your traffic: a cheap fast model as the default for reading code and small fixes, a stronger one you switch to deliberately for cross-module refactors and hard bugs. Running every task on your most expensive model is the fastest way to a surprising invoice, and it is rarely the difference between success and failure. Task description quality usually matters more than model tier.
| Topic | Link |
|---|---|
| SolonCode | |
Defaults and field names shift between versions, and the two documentation pages disagree on a couple of them today. When in doubt, the settings page in your installed build is the authority. Model ids and endpoint paths belong to your provider — re-check those in the console before you file a bug against the config.