Allow each scheduled job to pin its provider and model.

New jobs snapshot the current New Session default so quota is predictable.
cron_create inherits the calling session model unless provider+model are set.
The editor lists configured models; the job row shows which model will run.
This commit is contained in:
Whale-Zhang 2026-08-24 14:31:52 +08:00
parent ef8ff6e10d
commit b0d0db1553
11 changed files with 353 additions and 19 deletions

View file

@ -53,6 +53,23 @@ export function newId() {
return randomUUID()
}
export function normalizeJobModel(input = {}) {
const provider = typeof input.provider === 'string' ? input.provider.trim() : ''
const model = typeof input.model === 'string' ? input.model.trim() : ''
const reasoningEffort = typeof input.reasoningEffort === 'string'
? input.reasoningEffort.trim()
: (typeof input.reasoning_effort === 'string' ? input.reasoning_effort.trim() : '')
if (!provider && !model) {
return { provider: '', model: '', reasoningEffort: '' }
}
if (!provider || !model) {
const error = new Error('provider and model must be set together')
error.code = 'INVALID_JOB'
throw error
}
return { provider, model, reasoningEffort }
}
/**
* @param {object} input
* @param {object} state
@ -74,6 +91,7 @@ export function createJobRecord(input, state, now) {
const settings = state.settings || DEFAULT_SETTINGS
const schedule = validateSchedule(input.schedule, settings.timezone)
const timeoutMinutes = Number(input.timeoutMinutes)
const model = normalizeJobModel(input)
const job = {
id: String(input.id || newId()),
name,
@ -83,6 +101,9 @@ export function createJobRecord(input, state, now) {
timeoutMinutes: Number.isFinite(timeoutMinutes) && timeoutMinutes >= 1
? Math.min(240, Math.round(timeoutMinutes))
: 10,
provider: model.provider,
model: model.model,
reasoningEffort: model.reasoningEffort,
schedule: schedule.kind === 'cron'
? { kind: 'cron', expr: schedule.expr, timezone: schedule.timezone }
: { kind: 'at', at: new Date(schedule.at).toISOString(), timezone: schedule.timezone },