feat: update grok-cli ai sdk provider to v5 (#1252)
This commit is contained in:
153
packages/ai-sdk-provider-grok-cli/src/message-converter.ts
Normal file
153
packages/ai-sdk-provider-grok-cli/src/message-converter.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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: any) => 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, "'\\''") + "'";
|
||||
}
|
||||
Reference in New Issue
Block a user