(MOT-4353) feat(console,llm-router): standalone call cards + injectable llm-router config UI - #695
Conversation
… cards with terminal tabs Every function call renders as its own card (accordion group removed); consecutive call-like cards stack tight. Collapsed headers carry an args preview; errored calls say 'failed' (gate denials stay verb-less). Trigger-fired and notification rows share the card language: verb-first headers, TERMINAL/RAW JSON tabs, registration recovery (harness rows -> transcript register call, entry-id-exact notification correlation), CONDITIONS met chip, copy on every JSON pane. RegisterTriggerView renders gating conditions as an ONLY IF section. Assistant header renamed 'agent'.
…var key guidance
Console-injected configuration UI (docs/sops/injectable-console-ui.md):
per-provider credential cards with plain-text api_key detection steering
to ${ENV_VAR} references (one-click fix chip, partial-reference warning,
env refs unmasked / secrets masked), console Select for provider pickers,
stream budgets with human-readable echoes, and a system-prompt override
flow that shows the provider-declared prompt. Assets built by build.rs
(esbuild, SKIP_UI_BUILD escape) and embedded in the binary via the shared
iii-console-ui crate.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
skill-check — worker0 verified, 54 skipped (no docs/).
Four for four. Nicely done. |
|
Warning Review limit reached
Next review available in: 51 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds structured trigger and notification cards with registration metadata and condition details. It removes grouped function-trigger rendering. It also adds an embedded, build-on-demand configuration UI for llm-router and registers it at runtime. ChangesChat trigger registration and rendering
llm-router injectable console UI
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
… column 1180px dialog around a max-w-3xl form was ~400px of dead margin; 880px fits the column + padding, and narrow viewports keep the 100vw-2rem cap.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
console/web/src/components/chat/Message.tsx (2)
257-276: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMove the misplaced doc comment.
The comment at lines 257-263 describes
NotificationTerminal, but it sits directly above thehasConditionsdoc comment.NotificationTerminalis defined at line 309. Move this block above that function.🤖 Prompt for 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. In `@console/web/src/components/chat/Message.tsx` around lines 257 - 276, Move the doc comment describing the friendly notification tab so it directly precedes the NotificationTerminal function, leaving the hasConditions documentation immediately above hasConditions. Do not alter either comment’s wording or the functions’ behavior.
555-563: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe notify-versus-call rule is hand-coded in three places.
deliveryOfalready encodes it, andTriggerFiredTerminaluses it atMessage.tsxline 375. The inline'notify'and'harness::send'comparisons can drift from the helper.
console/web/src/components/chat/Message.tsx#L555-L563: derivecalledandnotifiedfromdeliveryOf(t.target)instead of comparing target strings.console/web/src/components/chat/MessageList.tsx#L120-L124: usedeliveryOf(t.target)to decide whether a fire is a notify delivery before it entersnotifyFireByName.🤖 Prompt for 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. In `@console/web/src/components/chat/Message.tsx` around lines 555 - 563, Replace the inline target-string checks in Message.tsx lines 555-563 with deliveryOf(t.target) to derive called and notified, preserving the existing null behavior. In MessageList.tsx lines 120-124, use deliveryOf(t.target) to determine whether the fire is a notify delivery before adding it to notifyFireByName.console/web/src/components/chat/engine/RegisterTriggerView.tsx (2)
170-174: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
isScalaris defined twice with identical bodies. Both definitions are new in this PR, and both files already import fromconsole/web/src/components/chat/engine/shared.tsx.
console/web/src/components/chat/engine/RegisterTriggerView.tsx#L170-L174: exportisScalarfromengine/shared.tsxand import it here.console/web/src/components/chat/Message.tsx#L251-L255: delete the local copy and importisScalarfromengine/shared.tsx, next to the existingFilterChipimport.🤖 Prompt for 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. In `@console/web/src/components/chat/engine/RegisterTriggerView.tsx` around lines 170 - 174, The duplicated isScalar helper should be centralized in shared.tsx. In console/web/src/components/chat/engine/RegisterTriggerView.tsx lines 170-174, export isScalar from engine/shared.tsx and import it for use; in console/web/src/components/chat/Message.tsx lines 251-255, remove the local definition and import isScalar alongside FilterChip.
193-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the chippable predicate once.
Lines 194 and 197 repeat the same expression with a negation. A single predicate removes the risk of the two branches drifting apart.
♻️ Proposed refactor
- const chippable = entries.filter( - ([, v]) => isScalar(v) || (Array.isArray(v) && v.every(isScalar)), - ) - const rest = entries.filter( - ([, v]) => !(isScalar(v) || (Array.isArray(v) && v.every(isScalar))), - ) + const isChippable = (v: unknown) => + isScalar(v) || (Array.isArray(v) && v.every(isScalar)) + const chippable = entries.filter(([, v]) => isChippable(v)) + const rest = entries.filter(([, v]) => !isChippable(v))🤖 Prompt for 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. In `@console/web/src/components/chat/engine/RegisterTriggerView.tsx` around lines 193 - 198, Extract the repeated scalar-or-scalar-array condition from the chippable and rest filters into a single named predicate near the entries processing in RegisterTriggerView. Reuse that predicate directly for chippable and with negation for rest, preserving the existing partitioning behavior.
🤖 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 `@console/web/src/components/chat/Message.tsx`:
- Line 191: The notification regex name group is too restrictive and rejects
names containing colons. In console/web/src/components/chat/Message.tsx:191-191
and console/web/src/components/chat/MessageList.tsx:158-158, update the name
capture to a lazy group so it captures through the last colon preceding the JSON
payload, preserving card rendering and notifyFireByName correlation.
In `@llm-router/build.rs`:
- Around line 132-153: Update subtree_older_than to validate the modification
time of root and every traversed child directory, not only surviving files.
Return false when any directory or file is newer than ceiling, so deletions that
update directory metadata invalidate dist_is_fresh and trigger the build.
In `@llm-router/ui/src/configuration/index.tsx`:
- Around line 150-159: Update the focusField handling in the useEffect to locate
the interactive control contained within the matching data-field wrapper, rather
than focusing the non-focusable wrapper itself. Preserve scrolling the matched
field into view, then focus the appropriate descendant control so keyboard focus
moves to the linked input or control.
- Around line 418-447: Associate the “system prompt” label with the conditional
textarea by giving the visible label an identifying value and assigning the same
value to the textarea’s id or equivalent labeling attribute. Update the elements
in the system-prompt section without changing the existing toggle or prompt
behavior.
---
Nitpick comments:
In `@console/web/src/components/chat/engine/RegisterTriggerView.tsx`:
- Around line 170-174: The duplicated isScalar helper should be centralized in
shared.tsx. In console/web/src/components/chat/engine/RegisterTriggerView.tsx
lines 170-174, export isScalar from engine/shared.tsx and import it for use; in
console/web/src/components/chat/Message.tsx lines 251-255, remove the local
definition and import isScalar alongside FilterChip.
- Around line 193-198: Extract the repeated scalar-or-scalar-array condition
from the chippable and rest filters into a single named predicate near the
entries processing in RegisterTriggerView. Reuse that predicate directly for
chippable and with negation for rest, preserving the existing partitioning
behavior.
In `@console/web/src/components/chat/Message.tsx`:
- Around line 257-276: Move the doc comment describing the friendly notification
tab so it directly precedes the NotificationTerminal function, leaving the
hasConditions documentation immediately above hasConditions. Do not alter either
comment’s wording or the functions’ behavior.
- Around line 555-563: Replace the inline target-string checks in Message.tsx
lines 555-563 with deliveryOf(t.target) to derive called and notified,
preserving the existing null behavior. In MessageList.tsx lines 120-124, use
deliveryOf(t.target) to determine whether the fire is a notify delivery before
adding it to notifyFireByName.
🪄 Autofix
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: d49daae5-8a80-4c60-bf70-39b9f3181ea7
⛔ Files ignored due to path filters (2)
llm-router/Cargo.lockis excluded by!**/*.lockpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (22)
console/web/src/components/chat/ChatView.tsxconsole/web/src/components/chat/FunctionTriggerGroup.stories.tsxconsole/web/src/components/chat/FunctionTriggerGroup.tsxconsole/web/src/components/chat/Message.tsxconsole/web/src/components/chat/MessageList.tsxconsole/web/src/components/chat/engine/RegisterTriggerView.tsxconsole/web/src/components/chat/engine/__tests__/parsers.test.tsconsole/web/src/components/chat/engine/parsers.tsconsole/web/src/components/chat/trigger-registration.test.tsconsole/web/src/components/function-trigger/FunctionTriggerCard.tsxllm-router/Cargo.tomlllm-router/build.rsllm-router/src/lib.rsllm-router/src/main.rsllm-router/src/ui.rsllm-router/ui/build.mjsllm-router/ui/package.jsonllm-router/ui/page.tsxllm-router/ui/src/configuration/index.tsxllm-router/ui/styles.cssllm-router/ui/tsconfig.jsonpnpm-workspace.yaml
💤 Files with no reviewable changes (2)
- console/web/src/components/chat/FunctionTriggerGroup.tsx
- console/web/src/components/chat/FunctionTriggerGroup.stories.tsx
| export function parseNotification( | ||
| content: string, | ||
| ): { name: string; payload: Record<string, unknown> } | null { | ||
| const m = /^\[notification\]\s*([^:]+):\s*(\{[\s\S]*\})\s*$/.exec(content) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The notification-name pattern rejects names that contain a colon. Both files use ([^:]+) for the name group, so a name such as db::exec never matches and the notification loses both its card rendering and its registration correlation.
console/web/src/components/chat/Message.tsx#L191-L191: change the name group to a lazy(.+?)so it binds to the last: {before the JSON payload.console/web/src/components/chat/MessageList.tsx#L158-L158: apply the same lazy group sonotifyFireByNamelookups use the full name.
🧰 Tools
🪛 OpenGrep (1.26.0)
[ERROR] 191-191: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
📍 Affects 2 files
console/web/src/components/chat/Message.tsx#L191-L191(this comment)console/web/src/components/chat/MessageList.tsx#L158-L158
🤖 Prompt for 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.
In `@console/web/src/components/chat/Message.tsx` at line 191, The notification
regex name group is too restrictive and rejects names containing colons. In
console/web/src/components/chat/Message.tsx:191-191 and
console/web/src/components/chat/MessageList.tsx:158-158, update the name capture
to a lazy group so it captures through the last colon preceding the JSON
payload, preserving card rendering and notifyFireByName correlation.
| fn subtree_older_than(root: &Path, ceiling: SystemTime) -> bool { | ||
| let Ok(read) = std::fs::read_dir(root) else { | ||
| return false; | ||
| }; | ||
| for entry in read.flatten() { | ||
| let path = entry.path(); | ||
| let Ok(meta) = entry.metadata() else { | ||
| return false; | ||
| }; | ||
| if meta.is_dir() { | ||
| if !subtree_older_than(&path, ceiling) { | ||
| return false; | ||
| } | ||
| continue; | ||
| } | ||
| let Ok(m) = meta.modified() else { | ||
| return false; | ||
| }; | ||
| if m > ceiling { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Invalidate generated assets after source deletion.
subtree_older_than checks only surviving files. It does not check the root or child-directory modification times. If ui/src/configuration/index.tsx is deleted, dist_is_fresh can retain the old bundle instead of running pnpm build.
Proposed fix
fn subtree_older_than(root: &Path, ceiling: SystemTime) -> bool {
+ let Ok(root_mtime) = root.metadata().and_then(|m| m.modified()) else {
+ return false;
+ };
+ if root_mtime > ceiling {
+ return false;
+ }
let Ok(read) = std::fs::read_dir(root) else {
return false;
};
- for entry in read.flatten() {
+ for entry in read {
+ let Ok(entry) = entry else {
+ return false;
+ };
let path = entry.path();
let Ok(meta) = entry.metadata() else {
return false;
};
+ let Ok(m) = meta.modified() else {
+ return false;
+ };
+ if m > ceiling {
+ return false;
+ }
if meta.is_dir() {
if !subtree_older_than(&path, ceiling) {
return false;
}
continue;
}
- let Ok(m) = meta.modified() else {
- return false;
- };
- if m > ceiling {
- return false;
- }
}
true
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn subtree_older_than(root: &Path, ceiling: SystemTime) -> bool { | |
| let Ok(read) = std::fs::read_dir(root) else { | |
| return false; | |
| }; | |
| for entry in read.flatten() { | |
| let path = entry.path(); | |
| let Ok(meta) = entry.metadata() else { | |
| return false; | |
| }; | |
| if meta.is_dir() { | |
| if !subtree_older_than(&path, ceiling) { | |
| return false; | |
| } | |
| continue; | |
| } | |
| let Ok(m) = meta.modified() else { | |
| return false; | |
| }; | |
| if m > ceiling { | |
| return false; | |
| } | |
| } | |
| fn subtree_older_than(root: &Path, ceiling: SystemTime) -> bool { | |
| let Ok(root_mtime) = root.metadata().and_then(|m| m.modified()) else { | |
| return false; | |
| }; | |
| if root_mtime > ceiling { | |
| return false; | |
| } | |
| let Ok(read) = std::fs::read_dir(root) else { | |
| return false; | |
| }; | |
| for entry in read { | |
| let Ok(entry) = entry else { | |
| return false; | |
| }; | |
| let path = entry.path(); | |
| let Ok(meta) = entry.metadata() else { | |
| return false; | |
| }; | |
| let Ok(m) = meta.modified() else { | |
| return false; | |
| }; | |
| if m > ceiling { | |
| return false; | |
| } | |
| if meta.is_dir() { | |
| if !subtree_older_than(&path, ceiling) { | |
| return false; | |
| } | |
| continue; | |
| } | |
| } |
🤖 Prompt for 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.
In `@llm-router/build.rs` around lines 132 - 153, Update subtree_older_than to
validate the modification time of root and every traversed child directory, not
only surviving files. Return false when any directory or file is newer than
ceiling, so deletions that update directory metadata invalidate dist_is_fresh
and trigger the build.
| const rootRef = useRef<HTMLDivElement>(null) | ||
| useEffect(() => { | ||
| const field = props.focusField?.[0] | ||
| if (!field || !rootRef.current) return | ||
| const el = rootRef.current.querySelector<HTMLElement>( | ||
| `[data-field="${CSS.escape(field)}"]`, | ||
| ) | ||
| el?.scrollIntoView({ block: 'center' }) | ||
| el?.focus() | ||
| }, [props.focusField]) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Focus an interactive control for focusField.
querySelector() returns the data-field wrapper. The current wrappers are a non-focusable <div> or <section>. The deep link scrolls, but .focus() leaves keyboard focus at the previous control.
Proposed fix
- const el = rootRef.current.querySelector<HTMLElement>(
+ const fieldRoot = rootRef.current.querySelector<HTMLElement>(
`[data-field="${CSS.escape(field)}"]`,
)
- el?.scrollIntoView({ block: 'center' })
- el?.focus()
+ fieldRoot?.scrollIntoView({ block: 'center' })
+ fieldRoot
+ ?.querySelector<HTMLElement>(
+ 'input, textarea, button, [role="combobox"], [tabindex]:not([tabindex="-1"])',
+ )
+ ?.focus()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const rootRef = useRef<HTMLDivElement>(null) | |
| useEffect(() => { | |
| const field = props.focusField?.[0] | |
| if (!field || !rootRef.current) return | |
| const el = rootRef.current.querySelector<HTMLElement>( | |
| `[data-field="${CSS.escape(field)}"]`, | |
| ) | |
| el?.scrollIntoView({ block: 'center' }) | |
| el?.focus() | |
| }, [props.focusField]) | |
| const rootRef = useRef<HTMLDivElement>(null) | |
| useEffect(() => { | |
| const field = props.focusField?.[0] | |
| if (!field || !rootRef.current) return | |
| const fieldRoot = rootRef.current.querySelector<HTMLElement>( | |
| `[data-field="${CSS.escape(field)}"]`, | |
| ) | |
| fieldRoot?.scrollIntoView({ block: 'center' }) | |
| fieldRoot | |
| ?.querySelector<HTMLElement>( | |
| 'input, textarea, button, [role="combobox"], [tabindex]:not([tabindex="-1"])', | |
| ) | |
| ?.focus() | |
| }, [props.focusField]) |
🤖 Prompt for 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.
In `@llm-router/ui/src/configuration/index.tsx` around lines 150 - 159, Update the
focusField handling in the useEffect to locate the interactive control contained
within the matching data-field wrapper, rather than focusing the non-focusable
wrapper itself. Preserve scrolling the matched field into view, then focus the
appropriate descendant control so keyboard focus moves to the linked input or
control.
| <div className="llmr-cfg-prompt-head"> | ||
| <span className="llmr-cfg-label">system prompt</span> | ||
| {typeof systemPrompt === 'string' ? ( | ||
| <button | ||
| type="button" | ||
| className="llmr-cfg-toggle" | ||
| onClick={() => set('system_prompt', undefined)} | ||
| > | ||
| use provider default | ||
| </button> | ||
| ) : ( | ||
| <button | ||
| type="button" | ||
| className="llmr-cfg-toggle" | ||
| // Prefill with the provider's own prompt: an override usually | ||
| // starts as an edit of it, not a blank page. | ||
| onClick={() => set('system_prompt', promptDefault ?? '')} | ||
| > | ||
| override | ||
| </button> | ||
| )} | ||
| </div> | ||
| {typeof systemPrompt === 'string' ? ( | ||
| <textarea | ||
| className="llmr-cfg-input llmr-cfg-textarea" | ||
| value={systemPrompt} | ||
| rows={4} | ||
| placeholder="override the provider-declared identity prompt" | ||
| onChange={(e) => set('system_prompt', e.target.value)} | ||
| /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Associate the system-prompt text area with its label.
The visible system prompt <span> is not a label. The <textarea> has no programmatic name for assistive technology.
Proposed fix
- <span className="llmr-cfg-label">system prompt</span>
+ <label
+ className="llmr-cfg-label"
+ htmlFor={`llmr-${id}-system-prompt`}
+ >
+ system prompt
+ </label>
@@
<textarea
+ id={`llmr-${id}-system-prompt`}
className="llmr-cfg-input llmr-cfg-textarea"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="llmr-cfg-prompt-head"> | |
| <span className="llmr-cfg-label">system prompt</span> | |
| {typeof systemPrompt === 'string' ? ( | |
| <button | |
| type="button" | |
| className="llmr-cfg-toggle" | |
| onClick={() => set('system_prompt', undefined)} | |
| > | |
| use provider default | |
| </button> | |
| ) : ( | |
| <button | |
| type="button" | |
| className="llmr-cfg-toggle" | |
| // Prefill with the provider's own prompt: an override usually | |
| // starts as an edit of it, not a blank page. | |
| onClick={() => set('system_prompt', promptDefault ?? '')} | |
| > | |
| override | |
| </button> | |
| )} | |
| </div> | |
| {typeof systemPrompt === 'string' ? ( | |
| <textarea | |
| className="llmr-cfg-input llmr-cfg-textarea" | |
| value={systemPrompt} | |
| rows={4} | |
| placeholder="override the provider-declared identity prompt" | |
| onChange={(e) => set('system_prompt', e.target.value)} | |
| /> | |
| <div className="llmr-cfg-prompt-head"> | |
| <label | |
| className="llmr-cfg-label" | |
| htmlFor={`llmr-${id}-system-prompt`} | |
| > | |
| system prompt | |
| </label> | |
| {typeof systemPrompt === 'string' ? ( | |
| <button | |
| type="button" | |
| className="llmr-cfg-toggle" | |
| onClick={() => set('system_prompt', undefined)} | |
| > | |
| use provider default | |
| </button> | |
| ) : ( | |
| <button | |
| type="button" | |
| className="llmr-cfg-toggle" | |
| // Prefill with the provider's own prompt: an override usually | |
| // starts as an edit of it, not a blank page. | |
| onClick={() => set('system_prompt', promptDefault ?? '')} | |
| > | |
| override | |
| </button> | |
| )} | |
| </div> | |
| {typeof systemPrompt === 'string' ? ( | |
| <textarea | |
| id={`llmr-${id}-system-prompt`} | |
| className="llmr-cfg-input llmr-cfg-textarea" | |
| value={systemPrompt} | |
| rows={4} | |
| placeholder="override the provider-declared identity prompt" | |
| onChange={(e) => set('system_prompt', e.target.value)} | |
| /> |
🤖 Prompt for 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.
In `@llm-router/ui/src/configuration/index.tsx` around lines 418 - 447, Associate
the “system prompt” label with the conditional textarea by giving the visible
label an identifying value and assigning the same value to the textarea’s id or
equivalent labeling attribute. Update the elements in the system-prompt section
without changing the existing toggle or prompt behavior.
Summary
Two UX tracks, one branch — the console's autonomous-activity cards and llm-router's configuration screen.
Console chat cards
ƒ engine::functions::list (prefix: "c…)),failedverb on errors (gate denials stay verb-less — the function never ran), duration.triggered …headers, TERMINAL / RAW JSON tabs, and a recovered REGISTRATION view (live harness rows first, else the transcript's ownengine::register_triggercall — notifications correlate by the subscription id embedded in their entry id, order-independent for idle-session wakes).CONDITIONS metchip on delivered fires — the harness evaluates gates before writing the fired record (trigger_deliver.rs), so a fire card existing proves the criteria passed.RegisterTriggerViewshows gatingconditionsas an ONLY IF section (the zod schema was silently stripping them).llm-router injectable configuration UI
llm-router/ui/per the injectable-console-ui SOP: config form registered overhost.configForms, assets built bybuild.rs(esbuild;SKIP_UI_BUILDescape) and embedded via the sharediii-console-uicrate.${PROVIDER_API_KEY}fix chip; partial references warn too; pure env refs render unmasked with a green note, real keys stay masked.Selectfor the default-provider and heuristic pickers (stale selections shown as(not connected)), stream budgets with human-readable echoes (= 5m,default · 32,000 tokens), system-prompt override flow that shows the provider-declared prompt and prefills it on override.Testing
Fixes MOT-4353
Summary by CodeRabbit
New Features
llm-router, including provider settings, credentials, routing preferences, stream budgets, and prompt customization.Improvements