talemate/talemate_frontend/src/components/AIClient.vue
veguAI 39bd02722d
0.25.0 (#100)
* flip title and name in recent scenes

* fix issue where a message could not be regenerated after applying continuity error fixes

* prompt tweaks

* allow json parameters for commands

* autocomplete improvements

* dialogue cleanup fixes

* fix issue with narrate after dialogue and llama3 (and other models that don't have a line break after the user prompt in their prompt template.

* expose ability to auto generate dialogue instructions to wsm character ux

* use b64_json response type

* move tag checks up so they match first

* fix typo

* prompt tweak

* api key support

* prompt tweaks

* editable parameters in prompt debugger / tester

* allow reseting of prompt params

* codemirror for prompt editor

* prompt tweaks

* more prompt debug tool tweaks

* some extra control for `context_history`

* new analytical preset (testing)

* add `join` and `llm_can_be_coerced` to jinja env

* support factual list summaries

* prompt tweaks to continuity check and fix

* new summarization method `facts` exposed to ux

* clamp mistral ai temperature according to their new requirements

* prompt tweaks

* better parsing of fixed dialogue response

* prompt tweaks

* fix intermittent empty meta issue

* history regen status progression and small ux tweaks

* summary entries should always be condensed

* google gemini support

* relock to install google-cloud-aiplatform for vertex ai inference

* fix instruction link

* better error handling of google safety validation and allow disabling of safety validation

* docs

* clarify credentials path requirements

* tweak error line identification

* handle quota limit error

* autocomplete ux wired to assistant plugin instead of command

* autocomplete narrative editing and fixes to autocomplete during dialog edit

* main input autocomplete tweaks

* allow new lines in main input

* 0.25.0 and relock

* fix issue with autocomplete elsewhere locking out main input

* better way to determine remote service

* prompt tweak

* fix rubberbanding issue when editing character attributes

* add open mistral 8x22

* fix continuity error check summary inclusion of target entry

* docs

* default context length to 8192

* linting
2024-05-05 22:16:03 +03:00

275 lines
No EOL
9.4 KiB
Vue

<template>
<div v-if="isConnected()">
<v-list v-for="(client, index) in state.clients" :key="index">
<v-list-item>
<v-divider v-if="index !== 0" class="mb-3"></v-divider>
<v-list-item-title>
<v-progress-circular v-if="client.status === 'busy'" indeterminate="disable-shrink" color="primary"
size="14"></v-progress-circular>
<v-icon v-else-if="client.status == 'warning'" color="orange" size="14">mdi-checkbox-blank-circle</v-icon>
<v-icon v-else-if="client.status == 'error'" color="red-darken-1" size="14">mdi-checkbox-blank-circle</v-icon>
<v-icon v-else-if="client.status == 'disabled'" color="grey-darken-2" size="14">mdi-checkbox-blank-circle</v-icon>
<v-icon v-else color="green" size="14">mdi-checkbox-blank-circle</v-icon>
{{ client.name }}
</v-list-item-title>
<v-list-item-subtitle class="text-caption" v-if="client.data.error_action != null">
<v-btn class="mt-1 mb-1" variant="tonal" :prepend-icon="client.data.error_action.icon" size="x-small" color="warning" @click.stop="callErrorAction(client, client.data.error_action)">
{{ client.data.error_action.title }}
</v-btn>
</v-list-item-subtitle>
<v-list-item-subtitle class="text-caption">
{{ client.model_name }}
</v-list-item-subtitle>
<v-list-item-subtitle class="text-caption">
{{ client.type }}
<v-chip label size="x-small" variant="outlined" class="ml-1">ctx {{ client.max_token_length }}</v-chip>
</v-list-item-subtitle>
<div density="compact">
<v-slider
hide-details
v-model="client.max_token_length"
:min="1024"
:max="128000"
:step="1024"
@update:modelValue="saveClientDelayed(client)"
@click.stop
density="compact"
></v-slider>
</div>
<v-list-item-subtitle class="text-center">
<v-tooltip text="No LLM prompt template for this model. Using default. Templates can be added in ./templates/llm-prompt" v-if="client.status === 'idle' && client.data && !client.data.has_prompt_template && client.data.meta.requires_prompt_template" max-width="200">
<template v-slot:activator="{ props }">
<v-icon x-size="14" class="mr-1" v-bind="props" color="orange">mdi-alert</v-icon>
</template>
</v-tooltip>
<v-tooltip :text="'Coercion active: ' + client.double_coercion" v-if="client.double_coercion" max-width="200">
<template v-slot:activator="{ props }">
<v-icon x-size="14" class="mr-1" v-bind="props" color="primary">mdi-account-lock-open</v-icon>
</template>
</v-tooltip>
<v-tooltip text="Edit client">
<template v-slot:activator="{ props }">
<v-btn size="x-small" class="mr-1" v-bind="props" variant="tonal" density="comfortable" rounded="sm" @click.stop="editClient(index)" icon="mdi-cogs"></v-btn>
</template>
</v-tooltip>
<v-tooltip text="Assign to all agents">
<template v-slot:activator="{ props }">
<v-btn size="x-small" class="mr-1" v-bind="props" variant="tonal" density="comfortable" rounded="sm" @click.stop="assignClientToAllAgents(index)" icon="mdi-transit-connection-variant"></v-btn>
</template>
</v-tooltip>
<v-tooltip text="Delete client">
<template v-slot:activator="{ props }">
<v-btn size="x-small" class="mr-1" v-bind="props" variant="tonal" density="comfortable" rounded="sm" @click.stop="deleteClient(index)" icon="mdi-close-thick"></v-btn>
</template>
</v-tooltip>
</v-list-item-subtitle>
</v-list-item>
</v-list>
<ClientModal :dialog="state.dialog" :formTitle="state.formTitle" @save="saveClient" @error="propagateError" @update:dialog="updateDialog"></ClientModal>
<v-alert type="warning" variant="tonal" v-if="state.clients.length === 0">You have no LLM clients configured. Add one.</v-alert>
<v-btn @click="openModal" elevation="0" prepend-icon="mdi-plus-box">Add client</v-btn>
</div>
</template>
<script>
import ClientModal from './ClientModal.vue';
export default {
components: {
ClientModal,
},
data() {
return {
saveDelayTimeout: null,
clientStatusCheck: null,
clientDeleted: false,
state: {
clients: [],
dialog: false,
currentClient: {
name: '',
type: '',
api_url: '',
model_name: '',
max_token_length: 8192,
double_coercion: null,
data: {
has_prompt_template: false,
}
}, // Add a new field to store the model name
formTitle: ''
}
}
},
inject: [
'getWebsocket',
'registerMessageHandler',
'isConnected',
'getAgents',
],
provide() {
return {
state: this.state
};
},
emits: [
'clients-updated',
'client-assigned',
'open-app-config',
],
methods: {
callErrorAction(client, action) {
if(action.action_name === 'openAppConfig') {
this.$emit('open-app-config', ...action.arguments);
}
},
configurationRequired() {
if(this.state.clients.length === 0) {
return true;
}
// cycle through clients and check if any are status 'error' or 'warning'
for (let i = 0; i < this.state.clients.length; i++) {
if (this.state.clients[i].status === 'error' || this.state.clients[i].status === 'warning') {
return true;
}
}
return false;
},
getActive() {
return this.state.clients.find(a => a.status === 'busy');
},
openModal() {
this.state.currentClient = {
name: 'TextGenWebUI',
type: 'textgenwebui',
api_url: 'http://localhost:5000',
model_name: '',
max_token_length: 8192,
data: {
has_prompt_template: false,
}
};
this.state.formTitle = 'Add Client';
this.state.dialog = true;
},
propagateError(error) {
this.$emit('error', error);
},
saveClientDelayed(client) {
client.dirty = true;
if (this.saveDelayTimeout) {
clearTimeout(this.saveDelayTimeout);
}
this.saveDelayTimeout = setTimeout(() => {
this.saveClient(client);
client.dirty = false;
}, 500);
},
saveClient(client) {
const index = this.state.clients.findIndex(c => c.name === client.name);
if (index === -1) {
this.state.clients.push(client);
} else {
this.state.clients[index] = client;
}
console.log("Saving client", client)
this.state.dialog = false; // Close the dialog after saving the client
this.$emit('clients-updated', this.state.clients);
},
editClient(index) {
this.state.currentClient = { ...this.state.clients[index] };
this.state.formTitle = 'Edit AI Client';
this.state.dialog = true;
},
deleteClient(index) {
if (window.confirm('Are you sure you want to delete this client?')) {
this.state.clients.splice(index, 1);
this.$emit('clients-updated', this.state.clients);
this.clientDeleted = true;
}
},
assignClientToAllAgents(index) {
let agents = this.getAgents();
let client = this.state.clients[index];
this.saveClient(client);
for (let i = 0; i < agents.length; i++) {
agents[i].client = client.name;
console.log("Assigning client", client.name, "to agent", agents[i].name);
}
this.$emit('client-assigned', agents);
},
updateDialog(newVal) {
this.state.dialog = newVal;
},
handleMessage(data) {
// Handle client_status message type
if (data.type === 'client_status') {
if(this.clientDeleted) {
// If we have just deleted a client, we need to wait for the next client_status message
this.clientDeleted = false;
return;
}
// Find the client with the given name
const client = this.state.clients.find(client => client.name === data.name);
if (client && !client.dirty) {
// Update the model name of the client
client.model_name = data.model_name;
client.model = client.model_name;
client.type = data.message;
client.status = data.status;
client.max_token_length = data.max_token_length;
client.api_url = data.api_url;
client.api_key = data.api_key;
client.double_coercion = data.data.double_coercion;
client.data = data.data;
} else if(!client) {
console.log("Adding new client", data);
this.state.clients.push({
name: data.name,
model_name: data.model_name,
model: data.model_name,
type: data.message,
status: data.status,
max_token_length: data.max_token_length,
api_url: data.api_url,
api_key: data.api_key,
double_coercion: data.data.double_coercion,
data: data.data,
});
// sort the clients by name
this.state.clients.sort((a, b) => (a.name > b.name) ? 1 : -1);
}
return;
}
}
},
created() {
this.registerMessageHandler(this.handleMessage);
},
}
</script>