The AI Assistant¶
The AI panel answers questions about the open model in plain language: where the largest stress is, which combination governs the reinforcement, what a command does. Show or hide it from View → Assistant panel.
It is optional. Every other part of xdfem2D works without it, and the panel says so when nothing is installed.
Local or remote¶
By default the assistant runs the model on your own machine through Ollama, and nothing about your structure leaves it.
If you have an API key, you can use the far more capable remote models from OpenAI, Google and others instead. They answer more reliably, are usually faster, and are noticeably better at building a model than a model that fits on a laptop. Press Providers… beside the model list, paste the key, and those models appear in the list below a separator, labelled with the provider.
Other providers¶
Most services speak OpenAI's API at a different address, so OpenAI, Google Gemini, Ollama Cloud, DeepSeek, Kimi (Moonshot), GLM (Zhipu), Mistral, Groq and OpenRouter are already listed under Providers…. Paste a key beside one and its models join the list.
Anthropic is not among them: its API is shaped differently enough to need a second client library and a second translation of every tool, which is a lot of machinery — and a lot of megabytes in the packaged application — for one provider out of ten. Claude models are available through OpenRouter.
Ollama somewhere else¶
Two different things, and only one of them sends your model away.
Ollama Cloud runs the models on Ollama's machines. Add an
OLLAMA_API_KEY under Providers… and it behaves like any other remote
service — same warning, same marking in the list. Useful on a laptop that
cannot hold an 8B model.
Your own Ollama on another machine — an office workstation with a GPU, say
— needs nothing here at all. The local provider follows Ollama's own
OLLAMA_HOST variable:
export OLLAMA_HOST=http://workstation.local:11434
The panel then lists that machine's models and nothing leaves your network. For work under a confidentiality obligation this is the arrangement to prefer: the speed of a real GPU with none of the disclosure.
For anything else — a service newer than this release, or a company gateway —
use Add endpoint… in the same dialog. It asks for a name and the base URL
of the OpenAI-compatible API, and writes them to
~/.xdfem2d/llm_endpoints.json:
{
"myprovider": {
"env": "MYPROVIDER_API_KEY",
"base_url": "https://api.example.com/v1",
"models": ["some-model"]
}
}
That file holds addresses, not secrets. An entry there overrides a built-in one, which is how to correct an address that has moved without waiting for a new release.
Where the keys are kept¶
In your system keychain — Keychain Access on macOS, and the equivalent
elsewhere — under the service name xdfem2d. They are encrypted at rest, and
you can inspect or revoke them there without going through this application.
They are never written to the preferences file, which is plain text, and never
into a .x2d, which would carry the key to whoever you send the model to.
An environment variable of the same name still works and is useful for
scripting, but it is second in line: a key stored through the dialog wins. Note
that launching from the Dock or Finder does not inherit shell variables, so a
key set only in ~/.zshrc will appear missing — another reason to prefer the
dialog.
Storing keys needs the keyring package, which ships with xdfem2D. It is not a
vault of its own — on macOS it is a thin layer over the system Keychain, the
same one Safari uses, and the entries appear in Keychain Access under the
service name xdfem2d. The first time a key is saved, macOS asks whether
Python may use the Keychain; answer Always Allow or it will ask again at
every start.
If it is missing from your environment, install it with pip install keyring.
The dialog says so rather than failing quietly, and falls back to environment
variables meanwhile.
No extra install is needed for any of them. The openai client is a normal
dependency of xdfem2D and ships with the packaged application, and every
provider above is reached through it — only the key is missing.
Choosing one sends your model to a third party. The question, and whatever the assistant reads to answer it — geometry, loads, sections, results — goes over the internet. For work under a confidentiality obligation, that is a decision to make deliberately, not one to discover afterwards; the panel asks once per session before the first remote question. Remote models are faster and make none of the mistakes the local ones make, and neither fact changes the first sentence of this paragraph.
What you need¶
The assistant talks to Ollama, which runs the language model on your own machine. Nothing about your model is sent anywhere.
- Install Ollama.
- Start it:
ollama serve - Fetch a model:
ollama pull granite4.1:8b - Press Refresh in the panel.
Neither Ollama nor the model ships with xdfem2D — the model alone is several gigabytes, and most installations never open this panel.
Choosing a model¶
Two things matter, and neither is obvious from the model's name.
It must support tool calling. The assistant never guesses: it asks the
application for the numbers through a set of query tools. A model that cannot
call them will answer anyway, from nothing, and the invented stresses look
exactly like real ones. The panel marks such models — no tool support.
Bigger is not a luxury here. This is the part worth reading twice. A smaller model answers faster and fits in less memory, so it looks like the sensible choice on a modest machine. It is not. In testing, a 3B model asked to locate the most stressed element named the right element, skipped the tool that knows where it is, and reported fabricated coordinates — adding that they were "derived from the element locations within the analysed mesh", which they were not. It did this in two runs out of five. The same model described required reinforcement as "the limit you must not exceed", inverting the meaning while quoting the correct figure.
A small model here does not give worse answers. It gives wrong answers that look exactly like right ones.
Tested models¶
Measured with tools/bench_assistant.py --task=query, 18 benchmark files,
99 questions, temperature 0, September 2026:
| Model | Correct | Median s/answer | Max s/answer | Notes |
|---|---|---|---|---|
qwen3.5:4b (--think=off) |
98/99 | 4.4 | 14.8 | Recommended — fastest of the group, same accuracy as the next two. |
granite4.2:3b |
98/99 | 6.2 | 16.2 | Equally reliable, slower; occasionally confuses bar peaks with shell peaks. |
granite4.1:8b |
96/99 | 11.8 | 23.4 | No longer a candidate — over twice as slow for the same or worse accuracy. |
The app now has three work modes (Auto / Create / Query — see the Task selector
in the panel); --task=create and --task=query benchmark them separately.
qwen3.5:4b is the current default, and the one being tuned first.
Times are from one machine. Without a usable GPU the same model can be several times slower.
These names will age. The benchmark will not: it computes the right answers from the model file itself, so it stays valid as models and files change.
PYTHONPATH=src python3 tools/bench_assistant.py --task=query yourfile.x2d a_model --runs=5
Rank by the stable column — questions answered correctly on every run. A model that is right four times in five is not one to compute with.
A tool call that comes back as text¶
Occasionally a model — even one that generally supports tool calling — writes
what looks like a tool call as plain text instead of actually making one:
something like {"tool_calls": [...]} appears as the answer itself. The
panel cannot act on that; it is not a real call, and the reply is marked
"answered without consulting the model — treat any numbers as unverified".
This is not the application failing to recognise a valid call — it is the underlying runtime (Ollama) failing to recognise the model's output as one, because the model drifted slightly off the exact format its own chat template expects. It tends to happen more with smaller models, and not always on the same question twice.
The Temperature control, beside the Model output combo, is the knob for this. Lower (around 0.2) keeps the model closer to the format it was trained to reproduce exactly — steadier tool use, less varied wording. It does not make the drift impossible, only less likely; a model with no tool support at all will not gain any by turning this down. Default is 0.7.
What it can and cannot do¶
It reads, and it can propose a new model. It cannot edit the currently open model in place, run the analysis, or write files directly.
It is given a short summary of the model up front and asks for detail through the tools, so it sees the geometry, loads, cases, results, nodal-averaged stresses, design results, constraints and this manual. Results are keyed to the analysed mesh — the elements that geometry objects expand into — and the assistant uses those ids, which is why they may not match what you drew.
Creating a model¶
Ask it to build a structure — "a 6 m simply supported beam", "a cantilever wall", "a simply supported slab" — and it answers with a new model, written as JSON. The application checks it and, if it is complete, offers a button to open it; you never have to trust it blind. This is genuinely useful for a quick starting point, but treat it as early and limited:
- it works from small templates — a beam (bars) or a wall (triangles) in the plane domain, or a slab (plate-bending triangles) or grillage (beam grid) in the plate domain — and edits their values; it is not a general modeller;
- the more elaborate the request, the more likely something is missing or wrong, so review what opens before you build on it;
- a capable model matters most here. A small local model often produces a structure that looks right and is not; a stronger remote model (with an API key) is markedly better both at answering and at building.
Open model applies a JSON model that opens cleanly right away — one click, same as before. One that does not open is opened instead in the same JSON editor described below, in place of just naming the problem — what stops one is usually a single wrong word, and the editor's Apply re-checks it after a fix, with no need to leave the application or hand-edit a saved file elsewhere.
It can also write a Python build script (using the xdfem2d API) when you
ask for one instead of a model. A script never runs on its own: Open model
loads it into the Script editor (Ctrl+Shift+E), and nothing happens to
the current model until you press Run yourself. The editor's Save…
button writes the text as it stands to a file (.py or .json),
independently of applying it.
Two habits make it more useful:
- Name the case. "In COMBO1" rather than "in the model": extremes differ between load cases and combinations, and the assistant will otherwise have to pick one and tell you which.
- Check anything that matters. Use Edit → Find Entity to jump to an element it names, and confirm the number in the results table. The assistant is a way to find things quickly, not a substitute for reading the results.
Design values exist only for combinations that were actually designed. Ask about an analysis case and a good model will tell you there is nothing there.