Skip to content

feat(plugin): add chat.variant hook for programmatic thinking/variant control#6662

Closed
code-yeongyu wants to merge 2 commits into
anomalyco:devfrom
code-yeongyu:feature/plugin-variant-hook
Closed

feat(plugin): add chat.variant hook for programmatic thinking/variant control#6662
code-yeongyu wants to merge 2 commits into
anomalyco:devfrom
code-yeongyu:feature/plugin-variant-hook

Conversation

@code-yeongyu

Copy link
Copy Markdown
Contributor

Summary

Add chat.variant plugin hook that allows plugins to programmatically control the thinking/variant level (high, max) before LLM requests are made.

Changes

  • Add chat.variant hook type definition in @opencode/plugin
  • Trigger hook in llm.ts before variant options are applied
  • Plugin receives current variant state and can override via output.variant or output.options

Use Case

Plugins can now programmatically control:

  • Anthropic's thinking budget (high/max)
  • OpenAI's reasoning effort
  • Other provider-specific variant options

This enables use cases like:

  • Always use high thinking for specific models
  • Dynamic thinking level based on conversation context
  • Conditional variant selection based on session/agent type

Example Usage

const myPlugin: Plugin = async () => ({
  "chat.variant": async (input, output) => {
    // Always use high thinking for Claude
    if (input.model.id.includes("claude")) {
      output.variant = "high"
    }
  }
})

Generated with assistance of OhMyOpenCode

… control

Allow plugins to override the variant/thinking level used for LLM requests.
This enables programmatic control over model variants (e.g., Anthropic's
thinking budget, OpenAI's reasoning effort) via the plugin system.

The hook receives:
- sessionID, agent, model context
- currentVariant: the variant selected via UI (ctrl+t)
- availableVariants: list of variants supported by the model

The hook can set:
- variant: override the variant name
- options: directly inject provider options (takes precedence)

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Tests:
- variant override
- options override
- preserve unchanged variant
- input parameter validation

Generated with assistance of OhMyOpenCode
@rekram1-node

Copy link
Copy Markdown
Collaborator

I don't think u need a brand new plugin for it.... See here:

    const variant = input.model.variants && input.user.variant ? input.model.variants[input.user.variant] : {}
    const options = pipe(
      ProviderTransform.options(input.model, input.sessionID, provider.options),
      mergeDeep(small),
      mergeDeep(input.model.options),
      mergeDeep(input.agent.options),
      mergeDeep(variant),
    )


    const params = await Plugin.trigger(
      "chat.params",
      {
        sessionID: input.sessionID,
        agent: input.agent,
        model: input.model,
        provider: Provider.getProvider(input.model.providerID),
        message: input.user,
      },
      {
        temperature: input.model.capabilities.temperature
          ? (input.agent.temperature ?? ProviderTransform.temperature(input.model))
          : undefined,
        topP: input.agent.topP ?? ProviderTransform.topP(input.model),
        topK: ProviderTransform.topK(input.model),
        options, << Variant stuff will already be here
      },
    )

But maybe thats too weird...

@rekram1-node rekram1-node reopened this Jan 2, 2026
@rekram1-node

Copy link
Copy Markdown
Collaborator

accidentally clicked close instead of comment oops

@code-yeongyu

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! I initially created this hook because I thought the variant changes wouldn't be reflected in the TUI. Let me do some more testing to verify this and get back to you.

@rekram1-node

Copy link
Copy Markdown
Collaborator

ah thats a fair point, I think u need to set the variant on the message then in that case

@code-yeongyu

Copy link
Copy Markdown
Contributor Author

I tried implementing it with chat.params like this:

"chat.params": async (input, output) => {
  const agent = input.agent as unknown as { name: string }
  const model = input.model as { variants?: Record<string, Record<string, unknown>> }
  const maxVariant = model.variants?.["max"]
  if (agent.name === "Sisyphus" && maxVariant) {
    output.options = { ...output.options, ...maxVariant }
  }
},

But the TUI does not reflect the change (no "max" indicator shown).

It would be pretty cool if the TUI could display "max" when the plugin sets it. Is doing this via plugin a bad approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants