Files
windmill/frontend/src/lib/components/copilot/RegexGen.svelte
T
556b4a41a1 feat: Support mistral anthropic for ai (#4692)
* wip: openai proxy and other ai proxy integration

* fixing migration script

* wip: support different ai provider in front, fix proxy openai

* wip: adding frontend ai provider

* updated copilot types

* wip: working on anthropic integration

* done AI proxy front

* adding new type and support for anthropic

* updating gitignore

* adding streaming response

* added streaming prompt

* push lib/gen

* wip: fixing anthropic

* anthropic fully supported

* fix backend missing var error and fully support stream event for anthropic

* remove gen directory

* fixing openapi file

* add support for mistral, and update create workspace components

* remove deref.json

* remove package-json

* openapi

* fix ui enable code

* added utility function for init workspace ai provider

* fix workspace switch bug

* update anthropic property and fixed frontend error

* fix workspace settings

* update error message and fix typo migration file

* chore: update openapi file

* fix dev file

* add .sqlx

* all

* update sqlx

---------

Co-authored-by: dieriba <t.dieriba@gmail.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Ruben Fiszel <ruben@rubenfiszel.com>
2024-11-14 14:24:35 +01:00

182 lines
4.7 KiB
Svelte

<script lang="ts">
import { base } from '$lib/base'
import { Button } from '../common'
import { getNonStreamingCompletion, type AiProviderTypes } from './lib'
import { sendUserToast } from '$lib/toast'
import Popup from '../common/popup/Popup.svelte'
import { copilotInfo } from '$lib/stores'
import { autoPlacement } from '@floating-ui/core'
import { ExternalLink, HistoryIcon, Wand2 } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
// state
let funcDesc: string = ''
let genLoading: boolean = false
let input: HTMLInputElement | undefined
let abortController: AbortController | undefined = undefined
const dispatch = createEventDispatcher()
async function onGenerate() {
if (funcDesc?.length <= 0) {
return
}
savePrompt()
genLoading = true
abortController = new AbortController()
const aiProvider = $copilotInfo.ai_provider
try {
const res = await getNonStreamingCompletion(
[
{
role: 'system',
content:
'Generate a regex pattern that one can use in a javascript Regex object. Output only the regex itself without any wrapping characters including the / characters. The regex should match the following:'
},
{
role: 'user',
content: funcDesc
}
],
abortController,
aiProvider as AiProviderTypes
)
dispatch('gen', { res: res, prompt: funcDesc })
funcDesc = ''
} catch (err) {
if (!abortController.signal.aborted) {
sendUserToast('Failed to generate regex: ' + err, true)
}
} finally {
genLoading = false
}
}
$: input && setTimeout(() => input?.focus(), 100)
let promptHistory: string[] = JSON.parse(getPromptsRegex() || '[]')
function getPromptsRegex(): string | undefined {
try {
return localStorage.getItem('prompts-regex') ?? undefined
} catch (e) {
console.error('error interacting with local storage', e)
}
return undefined
}
function savePrompt() {
if (promptHistory.includes(funcDesc)) {
return
}
promptHistory.unshift(funcDesc)
while (promptHistory.length > 5) {
promptHistory.pop()
}
try {
localStorage.setItem('prompts-regex', JSON.stringify(promptHistory))
} catch (e) {
console.error('error interacting with local storage', e)
}
}
function clearPromptHistory() {
promptHistory = []
try {
localStorage.setItem('prompts-regex', JSON.stringify(promptHistory))
} catch (e) {
console.error('error interacting with local storage', e)
}
}
</script>
<Popup
floatingConfig={{
middleware: [
autoPlacement({
allowedPlacements: ['bottom-start', 'bottom-end', 'top-start', 'top-end', 'top', 'bottom']
})
]
}}
let:close
>
<svelte:fragment slot="button">
<Button
title="Generate regexes from prompt"
btnClasses="text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
size="sm"
color={genLoading ? 'red' : 'light'}
spacingSize="md"
startIcon={{ icon: Wand2 }}
loading={genLoading}
propagateEvent
clickableWhileLoading
on:click={genLoading ? () => abortController?.abort() : () => {}}
/>
</svelte:fragment>
<div class="block text-primary z-[2000]">
{#if $copilotInfo.exists_ai_resource}
<div class="flex flex-col gap-4">
<div class="flex w-96">
<input
type="text"
bind:this={input}
bind:value={funcDesc}
on:keypress={({ key }) => {
if (key === 'Enter' && funcDesc?.length > 0) {
close(input || null)
onGenerate()
}
}}
placeholder={'Describe what the regex should do'}
/>
<Button
size="xs"
color="light"
buttonType="button"
btnClasses="!ml-2 text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700"
aria-label="Generate"
on:click={() => {
close(input || null)
onGenerate()
}}
disabled={funcDesc?.length <= 0}
iconOnly
startIcon={{ icon: Wand2 }}
/>
</div>
{#if promptHistory.length > 0}
<div class="w-96 flex flex-col gap-1">
{#each promptHistory as p}
<Button
size="xs2"
color="light"
btnClasses="justify-start overflow-x-scroll no-scrollbar"
startIcon={{ icon: HistoryIcon, classes: 'shrink-0' }}
on:click={() => {
funcDesc = p
}}>{p}</Button
>
{/each}
<button
class="underline text-xs text-start px-2 text-secondary font-normal"
on:click={clearPromptHistory}>clear history</button
>
</div>
{/if}
</div>
{:else}
<p class="text-sm">
Enable Windmill AI in the <a
href="{base}/workspace_settings?tab=ai"
target="_blank"
class="inline-flex flex-row items-center gap-1"
>
workspace settings <ExternalLink size={16} />
</a>
</p>
{/if}
</div>
</Popup>