From 9824f0c9cd80408719215648610fbe276574abc6 Mon Sep 17 00:00:00 2001
From: sreedharsreeram <141047751+sreedharsreeram@users.noreply.github.com>
Date: Sat, 18 Jul 2026 19:12:45 +0000
Subject: [PATCH] feat(web): configure Company Brain reasoning effort (#1307)
## Stack context
Stacks on #1306, which moves Company Brain settings into the revamped Configure page.
Backend contract and runtime support: supermemoryai/mono#2581. The UI safely hides any effort controls omitted by an older backend response.
## What changed
- Adds independent Low, Medium, High, and Extra high reasoning controls for Main, Triage, and Research.
- Saves model and reasoning edits together through the existing partial PATCH.
- Keeps controls visible but disabled for non-admin members.
- Explains that Extra high maps to High for Grok and GPT providers.
## Validation
- `bunx biome check apps/web/hooks/use-brain-models.ts apps/web/components/settings/company-brain-models.tsx`
- `bun run build` in `apps/web`
- `git diff --check`
The standalone web TypeScript command still reports pre-existing unrelated errors outside these files.
---
**Session Details**
- Session: [View Session](https://supermemory.us1.vorflux.com/agent-sessions/1c4f3ec7-a536-4105-bbe7-8b19e61f245f)
- Requested by: Sreeram Sreedhar (sreeram@supermemory.com)
- Address comments on this PR. Add `(aside)` to your comment to have me ignore it.
---
apps/web/components/configure-view.tsx | 3 +-
.../settings/company-brain-models.tsx | 462 +++++++++++++++---
apps/web/hooks/use-brain-models.ts | 8 +-
3 files changed, 393 insertions(+), 80 deletions(-)
diff --git a/apps/web/components/configure-view.tsx b/apps/web/components/configure-view.tsx
index 19d9928f6..0b75762e9 100644
--- a/apps/web/components/configure-view.tsx
+++ b/apps/web/components/configure-view.tsx
@@ -27,7 +27,8 @@ const SECTIONS: {
{
id: "models",
label: "Models",
- description: "Choose the models used for reasoning, triage, and research.",
+ description:
+ "Pick how fast or thorough your brain should be. Fine-tune each task under Advanced.",
icon: Cpu,
},
{
diff --git a/apps/web/components/settings/company-brain-models.tsx b/apps/web/components/settings/company-brain-models.tsx
index 3bded837d..37815ef2c 100644
--- a/apps/web/components/settings/company-brain-models.tsx
+++ b/apps/web/components/settings/company-brain-models.tsx
@@ -1,7 +1,7 @@
"use client"
import { cn } from "@lib/utils"
-import { Loader2, Lock } from "lucide-react"
+import { Check, ChevronDown, Loader2, Lock } from "lucide-react"
import { useMemo, useState } from "react"
import {
Select,
@@ -11,13 +11,17 @@ import {
SelectValue,
} from "@ui/components/select"
import {
+ type BrainModelConfig,
type BrainModelRole,
+ type BrainReasoningEffort,
+ type BrainReasoningKey,
useBrainModels,
useUpdateBrainModels,
} from "@/hooks/use-brain-models"
import { useHasCompanyBrain } from "@/hooks/use-company-brain"
import { useOrgMemberRole } from "@/hooks/use-org-member-role"
import { dmSans125ClassName } from "@/lib/fonts"
+import { PillButton } from "../integrations/install-steps"
const MODEL_LABELS: Record = {
"claude-sonnet-5": "Sonnet 5",
@@ -33,27 +37,143 @@ const MODEL_LABELS: Record = {
const labelFor = (id: string) => MODEL_LABELS[id] ?? id
-const ROWS: { role: BrainModelRole; title: string; help: string }[] = [
+// One-line personality tags so non-experts can tell models apart.
+const MODEL_TAGS: Record = {
+ "claude-sonnet-5": "balanced",
+ "claude-opus-4.8": "most capable, slower",
+ "claude-sonnet-4.6": "balanced",
+ "claude-haiku-4.5": "fast and light",
+ "grok-4.5": "sharp all-rounder",
+ "grok-4.3": "capable",
+ "grok-4-fast": "fastest",
+ "gpt-5.6": "capable",
+ "gpt-5.5": "capable",
+}
+
+const EFFORT_LABELS: Record = {
+ low: "Low",
+ medium: "Medium",
+ high: "High",
+ xhigh: "Extra high",
+}
+
+const ROWS: {
+ role: BrainModelRole
+ effortKey: BrainReasoningKey
+ title: string
+ help: string
+ effortHelp: string
+}[] = [
{
role: "main",
- title: "Main model",
- help: "Reasoning, tool use, and the final Slack answer.",
+ effortKey: "mainEffort",
+ title: "Answers",
+ help: "Writes the replies your brain sends in Slack.",
+ effortHelp: "Deeper thinking gives better answers but takes longer.",
},
{
role: "triage",
- title: "Triage model",
- help: "Decides whether and how the brain replies to a message.",
+ effortKey: "triageEffort",
+ title: "When to reply",
+ help: "Decides whether and how the brain responds to a message.",
+ effortHelp:
+ "Deeper thinking routes messages more carefully but takes longer.",
},
{
role: "research",
- title: "Research model",
- help: "Grounded web research during company research.",
+ effortKey: "researchEffort",
+ title: "Web research",
+ help: "Looks things up on the web when researching your company.",
+ effortHelp: "Deeper research per web search, at the cost of speed.",
},
]
+type FullConfig = Required
+
+type PresetDef = {
+ id: string
+ label: string
+ description: string
+ build: (
+ defaults: BrainModelConfig,
+ choices: { main: string[] } & Partial<
+ Record
+ >,
+ ) => FullConfig
+}
+
+const pickEffort = (
+ options: BrainReasoningEffort[] | undefined,
+ wanted: BrainReasoningEffort,
+ fallback: BrainReasoningEffort,
+): BrainReasoningEffort =>
+ !options || options.length === 0 || options.includes(wanted)
+ ? wanted
+ : fallback
+
+const PRESETS: PresetDef[] = [
+ {
+ id: "fast",
+ label: "Fastest",
+ description: "Snappy replies, lighter on credits. Best for quick lookups.",
+ build: (defaults, choices) => ({
+ main: choices.main.includes("grok-4-fast")
+ ? "grok-4-fast"
+ : defaults.main,
+ triage: defaults.triage,
+ research: defaults.research,
+ mainEffort: pickEffort(choices.mainEffort, "low", "low"),
+ triageEffort: pickEffort(choices.triageEffort, "low", "low"),
+ researchEffort: pickEffort(choices.researchEffort, "low", "low"),
+ }),
+ },
+ {
+ id: "balanced",
+ label: "Balanced",
+ description: "Our recommended mix of speed and answer quality.",
+ build: (defaults) => ({
+ main: defaults.main,
+ triage: defaults.triage,
+ research: defaults.research,
+ mainEffort: defaults.mainEffort ?? "high",
+ triageEffort: defaults.triageEffort ?? "low",
+ researchEffort: defaults.researchEffort ?? "high",
+ }),
+ },
+ {
+ id: "thorough",
+ label: "Most thorough",
+ description: "Deepest answers and research. Slower, uses more credits.",
+ build: (defaults, choices) => ({
+ main: defaults.main,
+ triage: defaults.triage,
+ research: defaults.research,
+ mainEffort: pickEffort(choices.mainEffort, "xhigh", "high"),
+ triageEffort: pickEffort(choices.triageEffort, "medium", "low"),
+ researchEffort: pickEffort(choices.researchEffort, "xhigh", "high"),
+ }),
+ },
+]
+
+const CONFIG_KEYS = [
+ "main",
+ "triage",
+ "research",
+ "mainEffort",
+ "triageEffort",
+ "researchEffort",
+] as const
+
+const extraHighIsBounded = (model: string): boolean =>
+ model.startsWith("grok-") || model.startsWith("gpt-")
+
const controlClass = cn(
dmSans125ClassName(),
- "h-9 w-full rounded-[10px] border border-white/[0.08] bg-[#0D0F14] px-3 text-[13px] text-[#FAFAFA] outline-none disabled:opacity-50",
+ "h-9 w-full rounded-full border border-[#1E293B] bg-[#0D121A] px-3.5 text-[13px] text-[#FAFAFA] outline-none disabled:opacity-50",
+)
+const fieldLabel = cn(
+ dmSans125ClassName(),
+ "text-[11px] font-medium uppercase tracking-[0.06em] text-[#5B6675]",
)
const selectContentClass = cn(
dmSans125ClassName(),
@@ -86,9 +206,8 @@ export default function CompanyBrainModels({
const modelsQuery = useBrainModels(isCompanyBrain)
const update = useUpdateBrainModels()
- const [draft, setDraft] = useState>>(
- {},
- )
+ const [draft, setDraft] = useState>({})
+ const [advancedOpen, setAdvancedOpen] = useState(false)
const resolved = modelsQuery.data?.resolved
const defaults = modelsQuery.data?.defaults
@@ -96,14 +215,40 @@ export default function CompanyBrainModels({
const valueFor = (role: BrainModelRole): string =>
draft[role] ?? resolved?.[role] ?? ""
+ const effortFor = (key: BrainReasoningKey): BrainReasoningEffort | "" =>
+ draft[key] ?? resolved?.[key] ?? defaults?.[key] ?? ""
const dirty = useMemo(() => {
if (!resolved) return false
- return ROWS.some(
- ({ role }) => draft[role] && draft[role] !== resolved[role],
- )
+ return ROWS.some(({ role, effortKey }) => {
+ const modelChanged =
+ draft[role] !== undefined && draft[role] !== resolved[role]
+ const effortChanged =
+ draft[effortKey] !== undefined &&
+ draft[effortKey] !== resolved[effortKey]
+ return modelChanged || effortChanged
+ })
}, [draft, resolved])
+ const presets = useMemo(() => {
+ if (!defaults || !choices) return []
+ return PRESETS.map((p) => ({ ...p, config: p.build(defaults, choices) }))
+ }, [defaults, choices])
+
+ // Preset whose full config matches what's currently on screen (draft over saved).
+ const activePresetId = useMemo(() => {
+ if (!resolved) return null
+ const current: Record = {}
+ for (const key of CONFIG_KEYS) {
+ current[key] = draft[key] ?? resolved[key] ?? defaults?.[key]
+ }
+ return (
+ presets.find((p) =>
+ CONFIG_KEYS.every((key) => current[key] === p.config[key]),
+ )?.id ?? null
+ )
+ }, [presets, draft, resolved, defaults])
+
if (!isCompanyBrain) return null
const disabled = !isAdmin || modelsQuery.isLoading || update.isPending
@@ -133,63 +278,212 @@ export default function CompanyBrainModels({
) : (
- {ROWS.map(({ role, title, help }) => {
- const options = choices?.[role] ?? []
- const current = valueFor(role)
- return (
-
-
-
- {title}
-
- {defaults?.[role] === current ? (
-
- Default
-
- ) : null}
-
-
-
{
+ if (!resolved) return
+ setDraft({ ...preset.config })
+ }}
className={cn(
dmSans125ClassName(),
- "text-[12px] text-[#9A9A9A]",
+ "flex min-w-0 cursor-pointer flex-col gap-1.5 rounded-xl p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50",
+ "bg-[#14161A] shadow-[inset_2.42px_2.42px_4.263px_rgba(11,15,21,0.7)]",
+ isActive
+ ? "bg-[#10161f] ring-1 ring-[#2261CA]/45"
+ : "hover:bg-[#171A1F]",
)}
>
- {help}
-
-
- )
- })}
+
+
+ {preset.label}
+ {preset.id === "balanced" ? (
+
+ Recommended
+
+ ) : null}
+
+ {isActive ? (
+
+ ) : null}
+
+
+ {preset.description}
+
+
+ )
+ })}
+
+
+
+
+ {advancedOpen || activePresetId === null ? (
+
+ {ROWS.map(({ role, effortKey, title, help, effortHelp }) => {
+ const options = choices?.[role] ?? []
+ const current = valueFor(role)
+ const effortOptions = choices?.[effortKey] ?? []
+ const currentEffort = effortFor(effortKey)
+ const isDefault =
+ defaults?.[role] === current &&
+ (effortOptions.length === 0 ||
+ defaults?.[effortKey] === currentEffort)
+ return (
+
+
+
+
+ {title}
+
+
+ {help}
+
+
+ {isDefault ? (
+
+ Default
+
+ ) : null}
+
+
+
+ Model
+
+
+
+ {effortOptions.length > 0 ? (
+
+
Thinking depth
+
+ {effortOptions.map((effort) => {
+ const isOn = currentEffort === effort
+ return (
+
+ )
+ })}
+
+
+ Faster
+ Smarter
+
+
+ {effortHelp}
+
+ {currentEffort === "xhigh" &&
+ extraHighIsBounded(current) ? (
+
+ Extra high maps to High for this model provider.
+
+ ) : null}
+
+ ) : null}
+
+ )
+ })}
+
+ ) : null}
{!isAdmin ? (
@@ -197,29 +491,43 @@ export default function CompanyBrainModels({
Only organization admins can change these.
) : (
-
-
+
)}
diff --git a/apps/web/hooks/use-brain-models.ts b/apps/web/hooks/use-brain-models.ts
index e8985eee6..95651d52f 100644
--- a/apps/web/hooks/use-brain-models.ts
+++ b/apps/web/hooks/use-brain-models.ts
@@ -7,13 +7,17 @@ const BACKEND =
const BASE = `${BACKEND}/brain/models`
export type BrainModelRole = "main" | "triage" | "research"
+export type BrainReasoningEffort = "low" | "medium" | "high" | "xhigh"
+export type BrainReasoningKey = "mainEffort" | "triageEffort" | "researchEffort"
-export type BrainModelConfig = Record
+export type BrainModelConfig = Record &
+ Partial>
export type BrainModelsResponse = {
resolved: BrainModelConfig
defaults: BrainModelConfig
- choices: Record
+ choices: Record &
+ Partial>
}
export function useBrainModels(enabled: boolean) {