Build an in-app model picker from ChatOSS's credential-free model list. Never ask for or handle API keys. Capability: chatApi (declared, never prompts).
List models
const models = await window.chatoss.chat.listModels();
// [{ id, name, source: 'local'|'cloud'|'custom', capabilities,
// contextLength, available, unavailableReason? }]
const defaultModel = await window.chatoss.chat.getDefaultModel();
Show models in your own <select>; disable rows where available is false. Save the chosen opaque id in your app/project state, then pass it explicitly.
listModels() exposes model ids and display metadata only — never credentials, tokens, or account details.
Run a turn
const result = await window.chatoss.chat.runTurn({
model: selectedModelId || defaultModel,
messages: [ // REQUIRED. Roles: system | user | assistant
{ role: 'system', content: 'You are concise.' },
{ role: 'user', content: 'Hello' }
],
onToken: (t) => { out.textContent += t; }, // streamed reply chunks — use for live UI
onThinking: (t) => {}, // streamed reasoning, when the model exposes it
tools: TOOLS, // optional function-calling (see below)
onToolCall: async (call) => '…', // executes your tools; return a STRING result
think: true, // optional: ask the model to reason first
signal: abortController.signal // optional: abort() stops the turn
});
// result = { content, thinking, toolCalls, usage?, aborted }
- If
modelis omitted, the app-wide ChatOSS default answers. - Multi-turn memory = keep your own
messagesarray and send all of it each turn. - Your app's chats see ONLY the tools you pass — never the user's other tools.
Function calling
Describe tools with JSON schema; the engine loops automatically (model calls tool → your onToolCall returns a string → model continues). There is no round limit — the loop runs until the model stops calling tools, so a long orchestration is not truncated part-way. It ends early only if you abort the turn (signal), which sets aborted on the result.
const TOOLS = [{
type: 'function',
function: {
name: 'add_item',
description: 'Add one item to the list.',
parameters: {
type: 'object',
properties: { text: { 'type': 'string' } },
required: ['text']
}
}
}];
const result = await window.chatoss.chat.runTurn({
messages: [
{ role: 'system', content: 'Manage the list with tools. Current list:\n' + serialize() },
{ role: 'user', content: userAsk }
],
tools: TOOLS,
onToolCall: async (call) => {
const args = call.function.arguments; // ALREADY PARSED to an object
if (call.function.name === 'add_item') { addItem(args.text); return 'Added ' + args.text; }
return 'Error: unknown tool';
}
});
Include the app's current state in the system message — the model can only act on what it sees.
For driving a full coding-agent loop (multi-round tool use over a project with modes and verification), call the Agent Engine instead — it's the battle-tested engine the built-in Code app uses.