@christianriedl/media 1.0.265 → 1.0.267

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@christianriedl/media",
3
- "version": "1.0.265",
3
+ "version": "1.0.267",
4
4
  "description": "RIC media interfaces",
5
5
 
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "author": "Christian Riedl",
19
19
  "license": "ISC",
20
20
  "dependencies": {
21
- "@christianriedl/utils": "^1.0.115",
21
+ "@christianriedl/utils": "^1.0.119",
22
22
  "@christianriedl/rest": "^1.0.84",
23
23
  "@christianriedl/epg": "^1.0.43"
24
24
  },
@@ -1,6 +1,5 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, computed, inject } from 'vue';
3
- import { VTreeview } from 'vuetify/labs/VTreeview';
4
3
  import { Dictionary, Helper } from '@christianriedl/utils';
5
4
  import { IPictureFile, EOrientation, EPictureFlags, getMediaBinSymbol } from '@christianriedl/media';
6
5
 
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { inject, ref, reactive, onMounted, onUnmounted, computed, StyleValue } from 'vue';
3
3
  import { useRouter, useRoute } from 'vue-router';
4
- import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService, ICompleteData, EScope, EDevice } from '@christianriedl/utils';
4
+ import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService, IResponseResult, ISchema, EScope, EDevice } from '@christianriedl/utils';
5
5
  import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IBook } from '@christianriedl/media';
6
6
  import BookLine from '../components/BookLine.vue';
7
7
 
@@ -283,7 +283,7 @@
283
283
  return true;
284
284
  }
285
285
  async function getAuthorDataFromAI(surName: string, givenName: string) : Promise<boolean> {
286
- const schema =
286
+ const schema : ISchema =
287
287
  {
288
288
  "name": "author_data",
289
289
  "schema": {
@@ -302,20 +302,20 @@
302
302
  },
303
303
  "required": ["geburtstag", "sterbetag", "geburtsland"],
304
304
  "type": "object"
305
- },
306
- "strict": true
305
+ }
307
306
  }
308
307
  const prompt = `Geburtstag, Sterbetag, Geburtsland von ${givenName} ${surName} ?`;
309
- const result = await openAI.completeJson(prompt, schema) as any;
310
- if (result) {
308
+ const result = await openAI.response(prompt, undefined, undefined, schema);
309
+ if (result.json) {
310
+ const json = result.json as any;
311
311
  if (!currentAuthor.yearOfBirth)
312
- currentAuthor.yearOfBirth = getYear(result.geburtstag);
312
+ currentAuthor.yearOfBirth = getYear(json.geburtstag);
313
313
  if (!currentAuthor.yearOfDeath)
314
- currentAuthor.yearOfDeath = result.sterbetag ? getYear(result.sterbetag) : undefined;
314
+ currentAuthor.yearOfDeath = json.sterbetag ? getYear(json.sterbetag) : undefined;
315
315
  if (!currentAuthor.category)
316
316
  currentAuthor.category = booksService.categories[0];
317
317
  if (!currentAuthor.country)
318
- currentAuthor.country = result.geburtsland;
318
+ currentAuthor.country = json.geburtsland;
319
319
  return true;
320
320
  }
321
321
  else {
@@ -332,7 +332,7 @@
332
332
  return 0;
333
333
  }
334
334
  async function getAuthorBooksFromAI(surName: string, givenName: string, authorId: number) : Promise <IBook[]> {
335
- const schema =
335
+ const schema: ISchema =
336
336
  {
337
337
  "name": "books",
338
338
  "schema": {
@@ -358,15 +358,15 @@
358
358
  },
359
359
  "required": ["werke"],
360
360
  "type": "object"
361
- },
362
- "strict": true
361
+ }
363
362
  }
364
363
  const prompt = `Alle Buchtitel in Deutsch mit Erscheinungsjahr von ${givenName} ${surName}`;
