Skip to content
KONIGI Patterns

AI Assistants / Starting a request / Mode switch

‹ 5 of 7 ›

Mode switch

People need to choose between a quick answer and more time spent on research or analysis.

Updated September 12, 2026

Problem

Some questions need a quick answer; others need research. People need to understand which behavior they are choosing and how long it may take.

Solution

Offer a small set of clearly named modes, such as searching the web or doing deeper research. Describe what each does and show meaningful differences in time or usage cost before people start.

Keep the mode distinct from the model choice. If the system chooses a mode automatically, show the choice and allow an override where supported.

Make the active mode visible near the message field. Say whether it applies to one request or stays active for the conversation, so a costly mode is not left on unnoticed.

Use when

The assistant offers different ways of working with meaningful differences in time, tools, or cost.

Don’t use when

The difference between modes is a few per cent of quality. A toggle implies a real fork, and offering one where none exists teaches people to fiddle with settings instead of writing better questions.

Trade-offs

Mode choices add a decision before sending a request. Automatic selection reduces that work but can choose poorly. Remembering a mode saves repeated setup and can also lead to unexpected usage.

Checklist

  • Is the mode control visually distinct from the model control?
  • Are the options verbs rather than nouns?
  • Is the time cost of the slowest mode stated before it runs?
  • Does the viewer learn what a mode costs against their quota?
  • Does the mode persist, and for how long, and is that visible?
  • If routing is automatic, is the escalation disclosed on the turn?
  • Can the viewer force the fast path?
  • What happens to a mode when the model is changed?
  • Is the active mode legible without opening a menu?
  • Can a long research mode be interrupted, and what survives?

Compare

ChatGPT separates search and extended reasoning as explicit toggles beside the composer, so effort is chosen independently of the model. Claude binds extended thinking to a budget and streams a summary of the work, so the slow mode is legible while it runs instead of only at the end. Perplexity builds the whole product around the mode, with focus and source-set controls that change where it searches rather than how hard it thinks. Gemini leans further toward routing the choice automatically. That suits a consumer audience, and it turns escalation into something to disclose rather than something to select.

Model picker chooses the model rather than its working mode. Reasoning disclosure summarizes a longer analysis. Source list shows material found through search.

Usage meter tracks the cost of these choices. Composer holds the mode control.

Mode switch anatomy A row of modes expressed as verbs beside the composer, each carrying its time and quota cost, kept visually separate from the model control, with an automatic escalation disclosed on the turn and a way to force the fast path. Verbs, not nouns, and separate from the model answer now 2s search first 15s · 1 credit think longer 90s · 4 credits research 9 min · 40 credits model Careful a ceiling, not an amount of effort escalated to think longer for this question answer fast instead a mode left on quietly spends a week of quota 1 2 3 4 1 MODES ARE VERBS A menu mixing nouns and verbs leaves nobody able to predict what any row does. 2 TIME AND COST UP FRONT Nine minutes and forty credits is a different decision than a toggle. 3 KEEP IT OFF THE MODEL A model is a capability ceiling. A mode is an amount of effort. Two controls. 4 DISCLOSE THE ESCALATION Most people never touch these, so routing decides the experience. Say what it chose. Each mode adds state to blame when an answer is bad, with no way to tell which was at fault.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

Modes written as verbs and priced in time and quota, kept visually apart from the model control. The model is a ceiling; the mode is how much effort gets spent under it, and one left on quietly spends a week of quota.

shadcn
npx shadcn@latest add toggle-group button
Tokens
--card--card-foreground--muted--muted-foreground--accent--accent-foreground--border--chart-1
model
escalated to think longer for this question

ModeSwitch.tsxA closed set of verbs, each priced from its seconds and credits, the model on its own row, and the escalation disclosed on the turn.

import { Button } from "@/components/ui/button";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

/**
 * A closed set, each one a verb. A mode is an amount of effort, and a menu
 * that mixes these with model names leaves nobody able to predict a row.
 * Time and credits are on every entry because the viewer is choosing whether
 * to wait and what to spend, and a toggle with no price understates both.
 */
