mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 08:17:22 +00:00
feat(api): add /api/thinking endpoints for budget config
- GET /api/thinking returns current config - PUT /api/thinking updates mode/override/tier_defaults - validate override bounds and level names Refs #307
This commit is contained in:
@@ -11,7 +11,15 @@ import {
|
||||
loadOrCreateUnifiedConfig,
|
||||
saveUnifiedConfig,
|
||||
getGlobalEnvConfig,
|
||||
getThinkingConfig,
|
||||
} from '../../config/unified-config-loader';
|
||||
import type { ThinkingConfig } from '../../config/unified-config-types';
|
||||
import {
|
||||
THINKING_BUDGET_MIN,
|
||||
THINKING_BUDGET_MAX,
|
||||
VALID_THINKING_LEVELS,
|
||||
THINKING_OFF_VALUES,
|
||||
} from '../../cliproxy';
|
||||
import { validateFilePath } from './route-helpers';
|
||||
|
||||
const router = Router();
|
||||
@@ -221,4 +229,99 @@ router.put('/global-env', (req: Request, res: Response): void => {
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== Thinking Configuration ====================
|
||||
|
||||
/**
|
||||
* GET /api/thinking - Get thinking budget configuration
|
||||
* Returns the thinking section from config.yaml
|
||||
*/
|
||||
router.get('/thinking', (_req: Request, res: Response): void => {
|
||||
try {
|
||||
const config = getThinkingConfig();
|
||||
res.json({ config });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: (error as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /api/thinking - Update thinking budget configuration
|
||||
* Updates the thinking section in config.yaml
|
||||
*/
|
||||
router.put('/thinking', (req: Request, res: Response): void => {
|
||||
try {
|
||||
const updates = req.body as Partial<ThinkingConfig>;
|
||||
const config = loadOrCreateUnifiedConfig();
|
||||
|
||||
// Validate mode if provided
|
||||
if (updates.mode !== undefined) {
|
||||
const validModes = ['auto', 'off', 'manual'];
|
||||
if (!validModes.includes(updates.mode)) {
|
||||
res.status(400).json({ error: `Invalid mode: must be one of ${validModes.join(', ')}` });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate override if provided (budget or level)
|
||||
if (updates.override !== undefined) {
|
||||
if (typeof updates.override === 'number') {
|
||||
if (
|
||||
!Number.isFinite(updates.override) ||
|
||||
updates.override < THINKING_BUDGET_MIN ||
|
||||
updates.override > THINKING_BUDGET_MAX
|
||||
) {
|
||||
res.status(400).json({
|
||||
error: `Invalid override: must be between ${THINKING_BUDGET_MIN} and ${THINKING_BUDGET_MAX}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (typeof updates.override === 'string') {
|
||||
const normalizedValue = updates.override.toLowerCase().trim();
|
||||
const validValues = [...VALID_THINKING_LEVELS, ...THINKING_OFF_VALUES] as readonly string[];
|
||||
if (!validValues.includes(normalizedValue)) {
|
||||
res.status(400).json({
|
||||
error: `Invalid override: must be a level name (${VALID_THINKING_LEVELS.join(', ')}) or a number`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate tier_defaults if provided
|
||||
if (updates.tier_defaults !== undefined) {
|
||||
const validLevels = [...VALID_THINKING_LEVELS] as string[];
|
||||
for (const [tier, level] of Object.entries(updates.tier_defaults)) {
|
||||
if (!['opus', 'sonnet', 'haiku'].includes(tier)) {
|
||||
res.status(400).json({ error: `Invalid tier: ${tier}` });
|
||||
return;
|
||||
}
|
||||
if (!validLevels.includes(level)) {
|
||||
res.status(400).json({
|
||||
error: `Invalid level for ${tier}: must be one of ${validLevels.join(', ')}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update thinking section
|
||||
config.thinking = {
|
||||
mode: updates.mode ?? config.thinking?.mode ?? 'auto',
|
||||
override: updates.override ?? config.thinking?.override,
|
||||
tier_defaults: {
|
||||
opus: updates.tier_defaults?.opus ?? config.thinking?.tier_defaults?.opus ?? 'high',
|
||||
sonnet: updates.tier_defaults?.sonnet ?? config.thinking?.tier_defaults?.sonnet ?? 'medium',
|
||||
haiku: updates.tier_defaults?.haiku ?? config.thinking?.tier_defaults?.haiku ?? 'low',
|
||||
},
|
||||
provider_overrides: updates.provider_overrides ?? config.thinking?.provider_overrides,
|
||||
show_warnings: updates.show_warnings ?? config.thinking?.show_warnings ?? true,
|
||||
};
|
||||
|
||||
saveUnifiedConfig(config);
|
||||
res.json({ success: true, config: config.thinking });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: (error as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user