@christianriedl/utils 1.0.140 → 1.0.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/iOpenAI.d.ts +8 -3
- package/package.json +1 -1
- package/src/components/OpenAI.vue +21 -7
- package/src/components/OpenAIConfig.vue +90 -28
- package/src/components/OpenAIFilter.vue +99 -0
- package/src/components/OpenAIMetaData.vue +11 -12
- package/src/components/SettingsLine.vue +13 -5
- package/src/views/SettingsPage.vue +2 -2
package/dist/iOpenAI.d.ts
CHANGED
|
@@ -81,9 +81,13 @@ export interface IComparisonFilter {
|
|
|
81
81
|
value: ItemType;
|
|
82
82
|
}
|
|
83
83
|
export interface ICompoundFilter {
|
|
84
|
-
filters: IComparisonFilter[]
|
|
84
|
+
filters: IComparisonFilter[];
|
|
85
85
|
type: 'and' | 'or';
|
|
86
86
|
}
|
|
87
|
+
export interface IOpenAIFilter {
|
|
88
|
+
fileFilter: ICompoundFilter;
|
|
89
|
+
vectorStoreFilter: Dictionary<string>;
|
|
90
|
+
}
|
|
87
91
|
export interface IOpenAIOptions {
|
|
88
92
|
model: string;
|
|
89
93
|
instructions?: string | null;
|
|
@@ -98,8 +102,8 @@ export interface IOpenAIService {
|
|
|
98
102
|
log: ILogger;
|
|
99
103
|
options: IOpenAIOptions;
|
|
100
104
|
modelNames: string[];
|
|
101
|
-
|
|
102
|
-
response(prompt: string | string[], tools?: string[], imageUrl?: string, json?: ISchema, fileSearchFilter?:
|
|
105
|
+
initializeModelNames(): Promise<boolean>;
|
|
106
|
+
response(prompt: string | string[], tools?: string[], imageUrl?: string, json?: ISchema, fileSearchFilter?: IOpenAIFilter): Promise<IResponseResult>;
|
|
103
107
|
clearContext(): void;
|
|
104
108
|
getModels(): Promise<string[]>;
|
|
105
109
|
}
|
|
@@ -139,6 +143,7 @@ export interface IOpenAIServiceWithVectorStore extends IOpenAIServiceWithTools {
|
|
|
139
143
|
vsMetadata: IDataItem[];
|
|
140
144
|
fileAttributes: IDataItem[];
|
|
141
145
|
initializeVectorStore(): Promise<boolean>;
|
|
146
|
+
initializeVectorStoreFiles(): Promise<boolean>;
|
|
142
147
|
getVectorStore(vectorStoreId: string): IVectorStore | undefined;
|
|
143
148
|
addVectorStore(vectorStoreName: string, metadata?: Dictionary<string>): Promise<boolean>;
|
|
144
149
|
addFile(fileUrl: string | File, vectorStoreName: string, attributes?: Dictionary<ItemType>, wait?: boolean): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { inject, ref, toRef, computed, onUnmounted } from 'vue';
|
|
3
3
|
import { appStateSymbol, appConfigSymbol, ESize, EDevice } from '@christianriedl/utils';
|
|
4
|
-
import { getOpenAISymbol,
|
|
4
|
+
import { getOpenAISymbol, IOpenAIServiceWithVectorStore, IResponseResult, IOpenAIFilter } from '@christianriedl/utils';
|
|
5
5
|
import Microphone from '@christianriedl/utils/src/components/Microphone.vue';
|
|
6
6
|
import Camera from '@christianriedl/utils/src/components/Camera.vue';
|
|
7
7
|
import OpenAIConfig from '@christianriedl/utils/src/components/OpenAIConfig.vue';
|
|
8
|
+
import OpenAIFilter from '@christianriedl/utils/src/components/OpenAIFilter.vue';
|
|
8
9
|
|
|
9
10
|
const props = defineProps<{ prompt: string, onlymicrophone?: boolean, tools?: string[] }>();
|
|
10
11
|
const emits = defineEmits<{
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
}>();
|
|
14
15
|
|
|
15
16
|
const getOpenAI = inject(getOpenAISymbol)!;
|
|
16
|
-
const openAI = getOpenAI();
|
|
17
|
+
const openAI = getOpenAI() as IOpenAIServiceWithVectorStore;
|
|
17
18
|
const appState = inject(appStateSymbol)!;
|
|
18
19
|
const isMobile = appState.isMobile && (appState.device != EDevice.iPad);
|
|
19
20
|
const appConfig = inject(appConfigSymbol)!;
|
|
@@ -23,11 +24,13 @@
|
|
|
23
24
|
const isCamera = ref(false);
|
|
24
25
|
const isUpload = ref(false);
|
|
25
26
|
const showConfig = ref(false);
|
|
27
|
+
const showFilter = ref(false);
|
|
26
28
|
const files = ref<File[]>([]);
|
|
27
29
|
const imageUrl = ref("");
|
|
28
30
|
const camera = ref<InstanceType<typeof Camera>>();
|
|
29
31
|
const htmlText = ref('');
|
|
30
32
|
const tools = ref(props.tools);
|
|
33
|
+
const filter = ref<IOpenAIFilter | undefined>(undefined);
|
|
31
34
|
|
|
32
35
|
onUnmounted(() => {
|
|
33
36
|
openAI.clearContext();
|
|
@@ -52,10 +55,10 @@
|
|
|
52
55
|
}
|
|
53
56
|
else {
|
|
54
57
|
if (tools.value !== undefined) {
|
|
55
|
-
answer = await openAI.response(question.value, tools.value);
|
|
58
|
+
answer = await openAI.response(question.value, tools.value, undefined, undefined, filter.value);
|
|
56
59
|
}
|
|
57
60
|
else {
|
|
58
|
-
answer = await openAI.response(question.value);
|
|
61
|
+
answer = await openAI.response(question.value, undefined, undefined, undefined, filter.value);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
if (answer && answer.text) {
|
|
@@ -150,6 +153,14 @@
|
|
|
150
153
|
return '';
|
|
151
154
|
}
|
|
152
155
|
}
|
|
156
|
+
function setFilter(newFilter: IOpenAIFilter) {
|
|
157
|
+
filter.value = newFilter;
|
|
158
|
+
showFilter.value = false;
|
|
159
|
+
}
|
|
160
|
+
async function showConfigDialog() {
|
|
161
|
+
if (await openAI.initializeModelNames())
|
|
162
|
+
showConfig.value = true;
|
|
163
|
+
}
|
|
153
164
|
</script>
|
|
154
165
|
|
|
155
166
|
<template>
|
|
@@ -157,7 +168,7 @@
|
|
|
157
168
|
<v-container v-else fluid class="bg-office">
|
|
158
169
|
<v-row dense align="center" class="font-weight-bold">
|
|
159
170
|
<v-col :cols="qcols">
|
|
160
|
-
<v-text-field name="subject" type="text" v-model="question" density="compact" hide-details></v-text-field>
|
|
171
|
+
<v-text-field name="subject" type="text" v-model="question" density="compact" hide-details append-inner-icon="$search" @click:appendInner="showFilter=true"></v-text-field>
|
|
161
172
|
</v-col>
|
|
162
173
|
<v-col :cols="12 - qcols">
|
|
163
174
|
<v-btn class="aibutton" @click="onComplete">AI</v-btn>
|
|
@@ -168,7 +179,7 @@
|
|
|
168
179
|
<v-btn variant="tonal" class="bg-office aibutton" @click="isUpload=true;replyLines=[]">
|
|
169
180
|
<v-icon size="large" icon="$image" color="primary"></v-icon>
|
|
170
181
|
</v-btn>
|
|
171
|
-
<v-btn variant="tonal" class="bg-office aibutton" @click="
|
|
182
|
+
<v-btn variant="tonal" class="bg-office aibutton" @click="showConfigDialog">
|
|
172
183
|
<v-icon size="large" icon="$settings" color="primary"></v-icon>
|
|
173
184
|
</v-btn>
|
|
174
185
|
</v-col>
|
|
@@ -192,7 +203,7 @@
|
|
|
192
203
|
<img width="100%" :src="imageUrl">
|
|
193
204
|
</v-col>
|
|
194
205
|
</v-row>
|
|
195
|
-
<v-row v-if="htmlText" dense
|
|
206
|
+
<v-row v-if="htmlText" dense>
|
|
196
207
|
<v-col cols="12">
|
|
197
208
|
<div v-html="htmlText"></div>
|
|
198
209
|
</v-col>
|
|
@@ -203,6 +214,9 @@
|
|
|
203
214
|
<v-dialog v-model="showConfig" :fullscreen="isMobile">
|
|
204
215
|
<OpenAIConfig :tools="tools" :ismobile="isMobile" @use="onUse" @back="showConfig=false"></OpenAIConfig>
|
|
205
216
|
</v-dialog>
|
|
217
|
+
<v-dialog v-model="showFilter" :fullscreen="isMobile">
|
|
218
|
+
<OpenAIFilter :vsmetadata="openAI.vsMetadata" :fileattributes="openAI.fileAttributes" :filter="filter" @back="setFilter"></OpenAIFilter>
|
|
219
|
+
</v-dialog>
|
|
206
220
|
</v-container>
|
|
207
221
|
</template>
|
|
208
222
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { inject, ref, reactive } from 'vue';
|
|
3
|
-
import { appConfigSymbol, AppConfig, Dictionary } from '@christianriedl/utils';
|
|
3
|
+
import { appConfigSymbol, AppConfig, Dictionary, IDataItem, ItemType, IVectorFile } from '@christianriedl/utils';
|
|
4
4
|
import { getOpenAISymbol, IOpenAIServiceWithVectorStore, IOpenAIAppConfig, IOpenAIOptions, ITool, IMcpTool, IFileSearchTool } from '@christianriedl/utils';
|
|
5
5
|
import OpenAIMetaData from '@christianriedl/utils/src/components/OpenAIMetaData.vue';
|
|
6
6
|
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
const selectedVectorStoreFile = ref('');
|
|
33
33
|
const showFunctions = ref(false);
|
|
34
34
|
const showMetaData = ref(false);
|
|
35
|
-
const
|
|
36
|
-
const metadata = ref<
|
|
35
|
+
const editVectorStoreMetaData = ref(false);
|
|
36
|
+
const metadata = ref<IDataItem[]>([]);
|
|
37
37
|
const toolName = ref('');
|
|
38
38
|
const toolFunctions = reactive<IFunction[]>([]);
|
|
39
39
|
let fileHandle: FileSystemFileHandle | null = null;
|
|
@@ -147,12 +147,17 @@
|
|
|
147
147
|
fileSearch[name] = [];
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
function vectorStoreSelected() {
|
|
151
|
-
|
|
150
|
+
async function vectorStoreSelected() {
|
|
151
|
+
|
|
152
|
+
selectedVectorStoreFile.value = 'Get vectorestore files ...';
|
|
153
|
+
if (!await openAI.initializeVectorStoreFiles())
|
|
154
|
+
return;
|
|
152
155
|
const vs = openAI.vectorStore![selectedVectorStoreName.value];
|
|
153
156
|
vectorStoreFiles.value.splice(0, vectorStoreFiles.value.length);
|
|
154
157
|
if (vs) {
|
|
155
|
-
|
|
158
|
+
for (const key in vs.files) {
|
|
159
|
+
vectorStoreFiles.value.splice(0, 0, vs.files[key].filename);
|
|
160
|
+
}
|
|
156
161
|
if (vectorStoreFiles.value.length > 0) {
|
|
157
162
|
selectedVectorStoreFile.value = vectorStoreFiles.value[0];
|
|
158
163
|
}
|
|
@@ -163,7 +168,12 @@
|
|
|
163
168
|
alert('Vector Store already exists, add a new name !');
|
|
164
169
|
return;
|
|
165
170
|
}
|
|
166
|
-
|
|
171
|
+
const metadata: Dictionary<string> = {};
|
|
172
|
+
for (const md of openAI.vsMetadata) {
|
|
173
|
+
metadata[md.name] = md.default.toString();
|
|
174
|
+
}
|
|
175
|
+
showMetaData.value = true;
|
|
176
|
+
if (!openAI.addVectorStore(selectedVectorStoreName.value, metadata)) {
|
|
167
177
|
alert('Vector Store not added, error ! ');
|
|
168
178
|
return;
|
|
169
179
|
}
|
|
@@ -179,8 +189,14 @@
|
|
|
179
189
|
async function editVectorStore() {
|
|
180
190
|
const vs = openAI.vectorStore![selectedVectorStoreName.value];
|
|
181
191
|
if (vs) {
|
|
182
|
-
|
|
183
|
-
metadata.value =
|
|
192
|
+
editVectorStoreMetaData.value = true;
|
|
193
|
+
metadata.value = [];
|
|
194
|
+
for (const md of openAI.vsMetadata) {
|
|
195
|
+
const mdCopy = Object.assign({}, md);
|
|
196
|
+
if (vs.metadata && vs.metadata[md.name])
|
|
197
|
+
mdCopy.value = vs.metadata[md.name];
|
|
198
|
+
metadata.value.push(mdCopy);
|
|
199
|
+
}
|
|
184
200
|
showMetaData.value = true;
|
|
185
201
|
}
|
|
186
202
|
}
|
|
@@ -189,15 +205,27 @@
|
|
|
189
205
|
alert('File already exists, add a new file path or url !');
|
|
190
206
|
return;
|
|
191
207
|
}
|
|
208
|
+
const attributes: Dictionary<ItemType> = {};
|
|
209
|
+
for (const att of openAI.fileAttributes) {
|
|
210
|
+
attributes[att.name] = att.default;
|
|
211
|
+
}
|
|
212
|
+
const vs = openAI.vectorStore![selectedVectorStoreName.value];
|
|
213
|
+
if (vs && vs.metadata) {
|
|
214
|
+
for (const key in vs.metadata) {
|
|
215
|
+
attributes[key] = vs.metadata[key];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
192
218
|
if (fileHandle) {
|
|
193
219
|
const file = await fileHandle.getFile();
|
|
194
220
|
fileHandle = null;
|
|
195
|
-
if (!await openAI.addFile(file, selectedVectorStoreName.value)) {
|
|
221
|
+
if (!await openAI.addFile(file, selectedVectorStoreName.value, attributes)) {
|
|
196
222
|
alert('File not added, error ! ');
|
|
197
223
|
}
|
|
198
224
|
}
|
|
199
|
-
|
|
200
|
-
|
|
225
|
+
else {
|
|
226
|
+
if (!await openAI.addFile(selectedVectorStoreFile.value, selectedVectorStoreName.value, attributes)) {
|
|
227
|
+
alert('File not added, error ! ');
|
|
228
|
+
}
|
|
201
229
|
}
|
|
202
230
|
}
|
|
203
231
|
async function deleteFile() {
|
|
@@ -216,29 +244,63 @@
|
|
|
216
244
|
async function editFile() {
|
|
217
245
|
const file = openAI.getFile(selectedVectorStoreFile.value);
|
|
218
246
|
if (file) {
|
|
219
|
-
|
|
220
|
-
metadata.value =
|
|
247
|
+
editVectorStoreMetaData.value = false;
|
|
248
|
+
metadata.value = [];
|
|
249
|
+
const vs = openAI.vectorStore![selectedVectorStoreName.value];
|
|
250
|
+
for (const md of openAI.fileAttributes) {
|
|
251
|
+
const mdCopy = Object.assign({}, md);
|
|
252
|
+
if (file.attributes && file.attributes[md.name])
|
|
253
|
+
mdCopy.value = file.attributes[md.name];
|
|
254
|
+
else if (vs && vs.metadata && vs.metadata[md.name])
|
|
255
|
+
mdCopy.value = vs.metadata[md.name];
|
|
256
|
+
metadata.value.push(mdCopy);
|
|
257
|
+
}
|
|
221
258
|
showMetaData.value = true;
|
|
222
259
|
}
|
|
223
260
|
}
|
|
224
|
-
async function saveMetaData(items?:
|
|
261
|
+
async function saveMetaData(items?: IDataItem[]) {
|
|
225
262
|
showMetaData.value = false;
|
|
226
263
|
if (items) {
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
264
|
+
if (editVectorStoreMetaData.value) {
|
|
265
|
+
const vs = openAI.vectorStore![selectedVectorStoreName.value];
|
|
266
|
+
if (vs) {
|
|
267
|
+
const md: Dictionary<string> = {};
|
|
268
|
+
for (const it of items) {
|
|
269
|
+
md[it.name] = it.value.toString();
|
|
270
|
+
}
|
|
271
|
+
if (await openAI.setAttributes(selectedVectorStoreName.value, null, md)) {
|
|
272
|
+
vs.metadata = md;
|
|
273
|
+
}
|
|
230
274
|
}
|
|
231
275
|
}
|
|
232
276
|
else {
|
|
233
277
|
const file = openAI.getFile(selectedVectorStoreFile.value);
|
|
234
278
|
if (file) {
|
|
235
|
-
|
|
236
|
-
|
|
279
|
+
const att: Dictionary<ItemType> = {};
|
|
280
|
+
for (const it of items) {
|
|
281
|
+
att[it.name] = it.value;
|
|
282
|
+
}
|
|
283
|
+
if (await openAI.setAttributes(selectedVectorStoreName.value, file.id, att)) {
|
|
284
|
+
file.attributes = att;
|
|
237
285
|
}
|
|
238
286
|
}
|
|
239
287
|
}
|
|
240
288
|
}
|
|
241
289
|
}
|
|
290
|
+
async function searchFile() {
|
|
291
|
+
let files: FileSystemFileHandle[] = [];
|
|
292
|
+
try {
|
|
293
|
+
files = await (window as any).showOpenFilePicker();
|
|
294
|
+
if (files.length > 0) {
|
|
295
|
+
fileHandle = files[0];
|
|
296
|
+
selectedVectorStoreFile.value = fileHandle.name;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
console.error('Error opening file picker:', e);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
242
304
|
</script>
|
|
243
305
|
|
|
244
306
|
<template>
|
|
@@ -375,9 +437,9 @@
|
|
|
375
437
|
<v-combobox v-model="selectedVectorStoreName" :items="vectorStoreNames" hide-details density="compact" @update:modelValue="vectorStoreSelected()"></v-combobox>
|
|
376
438
|
</v-col>
|
|
377
439
|
<v-col cols="2">
|
|
378
|
-
<v-btn disabled="!selectedVectorStoreName" variant="text" prepend-icon="$delete" @click="deleteVectorStore">VS</v-btn>
|
|
379
|
-
<v-btn disabled="!selectedVectorStoreName" variant="text" prepend-icon="$plus" @click="addVectorStore">VS</v-btn>
|
|
380
|
-
<v-btn disabled="!selectedVectorStoreName" variant="text" prepend-icon="$pencil" @click="editVectorStore">VS</v-btn>
|
|
440
|
+
<v-btn :disabled="!selectedVectorStoreName" variant="text" prepend-icon="$delete" @click="deleteVectorStore">VS</v-btn>
|
|
441
|
+
<v-btn :disabled="!selectedVectorStoreName" variant="text" prepend-icon="$plus" @click="addVectorStore">VS</v-btn>
|
|
442
|
+
<v-btn :disabled="!selectedVectorStoreName" variant="text" prepend-icon="$pencil" @click="editVectorStore">VS</v-btn>
|
|
381
443
|
</v-col>
|
|
382
444
|
<v-col cols="5">
|
|
383
445
|
<v-combobox v-model="selectedVectorStoreFile" :items="vectorStoreFiles" hide-details density="compact"></v-combobox>
|
|
@@ -385,15 +447,15 @@
|
|
|
385
447
|
<v-col cols="1">
|
|
386
448
|
<v-btn variant="text" prepend-icon="$search" @click="searchFile">FILE</v-btn>
|
|
387
449
|
</v-col>
|
|
388
|
-
<v-col cols="
|
|
389
|
-
<v-btn disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$delete" @click="deleteFile">FILE</v-btn>
|
|
390
|
-
<v-btn disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$plus" @click="addFile">FILE</v-btn>
|
|
391
|
-
<v-btn disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$pencil" @click="editFile">
|
|
450
|
+
<v-col cols="2">
|
|
451
|
+
<v-btn :disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$delete" @click="deleteFile">FILE</v-btn>
|
|
452
|
+
<v-btn :disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$plus" @click="addFile">FILE</v-btn>
|
|
453
|
+
<v-btn :disabled="!selectedVectorStoreFile" variant="text" prepend-icon="$pencil" @click="editFile">FILE</v-btn>
|
|
392
454
|
</v-col>
|
|
393
455
|
</v-row>
|
|
394
456
|
</template>
|
|
395
457
|
<v-dialog v-model="showMetaData">
|
|
396
|
-
<OpenAIMetaData :items="metadata" :vectorstore="
|
|
458
|
+
<OpenAIMetaData :items="metadata" :vectorstore="editVectorStoreMetaData" @back="saveMetaData"></OpenAIMetaData>
|
|
397
459
|
</v-dialog>
|
|
398
460
|
<v-dialog v-model="showFunctions">
|
|
399
461
|
<v-container fluid class="bg-office overflow-y-auto">
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { inject, computed, ref, StyleValue } from 'vue';
|
|
3
|
+
import { IDataItem, Dictionary, IComparisonFilter, ICompoundFilter, IOpenAIFilter } from '@christianriedl/utils'
|
|
4
|
+
import SettingsLine from '../components/SettingsLine.vue';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ vsmetadata: IDataItem[], fileattributes: IDataItem[], filter?: IOpenAIFilter }>();
|
|
7
|
+
const emits = defineEmits<{ (e: 'back', filter: IOpenAIFilter): void }>();
|
|
8
|
+
const items: IDataItem[] = [];
|
|
9
|
+
const opsvs = ['', 'eq'];
|
|
10
|
+
const opsfile = ['', 'eq', 'ne', 'gt', 'gte', 'lt', 'lte'];
|
|
11
|
+
const operators: Dictionary<string> = {};;
|
|
12
|
+
const numvs = props.vsmetadata.length;
|
|
13
|
+
|
|
14
|
+
for (const it of props.vsmetadata) {
|
|
15
|
+
items.push(Object.assign({}, it));
|
|
16
|
+
operators[it.name] = '';
|
|
17
|
+
}
|
|
18
|
+
for (const it of props.fileattributes) {
|
|
19
|
+
items.push(Object.assign({}, it));
|
|
20
|
+
operators[it.name] = '';
|
|
21
|
+
}
|
|
22
|
+
if (props.filter) {
|
|
23
|
+
if (props.filter.fileFilter) {
|
|
24
|
+
for (const f of props.filter.fileFilter.filters) {
|
|
25
|
+
const it = items.find(i => i.name === f.key);
|
|
26
|
+
if (it) {
|
|
27
|
+
it.value = f.value;
|
|
28
|
+
operators[it.name] = f.type;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (props.filter.vectorStoreFilter) {
|
|
33
|
+
for (const name in props.filter.vectorStoreFilter) {
|
|
34
|
+
const it = items.find(i => i.name === name);
|
|
35
|
+
if (it) {
|
|
36
|
+
it.value = props.filter.vectorStoreFilter[name];
|
|
37
|
+
operators[it.name] = 'eq';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function onChange(item: IDataItem, hasChanged: boolean) {
|
|
43
|
+
if (!hasChanged)
|
|
44
|
+
operators[item.name] = '';
|
|
45
|
+
}
|
|
46
|
+
function onBack() {
|
|
47
|
+
const filter: IOpenAIFilter = {
|
|
48
|
+
fileFilter: { type: 'and', filters: [] },
|
|
49
|
+
vectorStoreFilter: {}
|
|
50
|
+
};
|
|
51
|
+
for (let i = 0; i < items.length; i++) {
|
|
52
|
+
const item = items[i];
|
|
53
|
+
if (operators[item.name]) {
|
|
54
|
+
if (i < numvs) {
|
|
55
|
+
filter.vectorStoreFilter[item.name] = item.value;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const f: IComparisonFilter = { key: item.name, type: operators[item.name] as 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte', value: item.value };
|
|
59
|
+
filter.fileFilter.filters.push(f);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
emits('back', filter);
|
|
64
|
+
}
|
|
65
|
+
function onOperation(item: IDataItem, op: string) {
|
|
66
|
+
operators[item.name] = op;
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<v-container fluid class="bg-office">
|
|
72
|
+
<h4>FileSearch filter</h4>
|
|
73
|
+
<v-table>
|
|
74
|
+
<thead>
|
|
75
|
+
<tr>
|
|
76
|
+
<th class="font-weight-bold" style="width:40%">Name</th>
|
|
77
|
+
<th class="font-weight-bold" style="width:40%">Value</th>
|
|
78
|
+
<th class="font-weight-bold" style="width:20%">Op</th>
|
|
79
|
+
</tr>
|
|
80
|
+
</thead>
|
|
81
|
+
<tbody>
|
|
82
|
+
<settings-line v-for="(item, index) in items" :key="item.name" :item="item" :op="operators[item.name]"
|
|
83
|
+
:operations="index < numvs ? opsvs : opsfile" clearable @change="onChange(item, true)"
|
|
84
|
+
@clear="onChange(item, false)" @operation="(op) => onOperation(item, op)">
|
|
85
|
+
</settings-line>
|
|
86
|
+
<tr>
|
|
87
|
+
<td>
|
|
88
|
+
<v-btn @click="onBack()">
|
|
89
|
+
BACK
|
|
90
|
+
<v-icon icon="$back"></v-icon>
|
|
91
|
+
</v-btn>
|
|
92
|
+
</td>
|
|
93
|
+
<td></td>
|
|
94
|
+
<td></td>
|
|
95
|
+
</tr>
|
|
96
|
+
</tbody>
|
|
97
|
+
</v-table>
|
|
98
|
+
</v-container>
|
|
99
|
+
</template>
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
const emits = defineEmits<{ (e: 'back', items?: IDataItem[]): void }>();
|
|
8
8
|
const items: IDataItem[] = [];
|
|
9
9
|
for (const it of props.items) {
|
|
10
|
-
items.push(Object.assign({}, it);
|
|
10
|
+
items.push(Object.assign({}, it));
|
|
11
11
|
}
|
|
12
12
|
const header = props.vectorstore ? "Vector Store Metadata" : "File Attributes";
|
|
13
13
|
const changedItems = new Set<string>();
|
|
14
14
|
const changed = ref(false);
|
|
15
15
|
|
|
16
|
-
function onChange(item: IDataItem,
|
|
17
|
-
if (
|
|
16
|
+
function onChange(item: IDataItem, hasChanged: boolean) {
|
|
17
|
+
if (hasChanged)
|
|
18
18
|
changedItems.add(item.name);
|
|
19
19
|
else
|
|
20
20
|
changedItems.delete(item.name);
|
|
@@ -27,27 +27,26 @@
|
|
|
27
27
|
else {
|
|
28
28
|
emits('back');
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
}
|
|
31
31
|
</script>
|
|
32
32
|
|
|
33
33
|
<template>
|
|
34
|
-
<v-container fluid >
|
|
34
|
+
<v-container fluid class="bg-office">
|
|
35
35
|
<h4>{{header}}</h4>
|
|
36
36
|
<v-table>
|
|
37
37
|
<thead>
|
|
38
38
|
<tr>
|
|
39
|
-
<th style="width:50%">Name</th>
|
|
40
|
-
<th style="width:50%">Value</th>
|
|
39
|
+
<th class="font-weight-bold" style="width:50%">Name</th>
|
|
40
|
+
<th class="font-weight-bold" style="width:50%">Value</th>
|
|
41
41
|
</tr>
|
|
42
42
|
</thead>
|
|
43
43
|
<tbody>
|
|
44
|
-
<settings-line v-for="item in items" :key="item.name" :item="item" @change="onChange(item, true)" @clear="onChange(item, false)"></settings-line>
|
|
44
|
+
<settings-line v-for="item in items" :key="item.name" :item="item" @change="onChange(item, true)" clearable @clear="onChange(item, false)"></settings-line>
|
|
45
45
|
<tr>
|
|
46
|
-
<td
|
|
47
|
-
<v-btn @click="onBack
|
|
48
|
-
<v-btn v-if="changed" @click="onBack(true)">SAVE</v-btn>
|
|
46
|
+
<td>
|
|
47
|
+
<v-btn @click="onBack">BACK</v-btn>
|
|
49
48
|
</td>
|
|
50
|
-
<td
|
|
49
|
+
<td></td>
|
|
51
50
|
</tr>
|
|
52
51
|
</tbody>
|
|
53
52
|
</v-table>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { reactive } from 'vue';
|
|
2
|
+
import { reactive, ref } from 'vue';
|
|
3
3
|
import { IConfigItem, IDataItem, ItemType, EScope } from '@christianriedl/utils';
|
|
4
4
|
|
|
5
|
-
const props = defineProps<{ item: IConfigItem | IDataItem; scopes?: EScope, clearable?: boolean }>();
|
|
6
|
-
const emits = defineEmits<{ (e: 'change'): void, (e: 'clear'): void }>();
|
|
5
|
+
const props = defineProps<{ item: IConfigItem | IDataItem; scopes?: EScope, clearable?: boolean, op?: string, operations?: string[] }>();
|
|
6
|
+
const emits = defineEmits<{ (e: 'change'): void, (e: 'clear'): void, (e: 'operation', op: string): void }>();
|
|
7
7
|
|
|
8
8
|
const item = reactive(props.item);
|
|
9
9
|
const isString = typeof item.default === 'string' && !item.values;
|
|
@@ -14,10 +14,14 @@
|
|
|
14
14
|
const isVisible = !props.scopes || ((item as IConfigItem).scopes & props.scopes) != 0;
|
|
15
15
|
const enumValues: ItemType[] = item.values!;
|
|
16
16
|
const description = (item as IConfigItem).description || item.name;
|
|
17
|
+
const operation = ref(props.op ? props.op : '');
|
|
17
18
|
|
|
18
19
|
function onChange() {
|
|
19
20
|
emits('change');
|
|
20
21
|
}
|
|
22
|
+
function onChangeOperation() {
|
|
23
|
+
emits('operation', operation.value);
|
|
24
|
+
}
|
|
21
25
|
function onClear() {
|
|
22
26
|
item.value = item.default;
|
|
23
27
|
emits('clear');
|
|
@@ -26,8 +30,8 @@
|
|
|
26
30
|
|
|
27
31
|
<template>
|
|
28
32
|
<tr v-if="isVisible" class="settingsline">
|
|
29
|
-
<td
|
|
30
|
-
<td
|
|
33
|
+
<td >{{description}}</td>
|
|
34
|
+
<td>
|
|
31
35
|
<v-switch v-if="isBoolean" v-model="item.value" :disabled="isDisabled" density="compact" hide-details color="primary"
|
|
32
36
|
:append-icon="props.clearable ? '$clear' : undefined" @update:modelValue="onChange" @click:append="onClear"></v-switch>
|
|
33
37
|
<v-text-field v-if="isString" v-model="item.value" :disabled="isDisabled" :clearable="props.clearable" density="compact"
|
|
@@ -38,6 +42,10 @@
|
|
|
38
42
|
:items="enumValues" persistent-hint @update:modelValue="onChange" @click:clear="onClear" dense solo hide-details single-line>
|
|
39
43
|
</v-select>
|
|
40
44
|
</td>
|
|
45
|
+
<td v-if="props.operations">
|
|
46
|
+
<v-select density="compact" v-model="operation" :items="props.operations" persistent-hint @update:modelValue="onChangeOperation" dense solo hide-details single-line>
|
|
47
|
+
</v-select>
|
|
48
|
+
</td>
|
|
41
49
|
</tr>
|
|
42
50
|
</template>
|
|
43
51
|
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
<v-table>
|
|
66
66
|
<thead>
|
|
67
67
|
<tr>
|
|
68
|
-
<th style="width:50%">Topic</th>
|
|
69
|
-
<th style="width:50%">Parameter</th>
|
|
68
|
+
<th class="font-weight-bold" style="width:50%">Topic</th>
|
|
69
|
+
<th class="font-weight-bold" style="width:50%">Parameter</th>
|
|
70
70
|
</tr>
|
|
71
71
|
</thead>
|
|
72
72
|
<tbody>
|