Repository navigation
feat: support value references in LLM provider and model fields - #2433
Merged
Merged
Conversation
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.
📝 WalkthroughWalkthroughLLM 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. ChangesLLM provider resolution
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (10)
internal/cmn/schema/dag.schema.jsoninternal/core/spec/dag.gointernal/core/spec/llm_test.gointernal/core/spec/spec003_conformance_test.gointernal/core/spec/step.gointernal/core/value_fields.gointernal/core/value_fields_test.gointernal/runtime/builtin/chat/executor.gointernal/runtime/builtin/chat/executor_test.gospecs/003-value-resolution.md
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.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The root
llm:block (and its step-level counterpart) only resolved${...}references insystemandbase_url.providerandmodelwere rejected at load time by the provider whitelist, so a workflow could not select its model from a parameter or environment variable.Changes
llm.provider,llm.model,llm.api_key_nameand the array form'smodel[].provider,model[].name,model[].api_key_nameare now value-resolved fields.system,base_urlandmessages[].contentbehave as before.invalid provider: <resolved value>.llm.tools[]stays literal — a reference cannot select a tool DAG.llm.ParseProviderTypethat had never picked upzaioropencode, 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.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 lintdagu validateaccepts the DAG above; running it withPROVIDER=notaproviderfails the step withinvalid 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
llm.provider, stringllm.model, and array-formmodel[].providerandmodel[].name.${...}skip load-time checks and are validated after resolution when the step runs.openai-codex,zai,opencode,zhipu,zhipuai,glm).Bug Fixes
llm.api_key_nameliteral per its env-var contract;llm.tools[]remains literal.Written for commit 229ecc2. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation