// ***************************************************************************** // Copyright (C) 2025 STMicroelectronics and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { LanguageModelExchangeRequest, LanguageModelExchange, LanguageModelMonitoredStreamResponse, LanguageModelExchangeRequestResponse } from '@theia/ai-core/lib/common/language-model-interaction-model'; import { nls } from '@theia/core'; import * as React from '@theia/core/shared/react'; const getTextFromResponse = (response: LanguageModelExchangeRequestResponse): string => { // Handle monitored stream response if ('parts' in response) { let result = ''; for (const chunk of response.parts) { if ('content' in chunk && chunk.content) { result += chunk.content; } } return result; } // Handle text response if ('text' in response) { return response.text; } // Handle parsed response if ('content' in response) { return response.content; } return JSON.stringify(response); }; const renderTextWithNewlines = (text: string): React.ReactNode => text.split(/\\n|\n/).map((line, i) => ( {i > 0 &&
} {line}
)); const formatJson = (data: unknown): string => { try { return JSON.stringify(data, undefined, 2); } catch (error) { console.error('Error formatting JSON:', error); return 'Error formatting data'; } }; const formatTimestamp = (timestamp: number | undefined): string => timestamp ? new Date(timestamp).toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }) : 'N/A'; export interface ExchangeCardProps { exchange: LanguageModelExchange; selectedAgentId?: string; compactView?: boolean; renderNewlines?: boolean; } export const ExchangeCard: React.FC = ({ exchange, selectedAgentId, compactView = true, renderNewlines = false }) => { const earliestTimestamp = exchange.requests.reduce((earliest, req) => { const timestamp = req.metadata.timestamp as number || 0; return timestamp && (!earliest || timestamp < earliest) ? timestamp : earliest; }, 0); return (
{nls.localizeByDefault('ID')}: {exchange.id} {exchange.metadata.agent && ( {nls.localize('theia/ai/history/exchange-card/agentId', 'Agent')}: {exchange.metadata.agent} )}
{exchange.requests.map((request, index) => ( ))}
{earliestTimestamp > 0 && ( {nls.localize('theia/ai/history/exchange-card/timestamp', 'Started')}: {formatTimestamp(earliestTimestamp)} )}
); }; interface RequestCardProps { request: LanguageModelExchangeRequest; index: number; totalRequests: number; selectedAgentId?: string; compactView?: boolean; renderNewlines?: boolean; } const RequestCard: React.FC = ({ request, index, totalRequests, selectedAgentId, compactView = true, renderNewlines = false }) => { const isFromDifferentAgent = selectedAgentId && request.metadata.agent && request.metadata.agent !== selectedAgentId; const isStreamResponse = 'parts' in request.response; const getRequestContent = () => { if (compactView) { const content = formatJson(request.request.messages); return (
                        {renderNewlines ? renderTextWithNewlines(content) : content}
                    
); } else { const content = formatJson(request.request); return (
                    {renderNewlines ? renderTextWithNewlines(content) : content}
                
); } }; const getResponseContent = () => { if (compactView) { const content = getTextFromResponse(request.response); return (
                        {renderNewlines ? renderTextWithNewlines(content) : content}
                    
); } else if (isStreamResponse) { const streamResponse = request.response as LanguageModelMonitoredStreamResponse; return streamResponse.parts.map((part, i) => (
                        {renderNewlines ? renderTextWithNewlines(JSON.stringify(part, undefined, 2)) : JSON.stringify(part, undefined, 2)}
                    
)); } else { const content = formatJson(request.response); return (
                    {renderNewlines ? renderTextWithNewlines(content) : content}
                
); } }; return (
{totalRequests > 1 && (

{nls.localize('theia/ai/history/request-card/title', 'Request')} {index + 1}

)}
ID: {request.id} {request.metadata.agent && ( {nls.localize('theia/ai/history/request-card/agent', 'Agent')}: {request.metadata.agent} )} {nls.localize('theia/ai/history/request-card/model', 'Model')}: {request.languageModel} {!!request.metadata.promptVariantId && ( {!!request.metadata.isPromptVariantCustomized && ( [{nls.localize('theia/ai/history/edited', 'edited')}]{' '} )} {nls.localize('theia/ai/history/request-card/promptVariant', 'Prompt Variant')}: {request.metadata.promptVariantId as string} )}
{nls.localize('theia/ai/history/request-card/request', 'Request')}
{getRequestContent()}
{nls.localize('theia/ai/history/request-card/response', 'Response')}
{getResponseContent()}
{request.metadata.timestamp && ( {nls.localize('theia/ai/history/request-card/timestamp', 'Timestamp')}: {formatTimestamp(request.metadata.timestamp as number)} )}
); };