mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-28 14:04:41 -07:00
179 lines
5.2 KiB
TypeScript
179 lines
5.2 KiB
TypeScript
|
|
/**
|
||
|
|
* @license
|
||
|
|
* Copyright 2025 Google LLC
|
||
|
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { HistoryItemWithoutId } from '../types.js';
|
||
|
|
import type { ConversationRecord } from '@google/gemini-cli-core';
|
||
|
|
import type { Part } from '@google/genai';
|
||
|
|
import { partListUnionToString } from '@google/gemini-cli-core';
|
||
|
|
import { MessageType, ToolCallStatus } from '../types.js';
|
||
|
|
/**
|
||
|
|
* Converts session/conversation data into UI history and Gemini client history formats.
|
||
|
|
*/
|
||
|
|
export function convertSessionToHistoryFormats(
|
||
|
|
messages: ConversationRecord['messages'],
|
||
|
|
): {
|
||
|
|
uiHistory: HistoryItemWithoutId[];
|
||
|
|
clientHistory: Array<{ role: 'user' | 'model'; parts: Part[] }>;
|
||
|
|
} {
|
||
|
|
const uiHistory: HistoryItemWithoutId[] = [];
|
||
|
|
|
||
|
|
for (const msg of messages) {
|
||
|
|
// Add the message only if it has content
|
||
|
|
const contentString = partListUnionToString(msg.content);
|
||
|
|
if (msg.content && contentString.trim()) {
|
||
|
|
let messageType: MessageType;
|
||
|
|
switch (msg.type) {
|
||
|
|
case 'user':
|
||
|
|
messageType = MessageType.USER;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
messageType = MessageType.GEMINI;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
uiHistory.push({
|
||
|
|
type: messageType,
|
||
|
|
text: contentString,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add tool calls if present
|
||
|
|
if (
|
||
|
|
msg.type !== 'user' &&
|
||
|
|
'toolCalls' in msg &&
|
||
|
|
msg.toolCalls &&
|
||
|
|
msg.toolCalls.length > 0
|
||
|
|
) {
|
||
|
|
uiHistory.push({
|
||
|
|
type: 'tool_group',
|
||
|
|
tools: msg.toolCalls.map((tool) => ({
|
||
|
|
callId: tool.id,
|
||
|
|
name: tool.displayName || tool.name,
|
||
|
|
description: tool.description || '',
|
||
|
|
renderOutputAsMarkdown: tool.renderOutputAsMarkdown ?? true,
|
||
|
|
status:
|
||
|
|
tool.status === 'success'
|
||
|
|
? ToolCallStatus.Success
|
||
|
|
: ToolCallStatus.Error,
|
||
|
|
resultDisplay: tool.resultDisplay,
|
||
|
|
confirmationDetails: undefined,
|
||
|
|
})),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Convert to Gemini client history format
|
||
|
|
const clientHistory: Array<{ role: 'user' | 'model'; parts: Part[] }> = [];
|
||
|
|
|
||
|
|
for (const msg of messages) {
|
||
|
|
// Skip system/error messages and user slash commands
|
||
|
|
// if (msg.type === 'system' || msg.type === 'error') {
|
||
|
|
// continue;
|
||
|
|
// }
|
||
|
|
|
||
|
|
if (msg.type === 'user') {
|
||
|
|
// Skip user slash commands
|
||
|
|
const contentString = partListUnionToString(msg.content);
|
||
|
|
if (
|
||
|
|
contentString.trim().startsWith('/') ||
|
||
|
|
contentString.trim().startsWith('?')
|
||
|
|
) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add regular user message
|
||
|
|
clientHistory.push({
|
||
|
|
role: 'user',
|
||
|
|
parts: [{ text: contentString }],
|
||
|
|
});
|
||
|
|
} else if (msg.type === 'gemini') {
|
||
|
|
// Handle Gemini messages with potential tool calls
|
||
|
|
const hasToolCalls =
|
||
|
|
'toolCalls' in msg && msg.toolCalls && msg.toolCalls.length > 0;
|
||
|
|
|
||
|
|
if (hasToolCalls) {
|
||
|
|
// Create model message with function calls
|
||
|
|
const modelParts: Part[] = [];
|
||
|
|
|
||
|
|
// Add text content if present
|
||
|
|
const contentString = partListUnionToString(msg.content);
|
||
|
|
if (msg.content && contentString.trim()) {
|
||
|
|
modelParts.push({ text: contentString });
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add function calls
|
||
|
|
for (const toolCall of msg.toolCalls!) {
|
||
|
|
modelParts.push({
|
||
|
|
functionCall: {
|
||
|
|
name: toolCall.name,
|
||
|
|
args: toolCall.args,
|
||
|
|
...(toolCall.id && { id: toolCall.id }),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
clientHistory.push({
|
||
|
|
role: 'model',
|
||
|
|
parts: modelParts,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create single function response message with all tool call responses
|
||
|
|
const functionResponseParts: Part[] = [];
|
||
|
|
for (const toolCall of msg.toolCalls!) {
|
||
|
|
if (toolCall.result) {
|
||
|
|
// Convert PartListUnion result to function response format
|
||
|
|
let responseData: Part;
|
||
|
|
|
||
|
|
if (typeof toolCall.result === 'string') {
|
||
|
|
responseData = {
|
||
|
|
functionResponse: {
|
||
|
|
id: toolCall.id,
|
||
|
|
name: toolCall.name,
|
||
|
|
response: {
|
||
|
|
output: toolCall.result,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
};
|
||
|
|
} else if (Array.isArray(toolCall.result)) {
|
||
|
|
// toolCall.result is an array containing properly formatted
|
||
|
|
// function responses
|
||
|
|
functionResponseParts.push(...(toolCall.result as Part[]));
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
// Fallback for non-array results
|
||
|
|
responseData = toolCall.result;
|
||
|
|
}
|
||
|
|
|
||
|
|
functionResponseParts.push(responseData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Only add user message if we have function responses
|
||
|
|
if (functionResponseParts.length > 0) {
|
||
|
|
clientHistory.push({
|
||
|
|
role: 'user',
|
||
|
|
parts: functionResponseParts,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Regular Gemini message without tool calls
|
||
|
|
const contentString = partListUnionToString(msg.content);
|
||
|
|
if (msg.content && contentString.trim()) {
|
||
|
|
clientHistory.push({
|
||
|
|
role: 'model',
|
||
|
|
parts: [{ text: contentString }],
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
uiHistory,
|
||
|
|
clientHistory,
|
||
|
|
};
|
||
|
|
}
|