harn-stdlib 0.10.66

Embedded Harn standard library source catalog
Documentation
// @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"})
}