Skip to content

Commit

Permalink
#5033 - Ability to configure recommenders interactively on the annota…
Browse files Browse the repository at this point in the history
…tion page

- Added interactive modes to Azure and Ollama too
  • Loading branch information
reckart committed Sep 17, 2024
1 parent 720899d commit 8dd3139
Show file tree
Hide file tree
Showing 21 changed files with 715 additions and 149 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!--
Licensed to the Technische Universität Darmstadt under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The Technische Universität Darmstadt
licenses this file to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
<wicket:head>
<style>
.small p {
margin-bottom: 0.25em;
}
</style>
</wicket:head>
<wicket:panel>
<form wicket:id="form">
<div class="form-floating mb-3" wicket:enclosure="preset">
<select wicket:id="preset" class="form-select" wicket:message="placeholder:preset"/>
<label wicket:for="preset">
<wicket:label key="preset" />
</label>
</div>

<div class="form-floating mb-3" wicket:enclosure="promptingMode">
<select wicket:id="promptingMode" class="form-select" wicket:message="placeholder:promptingMode"/>
<label wicket:for="promptingMode">
<wicket:label key="promptingMode" />
</label>
</div>

<div class="form-floating mb-3" wicket:enclosure="extractionMode">
<select wicket:id="extractionMode" class="form-select" wicket:message="placeholder:extractionMode"/>
<label wicket:for="extractionMode">
<wicket:label key="extractionMode" />
</label>
</div>

<div class="form-floating mb-3" wicket:enclosure="format">
<select wicket:id="format" class="form-select" wicket:message="placeholder:format"/>
<label wicket:for="format">
<wicket:label key="format" />
</label>
</div>

<div class="form-floating mb-3" wicket:enclosure="prompt">
<textarea wicket:id="prompt" class="form-control mb-2" rows="10" style="min-height: 10em;" wicket:message="placeholder:promptJinja"/>
<label wicket:for="prompt">
<wicket:message key="promptJinja"/>
</label>
<div wicket:id="promptHints" class="small text-muted"/>
</div>
</form>
</wicket:panel>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Licensed to the Technische Universität Darmstadt under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Technische Universität Darmstadt
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.tudarmstadt.ukp.inception.recommendation.imls.azureaiopenai;

import static de.tudarmstadt.ukp.inception.recommendation.imls.azureaiopenai.client.GenerateResponseFormat.JSON;
import static de.tudarmstadt.ukp.inception.recommendation.imls.support.llm.response.ExtractionMode.MENTIONS_FROM_JSON;
import static de.tudarmstadt.ukp.inception.support.lambda.HtmlElementEvents.CHANGE_EVENT;

import java.util.List;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.spring.injection.annot.SpringBean;

import de.tudarmstadt.ukp.inception.recommendation.api.RecommendationService;
import de.tudarmstadt.ukp.inception.recommendation.api.model.Recommender;
import de.tudarmstadt.ukp.inception.recommendation.api.recommender.AbstractTraitsEditor;
import de.tudarmstadt.ukp.inception.recommendation.api.recommender.RecommendationEngineFactory;
import de.tudarmstadt.ukp.inception.recommendation.imls.support.llm.prompt.PromptingModeSelect;
import de.tudarmstadt.ukp.inception.recommendation.imls.support.llm.response.ExtractionModeSelect;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaAjaxFormComponentUpdatingBehavior;
import de.tudarmstadt.ukp.inception.support.lambda.LambdaBehavior;
import de.tudarmstadt.ukp.inception.support.markdown.MarkdownLabel;

