mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-18 06:17:06 +00:00
fix: metrics by codex
This commit is contained in:
parent
685bb5a5b8
commit
d0e365cb32
3 changed files with 111 additions and 45 deletions
103
config/config.js
103
config/config.js
|
|
@ -250,7 +250,7 @@ class ChatGPTConfig {
|
||||||
historyPollInterval: 300,
|
historyPollInterval: 300,
|
||||||
historyBatchSize: 120,
|
historyBatchSize: 120,
|
||||||
promptHeader: '# 以下是一些该群聊中可能相关的事实,你可以参考,但不要主动透露这些事实。',
|
promptHeader: '# 以下是一些该群聊中可能相关的事实,你可以参考,但不要主动透露这些事实。',
|
||||||
promptItemTemplate: '- ${fact}${topicSuffix}',
|
promptItemTemplate: '- ${fact}${topicSuffix}${timeSuffix}',
|
||||||
promptFooter: '',
|
promptFooter: '',
|
||||||
extractionSystemPrompt: `You are a knowledge extraction assistant that specialises in summarising long-term facts from group chat transcripts.
|
extractionSystemPrompt: `You are a knowledge extraction assistant that specialises in summarising long-term facts from group chat transcripts.
|
||||||
Read the provided conversation and identify statements that should be stored as long-term knowledge for the group.
|
Read the provided conversation and identify statements that should be stored as long-term knowledge for the group.
|
||||||
|
|
@ -282,7 +282,7 @@ Only include meaningful, verifiable group-specific information that is useful fo
|
||||||
maxRelevantItemsPerQuery: 3,
|
maxRelevantItemsPerQuery: 3,
|
||||||
minImportanceForInjection: 0,
|
minImportanceForInjection: 0,
|
||||||
promptHeader: '# 用户画像',
|
promptHeader: '# 用户画像',
|
||||||
promptItemTemplate: '- ${value}',
|
promptItemTemplate: '- ${value}${timeSuffix}',
|
||||||
promptFooter: '',
|
promptFooter: '',
|
||||||
extractionSystemPrompt: `You are an assistant that extracts long-term personal preferences or persona details about a user.
|
extractionSystemPrompt: `You are an assistant that extracts long-term personal preferences or persona details about a user.
|
||||||
Given a conversation snippet between the user and the bot, identify durable information such as preferences, nicknames, roles, speaking style, habits, or other facts that remain valid over time.
|
Given a conversation snippet between the user and the bot, identify durable information such as preferences, nicknames, roles, speaking style, habits, or other facts that remain valid over time.
|
||||||
|
|
@ -453,20 +453,13 @@ Return a JSON array of **strings**, and nothing else, without any other characte
|
||||||
? JSON.parse(content)
|
? JSON.parse(content)
|
||||||
: yaml.load(content)
|
: yaml.load(content)
|
||||||
|
|
||||||
// 只更新存在的配置项
|
// 处理加载的配置并和默认值合并
|
||||||
if (loadedConfig) {
|
if (loadedConfig) {
|
||||||
Object.keys(loadedConfig).forEach(key => {
|
const mergeResult = this._mergeConfig(loadedConfig)
|
||||||
if (['version', 'basic', 'bym', 'llm', 'management', 'chaite', 'memory'].includes(key)) {
|
if (mergeResult.changed) {
|
||||||
if (typeof loadedConfig[key] === 'object' && loadedConfig[key] !== null) {
|
logger?.debug?.('[Config] merged new defaults into persisted config; scheduling save')
|
||||||
// 对象的合并
|
this._triggerSave('code')
|
||||||
if (!this[key]) this[key] = {}
|
}
|
||||||
Object.assign(this[key], loadedConfig[key])
|
|
||||||
} else {
|
|
||||||
// 基本类型直接赋值
|
|
||||||
this[key] = loadedConfig[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Config loaded successfully')
|
logger.debug('Config loaded successfully')
|
||||||
|
|
@ -475,6 +468,68 @@ Return a JSON array of **strings**, and nothing else, without any other characte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mergeConfig (loadedConfig) {
|
||||||
|
let changed = false
|
||||||
|
|
||||||
|
const mergeInto = (target, source) => {
|
||||||
|
if (!source || typeof source !== 'object') {
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
if (!target || typeof target !== 'object') {
|
||||||
|
target = Array.isArray(source) ? [] : {}
|
||||||
|
}
|
||||||
|
const result = Array.isArray(source) ? [] : { ...target }
|
||||||
|
|
||||||
|
if (Array.isArray(source)) {
|
||||||
|
return source.slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetKeys = target && typeof target === 'object'
|
||||||
|
? Object.keys(target)
|
||||||
|
: []
|
||||||
|
for (const key of targetKeys) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of Object.keys(source)) {
|
||||||
|
const sourceValue = source[key]
|
||||||
|
const targetValue = target[key]
|
||||||
|
if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
|
||||||
|
result[key] = mergeInto(targetValue, sourceValue)
|
||||||
|
} else {
|
||||||
|
if (targetValue === undefined || targetValue !== sourceValue) {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
result[key] = sourceValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const sections = ['version', 'basic', 'bym', 'llm', 'management', 'chaite', 'memory']
|
||||||
|
for (const key of sections) {
|
||||||
|
const loadedValue = loadedConfig[key]
|
||||||
|
if (loadedValue === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (typeof loadedValue === 'object' && loadedValue !== null) {
|
||||||
|
const merged = mergeInto(this[key], loadedValue)
|
||||||
|
if (merged !== this[key]) {
|
||||||
|
this[key] = merged
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this[key] !== loadedValue) {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
this[key] = loadedValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { changed }
|
||||||
|
}
|
||||||
|
|
||||||
// 合并触发保存,防抖处理
|
// 合并触发保存,防抖处理
|
||||||
_triggerSave (origin) {
|
_triggerSave (origin) {
|
||||||
// 清除之前的定时器
|
// 清除之前的定时器
|
||||||
|
|
@ -482,20 +537,18 @@ Return a JSON array of **strings**, and nothing else, without any other characte
|
||||||
clearTimeout(this._saveTimer)
|
clearTimeout(this._saveTimer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录保存来源
|
const originLabel = origin || 'code'
|
||||||
this._saveOrigin = origin || 'code'
|
this._saveOrigin = originLabel
|
||||||
|
|
||||||
// 设置定时器延迟保存
|
|
||||||
this._saveTimer = setTimeout(() => {
|
this._saveTimer = setTimeout(() => {
|
||||||
this.saveToFile()
|
this.saveToFile(originLabel)
|
||||||
// 保存完成后延迟一下再清除来源标记
|
this._saveOrigin = null
|
||||||
setTimeout(() => {
|
|
||||||
this._saveOrigin = null
|
|
||||||
}, 100)
|
|
||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
saveToFile () {
|
saveToFile (origin = 'code') {
|
||||||
|
if (origin !== 'code') {
|
||||||
|
this._saveOrigin = 'external'
|
||||||
|
}
|
||||||
logger.debug('Saving config to file...')
|
logger.debug('Saving config to file...')
|
||||||
try {
|
try {
|
||||||
const config = {
|
const config = {
|
||||||
|
|
|
||||||
|
|
@ -610,15 +610,7 @@ function ensureVectorTable (db) {
|
||||||
let tablePresent = tableExists
|
let tablePresent = tableExists
|
||||||
|
|
||||||
let needsTableReset = false
|
let needsTableReset = false
|
||||||
if (storedModel && storedModel !== currentModel) {
|
|
||||||
needsTableReset = true
|
|
||||||
} else if (!storedModel && tableExists) {
|
|
||||||
// Unknown model metadata but table exists; keep it as-is.
|
|
||||||
dimension = storedDimension
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tableExists && storedDimension <= 0) {
|
if (tableExists && storedDimension <= 0) {
|
||||||
logger?.warn?.('[Memory] vec_group_facts exists but stored dimension is invalid, rebuilding table')
|
|
||||||
needsTableReset = true
|
needsTableReset = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -628,11 +620,11 @@ function ensureVectorTable (db) {
|
||||||
tablePresent = false
|
tablePresent = false
|
||||||
dimension = 0
|
dimension = 0
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger?.warn?.('[Memory] failed to drop vec_group_facts during model change:', err)
|
logger?.warn?.('[Memory] failed to drop vec_group_facts during dimension change:', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tablePresent) {
|
if (!tablePresent) {
|
||||||
if (dimension <= 0) {
|
if (dimension <= 0) {
|
||||||
dimension = parseDimension(preferredDimension)
|
dimension = parseDimension(preferredDimension)
|
||||||
}
|
}
|
||||||
|
|
@ -640,30 +632,36 @@ function ensureVectorTable (db) {
|
||||||
try {
|
try {
|
||||||
createVectorTable(db, dimension)
|
createVectorTable(db, dimension)
|
||||||
tablePresent = true
|
tablePresent = true
|
||||||
|
setMetaValue(db, META_VECTOR_MODEL_KEY, currentModel)
|
||||||
|
setMetaValue(db, META_VECTOR_DIM_KEY, String(dimension))
|
||||||
|
cachedVectorDimension = dimension
|
||||||
|
cachedVectorModel = currentModel
|
||||||
|
return cachedVectorDimension
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger?.error?.('[Memory] failed to (re)create vec_group_facts table:', err)
|
logger?.error?.('[Memory] failed to (re)create vec_group_facts table:', err)
|
||||||
dimension = 0
|
dimension = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (dimension > 0 && preferredDimension > 0 && dimension !== preferredDimension) {
|
|
||||||
logger?.debug?.('[Memory] vector table dimension (%s) differs from preferred (%s); keeping existing table', dimension, preferredDimension)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const metaDimensionValue = dimension > 0 ? String(dimension) : '0'
|
if (tablePresent && storedDimension > 0) {
|
||||||
setMetaValue(db, META_VECTOR_MODEL_KEY, currentModel)
|
cachedVectorDimension = storedDimension
|
||||||
setMetaValue(db, META_VECTOR_DIM_KEY, metaDimensionValue)
|
cachedVectorModel = storedModel || currentModel
|
||||||
|
return cachedVectorDimension
|
||||||
|
}
|
||||||
|
|
||||||
cachedVectorDimension = dimension > 0 ? dimension : 0
|
// At this point we failed to determine a valid dimension, set metadata to 0 to avoid loops.
|
||||||
|
setMetaValue(db, META_VECTOR_MODEL_KEY, currentModel)
|
||||||
|
setMetaValue(db, META_VECTOR_DIM_KEY, '0')
|
||||||
|
cachedVectorDimension = 0
|
||||||
cachedVectorModel = currentModel
|
cachedVectorModel = currentModel
|
||||||
return cachedVectorDimension
|
return cachedVectorDimension
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetVectorTableDimension (dimension) {
|
export function resetVectorTableDimension (dimension) {
|
||||||
if (!Number.isFinite(dimension) || dimension <= 0) {
|
if (!Number.isFinite(dimension) || dimension <= 0) {
|
||||||
throw new Error(`Invalid vector dimension: ${dimension}`)
|
throw new Error(`Invalid vector dimension: ${dimension}`)
|
||||||
}
|
}
|
||||||
const db = getMemoryDatabase()
|
const db = getMemoryDatabase()
|
||||||
logger?.info?.('[Memory] resetting group vector table dimension to %s', dimension)
|
|
||||||
try {
|
try {
|
||||||
db.exec('DROP TABLE IF EXISTS vec_group_facts')
|
db.exec('DROP TABLE IF EXISTS vec_group_facts')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -726,7 +724,12 @@ export function getMemoryDatabase () {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVectorDimension () {
|
export function getVectorDimension () {
|
||||||
if (cachedVectorDimension) {
|
const currentModel = ChatGPTConfig.llm?.embeddingModel || ''
|
||||||
|
if (cachedVectorModel && cachedVectorModel !== currentModel) {
|
||||||
|
cachedVectorDimension = null
|
||||||
|
cachedVectorModel = null
|
||||||
|
}
|
||||||
|
if (cachedVectorDimension !== null) {
|
||||||
return cachedVectorDimension
|
return cachedVectorDimension
|
||||||
}
|
}
|
||||||
const db = getMemoryDatabase()
|
const db = getMemoryDatabase()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ function formatUserMemories (memories, config) {
|
||||||
segments.push(header)
|
segments.push(header)
|
||||||
}
|
}
|
||||||
memories.forEach((item, index) => {
|
memories.forEach((item, index) => {
|
||||||
|
const timestamp = item.updated_at || item.created_at || ''
|
||||||
|
const timeSuffix = timestamp ? `(记录时间:${timestamp})` : ''
|
||||||
const context = {
|
const context = {
|
||||||
index,
|
index,
|
||||||
order: index + 1,
|
order: index + 1,
|
||||||
|
|
@ -33,7 +35,10 @@ function formatUserMemories (memories, config) {
|
||||||
sourceId: item.source_message_id || '',
|
sourceId: item.source_message_id || '',
|
||||||
groupId: item.group_id || '',
|
groupId: item.group_id || '',
|
||||||
createdAt: item.created_at || '',
|
createdAt: item.created_at || '',
|
||||||
updatedAt: item.updated_at || ''
|
updatedAt: item.updated_at || '',
|
||||||
|
timestamp,
|
||||||
|
time: timestamp,
|
||||||
|
timeSuffix
|
||||||
}
|
}
|
||||||
const line = renderTemplate(itemTemplate, context)
|
const line = renderTemplate(itemTemplate, context)
|
||||||
if (line) {
|
if (line) {
|
||||||
|
|
@ -61,6 +66,8 @@ function formatGroupFacts (facts, config) {
|
||||||
}
|
}
|
||||||
facts.forEach((item, index) => {
|
facts.forEach((item, index) => {
|
||||||
const topicSuffix = item.topic ? `(${item.topic})` : ''
|
const topicSuffix = item.topic ? `(${item.topic})` : ''
|
||||||
|
const timestamp = item.updated_at || item.created_at || ''
|
||||||
|
const timeSuffix = timestamp ? `(记录时间:${timestamp})` : ''
|
||||||
const context = {
|
const context = {
|
||||||
index,
|
index,
|
||||||
order: index + 1,
|
order: index + 1,
|
||||||
|
|
@ -70,6 +77,9 @@ function formatGroupFacts (facts, config) {
|
||||||
importance: item.importance ?? '',
|
importance: item.importance ?? '',
|
||||||
createdAt: item.created_at || '',
|
createdAt: item.created_at || '',
|
||||||
updatedAt: item.updated_at || '',
|
updatedAt: item.updated_at || '',
|
||||||
|
timestamp,
|
||||||
|
time: timestamp,
|
||||||
|
timeSuffix,
|
||||||
distance: item.distance ?? '',
|
distance: item.distance ?? '',
|
||||||
bm25: item.bm25_score ?? '',
|
bm25: item.bm25_score ?? '',
|
||||||
sourceMessages: item.source_messages || '',
|
sourceMessages: item.source_messages || '',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue