From 37068da677a9c9c36a45b2dbe4dbbe8af3c02819 Mon Sep 17 00:00:00 2001 From: mutugiii Date: Wed, 14 Aug 2024 22:20:22 +0300 Subject: [PATCH 1/3] DB config to support sending files --- chatapi/src/config/nano.config.ts | 6 ++++-- chatapi/src/services/chat.service.ts | 6 +++--- chatapi/src/utils/chat-assistant.utils.ts | 24 ++++++++++++++++------- chatapi/src/utils/chat-helpers.utils.ts | 3 +-- chatapi/src/utils/chat.utils.ts | 2 +- chatapi/src/utils/db.utils.ts | 14 +++++++++++-- 6 files changed, 38 insertions(+), 17 deletions(-) diff --git a/chatapi/src/config/nano.config.ts b/chatapi/src/config/nano.config.ts index 5b936d59b2..e9027d6163 100644 --- a/chatapi/src/config/nano.config.ts +++ b/chatapi/src/config/nano.config.ts @@ -3,6 +3,8 @@ import dotenv from 'dotenv'; dotenv.config(); -const db = nano(process.env.COUCHDB_HOST || 'http://couchdb:5984').use('chat_history'); +const db = nano(process.env.COUCHDB_HOST || 'http://couchdb:5984'); +const chatDB = db.use('chat_history'); +const resourceDB = db.use('resources'); -export default db; +export { chatDB, resourceDB }; diff --git a/chatapi/src/services/chat.service.ts b/chatapi/src/services/chat.service.ts index df314b29ab..34e93b968d 100644 --- a/chatapi/src/services/chat.service.ts +++ b/chatapi/src/services/chat.service.ts @@ -1,6 +1,6 @@ import { DocumentInsertResponse } from 'nano'; -import db from '../config/nano.config'; +import { chatDB } from '../config/nano.config'; import { aiChat } from '../utils/chat.utils'; import { retrieveChatHistory } from '../utils/db.utils'; import { handleChatError } from '../utils/chat-error.utils'; @@ -40,7 +40,7 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st } dbData.conversations.push({ 'query': content, 'response': '' }); - const res = await db.insert(dbData); + const res = await chatDB.insert(dbData); messages.push({ 'role': 'user', content }); @@ -52,7 +52,7 @@ export async function chat(data: any, stream?: boolean, callback?: (response: st dbData.updatedDate = Date.now(); dbData._id = res?.id; dbData._rev = res?.rev; - const couchSaveResponse = await db.insert(dbData); + const couchSaveResponse = await chatDB.insert(dbData); return { completionText, diff --git a/chatapi/src/utils/chat-assistant.utils.ts b/chatapi/src/utils/chat-assistant.utils.ts index 895a5aa1c9..8c96f65f0a 100644 --- a/chatapi/src/utils/chat-assistant.utils.ts +++ b/chatapi/src/utils/chat-assistant.utils.ts @@ -1,3 +1,4 @@ +import fs from 'fs'; import openai from '../config/openai.config'; import dotenv from 'dotenv'; @@ -9,14 +10,13 @@ dotenv.config(); * @returns Assistant object */ export async function createAssistant(model: string) { -// export async function createAssistant(model: string, additionalContext?: any) { - - // const instructions = process.env.ASSISTANT_INSTRUCTIONS + (additionalContext ? additionalContext?.data : ''); - return await openai.beta.assistants.create({ 'name': process.env.ASSISTANT_NAME, 'instructions': process.env.ASSISTANT_INSTRUCTIONS, - 'tools': [{ 'type': 'code_interpreter' }], + 'tools': [ + { 'type': 'code_interpreter' }, + { 'type': 'file_search' } + ], model, }); } @@ -25,12 +25,22 @@ export async function createThread() { return await openai.beta.threads.create(); } -export async function addToThread(threadId: any, message: string) { +export async function addToThread(threadId: any, message: string, attachment?: any) { + let attachmentBlob; + + if (attachment.valid) { + attachmentBlob = await openai.files.create({ + 'file': fs.createReadStream(attachment.doc), + 'purpose': 'assistants', + }); + } + return await openai.beta.threads.messages.create( threadId, { 'role': 'user', - 'content': message + 'content': message, + 'attachments': attachment.valid ? [{ 'file_id': attachmentBlob?.id, 'tools': [{ 'type': 'file_search' }] }] : [] } ); } diff --git a/chatapi/src/utils/chat-helpers.utils.ts b/chatapi/src/utils/chat-helpers.utils.ts index 70222d91e0..9f25264b2a 100644 --- a/chatapi/src/utils/chat-helpers.utils.ts +++ b/chatapi/src/utils/chat-helpers.utils.ts @@ -68,7 +68,6 @@ export async function aiChatStream( messages: ChatMessage[], aiProvider: AIProvider, assistant: boolean, - context: any, callback?: (response: string) => void ): Promise { const provider = providers[aiProvider.name]; @@ -141,7 +140,7 @@ export async function aiChatNonStream( const asst = await createAssistant(model); const thread = await createThread(); for (const message of messages) { - await addToThread(thread.id, message.content); + await addToThread(thread.id, message.content, context?.attachment); } const run = await createRun(thread.id, asst.id, context.data); await waitForRunCompletion(thread.id, run.id); diff --git a/chatapi/src/utils/chat.utils.ts b/chatapi/src/utils/chat.utils.ts index 0d5b344a51..6f8f4f0d5e 100644 --- a/chatapi/src/utils/chat.utils.ts +++ b/chatapi/src/utils/chat.utils.ts @@ -12,7 +12,7 @@ export async function aiChat( callback?: (response: string) => void ): Promise { if (stream) { - return await aiChatStream(messages, aiProvider, assistant, context, callback); + return await aiChatStream(messages, aiProvider, assistant, callback); } else { return await aiChatNonStream(messages, aiProvider, assistant, context); } diff --git a/chatapi/src/utils/db.utils.ts b/chatapi/src/utils/db.utils.ts index dccdeff46f..aaa70da8ed 100644 --- a/chatapi/src/utils/db.utils.ts +++ b/chatapi/src/utils/db.utils.ts @@ -1,4 +1,4 @@ -import db from '../config/nano.config'; +import { chatDB, resourceDB } from '../config/nano.config'; import { DbDoc } from '../models/db-doc.model'; import { ChatMessage } from '../models/chat-message.model'; @@ -9,7 +9,7 @@ import { ChatMessage } from '../models/chat-message.model'; */ async function getChatDocument(id: string) { try { - const res = await db.get(id) as DbDoc; + const res = await chatDB.get(id) as DbDoc; return { 'conversations': res.conversations, 'title': res.title, @@ -37,3 +37,13 @@ export async function retrieveChatHistory(dbData: any, messages: ChatMessage[]) messages.push({ 'role': 'assistant', 'content': response }); } } + +export async function fetchFileFromCouchDB(docId: string, attachmentName: string) { + try { + return await resourceDB.attachment.get(docId, attachmentName); + } catch (error) { + return { + 'error': 'Unable to retrieve file from CouchDB' + }; + } +} From e76392ea3ef4eb0aceb7f364377707c3471c6459 Mon Sep 17 00:00:00 2001 From: mutugiii Date: Fri, 23 Aug 2024 16:41:47 +0300 Subject: [PATCH 2/3] Send the resource attachments --- .../courses/step-view-courses/courses-step-view.component.html | 2 +- .../courses/step-view-courses/courses-step-view.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/courses/step-view-courses/courses-step-view.component.html b/src/app/courses/step-view-courses/courses-step-view.component.html index 33420c8a37..e8423829bd 100644 --- a/src/app/courses/step-view-courses/courses-step-view.component.html +++ b/src/app/courses/step-view-courses/courses-step-view.component.html @@ -77,7 +77,7 @@

Step {{
- +
diff --git a/src/app/courses/step-view-courses/courses-step-view.component.ts b/src/app/courses/step-view-courses/courses-step-view.component.ts index 5fbe643f69..ef9c70a8e7 100644 --- a/src/app/courses/step-view-courses/courses-step-view.component.ts +++ b/src/app/courses/step-view-courses/courses-step-view.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; -import { CoursesService } from '../courses.service'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuTrigger } from '@angular/material/menu'; import { Subject, combineLatest } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { CoursesService } from '../courses.service'; import { UserService } from '../../shared/user.service'; import { SubmissionsService } from '../../submissions/submissions.service'; import { ResourcesService } from '../../resources/resources.service'; From 7f79f1ded51015e157a89da2110b8b8086e287af Mon Sep 17 00:00:00 2001 From: mutugiii Date: Mon, 26 Aug 2024 22:59:59 +0300 Subject: [PATCH 3/3] WIP: sending the files - context length errors --- chatapi/src/utils/chat-assistant.utils.ts | 2 +- chatapi/src/utils/chat-helpers.utils.ts | 11 +++++++++-- chatapi/src/utils/db.utils.ts | 4 ++-- .../courses-step-view.component.html | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/chatapi/src/utils/chat-assistant.utils.ts b/chatapi/src/utils/chat-assistant.utils.ts index 8c96f65f0a..cd4dd5cd11 100644 --- a/chatapi/src/utils/chat-assistant.utils.ts +++ b/chatapi/src/utils/chat-assistant.utils.ts @@ -30,7 +30,7 @@ export async function addToThread(threadId: any, message: string, attachment?: a if (attachment.valid) { attachmentBlob = await openai.files.create({ - 'file': fs.createReadStream(attachment.doc), + 'file': attachment.doc, 'purpose': 'assistants', }); } diff --git a/chatapi/src/utils/chat-helpers.utils.ts b/chatapi/src/utils/chat-helpers.utils.ts index 9f25264b2a..014e212856 100644 --- a/chatapi/src/utils/chat-helpers.utils.ts +++ b/chatapi/src/utils/chat-helpers.utils.ts @@ -12,6 +12,7 @@ import { retrieveResponse, createAndHandleRunWithStreaming, } from './chat-assistant.utils'; +import { fetchFileFromCouchDB } from './db.utils'; const modelsConfig = JSON.parse(process.env.MODELS_CONFIG || '{}'); @@ -134,20 +135,26 @@ export async function aiChatNonStream( throw new Error('Unsupported AI provider'); } const model = aiProvider.model ?? provider.defaultModel; + let attachment: any = { 'valid': false }; + + if (context?.attachment) { + const attachmentFile = await fetchFileFromCouchDB(context.attachment?.id, context.attachment?.filename); + attachment = { 'valid': true, 'doc': attachmentFile }; + } if(assistant) { try { const asst = await createAssistant(model); const thread = await createThread(); for (const message of messages) { - await addToThread(thread.id, message.content, context?.attachment); + await addToThread(thread.id, message.content, attachment); } const run = await createRun(thread.id, asst.id, context.data); await waitForRunCompletion(thread.id, run.id); return await retrieveResponse(thread.id); } catch (error) { - return 'Error processing request'; + return `Error processing request ${error}`; } } diff --git a/chatapi/src/utils/db.utils.ts b/chatapi/src/utils/db.utils.ts index aaa70da8ed..b5f3ce5da1 100644 --- a/chatapi/src/utils/db.utils.ts +++ b/chatapi/src/utils/db.utils.ts @@ -40,10 +40,10 @@ export async function retrieveChatHistory(dbData: any, messages: ChatMessage[]) export async function fetchFileFromCouchDB(docId: string, attachmentName: string) { try { - return await resourceDB.attachment.get(docId, attachmentName); + return await resourceDB.attachment.get(docId, attachmentName, { 'binary': true }); } catch (error) { return { - 'error': 'Unable to retrieve file from CouchDB' + 'error': `Unable to retrieve file from CouchDB ${error}` }; } } diff --git a/src/app/courses/step-view-courses/courses-step-view.component.html b/src/app/courses/step-view-courses/courses-step-view.component.html index e8423829bd..2ff72dfcb5 100644 --- a/src/app/courses/step-view-courses/courses-step-view.component.html +++ b/src/app/courses/step-view-courses/courses-step-view.component.html @@ -77,7 +77,7 @@

Step {{

- +