Files
claude-task-master/packages/ai-sdk-provider-grok-cli/src/message-converter.ts
2025-09-30 10:41:43 +02:00

154 lines
3.8 KiB
TypeScript

/**
* Message format conversion utilities for Grok CLI provider
*/
import type { GrokCliMessage, GrokCliResponse } from './types.js';
/**
* AI SDK message type (simplified interface)
*/
interface AISDKMessage {
role: string;
content:
| string
| Array<{ type: string; text?: string }>
| { text?: string; [key: string]: unknown };
}
/**
* Convert AI SDK messages to Grok CLI compatible format
* @param messages - AI SDK message array
* @returns Grok CLI compatible messages
*/
export function convertToGrokCliMessages(
messages: AISDKMessage[]
): GrokCliMessage[] {
return messages.map((message) => {
// Handle different message content types
let content = '';
if (typeof message.content === 'string') {
content = message.content;
} else if (Array.isArray(message.content)) {
// Handle multi-part content (text and images)
content = message.content
.filter((part) => part.type === 'text')
.map((part) => part.text || '')
.join('\n');
} else if (message.content && typeof message.content === 'object') {
// Handle object content
content = message.content.text || JSON.stringify(message.content);
}
return {
role: message.role,
content: content.trim()
};
});
}
/**
* Convert Grok CLI response to AI SDK format
* @param responseText - Raw response text from Grok CLI (JSONL format)
* @returns AI SDK compatible response object
*/
export function convertFromGrokCliResponse(responseText: string): {
text: string;
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
} {
try {
// Grok CLI outputs JSONL format - each line is a separate JSON message
const lines = responseText
.trim()
.split('\n')
.filter((line) => line.trim());
// Parse each line as JSON and find assistant messages
const messages: GrokCliResponse[] = [];
for (const line of lines) {
try {
const message = JSON.parse(line) as GrokCliResponse;
messages.push(message);
} catch (parseError) {
// Skip invalid JSON lines
continue;
}
}
// Find the last assistant message
const assistantMessage = messages
.filter((msg) => msg.role === 'assistant')
.pop();
if (assistantMessage && assistantMessage.content) {
return {
text: assistantMessage.content,
usage: assistantMessage.usage
? {
promptTokens: assistantMessage.usage.prompt_tokens || 0,
completionTokens: assistantMessage.usage.completion_tokens || 0,
totalTokens: assistantMessage.usage.total_tokens || 0
}
: undefined
};
}
// Fallback: if no assistant message found, return the raw text
return {
text: responseText.trim(),
usage: undefined
};
} catch (error) {
// If parsing fails completely, treat as plain text response
return {
text: responseText.trim(),
usage: undefined
};
}
}
/**
* Create a prompt string for Grok CLI from messages
* @param messages - AI SDK message array
* @returns Formatted prompt string
*/
export function createPromptFromMessages(messages: AISDKMessage[]): string {
const grokMessages = convertToGrokCliMessages(messages);
// Create a conversation-style prompt
const prompt = grokMessages
.map((message) => {
switch (message.role) {
case 'system':
return `System: ${message.content}`;
case 'user':
return `User: ${message.content}`;
case 'assistant':
return `Assistant: ${message.content}`;
default:
return `${message.role}: ${message.content}`;
}
})
.join('\n\n');
return prompt;
}
/**
* Escape shell arguments for safe CLI execution
* @param arg - Argument to escape
* @returns Shell-escaped argument
*/
export function escapeShellArg(arg: string | unknown): string {
if (typeof arg !== 'string') {
arg = String(arg);
}
// Replace single quotes with '\''
return "'" + (arg as string).replace(/'/g, "'\\''") + "'";
}