Skip to content

Commit c3782a9

Browse files
committed
feat(core): plumb reasoningLevel through GenerateInput + agent.ts
Adds the per-call override channel that the desktop main process will use to forward ProviderEntry.reasoningLevel into generation requests. When the caller passes an explicit level it takes precedence over reasoningForModel's model-family default; when absent, behavior is unchanged. - GenerateInput.reasoningLevel?: ReasoningLevel - ApplyCommentInput.reasoningLevel?: ReasoningLevel - ModelRunInput.reasoningLevel flows to completeWithRetry() - reasoningForModel exported so agent.ts reuses the same whitelist - agent.ts resolves initialState.thinkingLevel as input.reasoningLevel ?? reasoningForModel(model) ?? 'off'
1 parent 7ff1702 commit c3782a9

8 files changed

Lines changed: 148 additions & 4 deletions

File tree

apps/desktop/src/renderer/src/views/hub/ExampleCard.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,27 @@ export function ExampleCard({ example, onUsePrompt }: ExampleCardProps) {
105105
color: skin.ink,
106106
}}
107107
>
108-
{/* Large watermark glyph */}
108+
{/* SVG thumbnail — fades in on hover so the default card stays clean
109+
and editorial, but hovering reveals the concrete layout preview. */}
110+
<div
111+
aria-hidden
112+
className="absolute inset-0 opacity-0 transition-opacity duration-[var(--duration-base)] ease-[var(--ease-out)] group-hover:opacity-100"
113+
// biome-ignore lint/security/noDangerouslySetInnerHtml: thumbnails are static bundled strings authored in-repo, not user content
114+
dangerouslySetInnerHTML={{ __html: example.thumbnail }}
115+
/>
116+
{/* Gradient scrim under the title so text stays legible over the SVG */}
117+
<div
118+
aria-hidden
119+
className="absolute inset-x-0 bottom-0 h-[62%] opacity-0 transition-opacity duration-[var(--duration-base)] ease-[var(--ease-out)] group-hover:opacity-95"
120+
style={{
121+
background: `linear-gradient(180deg, transparent 0%, ${skin.to} 92%)`,
122+
}}
123+
/>
124+
{/* Large watermark glyph — visible on the resting state, fades out on hover */}
109125
<Icon
110126
aria-hidden
111127
strokeWidth={1}
112-
className="absolute -right-2 -bottom-4 w-[140px] h-[140px] opacity-[0.07]"
128+
className="absolute -right-2 -bottom-4 w-[140px] h-[140px] opacity-[0.07] transition-opacity duration-[var(--duration-base)] ease-[var(--ease-out)] group-hover:opacity-0"
113129
style={{ color: skin.ink }}
114130
/>
115131
{/* Accent dot */}

packages/core/src/agent.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import type {
4747
GenerateOutput,
4848
ReferenceUrlContext,
4949
} from './index.js';
50+
import { reasoningForModel } from './index.js';
5051
import { type CoreLogger, NOOP_LOGGER } from './logger.js';
5152
import { composeSystemPrompt } from './prompts/index.js';
5253
import { makeDeclareTweakSchemaTool } from './tools/declare-tweak-schema.js';
@@ -710,6 +711,13 @@ export async function generateViaAgent(
710711
skillWarnings: skillResult.warnings.length,
711712
});
712713

