-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
feat: Workflows function calling support
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<template> | ||
<div ref="builderEditor" class="BuilderEmbeddedCodeEditor"> | ||
<div ref="editorContainer" class="editorContainer"></div> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import * as monaco from "monaco-editor"; | ||
import "./builderEditorWorker"; | ||
import { inject, onMounted, onUnmounted, Ref, ref, toRefs, watch } from "vue"; | ||
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 10 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
|
||
import injectionKeys from "../injectionKeys"; | ||
const wf = inject(injectionKeys.core); | ||
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 13 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
|
||
const wfbm = inject(injectionKeys.builderManager); | ||
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.9)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.10)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.11)
Check warning on line 14 in src/ui/src/builder/BuilderEmbeddedCodeEditor.vue GitHub Actions / build (3.12)
|
||
const builderEditor: Ref<HTMLElement> = ref(null); | ||
const editorContainer: Ref<HTMLElement> = ref(null); | ||
let editor: monaco.editor.IStandaloneCodeEditor = null; | ||
// const editorCode = editor.getValue(); | ||
// <input | ||
// :value="props.modelValue" | ||
// @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" | ||
// /> | ||
const props = defineProps<{ | ||
modelValue: string; | ||
}>(); | ||
const { modelValue } = toRefs(props); | ||
const emit = defineEmits(["update:modelValue"]); | ||
// watch(modelValue, (newCode) => { | ||
// const currentCode = | ||
// editor.setValue(newCode); | ||
// }); | ||
onMounted(() => { | ||
const targetEl = editorContainer.value; | ||
editor = monaco.editor.create(targetEl, { | ||
value: modelValue.value, | ||
language: "json", | ||
minimap: { | ||
enabled: false, | ||
}, | ||
lineNumbers: "off", | ||
scrollbar: { | ||
vertical: "auto", | ||
horizontal: "auto", | ||
}, | ||
fontSize: 12, | ||
folding: false, | ||
// theme: "", | ||
}); | ||
editor.getModel().onDidChangeContent(() => { | ||
const newCode = editor.getValue(); | ||
emit("update:modelValue", newCode); | ||
}); | ||
window.addEventListener("resize", updateDimensions.bind(this)); | ||
}); | ||
function updateDimensions() { | ||
editor.layout(); | ||
} | ||
onUnmounted(() => { | ||
editor.dispose(); | ||
window.removeEventListener("resize", updateDimensions.bind(this)); | ||
}); | ||
</script> | ||
|
||
<style scoped> | ||
@import "./sharedStyles.css"; | ||
.BuilderEditor { | ||
} | ||
.editorContainer { | ||
height: 200px; | ||
} | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,287 @@ | ||
<template> | ||
<div | ||
class="BuilderFieldsTools colorTransformer" | ||
:data-automation-key="props.fieldKey" | ||
> | ||
<div class="tools"> | ||
<div | ||
v-for="(tool, toolName) in tools" | ||
:key="toolName" | ||
class="tool" | ||
@click="editTool(toolName)" | ||
> | ||
<div class="toolName">{{ toolName }}</div> | ||
<button class="delete" @click.stop="deleteTool(toolName)"> | ||
<i class="material-symbols-outlined">delete</i> | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<WdsButton size="small" @click="resetAndShowToolFormModal"> | ||
<i class="material-symbols-outlined">add</i> | ||
Add tool</WdsButton | ||
> | ||
<BuilderModal | ||
v-if="toolForm.isShown" | ||
:close-action="customHandlerModalCloseAction" | ||
icon="add" | ||
modal-title="Add tool" | ||
> | ||
<WdsDropdownInput v-model="toolForm.type"> | ||
<option value="function">Function</option> | ||
<option value="graph">Knowledge graph</option> | ||
</WdsDropdownInput> | ||
<WdsTextInput v-model="toolForm.name"></WdsTextInput> | ||
<template v-if="toolForm.type == 'function'"> | ||
<BuilderEmbeddedCodeEditor | ||
v-model="toolForm.code" | ||
></BuilderEmbeddedCodeEditor> | ||
</template> | ||
<template v-if="toolForm.type == 'graph'"> | ||
<WdsTextInput v-model="toolForm.graphId"></WdsTextInput> | ||
</template> | ||
<div> | ||
<WdsButton @click="saveToolForm">Save</WdsButton> | ||
</div> | ||
</BuilderModal> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { toRefs, inject, computed, ref } from "vue"; | ||
import { Component, FieldControl } from "@/writerTypes"; | ||
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.9)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.9)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 52 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
|
||
import { useComponentActions } from "./useComponentActions"; | ||
import injectionKeys from "../injectionKeys"; | ||
import WdsButton from "@/wds/WdsButton.vue"; | ||
import BuilderModal, { ModalAction } from "./BuilderModal.vue"; | ||
import WdsTextareaInput from "@/wds/WdsTextareaInput.vue"; | ||
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.9)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.9)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.10)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.11)
Check warning on line 57 in src/ui/src/builder/BuilderFieldsTools.vue GitHub Actions / build (3.12)
|
||
import WdsTextInput from "@/wds/WdsTextInput.vue"; | ||
import WdsDropdownInput from "@/wds/WdsDropdownInput.vue"; | ||
import BuilderEmbeddedCodeEditor from "./BuilderEmbeddedCodeEditor.vue"; | ||
type FunctionTool = { | ||
type: "function"; | ||
description: string; | ||
parameters: Record< | ||
string, | ||
{ | ||
type: string; | ||
description: string; | ||
} | ||
>; | ||
}; | ||
type GraphTool = { | ||
type: "graph"; | ||
graph_ids: string[]; | ||
}; | ||
type Tool = FunctionTool | GraphTool; | ||
type ToolForm = { | ||
isShown: boolean; | ||
type: "function" | "graph"; | ||
originalName?: string; | ||
name: string; | ||
code: string; | ||
graphId: string; | ||
}; | ||
const wf = inject(injectionKeys.core); | ||
const ssbm = inject(injectionKeys.builderManager); | ||
const { setContentValue } = useComponentActions(wf, ssbm); | ||
const initFunctionToolCode = ` | ||
{ | ||
"description": "Gets info for an employee, given an employee id", | ||
"parameters": { | ||
"id": {"type": "string", "description": "Id of the employee"} | ||
} | ||
} | ||
`.trim(); | ||
const toolFormInitValue = { | ||
isShown: false, | ||
type: "function" as "function" | "graph", | ||
name: "new_tool", | ||
code: initFunctionToolCode, | ||
graphId: "999-999", | ||
}; | ||
const toolForm = ref<ToolForm>(toolFormInitValue); | ||
const props = defineProps<{ | ||
componentId: Component["id"]; | ||
fieldKey: string; | ||
}>(); | ||
const { componentId, fieldKey } = toRefs(props); | ||
const component = computed(() => wf.getComponentById(componentId.value)); | ||
const tools = computed<Record<string, Tool>>(() => { | ||
let value = {}; | ||
try { | ||
value = JSON.parse(component.value.content[fieldKey.value]); | ||
} catch { | ||
value = {}; | ||
} | ||
return value; | ||
}); | ||
function resetAndShowToolFormModal() { | ||
toolForm.value = { | ||
...toolFormInitValue, | ||
isShown: true, | ||
}; | ||
} | ||
function getToolFromForm(): Tool { | ||
const { type, code, graphId } = toolForm.value; | ||
if (type == "function") { | ||
return { | ||
...JSON.parse(code), | ||
type, | ||
}; | ||
} | ||
if (type == "graph") { | ||
return { | ||
type, | ||
graph_ids: [graphId], | ||
}; | ||
} | ||
throw "Unexpected tool type."; | ||
} | ||
function validateToolForm(form: ToolForm): string[] { | ||
let errors = []; | ||
const { originalName, name } = form; | ||
if (!name) { | ||
errors.push("The name cannot be empty."); | ||
} | ||
if ( | ||
Object.keys(tools.value).includes(name) && | ||
!(originalName || originalName == name) | ||
) { | ||
errors.push("An existing tool with the specified name already exists."); | ||
} | ||
return errors; | ||
} | ||
function saveToolForm() { | ||
const formErrors = validateToolForm(toolForm.value); | ||
if (formErrors.length > 0) { | ||
formErrors.forEach(alert); | ||
return; | ||
} | ||
let toolFromForm: ReturnType<typeof getToolFromForm>; | ||
try { | ||
toolFromForm = getToolFromForm(); | ||
} catch { | ||
alert("Incorrect tool definition"); | ||
return; | ||
} | ||
const newFieldValue = JSON.stringify({ | ||
...tools.value, | ||
...(toolForm.value.originalName | ||
? { [toolForm.value.originalName]: undefined } | ||
: {}), | ||
[toolForm.value.name]: toolFromForm, | ||
}); | ||
setContentValue(component.value.id, fieldKey.value, newFieldValue); | ||
toolForm.value.isShown = false; | ||
} | ||
function getFormFromToolEntry(toolName: string, tool: Tool): ToolForm { | ||
const { type } = tool; | ||
if (type == "function") { | ||
return { | ||
isShown: true, | ||
type: "function", | ||
originalName: toolName, | ||
name: toolName, | ||
graphId: "", | ||
code: JSON.stringify(tool, undefined, 2), | ||
}; | ||
} | ||
if (type == "graph") { | ||
return { | ||
isShown: true, | ||
type: "graph", | ||
originalName: toolName, | ||
name: toolName, | ||
graphId: tool.graph_ids?.[0], | ||
code: "", | ||
}; | ||
} | ||
throw "Unexpected tool type;"; | ||
} | ||
function editTool(toolName: string) { | ||
const tool = tools.value?.[toolName]; | ||
if (!tool) return; | ||
toolForm.value = getFormFromToolEntry(toolName, tool); | ||
} | ||
function deleteTool(toolName: string) { | ||
const newFieldValue = JSON.stringify({ | ||
...tools.value, | ||
[toolName]: undefined, | ||
}); | ||
setContentValue(component.value.id, fieldKey.value, newFieldValue); | ||
} | ||
const customHandlerModalCloseAction: ModalAction = { | ||
desc: "Close", | ||
fn: () => { | ||
toolForm.value.isShown = false; | ||
}, | ||
}; | ||
</script> | ||
|
||
<style scoped> | ||
@import "./sharedStyles.css"; | ||
.BuilderFieldsTools { | ||
--separatorColor: var(--builderSeparatorColor); | ||
--intensifiedButtonColor: red; | ||
} | ||
.tools { | ||
margin-bottom: 8px; | ||
} | ||
.tool { | ||
display: flex; | ||
align-items: center; | ||
min-height: 38px; | ||
padding-top: 4px; | ||
margin-top: 4px; | ||
cursor: pointer; | ||
} | ||
.tool:not(:first-of-type) { | ||
border-top: 1px solid var(--builderSeparatorColor); | ||
} | ||
.tool .toolName { | ||
flex: 1 0 auto; | ||
} | ||
.tool .delete { | ||
display: none; | ||
background: var(--builderBackgroundColor); | ||
padding: 2px 4px 2px 4px; | ||
font-size: 1.1rem; | ||
} | ||
.tool:hover .delete { | ||
display: block; | ||
} | ||
.addToolTextarea { | ||
font-family: monospace; | ||
} | ||
</style> | ||
|
||
<style scoped> | ||
@import "./sharedStyles.css"; | ||
</style> |