feat(prompts): a standard way to pin a model and inference parameters on a prompt - #17784
Draft
benlangfeld wants to merge 2 commits into
Draft
benlangfeld wants to merge 2 commits into
benlangfeld wants to merge 2 commits into
Conversation
… on a prompt A prompt's `config` is a free-form JSON object, and three surfaces had grown their own conventions on top of it. Tools had a shared, tested parser; the model and its parameters did not - `provider` and `model` were read by a private schema inside a React hook, and `temperature`, `top_p`, `max_tokens`, `maxReasoningTokens` and `providerOptions` were parsed out of the config and then discarded. Pinning a temperature on a prompt had no effect on an experiment run. Give that facet the same home the tools facet has: `parsePromptModelConfig` in shared, with a `none | valid | invalid` contract. Reading only - validating `config` on write would reject prompts carrying keys Langfuse does not know, and the free-form object is a published contract. Experiments now apply the pinned parameters, not just the model. Switching provider resets the tunables to that adapter's defaults, so the overlay is re-applied there too, clamped to the adapter's temperature ceiling and respecting Anthropic's temperature/top_p exclusivity. An unreadable config warns next to the prompt picker rather than running with half of it. "Open in playground" carried nothing from `config`; it now carries the model, its parameters and the prompt's tools. Saving a playground window as a prompt writes the model parameters back. The prompt form gets a Model section above the JSON editor, using the same picker as the playground and experiments. It derives from the config string and writes back through the handlers, so hand-editing the JSON and using the picker stay in agreement, and it touches only the keys it owns. The provider and model list derivation moves to `getLlmConnectionOptions` so the form and the hook share it rather than forking. Its two orderings differ and are load-bearing; tests pin them. Fern documents the reserved keys on `config` and `lastConfig`. Types stay `unknown`, so generated clients are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
main moved the playground's cross-feature imports behind a barrel (`@/src/features/playground`), which collided with the new `PlaygroundCache` import in the prompt form. Take the barrel, and route the rest of this branch's prompts -> playground imports through it too: `PlaygroundCache`, `getDefaultAdapterParams`, `getLlmConnectionOptions` and `resolveConnectionProvider` join the feature's public surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This branch has not been deployed
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 does this PR do?
A prompt's
configis a free-form JSON object, and three surfaces had grown their own conventions on top of it. Tools already had a shared, tested parser (parsePromptToolConfig). The model and its parameters did not:providerandmodelwere read by a private schema inside a React hook, andtemperature,top_p,max_tokens,maxReasoningTokensandproviderOptionswere parsed out of the config and then thrown away. Pinning a temperature on a prompt had no effect on an experiment run.This gives that facet the same home the tools facet has, and wires it through every surface that should honour it.
The standard
parsePromptModelConfigin@langfuse/shared, with the samenone | valid | invalidcontract as the tools parser. It ownsprovider,model,temperature,top_p,max_tokens,maxReasoningTokensandproviderOptions, coerces numbers written as strings, ignores keys it does not own, and rejects the whole config if any one recognised key is unreadable rather than applying half of it.Deliberately read-side only. Validating
configon write would reject prompts carrying keys Langfuse does not know about, and the free-form object is a published contract.What now uses it
config, even though the generation branch a few lines below did exactly this mapping. It now carries the model, its parameters and the prompt's tools.ModelParameterspicker as the playground and experiments, opt-in via a checkbox. It derives from the config string on every render and writes back through the handlers — no mirrored state — so hand-editing the JSON and using the picker stay in agreement. It touches only the seven keys it owns;toolsand your own metadata survive unpinning.The provider/model list derivation moved to
getLlmConnectionOptionsso the form and the hook share it rather than forking. Its two orderings differ and are load-bearing (the combined picker lists default models first; a provider's own model list lists custom models first) — tests pin both.Fern documents the reserved keys on
configandlastConfig, andweb/public/generated/api/openapi.ymlis regenerated. Types stayunknown, so generated clients are unaffected.Type of change
Mandatory Tasks
Verification
Tests 20 passed (20)— new shared parser suiteTests 221 passed (221)— sharedsrc/server/llmandsrc/features/promptsTest Files 466 passed | 1 skipped (467),Tests 4565 passed | 51 skipped (4616)— full web client suite, including 11 newllmConnectionOptionstests and 9 newPromptModelConfigSectiontestsTasks: 8 successful, 8 totalwithCached: 0 cached, 8 total— repo lint, executed rather than replayed from cacheAll matched files use Prettier code style!pnpm exec knip: clean; web and worker typecheck: cleanpnpm run openapi:checkregenerated the served spec; it is committed hereWhat a reviewer should doubt
/project/<id>/prompts/new, with and without LLM connections configured.PromptModelConfigSectionkeeps a ref to the last written config because the combined picker callsupdateModelParamValuetwice synchronously (provider, then model) and the second call would otherwise clobber the first. There is a test for it, but the pattern deserves a second opinion.PlaygroundCache.modelParamsrequires a provider and model. Widening that type looked riskier than the gap is worth.toolsare read into the playground but not written back on save, andmaxReasoningTokensstill never reachesmapLegacyLLMCompletionParams(pre-existing).🤖 Generated with Claude Code