From 17c8a3c3b8fba83542bee7ea24518467d096f9b4 Mon Sep 17 00:00:00 2001 From: dnovacik Date: Sat, 17 Feb 2024 15:27:36 +0100 Subject: [PATCH] refactor: correct model used in chat, code style refactor --- src/backend/services/ollama.ts | 54 +++------ src/backend/services/prompts.ts | 2 +- src/frontend/index.tsx | 2 +- src/frontend/utils/transaction.ts | 191 +++++++++++++++++------------- src/frontend/utils/types.ts | 19 ++- src/frontend/utils/utils.ts | 26 ++-- src/frontend/views/chat.tsx | 59 +++++---- 7 files changed, 186 insertions(+), 167 deletions(-) diff --git a/src/backend/services/ollama.ts b/src/backend/services/ollama.ts index c5b61e1..3e93a0b 100644 --- a/src/backend/services/ollama.ts +++ b/src/backend/services/ollama.ts @@ -1,16 +1,15 @@ import { app, ipcMain } from 'electron'; -import { Ollama, ProgressResponse } from 'ollama'; +import { Ollama } from 'ollama'; import { execFile, ChildProcess } from 'child_process'; import fs from 'fs'; -import { isDev, sendOllamaStatusToRenderer } from '..'; +import { sendOllamaStatusToRenderer } from '..'; import { MOR_PROMPT } from './prompts'; // events -import { IpcMainChannel, OllamaChannel } from '../../events'; +import { IpcMainChannel } from '../../events'; import { createDirectoryElevated, executeCommandElevated, - getDefaultAppDataPathByPlatform, getExecutablePathByPlatform, killProcess, runDelayed, @@ -22,7 +21,6 @@ import { logger } from './logger'; // constants const DEFAULT_OLLAMA_URL = 'http://127.0.0.1:11434/'; -const DEFAULT_OLLAMA_MODEL = 'mistral'; // commands export const SERVE_OLLAMA_CMD = 'ollama serve'; @@ -42,7 +40,7 @@ export const loadOllama = async () => { }); await sendOllamaStatusToRenderer( - `Local instance of ollama is running and connected at ${DEFAULT_OLLAMA_URL}`, + `local instance of ollama is running and connected at ${DEFAULT_OLLAMA_URL}`, ); return true; @@ -58,7 +56,7 @@ export const loadOllama = async () => { }); await sendOllamaStatusToRenderer( - `Local instance of ollama is running and connected at ${DEFAULT_OLLAMA_URL}`, + `local instance of ollama is running and connected at ${DEFAULT_OLLAMA_URL}`, ); return true; @@ -73,7 +71,7 @@ export const isOllamaInstanceRunning = async (url?: string): Promise => try { const usedUrl = url ?? DEFAULT_OLLAMA_URL; - await sendOllamaStatusToRenderer(`Checking if ollama instance is running at ${usedUrl}`); + await sendOllamaStatusToRenderer(`checking if ollama instance is running at ${usedUrl}`); const ping = await fetch(usedUrl); @@ -84,7 +82,7 @@ export const isOllamaInstanceRunning = async (url?: string): Promise => }; export const packedExecutableOllamaSpawn = async (customDataPath?: string) => { - await sendOllamaStatusToRenderer(`Trying to spawn locally installed ollama`); + await sendOllamaStatusToRenderer(`trying to spawn locally installed ollama`); try { spawnLocalExecutable(customDataPath); @@ -145,25 +143,19 @@ export const getOllamaExecutableAndAppDataPath = ( }; export const askOllama = async (model: string, message: string) => { - //const engineeredPrompt = `${MOR_PROMPT}\n\nUser: ${message}\n\nResponse:` return await ollama.chat({ model, messages: [ - { - role: 'system', - content: MOR_PROMPT + { + role: 'system', + content: MOR_PROMPT, }, - { - role: 'user', - content: ` Now answer the following question: ${message}. Response:` - } - ] - + { + role: 'user', + content: ` Now answer the following question: ${message}. Response:`, + }, + ], }); - /*return await ollama.generate({ - model: model, - prompt: engineeredPrompt - })*/ }; export const getOrPullModel = async (model: string) => { @@ -203,22 +195,6 @@ export const getAllLocalModels = async () => { return await ollama.list(); }; -export const consumeStream = async (stream: AsyncGenerator) => { - for await (const part of stream) { - if (part.digest) { - let percent = 0; - - if (part.completed && part.total) { - percent = Math.round((part.completed / part.total) * 100); - - return `${part.status} ${percent}%`; - } - } else { - return part.status; - } - } -}; - export const stopOllama = async () => { if (!ollamaProcess) { return; diff --git a/src/backend/services/prompts.ts b/src/backend/services/prompts.ts index 58142b0..78cc07d 100644 --- a/src/backend/services/prompts.ts +++ b/src/backend/services/prompts.ts @@ -51,7 +51,7 @@ Response: "{ }" `; -const errorHandling = `If a question is initiating a buy or transfer transaction and the user doesn't specify an amount in ETH. Gently decline to send the transaction +export const errorHandling = `If a question is initiating a buy or transfer transaction and the user doesn't specify an amount in ETH. Gently decline to send the transaction and request the amount to buy or transfer (depending on their transaction type) in ethereum. If a question is initiating a sell transaction and the user doesn't specify an amount in tokens. Gently decline to send the transaction diff --git a/src/frontend/index.tsx b/src/frontend/index.tsx index 92b8973..48cb6b1 100644 --- a/src/frontend/index.tsx +++ b/src/frontend/index.tsx @@ -73,7 +73,7 @@ const AppRoot = () => { return; } else { - console.error(`Something went wrong with pulling model ${'mistral'}`); + console.error(`Something went wrong with pulling model ${'orca-mini'}`); } } diff --git a/src/frontend/utils/transaction.ts b/src/frontend/utils/transaction.ts index 9f76381..8485426 100644 --- a/src/frontend/utils/transaction.ts +++ b/src/frontend/utils/transaction.ts @@ -1,99 +1,122 @@ -import { ethers } from "ethers"; -import { SDKProvider } from "@metamask/sdk"; -import { transactionParams } from "./types"; +import { ethers } from 'ethers'; +import { SDKProvider } from '@metamask/sdk'; +import { Transaction, TransactionParams } from './types'; -export const isTransactionIntiated = (transaction: transactionParams) => { - return !(Object.keys(transaction).length === 0); -} +export const isTransactionInitiated = (transaction: TransactionParams) => { + return !(Object.keys(transaction).length === 0); +}; -export const buildTransaction = (transaction: transactionParams, account: string | undefined, gasPrice: string, provider: SDKProvider | undefined) => { - const transactionType = transaction.type.toLowerCase(); - - let tx: any - switch (transactionType) { - case "transfer": - tx = buildTransferTransaction(transaction, account, gasPrice); - break; - default: - throw Error(`Transaction of type ${transactionType} is not yet supported`); - } - //returned wrapped call with method for metamask with transaction params - return { - "method": "eth_sendTransaction", - "params": [tx] - } +export const buildTransaction = ( + transaction: TransactionParams, + account: string, + gasPrice: string, +) => { + const transactionType = transaction.type.toLowerCase(); + + let tx: Transaction; + + switch (transactionType) { + case 'transfer': + tx = buildTransferTransaction(transaction, account, gasPrice); + break; + default: + throw Error(`Transaction of type ${transactionType} is not yet supported`); } -const buildTransferTransaction = (transaction: transactionParams, account: string | undefined, gasPrice: any) => { - return { - from: account, - to: transaction.targetAddress, - gas: "0x76c0", //for more complex tasks estimate this from metamast - gasPrice: gasPrice, - value: '0x' + ethers.parseEther(transaction.ethAmount).toString(16), - data: "0x000000" - } -} + // returned wrapped call with method for metamask with transaction params + return { + method: 'eth_sendTransaction', + params: [tx], + }; +}; + +const buildTransferTransaction = ( + transaction: TransactionParams, + account: string, + gasPrice: any, +): Transaction => { + return { + from: account, + to: transaction.targetAddress, + gas: '0x76c0', //for more complex tasks estimate this from metamast + gasPrice: gasPrice, + value: '0x' + ethers.parseEther(transaction.ethAmount).toString(16), + data: '0x000000', + }; +}; //TODO: take chain ID to get arb balance or w/e chain const formatWalletBalance = (balanceWeiHex: string) => { - const balanceBigInt = BigInt(balanceWeiHex) - const balance = ethers.formatUnits(balanceBigInt, "ether"); - return parseFloat(balance).toFixed(2) + " " + "ETH"; -} - -export const handleBalanceRequest = async (provider: SDKProvider | undefined, account: string | undefined, response: string) => { - const blockNumber = await provider?.request({ - "method": "eth_blockNumber", - "params": [] - }); - - const balanceWeiHex = await provider?.request({ - "method": "eth_getBalance", - "params": [ - account, - blockNumber - ] - }); - if(typeof balanceWeiHex === 'string'){ - return response + " " + formatWalletBalance(balanceWeiHex); - } else { - console.error('Failed to retrieve a valid balance.'); - throw Error('Invalid Balance Recieved from MetaMask.') - } -} + const balanceBigInt = BigInt(balanceWeiHex); + const balance = ethers.formatUnits(balanceBigInt, 'ether'); + + return `${parseFloat(balance).toFixed(2)} ETH`; +}; + +export const handleBalanceRequest = async ( + provider: SDKProvider | undefined, + account: string | undefined, + response: string, +) => { + const blockNumber = await provider?.request({ + method: 'eth_blockNumber', + params: [], + }); + + const balanceWeiHex = await provider?.request({ + method: 'eth_getBalance', + params: [account, blockNumber], + }); + + if (typeof balanceWeiHex === 'string') { + return `${response} ${formatWalletBalance(balanceWeiHex)}`; + } else { + console.error('Failed to retrieve a valid balance.'); + + throw Error('Invalid Balance Received from MetaMask.'); + } +}; const estimateGasWithOverHead = (estimatedGasMaybe: string) => { - const estimatedGas = parseInt(estimatedGasMaybe, 16); - //console.log("Gas Limit: " + estimatedGas) - const gasLimitWithOverhead = Math.ceil(estimatedGas * 5); - return "0x" + gasLimitWithOverhead.toString(16); -} - -export const handleTransactionRequest = async (provider: SDKProvider | undefined, transaction: transactionParams, account: string | undefined) => { - const gasPrice = await provider?.request({ - "method": "eth_gasPrice", - "params": [] - }); - //Sanity Check - if(typeof gasPrice !== 'string'){ + const estimatedGas = parseInt(estimatedGasMaybe, 16); + const gasLimitWithOverhead = Math.ceil(estimatedGas * 5); + + return `0x${gasLimitWithOverhead.toString(16)}`; +}; + +export const handleTransactionRequest = async ( + provider: SDKProvider | undefined, + transaction: TransactionParams, + account: string, +) => { + const gasPrice = await provider?.request({ + method: 'eth_gasPrice', + params: [], + }); + + // Sanity Check + if (typeof gasPrice !== 'string') { console.error('Failed to retrieve a valid gasPrice'); - throw new Error('Invalid gasPrice received'); - } - let builtTx = buildTransaction(transaction, account, gasPrice, provider); - let estimatedGas = await provider?.request({ - "method": "eth_estimateGas", - "params": [builtTx] - }); - //Sanity Check - if(typeof estimatedGas !== 'string'){ + throw new Error('Invalid gasPrice received'); + } + + const builtTx = buildTransaction(transaction, account, gasPrice); + + const estimatedGas = await provider?.request({ + method: 'eth_estimateGas', + params: [builtTx], + }); + + //Sanity Check + if (typeof estimatedGas !== 'string') { console.error('Failed to estimate Gas with metamask'); + throw new Error('Invalid gasPrice received'); - } + } - const gasLimitWithOverhead = estimateGasWithOverHead(estimatedGas) - builtTx.params[0].gas = gasLimitWithOverhead; // Update the transaction with the new gas limit in hex - return builtTx -} + const gasLimitWithOverhead = estimateGasWithOverHead(estimatedGas); + builtTx.params[0].gas = gasLimitWithOverhead; // Update the transaction with the new gas limit in hex + return builtTx; +}; diff --git a/src/frontend/utils/types.ts b/src/frontend/utils/types.ts index e5118fd..bfe55ab 100644 --- a/src/frontend/utils/types.ts +++ b/src/frontend/utils/types.ts @@ -1,8 +1,17 @@ export type ModelResponse = { - response: string; - transaction: transactionParams + response: string; + transaction: TransactionParams; }; -export type transactionParams = { - [key: string]: string -} \ No newline at end of file +export type TransactionParams = { + [key: string]: string; +}; + +export type Transaction = { + from: string; + to: string; + gas: string; + gasPrice: any; + value: string; + data: string; +}; diff --git a/src/frontend/utils/utils.ts b/src/frontend/utils/utils.ts index 7ddaa85..715f455 100644 --- a/src/frontend/utils/utils.ts +++ b/src/frontend/utils/utils.ts @@ -1,16 +1,16 @@ -import { ModelResponse } from "./types"; +import { ModelResponse } from './types'; -export function parseResponse(jsonString: string){ - // Assert the type of the parsed object. - const parsed = JSON.parse(jsonString); +export const parseResponse = (jsonString: string) => { + // Assert the type of the parsed object. + const parsed = JSON.parse(jsonString); - if (isModelResponse(parsed)) { - return { response: parsed.response, transaction: parsed.transaction }; - } else { - throw new Error("Invalid ModelResponse format"); - } -} + if (isModelResponse(parsed)) { + return { response: parsed.response, transaction: parsed.transaction }; + } else { + throw new Error('Invalid ModelResponse format'); + } +}; -function isModelResponse(object: any): object is ModelResponse { - return 'response' in object && 'transaction' in object; -} \ No newline at end of file +const isModelResponse = (object: any): object is ModelResponse => { + return 'response' in object && 'transaction' in object; +}; diff --git a/src/frontend/views/chat.tsx b/src/frontend/views/chat.tsx index 18b404b..287ceef 100644 --- a/src/frontend/views/chat.tsx +++ b/src/frontend/views/chat.tsx @@ -1,6 +1,7 @@ // libs import React, { FormEvent, useEffect, useState } from 'react'; import Styled from 'styled-components'; +import { useSDK } from '@metamask/sdk-react'; import { ThreeDots } from 'react-loader-spinner'; // types and helpers @@ -8,13 +9,16 @@ import { AIMessage } from '../types'; import { OllamaChannel } from './../../events'; import { useAIMessagesContext } from '../contexts'; -import { isTransactionIntiated, handleBalanceRequest, handleTransactionRequest } from '../utils/transaction'; -import { useSDK } from '@metamask/sdk-react'; -import {parseResponse} from '../utils/utils' -import { transactionParams } from '../utils/types'; +import { + isTransactionInitiated, + handleBalanceRequest, + handleTransactionRequest, +} from '../utils/transaction'; +import { parseResponse } from '../utils/utils'; +import { TransactionParams } from '../utils/types'; const ChatView = (): JSX.Element => { - const [selectedModel, setSelectedModel] = useState('mistral'); + const [selectedModel, setSelectedModel] = useState('orca-mini'); const [dialogueEntries, setDialogueEntries] = useAIMessagesContext(); const [inputValue, setInputValue] = useState(''); const [currentQuestion, setCurrentQuestion] = useState(); @@ -43,43 +47,48 @@ const ChatView = (): JSX.Element => { ...dialogueEntries, { question: question, answer: message, answered: true }, ]); - } + }; -const processResponse = async (question: string, response: string, transaction: transactionParams) => { - if (!isTransactionIntiated(transaction)){ + const processResponse = async ( + question: string, + response: string, + transaction: TransactionParams, + ) => { + if (!isTransactionInitiated(transaction)) { updateDialogueEntries(question, response); //no additional logic in this case + return; } - //Sanity Checks: - if(!account || !provider){ - const errorMessage = `Error: Please connect to metamask` + // Sanity Checks: + if (!account || !provider) { + const errorMessage = `Error: Please connect to metamask`; updateDialogueEntries(question, errorMessage); + return; } - if (transaction.type.toLowerCase() === "balance") { + if (transaction.type.toLowerCase() === 'balance') { let message: string; try { message = await handleBalanceRequest(provider, account, response); - } catch (error){ - message = `Error: Failed to retrieve a valid balance from Metamask, try reconnecting.` + } catch (error) { + message = `Error: Failed to retrieve a valid balance from Metamask, try reconnecting.`; } updateDialogueEntries(question, message); } else { try { const builtTx = await handleTransactionRequest(provider, transaction, account); updateDialogueEntries(question, response); - console.log("from: " + builtTx.params[0].from); + console.log('from: ' + builtTx.params[0].from); await provider?.request(builtTx); - } catch (error){ - const badTransactionMessage = "Error: There was an error sending your transaction, if the transaction type is balance or transfer please reconnect to metamask" - updateDialogueEntries(question, badTransactionMessage); + } catch (error) { + const badTransactionMessage = + 'Error: There was an error sending your transaction, if the transaction type is balance or transfer please reconnect to metamask'; + updateDialogueEntries(question, badTransactionMessage); } } - -} - + }; const handleQuestionAsked = async (question: string) => { if (isOllamaBeingPolled) { @@ -96,14 +105,13 @@ const processResponse = async (question: string, response: string, transaction: setIsOllamaBeingPolled(true); - const inference = await window.backendBridge.ollama.question({ model: selectedModel, query: question, }); if (inference) { - const { response, transaction } = parseResponse(inference.message.content) + const { response, transaction } = parseResponse(inference.message.content); await processResponse(question, response, transaction); } @@ -150,7 +158,10 @@ const processResponse = async (question: string, response: string, transaction: } }} /> - handleQuestionAsked(inputValue)} /> + handleQuestionAsked(inputValue)} + /> {/*
handleQuestionAsked('How much is 5 times 5?')}>Ask Olama