365
- const result = await openAI.completeJson(prompt, schema) as any;
366
- if (result && result.werke) {
364
+ const result = await openAI.response(prompt, undefined, undefined, schema);
365
+ if (result && result.json) {
367
366
  const books: IBook[] = [];
368
- for (let i = 0; i < result.werke.length; i++) {
369
- books.push ({ id: i, authorId: authorId, title: result.werke[i].werk, year : result.werke[i].erscheinungsjahr});
367
+ const json = result.json as any;
368
+ for (let i = 0; i < json.werke.length; i++) {
369
+ books.push ({ id: i, authorId: authorId, title: json.werke[i].werk, year : json.werke[i].erscheinungsjahr});
370
370
  }
371
371
  return books;
372
372
  }
@@ -377,9 +377,9 @@
377
377
  }
378
378
  async function getBookDescriptionFromAI(surName: string, givenName: string, title: string, prefix?: string) : Promise <string> {
379
379
  const prompt = `Eine ${prefix} Inhaltsangabe von dem Buch "${title}" von ${givenName} ${surName}`;
380
- const result = await openAI.complete(prompt);
381
- if (result && result.choices) {
382
- return result.choices[0];
380
+ const result = await openAI.response(prompt);
381
+ if (result && result.text) {
382
+ return result.text;
383
383
  }
384
384
  else {
385
385
  alert("No AI result for : " + prompt);
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { inject, ref, reactive, onMounted, onUnmounted, computed, StyleValue } from 'vue';
2
+ import { inject, ref, reactive, computed } from 'vue';
3
3
  import { useRouter, useRoute } from 'vue-router';
4
- import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService, ICompleteData, EScope, EDevice } from '@christianriedl/utils';
5
- import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IFullBook, IPlace } from '@christianriedl/media';
4
+ import { IAppState, appStateSymbol, EScope, EDevice } from '@christianriedl/utils';
5
+ import { BooksService, getBooksSymbol, IFullBook, IPlace } from '@christianriedl/media';
6
6
  import BookPlace from '../components/BookPlace.vue';
7
7
 
8
8
  const route = useRoute();
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { inject, ref, reactive, onMounted, onUnmounted, computed, StyleValue } from 'vue';
3
- import { IAppState, appStateSymbol, getOpenAISymbol, IOpenAIService, ICompleteData, EScope, EDevice } from '@christianriedl/utils';
2
+ import { inject, ref, reactive, computed, StyleValue } from 'vue';
3
+ import { IAppState, appStateSymbol, EDevice } from '@christianriedl/utils';
4
4
  import { BooksService, getBooksSymbol, IAuthorShort, IAuthor, IFullBook } from '@christianriedl/media';
5
5
  import BookSearchLine from '../components/BookSearchLine.vue';
6
6
 
@@ -2,6 +2,7 @@
2
2
  import { inject, ref, reactive, watch, computed, onMounted, StyleValue } from 'vue';
3
3
  import { useRouter, useRoute } from 'vue-router';
4
4
  import { IAppState, appStateSymbol, appConfigSymbol, EScope, Helper } from '@christianriedl/utils';
5
+ import { getOpenAISymbol, IOpenAIServiceWithVectorStore } from '@christianriedl/utils';
5
6
  import { getMediaBinSymbol, getMediaInstanceSymbol, IDocumentInfo, IMediaAppConfig } from '@christianriedl/media';
6
7
  import FileUpload from '../components/FileUpload.vue';
7
8
 
@@ -9,6 +10,8 @@
9
10
  const router = useRouter();
10
11
  const appState = inject(appStateSymbol)!;
11
12
  const mediaAppConfig = inject(appConfigSymbol)! as unknown as IMediaAppConfig;
13
+ const getAiService = inject(getOpenAISymbol)!;
14
+ const aiService = getAiService() as IOpenAIServiceWithVectorStore;
12
15
  const officeExtensions: string[] = mediaAppConfig.officeExtensions.split(',');
13
16
  const isMobile = appState.isMobile;
14
17
  const getMediaService = inject(getMediaBinSymbol)!;
@@ -27,86 +30,94 @@
27
30
  const directoryInfos = reactive<IDocumentInfo[]>([]);
28
31
  const fileInfos = reactive<IDocumentInfo[]>([]);
29
32
  const heightStyle = computed<StyleValue>(() => { return { height: appState.bodyHeight.value + 'px', overflowY: 'auto' } });
33
+ const inVectorStore = reactive<Dictionary<string>>({});
30
34
 
31
35
  onMounted(() => {
32
- mediaService.log.trace('Documents created');
33
- start();
36
+ mediaService.log.trace('Documents created');
37
+ start();
34
38
  });
35
39
  async function start() {
36
- if (route.query && route.query.path) {
37
- path.value = decodeURIComponent(route.query.path.toString());
38
- }
39
- else {
40
- path.value = "";
41
- }
42
- getItems(path.value);
40
+ if (route.query && route.query.path) {
41
+ path.value = decodeURIComponent(route.query.path.toString());
42
+ }
43
+ else {
44
+ path.value = "";
45
+ }
46
+ aiService.initializeVectorStore()
47
+ .then(() => {
48
+ for (const key in aiService.vectorStore) {
49
+ const store = aiService.vectorStore[key];
50
+ for (const file in store.files) {
51
+ inVectorStore[store.files[file]] = key;
52
+ }
53
+ }
54
+ });
55
+ getItems(path.value);
43
56
  }
44
57
  watch(() => route.query, () => start());
45
58
 
46
59
  async function getItems(fullPath: string) {
47
- if (isMobile) {
48
- const directories = await mediaService.getDirectories(fullPath);
49
- directoryNames.splice(0, directoryNames.length, ...directories);
50
- const files = await mediaService.getFiles(fullPath);
51
- fileNames.splice(0, fileNames.length, ...files);
52
- mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
53
- }
54
- else {
55
- const directories = await mediaService.getDirectoriesFull(fullPath);
56
- directoryInfos.splice(0, directoryInfos.length, ...directories);
57
- const files = await mediaService.getFilesFull(fullPath);
58
- fileInfos.splice(0, fileInfos.length, ...files);
59
- mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
60
- }
61
- backVisible.value = path.value.length > 0;
60
+ if (isMobile) {
61
+ const directories = await mediaService.getDirectories(fullPath);
62
+ directoryNames.splice(0, directoryNames.length, ...directories);
63
+ const files = await mediaService.getFiles(fullPath);
64
+ fileNames.splice(0, fileNames.length, ...files);
65
+ mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
66
+ }
67
+ else {
68
+ const directories = await mediaService.getDirectoriesFull(fullPath);
69
+ directoryInfos.splice(0, directoryInfos.length, ...directories);
70
+ const files = await mediaService.getFilesFull(fullPath);
71
+ fileInfos.splice(0, fileInfos.length, ...files);
72
+ mediaService.log.trace(`At ${fullPath} : ${directories.length} directories, ${files.length} files`);
73
+ }
74
+ backVisible.value = path.value.length > 0;
62
75
  }
63
76
  async function onSelect(name: string) {
64
- const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
65
- router.push ({path: 'documents', query: { path: encodeURIComponent(fullPath) }});
77
+ const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
78
+ router.push({ path: 'documents', query: { path: encodeURIComponent(fullPath) } });
66
79
  }
67
80
  async function onSearch() {
68
- const files = await mediaService.searchFiles(path.value, contains.value, like.value, recursive.value);
69
- directoryInfos.splice(0, directoryInfos.length);
70
- fileInfos.splice(0, fileInfos.length, ...files);
71
- mediaService.log.trace(`At ${path.value} : search ${files.length} files`);
81
+ const files = await mediaService.searchFiles(path.value, contains.value, like.value, recursive.value);
82
+ directoryInfos.splice(0, directoryInfos.length);
83
+ fileInfos.splice(0, fileInfos.length, ...files);
84
+ mediaService.log.trace(`At ${path.value} : search ${files.length} files`);
72
85
  }
73
86
  function download(name: string, folder?: string) {
74
- if (!folder)
75
- folder = path.value;
76
- let url = mediaService.getFileUrl(folder.length > 0 ? (folder + '/' + name) : name);
77
- const i = name.lastIndexOf('.');
78
- if (i > 0) {
79
- const ext = name.substring(i + 1);
80
- if (officeExtensions.includes(ext)) {
81
- url = 'https://view.officeapps.live.com/op/view.aspx?src=' + url;
82
- }
83
- }
84
- window.open(url);
87
+ cccccctefcfkhjggitufgrlhcebiccnlcctfrkghrkrd
88
+ const i = name.lastIndexOf('.');
89
+ if (i > 0) {
90
+ const ext = name.substring(i + 1);
91
+ if (officeExtensions.includes(ext)) {
92
+ url = 'https://view.officeapps.live.com/op/view.aspx?src=' + url;
93
+ }
94
+ }
95
+ window.open(url);
85
96
  }
86
97
  async function listBack() {
87
- router.back();
98
+ router.back();
88
99
  }
89
- async function shareDir (name: string) {
90
- const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
91
- if (!await checkPublic(fullPath)) {
92
- window.alert ("Not a public folder !!");
93
- return;
94
- }
95
- const url = `https://www.christian-riedl.com/photos/documents?path=${encodeURIComponent(fullPath)}`;
96
- await window.navigator.clipboard.writeText(url);
97
- window.alert(`Created and Pasted : ${url} !`);
100
+ async function shareDir(name: string) {
101
+ const fullPath = path.value.length > 0 ? (path.value + '/' + name) : name;
102
+ if (!await checkPublic(fullPath)) {
103
+ window.alert("Not a public folder !!");
104
+ return;
105
+ }
106
+ const url = `https://www.christian-riedl.com/photos/documents?path=${encodeURIComponent(fullPath)}`;
107
+ await window.navigator.clipboard.writeText(url);
108
+ window.alert(`Created and Pasted : ${url} !`);
98
109
  }
99
110
  async function shareFile(name: string, folder?: string) {
100
- if (!folder)
101
- folder = path.value;
102
- const fullPath = folder.length > 0 ? (folder + '/' + name) : name;
103
- if (!await checkPublic(fullPath)) {
104
- window.alert ("Not a public folder !!");
105
- return;
106
- }
107
- const url = mediaService.getFileUrl(fullPath);;
108
- await window.navigator.clipboard.writeText(url);
109
- window.alert(`Created and Pasted : ${url} !`);
111
+ if (!folder)
112
+ folder = path.value;
113
+ const fullPath = folder.length > 0 ? (folder + '/' + name) : name;
114
+ if (!await checkPublic(fullPath)) {
115
+ window.alert("Not a public folder !!");
116
+ return;
117
+ }
118
+ const url = mediaService.getFileUrl(fullPath);;
119
+ await window.navigator.clipboard.writeText(url);
120
+ window.alert(`Created and Pasted : ${url} !`);
110
121
  }
111
122
  async function deleteDirOrFile(name: string, folder?: string) {
112
123
  if (!folder)
@@ -127,29 +138,60 @@
127
138
  function uploadDone() {
128
139
  window.location.reload();
129
140
  }
130
- async function checkPublic (path: string) : Promise<boolean> {
131
- const instanceInfo = await instanceService.getInformation();
132
- if (instanceInfo != null && instanceInfo.publicDocumentRoots) {
133
- const publicRoots = instanceInfo.publicDocumentRoots.split(';');
134
- for (let i = 0; i < publicRoots.length; i++) {
135
- if (path.startsWith(publicRoots[i]))
136
- return true;
137
- }
138
- }
139
- return false;
141
+ async function checkPublic(path: string): Promise<boolean> {
142
+ const instanceInfo = await instanceService.getInformation();
143
+ if (instanceInfo != null && instanceInfo.publicDocumentRoots) {
144
+ const publicRoots = instanceInfo.publicDocumentRoots.split(';');
145
+ for (let i = 0; i < publicRoots.length; i++) {
146
+ if (path.startsWith(publicRoots[i]))
147
+ return true;
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+ function vectorColor(file: string): string | undefined {
153
+ if (inVectorStore[file]) {
154
+ return 'warning';
155
+ }
156
+ return undefined;
157
+ }
158
+ async function vectorStore(file: string) {
159
+ if (inVectorStore[file]) {
160
+ if (window.confirm(`File is in vectorstore ${inVectorStore[file]}, delete ?`)) {
161
+ const store = aiService.vectorStore[inVectorStore[file]]
162
+ for (const vsf in store.files) {
163
+ if (store.files[vsf] == file) {
164
+ if (await aiService.deleteFile(vsf))
165
+ delete inVectorStore[file];
166
+ break;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ else {
172
+ const name = window.prompt(`Enter vectorstore name for ${file} to add it`);
173
+ if (name != null) {
174
+ if (!folder)
175
+ folder = path.value;
176
+ let url = mediaService.getFileUrl(folder.length > 0 ? (folder + '/' + name) : name);
177
+ if (await aiService.addFile(url, name) {
178
+ inVectorStore[file] = name;
179
+ }
180
+ }
181
+ }
140
182
  }
141
- function info (item: IDocumentInfo) : string {
142
- let sizeStr = "";
143
- if (item.size) {
144
- if (item.size < 10000)
145
- sizeStr = ' ' + item.size.toString() + ' B';
146
- else if (item.size < 10000000)
147
- sizeStr = ' ' + Helper.round (item.size / 1000, 2) + ' Kb';
148
- else
149
- sizeStr = ' ' + Helper.round (item.size / 1000000, 2) + ' Mb';
150
- }
151
- const str = ` - (${Helper.formatDate(item.date)}${sizeStr})`;
152
- return str;
183
+ function info(item: IDocumentInfo): string {
184
+ let sizeStr = "";
185
+ if (item.size) {
186
+ if (item.size < 10000)
187
+ sizeStr = ' ' + item.size.toString() + ' B';
188
+ else if (item.size < 10000000)
189
+ sizeStr = ' ' + Helper.round(item.size / 1000, 2) + ' Kb';
190
+ else
191
+ sizeStr = ' ' + Helper.round(item.size / 1000000, 2) + ' Mb';
192
+ }
193
+ const str = ` - (${Helper.formatDate(item.date)}${sizeStr})`;
194
+ return str;
153
195
  }
154
196
  </script>
155
197
  <template>
@@ -162,12 +204,13 @@
162
204
  <v-col cols="1"><v-btn v-if="isAdmin" icon="$delete" class="bg-office" density="compact" variant="flat" @click.stop="deleteDirOrFile(dir)"></v-btn></v-col>
163
205
  <v-divider />
164
206
  </v-row>
165
- <v-row v-for="file in fileNames" :key="file" class="text-decoration-underline text-primary">
166
- <v-col cols="10"><p class="font-weight-medium text-decoration-underline text-primary" @click.stop="download(file)">{{file}}</p></v-col>
167
- <v-col cols="1"><v-btn v-if="isAdmin" icon="$share" class="bg-office" density="compact" variant="flat" @click.stop="shareFile(file)"></v-btn></v-col>
168
- <v-col cols="1"><v-btn v-if="isAdmin" icon="$delete" class="bg-office" density="compact" variant="flat" @click.stop="deleteDirOrFile(file)"></v-btn></v-col>
169
- <v-divider />
170
- </v-row>
207
+ <v-row v-for="file in fileNames" :key="file" class="text-decoration-underline text-primary">
208
+ <v-col cols="9"><p class="font-weight-medium text-decoration-underline text-primary" @click.stop="download(file)">{{file}}</p></v-col>
209
+ <v-col cols="1"><v-btn v-if="isAdmin" icon="$brain" class="bg-office" density="compact" variant="flat" :color="vectorColor(file)" @click.stop="vectorStore(file)"></v-btn></v-col>
210
+ <v-col cols="1"><v-btn v-if="isAdmin" icon="$share" class="bg-office" density="compact" variant="flat" @click.stop="shareFile(file)"></v-btn></v-col>
211
+ <v-col cols="1"><v-btn v-if="isAdmin" icon="$delete" class="bg-office" density="compact" variant="flat" @click.stop="deleteDirOrFile(file)"></v-btn></v-col>
212
+ <v-divider />
213
+ </v-row>
171
214
  </v-container>
172
215
  <v-container v-else fluid>
173
216
  <v-row dense align="center">