Show reasoning effort descriptions in composer suggestions

This commit is contained in:
murashit 2026-07-08 11:48:35 +09:00
parent 19d35f7e33
commit 0ce5ceedd5
6 changed files with 42 additions and 5 deletions

View file

@ -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],

View file

@ -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 })),

View file

@ -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)

View file

@ -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,

View file

@ -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"],

View file

@ -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,
});