contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
494 lines (425 loc) • 14.3 kB
Markdown
# Conversation Message Architecture Design
## Overview
This document defines the hybrid message handling system that supports both modern conversation APIs (OpenAI, Anthropic, DeepSeek, OpenRouter) and simple prompt-based providers (HuggingFace, Ollama, Replicate) while maintaining backward compatibility.
## Current Problems
### 1. Single Prompt Concatenation Issue
```typescript
// ❌ Current problematic approach
context += `System: ${session.systemPrompt}\n\n`;
context += fileTreeContext;
context += toolInstructions;
context += conversationHistory;
context += `Human: ${currentMessage.content}\n\nAssistant:`;
// Sent as single user message - loses conversation structure
messages: [{ role: "user", content: context }]
```
### 2. Tool Response Integration Issues
- XML tool responses are injected as user messages
- Breaks conversation flow for conversation-capable providers
- Tool responses appear in UI when they should be internal
### 3. Provider Capability Mismatch
- All providers treated the same despite different capabilities
- No fallback strategy for simple prompt providers
- Google Gemini has custom format requirements
## New Message Architecture
### 1. Enhanced Message Interface
```typescript
export interface ConversationMessage {
id: string;
role: 'system' | 'user' | 'assistant';
content: string;
timestamp: number;
metadata?: {
isToolResponse?: boolean;
toolCallId?: string;
provider?: string;
internal?: boolean; // Hide from UI
};
}
export interface ChatSession {
id: string;
messages: ConversationMessage[];
systemPrompt?: string;
createdAt: number;
updatedAt: number;
providerCapabilities?: ProviderCapabilities;
}
```
### 2. Provider Capability Detection
```typescript
export interface ProviderCapabilities {
supportsConversation: boolean;
supportsSystemMessages: boolean;
supportsToolCalls: boolean;
customFormat?: 'gemini' | 'standard';
maxContextLength?: number;
}
export const PROVIDER_CAPABILITIES: Record<string, ProviderCapabilities> = {
OpenAI: {
supportsConversation: true,
supportsSystemMessages: true,
supportsToolCalls: true,
customFormat: 'standard'
},
Anthropic: {
supportsConversation: true,
supportsSystemMessages: true,
supportsToolCalls: true,
customFormat: 'standard'
},
DeepSeek: {
supportsConversation: true,
supportsSystemMessages: true,
supportsToolCalls: true,
customFormat: 'standard'
},
OpenRouter: {
supportsConversation: true,
supportsSystemMessages: true,
supportsToolCalls: true,
customFormat: 'standard'
},
Google: {
supportsConversation: true,
supportsSystemMessages: false, // Uses different format
supportsToolCalls: true,
customFormat: 'gemini'
},
HuggingFace: {
supportsConversation: false,
supportsSystemMessages: false,
supportsToolCalls: false,
customFormat: 'standard'
},
Ollama: {
supportsConversation: false,
supportsSystemMessages: false,
supportsToolCalls: false,
customFormat: 'standard'
},
Replicate: {
supportsConversation: false,
supportsSystemMessages: false,
supportsToolCalls: false,
customFormat: 'standard'
}
};
```
### 3. System Prompt Composition Strategy
```typescript
export interface SystemPromptComponents {
base: string; // Core agent instructions
fileTree: string; // Current project structure
tools: string; // Available tools and usage
context: string; // Additional context
}
export class SystemPromptComposer {
static compose(components: SystemPromptComponents): string {
return [
components.base,
components.fileTree ? `\n\n## Current Project Structure\n\n${components.fileTree}` : '',
components.tools ? `\n\n${components.tools}` : '',
components.context ? `\n\n## Additional Context\n\n${components.context}` : ''
].filter(Boolean).join('');
}
}
```
### 4. Message Format Strategies
#### A. Conversation-Capable Providers (OpenAI, Anthropic, DeepSeek, OpenRouter)
```typescript
// ✅ Proper conversation format
const messages = [
{
role: "system",
content: SystemPromptComposer.compose({
base: session.systemPrompt,
fileTree: await toolManager.generateFileTreeContext(),
tools: toolManager.generateToolInstructions(),
context: ""
})
},
{ role: "user", content: "First user message" },
{ role: "assistant", content: "First assistant response" },
{ role: "user", content: "Tool response XML (internal)", metadata: { internal: true } },
{ role: "assistant", content: "Assistant response with tool results" },
{ role: "user", content: "Current user message" }
];
```
#### B. Simple Prompt Providers (HuggingFace, Ollama, Replicate)
```typescript
// ✅ Fallback to concatenated prompt format
const prompt = [
`System: ${systemPrompt}`,
`\n\n## Conversation History\n`,
...conversationHistory.map(msg => `${msg.role}: ${msg.content}`),
`\n\nHuman: ${currentMessage}\n\nAssistant:`
].join('');
```
#### C. Google Gemini Custom Format
```typescript
// ✅ Gemini-specific format
const parts = [
{ text: systemPrompt },
...conversationHistory.map(msg => ({
role: msg.role === 'assistant' ? 'model' : 'user',
parts: [{ text: msg.content }]
}))
];
```
### 5. Tool Response Integration Strategy
#### For Conversation-Capable Providers:
```typescript
// Tool responses as internal user messages
const toolResponseMessage: ConversationMessage = {
id: generateId(),
role: 'user',
content: xmlToolResponses,
timestamp: Date.now(),
metadata: {
isToolResponse: true,
internal: true, // Hidden from UI
toolCallId: toolCall.id
}
};
```
#### For Simple Prompt Providers:
```typescript
// Tool responses integrated into prompt context
const promptWithToolResults = [
basePrompt,
'\n\n## Tool Execution Results\n',
xmlToolResponses,
'\n\nBased on the tool results above, please provide your response:'
].join('');
```
## Implementation Plan
### Phase 1: Interface Updates
1. Update `ConversationMessage` interface with metadata
2. Add `ProviderCapabilities` interface and constants
3. Create `SystemPromptComposer` utility class
### Phase 2: Provider Detection
1. Add capability detection to each provider
2. Update `LLMInterface` with capability methods
3. Implement provider-specific message formatting
### Phase 3: Message Handling Refactor
1. Replace `buildConversationContext()` with `buildMessageArray()`
2. Implement hybrid message formatting logic
3. Update tool response integration
### Phase 4: UI Integration
1. Filter internal messages from UI display
2. Update message rendering logic
3. Add provider capability indicators
## Benefits
1. **Proper Conversation Structure**: Maintains role separation for conversation-capable providers
2. **Backward Compatibility**: Fallback strategy for simple prompt providers
3. **Tool Integration**: Clean separation of tool responses from user conversation
4. **Provider Optimization**: Each provider uses its optimal format
5. **UI Clarity**: Internal messages hidden from user interface
6. **Extensibility**: Easy to add new providers with different capabilities
## Migration Strategy
1. **Gradual Rollout**: Implement provider by provider
2. **Feature Flags**: Toggle between old and new systems
3. **Validation**: Extensive testing with each provider type
4. **Monitoring**: Track conversation quality improvements
## Detailed Implementation Specifications
### 1. Updated LLM Interface
```typescript
export interface LLMInterface {
configure(config: Record<string, any>): void;
executePrompt(prompt: string, options?: PromptOptions): Promise<PromptResponse>;
executeConversation?(messages: ConversationMessage[], options?: PromptOptions): Promise<PromptResponse>;
executePrompts(prompts: string[], options?: PromptOptions): Promise<PromptResponse[]>;
imagine?(prompt: string, options?: ImageOptions): Promise<ImageResponse>;
getConfig(): Record<string, any>;
isConfigured(): Promise<boolean>;
getCapabilities(): ProviderCapabilities;
supportsConversation(): boolean;
}
```
### 2. Message Builder Classes
```typescript
export class ConversationMessageBuilder {
static buildForProvider(
session: ChatSession,
currentMessage: string,
provider: string,
toolManager: ToolManager
): Promise<ConversationMessage[] | string> {
const capabilities = PROVIDER_CAPABILITIES[provider];
if (capabilities.supportsConversation) {
return this.buildConversationArray(session, currentMessage, capabilities, toolManager);
} else {
return this.buildPromptString(session, currentMessage, toolManager);
}
}
private static async buildConversationArray(
session: ChatSession,
currentMessage: string,
capabilities: ProviderCapabilities,
toolManager: ToolManager
): Promise<ConversationMessage[]> {
const messages: ConversationMessage[] = [];
// Add system message if supported
if (capabilities.supportsSystemMessages) {
const systemContent = await this.buildSystemPrompt(session, toolManager);
messages.push({
id: generateId(),
role: 'system',
content: systemContent,
timestamp: Date.now()
});
}
// Add conversation history (excluding internal tool responses for UI)
const conversationMessages = session.messages
.filter(msg => !msg.metadata?.internal)
.slice(-10); // Keep last 10 for context
messages.push(...conversationMessages);
// Add current user message
messages.push({
id: generateId(),
role: 'user',
content: currentMessage,
timestamp: Date.now()
});
return messages;
}
private static async buildPromptString(
session: ChatSession,
currentMessage: string,
toolManager: ToolManager
): Promise<string> {
const systemPrompt = await this.buildSystemPrompt(session, toolManager);
const conversationHistory = session.messages
.filter(msg => !msg.metadata?.internal)
.slice(-10)
.map(msg => `${msg.role}: ${msg.content}`)
.join('\n\n');
return [
`System: ${systemPrompt}`,
conversationHistory ? `\n\nConversation History:\n${conversationHistory}` : '',
`\n\nHuman: ${currentMessage}\n\nAssistant:`
].filter(Boolean).join('');
}
private static async buildSystemPrompt(
session: ChatSession,
toolManager: ToolManager
): Promise<string> {
const fileTreeContext = await toolManager.generateFileTreeContext();
const toolInstructions = toolManager.generateToolInstructions();
return SystemPromptComposer.compose({
base: session.systemPrompt || 'You are a helpful AI assistant.',
fileTree: fileTreeContext,
tools: toolInstructions,
context: ''
});
}
}
```
### 3. Provider-Specific Implementations
```typescript
// Example: Enhanced OpenAI Provider
export class OpenAIProvider extends BaseLLM {
getCapabilities(): ProviderCapabilities {
return PROVIDER_CAPABILITIES.OpenAI;
}
supportsConversation(): boolean {
return true;
}
async executeConversation(
messages: ConversationMessage[],
options: PromptOptions = {}
): Promise<PromptResponse> {
if (!(await this.isConfigured())) {
throw new Error("OpenAI provider is not configured");
}
const openAIMessages = messages.map(msg => ({
role: msg.role,
content: msg.content
}));
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify({
model: this.config.model,
messages: openAIMessages,
temperature: options.temperature ?? 0.7,
max_tokens: options.maxTokens,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`OpenAI API error: ${error.error?.message || "Unknown error"}`);
}
const data = await response.json();
return {
content: data.choices[0].message.content,
raw: data,
};
}
}
```
### 4. Tool Response Handling
```typescript
export class ToolResponseHandler {
static async integrateToolResponses(
session: ChatSession,
toolResponses: ToolResponse[],
provider: string
): Promise<void> {
const capabilities = PROVIDER_CAPABILITIES[provider];
const xmlResponses = toolResponses
.map(tr => session.toolManager.generateToolResponse(tr))
.join('\n\n');
if (capabilities.supportsConversation) {
// Add as internal user message for conversation providers
const toolResponseMessage: ConversationMessage = {
id: generateId(),
role: 'user',
content: xmlResponses,
timestamp: Date.now(),
metadata: {
isToolResponse: true,
internal: true,
toolCallId: toolResponses[0]?.id
}
};
session.messages.push(toolResponseMessage);
} else {
// For simple prompt providers, tool responses will be integrated
// into the prompt string during buildPromptString()
const toolContextMessage: ConversationMessage = {
id: generateId(),
role: 'user',
content: `Tool Results:\n${xmlResponses}`,
timestamp: Date.now(),
metadata: {
isToolResponse: true,
internal: false, // Visible in simple prompt providers
toolCallId: toolResponses[0]?.id
}
};
session.messages.push(toolContextMessage);
}
}
}
```
## Testing Strategy
### 1. Provider Capability Tests
- Verify each provider reports correct capabilities
- Test conversation vs prompt format selection
- Validate system prompt composition
### 2. Message Format Tests
- Test conversation array building for each provider type
- Verify prompt string fallback for simple providers
- Test Google Gemini custom format handling
### 3. Tool Integration Tests
- Verify tool responses are properly integrated
- Test internal message filtering for UI
- Validate XML tool response formatting
### 4. Migration Tests
- Test backward compatibility with existing sessions
- Verify gradual provider migration
- Test feature flag toggling