Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 35 additions & 44 deletions src/providers/anthropic/chatComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,30 @@ interface AnthropicMessage extends Message, AnthropicPromptCache {
}

const transformAssistantMessage = (msg: Message): AnthropicMessage => {
let content: AnthropicContentItem[] = [];
let transformedContent: AnthropicContentItem[] = [];
let inputContent: ContentType[] | string | undefined =
msg.content_blocks ?? msg.content;
const containsToolCalls = msg.tool_calls && msg.tool_calls.length;

if (msg.content && typeof msg.content === 'string') {
content.push({
if (inputContent && typeof inputContent === 'string') {
transformedContent.push({
type: 'text',
text: msg.content,
text: inputContent,
});
} else if (
msg.content &&
typeof msg.content === 'object' &&
msg.content.length
inputContent &&
typeof inputContent === 'object' &&
inputContent.length
) {
msg.content.forEach((item) => {
if (['text', 'thinking'].includes(item.type))
content.push(item as AnthropicContentItem);
inputContent.forEach((item) => {
if (item.type !== 'tool_use') {
transformedContent.push(item as AnthropicContentItem);
}
});
}
if (containsToolCalls) {
msg.tool_calls.forEach((toolCall: any) => {
content.push({
transformedContent.push({
type: 'tool_use',
name: toolCall.function.name,
id: toolCall.id,
Expand All @@ -104,7 +107,7 @@ const transformAssistantMessage = (msg: Message): AnthropicMessage => {
}
return {
role: msg.role,
content: content as AnthropicMessageContentItem[],
content: transformedContent as AnthropicMessageContentItem[],
};
};

Expand Down Expand Up @@ -353,23 +356,14 @@ interface AnthorpicTextContentItem {
text: string;
}

interface AnthropicThinkingContentItem {
type: 'thinking';
thinking: string;
signature: string;
}

interface AnthropicToolContentItem {
type: 'tool_use';
name: string;
id: string;
input: Record<string, any>;
}

type AnthropicContentItem =
| AnthorpicTextContentItem
| AnthropicThinkingContentItem
| AnthropicToolContentItem;
type AnthropicContentItem = AnthorpicTextContentItem | AnthropicToolContentItem;

export interface AnthropicChatCompleteResponse {
id: string;
Expand All @@ -391,12 +385,10 @@ export interface AnthropicChatCompleteStreamResponse {
type: string;
index: number;
delta: {
type: string;
type?: string;
text?: string;
thinking?: string;
partial_json?: string;
stop_reason?: string;
signature?: string;
};
content_block?: {
type: string;
Expand Down Expand Up @@ -470,18 +462,10 @@ export const AnthropicChatCompleteResponseTransform: (
const shouldSendCacheUsage =
cache_creation_input_tokens || cache_read_input_tokens;

let content: AnthropicContentItem[] | string = strictOpenAiCompliance
? ''
: [];
let content: string = '';
response.content.forEach((item) => {
if (!strictOpenAiCompliance && Array.isArray(content)) {
if (['text', 'thinking'].includes(item.type)) {
content.push(item);
}
} else {
if (item.type === 'text') {
content += item.text;
}
if (item.type === 'text') {
content += item.text;
}
});

Expand Down Expand Up @@ -510,6 +494,11 @@ export const AnthropicChatCompleteResponseTransform: (
message: {
role: 'assistant',
content,
...(!strictOpenAiCompliance && {
content_blocks: response.content.filter(
(item) => item.type !== 'tool_use'
),
}),
tool_calls: toolCalls.length ? toolCalls : undefined,
},
index: 0,
Expand Down Expand Up @@ -688,12 +677,12 @@ export const AnthropicChatCompleteStreamChunkTransform: (
}

const content = parsedChunk.delta?.text;
const thinking = !strictOpenAiCompliance
? parsedChunk.delta?.thinking
: undefined;
const signature = !strictOpenAiCompliance
? parsedChunk.delta?.signature
: undefined;

const contentBlockObject = {
index: parsedChunk.index,
delta: parsedChunk.delta ?? parsedChunk.content_block ?? {},
};
delete contentBlockObject.delta.type;

return (
`data: ${JSON.stringify({
Expand All @@ -706,9 +695,11 @@ export const AnthropicChatCompleteStreamChunkTransform: (
{
delta: {
content,
thinking,
signature,
tool_calls: toolCalls.length ? toolCalls : undefined,
...(!strictOpenAiCompliance &&
!toolCalls.length && {
content_blocks: [contentBlockObject],
}),
},
index: 0,
logprobs: null,
Expand Down
118 changes: 77 additions & 41 deletions src/providers/bedrock/chatComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ChatCompletionResponse,
ErrorResponse,
ProviderConfig,
StreamContentBlock,
} from '../types';
import {
generateErrorResponse,
Expand Down Expand Up @@ -39,16 +40,16 @@ export interface BedrockChatCompletionsParams extends Params {
};
anthropic_version?: string;
countPenalty?: number;
thinking?: {
type: string;
budget_tokens: number;
};
}

export interface BedrockConverseAnthropicChatCompletionsParams
extends BedrockChatCompletionsParams {
anthropic_version?: string;
user?: string;
thinking?: {
type: string;
budget_tokens: number;
};
}

export interface BedrockConverseCohereChatCompletionsParams
Expand Down Expand Up @@ -98,6 +99,12 @@ const transformAndAppendThinkingMessageItem = (
},
},
});
} else if (item.type === 'redacted_thinking') {
out.push({
reasoningContent: {
redactedContent: item.data,
},
});
}
};

Expand All @@ -114,13 +121,15 @@ const getMessageContent = (message: Message) => {
];
}
const out: BedrockContentItem[] = [];
const inputContent: ContentType[] | string | undefined =
message.content_blocks ?? message.content;
// if message is a string, return a single element array with the text
if (typeof message.content === 'string') {
if (typeof inputContent === 'string') {
out.push({
text: message.content,
text: inputContent,
});
} else if (message.content) {
message.content.forEach((item) => {
} else if (inputContent) {
inputContent.forEach((item) => {
if (item.type === 'text') {
out.push({
text: item.text || '',
Expand Down Expand Up @@ -313,10 +322,11 @@ type BedrockContentItem = {
input: object;
};
reasoningContent?: {
reasoningText: {
reasoningText?: {
signature: string;
text: string;
};
redactedContent?: string;
};
image?: {
source: {
Expand Down Expand Up @@ -364,6 +374,30 @@ export const BedrockErrorResponseTransform: (
return undefined;
};

const transformContentBlocks = (contentBlocks: BedrockContentItem[]) => {
const output: ContentType[] = [];
contentBlocks.forEach((contentBlock) => {
if (contentBlock.text) {
output.push({
type: 'text',
text: contentBlock.text,
});
} else if (contentBlock.reasoningContent?.reasoningText) {
output.push({
type: 'thinking',
thinking: contentBlock.reasoningContent.reasoningText.text,
signature: contentBlock.reasoningContent.reasoningText.signature,
});
} else if (contentBlock.reasoningContent?.redactedContent) {
output.push({
type: 'redacted_thinking',
data: contentBlock.reasoningContent.redactedContent,
});
}
});
return output;
};

export const BedrockChatCompleteResponseTransform: (
response: BedrockChatCompletionResponse | BedrockErrorResponse,
responseStatus: number,
Expand All @@ -383,28 +417,15 @@ export const BedrockChatCompleteResponseTransform: (
}

if ('output' in response) {
let content: string | ContentType[] = strictOpenAiCompliance ? '' : [];
if (!strictOpenAiCompliance) {
response.output.message.content.forEach((item) => {
if (item.text && Array.isArray(content)) {
content.push({
type: 'text',
text: item.text,
});
} else if (item.reasoningContent && Array.isArray(content)) {
content.push({
type: 'thinking',
thinking: item.reasoningContent.reasoningText.text,
signature: item.reasoningContent.reasoningText.signature,
});
}
});
} else {
content = response.output.message.content
.filter((item) => item.text)
.map((item) => item.text)
.join('\n');
}
let content: string = '';
content = response.output.message.content
.filter((item) => item.text)
.map((item) => item.text)
.join('\n');
const contentBlocks = !strictOpenAiCompliance
? transformContentBlocks(response.output.message.content)
: undefined;

const responseObj: ChatCompletionResponse = {
id: Date.now().toString(),
object: 'chat.completion',
Expand All @@ -417,6 +438,9 @@ export const BedrockChatCompleteResponseTransform: (
message: {
role: 'assistant',
content,
...(!strictOpenAiCompliance && {
content_blocks: contentBlocks,
}),
},
finish_reason: response.stopReason,
},
Expand Down Expand Up @@ -455,8 +479,9 @@ export interface BedrockChatCompleteStreamChunk {
input: object;
};
reasoningContent?: {
signature: string;
text: string;
text?: string;
signature?: string;
redactedContent?: string;
};
};
start?: {
Expand Down Expand Up @@ -552,12 +577,21 @@ export const BedrockChatCompleteStreamChunkTransform: (
}

const content = parsedChunk.delta?.text;
const thinking = !strictOpenAiCompliance
? parsedChunk.delta?.reasoningContent?.text
: undefined;
const signature = !strictOpenAiCompliance
? parsedChunk.delta?.reasoningContent?.signature
: undefined;

const contentBlockObject: StreamContentBlock = {
index: parsedChunk.contentBlockIndex ?? 0,
delta: {},
};
if (parsedChunk.delta?.reasoningContent?.text)
contentBlockObject.delta.thinking = parsedChunk.delta.reasoningContent.text;
if (parsedChunk.delta?.reasoningContent?.signature)
contentBlockObject.delta.signature =
parsedChunk.delta.reasoningContent.signature;
if (parsedChunk.delta?.text)
contentBlockObject.delta.text = parsedChunk.delta.text;
if (parsedChunk.delta?.reasoningContent?.redactedContent)
contentBlockObject.delta.data =
parsedChunk.delta.reasoningContent.redactedContent;

return `data: ${JSON.stringify({
id: fallbackId,
Expand All @@ -571,8 +605,10 @@ export const BedrockChatCompleteStreamChunkTransform: (
delta: {
role: 'assistant',
content,
thinking,
signature,
...(!strictOpenAiCompliance &&
!toolCalls.length && {
content_blocks: [contentBlockObject],
}),
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
},
},
Expand Down
18 changes: 10 additions & 8 deletions src/providers/google-vertex-ai/chatComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -895,12 +895,12 @@ export const VertexAnthropicChatCompleteStreamChunkTransform: (
}

const content = parsedChunk.delta?.text;
const thinking = !strictOpenAiCompliance
? parsedChunk.delta?.thinking
: undefined;
const signature = !strictOpenAiCompliance
? parsedChunk.delta?.signature
: undefined;

const contentBlockObject = {
index: parsedChunk.index,
delta: parsedChunk.delta ?? parsedChunk.content_block ?? {},
};
delete contentBlockObject.delta.type;

return (
`data: ${JSON.stringify({
Expand All @@ -913,9 +913,11 @@ export const VertexAnthropicChatCompleteStreamChunkTransform: (
{
delta: {
content,
thinking,
signature,
tool_calls: toolCalls.length ? toolCalls : undefined,
...(!strictOpenAiCompliance &&
!toolCalls.length && {
content_blocks: [contentBlockObject],
}),
},
index: 0,
logprobs: null,
Expand Down
Loading