Files
windmill/frontend/src/lib/components/JsonEditor.svelte
centdix 5f2101a32b feat(aiagent): allow giving messages history (#7395)
* handle messages array for ai agent

* better

* nit

* make tool_calls and tool_call_id nullable

* fix empty json behavior

* nits

* cleaning

* feat(backend): replace messages/messages_context_length with history oneOf field

Replace the separate 'messages' array and 'messages_context_length' fields with
a single 'history' field that uses a oneOf discriminator.

The 'history' field can be either:
- 'auto' mode: automatically manages conversation history with memory, takes a
  'context_length' number parameter
- 'manual' mode: bypasses memory and uses explicitly provided messages array

Backward compatibility is maintained: if 'messages_context_length' is provided
in the old schema format, it is automatically converted to 'auto' mode with the
specified context_length.

Co-authored-by: centdix <centdix@users.noreply.github.com>

* feat(frontend): replace messages/messages_context_length with history oneOf field

Replace the separate 'messages' array and 'messages_context_length' fields with
a single 'history' field in the AI agent schema.

The 'history' field uses a oneOf discriminator with two modes:
- 'auto': { mode: 'auto', context_length: number } - automatically manages
  conversation history with memory
- 'manual': { mode: 'manual', messages: array } - bypasses memory and uses
  explicitly provided messages

The schema includes comprehensive descriptions for each mode explaining the
behavior. The order array has been updated to include 'history' in place of
the old 'messages_context_length' and 'messages' fields.

Co-authored-by: centdix <centdix@users.noreply.github.com>

* fix(frontend): add support for 'mode' discriminator in oneOf rendering

Update ArgInput.svelte to properly handle oneOf schemas that use 'mode' as the
discriminator field, in addition to the existing 'kind' and 'label' support.

Changes:
- Updated tagKey derivation to check for 'mode' first, then 'kind', then 'label'
- Added 'mode' to the onOneOfChange function to track mode changes
- Added 'mode' to the list of keys excluded from enum validation
- Added 'mode' to hiddenArgs to prevent it from being shown in the form
- Added title fields to the history oneOf variants in flowInfers.ts

This allows the AI agent's history field to properly render with toggle buttons
for 'auto' and 'manual' modes.

Co-authored-by: centdix <centdix@users.noreply.github.com>

* fixes

* frontend fix

* nit

* cleaning

* cleaning

* better

* reword

* reword

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: centdix <centdix@users.noreply.github.com>
2025-12-20 15:43:44 +02:00

96 lines
2.3 KiB
Svelte

<script lang="ts">
import '@codingame/monaco-vscode-standalone-json-language-features'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
import { createEventDispatcher, untrack } from 'svelte'
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
import Button from './common/button/Button.svelte'
import { twMerge } from 'tailwind-merge'
import { inputBorderClass } from './text_input/TextInput.svelte'
interface Props {
code: string | undefined
value?: any
error?: string
editor?: SimpleEditor | undefined
small?: boolean
loadAsync?: boolean
class?: string | undefined
disabled?: boolean
fixedOverflowWidgets?: boolean
}
let {
code = $bindable(),
value = $bindable(undefined),
error = $bindable(),
editor = $bindable(undefined),
small = true,
loadAsync = false,
class: clazz = undefined,
disabled = false,
fixedOverflowWidgets = true
}: Props = $props()
let tooBig = $derived(code && code?.length > 1000000)
let loadTooBigAnyway = $state(false)
let focused = $state(false)
const dispatch = createEventDispatcher()
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
function parseJson() {
try {
if (code == '') {
value = undefined
} else {
value = JSON.parse(code ?? '')
}
dispatchIfMounted('changeValue', value)
error = ''
} catch (e) {
error = e.message
}
}
$effect(() => {
code != undefined && untrack(() => parseJson())
})
</script>
{#if tooBig && !loadTooBigAnyway}
<div class="flex-1 text-sm">
JSON is too big
<Button size="xs2" variant="default" on:click={() => (loadTooBigAnyway = true)}>
Load anyway
</Button>
</div>
{:else}
<div class="flex flex-col w-full">
<div
class={twMerge(
'w-full rounded-md overflow-auto',
inputBorderClass({ error: !!error, forceFocus: focused })
)}
>
<SimpleEditor
{loadAsync}
{small}
on:focus={() => (dispatch('focus'), (focused = true))}
on:blur={() => (dispatch('blur'), (focused = false))}
bind:this={editor}
on:change
autoHeight
lang="json"
bind:code
class={clazz}
{disabled}
{fixedOverflowWidgets}
renderLineHighlight="none"
/>
</div>
{#if error != ''}
<span class="text-red-600 text-xs mt-1">{error}</span>
{/if}
</div>
{/if}