mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
- Added a new `session_mode` column to the `conversations` table with a default value of 'design'. - Implemented logic to handle `session_mode` in conversation creation, updates, and retrieval. - Enhanced the API to support `session_mode` in conversation requests, allowing for 'chat' or 'design' modes. - Updated the web application to include a session mode toggle, enabling users to switch between chat and design modes seamlessly. - Adjusted system prompts to reflect the current session mode, providing context-aware responses. This feature enhances the user experience by allowing for more flexible conversation management, catering to different interaction styles.
130 lines
5 KiB
TypeScript
130 lines
5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
|
||
import { composeSystemPrompt } from '../src/prompts/system.js';
|
||
import { DISCOVERY_AND_PHILOSOPHY } from '../src/prompts/discovery.js';
|
||
|
||
// Guard: the contracts copy of DISCOVERY_AND_PHILOSOPHY must have the same
|
||
// cap removal as apps/daemon/src/prompts/discovery.ts. The web app imports
|
||
// composeSystemPrompt from @open-design/contracts, so only testing the daemon
|
||
// copy leaves the web-originated chat path unguarded.
|
||
describe('DISCOVERY_AND_PHILOSOPHY (contracts copy) — TodoWrite plan item count', () => {
|
||
it('does not cap the plan at 10 items via "5–10" wording', () => {
|
||
expect(DISCOVERY_AND_PHILOSOPHY).not.toMatch(/5[–\-]10\s+short\s+imperative/);
|
||
});
|
||
|
||
it('does not cap the plan at 10 items via "5 to 10" wording', () => {
|
||
expect(DISCOVERY_AND_PHILOSOPHY).not.toMatch(/5 to 10\s+(?:short\s+)?items/i);
|
||
});
|
||
|
||
it('does not re-introduce a numeric cap via "at most / maximum / no more than" phrasing', () => {
|
||
expect(DISCOVERY_AND_PHILOSOPHY).not.toMatch(
|
||
/(?:at most|maximum|no more than)\s+1[0-9]\s+(?:todo|plan|step|item)/i,
|
||
);
|
||
});
|
||
|
||
it('still instructs the agent to write a TodoWrite plan', () => {
|
||
expect(DISCOVERY_AND_PHILOSOPHY).toContain('TodoWrite');
|
||
expect(DISCOVERY_AND_PHILOSOPHY).toContain('RULE 3');
|
||
});
|
||
|
||
it('also absent from the composed system prompt', () => {
|
||
const prompt = composeSystemPrompt({});
|
||
expect(prompt).not.toMatch(/5[–\-]10\s+short\s+imperative/);
|
||
});
|
||
|
||
it('uses a top-level Chat mode override for conversational sessions', () => {
|
||
const prompt = composeSystemPrompt({ sessionMode: 'chat' });
|
||
|
||
expect(prompt).toContain('# Chat mode — standard conversation');
|
||
expect(prompt).toContain('https://github.com/nexu-io/open-design');
|
||
expect(prompt).toContain('https://open-design.ai/');
|
||
expect(prompt).toContain('https://discord.com/invite/9ptkbbqRu');
|
||
expect(prompt).toContain('do not emit a default discovery `<question-form>`');
|
||
expect(prompt.indexOf('# Chat mode — standard conversation')).toBeLessThan(
|
||
prompt.indexOf(DISCOVERY_AND_PHILOSOPHY),
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('composeSystemPrompt', () => {
|
||
it('injects Chinese quick brief guidance when the UI locale is zh-CN', () => {
|
||
const prompt = composeSystemPrompt({ locale: 'zh-CN' });
|
||
|
||
expect(prompt).toContain('# UI locale override');
|
||
expect(prompt).toContain('`zh-CN` (Simplified Chinese)');
|
||
expect(prompt).toContain('快速简报 — 30 秒');
|
||
expect(prompt).toContain('目标用户');
|
||
expect(prompt).toContain('视觉调性');
|
||
expect(prompt).toContain('Keep machine-readable ids and object option `value` fields exact and unlocalized');
|
||
});
|
||
|
||
it('preserves canonical default task-type options under locale overrides', () => {
|
||
const prompt = composeSystemPrompt({ locale: 'zh-CN' });
|
||
|
||
expect(prompt).toContain(
|
||
'keep the `taskType` option labels as the canonical routing choices',
|
||
);
|
||
for (const option of [
|
||
'Prototype',
|
||
'Live artifact',
|
||
'Slide deck',
|
||
'Image',
|
||
'Video',
|
||
'HyperFrames',
|
||
'Audio',
|
||
'Other',
|
||
]) {
|
||
expect(prompt).toContain(`"${option}"`);
|
||
}
|
||
expect(prompt).not.toContain('option labels as `原型`');
|
||
expect(prompt).not.toContain('`实时作品`');
|
||
});
|
||
|
||
it('preserves canonical default task-type options for zh-TW locale overrides', () => {
|
||
const prompt = composeSystemPrompt({ locale: 'zh-TW' });
|
||
|
||
expect(prompt).toContain('# UI locale override');
|
||
expect(prompt).toContain('`zh-TW` (Traditional Chinese)');
|
||
expect(prompt).toContain(
|
||
'keep the `taskType` option labels as the canonical routing choices',
|
||
);
|
||
for (const option of [
|
||
'Prototype',
|
||
'Live artifact',
|
||
'Slide deck',
|
||
'Image',
|
||
'Video',
|
||
'HyperFrames',
|
||
'Audio',
|
||
'Other',
|
||
]) {
|
||
expect(prompt).toContain(`"${option}"`);
|
||
}
|
||
expect(prompt).not.toContain('快速简报 — 30 秒');
|
||
expect(prompt).not.toContain('option labels as `原型`');
|
||
expect(prompt).not.toContain('`实时作品`');
|
||
});
|
||
|
||
it('treats an active design system as the visual direction', () => {
|
||
const prompt = composeSystemPrompt({
|
||
designSystemTitle: 'ComfyUI',
|
||
designSystemBody: '# ComfyUI\n\n--accent: #ffd500',
|
||
metadata: { kind: 'prototype' } as any,
|
||
activeStageBlocks: [
|
||
'\n\n## Active stage: plan\n\n### direction-picker\n\nAsk for 3-5 directions.',
|
||
],
|
||
});
|
||
|
||
expect(prompt).toContain('## Active design system — ComfyUI');
|
||
expect(prompt).toContain('Active design system exception');
|
||
expect(prompt).toContain(
|
||
'the active design system is the visual direction for this project',
|
||
);
|
||
expect(prompt).toContain('Do not ask the user to pick a separate theme color');
|
||
expect(prompt).toContain('Do not emit a direction question-form');
|
||
expect(prompt).not.toContain('<question-form id="direction"');
|
||
expect(prompt.indexOf('## Active design system visual direction')).toBeGreaterThan(
|
||
prompt.indexOf('### direction-picker'),
|
||
);
|
||
});
|
||
});
|