714+
// Resolve reasoning/thinking level: explicit per-call override (sourced
715+
// from ProviderEntry.reasoningLevel by the desktop main process) takes
716+
// precedence, then the model-family default from reasoningForModel. If
717+
// neither yields a value the agent runs with 'off', matching
718+
// pi-agent-core's default.
719+
const thinkingLevel = input.reasoningLevel ?? reasoningForModel(input.model) ?? 'off';
720+
713721
// Build the Agent. convertToLlm narrows AgentMessage (may include custom
714722
// types) to the LLM-visible Message subset.
715723
const agent = new Agent({
@@ -718,6 +726,7 @@ export async function generateViaAgent(
718726
model: piModel as unknown as PiAiModel<'openai-completions'>,
719727
messages: historyAsAgentMessages,
720728
tools,
729+
thinkingLevel,
721730
},
722731
convertToLlm: (messages) =>
723732
messages.filter(

packages/core/src/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ export interface GenerateInput {
8181
/** v3 extra HTTP headers merged into the outbound request (gateway auth). */
8282
httpHeaders?: Record<string, string> | undefined;
8383
allowKeyless?: boolean | undefined;
84+
/**
85+
* Per-call reasoning level override. Typically sourced from
86+
* `ProviderEntry.reasoningLevel`. When absent, core computes a default
87+
* via `reasoningForModel`.
88+
*/
89+
reasoningLevel?: ReasoningLevel | undefined;
8490
designSystem?: StoredDesignSystem | null | undefined;
8591
attachments?: AttachmentContext[] | undefined;
8692
referenceUrl?: ReferenceUrlContext | null | undefined;
@@ -106,6 +112,8 @@ export interface ApplyCommentInput {
106112
wire?: 'openai-chat' | 'openai-responses' | 'anthropic' | undefined;
107113
httpHeaders?: Record<string, string> | undefined;
108114
allowKeyless?: boolean | undefined;
115+
/** @see GenerateInput.reasoningLevel */
116+
reasoningLevel?: ReasoningLevel | undefined;
109117
designSystem?: StoredDesignSystem | null | undefined;
110118
attachments?: AttachmentContext[] | undefined;
111119
referenceUrl?: ReferenceUrlContext | null | undefined;
@@ -140,6 +148,7 @@ interface ModelRunInput {
140148
wire?: 'openai-chat' | 'openai-responses' | 'anthropic' | undefined;
141149
httpHeaders?: Record<string, string> | undefined;
142150
allowKeyless?: boolean | undefined;
151+
reasoningLevel?: ReasoningLevel | undefined;
143152
signal?: AbortSignal | undefined;
144153
onRetry?: ((info: RetryReason) => void) | undefined;
145154
messages: ChatMessage[];
@@ -522,7 +531,7 @@ const OPENROUTER_REASONING_MODEL_RE = new RegExp(
522531
'i',
523532
);
524533

525-
function reasoningForModel(model: ModelRef): ReasoningLevel | undefined {
534+
export function reasoningForModel(model: ModelRef): ReasoningLevel | undefined {
526535
// Whitelist by (provider, modelId) pair. Substring matches across providers
527536
// are unsafe: an OpenRouter or Groq pass-through id like
528537
// `anthropic/claude-4` or any third-party id containing "o1"/"r1" would
@@ -613,6 +622,7 @@ export async function generate(input: GenerateInput): Promise<GenerateOutput> {
613622
wire: input.wire,
614623
httpHeaders: input.httpHeaders,
615624
allowKeyless: input.allowKeyless,
625+
reasoningLevel: input.reasoningLevel,
616626
signal: input.signal,
617627
onRetry: input.onRetry,
618628
messages,
@@ -665,6 +675,7 @@ export async function applyComment(input: ApplyCommentInput): Promise<GenerateOu
665675
wire: input.wire,
666676
httpHeaders: input.httpHeaders,
667677
allowKeyless: input.allowKeyless,
678+
reasoningLevel: input.reasoningLevel,
668679
signal: input.signal,
669680
onRetry: input.onRetry,
670681
messages,

packages/templates/src/examples/examples.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { zhCNExamples } from './locales/zh-CN';
66
describe('examples gallery', () => {
77
it('ships at least 6 curated examples', () => {
88
expect(EXAMPLES.length).toBeGreaterThanOrEqual(6);
9-
expect(EXAMPLES.length).toBeLessThanOrEqual(20);
9+
expect(EXAMPLES.length).toBeLessThanOrEqual(30);
1010
});
1111

1212
it('every example has a unique id', () => {

packages/templates/src/examples/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ import { type Locale, availableLocales, normalizeLocale } from '@open-codesign/i
1515
import { enExamples } from './locales/en';
1616
import { zhCNExamples } from './locales/zh-CN';
1717
import {
18+
thumbAiHero,
19+
thumbAuth,
1820
thumbBlog,
1921
thumbCalendar,
2022
thumbCaseStudy,
2123
thumbChat,
2224
thumbCosmic,
2325
thumbDashboard,
2426
thumbEmail,
27+
thumbKanban,
2528
thumbLanding,
2629
thumbMobile,
2730
thumbOrganic,
@@ -30,6 +33,9 @@ import {
3033
thumbPricing,
3134
thumbReceipt,
3235
thumbSettings,
36+
thumbStatsCounter,
37+
thumbTimeline,
38+
thumbWeather,
3339
} from './thumbnails';
3440

3541
export type ExampleCategory =
@@ -162,6 +168,48 @@ export const EXAMPLES: Example[] = [
162168
prompt:
163169
'Design a settings page for a SaaS application. Left sidebar with setting categories (Profile, Notifications, Security, Billing, Team, Integrations). Main panel shows the active category with form fields: text inputs, toggle switches, dropdown selects, a danger zone with red "Delete account" button. Include a top bar with breadcrumbs and a "Save changes" button. Light theme, clean form layout, proper spacing between sections, accessible focus states on all inputs.',
164170
},
171+
{
172+
id: 'auth-signin',
173+
category: 'ui',
174+
thumbnail: thumbAuth,
175+
prompt:
176+
'Design a sign-in screen for a SaaS product called Lumen. A centered card on a dark starry background holds: product wordmark, "Welcome back" headline, email + password inputs, a primary sign-in button in brand indigo, "Forgot password?" link on the right, an OR divider, three social sign-in buttons (Google / GitHub / Apple) with brand glyphs, and a "Don\'t have an account? Sign up" footer link. Rounded inputs, clear focus rings, subtle card elevation.',
177+
},
178+
{
179+
id: 'kanban-board',
180+
category: 'ui',
181+
thumbnail: thumbKanban,
182+
prompt:
183+
'Design a kanban board for a product team. Three columns: Backlog, In progress, Done — each with a colored header bar (amber / blue / green) and a count pill. Each column holds 3-5 task cards showing title, a short description, an avatar stack of assignees, and a priority tag. Top bar has the project name, a board/list view toggle, filter chips, and an "Add task" button on the right. Soft gray canvas, white cards, rounded corners, lift-on-hover shadow.',
184+
},
185+
{
186+
id: 'ai-product-hero',
187+
category: 'marketing',
188+
thumbnail: thumbAiHero,
189+
prompt:
190+
'Design a hero section for an AI writing assistant called Inkwell. Deep navy → violet gradient background with a soft glowing orb on the right surrounded by concentric rings. Large serif-meets-sans headline, a two-line subhead, an animated caret cursor after the headline (steady blink), and a pair of CTAs (primary gradient button, ghost secondary). Sparse star field, subtle grain overlay, confident editorial feel.',
191+
},
192+
{
193+
id: 'weather-card',
194+
category: 'mobile',
195+
thumbnail: thumbWeather,
196+
prompt:
197+
'Design a mobile weather home screen inside a phone frame. Soft sky-blue → indigo gradient background, a large glass-morphism card in the center showing: city name, current temperature in thin 72px weight, condition glyph (sun behind clouds), high/low, and a horizontal 6-hour forecast strip with small icons + temperatures. Below the card, a second smaller card with "Next 7 days" summary bars. Rounded corners everywhere, gentle translucency, generous padding.',
198+
},
199+
{
200+
id: 'timeline-changelog',
201+
category: 'document',
202+
thumbnail: thumbTimeline,
203+
prompt:
204+
'Design a product changelog page presented as a vertical timeline. Left spine connects four release dots in different accent colors. Each entry has: a date label, a version tag, a headline, a 2-3 line description, and optional inline mini-tags (feature / fix / breaking). Above the timeline sits a filter row (All / Features / Fixes / Breaking) and a subscribe-to-RSS pill. Warm off-white background, serif headings, restrained typography.',
205+
},
206+
{
207+
id: 'stats-counter',
208+
category: 'animation',
209+
thumbnail: thumbStatsCounter,
210+
prompt:
211+
'Design a stats strip section for a landing page with three large animated number counters that count up from 0 to their target on scroll into view (2.4M users, 99.8% uptime, 180 countries). Each stat sits in a translucent card on a deep navy background, with its own neon accent color (sky / violet / green) glowing behind the number. Small all-caps label below each number, tight letter-spacing. Counters ease-out over ~1.2s, no JS libraries — pure IntersectionObserver + requestAnimationFrame.',
212+
},
165213
];
166214

167215
const REGISTRY: Record<Locale, Record<string, ExampleContent>> = {

packages/templates/src/examples/locales/en.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,28 @@ export const enExamples: Record<string, ExampleContent> = {
6262
title: 'App settings page',
6363
description: 'Sidebar-navigated settings with forms, toggles, and danger zone section.',
6464
},
65+
'auth-signin': {
66+
title: 'Sign-in screen',
67+
description: 'Centered card on a starry backdrop with email, social logins, and sign-up link.',
68+
},
69+
'kanban-board': {
70+
title: 'Kanban project board',
71+
description: 'Three-column board with task cards, assignee avatars, and priority tags.',
72+
},
73+
'ai-product-hero': {
74+
title: 'AI product hero',
75+
description: 'Gradient hero with glowing orb, blinking caret headline, and dual CTAs.',
76+
},
77+
'weather-card': {
78+
title: 'Mobile weather card',
79+
description: 'Glass-morphism weather screen with hourly strip and seven-day summary.',
80+
},
81+
'timeline-changelog': {
82+
title: 'Changelog timeline',
83+
description: 'Vertical timeline of releases with color-coded dots and filterable categories.',
84+
},
85+
'stats-counter': {
86+
title: 'Animated stats strip',
87+
description: 'Trio of count-up number cards with neon accents and glowing backgrounds.',
88+
},
6589
};

packages/templates/src/examples/locales/zh-CN.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,28 @@ export const zhCNExamples: Record<string, ExampleContent> = {
6161
title: '应用设置页面',
6262
description: '侧栏导航的设置页:表单、开关、危险操作区。',
6363
},
64+
'auth-signin': {
65+
title: '登录页',
66+
description: '星空背景中心卡片:邮箱登录、第三方登录、注册入口。',
67+
},
68+
'kanban-board': {
69+
title: '看板项目面板',
70+
description: '三列任务面板:卡片、成员头像堆叠、优先级标签。',
71+
},
72+
'ai-product-hero': {
73+
title: 'AI 产品主视觉',
74+
description: '渐变背景 + 发光球体 + 闪烁光标标题 + 双 CTA。',
75+
},
76+
'weather-card': {
77+
title: '移动端天气卡片',
78+
description: '玻璃拟态天气界面:当前温度、小时预报条、七日概览。',
79+
},
80+
'timeline-changelog': {
81+
title: '版本更新时间线',
82+
description: '竖向时间线:配色圆点 + 版本标签 + 分类筛选。',
83+
},
84+
'stats-counter': {
85+
title: '动态数据统计条',
86+
description: '三枚滚动计数卡片,霓虹色光晕,纯原生实现。',
87+
},
6488
};

0 commit comments

Comments
 (0)