Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 使用更靠谱的方法从json提取数据 #193

Merged
merged 3 commits into from
Dec 24, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 26 additions & 77 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,15 +357,6 @@ async function fetchSession(): Promise<ApiSession> {
return session!
}

class LinkedListItem<T> {
value: T
child: LinkedListItem<T> | null = null

constructor(value: T) {
this.value = value
}
}

export interface ConversationResult {
id: string
title: string
Expand All @@ -387,7 +378,7 @@ const modelMapping: { [key in ModelSlug]: string } & { [key: string]: string } =
'text-davinci-002': 'GTP-3.5',
}

export function processConversation(conversation: ApiConversationWithId, conversationChoices: Array<number | null> = []): ConversationResult {
export function processConversation(conversation: ApiConversationWithId, _conversationChoices: Array<number | null> = []): ConversationResult {
const title = conversation.title || 'ChatGPT Conversation'
const createTime = conversation.create_time
const updateTime = conversation.update_time
Expand All @@ -406,74 +397,11 @@ export function processConversation(conversation: ApiConversationWithId, convers
}
}

const result: ConversationNode[] = []
const nodes = Object.values(conversation.mapping)
const root = nodes.find(node => !node.parent)
if (!root) throw new Error('No root node found.')

const nodeMap = new Map(Object.entries(conversation.mapping))
const tail = new LinkedListItem(root)
const queue = [tail]
let index = -1
while (queue.length > 0) {
const current = queue.shift()!
const node = nodeMap.get(current.value.id)
if (!node) throw new Error('No node found.')

const role = node.message?.author.role
let isContinueGeneration = false
if (role === 'assistant' || role === 'user' || role === 'tool') {
const prevNode = result[result.length - 1]

// If the previous node is also an assistant, we merge them together.
// This is to improve the output of the conversation when an official
// continuation is used. (#146)
if (role === 'assistant'
&& prevNode?.message
&& prevNode.message.author.role === 'assistant'
&& prevNode.message.recipient === 'all'
&& prevNode.message.content.content_type === 'text'
&& node.message
&& node.message.recipient === 'all'
&& node.message.content.content_type === 'text'
) {
isContinueGeneration = true
// the last part of the previous node should directly concat to the first part of the current node
prevNode.message.content.parts[prevNode.message.content.parts.length - 1] += node.message.content.parts[0]
prevNode.message.content.parts.push(...node.message.content.parts.slice(1))
}
else {
result.push(node)
}
}

if (node.children.length === 0) continue

const _last = node.children.length - 1
let choice = 0
// If the current node is an continue generation like [A -> B], A will always
// only have one child which is the continue generation node B. In this case,
// when we are processing A, we don't know we have a continue generation node
// and no matter what choice we choose, we will always get B, so it's acceptable
// And here in B, we will use the previous choice to get the correct child node
if (isContinueGeneration) {
choice = conversationChoices[index] ?? _last
}
// Conversation choices will only applied to nodes with message
else if ('message' in node && node.message?.recipient === 'all' && node.message.author.role !== 'tool') {
index++
choice = conversationChoices[index] ?? _last
}

const childId = node.children[choice] ?? node.children[_last]
if (!childId) throw new Error('No child node found.')
let result: ConversationNode[] = []

const child = nodeMap.get(childId)
if (!child) throw new Error('No child node found.')

const childItem = new LinkedListItem(child)
current.child = childItem
queue.push(childItem)
const bottomMostNodeId = conversation.current_node
if (bottomMostNodeId) {
result = extractConversationResult(conversation.mapping, bottomMostNodeId)
}

return {
Expand All @@ -486,3 +414,24 @@ export function processConversation(conversation: ApiConversationWithId, convers
conversationNodes: result,
}
}

function extractConversationResult(conversationData: Record<string, ConversationNode>, startNodeId: string): ConversationNode[] {
const result: ConversationNode[] = []
let currentNodeId: string | undefined = startNodeId

while (currentNodeId) {
const node: ConversationNode = conversationData[currentNodeId]
if (!node) {
break// Node not found
}
if (node.message?.author.role === 'system') {
break// Stop at system message
}

result.push(node)

currentNodeId = node.parent
}

return result.reverse()// Reverse the result to start from the root
}