// @harn-entrypoint-category llm.stdlib
//
// std/llm/defaults — task-pinned, catalog-aware option packs that
// produce a complete `llm_call`-ready dict by layering:
//
// 1. resolved_options(opts) (runtime catalog defaults)
// 2. task overlay (only fills unset fields)
// 3. harness.llm reasoning policy (capability-owned lowering)
// 4. recommend_max_output_tokens() (only when prompt is provided and
// no max_tokens is already present)
// 5. user opts (highest precedence — wins)
//
// User opts always win. Example: pack_for({reasoning_task: "judge", temperature: 0.42})
// returns temperature == 0.42 (overrides judge's 0.0 task default).
//
// Model and provider reasoning quirks belong to the runtime capability
// matrix. This module deliberately contains no model-ID, provider-family, or
// lineage branches.
import { recommend_max_output_tokens } from "std/llm/budget"
import { resolved_options } from "std/llm/catalog"
fn __has_key(d, key) {
if type_of(d) != "dict" {
return false
}
return contains(d.keys(), key)
}
// -------------------------------------------------------------------------------------------------
// task overlay
// -------------------------------------------------------------------------------------------------
/**
* Per-task defaults; tunable, NOT a published vendor recommendation.
* Only fills fields that effort/thinking layers haven't already set.
* task ∈ {"chat","agent","refine","judge","summarize","code","json"}.
*/
fn __task_overlay(task) {
if task == "chat" {
return {temperature: 0.7, top_p: 0.95, schema_retries: 0, output: "text"}
}
if task == "agent" {
return {temperature: 0.5, top_p: 0.95, schema_retries: 0, output: "text"}
}
if task == "refine" {
return {temperature: 0.4, top_p: 0.9, schema_retries: 1, output: "text"}
}
if task == "judge" {
return {temperature: 0.0, top_p: 1.0, schema_retries: 2, output: "json"}
}
if task == "summarize" {
return {temperature: 0.3, top_p: 0.9, schema_retries: 0, output: "text"}
}
if task == "code" {
return {temperature: 0.2, top_p: 0.95, schema_retries: 0, output: "text"}
}
if task == "json" {
return {temperature: 0.1, top_p: 1.0, schema_retries: 2, output: "json"}
}
// unknown task → no overlay
return {}
}
// -------------------------------------------------------------------------------------------------
// helpers
// -------------------------------------------------------------------------------------------------
fn __safe_resolved_options(llm: HarnessLlm, opts) {
// Fall back to a minimal echo dict if resolved_options throws (e.g. when
// opts.model is missing). pack_for already requires opts.model, so this
// is defensive — never hit in normal paths.
const r = try {
resolved_options(llm, opts)
}
if is_err(r) {
return {model: opts?.model ?? "", provider: opts?.provider ?? ""}
}
return unwrap(r)
}
fn __fill_unset(result, overlay) {
// task_overlay's "fill only when unset" semantics. Iterate overlay keys,
// assign only those missing from result.
let out = result
for key in overlay.keys() {
if !__has_key(out, key) {
out[key] = overlay[key]
}
}
return out
}
// `pack_for` has a richer task vocabulary than the runtime reasoning policy.
// Lower task profiles onto the canonical reasoning-task vocabulary before the
// capability owner selects a reasoning level.
fn __reasoning_task(task) {
if task == "judge" {
return "verify"
}
if task == "refine" || task == "json" {
return "chat"
}
return task
}
// -------------------------------------------------------------------------------------------------
// public API
// -------------------------------------------------------------------------------------------------
/**
* pack_for(opts) -> dict
*
* Returns an `llm_call`-ready options dict, resolved through the runtime
* catalog and capability-owned reasoning policy, then pinned to a task. User
* opts always win.
*
* Required: opts.model
* Optional: every canonical `llm_call` option, including `reasoning_policy`,
* `reasoning_scale`, `reasoning_task`, `thinking`, and `effort`, plus
* opts.tool_format, opts.schema_retries, opts.session_id
*
* Example: pack_for({model: "claude-sonnet-4-5", reasoning_task: "judge", temperature: 0.42})
* → result has temperature == 0.42 (user override wins over judge's 0.0
* default).
*
* @effects: []
* @errors: []
*/
pub fn pack_for(_agent: HarnessAgent, llm: HarnessLlm, opts) {
if type_of(opts) != "dict" {
throw "pack_for: opts must be a dict"
}
if opts?.model == nil || opts.model == "" {
throw "pack_for: opts.model is required"
}
const model = opts.model
// 1. Runtime catalog defaults. Pass only model+provider so unrelated
// user-supplied policy and low-level reasoning keys don't leak into the
// resolved dict prematurely.
const resolved_input = if opts?.provider != nil {
{model: model, provider: opts.provider}
} else {
{model: model}
}
let result = __safe_resolved_options(llm, resolved_input)
// 2. Task overlay — only fill fields not already set by the catalog.
const reasoning_task = opts?.reasoning_task ?? "chat"
const overlay = __task_overlay(reasoning_task)
result = __fill_unset(result, overlay)
// 3. Runtime reasoning policy. Capability/catalog data owns every route-
// specific decision; explicit low-level `thinking`/`effort` still wins.
result = llm.apply_reasoning_policy(
result
+ opts
+ {
reasoning_policy: opts?.reasoning_policy ?? "auto",
reasoning_scale: opts?.reasoning_scale ?? "medium",
reasoning_task: __reasoning_task(reasoning_task),
},
)
// 4. Recommended max_tokens when caller supplied a prompt and no catalog,
// task, or caller setting already supplies one.
if opts?.prompt != nil && opts?.max_tokens == nil && !__has_key(result, "max_tokens") {
const recommended = try {
recommend_max_output_tokens(
llm,
{
prompt: opts.prompt,
system: opts?.system ?? "",
model: model,
task_kind: reasoning_task,
headroom: 0.1,
},
)
}
if !is_err(recommended) {
result = result + {max_tokens: unwrap(recommended)}
}
}
// 5. Canonical user options — highest precedence. Policy keys remain in the
// returned request for introspection; direct `thinking` and `effort` pins
// replace any generated low-level setting.
result = result + opts
// 6. Re-pin model. resolved_options already set provider, but if the
// caller passed a different model id at the top level it must win.
result = result + {model: model}
return result
}
/**
* pack_chat(model, opts) — convenience wrapper for task: "chat".
*
* @effects: []
* @errors: []
*/
pub fn pack_chat(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "chat"})
}
/**
* pack_agent(model, opts) — convenience wrapper for task: "agent".
*
* @effects: []
* @errors: []
*/
pub fn pack_agent(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "agent"})
}
/**
* pack_refine(model, opts) — convenience wrapper for task: "refine".
*
* @effects: []
* @errors: []
*/
pub fn pack_refine(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "refine"})
}
/**
* pack_judge(model, opts) — convenience wrapper for task: "judge".
*
* @effects: []
* @errors: []
*/
pub fn pack_judge(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "judge"})
}
/**
* pack_summarize(model, opts) — convenience wrapper for task: "summarize".
*
* @effects: []
* @errors: []
*/
pub fn pack_summarize(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "summarize"})
}
/**
* pack_code(model, opts) — convenience wrapper for task: "code".
*
* @effects: []
* @errors: []
*/
pub fn pack_code(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "code"})
}
/**
* pack_json(model, opts) — convenience wrapper for task: "json".
*
* @effects: []
* @errors: []
*/
pub fn pack_json(agent: HarnessAgent, llm: HarnessLlm, model, opts = nil) {
const base = opts ?? {}
return pack_for(agent, llm, base + {model: model, reasoning_task: "json"})
}