mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 06:15:03 +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:
1 parent
4d361b2ecf
commit
9a2598fb61
1 file changed
+103
@@ -11,7 +11,15 @@ import {
|
|||||||
loadOrCreateUnifiedConfig,
|
loadOrCreateUnifiedConfig,
|
||||||
saveUnifiedConfig,
|
saveUnifiedConfig,
|
||||||
getGlobalEnvConfig,
|
getGlobalEnvConfig,
|
||||||
|
getThinkingConfig,
|
||||||
} from '../../config/unified-config-loader';
|
} 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';
|
import { validateFilePath } from './route-helpers';
|
||||||
|
|
||||||
const router = Router();
|
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;
|
export default router;
|
||||||
Reference in new issue
Block a user