Skip to content

feat: support value references in LLM provider and model fields - #2433

Merged
yohamta0 merged 3 commits into
mainfrom
feat/llm-config-variables
Jul 25, 2026
Merged

yohamta0 merged 3 commits into
mainfrom
feat/llm-config-variables

Conversation

@yohamta0

@yohamta0 yohamta0 commented Jul 25, 2026 •

Copy link
Copy Markdown
Member

What

The root llm: block (and its step-level counterpart) only resolved ${...} references in system and base_url. provider and model were rejected at load time by the provider whitelist, so a workflow could not select its model from a parameter or environment variable.

params:
  - name: PROVIDER
    default: anthropic
  - name: MODEL
    default: claude-sonnet-4-6

llm:
  provider: ${params.PROVIDER}
  model: ${params.MODEL}
  system: You are helpful

steps:
  - id: ask
    action: chat.completion
    with:
      prompt: hello

Changes

  • llm.provider, llm.model, llm.api_key_name and the array form's model[].provider, model[].name, model[].api_key_name are now value-resolved fields. system, base_url and messages[].content behave as before.
  • A provider carrying a reference skips the load-time whitelist check; it is validated against the supported provider list at step start, after resolution. A bad resolved value fails the step with invalid provider: <resolved value>.
  • llm.tools[] stays literal — a reference cannot select a tool DAG.
  • The DAG-level provider whitelist was a hand-maintained copy of llm.ParseProviderType that had never picked up zai or opencode, so those were accepted on a step but rejected at the root. Both levels now share one check, and the JSON schema enum is realigned with it.
  • Spec 003's value-resolution matrix is updated to match, along with its conformance tests.

Numeric fields (temperature, max_tokens, top_p) are typed as numbers in YAML and are not covered.

Test plan

  • go test ./internal/core/... ./internal/runtime/...
  • make lint
  • End-to-end: dagu validate accepts the DAG above; running it with PROVIDER=notaprovider fails the step with invalid provider: notaprovider, confirming the reference is substituted before the provider is constructed.

Docs live in a separate repo and need a follow-up there.


Summary by cubic

Enable choosing LLM provider and model via ${...} references at runtime, with provider validation deferred until resolution. Steps now fail early if provider or model resolve to empty to avoid downstream API errors.

  • New Features

    • Runtime resolution for llm.provider, string llm.model, and array-form model[].provider and model[].name.
    • Providers with ${...} skip load-time checks and are validated after resolution when the step runs.
    • JSON schema accepts references and adds aliases (openai-codex, zai, opencode, zhipu, zhipuai, glm).
  • Bug Fixes

    • Reject provider or model that resolve to empty; inherited configs with no model now error at step start.
    • Unified provider validation; removed the stale DAG-level whitelist.
    • Kept llm.api_key_name literal per its env-var contract; llm.tools[] remains literal.
    • Updated Spec 003 to cover provider/model resolution.

Written for commit 229ecc2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added runtime value resolution for LLM providers and model names using parameter and environment references.
    • Expanded LLM configuration fields that support value resolution, including provider, model, endpoint, and API key name.
    • Added support for runtime-resolved providers in model configurations.
  • Bug Fixes

    • Improved validation to accept deferred provider references while continuing to reject unsupported concrete providers.
  • Documentation

    • Updated value-resolution guidance for LLM configuration fields.

Root `llm:` and step-level `llm:` accepted `${params.x}` / `${env.X}` in
`system` and `base_url` only. Provider and model names were rejected at load
time by the provider whitelist, so a workflow could not pick its model from a
parameter.

Provider, model, and API key name now resolve like any other value-resolved
field. A provider carrying a reference skips the load-time whitelist check and
is validated against the supported provider list at step start, once resolved.
Tool-name lists stay literal — a reference cannot select a tool DAG.