public class AzureAiOpenAiInteractionPanel
extends AbstractTraitsEditor
{
private static final long serialVersionUID = 1677442652521110324L;

private static final String MID_FORM = "form";
private static final String MID_PROMPT = "prompt";
private static final String MID_NAME = "name";
private static final String MID_PRESET = "preset";
private static final String MID_FORMAT = "format";
private static final String MID_EXTRACTION_MODE = "extractionMode";
private static final String MID_PROMPTING_MODE = "promptingMode";
private static final String MID_PROMPT_HINTS = "promptHints";

private @SpringBean RecommendationService recommendationService;
private @SpringBean RecommendationEngineFactory<AzureAiOpenAiRecommenderTraits> toolFactory;

private final CompoundPropertyModel<AzureAiOpenAiRecommenderTraits> traits;

public AzureAiOpenAiInteractionPanel(String aId, IModel<Recommender> aRecommender,
IModel<List<Preset>> aPresets)
{
super(aId, aRecommender);

setOutputMarkupId(true);

traits = CompoundPropertyModel.of(toolFactory.readTraits(aRecommender.getObject()));

var form = new Form<AzureAiOpenAiRecommenderTraits>(MID_FORM, traits)
{
private static final long serialVersionUID = -1;

@Override
protected void onSubmit()
{
super.onSubmit();
toolFactory.writeTraits(aRecommender.getObject(), traits.getObject());
}
};
form.setOutputMarkupPlaceholderTag(true);

var presetSelect = new DropDownChoice<Preset>(MID_PRESET);
presetSelect.setModel(Model.of());
presetSelect.setChoiceRenderer(new ChoiceRenderer<>(MID_NAME));
presetSelect.setChoices(aPresets);
presetSelect.add(new LambdaAjaxFormComponentUpdatingBehavior(CHANGE_EVENT,
_target -> applyPreset(form, presetSelect.getModelObject(), _target)));
form.add(presetSelect);

form.add(new TextArea<String>(MID_PROMPT));

var markdownLabel = new MarkdownLabel(MID_PROMPT_HINTS,
LoadableDetachableModel.of(this::getPromptHints));
markdownLabel.setOutputMarkupId(true);
form.add(markdownLabel);

form.add(new PromptingModeSelect(MID_PROMPTING_MODE)
.add(new LambdaAjaxFormComponentUpdatingBehavior(CHANGE_EVENT,
_target -> _target.add(markdownLabel))));

var responseFormat = new AzureAiOpenAiResponseFormatSelect(MID_FORMAT);
responseFormat.setOutputMarkupPlaceholderTag(true);
responseFormat.add(new LambdaAjaxFormComponentUpdatingBehavior(CHANGE_EVENT));
responseFormat.add(LambdaBehavior
.visibleWhen(traits.map(t -> t.getExtractionMode() != MENTIONS_FROM_JSON)));
form.add(responseFormat);

form.add(new ExtractionModeSelect(MID_EXTRACTION_MODE, traits.bind(MID_EXTRACTION_MODE),
getModel())
.add(new LambdaAjaxFormComponentUpdatingBehavior(CHANGE_EVENT,
_target -> actionExtractionModeChanged(markdownLabel,
responseFormat, _target))));

add(form);
}

private void actionExtractionModeChanged(MarkdownLabel markdownLabel,
AzureAiOpenAiResponseFormatSelect responseFormat, AjaxRequestTarget _target)
{
if (traits.getObject().getExtractionMode() == MENTIONS_FROM_JSON) {
traits.getObject().setFormat(JSON);
}

_target.add(markdownLabel, responseFormat);
}

private void applyPreset(Form<AzureAiOpenAiRecommenderTraits> aForm, Preset aPreset,
AjaxRequestTarget aTarget)
{
if (aPreset != null) {
var settings = traits.getObject();
settings.setPrompt(aPreset.getPrompt());
settings.setExtractionMode(aPreset.getExtractionMode());
settings.setFormat(aPreset.getFormat());
settings.setPromptingMode(aPreset.getPromptingMode());
}
aTarget.add(aForm);
}

private String getPromptHints()
{
return traits.getObject().getPromptingMode().getHints();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
*/
package de.tudarmstadt.ukp.inception.recommendation.imls.azureaiopenai;

import static java.util.Collections.emptyList;
import static org.apache.uima.cas.CAS.TYPE_NAME_STRING;

import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.util.ListModel;
import org.slf4j.Logger;
Expand Down Expand Up @@ -118,14 +119,26 @@ public boolean isMultipleRecommendationProvider()
return false;
}

@Override
public boolean isInteractive(Recommender aRecommender)
{
return readTraits(aRecommender).isInteractive();
}

@Override
public Panel createInteractionPanel(String aId, IModel<Recommender> aModel)
{
return new AzureAiOpenAiInteractionPanel(aId, aModel, new ListModel<>(getPresets()));
}

private List<Preset> getPresets()
{
try {
return presets.get().get();
}
catch (Exception e) {
LOG.error("Unable to load presets", e);
return Collections.emptyList();
return emptyList();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public class AzureAiOpenAiRecommenderTraits

private @JsonInclude(NON_EMPTY) Map<String, Object> options = new LinkedHashMap<String, Object>();

private boolean interactive;

public AuthenticationTraits getAuthentication()
{
return authentication;
Expand Down Expand Up @@ -124,4 +126,14 @@ public void setOptions(Map<String, Object> aOptions)
options.clear();
options.putAll(aOptions);
}

public boolean isInteractive()
{
return interactive;
}

public void setInteractive(boolean aInteractive)
{
interactive = aInteractive;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,45 +42,57 @@
<div wicket:id="authentication"/>
</div>
</div>
<div class="row form-row" wicket:enclosure="preset">
<div class="row form-row" wicket:enclosure="interactive">
<div class="offset-sm-3 col-sm-9">
<select wicket:id="preset" class="form-select"/>
</div>
<div class="form-check form-switch">
<input wicket:id="interactive" class="form-check-input" type="checkbox"/>
<label wicket:for="interactive" class="form-check-label">
<wicket:label key="interactive"/>
</label>
</div>
</div>
</div>
<div class="row form-row" wicket:enclosure="promptingMode">
<label wicket:for="promptingMode" class="col-sm-3 control-label">
<wicket:label key="promptingMode" />
</label>
<div class="col-sm-9">
<select wicket:id="promptingMode" class="form-select"/>
<div wicket:id="promptContainer">
<div class="row form-row" wicket:enclosure="preset">
<div class="offset-sm-3 col-sm-9">
<select wicket:id="preset" class="form-select"/>
</div>
</div>
</div>
<div class="row form-row" wicket:enclosure="format">
<label wicket:for="format" class="col-sm-3 control-label">
<wicket:label key="format" />
</label>
<div class="col-sm-9">
<select wicket:id="format" class="form-select"/>
<div class="row form-row" wicket:enclosure="promptingMode">
<label wicket:for="promptingMode" class="col-sm-3 control-label">
<wicket:label key="promptingMode" />
</label>
<div class="col-sm-9">
<select wicket:id="promptingMode" class="form-select"/>
</div>
</div>
</div>
<div class="row form-row" wicket:enclosure="extractionMode">
<label wicket:for="extractionMode" class="col-sm-3 control-label">
<wicket:label key="extractionMode" />
</label>
<div class="col-sm-9">
<select wicket:id="extractionMode" class="form-select"/>
<div class="row form-row" wicket:enclosure="format">
<label wicket:for="format" class="col-sm-3 control-label">
<wicket:label key="format" />
</label>
<div class="col-sm-9">
<select wicket:id="format" class="form-select"/>
</div>
</div>
</div>
<div class="row form-row" wicket:enclosure="prompt">
<label class="col-sm-3 col-form-label" wicket:for="prompt">
<wicket:message key="prompt"/>
<div class="small text-muted mb-2">
<i class="fas fa-torii-gate"></i> Jinja supported
<div class="row form-row" wicket:enclosure="extractionMode">
<label wicket:for="extractionMode" class="col-sm-3 control-label">
<wicket:label key="extractionMode" />
</label>
<div class="col-sm-9">
<select wicket:id="extractionMode" class="form-select"/>
</div>
</div>
<div class="row form-row" wicket:enclosure="prompt">
<label class="col-sm-3 col-form-label" wicket:for="prompt">
<wicket:message key="prompt"/>
<div class="small text-muted mb-2">
<i class="fas fa-torii-gate"></i> Jinja supported
</div>
<div wicket:id="promptHints" class="small text-muted"/>
</label>
<div class="col-sm-9">
<textarea wicket:id="prompt" class="form-control" rows="10"/>
</div>
<div wicket:id="promptHints" class="small text-muted"/>
</label>
<div class="col-sm-9">
<textarea wicket:id="prompt" class="form-control" rows="10"/>
</div>
</div>
</form>
Expand Down
Loading

0 comments on commit 8dd3139

Please sign in to comment.