Skip to content

Commit dc2dd46

Browse files
Implement TSX-based prompt composition for AgentPanelProvider (#397)
- Replace string concatenation with sophisticated TSX-based prompt composition - Add AgentPrompt component with intelligent priority-based message ordering - Implement token allocation strategy with configurable limits - Add graceful fallback from TSX to string concatenation for backward compatibility - Extend AgentCapabilities interface with TSX support metadata - Create comprehensive test suite for TSX integration - Add conversation history integration with smart truncation - Implement configuration options for token limits and prioritization Resolves #378
1 parent 281bef9 commit dc2dd46

File tree

4 files changed

+522
-13
lines changed

4 files changed

+522
-13
lines changed

wu-wei/src/interfaces/agentInterface.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,15 @@ export interface AgentCapabilities {
5353
version: string;
5454
methods: string[];
5555
description?: string;
56-
metadata?: Record<string, any>;
56+
metadata?: {
57+
promptSupport?: {
58+
supportsPrompts: boolean;
59+
supportsTsxMessages?: boolean;
60+
promptParameterName?: string;
61+
variableResolution?: boolean;
62+
};
63+
[key: string]: any;
64+
};
5765
}
5866

5967
/**

wu-wei/src/providers/agentPanelProvider.ts

Lines changed: 176 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,42 @@ import {
1010
GitHubCopilotAgent,
1111
AgentMessage
1212
} from '../interfaces/agentInterface';
13-
import { PromptService, PromptUsageContext } from '../shared/promptManager/types';
13+
import { PromptService, PromptUsageContext, TsxRenderOptions, TsxRenderResult } from '../shared/promptManager/types';
1414
import { PromptServiceFactory } from '../shared/promptManager/PromptServiceFactory';
15+
import { AgentPrompt } from '../shared/promptManager/tsx/components/AgentPrompt';
16+
import { ChatMessage, DEFAULT_PRIORITIES } from '../shared/promptManager/tsx/types';
17+
18+
/**
19+
* Configuration interface for agent prompt handling
20+
*/
21+
interface AgentPromptConfig {
22+
maxTokens: number;
23+
historyMessageCount: number;
24+
enablePrioritization: boolean;
25+
fallbackToStringConcatenation: boolean;
26+
}
1527

1628
/**
1729
* Wu Wei Agent Panel Provider (Enhanced with Prompt Integration)
1830
* Provides a panel for triggering agents with messages using separated HTML, CSS, and JavaScript files
1931
* Phase 4: Added prompt selection and integration capabilities
32+
* Phase 5: Enhanced with TSX-based prompt composition (Issue #378)
2033
*/
2134
export class AgentPanelProvider extends BaseWebviewProvider implements vscode.WebviewViewProvider {
2235
private _agentRegistry: AgentRegistry;
2336
private _messageHistory: AgentMessage[] = [];
2437
private _promptService: PromptService;
2538
private _selectedPromptContext?: PromptUsageContext;
39+
private _agentPromptConfig: AgentPromptConfig = {
40+
maxTokens: 4096,
41+
historyMessageCount: 4,
42+
enablePrioritization: true,
43+
fallbackToStringConcatenation: true
44+
};
2645

2746
constructor(context: vscode.ExtensionContext) {
2847
super(context);
29-
logger.debug('Wu Wei Agent Panel Provider initialized with prompt integration');
48+
logger.debug('Wu Wei Agent Panel Provider initialized with prompt integration and TSX support');
3049

3150
// Initialize prompt service
3251
this._promptService = PromptServiceFactory.createService(context);
@@ -266,6 +285,21 @@ export class AgentPanelProvider extends BaseWebviewProvider implements vscode.We
266285
return params;
267286
}
268287

288+
// Try TSX-based rendering first if enabled
289+
if (this._agentPromptConfig.enablePrioritization) {
290+
try {
291+
return await this.enhanceParamsWithTsxPrompt(params, promptContext, agent);
292+
} catch (error) {
293+
logger.warn('TSX prompt rendering failed, falling back to string concatenation', error);
294+
295+
// If fallback is disabled, re-throw the error
296+
if (!this._agentPromptConfig.fallbackToStringConcatenation) {
297+
throw error;
298+
}
299+
}
300+
}
301+
302+
// Original string concatenation logic (fallback)
269303
const capabilities = agent.getCapabilities();
270304
const promptSupport = capabilities.metadata?.promptSupport;
271305

@@ -320,6 +354,146 @@ export class AgentPanelProvider extends BaseWebviewProvider implements vscode.We
320354
return params;
321355
}
322356

357+
/**
358+
* Enhanced TSX-based prompt parameter enhancement
359+
* Replaces string concatenation with intelligent TSX composition
360+
*/
361+
private async enhanceParamsWithTsxPrompt(
362+
params: any,
363+
promptContext: any,
364+
agent: AbstractAgent
365+
): Promise<any> {
366+
if (!promptContext) {
367+
return params;
368+
}
369+
370+
const userInput = params.message || params.question || params.query || params.input;
371+
if (!userInput) {
372+
throw new Error('Please provide a custom message to combine with the prompt template');
373+
}
374+
375+
// Render the prompt with variables
376+
const rendered = await this._promptService.renderPromptWithVariables(
377+
promptContext.promptId,
378+
promptContext.variables
379+
);
380+
381+
// Convert message history to ChatMessage format
382+
const conversationHistory: ChatMessage[] = this._messageHistory
383+
.filter(msg => msg.type === 'request' || msg.type === 'response')
384+
.slice(-this._agentPromptConfig.historyMessageCount)
385+
.map(msg => ({
386+
role: msg.type === 'request' ? 'user' : 'assistant',
387+
content: this.extractMessageContent(msg),
388+
timestamp: msg.timestamp,
389+
id: msg.id
390+
}));
391+
392+
// Prepare TSX rendering options
393+
const tsxOptions: TsxRenderOptions = {
394+
modelMaxPromptTokens: this._agentPromptConfig.maxTokens,
395+
enablePrioritization: this._agentPromptConfig.enablePrioritization,
396+
tokenBudget: this._agentPromptConfig.maxTokens
397+
};
398+
399+
// Render TSX prompt with intelligent composition
400+
const tsxResult: TsxRenderResult = await this._promptService.renderTsxPrompt(
401+
AgentPrompt,
402+
{
403+
systemPrompt: rendered,
404+
userInput: userInput,
405+
conversationHistory: conversationHistory,
406+
contextData: params.context || '',
407+
maxTokens: this._agentPromptConfig.maxTokens,
408+
priorityStrategy: DEFAULT_PRIORITIES
409+
},
410+
tsxOptions
411+
);
412+
413+
// Check agent capabilities for TSX support
414+
const capabilities = agent.getCapabilities();
415+
const promptSupport = capabilities.metadata?.promptSupport;
416+
417+
if (promptSupport?.supportsPrompts && promptSupport.supportsTsxMessages) {
418+
// Agent supports TSX messages directly
419+
return {
420+
...params,
421+
messages: tsxResult.messages,
422+
tokenCount: tsxResult.tokenCount,
423+
renderingMetadata: tsxResult.renderingMetadata
424+
};
425+
} else {
426+
// Convert TSX messages back to string format for compatibility
427+
const combinedMessage = this.convertTsxMessagesToString(tsxResult.messages);
428+
429+
return {
430+
...params,
431+
message: combinedMessage,
432+
tokenCount: tsxResult.tokenCount,
433+
renderingMetadata: tsxResult.renderingMetadata
434+
};
435+
}
436+
}
437+
438+
/**
439+
* Extract content from AgentMessage for conversation history
440+
*/
441+
private extractMessageContent(message: AgentMessage): string {
442+
if (message.type === 'request') {
443+
return message.params?.message || message.params?.query || message.params?.input || 'Request';
444+
} else if (message.type === 'response') {
445+
return message.result?.message || message.result?.content || JSON.stringify(message.result || {});
446+
}
447+
return 'Unknown message';
448+
}
449+
450+
/**
451+
* Convert TSX messages to string format for agents that don't support TSX
452+
*/
453+
private convertTsxMessagesToString(messages: vscode.LanguageModelChatMessage[]): string {
454+
return messages.map(msg => {
455+
// Map roles to string labels
456+
let role = 'USER';
457+
if (msg.role === vscode.LanguageModelChatMessageRole.User) {
458+
role = 'USER';
459+
} else {
460+
// For any other role (system, assistant, etc.), use generic labels
461+
role = 'ASSISTANT';
462+
}
463+
464+
// Extract text content from message
465+
const content = Array.isArray(msg.content)
466+
? msg.content.map(part => {
467+
if (typeof part === 'string') {
468+
return part;
469+
} else if (part && typeof part === 'object' && 'text' in part) {
470+
return part.text;
471+
} else if (part && typeof part === 'object' && 'value' in part) {
472+
return String(part.value);
473+
}
474+
return JSON.stringify(part);
475+
}).join(' ')
476+
: String(msg.content);
477+
478+
return `${role}: ${content}`;
479+
}).join('\n\n');
480+
}
481+
482+
/**
483+
* Update agent prompt configuration
484+
*/
485+
public updateAgentPromptConfig(config: Partial<AgentPromptConfig>): void {
486+
this._agentPromptConfig = { ...this._agentPromptConfig, ...config };
487+
logger.info('Agent prompt configuration updated', this._agentPromptConfig);
488+
}
489+
490+
/**
491+
* Get current agent prompt configuration
492+
*/
493+
public getAgentPromptConfig(): AgentPromptConfig {
494+
return { ...this._agentPromptConfig };
495+
}
496+
323497
private async sendAvailablePrompts(): Promise<void> {
324498
try {
325499
const prompts = await this._promptService.getAllPrompts();

wu-wei/src/shared/promptManager/tsx/components/AgentPrompt.tsx

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import { PromptElement } from '@vscode/prompt-tsx';
1+
import { PromptElement, SystemMessage, UserMessage } from '@vscode/prompt-tsx';
22
import { AgentPromptProps, DEFAULT_PRIORITIES } from '../types';
33
import { PromptHelpers } from '../utils';
4-
// import { SystemInstructionMessage } from './SystemInstructionMessage';
5-
// import { UserQueryMessage } from './UserQueryMessage';
6-
// import { ConversationHistoryMessages } from './ConversationHistoryMessages';
7-
// import { ContextDataMessage } from './ContextDataMessage';
4+
import { ConversationHistoryMessages } from './ConversationHistoryMessages';
85

96
/**
107
* AgentPrompt - Main prompt composition component with priority-based message composition
118
* This component orchestrates all message types and handles token budgeting and prioritization
9+
*
10+
* Replaces string concatenation in AgentPanelProvider with intelligent TSX-based composition
1211
*/
1312
export class AgentPrompt extends PromptElement<AgentPromptProps> {
1413
render() {
@@ -17,7 +16,7 @@ export class AgentPrompt extends PromptElement<AgentPromptProps> {
1716
userInput,
1817
conversationHistory = [],
1918
contextData,
20-
maxTokens = 4000,
19+
maxTokens = 4096,
2120
priorityStrategy = DEFAULT_PRIORITIES,
2221
...rest
2322
} = this.props;
@@ -28,12 +27,53 @@ export class AgentPrompt extends PromptElement<AgentPromptProps> {
2827
throw new Error(`AgentPrompt validation failed: ${errors.join(', ')}`);
2928
}
3029

31-
// For now, return a simple structure while we resolve typing issues
30+
// Split conversation history for prioritization
31+
const recentHistory = conversationHistory.slice(-2); // Last 2 messages - high priority
32+
const olderHistory = conversationHistory.slice(0, -2); // Older messages - medium priority
33+
34+
// Prepare context data with token limit
35+
const contextWithLabel = contextData
36+
? `Additional Context:\n${PromptHelpers.truncateToTokenLimit(contextData, Math.floor(maxTokens * 0.2))}`
37+
: null;
38+
3239
return (
3340
<>
34-
{/* TODO: Implement full component composition once typing issues are resolved */}
35-
{systemPrompt}
36-
{userInput}
41+
{/* System instructions - highest priority, never pruned */}
42+
<SystemMessage priority={priorityStrategy.systemInstructions}>
43+
{systemPrompt}
44+
</SystemMessage>
45+
46+
{/* Current user query - very high priority */}
47+
<UserMessage priority={priorityStrategy.userQuery}>
48+
{userInput}
49+
</UserMessage>
50+
51+
{/* Recent conversation history - high priority */}
52+
{recentHistory.length > 0 && (
53+
<ConversationHistoryMessages
54+
history={recentHistory}
55+
priority={85}
56+
maxMessages={2}
57+
includeTimestamps={false}
58+
/>
59+
)}
60+
61+
{/* Older conversation history - medium priority */}
62+
{olderHistory.length > 0 && (
63+
<ConversationHistoryMessages
64+
history={olderHistory}
65+
priority={priorityStrategy.conversationHistory}
66+
maxMessages={4}
67+
includeTimestamps={false}
68+
/>
69+
)}
70+
71+
{/* Additional context - flexible priority, can be pruned */}
72+
{contextWithLabel && (
73+
<UserMessage priority={priorityStrategy.contextData}>
74+
{contextWithLabel}
75+
</UserMessage>
76+
)}
3777
</>
3878
);
3979
}
@@ -67,4 +107,23 @@ export class AgentPrompt extends PromptElement<AgentPromptProps> {
67107

68108
return messageCount;
69109
}
110+
111+
/**
112+
* Get token allocation strategy for different content types
113+
*/
114+
private getTokenAllocation(maxTokens: number): {
115+
system: number;
116+
userQuery: number;
117+
recentHistory: number;
118+
olderHistory: number;
119+
context: number;
120+
} {
121+
return {
122+
system: Math.floor(maxTokens * 0.3), // 30% for system instructions
123+
userQuery: Math.floor(maxTokens * 0.2), // 20% for user query
124+
recentHistory: Math.floor(maxTokens * 0.2), // 20% for recent history
125+
olderHistory: Math.floor(maxTokens * 0.15), // 15% for older history
126+
context: Math.floor(maxTokens * 0.15) // 15% for context data
127+
};
128+
}
70129
}

0 commit comments

Comments
 (0)