AI Playground
The AI Playground page (/aiPlayground, lib/Pages/AiPlaygroundPage/) chats with a language model
and shows how fast it answers. It is LAI-F1 and LAI-F2 of
ANTfrastructure's language-AI plan,
built from ANThology's chat widgets
and OxidANT's text bridge.
Where the answers come from
| Source | Platforms | How |
|---|---|---|
| Endpoint | every platform | An OpenAI-compatible server by base URL, model and optional API key. The default is http://127.0.0.1:18200 with smollm2_135m_instruct-cpu, OrchestrANT's orchestrant-serve on ModelANT's SmolLM2 |
| Local model | Linux, Windows | A GenAI model directory, run in the app's process on the chain's ONNX Runtime GenAI with the CPU, CUDA or DirectML provider |
Native builds go through OxidANT's text_generate, which streams each piece as it is decoded. The
web build has no such bridge, so it fetches one whole answer from the endpoint and shows it at once;
there the decode rate stays 0 and the time to the first token is the request's whole time. The local
option is offered only where text_local_available loads the chain GenAI.
Saved settings
Every change is saved at once and restored at the next start: a JSON file,
ai_playground_settings.json, in the app's support directory on native builds, and the browser's
local storage on the web (playground_settings_store.dart). The API key is never written.
PlaygroundSettings.toJson leaves it out, every store writes only that encoding, and the key lives
in memory until the app closes. A file that cannot be read shows its error above the conversation
and the defaults apply.
Seed, JSON schema and stop strings
Under the temperature the form takes a seed (empty draws a fresh answer each time), a JSON schema
the answer must follow (empty for free text) and stop strings, one per line. The web build sends
them as OpenAI's seed, response_format ({"type": "json_schema", "json_schema": {"name": "response", "schema": ..., "strict": true}}) and stop (endpoint_request.dart); a field left
empty is left out of the request. A schema that is not a JSON object fails the request with its
error. Native builds hand all three to OxidANT's text_generate: its endpoint backend sends the
same three fields, and a local model takes the seed as GenAI's random_seed, the schema through
llguidance and the stop strings itself, ending with finish reason stop. A local model without
llguidance, such as Windows' GenAI, answers a schema with json_schema is unsupported on this lane,
shown above the conversation.
Using it
Each message is sent with the system prompt and the whole conversation before it. While an answer runs, the button stops it after its current token, and the answer is marked as stopped. Under each finished answer the page shows the token count, the time to the first token and the decode rate. A failure, such as an unreachable endpoint or a missing model, appears above the conversation. "New conversation" clears it.
Tests
test/ai_playground_test.dart drives the controller and the view with a fake backend: streaming,
the figures, the conversation history, waiting and stopping, and a failure. It also checks that the
bridge client hands the settings to a mocked Rust core.
test/ai_playground_settings_test.dart saves settings with a planted API key, reads every byte
under the store's directory and finds no trace of it, then restores the rest in a second controller,
as a restart does. It also covers a broken file, a store that cannot write, the default store through
the mocked support directory, the request body and the three new fields.
integration_test/text_generate_test.dart runs on the Linux lanes in the built app, under Xvfb,
against the chain GenAI through OxidANT's bridge. The lane writes the hub's tiny GenAI model
(tiny-genai-model.py) and fetches SmolLM2-135M through the hub's fetch-data-lock.py from
third_party/OxidANT/resources/models/llm.lock, checked by sha256, then passes both directories as
--dart-defines. Greedy decoding of abc must give exactly defg, and SmolLM2-135M's answer to
"What is the capital of France?" must contain Paris. A third test points the playground at a
recording loopback server and checks that the request body carries seed, response_format and
stop. An unset model directory fails the test rather than skipping it.