mirror of
https://github.com/moeru-ai/airi.git
synced 2026-10-30 19:34:10 +08:00
## Description A tool call row shows the summary of the call, but every call of a tool without its own icon shows the same magic wand. With this PR, the model picks an icon for each call, from the Tabler icon set. - Every tool gets a second argument `call_icon` after `call_summary`: up to three Tabler icon names for what the call does, best first. The tool never gets it, the same as `call_summary`. - The chat draws the first name that exists. A name that does not exist goes through a search of the icon names, so a close name still finds an icon. Without a match, the row keeps the default icon of the tool. - The picked icon replaces the `--un-icon` mask of the icon element that the row and the pill already have, so size and color stay. There is no extra request, and nothing is stored outside the recorded arguments. - Cards can use any Tabler icon. The card model and the chat model for reply cards get `builtIn_searchIcons`. It searches the words of the icon names, and a variant, for example `bookmark-off`, goes with its icon. The card validator rejects a name that does not exist. - The default tool icons, the skill icons, and the error icon of the pill are Tabler outline icons too, so one row of icons uses one set. - A task beside the chat, for example the card model, gets only its own tools. It does not get the tools of the chat, or the web search that OpenAI Responses adds from the provider settings. Why the model writes names: I compared names with local matching on two test sets. One set has 400 English tool call labels, in four kinds. The other has 104 labels in the 10 languages of AIRI. `gpt-6-sol` judged each icon. The first icon from names fit 89% and 91% of the labels. The best local method fit 74%. That method was a search of the Tabler tags with a trained ranker, or EmbeddingGemma on the label itself. Names cost a few tokens per call. In the 1437 labels, the names held a real icon name 1427 times, so the search that corrects a name runs rarely. ## Changes | Part | Change | | --- | --- | | `stores/ai/chat-llm/tool-call-summary.ts` | Adds `call_icon` after `call_summary`, and removes both before the tool gets its input. `readCallSummary` reads the line and the icon names, each with its own fallback. | | `libs/tabler-icons.ts` | New. Loads the Tabler icons on first use. Draws an icon by name for cards, searches icons by the words of their names, and turns `call_icon` candidates into the `--un-icon` mask of a row. | | `tools/search-icons.ts` | New. `builtIn_searchIcons` for the card model and for reply cards. | | `tool-call-group.vue` | The row and the pill draw the picked icon. The error icon of the pill is Tabler. | | `tools/icons.ts` | The default tool icons and the skill icons are Tabler outline icons. | | `libs/cards/catalog.ts`, `libs/cards/program.ts` | Cards draw Tabler icons, and the validator checks the names against the Tabler set. | | `stores/tool-cards.ts`, `stores/chat.ts` | The card model gets the icon search. A chat that draws reply cards gets it with the card tool. | | `composables/text-generation.ts`, `stores/ai/chat-llm/llm.ts`, `@proj-airi/agents` | `generateText` takes the tools of a task. A request with task tools gets no tools of the chat and no tools of the provider. | | `@proj-airi/core-agent` | New stream option `providerTools`. When it is `false`, OpenAI Responses adds no web search. | | `package.json` files | `@iconify-json/tabler` for stage-ui and for the UnoCSS classes of the desktop, mobile, and Kirie apps. It is the only source of the icons. | ## Verification - The pre-commit hook linted the staged files of each commit. I did not run the typecheck, the tests, or the full lint locally. CI runs them. - `.vishot/capture-tool-call-icons.mjs` (local and ignored) puts one reply with three calls into the stage-web chat. Each call has a summary and icon names: a web search and a weather call that finished, and a page read that failed. It opens the pill and captures at 1440×1600, light, English. The same script ran against `main` (`c8943f47c`) and against this branch. ## Visual changes | Before | After | |---|---| |  |  | | Chat / Tool call panel | Chat / Tool call panel | ## Additional Context - Each tool schema in a request grows by one more property, and each call carries up to three short names. - The icon search matches the words of the icon names only. A search for an idea, for example "save", finds fewer icons than a search of the Tabler tags. The model searches again with a thing that the icon draws, for example "device floppy". The names that a model writes in `call_icon` almost never need the search. - A tool card saved before this PR keeps the MingCute icon names that its model wrote. A name that Tabler does not have draws no icon. Making the card again fixes it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)