@@ -78,6 +78,20 @@ function isImageContentBlock(block: { type: string }): block is ImageContent {
|
||||
return block.type === "image";
|
||||
}
|
||||
|
||||
function isEncryptedReasoningDetail(detail: unknown): detail is OpenAIEncryptedReasoningDetail {
|
||||
if (typeof detail !== "object" || detail === null) {
|
||||
return false;
|
||||
}
|
||||
const candidate = detail as Record<string, unknown>;
|
||||
return (
|
||||
candidate.type === "reasoning.encrypted" &&
|
||||
typeof candidate.id === "string" &&
|
||||
candidate.id.length > 0 &&
|
||||
typeof candidate.data === "string" &&
|
||||
candidate.data.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
export interface OpenAICompletionsOptions extends StreamOptions {
|
||||
toolChoice?: "auto" | "none" | "required" | { type: "function"; function: { name: string } };
|
||||
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
@@ -96,6 +110,12 @@ type ResolvedChatTemplateKwargValue = string | number | boolean | null;
|
||||
|
||||
type ChatCompletionInstructionMessageParam = ChatCompletionDeveloperMessageParam | ChatCompletionSystemMessageParam;
|
||||
|
||||
type OpenAIEncryptedReasoningDetail = {
|
||||
type: "reasoning.encrypted";
|
||||
id: string;
|
||||
data: string;
|
||||
};
|
||||
|
||||
type ChatCompletionTextPartWithCacheControl = ChatCompletionContentPartText & {
|
||||
cache_control?: OpenAICompatCacheControl;
|
||||
};
|
||||
@@ -177,6 +197,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions", OpenA
|
||||
let hasFinishReason = false;
|
||||
const toolCallBlocksByIndex = new Map<number, StreamingToolCallBlock>();
|
||||
const toolCallBlocksById = new Map<string, StreamingToolCallBlock>();
|
||||
const pendingReasoningDetailsByToolCallId = new Map<string, string>();
|
||||
const blocks = output.content as StreamingBlock[];
|
||||
const getContentIndex = (block: StreamingBlock) => blocks.indexOf(block);
|
||||
const finishBlock = (block: StreamingBlock) => {
|
||||
@@ -232,6 +253,16 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions", OpenA
|
||||
}
|
||||
return thinkingBlock;
|
||||
};
|
||||
const applyPendingReasoningDetail = (block: StreamingToolCallBlock) => {
|
||||
if (!block.id) {
|
||||
return;
|
||||
}
|
||||
const pendingReasoningDetail = pendingReasoningDetailsByToolCallId.get(block.id);
|
||||
if (pendingReasoningDetail) {
|
||||
block.thoughtSignature = pendingReasoningDetail;
|
||||
pendingReasoningDetailsByToolCallId.delete(block.id);
|
||||
}
|
||||
};
|
||||
const ensureToolCallBlock = (toolCall: StreamingToolCallDelta) => {
|
||||
const streamIndex = typeof toolCall.index === "number" ? toolCall.index : undefined;
|
||||
let block = streamIndex !== undefined ? toolCallBlocksByIndex.get(streamIndex) : undefined;
|
||||
@@ -267,6 +298,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions", OpenA
|
||||
if (toolCall.id) {
|
||||
toolCallBlocksById.set(toolCall.id, block);
|
||||
}
|
||||
applyPendingReasoningDetail(block);
|
||||
return block;
|
||||
};
|
||||
|
||||
@@ -376,15 +408,16 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions", OpenA
|
||||
}
|
||||
}
|
||||
|
||||
const reasoningDetails = (choice.delta as any).reasoning_details;
|
||||
if (reasoningDetails && Array.isArray(reasoningDetails)) {
|
||||
const reasoningDetails = (choice.delta as { reasoning_details?: unknown }).reasoning_details;
|
||||
if (Array.isArray(reasoningDetails)) {
|
||||
for (const detail of reasoningDetails) {
|
||||
if (detail.type === "reasoning.encrypted" && detail.id && detail.data) {
|
||||
const matchingToolCall = output.content.find(
|
||||
(b) => b.type === "toolCall" && b.id === detail.id,
|
||||
) as ToolCall | undefined;
|
||||
if (isEncryptedReasoningDetail(detail)) {
|
||||
const serializedDetail = JSON.stringify(detail);
|
||||
const matchingToolCall = toolCallBlocksById.get(detail.id);
|
||||
if (matchingToolCall) {
|
||||
matchingToolCall.thoughtSignature = JSON.stringify(detail);
|
||||
matchingToolCall.thoughtSignature = serializedDetail;
|
||||
} else {
|
||||
pendingReasoningDetailsByToolCallId.set(detail.id, serializedDetail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user