export const MODES = {
  answer:   { verb: "answer now",   seconds: 2,   credits: 0 },
  search:   { verb: "search first", seconds: 15,  credits: 1 },
  think:    { verb: "think longer", seconds: 90,  credits: 4 },
  research: { verb: "research",     seconds: 540, credits: 40 },
} as const;
export type Mode = keyof typeof MODES;

const time = (s: number) => (s >= 120 ? `${Math.round(s / 60)} min` : `${s}s`);
const credits = (n: number) => (n === 0 ? "" : n === 1 ? " · 1 credit" : ` · ${n} credits`);
/** "9 min · 40 credits", computed rather than typed, so it cannot drift. */
export const price = (m: Mode) => `${time(MODES[m].seconds)}${credits(MODES[m].credits)}`;

type Props = {
  value: Mode;
  onChange: (mode: Mode) => void;
  /** The model is a separate control on purpose. It is a capability ceiling,
   *  and it sits on its own row so it cannot be mistaken for an effort level. */
  model: string;
  onPickModel: () => void;
  /** Where routing escalated the current turn past the chosen mode. Most
   *  people never touch the row above, so this is the part they see. */
  escalatedTo?: Mode;
  onForceFast: () => void;
};

export function ModeSwitch({ value, onChange, model, onPickModel, escalatedTo, onForceFast }: Props) {
  return (
    <div className="rounded-lg border bg-card p-4 text-card-foreground">
      <ToggleGroup
        type="single"
        value={value}
        onValueChange={(v) => v && onChange(v as Mode)}
        aria-label="How much effort to spend"
        className="grid grid-cols-2 gap-3 sm:grid-cols-4"
      >
        {(Object.keys(MODES) as Mode[]).map((m) => (
          <ToggleGroupItem
            key={m}
            value={m}
            className="h-auto flex-col items-start gap-1 rounded-md border px-3 py-2 data-[state=on]:border-chart-1 data-[state=on]:bg-chart-1/10 data-[state=on]:text-chart-1"
          >
            <span className="text-xs">{MODES[m].verb}</span>
            <span className="text-xs font-normal tabular-nums text-muted-foreground">{price(m)}</span>
          </ToggleGroupItem>
        ))}
      </ToggleGroup>

      <div className="mt-4 flex items-center gap-3 border-t border-dashed pt-3 text-xs">
        <span className="text-muted-foreground">model</span>
        <Button variant="outline" size="sm" className="h-6 px-2.5 text-xs" onClick={onPickModel}>{model}</Button>
      </div>

      {escalatedTo && escalatedTo !== value && (
        <div className="mt-4 flex items-center gap-3 rounded-lg border bg-muted px-3 py-2 text-xs">
          <span>escalated to {MODES[escalatedTo].verb} for this question</span>
          <button type="button" onClick={onForceFast} className="ml-auto text-muted-foreground underline underline-offset-2">
            answer fast instead
          </button>
        </div>
      )}
    </div>
  );
}

demo.tsxHow it is called: answer now chosen, routing escalated this question to think longer, fast path one click away.

import { useState } from "react";
import { ModeSwitch, type Mode } from "./ModeSwitch";

/**
 * Answer now is the chosen mode, and routing escalated this one question to
 * think longer. Forcing the fast path clears the escalation; picking a mode
 * changes what every turn from here does.
 */
export default function Demo() {
  const [mode, setMode] = useState<Mode>("answer");
  const [escalatedTo, setEscalatedTo] = useState<Mode | undefined>("think");

  return (
    <ModeSwitch
      value={mode}
      onChange={setMode}
      model="Careful"
      onPickModel={() => {}}
      escalatedTo={escalatedTo}
      onForceFast={() => setEscalatedTo(undefined)}
    />
  );
}
What it renders. Identical markup in both panes, with only the token values changing.

Examples

No screenshots yet for this pattern. Browse Products to see other examples.