@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
100 lines • 3.53 kB
JavaScript
/**
* Checks if an assistant message is empty (no content and no tool calls).
* Empty assistant messages cause API errors:
* "400 Bad Request: Assistant message must have either content or tool_calls, but not none."
*
* Exported for testing purposes.
*/
export function isEmptyAssistantMessage(message) {
if (message.role !== 'assistant') {
return false;
}
// Check for content - handle both string and array content formats
const hasContent = Array.isArray(message.content)
? message.content.length > 0
: typeof message.content === 'string' && message.content.trim().length > 0;
// Tool calls are in a separate property for AI SDK messages
const hasToolCalls = 'toolCalls' in message &&
Array.isArray(message.toolCalls) &&
message.toolCalls.length > 0;
return !hasContent && !hasToolCalls;
}
/**
* Convert our Message format to AI SDK v6 ModelMessage format
*
* Tool messages: Converted to AI SDK tool-result format with proper structure.
*/
export function convertToModelMessages(messages) {
return messages.map((msg) => {
if (msg.role === 'tool') {
// Convert to AI SDK tool-result format
// AI SDK expects: { role: 'tool', content: [{ type: 'tool-result', toolCallId, toolName, output }] }
// where output is { type: 'text', value: string } or { type: 'json', value: JSONValue }
return {
role: 'tool',
content: [
{
type: 'tool-result',
toolCallId: msg.tool_call_id || '',
toolName: msg.name || '',
output: {
type: 'text',
value: msg.content,
},
},
],
};
}
if (msg.role === 'system') {
return {
role: 'system',
content: msg.content,
};
}
if (msg.role === 'user') {
return {
role: 'user',
content: msg.content,
};
}
if (msg.role === 'assistant') {
// Build content array
const content = [];
// Add text content if present
if (msg.content) {
content.push({
type: 'text',
text: msg.content,
});
}
// Add tool calls if present (for auto-executed messages)
if (msg.tool_calls && msg.tool_calls.length > 0) {
for (const toolCall of msg.tool_calls) {
content.push({
type: 'tool-call',
toolCallId: toolCall.id,
toolName: toolCall.function.name,
input: toolCall.function.arguments,
});
}
}
// If no content at all, add empty text to avoid empty message
if (content.length === 0) {
content.push({
type: 'text',
text: '',
});
}
return {
role: 'assistant',
content,
};
}
// Fallback - should never happen
return {
role: 'user',
content: msg.content,
};
});
}
//# sourceMappingURL=message-converter.js.map