From 18359df688225d190d9037a4916454f806157b82 Mon Sep 17 00:00:00 2001 From: danielcampagnolitg <138441775+danielcampagnolitg@users.noreply.github.com> Date: Sun, 27 Oct 2024 20:57:07 +0800 Subject: [PATCH] Update chat. Add cerebras. Align message with ai package (#33) --- frontend/angular.json | 13 +- frontend/package-lock.json | 143 ++++- frontend/package.json | 5 +- frontend/public/images/splash.png | Bin 0 -> 61943 bytes .../modules/actions/actions.component.html | 15 +- .../modules/admin/apps/chat/chat.component.ts | 7 +- .../modules/admin/apps/chat/chat.service.ts | 148 +++-- .../app/modules/admin/apps/chat/chat.types.ts | 26 +- .../apps/chat/chats/chats.component.html | 14 +- .../admin/apps/chat/chats/chats.component.ts | 19 + .../conversation/conversation.component.html | 54 +- .../conversation/conversation.component.scss | 415 +++++++++++++ .../conversation/conversation.component.ts | 132 +++- .../agent-llm-calls.component.html | 1 + .../agents/new-agent/new-agent.component.html | 20 +- .../agents/new-agent/new-agent.component.ts | 37 +- .../edit/code-review-edit.component.html | 23 +- .../edit/code-review-edit.component.ts | 25 +- .../profile/account/account.component.html | 19 +- .../profile/account/account.component.ts | 1 + frontend/src/index.html | 7 +- package-lock.json | 569 ++++++++++-------- package.json | 4 +- src/agent/cachingCodeGenAgentRunner.ts | 24 +- src/chat/chatService.test.ts | 8 +- src/chat/chatTypes.ts | 2 + src/cli/util.ts | 2 +- src/llm/base-llm.ts | 10 +- src/llm/llm.ts | 22 +- src/llm/llmCallService/llmCallService.ts | 2 +- src/llm/llmFactory.ts | 2 + src/llm/models/anthropic-vertex.ts | 74 ++- src/llm/models/cerebras.ts | 115 +++- src/llm/models/llm.int.ts | 4 +- src/modules/firestore/firestoreChatService.ts | 13 +- src/routes/chat/chat-routes.ts | 8 +- src/swe/discovery/selectFilesAgent.ts | 12 +- 37 files changed, 1480 insertions(+), 515 deletions(-) create mode 100644 frontend/public/images/splash.png diff --git a/frontend/angular.json b/frontend/angular.json index 9b369998..a82b93b8 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -56,7 +56,18 @@ "src/styles/styles.scss", "src/styles/tailwind.scss" ], - "scripts": [] + "scripts": [ + "node_modules/prismjs/prism.js", + "node_modules/prismjs/components/prism-csharp.min.js", + "node_modules/prismjs/components/prism-bash.min.js", + "node_modules/prismjs/components/prism-css.min.js", + "node_modules/prismjs/components/prism-hcl.min.js", + "node_modules/prismjs/components/prism-javascript.min.js", + "node_modules/prismjs/components/prism-json.min.js", + "node_modules/prismjs/components/prism-python.min.js", + "node_modules/prismjs/components/prism-typescript.min.js", + "node_modules/prismjs/components/prism-yaml.min.js" + ] }, "configurations": { "production": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6c62a45a..2da3ff05 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "fuse-angular", + "name": "@sophia/ui", "version": "20.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "fuse-angular", + "name": "@sophia/ui", "version": "20.0.0", "license": "https://themeforest.net/licenses/standard", "dependencies": { @@ -26,10 +26,10 @@ "highlight.js": "11.9.0", "lodash-es": "4.17.21", "luxon": "3.4.4", - "ng-apexcharts": "1.11.0", "ngx-markdown": "^18.1.0", "ngx-quill": "26.0.1", "perfect-scrollbar": "1.5.5", + "prismjs": "^1.29.0", "quill": "2.0.2", "rxjs": "7.8.1", "tslib": "2.6.2", @@ -39,6 +39,7 @@ "@angular-devkit/build-angular": "18.0.2", "@angular/cli": "18.0.2", "@angular/compiler-cli": "18.0.1", + "@ngx-rocket/scripts": "^5.2.3", "@tailwindcss/typography": "0.5.13", "@types/chroma-js": "2.4.4", "@types/crypto-js": "4.2.2", @@ -4602,6 +4603,121 @@ "webpack": "^5.54.0" } }, + "node_modules/@ngx-rocket/ascii-logo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ngx-rocket/ascii-logo/-/ascii-logo-1.1.0.tgz", + "integrity": "sha512-iGTX2tIAQmo/z2xiOsTAMxvO4wwHJkmutTzfJ+T/0Ebig54e1L2kgvctFRVHSj86UQUn8duzpuzcJJ4K4NK4jg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ngx-rocket/scripts": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@ngx-rocket/scripts/-/scripts-5.2.3.tgz", + "integrity": "sha512-024H2U1H/07ISfkt/PMXOp3sl3+bWbncsjPVgxzU9eAAkNxkt56Z/Llzy46l6h2fsrGkjiGvUMFKGHWQawCgJg==", + "dev": true, + "dependencies": { + "@ngx-rocket/ascii-logo": "^1.1.0", + "chalk": "^4.1.0", + "fs-extra": "^10.0.0", + "lodash.get": "^4.4.2", + "minimist": "^1.2.5" + }, + "bin": { + "ngx-scripts": "bin/ngx-scripts" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@ngx-rocket/scripts/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@ngx-rocket/scripts/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -11484,6 +11600,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -12272,20 +12394,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/ng-apexcharts": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.11.0.tgz", - "integrity": "sha512-bzZ2qMYpJJ1V/Yfp9eC5y7BuxrlZx4fc9ev9pNHW8qEs5B0yE5yW25WrLhoGTYsfeFZfiADWeAmZkceEiihpFw==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "^18.0.1", - "@angular/core": "^18.0.1", - "apexcharts": "^3.49.1", - "rxjs": "^6.5.5 || ^7.4.0" - } - }, "node_modules/ngx-markdown": { "version": "18.1.0", "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-18.1.0.tgz", @@ -13702,7 +13810,6 @@ "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "optional": true, "engines": { "node": ">=6" } diff --git a/frontend/package.json b/frontend/package.json index caa7619a..557c4987 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,10 +35,10 @@ "highlight.js": "11.9.0", "lodash-es": "4.17.21", "luxon": "3.4.4", - "ng-apexcharts": "1.11.0", "ngx-markdown": "^18.1.0", "ngx-quill": "26.0.1", "perfect-scrollbar": "1.5.5", + "prismjs": "^1.29.0", "quill": "2.0.2", "rxjs": "7.8.1", "tslib": "2.6.2", @@ -48,6 +48,7 @@ "@angular-devkit/build-angular": "18.0.2", "@angular/cli": "18.0.2", "@angular/compiler-cli": "18.0.1", + "@ngx-rocket/scripts": "^5.2.3", "@tailwindcss/typography": "0.5.13", "@types/chroma-js": "2.4.4", "@types/crypto-js": "4.2.2", @@ -75,4 +76,4 @@ "typescript": "5.4.5", "typescript-eslint": "8.2.0" } -} \ No newline at end of file +} diff --git a/frontend/public/images/splash.png b/frontend/public/images/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..c74d1f636f98dca6f93da2aefa59bbc4dbd81205 GIT binary patch literal 61943 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
-
+
- -
-

- Actions -

-
+
Actions
- +
diff --git a/frontend/src/app/modules/admin/apps/chat/chat.component.ts b/frontend/src/app/modules/admin/apps/chat/chat.component.ts index 13e91170..3c5e2e43 100644 --- a/frontend/src/app/modules/admin/apps/chat/chat.component.ts +++ b/frontend/src/app/modules/admin/apps/chat/chat.component.ts @@ -13,9 +13,4 @@ import { RouterOutlet } from '@angular/router'; standalone: true, imports: [RouterOutlet], }) -export class ChatComponent { - /** - * Constructor - */ - constructor() {} -} +export class ChatComponent {} diff --git a/frontend/src/app/modules/admin/apps/chat/chat.service.ts b/frontend/src/app/modules/admin/apps/chat/chat.service.ts index 67086476..1f29799c 100644 --- a/frontend/src/app/modules/admin/apps/chat/chat.service.ts +++ b/frontend/src/app/modules/admin/apps/chat/chat.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Chat, LlmMessage } from 'app/modules/admin/apps/chat/chat.types'; +import {Chat, ChatMessage, LlmMessage} from 'app/modules/admin/apps/chat/chat.types'; import { BehaviorSubject, Observable, @@ -10,7 +10,7 @@ import { switchMap, take, tap, - throwError, + throwError, catchError, } from 'rxjs'; @Injectable({ providedIn: 'root' }) @@ -21,7 +21,10 @@ export class ChatService { /** * Constructor */ - constructor(private _httpClient: HttpClient) {} + constructor(private _httpClient: HttpClient) { + console.log('ChatService') + this.getChats(); + } // ----------------------------------------------------------------------------------------------------- // @ Accessors @@ -73,6 +76,9 @@ export class ChatService { tap(() => { const currentChats = this._chats.value || []; this._chats.next(currentChats.filter(chat => chat.id !== chatId)); + if (this._chat.getValue().id === chatId) { + this._chat.next(null); + } }) ); } @@ -85,7 +91,7 @@ export class ChatService { getChatById(id: string): Observable { if(!id?.trim()) { console.log(`nullish chat id "${id}"`) - const chat: Chat = {messages:[], id: null, title: ''} + const chat: Chat = {messages:[], id: null, title: '', updatedAt: Date.now() } this._chat.next(chat); return this._chats } @@ -104,12 +110,18 @@ export class ChatService { const llmMsg = msg as LlmMessage return { ...msg, - value: llmMsg.text, + value: llmMsg.content, isMine: llmMsg.role === 'user' } - }) + }), + updatedAt: chat.updatedAt } - console.log(chat) + // this._chats doesn't have the messages, so we need to update it when we load a chat + const chats = this._chats.getValue() + const chatIndex = chats.findIndex(chat => chat.id === id); + chats[chatIndex] = chat; + this._chats.next(chats); + this._chat.next(chat); // Return the chat @@ -177,50 +189,12 @@ export class ChatService { ); } - /** - * Update chat - * - * @param id - * @param chat - */ - updateChat2(id: string, chat: Chat): Observable { - return this.chats$.pipe( - take(1), - switchMap((chats) => - this._httpClient - .patch('api/apps/chat/chat', { - id, - chat, - }) - .pipe( - map((updatedChat) => { - // Find the index of the updated chat - const index = chats.findIndex( - (item) => item.id === id - ); - - // Update the chat - chats[index] = updatedChat; - - // Update the chats - this._chats.next(chats); - - // Update the chat if it's selected - this._chat.next(updatedChat); - - // Return the updated chat - return updatedChat; - }) - ) - ) - ); - } /** * Reset the selected chat */ resetChat(): void { - this._chat.next({ id: '', messages: [], title: '' }); + this._chat.next({ id: '', messages: [], title: '', updatedAt: Date.now() }); } @@ -241,31 +215,43 @@ export class ChatService { map((data: any) => { const llmMessage = data.data; - const newMessages = [ + const newMessages: ChatMessage[] = [ { - value: message, + content: message, isMine: true, }, { - value: llmMessage, + content: llmMessage, isMine: false, }, ] - // // Find the index of the updated chat + // Find the index of the updated chat const index = chats.findIndex( (item) => item.id === chatId ); - // - // // Update the chat - const chat = chats[index]; + if(index < 0) { + console.log(`Couldn't find chat with id ${chatId} from ${chats.length} chats`); + } + + // Update the chat + const chat = chats[index]; + if(chat.messages === null || chat.messages === undefined) { + console.log(`nullish messages for ${JSON.stringify(chat)} at index ${index}`) + chat.messages = [] + } chat.messages.push(...newMessages); - // // Update the chats + + // Move the chat to the top of the list + chats.splice(index, 1); + chats.unshift(chat); + + // Update the chats this._chats.next(chats); - // - // // Update the chat if it's selected + + // Update the chat if it's selected this._chat.next(chat); - // - // // Return the updated chat + + // Return the updated chat return chat; }) ) @@ -274,12 +260,54 @@ export class ChatService { } /** - * Send an audio message * * @param chatId + * @param message * @param llmId - * @param audio */ + regenerateMessage(chatId: string, message: string, llmId: string): Observable { + if (!chatId?.trim() || !message?.trim() || !llmId?.trim()) { + return throwError(() => new Error('Invalid parameters for regeneration')); + } + + return this.chats$.pipe( + take(1), + switchMap((chats) => { + const chatIndex = chats.findIndex(item => item.id === chatId); + if (chatIndex === -1) { + return throwError(() => new Error(`Chat not found: ${chatId}`)); + } + + return this._httpClient + .post(`api/chat/${chatId}/regenerate`, { text: message, llmId }) + .pipe( + map((data: any) => { + const llmMessage = data.data; + const newMessage = { + value: llmMessage, + isMine: false, + llmId: llmId, + }; + + const chat = chats[chatIndex]; + chat.messages.push(newMessage); + chat.lastMessage = llmMessage; + + // Update states + this._chats.next(chats); + this._chat.next(chat); + + return chat; + }), + catchError(error => { + console.error('Error regenerating message:', error); + return throwError(() => new Error('Failed to regenerate message')); + }) + ); + }) + ); + } + sendAudioMessage(chatId: string, llmId: string, audio: Blob): Observable { return this.chats$.pipe( take(1), diff --git a/frontend/src/app/modules/admin/apps/chat/chat.types.ts b/frontend/src/app/modules/admin/apps/chat/chat.types.ts index 1c3ee645..2004e316 100644 --- a/frontend/src/app/modules/admin/apps/chat/chat.types.ts +++ b/frontend/src/app/modules/admin/apps/chat/chat.types.ts @@ -33,9 +33,11 @@ export interface Contact { }; } + + export interface LlmMessage { role: 'system' | 'user' | 'assistant'; - text: string; + content: string; /** The LLM which generated the text (only when role=assistant) */ llmId?: string; /** Set the cache_control flag with Claude models */ @@ -43,6 +45,17 @@ export interface LlmMessage { time?: number; } +export interface ChatMessage { + id?: string; + chatId?: string; + contactId?: string; + isMine?: boolean; + content?: string; + llmId?: string; + createdAt?: string; + generating?: boolean; +} + export interface Chat { id: string; title: string; @@ -51,13 +64,6 @@ export interface Chat { unreadCount?: number; lastMessage?: string; lastMessageAt?: string; - messages?: { - id?: string; - chatId?: string; - contactId?: string; - isMine?: boolean; - value?: string; - llmId?: string; - createdAt?: string; - }[]; + updatedAt: number; + messages?: ChatMessage[]; } diff --git a/frontend/src/app/modules/admin/apps/chat/chats/chats.component.html b/frontend/src/app/modules/admin/apps/chat/chats/chats.component.html index f52582ab..1f1450a1 100644 --- a/frontend/src/app/modules/admin/apps/chat/chats/chats.component.html +++ b/frontend/src/app/modules/admin/apps/chat/chats/chats.component.html @@ -92,6 +92,7 @@ > {{ chat.title }}
+
+ @if (hoveredChatId === chat.id) { - + }
diff --git a/frontend/src/app/modules/admin/apps/chat/chats/chats.component.ts b/frontend/src/app/modules/admin/apps/chat/chats/chats.component.ts index 017fa8c6..f65c4a04 100644 --- a/frontend/src/app/modules/admin/apps/chat/chats/chats.component.ts +++ b/frontend/src/app/modules/admin/apps/chat/chats/chats.component.ts @@ -17,6 +17,7 @@ import { RouterLink, RouterOutlet } from '@angular/router'; import { ChatService } from 'app/modules/admin/apps/chat/chat.service'; import { Chat } from 'app/modules/admin/apps/chat/chat.types'; import { Subject, takeUntil } from 'rxjs'; +import { FuseConfirmationService } from '@fuse/services/confirmation'; @Component({ selector: 'chat-chats', @@ -49,6 +50,7 @@ export class ChatsComponent implements OnInit, OnDestroy { constructor( private _chatService: ChatService, private _changeDetectorRef: ChangeDetectorRef, + private confirmationService: FuseConfirmationService, ) {} // ----------------------------------------------------------------------------------------------------- @@ -113,6 +115,23 @@ export class ChatsComponent implements OnInit, OnDestroy { ); } + /** + * Delete the current chat + */ + deleteChat(event: MouseEvent, chat: Chat): void { + // event.stopPropagation(); + this.confirmationService.open({ + message: 'Are you sure you want to delete this chat?', + }).afterClosed().subscribe((result) => { + console.log(result); + if(result === 'confirmed') { + this._chatService.deleteChat(chat.id).subscribe(() => { + // Do we need to handle if it's the currently selected chat? + }); + } + }); + } + /** * Track by function for ngFor loops * diff --git a/frontend/src/app/modules/admin/apps/chat/conversation/conversation.component.html b/frontend/src/app/modules/admin/apps/chat/conversation/conversation.component.html index 335dc367..0798a052 100644 --- a/frontend/src/app/modules/admin/apps/chat/conversation/conversation.component.html +++ b/frontend/src/app/modules/admin/apps/chat/conversation/conversation.component.html @@ -115,9 +115,9 @@
@@ -130,9 +130,9 @@
@@ -143,12 +143,41 @@ >
} + + -
+ - > - + + @if (!message.isMine) { + + + + }
@@ -180,22 +209,25 @@
+
+ + -
-
- + + diff --git a/frontend/src/app/modules/agents/new-agent/new-agent.component.ts b/frontend/src/app/modules/agents/new-agent/new-agent.component.ts index 70f03f27..8ec4998e 100644 --- a/frontend/src/app/modules/agents/new-agent/new-agent.component.ts +++ b/frontend/src/app/modules/agents/new-agent/new-agent.component.ts @@ -24,6 +24,7 @@ import { LlmService } from "../services/llm.service"; import { map } from "rxjs"; import { MatProgressSpinner } from "@angular/material/progress-spinner"; import { MatCheckboxModule } from "@angular/material/checkbox"; +import {MatCard, MatCardContent} from "@angular/material/card"; interface StartAgentResponse { data: { @@ -38,23 +39,25 @@ const defaultType/*: AgentType*/ = 'codegen'; templateUrl: './new-agent.component.html', encapsulation: ViewEncapsulation.None, standalone: true, - imports: [ - MatIconModule, - FormsModule, - MatFormFieldModule, - NgClass, - MatInputModule, - TextFieldModule, - ReactiveFormsModule, - MatButtonToggleModule, - MatButtonModule, - MatSelectModule, - MatOptionModule, - MatCheckboxModule, - MatChipsModule, - MatDatepickerModule, - MatProgressSpinner, - ], + imports: [ + MatIconModule, + FormsModule, + MatFormFieldModule, + NgClass, + MatInputModule, + TextFieldModule, + ReactiveFormsModule, + MatButtonToggleModule, + MatButtonModule, + MatSelectModule, + MatOptionModule, + MatCheckboxModule, + MatChipsModule, + MatDatepickerModule, + MatProgressSpinner, + MatCard, + MatCardContent, + ], }) export class NewAgentComponent implements OnInit { functions: string[] = []; diff --git a/frontend/src/app/modules/code-review/edit/code-review-edit.component.html b/frontend/src/app/modules/code-review/edit/code-review-edit.component.html index 57d390cd..5ff8318f 100644 --- a/frontend/src/app/modules/code-review/edit/code-review-edit.component.html +++ b/frontend/src/app/modules/code-review/edit/code-review-edit.component.html @@ -1,18 +1,15 @@
- -
-
-
-

- {{ configId ? 'Edit' : 'Create' }} Code Review Configuration -

-
+ + +
+
+
{{ configId ? 'Edit' : 'Create' }} Code Review Configuration
+
-
-
-
+ +
Description @@ -137,6 +134,6 @@

-
-
+ +
diff --git a/frontend/src/app/modules/code-review/edit/code-review-edit.component.ts b/frontend/src/app/modules/code-review/edit/code-review-edit.component.ts index 4432cfc6..1f58c16e 100644 --- a/frontend/src/app/modules/code-review/edit/code-review-edit.component.ts +++ b/frontend/src/app/modules/code-review/edit/code-review-edit.component.ts @@ -17,22 +17,25 @@ import {MatButtonModule} from "@angular/material/button"; import {MatFormFieldModule} from "@angular/material/form-field"; import {MatIconModule} from "@angular/material/icon"; import {MatInputModule} from "@angular/material/input"; +import {MatCard, MatCardContent} from "@angular/material/card"; @Component({ selector: 'app-code-review-edit', templateUrl: './code-review-edit.component.html', standalone: true, - imports: [ - CommonModule, - MatSnackBarModule, - ReactiveFormsModule, - MatButtonModule, - MatFormFieldModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, // Add this line - MatInputModule, // Add this line - ], + imports: [ + CommonModule, + MatSnackBarModule, + ReactiveFormsModule, + MatButtonModule, + MatFormFieldModule, + MatChipsModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + MatCard, + MatCardContent, + ], }) export class CodeReviewEditComponent implements OnInit { editForm: FormGroup; diff --git a/frontend/src/app/modules/profile/account/account.component.html b/frontend/src/app/modules/profile/account/account.component.html index 97ad701e..119c817a 100644 --- a/frontend/src/app/modules/profile/account/account.component.html +++ b/frontend/src/app/modules/profile/account/account.component.html @@ -55,48 +55,55 @@
LLM API Keys
- +
Anthropic
- +
OpenAI
- +
Groq
- +
TogetherAI
- +
Fireworks
- +
DeepSeek
+ +
+ + Cerebras + + +
diff --git a/frontend/src/app/modules/profile/account/account.component.ts b/frontend/src/app/modules/profile/account/account.component.ts index ede23372..70caebd9 100644 --- a/frontend/src/app/modules/profile/account/account.component.ts +++ b/frontend/src/app/modules/profile/account/account.component.ts @@ -64,6 +64,7 @@ export class SettingsAccountComponent implements OnInit { togetheraiKey: new FormControl(''), fireworksKey: new FormControl(''), deepseekKey: new FormControl(''), + cerebrasKey: new FormControl(''), }), functionConfig: new FormGroup({ GitHub: new FormGroup({ diff --git a/frontend/src/index.html b/frontend/src/index.html index a4c5e57b..4f56a8d6 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -44,9 +44,9 @@ - + + Sophia logo +
@@ -56,5 +56,6 @@ + diff --git a/package-lock.json b/package-lock.json index 308677a5..fdd96768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,8 +49,7 @@ "@types/axios": "^0.14.0", "@types/chai": "^4.3.16", "@types/pg": "^8.11.4", - "ai": "~3.4", - "anthropic-vertex-ai": "github:nalaso/anthropic-vertex-ai", + "ai": "3.4.20", "api": "^6.1.1", "axios": "^1.7.2", "axios-retry": "^4.1.0", @@ -62,7 +61,6 @@ "fast-glob": "^3.3.2", "fast-xml-parser": "^4.3.6", "fastify": "^4.26.2", - "fastify-cors": "^6.1.0", "fastify-healthcheck": "^4.4.0", "fastify-raw-body": "^4.3.0", "firebase-admin": "^12.1.0", @@ -265,13 +263,13 @@ } }, "node_modules/@ai-sdk/react": { - "version": "0.0.59", - "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-0.0.59.tgz", - "integrity": "sha512-1WbgO3J2/OoheMuNMxy5itJ3NVqOpqpAQxFNp7AoXgnDv4wDF4kTif61rTlKh7dCPvBHj2HXLmob+TrVFaWhYw==", + "version": "0.0.66", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-0.0.66.tgz", + "integrity": "sha512-hSHm+KR8/ciq2NksxZVyRIibODVrEKPbBrdAzHXI/tmFbpz/9/a/VibgX2+EIlj/M9K8Hkt1epxGLWKRwIfBwg==", "dependencies": { - "@ai-sdk/provider-utils": "1.0.19", - "@ai-sdk/ui-utils": "0.0.44", - "swr": "2.2.5" + "@ai-sdk/provider-utils": "1.0.22", + "@ai-sdk/ui-utils": "0.0.48", + "swr": "^2.2.5" }, "engines": { "node": ">=18" @@ -289,13 +287,46 @@ } } }, + "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@ai-sdk/solid": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@ai-sdk/solid/-/solid-0.0.47.tgz", - "integrity": "sha512-lVMxIxtuNqoo/TObSFGflEP2dUeJv7bfPQbS4jHTZGBNlyhgBRY2Xc19yNjA3QKRfvQNDVoQusqxn+18MiHJJQ==", + "version": "0.0.52", + "resolved": "https://registry.npmjs.org/@ai-sdk/solid/-/solid-0.0.52.tgz", + "integrity": "sha512-DPTWHLbMlrOMs8VAXW/finVLSlZDA2JbUKCH6fnlrr29L6Go9C1+xai92iBFF7GVb/bM/pVXXmuuoXWOGWknUQ==", "dependencies": { - "@ai-sdk/provider-utils": "1.0.19", - "@ai-sdk/ui-utils": "0.0.44" + "@ai-sdk/provider-utils": "1.0.22", + "@ai-sdk/ui-utils": "0.0.48" }, "engines": { "node": ">=18" @@ -309,14 +340,47 @@ } } }, + "node_modules/@ai-sdk/solid/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/solid/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@ai-sdk/svelte": { - "version": "0.0.49", - "resolved": "https://registry.npmjs.org/@ai-sdk/svelte/-/svelte-0.0.49.tgz", - "integrity": "sha512-gV0MhaWxkatjf7uJrCAHO3bWrihokNUwGhuMCgyG+y53lwJKAYhR0zCoDRM2HnTJ89fdnx/PVe3R9fOWEVY5qA==", + "version": "0.0.54", + "resolved": "https://registry.npmjs.org/@ai-sdk/svelte/-/svelte-0.0.54.tgz", + "integrity": "sha512-ZON1RjiKmFYYFB2ukswE2Q6Ca/MjfFlpwCjtmFYPDa6BAXS63ZcgId9l2BkbmOJLJWzZFwmlo905iaNFQDaWAw==", "dependencies": { - "@ai-sdk/provider-utils": "1.0.19", - "@ai-sdk/ui-utils": "0.0.44", - "sswr": "2.1.0" + "@ai-sdk/provider-utils": "1.0.22", + "@ai-sdk/ui-utils": "0.0.48", + "sswr": "^2.1.0" }, "engines": { "node": ">=18" @@ -330,16 +394,82 @@ } } }, + "node_modules/@ai-sdk/svelte/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/svelte/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@ai-sdk/ui-utils": { - "version": "0.0.44", - "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-0.0.44.tgz", - "integrity": "sha512-0qiyun/n5zqJzQs/WfQT86dZE5DiDhSHJc7b7ZGLYvNMztHkRQmak2zUCZP4IyGVZEicyEPQK6NEEpBgkmd3Dg==", + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-0.0.48.tgz", + "integrity": "sha512-kKol0xTG4OFDqu77WqVVpPvpABXIXs67Ivlqg6qxD69pD/cFZ2zQVliBcDxPBJoz08Oyf5BxU5Ht9i8J99QKcA==", "dependencies": { - "@ai-sdk/provider": "0.0.23", - "@ai-sdk/provider-utils": "1.0.19", - "json-schema": "0.4.0", - "secure-json-parse": "2.7.0", - "zod-to-json-schema": "3.23.2" + "@ai-sdk/provider": "0.0.26", + "@ai-sdk/provider-utils": "1.0.22", + "json-schema": "^0.4.0", + "secure-json-parse": "^2.7.0", + "zod-to-json-schema": "^3.23.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/ui-utils/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" }, "engines": { "node": ">=18" @@ -354,13 +484,13 @@ } }, "node_modules/@ai-sdk/vue": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@ai-sdk/vue/-/vue-0.0.51.tgz", - "integrity": "sha512-6RjuuRGf749EjnsfbETJpF0fmq6a1lF6qUUUnd/Q1Ojf0tX8fI4qwvNykbECZHWuIj42EqZ3HDuNNR9c8oG4rA==", + "version": "0.0.57", + "resolved": "https://registry.npmjs.org/@ai-sdk/vue/-/vue-0.0.57.tgz", + "integrity": "sha512-p4wwEO/KRfZaRUZu7UIOlb0odHDHTuUsFEFQ2zlFQK0IlCoTvtqJkSyQl26o+6whiUUUNVqZepluX+WHGSrAaA==", "dependencies": { - "@ai-sdk/provider-utils": "1.0.19", - "@ai-sdk/ui-utils": "0.0.44", - "swrv": "1.0.4" + "@ai-sdk/provider-utils": "1.0.22", + "@ai-sdk/ui-utils": "0.0.48", + "swrv": "^1.0.4" }, "engines": { "node": ">=18" @@ -374,6 +504,39 @@ } } }, + "node_modules/@ai-sdk/vue/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/vue/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1348,18 +1511,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } @@ -1442,12 +1605,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "peer": true, "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1469,14 +1632,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "peer": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -8463,13 +8625,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.8.tgz", - "integrity": "sha512-Uzlxp91EPjfbpeO5KtC0KnXPkuTfGsNDeaKQJxQN718uz+RqDYarEf7UhQJGK+ZYloD2taUbHTI2J4WrUaZQNA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "peer": true, "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.8", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" @@ -8482,26 +8644,26 @@ "peer": true }, "node_modules/@vue/compiler-dom": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.8.tgz", - "integrity": "sha512-GUNHWvoDSbSa5ZSHT9SnV5WkStWfzJwwTd6NMGzilOE/HM5j+9EB9zGXdtu/fCNEmctBqMs6C9SvVPpVPuk1Eg==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "peer": true, "dependencies": { - "@vue/compiler-core": "3.5.8", - "@vue/shared": "3.5.8" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.8.tgz", - "integrity": "sha512-taYpngQtSysrvO9GULaOSwcG5q821zCoIQBtQQSx7Uf7DxpR6CIHR90toPr9QfDD2mqHQPCSgoWBvJu0yV9zjg==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", "peer": true, "dependencies": { "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.8", - "@vue/compiler-dom": "3.5.8", - "@vue/compiler-ssr": "3.5.8", - "@vue/shared": "3.5.8", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.47", @@ -8515,63 +8677,63 @@ "peer": true }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.8.tgz", - "integrity": "sha512-W96PtryNsNG9u0ZnN5Q5j27Z/feGrFV6zy9q5tzJVyJaLiwYxvC0ek4IXClZygyhjm+XKM7WD9pdKi/wIRVC/Q==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.8", - "@vue/shared": "3.5.8" + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/reactivity": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.8.tgz", - "integrity": "sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", + "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", "peer": true, "dependencies": { - "@vue/shared": "3.5.8" + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.8.tgz", - "integrity": "sha512-fJuPelh64agZ8vKkZgp5iCkPaEqFJsYzxLk9vSC0X3G8ppknclNDr61gDc45yBGTaN5Xqc1qZWU3/NoaBMHcjQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", + "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.8", - "@vue/shared": "3.5.8" + "@vue/reactivity": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.8.tgz", - "integrity": "sha512-DpAUz+PKjTZPUOB6zJgkxVI3GuYc2iWZiNeeHQUw53kdrparSTG6HeXUrYDjaam8dVsCdvQxDz6ZWxnyjccUjQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", + "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.8", - "@vue/runtime-core": "3.5.8", - "@vue/shared": "3.5.8", + "@vue/reactivity": "3.5.12", + "@vue/runtime-core": "3.5.12", + "@vue/shared": "3.5.12", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.8.tgz", - "integrity": "sha512-7AmC9/mEeV9mmXNVyUIm1a1AjUhyeeGNbkLh39J00E7iPeGks8OGRB5blJiMmvqSh8SkaS7jkLWSpXtxUCeagA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", + "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.5.8", - "@vue/shared": "3.5.8" + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { - "vue": "3.5.8" + "vue": "3.5.12" } }, "node_modules/@vue/shared": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.8.tgz", - "integrity": "sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", "peer": true }, "node_modules/abbrev": { @@ -8702,24 +8864,23 @@ } }, "node_modules/ai": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ai/-/ai-3.4.0.tgz", - "integrity": "sha512-79MZD3zoljLBf7Jtmd39bhDA3W7WPsozIi99sLrZlgxzh3JBX1XtCxHwrUYHQtAxl21BGzHmXpgNfLiQjdTAfA==", - "dependencies": { - "@ai-sdk/provider": "0.0.23", - "@ai-sdk/provider-utils": "1.0.19", - "@ai-sdk/react": "0.0.59", - "@ai-sdk/solid": "0.0.47", - "@ai-sdk/svelte": "0.0.49", - "@ai-sdk/ui-utils": "0.0.44", - "@ai-sdk/vue": "0.0.51", + "version": "3.4.20", + "resolved": "https://registry.npmjs.org/ai/-/ai-3.4.20.tgz", + "integrity": "sha512-UnU7VoIzwx0sql5W+39oLazmiovNRqX+AUkvLwuzlRWrp0O9BjqA/ZlvdmfBxhBPZMruzvL659q8ET38l04fUw==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "@ai-sdk/provider-utils": "1.0.22", + "@ai-sdk/react": "0.0.66", + "@ai-sdk/solid": "0.0.52", + "@ai-sdk/svelte": "0.0.54", + "@ai-sdk/ui-utils": "0.0.48", + "@ai-sdk/vue": "0.0.57", "@opentelemetry/api": "1.9.0", "eventsource-parser": "1.1.2", - "json-schema": "0.4.0", + "json-schema": "^0.4.0", "jsondiffpatch": "0.6.0", - "nanoid": "3.3.6", - "secure-json-parse": "2.7.0", - "zod-to-json-schema": "3.23.2" + "secure-json-parse": "^2.7.0", + "zod-to-json-schema": "^3.23.3" }, "engines": { "node": ">=18" @@ -8749,6 +8910,39 @@ } } }, + "node_modules/ai/node_modules/@ai-sdk/provider": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.26.tgz", + "integrity": "sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ai/node_modules/@ai-sdk/provider-utils": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.22.tgz", + "integrity": "sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==", + "dependencies": { + "@ai-sdk/provider": "0.0.26", + "eventsource-parser": "^1.1.2", + "nanoid": "^3.3.7", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/ai/node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -8757,23 +8951,6 @@ "node": ">=8.0.0" } }, - "node_modules/ai/node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8904,71 +9081,6 @@ "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", "dev": true }, - "node_modules/anthropic-vertex-ai": { - "version": "1.0.0", - "resolved": "git+ssh://git@github.com/nalaso/anthropic-vertex-ai.git#1733d089505e67496cdc83654af13e85917073b7", - "dependencies": { - "@ai-sdk/provider": "0.0.22", - "@ai-sdk/provider-utils": "1.0.17", - "google-auth-library": "^9.12.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.0.0" - } - }, - "node_modules/anthropic-vertex-ai/node_modules/@ai-sdk/provider": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-0.0.22.tgz", - "integrity": "sha512-smZ1/2jL/JSKnbhC6ama/PxI2D/psj+YAe0c0qpd5ComQCNFltg72VFf0rpUSFMmFuj1pCCNoBOCrvyl8HTZHQ==", - "dependencies": { - "json-schema": "0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/anthropic-vertex-ai/node_modules/@ai-sdk/provider-utils": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-1.0.17.tgz", - "integrity": "sha512-2VyeTH5DQ6AxqvwdyytKIeiZyYTyJffpufWjE67zM2sXMIHgYl7fivo8m5wVl6Cbf1dFPSGKq//C9s+lz+NHrQ==", - "dependencies": { - "@ai-sdk/provider": "0.0.22", - "eventsource-parser": "1.1.2", - "nanoid": "3.3.6", - "secure-json-parse": "2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/anthropic-vertex-ai/node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -9281,9 +9393,9 @@ "license": "Python-2.0" }, "node_modules/aria-query": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", - "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "peer": true, "engines": { "node": ">= 0.4" @@ -13817,36 +13929,6 @@ "toad-cache": "^3.3.0" } }, - "node_modules/fastify-cors": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/fastify-cors/-/fastify-cors-6.1.0.tgz", - "integrity": "sha512-QBKz32IoY/iuT74CunRY1XOSpjSTIOh9E3FxulXIBhd0D2vdgG0kDvy0eG6HA/88sRfWHeba43LkGEXPz0Rh8g==", - "deprecated": "Please use @fastify/cors@7.0.0 instead", - "dependencies": { - "fastify-cors-deprecated": "npm:fastify-cors@6.0.3", - "process-warning": "^1.0.0" - } - }, - "node_modules/fastify-cors-deprecated": { - "name": "fastify-cors", - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/fastify-cors/-/fastify-cors-6.0.3.tgz", - "integrity": "sha512-fMbXubKKyBHHCfSBtsCi3+7VyVRdhJQmGes5gM+eGKkRErCdm0NaYO0ozd31BQBL1ycoTIjbqOZhJo4RTF/Vlg==", - "dependencies": { - "fastify-plugin": "^3.0.0", - "vary": "^1.1.2" - } - }, - "node_modules/fastify-cors-deprecated/node_modules/fastify-plugin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.1.tgz", - "integrity": "sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==" - }, - "node_modules/fastify-cors/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, "node_modules/fastify-healthcheck": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/fastify-healthcheck/-/fastify-healthcheck-4.4.0.tgz", @@ -18535,9 +18617,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -24289,15 +24371,6 @@ "node": ">=0.6.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -25220,16 +25293,16 @@ } }, "node_modules/vue": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.8.tgz", - "integrity": "sha512-hvuvuCy51nP/1fSRvrrIqTLSvrSyz2Pq+KQ8S8SXCxTWVE0nMaOnSDnSOxV1eYmGfvK7mqiwvd1C59CEEz7dAQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", + "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.8", - "@vue/compiler-sfc": "3.5.8", - "@vue/runtime-dom": "3.5.8", - "@vue/server-renderer": "3.5.8", - "@vue/shared": "3.5.8" + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-sfc": "3.5.12", + "@vue/runtime-dom": "3.5.12", + "@vue/server-renderer": "3.5.12", + "@vue/shared": "3.5.12" }, "peerDependencies": { "typescript": "*" @@ -25896,9 +25969,9 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz", - "integrity": "sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw==", + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz", + "integrity": "sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==", "peerDependencies": { "zod": "^3.23.3" } diff --git a/package.json b/package.json index ca626a3a..f0cf40a7 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,7 @@ "@types/axios": "^0.14.0", "@types/chai": "^4.3.16", "@types/pg": "^8.11.4", - "ai": "~3.4", - "anthropic-vertex-ai": "github:nalaso/anthropic-vertex-ai", + "ai": "3.4.20", "api": "^6.1.1", "axios": "^1.7.2", "axios-retry": "^4.1.0", @@ -102,7 +101,6 @@ "fast-glob": "^3.3.2", "fast-xml-parser": "^4.3.6", "fastify": "^4.26.2", - "fastify-cors": "^6.1.0", "fastify-healthcheck": "^4.4.0", "fastify-raw-body": "^4.3.0", "firebase-admin": "^12.1.0", diff --git a/src/agent/cachingCodeGenAgentRunner.ts b/src/agent/cachingCodeGenAgentRunner.ts index faac1433..50e13903 100644 --- a/src/agent/cachingCodeGenAgentRunner.ts +++ b/src/agent/cachingCodeGenAgentRunner.ts @@ -128,13 +128,13 @@ export async function runCachingCodegenAgent(agent: AgentContext): Promise response as per the system instructions provided given the user request, available functions, memory items and recent function call history' + content: planningPrompt, // 'Generate a response as per the system instructions provided given the user request, available functions, memory items and recent function call history' }; - agent.messages[6] = { role: 'assistant', text: '' }; + agent.messages[6] = { role: 'assistant', content: '' }; agent.messages.length = 7; // If we've restarted remove any extra messages const agentPlanResponse: string = `\n${await agentLLM.generateTextFromMessages(agent.messages, { id: 'dynamicAgentPlan', stopSequences, temperature: 0.6, })}`; - agent.messages[6] = { role: 'assistant', text: agentPlanResponse }; + agent.messages[6] = { role: 'assistant', content: agentPlanResponse }; // Code gen for function calling ----------- agent.messages[7] = { role: 'user', - text: codingPrompt, // 'Generate a coding response as per the system instructions provided given the user request, memory items, recent function call history and plan' + content: codingPrompt, // 'Generate a coding response as per the system instructions provided given the user request, memory items, recent function call history and plan' }; - agent.messages[8] = { role: 'assistant', text: '' }; + agent.messages[8] = { role: 'assistant', content: '' }; const agentCodeResponse: string = `\n${await agentLLM.generateTextFromMessages(agent.messages, { id: 'dynamicAgentCode', stopSequences, temperature: 0.7, })}`; console.log(agentCodeResponse); - agent.messages[8] = { role: 'assistant', text: agentCodeResponse }; + agent.messages[8] = { role: 'assistant', content: agentCodeResponse }; const llmPythonCode = extractPythonCode(agentCodeResponse); // Function calling ---------------- @@ -327,7 +327,7 @@ main()`.trim(); // TODO output any saved memory items agent.messages[9] = { role: 'user', - text: `${pythonScriptResult}\nReview the results of the scripts and make any observations about the output/errors, then provide an updated planning response.`, + content: `${pythonScriptResult}\nReview the results of the scripts and make any observations about the output/errors, then provide an updated planning response.`, }; currentFunctionHistorySize = agent.functionCallHistory.length; diff --git a/src/chat/chatService.test.ts b/src/chat/chatService.test.ts index 988a6b82..6885ea25 100644 --- a/src/chat/chatService.test.ts +++ b/src/chat/chatService.test.ts @@ -15,8 +15,8 @@ export function runChatServiceTests(createService: () => ChatService, beforeEach const sampleChat: Chat = { id: 'test-chat-id', messages: [ - { role: 'user', text: 'Hello' }, - { role: 'assistant', text: 'Hi there! How can I help you?' }, + { role: 'user', content: 'Hello' }, + { role: 'assistant', content: 'Hi there! How can I help you?' }, ], updatedAt: Date.now(), userId: SINGLE_USER_ID, @@ -63,7 +63,7 @@ export function runChatServiceTests(createService: () => ChatService, beforeEach userId: SINGLE_USER_ID, visibility: 'private', title: 'test', - messages: [{ role: 'user', text: 'Parent message' }], + messages: [{ role: 'user', content: 'Parent message' }], updatedAt: Date.now(), parentId: undefined, rootId: undefined, @@ -77,7 +77,7 @@ export function runChatServiceTests(createService: () => ChatService, beforeEach rootId: parentChat.id, title: 'test', updatedAt: Date.now(), - messages: [{ role: 'assistant', text: 'Child message' }], + messages: [{ role: 'assistant', content: 'Child message' }], }; await service.saveChat(parentChat); diff --git a/src/chat/chatTypes.ts b/src/chat/chatTypes.ts index 74e85989..58331c22 100644 --- a/src/chat/chatTypes.ts +++ b/src/chat/chatTypes.ts @@ -15,6 +15,8 @@ export interface Chat { export type ChatPreview = Omit; +export const CHAT_PREVIEW_KEYS: Array = ['id', 'userId', 'visibility', 'title', 'updatedAt', 'parentId', 'rootId']; + export interface ChatList { chats: ChatPreview[]; hasMore: boolean; diff --git a/src/cli/util.ts b/src/cli/util.ts index bc357cf2..99b7055b 100644 --- a/src/cli/util.ts +++ b/src/cli/util.ts @@ -7,7 +7,7 @@ import { GitLab } from '#functions/scm/gitlab'; import { FileSystemService } from '#functions/storage/fileSystemService'; import { Claude3_Opus, ClaudeLLMs } from '#llm/models/anthropic'; -import { Claude3_5_Sonnet_Vertex, Claude3_Haiku_Vertex, Claude3_Sonnet_Vertex, ClaudeVertexLLMs } from '#llm/models/anthropic-vertex'; +import { Claude3_5_Sonnet_Vertex, Claude3_Haiku_Vertex, ClaudeVertexLLMs } from '#llm/models/anthropic-vertex'; import { GPT4o } from '#llm/models/openai'; import { Gemini_1_5_Pro } from '#llm/models/vertexai'; import { MultiLLM } from '#llm/multi-llm'; diff --git a/src/llm/base-llm.ts b/src/llm/base-llm.ts index d68bdf78..2108cec3 100644 --- a/src/llm/base-llm.ts +++ b/src/llm/base-llm.ts @@ -100,11 +100,10 @@ export abstract class BaseLLM implements LLM { async generateTextFromMessages(llmMessages: LlmMessage[], opts?: GenerateTextOptions): Promise { return withActiveSpan(`generateTextFromMessages ${opts?.id ?? ''}`, async (span) => { const messages: CoreMessage[] = llmMessages.map((msg) => { - const coreMsg: CoreMessage = { role: msg.role, content: msg.text }; if (msg.cache === 'ephemeral') { - coreMsg.experimental_providerMetadata = { anthropic: { cacheControl: { type: 'ephemeral' } } }; + msg.experimental_providerMetadata = { anthropic: { cacheControl: { type: 'ephemeral' } } }; } - return coreMsg; + return msg; }); const prompt = messages.map((m) => m.content).join('\n'); @@ -177,11 +176,10 @@ export abstract class BaseLLM implements LLM { async streamText(llmMessages: LlmMessage[], onChunk: ({ string }) => void, opts?: GenerateTextOptions): Promise> { return withActiveSpan(`streamText ${opts?.id ?? ''}`, async (span) => { const messages: CoreMessage[] = llmMessages.map((msg) => { - const coreMsg: CoreMessage = { role: msg.role, content: msg.text }; if (msg.cache === 'ephemeral') { - coreMsg.experimental_providerMetadata = { anthropic: { cacheControl: { type: 'ephemeral' } } }; + msg.experimental_providerMetadata = { anthropic: { cacheControl: { type: 'ephemeral' } } }; } - return coreMsg; + return msg; }); const prompt = messages.map((m) => m.content).join('\n'); diff --git a/src/llm/llm.ts b/src/llm/llm.ts index c59990fd..7393eb96 100644 --- a/src/llm/llm.ts +++ b/src/llm/llm.ts @@ -1,5 +1,5 @@ // https://github.com/AgentOps-AI/tokencost/blob/main/tokencost/model_prices.json -import { StreamTextResult } from 'ai'; +import { CoreAssistantMessage, CoreMessage, CoreSystemMessage, CoreToolMessage, CoreUserMessage, FilePart, ImagePart, StreamTextResult, TextPart } from 'ai'; export interface GenerateTextOptions { type?: 'text' | 'json'; @@ -31,21 +31,27 @@ export type GenerateJsonOptions = Omit; */ export type GenerateFunctionOptions = Omit; -export interface LlmMessage { - role: 'system' | 'user' | 'assistant'; - text: string; +type AiMessage = CoreSystemMessage | CoreUserMessage | CoreAssistantMessage | CoreToolMessage; + +export type LlmMessage = AiMessage & { + // /** + // * TextPart { type: "text" , text: string } + // * ImagePart { type: "image", image: string | Uint8Array | ArrayBuffer | Buffer | URL, mimeType?: string } + // * FilePart { type: "file", data: string | Uint8Array | ArrayBuffer | Buffer | URL, mimeType: string } + // */ + // content: string | Array; /** The LLM which generated the text (only when role=assistant) */ llmId?: string; /** Set the cache_control flag with Claude models */ cache?: 'ephemeral'; /** Time the message was sent */ time?: number; -} +}; export function system(text: string, cache = false): LlmMessage { return { role: 'system', - text: text, + content: text, cache: cache ? 'ephemeral' : undefined, }; } @@ -53,7 +59,7 @@ export function system(text: string, cache = false): LlmMessage { export function user(text: string, cache = false): LlmMessage { return { role: 'user', - text: text, + content: text, cache: cache ? 'ephemeral' : undefined, }; } @@ -66,7 +72,7 @@ export function user(text: string, cache = false): LlmMessage { export function assistant(text: string): LlmMessage { return { role: 'assistant', - text: text, + content: text, }; } diff --git a/src/llm/llmCallService/llmCallService.ts b/src/llm/llmCallService/llmCallService.ts index cdaa70e0..787812b2 100644 --- a/src/llm/llmCallService/llmCallService.ts +++ b/src/llm/llmCallService/llmCallService.ts @@ -1,4 +1,4 @@ -import { CreateLlmRequest, LlmCall, LlmRequest } from '#llm/llmCallService/llmCall'; +import { CreateLlmRequest, LlmCall } from '#llm/llmCallService/llmCall'; export interface CallerId { agentId?: string; diff --git a/src/llm/llmFactory.ts b/src/llm/llmFactory.ts index 58b5e9e3..f3a08fc5 100644 --- a/src/llm/llmFactory.ts +++ b/src/llm/llmFactory.ts @@ -2,6 +2,7 @@ import { AgentLLMs } from '#agent/agentContextTypes'; import { LLM } from '#llm/llm'; import { anthropicLLMRegistry } from '#llm/models/anthropic'; import { anthropicVertexLLMRegistry } from '#llm/models/anthropic-vertex'; +import { cerebrasLLMRegistry } from '#llm/models/cerebras'; import { deepseekLLMRegistry } from '#llm/models/deepseek'; import { fireworksLLMRegistry } from '#llm/models/fireworks'; import { groqLLMRegistry } from '#llm/models/groq'; @@ -23,6 +24,7 @@ export const LLM_FACTORY: Record LLM> = { ...togetherLLMRegistry(), ...vertexLLMRegistry(), ...deepseekLLMRegistry(), + ...cerebrasLLMRegistry(), ...ollamaLLMRegistry(), ...blueberryLLMRegistry(), ...{ 'mock:mock': () => mockLLM }, diff --git a/src/llm/models/anthropic-vertex.ts b/src/llm/models/anthropic-vertex.ts index 26685946..86794842 100644 --- a/src/llm/models/anthropic-vertex.ts +++ b/src/llm/models/anthropic-vertex.ts @@ -211,12 +211,12 @@ class AnthropicVertexLLM extends BaseLLM { let systemPrompt: string | undefined; if (messages[0].role === 'system') { - systemPrompt = messages[0].text; + systemPrompt = messages[0].content as string; span.setAttribute('systemPrompt', systemPrompt); messages = messages.slice(1); } - const userPrompt = messages.map((msg) => msg.text).join('\n'); + const userPrompt = messages.map((msg) => msg.content).join('\n'); span.setAttributes({ userPrompt, @@ -240,13 +240,79 @@ class AnthropicVertexLLM extends BaseLLM { let systemMessage: Anthropic.Messages.TextBlockParam[] | undefined = undefined; if (messages[0].role === 'system') { const message = messages.splice(0, 1)[0]; - systemMessage = [{ type: 'text', text: message.text }]; + systemMessage = [{ type: 'text', text: message.content as string }]; // if(source.cache) // systemMessage[0].cacheControl = 'ephemeral' } + /* + The Anthropic types are + export interface MessageParam { + content: string | Array; + + role: 'user' | 'assistant'; + } + export interface TextBlockParam { + text: string; + + type: 'text'; + } + export interface ImageBlockParam { + source: ImageBlockParam.Source; + + type: 'image'; + } + + export namespace ImageBlockParam { + export interface Source { + data: string; + + media_type: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp'; + + type: 'base64'; + } + } + */ + const anthropicMessages: Anthropic.Messages.MessageParam[] = messages.map((message) => { - return { role: message.role as 'user' | 'assistant', content: message.text }; + let content: string | Array; + + if (typeof message.content === 'string') { + content = message.content; + } else if (Array.isArray(message.content)) { + content = message.content.map((part) => { + if (part.type === 'text') { + return { + type: 'text', + text: part.text, + } as Anthropic.Messages.TextBlockParam; + } + if (part.type === 'image') { + return { + type: 'image', + source: { + type: 'base64', + data: part.image.toString(), + media_type: part.mimeType || 'image/png', + }, + } as Anthropic.Messages.ImageBlockParam; + // } else if (part.type === 'file') { + // // Convert files to text representation since Anthropic doesn't support files + // return { + // type: 'text', + // text: `[File attachment]`, + // } as Anthropic.Messages.TextBlockParam; + } + throw new Error(`Unsupported message type ${part.type}`); + }); + } else { + content = '[No content]'; + } + + return { + role: message.role as 'user' | 'assistant', + content, + }; }); message = await this.api().messages.create({ system: systemMessage, diff --git a/src/llm/models/cerebras.ts b/src/llm/models/cerebras.ts index 88b364eb..f6008cc3 100644 --- a/src/llm/models/cerebras.ts +++ b/src/llm/models/cerebras.ts @@ -1,14 +1,19 @@ import Cerebras from '@cerebras/cerebras_cloud_sdk'; -import { agentContext } from '#agent/agentContextLocalStorage'; -import { addCost } from '#agent/agentContextLocalStorage'; +import { CompletionCreateParams, CompletionCreateParamsNonStreaming } from '@cerebras/cerebras_cloud_sdk/src/resources/chat/completions'; +import { addCost, agentContext } from '#agent/agentContextLocalStorage'; import { AgentLLMs } from '#agent/agentContextTypes'; import { LlmCall } from '#llm/llmCallService/llmCall'; +import { logger } from '#o11y/logger'; import { withActiveSpan } from '#o11y/trace'; import { currentUser } from '#user/userService/userContext'; import { appContext } from '../../app'; import { RetryableError } from '../../cache/cacheRetry'; import { BaseLLM } from '../base-llm'; -import { GenerateTextOptions, LLM, combinePrompts } from '../llm'; +import { GenerateTextOptions, LLM, LlmMessage, combinePrompts } from '../llm'; + +import SystemMessageRequest = CompletionCreateParams.SystemMessageRequest; +import AssistantMessageRequest = CompletionCreateParams.AssistantMessageRequest; +import UserMessageRequest = CompletionCreateParams.UserMessageRequest; export const CEREBRAS_SERVICE = 'cerebras'; @@ -85,6 +90,107 @@ export class CerebrasLLM extends BaseLLM { return this._client; } + async generateTextFromMessages(llmMessages: LlmMessage[], opts?: GenerateTextOptions): Promise { + return withActiveSpan(`generateTextFromMessages ${opts?.id ?? ''}`, async (span) => { + const messages: Array< + CompletionCreateParams.SystemMessageRequest | CompletionCreateParams.UserMessageRequest | CompletionCreateParams.AssistantMessageRequest + > = llmMessages.map((msg) => { + switch (msg.role) { + case 'system': { + const system: SystemMessageRequest = { + role: 'system', + content: msg.content as string, + }; + return system; + } + case 'user': { + const user: UserMessageRequest = { + role: 'user', + content: msg.content as string, + }; + return user; + } + case 'assistant': { + const assistant: AssistantMessageRequest = { + role: 'assistant', + content: msg.content as string, + }; + return assistant; + } + default: + throw new Error(`Unsupported role ${msg.role}`); + } + }); + + span.setAttributes({ + model: this.model, + service: this.service, + caller: agentContext()?.callStack.at(-1) ?? '', + }); + if (opts?.id) span.setAttribute('id', opts.id); + + const llmCallSave: Promise = appContext().llmCallService.saveRequest({ + messages: llmMessages, + llmId: this.getId(), + agentId: agentContext()?.agentId, + callStack: agentContext()?.callStack.join(' > '), + }); + const requestTime = Date.now(); + + try { + const params: CompletionCreateParamsNonStreaming = { + messages, + model: this.model, + temperature: opts?.temperature ?? undefined, + top_p: opts?.topP ?? undefined, + stop: opts?.stopSequences, + }; + const completion = await this.client().chat.completions.create(params); + + const responseText = completion.choices[0]?.message?.content || ''; + const finishTime = Date.now(); + const llmCall: LlmCall = await llmCallSave; + + const inputTokens = completion.usage.prompt_tokens; + const outputTokens = completion.usage.completion_tokens; + const prompt = messages.map((m) => m.content).join('\n'); + const inputCost = this.calculateInputCost(prompt); + const outputCost = this.calculateOutputCost(responseText); + const cost = inputCost + outputCost; + addCost(cost); + + llmCall.responseText = responseText; + llmCall.timeToFirstToken = null; + llmCall.totalTime = finishTime - requestTime; + llmCall.cost = cost; + llmCall.inputTokens = inputTokens; + llmCall.outputTokens = outputTokens; + + span.setAttributes({ + inputTokens, + outputTokens, + response: responseText, + inputCost: inputCost.toFixed(4), + outputCost: outputCost.toFixed(4), + cost: cost.toFixed(4), + outputChars: responseText.length, + callStack: agentContext()?.callStack.join(' > '), + }); + + try { + await appContext().llmCallService.saveResponse(llmCall); + } catch (e) { + logger.error(e); + } + + return responseText; + } catch (e) { + if (this.isRetryableError(e)) throw new RetryableError(e); + throw e; + } + }); + } + async generateText(userPrompt: string, systemPrompt?: string, opts?: GenerateTextOptions): Promise { return withActiveSpan(`generateText ${opts?.id ?? ''}`, async (span) => { const prompt = combinePrompts(userPrompt, systemPrompt); @@ -124,6 +230,9 @@ export class CerebrasLLM extends BaseLLM { const completion = await this.client().chat.completions.create({ messages, model: this.model, + temperature: opts?.temperature ?? undefined, + top_p: opts?.topP ?? undefined, + stop: opts?.stopSequences, // Cerebras uses 'stop' instead of 'stop_sequences' }); const responseText = completion.choices[0]?.message?.content || ''; diff --git a/src/llm/models/llm.int.ts b/src/llm/models/llm.int.ts index d89a322d..9e832fc7 100644 --- a/src/llm/models/llm.int.ts +++ b/src/llm/models/llm.int.ts @@ -17,11 +17,11 @@ describe('LLMs', () => { const SKY_MESSAGES: LlmMessage[] = [ { role: 'system', - text: 'Answer in one word.', + content: 'Answer in one word.', }, { role: 'user', - text: 'What colour is the day sky? (Hint: starts with b)', + content: 'What colour is the day sky? (Hint: starts with b)', }, ]; diff --git a/src/modules/firestore/firestoreChatService.ts b/src/modules/firestore/firestoreChatService.ts index be1cf97a..b6e56176 100644 --- a/src/modules/firestore/firestoreChatService.ts +++ b/src/modules/firestore/firestoreChatService.ts @@ -1,6 +1,6 @@ import { randomUUID } from 'crypto'; import { Firestore } from '@google-cloud/firestore'; -import { Chat, ChatPreview, ChatService } from '#chat/chatTypes'; +import { CHAT_PREVIEW_KEYS, Chat, ChatPreview, ChatService } from '#chat/chatTypes'; import { logger } from '#o11y/logger'; import { span } from '#o11y/trace'; import { currentUser } from '#user/userService/userContext'; @@ -38,6 +38,13 @@ export class FirestoreChatService implements ChatService { rootId: data.rootId, messages: data.messages, }; + + // Backwards compatability + for (const message of chat.messages) { + const oldMessage = message as any; + if (oldMessage.text) message.content = oldMessage.text; + } + if (chat.visibility !== 'private' && chat.userId !== currentUser().id) { throw new Error('Chat not visible.'); } @@ -70,12 +77,14 @@ export class FirestoreChatService implements ChatService { } @span() - async listChats(startAfterId?: string, limit = 50): Promise<{ chats: ChatPreview[]; hasMore: boolean }> { + async listChats(startAfterId?: string, limit = 100): Promise<{ chats: ChatPreview[]; hasMore: boolean }> { try { const userId = currentUser().id; + logger.info(`list ${limit} chats for ${userId} ${startAfterId ? `after ${startAfterId}` : ''}`); let query = this.db .collection('Chats') + .select(...CHAT_PREVIEW_KEYS) .where('userId', '==', userId) .orderBy('updatedAt', 'desc') .limit(limit + 1); diff --git a/src/routes/chat/chat-routes.ts b/src/routes/chat/chat-routes.ts index c0ba2a0a..76e87073 100644 --- a/src/routes/chat/chat-routes.ts +++ b/src/routes/chat/chat-routes.ts @@ -66,10 +66,10 @@ export async function chatRoutes(fastify: AppFastifyInstance) { 'The following message is the first message in a new chat conversation. Your task is to create a short title for the conversation. Respond only with the title, nothing else', ); - chat.messages.push({ role: 'user', text: text, time: Date.now() }); //, cache: cache ? 'ephemeral' : undefined // remove any previous cache marker + chat.messages.push({ role: 'user', content: text, time: Date.now() }); //, cache: cache ? 'ephemeral' : undefined // remove any previous cache marker const generatedMessage = await llm.generateTextFromMessages(chat.messages); - chat.messages.push({ role: 'assistant', text: generatedMessage, llmId: llmId, time: Date.now() }); + chat.messages.push({ role: 'assistant', content: generatedMessage, llmId: llmId, time: Date.now() }); if (titlePromise) chat.title = await titlePromise; @@ -107,10 +107,10 @@ export async function chatRoutes(fastify: AppFastifyInstance) { } if (!llm.isConfigured()) return sendBadRequest(reply, `LLM ${llm.getId()} is not configured`); - chat.messages.push({ role: 'user', text: text, time: Date.now() }); //, cache: cache ? 'ephemeral' : undefined // remove any previous cache marker + chat.messages.push({ role: 'user', content: text, time: Date.now() }); //, cache: cache ? 'ephemeral' : undefined // remove any previous cache marker const generatedMessage = await llm.generateTextFromMessages(chat.messages); - chat.messages.push({ role: 'assistant', text: generatedMessage, llmId, time: Date.now() }); + chat.messages.push({ role: 'assistant', content: generatedMessage, llmId, time: Date.now() }); await fastify.chatService.saveChat(chat); diff --git a/src/swe/discovery/selectFilesAgent.ts b/src/swe/discovery/selectFilesAgent.ts index 0573fa82..83c21371 100644 --- a/src/swe/discovery/selectFilesAgent.ts +++ b/src/swe/discovery/selectFilesAgent.ts @@ -165,7 +165,7 @@ export async function selectFilesAgent(requirements: string, projectInfo?: Proje ${requirements} `; - messages.push({ role: 'user', text: initialPrompt }); + messages.push({ role: 'user', content: initialPrompt }); const maxIterations = 5; let iterationCount = 0; @@ -207,19 +207,19 @@ ${stageInstructions} `; // Add the current prompt to messages - messages.push({ role: 'user', text: currentPrompt }); + messages.push({ role: 'user', content: currentPrompt }); // Call the LLM with the current messages const assistantResponse = await llms().medium.generateJsonFromMessages(messages); // Add the assistant's response to the conversation history - messages.push({ role: 'assistant', text: JSON.stringify(assistantResponse) }); + messages.push({ role: 'assistant', content: JSON.stringify(assistantResponse) }); // Handle the assistant's response based on the current stage if (currentStage === 'initial' && assistantResponse.inspectFiles) { // Read and provide the contents of the requested files const fileContents = await readFileContents(assistantResponse.inspectFiles); - messages.push({ role: 'user', text: fileContents }); + messages.push({ role: 'user', content: fileContents }); stagedFiles = assistantResponse.inspectFiles; } else if (currentStage === 'post_inspect' && (assistantResponse.selectFiles || assistantResponse.ignoreFiles)) { // Process selected files and remove ignored files from staging @@ -232,7 +232,7 @@ ${stageInstructions} // Ensure all staged files have been processed if (stagedFiles.length > 0) { const message = `Please respond with select or ignore for the remaining files in the same JSON format as before.\n${JSON.stringify(stagedFiles)}`; - messages.push({ role: 'user', text: message }); + messages.push({ role: 'user', content: message }); } else { // Move to next stage stagedFiles = []; @@ -241,7 +241,7 @@ ${stageInstructions} if (assistantResponse.inspectFiles) { // Read and provide the contents of the requested files const fileContents = await readFileContents(assistantResponse.inspectFiles); - messages.push({ role: 'user', text: fileContents }); + messages.push({ role: 'user', content: fileContents }); stagedFiles = assistantResponse.inspectFiles; } else if (assistantResponse.complete) { // Mark the selection process as complete

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 N002ovPDHLkV1mZYAE5vM literal 0 HcmV?d00001 diff --git a/frontend/src/app/modules/actions/actions.component.html b/frontend/src/app/modules/actions/actions.component.html index cbed5be7..576c77e9 100644 --- a/frontend/src/app/modules/actions/actions.component.html +++ b/frontend/src/app/modules/actions/actions.component.html @@ -1,22 +1,13 @@