The DAG-level provider whitelist was a stale copy of ParseProviderType that had
never picked up zai or opencode; both levels now share one check, and the JSON
schema enum is realigned with it.
Copilot AI review requested due to automatic review settings July 25, 2026 15:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

LLM provider validation now accepts runtime references, additional LLM fields participate in value resolution, and chat execution resolves model providers and names before creating providers and running models.

Changes

LLM provider resolution

Layer / File(s) Summary
Provider validation contract
internal/cmn/schema/dag.schema.json, internal/core/spec/*.go, internal/core/spec/llm_test.go
Provider schemas accept expanded provider names and ${...} references; core validation defers referenced providers and validates concrete values consistently.
LLM field resolution surfaces
internal/core/value_fields*, internal/core/spec/spec003_conformance_test.go, specs/003-value-resolution.md
LLM-level and model-level provider, model, name, endpoint, and API key fields are included in resolution coverage; tool lists remain literal.
Chat execution resolution
internal/runtime/builtin/chat/executor*.go
The chat executor resolves model providers and names before execution, defers parsing referenced providers, and validates providers during creation.
Estimated code review effort: 3 (Moderate) ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChatExecutor
  participant RuntimeContext
  participant ProviderFactory
  ChatExecutor->>RuntimeContext: resolve model provider and name references
  RuntimeContext-->>ChatExecutor: resolved model entries
  ChatExecutor->>ProviderFactory: create provider for resolved provider
  ProviderFactory-->>ChatExecutor: provider or parse error
  ChatExecutor->>ProviderFactory: run model with resolved configuration
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding value references to LLM provider and model fields.
Description check ✅ Passed The description covers the change, impact, and test plan, though it doesn't follow the exact template headings or include related issues/checklist.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/llm-config-variables

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/runtime/builtin/chat/executor.go`:
- Around line 366-385: Update createProviderForModel to resolve each model’s
api_key_name via runtime.ResolveString before using it for credential lookup, so
parameter-based names resolve to the corresponding environment variable value
rather than being passed literally. Add a regression test covering a parameter
that resolves to an environment-variable name and assert the provider receives
that environment value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 265f3200-b6e4-404b-b9b8-dbdb643cf891

📥 Commits

Reviewing files that changed from the base of the PR and between 2166de4 and 2a2fda7.

📒 Files selected for processing (10)
  • internal/cmn/schema/dag.schema.json
  • internal/core/spec/dag.go
  • internal/core/spec/llm_test.go
  • internal/core/spec/spec003_conformance_test.go
  • internal/core/spec/step.go
  • internal/core/value_fields.go
  • internal/core/value_fields_test.go
  • internal/runtime/builtin/chat/executor.go
  • internal/runtime/builtin/chat/executor_test.go
  • specs/003-value-resolution.md

Comment thread internal/runtime/builtin/chat/executor.go
api_key_name has its own contract: the plain, $-prefixed, and braced forms all
name an environment variable whose value becomes the credential. Listing it as a
value-resolved field implied ${params.KEY_NAME} would work, but a reference that
yields a name resolves to that name and is then sent as the credential itself.

Resolving the field before the environment lookup would break the ${VAR} and
${env.VAR} forms, which already resolve straight to the key. Drop the opt-in
instead and state the field's own rule in the excluded-surface table.
Copilot AI review requested due to automatic review settings July 25, 2026 15:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Load-time validation only sees the raw field, so `model: ${params.MODEL}` passes
it while the reference can still resolve to nothing. The empty name reached
ChatRequest.Model and turned into an external API failure. An inherited root
config with no model at all had the same effect.

Check both fields after resolution so the step fails at start with the reference
that produced nothing. Provider was already rejected by ParseProviderType, but
only as an unnamed invalid value.
Copilot AI review requested due to automatic review settings July 25, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@yohamta0
yohamta0 merged commit 7eae2ab into main Jul 25, 2026
11 checks passed
@yohamta0
yohamta0 deleted the feat/llm-config-variables branch July 25, 2026 16:10
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