13
13
//
14
14
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
// *****************************************************************************
16
- import { Agent , AgentService , CommunicationRecordingService , CommunicationRequestEntry , CommunicationResponseEntry } from '@theia/ai-core' ;
16
+ import { Agent , AgentService , LanguageModelService , SessionEvent } from '@theia/ai-core' ;
17
+ import { AiSemanticRequest } from '@theia/ai-core/lib/common/language-model-interaction-model' ;
17
18
import { codicon , ReactWidget , StatefulWidget } from '@theia/core/lib/browser' ;
18
19
import { inject , injectable , postConstruct } from '@theia/core/shared/inversify' ;
19
20
import * as React from '@theia/core/shared/react' ;
20
- import { CommunicationCard } from './ai-history-communication -card' ;
21
+ import { SemanticRequestCard } from './ai-history-semantic-request -card' ;
21
22
import { SelectComponent , SelectOption } from '@theia/core/lib/browser/widgets/select-component' ;
22
23
import { deepClone , nls } from '@theia/core' ;
23
24
24
25
namespace AIHistoryView {
25
26
export interface State {
26
27
chronological : boolean ;
28
+ selectedAgentId ?: string ;
27
29
}
28
30
}
29
31
30
32
@injectable ( )
31
33
export class AIHistoryView extends ReactWidget implements StatefulWidget {
32
- @inject ( CommunicationRecordingService )
33
- protected recordingService : CommunicationRecordingService ;
34
+ @inject ( LanguageModelService )
35
+ protected languageModelService : LanguageModelService ;
34
36
@inject ( AgentService )
35
37
protected readonly agentService : AgentService ;
36
38
37
39
public static ID = 'ai-history-widget' ;
38
40
static LABEL = nls . localize ( 'theia/ai/history/view/label' , 'AI Agent History [Alpha]' ) ;
39
41
40
- protected selectedAgent ?: Agent ;
41
-
42
42
protected _state : AIHistoryView . State = { chronological : false } ;
43
43
44
44
constructor ( ) {
@@ -74,27 +74,21 @@ export class AIHistoryView extends ReactWidget implements StatefulWidget {
74
74
@postConstruct ( )
75
75
protected init ( ) : void {
76
76
this . update ( ) ;
77
- this . toDispose . push ( this . recordingService . onDidRecordRequest ( entry => this . historyContentUpdated ( entry ) ) ) ;
78
- this . toDispose . push ( this . recordingService . onDidRecordResponse ( entry => this . historyContentUpdated ( entry ) ) ) ;
79
- this . toDispose . push ( this . recordingService . onStructuralChange ( ( ) => this . update ( ) ) ) ;
77
+ this . toDispose . push ( this . languageModelService . onSessionChanged ( ( event : SessionEvent ) => this . historyContentUpdated ( event ) ) ) ;
80
78
this . selectAgent ( this . agentService . getAllAgents ( ) [ 0 ] ) ;
81
79
}
82
80
83
81
protected selectAgent ( agent : Agent | undefined ) : void {
84
- this . selectedAgent = agent ;
85
- this . update ( ) ;
82
+ this . state = { ...this . state , selectedAgentId : agent ?. id } ;
86
83
}
87
84
88
- protected historyContentUpdated ( entry : CommunicationRequestEntry | CommunicationResponseEntry ) : void {
89
- if ( entry . agentId === this . selectedAgent ?. id ) {
90
- this . update ( ) ;
91
- }
85
+ protected historyContentUpdated ( event : SessionEvent ) : void {
86
+ this . update ( ) ;
92
87
}
93
88
94
89
render ( ) : React . ReactNode {
95
90
const selectionChange = ( value : SelectOption ) => {
96
- this . selectedAgent = this . agentService . getAllAgents ( ) . find ( agent => agent . id === value . value ) ;
97
- this . update ( ) ;
91
+ this . selectAgent ( this . agentService . getAllAgents ( ) . find ( agent => agent . id === value . value ) ) ;
98
92
} ;
99
93
const agents = this . agentService . getAllAgents ( ) ;
100
94
if ( agents . length === 0 ) {
@@ -112,7 +106,7 @@ export class AIHistoryView extends ReactWidget implements StatefulWidget {
112
106
description : agent . description || ''
113
107
} ) ) }
114
108
onChange = { selectionChange }
115
- defaultValue = { this . selectedAgent ?. id } />
109
+ defaultValue = { this . state . selectedAgentId } />
116
110
< div className = 'agent-history' >
117
111
{ this . renderHistory ( ) }
118
112
</ div >
@@ -121,24 +115,41 @@ export class AIHistoryView extends ReactWidget implements StatefulWidget {
121
115
}
122
116
123
117
protected renderHistory ( ) : React . ReactNode {
124
- if ( ! this . selectedAgent ) {
118
+ if ( ! this . state . selectedAgentId ) {
125
119
return < div className = 'theia-card no-content' > { nls . localize ( 'theia/ai/history/view/noAgentSelected' , 'No agent selected.' ) } </ div > ;
126
120
}
127
- const history = [ ...this . recordingService . getHistory ( this . selectedAgent . id ) ] ;
128
- if ( history . length === 0 ) {
121
+
122
+ const semanticRequests = this . getSemanticRequestsByAgent ( this . state . selectedAgentId ) ;
123
+
124
+ if ( semanticRequests . length === 0 ) {
125
+ const selectedAgent = this . agentService . getAllAgents ( ) . find ( agent => agent . id === this . state . selectedAgentId ) ;
129
126
return < div className = 'theia-card no-content' >
130
- { nls . localize ( 'theia/ai/history/view/noHistoryForAgent' , 'No history available for the selected agent \'{0}\'' , this . selectedAgent . name ) }
127
+ { nls . localize ( 'theia/ai/history/view/noHistoryForAgent' , 'No history available for the selected agent \'{0}\'' , selectedAgent ? .name || this . state . selectedAgentId ) }
131
128
</ div > ;
132
129
}
133
- if ( ! this . state . chronological ) {
134
- history . reverse ( ) ;
135
- }
136
- return history . map ( entry => < CommunicationCard key = { entry . requestId } entry = { entry } /> ) ;
130
+
131
+ // Sort requests by timestamp (using the first sub-request's timestamp)
132
+ const sortedRequests = [ ...semanticRequests ] . sort ( ( a , b ) => {
133
+ const aTimestamp = a . requests [ 0 ] ?. metadata . timestamp as number || 0 ;
134
+ const bTimestamp = b . requests [ 0 ] ?. metadata . timestamp as number || 0 ;
135
+ return this . state . chronological ? aTimestamp - bTimestamp : bTimestamp - aTimestamp ;
136
+ } ) ;
137
+
138
+ return sortedRequests . map ( request => < SemanticRequestCard key = { request . id } semanticRequest = { request } selectedAgentId = { this . state . selectedAgentId } /> ) ;
137
139
}
138
140
139
- protected onClick ( e : React . MouseEvent < HTMLDivElement > , agent : Agent ) : void {
140
- e . stopPropagation ( ) ;
141
- this . selectAgent ( agent ) ;
141
+ /**
142
+ * Get all semantic requests for a specific agent.
143
+ * Includes all requests in which the agent is involved, either as the main request or as a sub-request.
144
+ * @param agentId The agent ID to filter by
145
+ */
146
+ protected getSemanticRequestsByAgent ( agentId : string ) : AiSemanticRequest [ ] {
147
+ return this . languageModelService . sessions . flatMap ( session =>
148
+ session . requests . filter ( request =>
149
+ request . metadata . agent === agentId ||
150
+ request . requests . some ( subRequest => subRequest . metadata . agent === agentId )
151
+ )
152
+ ) ;
142
153
}
143
154
144
155
public sortHistory ( chronological : boolean ) : void {
0 commit comments