From 0ce5ceedd593b27a469d6e9cc7817520d658ee0f Mon Sep 17 00:00:00 2001 From: murashit Date: Wed, 8 Jul 2026 11:48:35 +0900 Subject: [PATCH] Show reasoning effort descriptions in composer suggestions --- src/app-server/protocol/catalog.ts | 6 +++++- src/app-server/query/snapshots.ts | 1 + src/domain/catalog/metadata.ts | 14 ++++++++++++++ .../chat/application/composer/suggestions.ts | 9 +++++++-- tests/app-server/catalog.test.ts | 4 ++++ .../chat/application/composer/suggestions.test.ts | 13 +++++++++++-- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/app-server/protocol/catalog.ts b/src/app-server/protocol/catalog.ts index 48570e49..27e1543a 100644 --- a/src/app-server/protocol/catalog.ts +++ b/src/app-server/protocol/catalog.ts @@ -6,7 +6,7 @@ export interface CatalogModel { displayName: string; description: string; hidden: boolean; - supportedReasoningEfforts: readonly { reasoningEffort: string }[]; + supportedReasoningEfforts: readonly { reasoningEffort: string; description: string }[]; defaultReasoningEffort: string | null; inputModalities: readonly string[]; additionalSpeedTiers: readonly string[]; @@ -56,6 +56,10 @@ function modelMetadataFromCatalogModel(model: CatalogModel): ModelMetadata { description: model.description, hidden: model.hidden, supportedReasoningEfforts: model.supportedReasoningEfforts.map((option) => option.reasoningEffort), + reasoningEffortOptions: model.supportedReasoningEfforts.map((option) => ({ + reasoningEffort: option.reasoningEffort, + description: option.description, + })), defaultReasoningEffort: model.defaultReasoningEffort, inputModalities: [...model.inputModalities], additionalSpeedTiers: [...model.additionalSpeedTiers], diff --git a/src/app-server/query/snapshots.ts b/src/app-server/query/snapshots.ts index 4890434d..738950c1 100644 --- a/src/app-server/query/snapshots.ts +++ b/src/app-server/query/snapshots.ts @@ -13,6 +13,7 @@ export function cloneModelMetadata(models: readonly ModelMetadata[]): ModelMetad return models.map((model) => ({ ...model, supportedReasoningEfforts: [...model.supportedReasoningEfforts], + ...(model.reasoningEffortOptions ? { reasoningEffortOptions: model.reasoningEffortOptions.map((option) => ({ ...option })) } : {}), inputModalities: [...model.inputModalities], additionalSpeedTiers: [...model.additionalSpeedTiers], serviceTiers: model.serviceTiers.map((tier) => ({ ...tier })), diff --git a/src/domain/catalog/metadata.ts b/src/domain/catalog/metadata.ts index 00fb71e6..ff8c5915 100644 --- a/src/domain/catalog/metadata.ts +++ b/src/domain/catalog/metadata.ts @@ -3,6 +3,11 @@ interface ModelServiceTier { readonly name: string; } +interface ReasoningEffortMetadata { + readonly reasoningEffort: string; + readonly description: string; +} + export interface ModelMetadata { readonly id: string; readonly model: string; @@ -10,6 +15,7 @@ export interface ModelMetadata { readonly description: string; readonly hidden: boolean; readonly supportedReasoningEfforts: readonly string[]; + readonly reasoningEffortOptions?: readonly ReasoningEffortMetadata[]; readonly defaultReasoningEffort: string | null; readonly inputModalities: readonly string[]; readonly additionalSpeedTiers: readonly string[]; @@ -54,6 +60,14 @@ export function supportedEffortsForModelMetadata(model: ModelMetadata | null): R ); } +export function reasoningEffortDescriptionForModelMetadata(model: ModelMetadata | null, effort: ReasoningEffort): string | null { + const normalizedEffort = normalizeReasoningEffort(effort); + if (!normalizedEffort) return null; + const option = model?.reasoningEffortOptions?.find((item) => normalizeReasoningEffort(item.reasoningEffort) === normalizedEffort); + const description = option?.description.trim(); + return description ? description : null; +} + export function sortedModelMetadata(models: readonly ModelMetadata[]): ModelMetadata[] { return [...models] .filter((model) => !model.hidden) diff --git a/src/features/chat/application/composer/suggestions.ts b/src/features/chat/application/composer/suggestions.ts index ae8bd385..e5b3f9c1 100644 --- a/src/features/chat/application/composer/suggestions.ts +++ b/src/features/chat/application/composer/suggestions.ts @@ -1,6 +1,11 @@ import { prepareFuzzySearch, type SearchResult, sortSearchResults } from "obsidian"; import type { ModelMetadata, SkillMetadata } from "../../../../domain/catalog/metadata"; -import { findModelMetadataByIdOrName, sortedModelMetadata, supportedEffortsForModelMetadata } from "../../../../domain/catalog/metadata"; +import { + findModelMetadataByIdOrName, + reasoningEffortDescriptionForModelMetadata, + sortedModelMetadata, + supportedEffortsForModelMetadata, +} from "../../../../domain/catalog/metadata"; import type { RuntimePermissionProfileSummary } from "../../../../domain/runtime/permissions"; import { shortThreadId } from "../../../../domain/threads/id"; import type { Thread } from "../../../../domain/threads/model"; @@ -533,7 +538,7 @@ function reasoningEffortOverrideSuggestions( }, ...efforts.map((effort) => ({ display: effort, - detail: modelDetail, + detail: reasoningEffortDescriptionForModelMetadata(model, effort) ?? modelDetail, replacement: effort, start, appendSpaceOnInsert: true, diff --git a/tests/app-server/catalog.test.ts b/tests/app-server/catalog.test.ts index 76a5b8e8..98e4b11c 100644 --- a/tests/app-server/catalog.test.ts +++ b/tests/app-server/catalog.test.ts @@ -21,6 +21,10 @@ describe("app-server catalog mappers", () => { description: "Primary model", hidden: false, supportedReasoningEfforts: ["low", "high"], + reasoningEffortOptions: [ + { reasoningEffort: "low", description: "Low" }, + { reasoningEffort: "high", description: "High" }, + ], defaultReasoningEffort: "high", inputModalities: ["text"], additionalSpeedTiers: ["fast"], diff --git a/tests/features/chat/application/composer/suggestions.test.ts b/tests/features/chat/application/composer/suggestions.test.ts index d1f4d93c..3a77f8e2 100644 --- a/tests/features/chat/application/composer/suggestions.test.ts +++ b/tests/features/chat/application/composer/suggestions.test.ts @@ -516,11 +516,20 @@ describe("composer suggestions", () => { }); it("suggests reasoning effort arguments for /reasoning", () => { - const models = [model("gpt-5.5", ["low", "medium", "high"]), model("gpt-5.4-mini", ["minimal", "low", "medium"])]; + const models = [ + model("gpt-5.5", ["low", "medium", "high"], { + reasoningEffortOptions: [ + { reasoningEffort: "low", description: "Brief reasoning" }, + { reasoningEffort: "medium", description: "Balanced reasoning" }, + { reasoningEffort: "high", description: "Deep reasoning" }, + ], + }), + model("gpt-5.4-mini", ["minimal", "low", "medium"]), + ]; expect(activeComposerSuggestions("/reasoning h", notes, [], [], models, "gpt-5.5")[0]).toMatchObject({ display: "high", - detail: "Supported by gpt-5.5", + detail: "Deep reasoning", replacement: "high", appendSpaceOnInsert: true, });