mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-13 04:40:47 +08:00
Normalize doubled provider/model routes on cron create and fire.
Agents often pass zte/Qwen3-… as both provider and model; split combined routes so scheduled runs resolve a real model. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
3ce60b200c
commit
4472a9c74b
5 changed files with 95 additions and 15 deletions
28
lib/tools.js
28
lib/tools.js
|
|
@ -20,6 +20,7 @@ import {
|
|||
jobVisibleToIdentity,
|
||||
UNASSIGNED_OWNER,
|
||||
} from './ownership.js'
|
||||
import { splitProviderModel } from './store.js'
|
||||
|
||||
const JOB_SCHEMA = {
|
||||
type: 'object',
|
||||
|
|
@ -328,20 +329,27 @@ async function requireOwnedJob(service, id, peer, identity = null) {
|
|||
|
||||
export function callerModelSelection(exec) {
|
||||
const opts = exec?.agent?.options || {}
|
||||
const provider = String(opts.provider || '').trim()
|
||||
const model = String(opts.model || '').trim()
|
||||
if (!provider || !model) return { provider: '', model: '', reasoningEffort: '' }
|
||||
const split = splitProviderModel(opts.provider, opts.model)
|
||||
if (!split.provider || !split.model) return { provider: '', model: '', reasoningEffort: '' }
|
||||
const reasoningEffort = String(opts.reasoningEffort || '').trim()
|
||||
return { provider, model, reasoningEffort }
|
||||
return { provider: split.provider, model: split.model, reasoningEffort }
|
||||
}
|
||||
|
||||
export function resolveCreateModel(args, exec) {
|
||||
const provider = typeof args?.provider === 'string' ? args.provider.trim() : ''
|
||||
const model = typeof args?.model === 'string' ? args.model.trim() : ''
|
||||
const reasoningEffort = typeof args?.reasoning_effort === 'string'
|
||||
? args.reasoning_effort.trim()
|
||||
: (typeof args?.reasoningEffort === 'string' ? args.reasoningEffort.trim() : '')
|
||||
if (provider && model) return { provider, model, reasoningEffort }
|
||||
const explicit = splitProviderModel(args?.provider, args?.model)
|
||||
if (explicit.provider && explicit.model) {
|
||||
return { provider: explicit.provider, model: explicit.model, reasoningEffort }
|
||||
}
|
||||
// One of provider/model alone (after split) → ignore; inherit session instead of throwing.
|
||||
if (explicit.provider || explicit.model) {
|
||||
const fromSession = callerModelSelection(exec)
|
||||
if (fromSession.provider && fromSession.model) {
|
||||
return { ...fromSession, reasoningEffort: reasoningEffort || fromSession.reasoningEffort }
|
||||
}
|
||||
}
|
||||
const fromSession = callerModelSelection(exec)
|
||||
return { ...fromSession, reasoningEffort: reasoningEffort || fromSession.reasoningEffort }
|
||||
}
|
||||
|
|
@ -379,8 +387,8 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
timezone: { type: 'string', description: 'IANA timezone for expr/at. Default Asia/Shanghai. Do not pass UTC unless the user asked for UTC.' },
|
||||
time_zone: { type: 'string', description: 'Alias of timezone.' },
|
||||
cwd: { type: 'string', description: 'Filesystem path of the workspace this job should run in. When the user is in a workspace conversation, pass THAT workspace path (current session cwd). If omitted, the current session cwd is used. WhatsApp/IM creates always use the current session cwd.' },
|
||||
provider: { type: 'string', description: 'Provider route for this job (e.g. minimax-cn, deepseek). Must be passed with model. If omitted, the current session model is stored so quota stays predictable.' },
|
||||
model: { type: 'string', description: 'Model id for this job. Must be passed with provider. Scheduled runs bill this model.' },
|
||||
provider: { type: 'string', description: 'Provider id only (e.g. zte, deepseek, minimax-cn). Do NOT pass "provider/model". Must pair with model, or omit both to inherit the current session.' },
|
||||
model: { type: 'string', description: 'Bare model id only (e.g. Qwen3-235B-A22B). Do NOT pass "provider/model" or repeat the provider. Must pair with provider, or omit both to inherit the current session.' },
|
||||
reasoning_effort: { type: 'string', description: 'Optional reasoning effort for this job.' },
|
||||
timeout_minutes: { type: 'integer', description: 'Per-run timeout in minutes, 1-240.' },
|
||||
enabled: { type: 'boolean', description: 'If false, create paused. Default true.' },
|
||||
|
|
@ -807,7 +815,7 @@ export function cronGuidanceText(nowMs = Date.now(), timeZone = 'Asia/Shanghai')
|
|||
'For "in N minutes / 一分钟后", call cron_create with after_minutes=N only (do not also pass at, hour, or expr).',
|
||||
'For a clock time tonight, pass only hour and minute in 24h (晚上11点34 → hour=23, minute=34; 零点33 → hour=0, minute=33). Extra at/expr fields are ignored.',
|
||||
'Working directory: if the user is chatting in a workspace, pass cwd as that workspace filesystem path (the current session working directory). If they name another workspace, use that path. If cwd is omitted, cron_create uses the current session cwd.',
|
||||
'Model: pass provider+model for the job. If omitted, cron_create stores the current session model. Scheduled runs consume that model\'s quota.',
|
||||
'Model: omit provider+model to inherit the current session. If you pass them, use bare ids only (provider=zte, model=Qwen3-235B-A22B) — never pass "zte/Qwen3-…" as either field.',
|
||||
'Delivery: when chatting on WhatsApp/IM, omit delivery so the job defaults to im for the current chat (group→same group, DM→same DM). A 投递目标 is reused or auto-created. On Web/DSH, default is dsh. Or pass delivery=im with im_bot_id+im_target_id.',
|
||||
'Session mirror: by default the run summary is NOT injected into the origin WhatsApp/Web chat. Pass mirror_to_session=true only when the user wants follow-up context in that chat. Full tool traces always stay in run history; IM delivery (when configured) is independent.',
|
||||
'Agent preset: omit agent_preset to inherit (WhatsApp chat/group preset → creating session → Host default). Pass agent_preset to pin a preset for every scheduled run.',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue