Skip to content

Plugin SDK: add host-owned structured runtime LLM#80203

Closed
100yenadmin wants to merge 4 commits into
openclaw:mainfrom
electricsheephq:codex/plugin-inference-followup
Closed

Plugin SDK: add host-owned structured runtime LLM#80203
100yenadmin wants to merge 4 commits into
openclaw:mainfrom
electricsheephq:codex/plugin-inference-followup

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented May 10, 2026

Copy link
Copy Markdown
Contributor

Why this should exist

OpenClaw already has two useful but incomplete lanes for plugin-side model work:

  • api.runtime.llm.complete(...) for trusted host-owned text completion
  • api.runtime.mediaUnderstanding.extractStructuredWithModel(...) in #79334 for provider-owned media-first structured extraction

What is still missing is the general middle lane for plugins that need bounded host-owned structured inference without:

  • handling OAuth/API credentials directly,
  • inventing bespoke shell bridges, or
  • stretching media-understanding into every structured workload.

That gap shows up across a lot more than one plugin family:

  • knowledge-base plugins turning text or screenshots into normalized evidence
  • support plugins extracting issue summaries, repro steps, and error clues from screenshots plus notes
  • CRM plugins extracting companies, people, dates, action items, and sentiment from inbound content
  • finance/admin plugins extracting vendor, totals, tax, and due dates from receipts or invoices
  • migration/import plugins mapping arbitrary raw content into plugin-owned JSON before storage

The goal of this PR is to make that generic host-owned lane first-class.

What this adds

This PR adds:

  • api.runtime.llm.completeStructured(...)
  • typed structured input blocks for text and optional images
  • optional jsonMode, jsonSchema, schemaName, timeoutMs, and profile
  • parsed JSON results when JSON mode is requested
  • the same host-owned model/auth/runtime preparation path as api.runtime.llm.complete(...)
  • the same trust gating model for model and agent overrides, plus a new explicit gate for auth-profile overrides

Why this belongs under runtime.llm

This API is deliberately the generic agent-bound runtime lane.

It reuses the same host-owned completion path as complete(...), then adds the structured affordances plugin authors actually need:

  • prompt shaping
  • optional image inputs
  • JSON/schema validation
  • timeout control
  • controlled typed output

That makes it the right home for general structured plugin inference.

Boundary vs #79334

This PR is a sister, not a replacement.

  • runtime.llm.completeStructured(...) is the generic agent-bound structured inference lane
  • mediaUnderstanding.extractStructuredWithModel(...) in #79334 remains the narrower provider-owned media capability lane

That separation keeps both seams honest:

  • use runtime.llm.completeStructured(...) when the plugin wants host-owned structured inference against the active agent/runtime path
  • use mediaUnderstanding.extractStructuredWithModel(...) when the plugin wants explicit provider/model media-routing behavior
flowchart LR
  Plugin["Trusted plugin"] --> LLM["api.runtime.llm.completeStructured(...)\nagent-bound generic lane"]
  Plugin --> Media["api.runtime.mediaUnderstanding.extractStructuredWithModel(...)\nprovider-owned media lane"]
  LLM --> Host["host-owned simple completion runtime"]
  Media --> Provider["media-understanding provider registry"]
Loading

Safety and trust model

This stays host-owned end to end.

Plugins do not receive raw OAuth tokens, refresh tokens, or provider secrets.

The host still owns:

  • auth resolution
  • provider runtime preparation
  • model routing
  • timeout handling
  • agent binding
  • trust gating

This PR also closes a subtle trust gap by treating auth-profile selection as a real override. profile now requires explicit opt-in via:

  • plugins.entries.<id>.llm.allowProfileOverride: true

The full runtime trust picture is now:

  • model overrides: allowModelOverride + optional allowedModels
  • cross-agent calls: allowAgentIdOverride
  • auth-profile selection: allowProfileOverride

Embedded model-ref suffixes now flow through that same gate, and conflicting profile vs model@profile inputs fail closed.

Behavior details

A few implementation details here are deliberate and worth calling out:

  • JSON mode is explicit: parsed JSON is only returned when jsonMode: true or jsonSchema is provided
  • Image inputs respect host image fallback behavior: when the active model is text-only, structured image calls reuse the host image-model fallback path instead of failing early when a configured image model exists
  • Context-engine parity is preserved: session-bound context-engine runtime hooks now expose completeStructured(...) alongside complete(...)

Example

const result = await api.runtime.llm.completeStructured({
  instructions: "Extract vendor, total, and searchable tags.",
  input: [
    {
      type: "image",
      buffer: receiptBuffer,
      mimeType: "image/png",
      fileName: "receipt.png",
    },
    { type: "text", text: "Prefer the printed total over handwritten notes." },
  ],
  jsonSchema: {
    type: "object",
    properties: {
      vendor: { type: "string" },
      total: { type: "number" },
      tags: { type: "array", items: { type: "string" } },
    },
    required: ["vendor", "total"],
  },
  purpose: "receipts.extract",
});

What changed

  • add LlmCompleteStructured* runtime types
  • add runtime.llm.completeStructured(...) to the plugin runtime facade
  • add trust gating for auth-profile override selection
  • extend the plugin config schema/docs with plugins.entries.<id>.llm.allowProfileOverride
  • reuse host image-model fallback routing for structured image calls
  • expose completeStructured(...) to context-engine runtime hooks
  • add runtime tests for JSON/schema validation, image inputs, profile trust, image fallback behavior, timeout behavior, and context-engine binding
  • add regression tests that block model@profile bypasses and reject conflicting explicit vs embedded auth-profile selection
  • regenerate the plugin SDK API baseline

Validation

  • pnpm test -- src/plugins/runtime/runtime-llm.runtime.test.ts src/plugins/runtime/index.test.ts src/plugins/config-state.test.ts src/config/schema.help.quality.test.ts
  • pnpm plugin-sdk:api:gen
  • pnpm plugin-sdk:api:check
  • pnpm check:changed

Real behavior proof

  • Behavior or issue addressed:

    • Plugins could express auth-profile selection either as profile: "..." or as a trailing model-ref suffix like openai/gpt-5.5@work.
    • Before this fix, the new trust gate only checked the explicit profile field, so embedded model@profile requests could steer credential selection without allowProfileOverride.
  • Real environment tested:

    • Local OpenClaw checkout on codex/plugin-inference-followup using the real runtime implementation through node --import tsx on macOS, with no test mocks in the proof command.
  • Exact steps or command run after this patch:

    node --import tsx -e 'import { createRuntimeLlm } from "./src/plugins/runtime/runtime-llm.runtime.ts"; const cfg = { agents: { defaults: { model: "openai/gpt-5.5" } } }; const llm = createRuntimeLlm({ getConfig: () => cfg, authority: { caller: { kind: "host", id: "proof" }, allowComplete: true, allowModelOverride: true, agentId: "ada" } }); for (const [label, params] of [["embedded-profile-blocked", { model: "openai/gpt-5.5@openai-codex:work", instructions: "Extract summary.", input: [{ type: "text", text: "Hello" }] }],["conflicting-profile-rejected", { model: "openai/gpt-5.5@openai-codex:work", profile: "openai-codex:other", instructions: "Extract summary.", input: [{ type: "text", text: "Hello" }] }]]) { try { await llm.completeStructured(params); console.log(label + ": UNEXPECTED_SUCCESS"); } catch (error) { console.log(label + ": " + (error instanceof Error ? error.message : String(error))); } }'
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
    Terminal capture:

    embedded-profile-blocked: Plugin LLM completion cannot override the auth profile.
    conflicting-profile-rejected: Plugin LLM completion received conflicting auth profiles in model and profile fields.
    

    Supplemental focused regression coverage:

    ✓ rejects structured auth-profile overrides without explicit trust
    ✓ rejects auth-profile suffixes in structured model refs without explicit trust
    ✓ treats auth-profile suffixes in structured model refs as profile overrides when trusted
    ✓ rejects conflicting explicit and embedded structured auth-profile overrides
    
  • Observed result after fix:

    • The real runtime now fails closed on both bypass shapes before host auth preparation.
    • Structured callers can no longer pick a credential profile through model@profile unless allowProfileOverride is explicitly trusted.
    • Conflicting explicit and embedded profile selections are rejected instead of being resolved implicitly.
  • What was not tested:

    • A live provider-backed completion after an allowed profile override was not exercised in this proof step.
    • The trusted-path wiring is covered by the focused runtime tests above.
  • Before evidence (optional but encouraged):

    • Before this patch, profile was gated but the embedded model@profile form was not normalized before auth-profile policy evaluation.

Non-goals

  • no raw OAuth/token exposure to plugins
  • no plugin-specific routes or schemas in core
  • no replacement of #79334
  • no tool-using long-running agent workflow lane

Closes #80188.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime agents Agent runtime and tooling plugin: file-transfer size: L labels May 10, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 10, 2026
@clawsweeper

clawsweeper Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 2, 2026, 1:07 AM ET / 05:07 UTC.

Summary
Review failed before ClawSweeper could summarize the requested change.

PR surface: Source +421, Tests +537, Docs +43, Generated 0. Total +1001 across 20 files.

Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path.

Review metrics: none identified.

Merge readiness
Overall: 🌊 off-meta tidepool
Proof: 🌊 off-meta tidepool
Patch quality: 🌊 off-meta tidepool
Result: rating does not apply to this item.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] No close action taken because the review did not complete.

Maintainer options:

  1. Decide the mitigation before merge
    Retry the Codex review after fixing the execution failure.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Review did not complete, so no work-lane recommendation was made.
Review details

Best possible solution:

Retry the Codex review after fixing the execution failure.

Do we have a high-confidence way to reproduce the issue?

Unclear. The review failed before ClawSweeper could establish a reproduction path.

Is this the best way to solve the issue?

Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction.

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 93fd17447abd.

Label changes

Label changes:

  • remove P2: Current review triage priority is none.
  • remove impact:security: Current review selected no impact labels.
  • remove impact:auth-provider: Current review selected no impact labels.

Label justifications:

  • rating: 🌊 off-meta tidepool: Overall readiness is 🌊 off-meta tidepool; proof is 🌊 off-meta tidepool and patch quality is 🌊 off-meta tidepool.
Evidence reviewed

PR surface:

Source +421, Tests +537, Docs +43, Generated 0. Total +1001 across 20 files.

View PR surface stats
Area Files Added Removed Net
Source 12 481 60 +421
Tests 4 543 6 +537
Docs 3 47 4 +43
Config 0 0 0 0
Generated 1 2 2 0
Other 0 0 0 0
Total 20 1073 72 +1001

What I checked:

  • failure reason: codex execution failed.
  • codex failure detail: Codex review failed for this PR with exit 1.
  • codex stdout: Per-item Codex failure; continuing with the rest of the shard.

Likely related people:

  • unknown: Codex failed before it could trace repository history. (role: review did not complete; confidence: low)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 10, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 10, 2026
@100yenadmin

Copy link
Copy Markdown
Contributor Author

Follow-up hardening on the latest head (0958496f9d):

  • closed the original model@profile auth-profile bypass
  • added contributor real-behavior proof so the external-PR proof gate passes
  • normalized same-as-default model@profile requests to behave like profile-only selection instead of requiring allowModelOverride
  • kept real model changes gated on both allowModelOverride and allowProfileOverride
  • expanded regression coverage across runtime.llm.complete(...), runtime.llm.completeStructured(...), and plugin-scoped plugins.entries.<id>.llm.allowProfileOverride policy

Focused validation rerun on this head:

  • pnpm test -- src/plugins/runtime/runtime-llm.runtime.test.ts src/plugins/runtime/index.test.ts src/plugins/config-state.test.ts src/config/schema.help.quality.test.ts
  • pnpm plugin-sdk:api:check
  • pnpm check:changed

@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. impact:security Security boundary, credential, authz, sandbox, or sensitive-data risk. impact:auth-provider Auth, provider routing, model choice, or SecretRef resolution may break. labels May 17, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 1, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Jun 1, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jun 2, 2026
@electricsheephq electricsheephq closed this by deleting the head repository Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation gateway Gateway runtime impact:auth-provider Auth, provider routing, model choice, or SecretRef resolution may break. impact:security Security boundary, credential, authz, sandbox, or sensitive-data risk. P2 Normal backlog priority with limited blast radius. plugin: file-transfer proof: supplied External PR includes structured after-fix real behavior proof. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK follow-up: host-owned structured plugin inference beyond media-understanding

2 participants