mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-09-09 01:17:06 +08:00
add LlmModelUsageRecord table
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
import { requireTypeorm } from "../utils/module-loader";
|
||||||
|
const { Entity, PrimaryGeneratedColumn, Column } = requireTypeorm()
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class LlmModelUsageRecord {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
providerCompleteApiUrl: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
model: string
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
providerApiSecretMd5: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
completionTokens?: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
promptTokens?: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
promptCacheHitTokens?: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
promptCacheMissTokens?: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
totalTokens?: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
requestStartTime: Date
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
requestEndTime?: Date
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
hasError: boolean
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
errorMessage: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
requestScene?: number
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import { CompanyInfoChangeLog } from "./entity/CompanyInfoChangeLog";
|
|||||||
import { JobInfoChangeLog } from "./entity/JobInfoChangeLog";
|
import { JobInfoChangeLog } from "./entity/JobInfoChangeLog";
|
||||||
import { MarkAsNotSuitLog } from "./entity/MarkAsNotSuitLog";
|
import { MarkAsNotSuitLog } from "./entity/MarkAsNotSuitLog";
|
||||||
import { ChatMessageRecord } from "./entity/ChatMessageRecord";
|
import { ChatMessageRecord } from "./entity/ChatMessageRecord";
|
||||||
|
import { LlmModelUsageRecord } from "./entity/LlmModelUsageRecord";
|
||||||
|
|
||||||
function getBossInfoIfIsEqual (savedOne, currentOne) {
|
function getBossInfoIfIsEqual (savedOne, currentOne) {
|
||||||
if (savedOne === currentOne) {
|
if (savedOne === currentOne) {
|
||||||
@@ -307,3 +308,21 @@ export async function saveChatMessageRecord(
|
|||||||
//#endregion
|
//#endregion
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function saveGptCompletionRequestRecord(
|
||||||
|
ds: DataSource,
|
||||||
|
records: LlmModelUsageRecord[]
|
||||||
|
) {
|
||||||
|
//#region mark-as-not-suit-log
|
||||||
|
const list = records.map(it => {
|
||||||
|
const o = new LlmModelUsageRecord()
|
||||||
|
for (const k of Object.keys(it)) {
|
||||||
|
o[k] = it[k]
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
const chatMessageRecordRepository = ds.getRepository(LlmModelUsageRecord);
|
||||||
|
await chatMessageRecordRepository.save(list);
|
||||||
|
//#endregion
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { VJobLibrary } from "./entity/VJobLibrary";
|
|||||||
import { VCompanyLibrary } from "./entity/VCompanyLibrary"
|
import { VCompanyLibrary } from "./entity/VCompanyLibrary"
|
||||||
import { VMarkAsNotSuitLog } from "./entity/VMarkAsNotSuitLog"
|
import { VMarkAsNotSuitLog } from "./entity/VMarkAsNotSuitLog"
|
||||||
import { ChatMessageRecord } from './entity/ChatMessageRecord'
|
import { ChatMessageRecord } from './entity/ChatMessageRecord'
|
||||||
|
import { LlmModelUsageRecord } from './entity/LlmModelUsageRecord'
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
import sqlite3 from 'sqlite3';
|
||||||
import { saveChatStartupRecord, saveJobInfoFromRecommendPage, saveMarkAsNotSuitRecord } from "./handlers";
|
import { saveChatStartupRecord, saveJobInfoFromRecommendPage, saveMarkAsNotSuitRecord } from "./handlers";
|
||||||
@@ -53,6 +54,7 @@ export function initDb(dbFilePath) {
|
|||||||
MarkAsNotSuitLog,
|
MarkAsNotSuitLog,
|
||||||
VMarkAsNotSuitLog,
|
VMarkAsNotSuitLog,
|
||||||
ChatMessageRecord,
|
ChatMessageRecord,
|
||||||
|
LlmModelUsageRecord,
|
||||||
],
|
],
|
||||||
migrations: [
|
migrations: [
|
||||||
UpdateChatStartupLogTable1729182577167,
|
UpdateChatStartupLogTable1729182577167,
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"prettier": "^3.2.4",
|
"prettier": "^3.2.4",
|
||||||
"sass": "^1.70.0",
|
"sass": "^1.70.0",
|
||||||
|
"spark-md5": "^3.0.2",
|
||||||
"terser": "^5.37.0",
|
"terser": "^5.37.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"unocss": "^0.58.5",
|
"unocss": "^0.58.5",
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { saveGptCompletionRequestRecord } from '@geekgeekrun/sqlite-plugin/dist/handlers'
|
||||||
|
|
||||||
|
export const RequestSceneEnum = {
|
||||||
|
testing: 1,
|
||||||
|
readNoReplyAutoReminder: 2,
|
||||||
|
geekAutoStartChatWithBoss: 3
|
||||||
|
}
|
||||||
|
export const providerApiSecretToMd5Map = {}
|
||||||
|
|
||||||
|
let dbInitPromise
|
||||||
|
export const recordGptCompletionRequest = async (payload) => {
|
||||||
|
const { getPublicDbFilePath } = await import(
|
||||||
|
'@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||||
|
)
|
||||||
|
const { initDb } = await import('@geekgeekrun/sqlite-plugin')
|
||||||
|
const SparkMD5 = await import('spark-md5')
|
||||||
|
|
||||||
|
if (!dbInitPromise) {
|
||||||
|
dbInitPromise = initDb(getPublicDbFilePath())
|
||||||
|
}
|
||||||
|
const ds = await dbInitPromise
|
||||||
|
const o = { ...payload }
|
||||||
|
if (!providerApiSecretToMd5Map[o.providerApiSecret]) {
|
||||||
|
providerApiSecretToMd5Map[o.providerApiSecret] = SparkMD5.hash(o.providerApiSecret)
|
||||||
|
}
|
||||||
|
o.providerApiSecretMd5 = providerApiSecretToMd5Map[o.providerApiSecret]
|
||||||
|
delete o.providerApiSecret
|
||||||
|
await saveGptCompletionRequestRecord(ds, [o])
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
import { sleepWithRandomDelay, sleep } from '@geekgeekrun/utils/sleep.mjs'
|
import { sleepWithRandomDelay, sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||||
import { completes } from '@geekgeekrun/utils/gpt-request.mjs'
|
import { completes } from '@geekgeekrun/utils/gpt-request.mjs'
|
||||||
|
import { recordGptCompletionRequest, RequestSceneEnum } from '../../features/llm-request-log'
|
||||||
import {
|
import {
|
||||||
readConfigFile,
|
readConfigFile,
|
||||||
readStorageFile,
|
readStorageFile,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||||
import { formatResumeJsonToMarkdown } from '../../../common/utils/resume'
|
import { formatResumeJsonToMarkdown } from '../../../common/utils/resume'
|
||||||
import { SINGLE_ITEM_DEFAULT_SERVE_WEIGHT } from '../../../common/constant'
|
import { SINGLE_ITEM_DEFAULT_SERVE_WEIGHT } from '../../../common/constant'
|
||||||
|
import { LlmModelUsageRecord } from '@geekgeekrun/sqlite-plugin/dist/entity/LlmModelUsageRecord'
|
||||||
|
|
||||||
export const sendLookForwardReplyEmotion = async (page: Page) => {
|
export const sendLookForwardReplyEmotion = async (page: Page) => {
|
||||||
const emotionEntryButtonProxy = await page.$('.chat-conversation .message-controls .btn-emotion')
|
const emotionEntryButtonProxy = await page.$('.chat-conversation .message-controls .btn-emotion')
|
||||||
@@ -52,7 +54,7 @@ const pickLlmConfigFromList = (llmConfigList) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const index = Math.floor(pool.length * Math.random())
|
const index = Math.floor(pool.length * Math.random())
|
||||||
return llmConfigList.find(it => it.id === pool[index]) ?? null
|
return llmConfigList.find((it) => it.id === pool[index]) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
// let _index = 0
|
// let _index = 0
|
||||||
@@ -149,11 +151,22 @@ export const sendGptContent = async (page: Page, chatRecords) => {
|
|||||||
const llmConfigList = await readConfigFile('llm.json')
|
const llmConfigList = await readConfigFile('llm.json')
|
||||||
const llmConfig = pickLlmConfigFromList(llmConfigList)
|
const llmConfig = pickLlmConfigFromList(llmConfigList)
|
||||||
if (!llmConfig) {
|
if (!llmConfig) {
|
||||||
throw new Error(`CANNOT_FIND_A_USABLE_MODEL`);
|
throw new Error(`CANNOT_FIND_A_USABLE_MODEL`)
|
||||||
}
|
}
|
||||||
console.log(llmConfig.providerCompleteApiUrl)
|
console.log(llmConfig.providerCompleteApiUrl)
|
||||||
|
const llmRequestRecord: Omit<LlmModelUsageRecord, 'id' | 'providerApiSecretMd5'> & {
|
||||||
|
providerApiSecret: string
|
||||||
|
} = {
|
||||||
|
providerCompleteApiUrl: llmConfig.providerCompleteApiUrl,
|
||||||
|
model: llmConfig.model,
|
||||||
|
providerApiSecret: llmConfig.providerApiSecret,
|
||||||
|
requestStartTime: new Date(),
|
||||||
|
hasError: false,
|
||||||
|
errorMessage: '',
|
||||||
|
requestScene: RequestSceneEnum.readNoReplyAutoReminder
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
res = await completes(
|
const completion = await completes(
|
||||||
{
|
{
|
||||||
baseURL: llmConfig.providerCompleteApiUrl,
|
baseURL: llmConfig.providerCompleteApiUrl,
|
||||||
apiKey: llmConfig.providerApiSecret,
|
apiKey: llmConfig.providerApiSecret,
|
||||||
@@ -161,9 +174,28 @@ export const sendGptContent = async (page: Page, chatRecords) => {
|
|||||||
},
|
},
|
||||||
chatList
|
chatList
|
||||||
)
|
)
|
||||||
|
res = completion?.choices?.[0] ?? null
|
||||||
|
Object.assign(llmRequestRecord, {
|
||||||
|
completionTokens: completion.usage?.completion_token ?? null,
|
||||||
|
promptCacheHitTokens: completion.usage?.prompt_cache_hit_tokens ?? null,
|
||||||
|
promptCacheMissTokens: completion.usage?.prompt_cache_miss_tokens ?? null,
|
||||||
|
promptTokens: completion.usage?.prompt_tokens ?? null,
|
||||||
|
totalTokens: completion.usage?.total_tokens ?? null
|
||||||
|
} as LlmModelUsageRecord)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('request failed', err)
|
console.log('request failed', err)
|
||||||
blockModelSet.add(llmConfig.id)
|
blockModelSet.add(llmConfig.id)
|
||||||
|
Object.assign(llmRequestRecord, {
|
||||||
|
hasError: true,
|
||||||
|
errorMessage: err?.message ?? ''
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
llmRequestRecord.requestEndTime = new Date()
|
||||||
|
try {
|
||||||
|
await recordGptCompletionRequest(llmRequestRecord)
|
||||||
|
} catch (err) {
|
||||||
|
console.log('CANNOT_SAVE_LLM_COMPLETION_LOG', err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(res)
|
console.log(res)
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ export async function completes(
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log(completion.choices[0].message.content);
|
console.log(completion.choices[0].message.content);
|
||||||
return completion.choices?.[0] ?? null;
|
return completion;
|
||||||
}
|
}
|
||||||
Generated
+7
@@ -229,6 +229,9 @@ importers:
|
|||||||
sass:
|
sass:
|
||||||
specifier: ^1.70.0
|
specifier: ^1.70.0
|
||||||
version: 1.70.0
|
version: 1.70.0
|
||||||
|
spark-md5:
|
||||||
|
specifier: ^3.0.2
|
||||||
|
version: 3.0.2
|
||||||
terser:
|
terser:
|
||||||
specifier: ^5.37.0
|
specifier: ^5.37.0
|
||||||
version: 5.37.0
|
version: 5.37.0
|
||||||
@@ -5779,6 +5782,10 @@ packages:
|
|||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/spark-md5@3.0.2:
|
||||||
|
resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/sprintf-js@1.1.3:
|
/sprintf-js@1.1.3:
|
||||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
|||||||
Reference in New Issue
Block a user