refactor: extract body builders and types to src/provider-bodies.ts

Move buildAnthropicMessagesBody, buildOpenAiChatBody, buildOpenAiResponsesBody,
buildGeminiBody, tokenLimitFieldForOpenAiChat and their type interfaces to a
new module. Reduces providers.ts from 494 to 367 lines (under 400 target).

Change-Id: Ib124d5cf5f2f66bac03acbf83e1a224cb6325d12
This commit is contained in:
fancivez 2026-04-27 13:46:46 +08:00 committed by wujunchen
parent c111865fbd
commit 5eb005f293
3 changed files with 176 additions and 154 deletions

36
main.js

File diff suppressed because one or more lines are too long

143
src/provider-bodies.ts Normal file
View file

@ -0,0 +1,143 @@
'use strict';
import {
ANTHROPIC_CARD_TOOL_NAME,
anthropicCardTool,
cardOutputSchema,
openAiJsonSchemaResponseFormat,
openAiResponsesTextFormat,
} from './schema';
import { API_FORMATS, getApiFormat, getApiPreset, modelForApi } from './settings';
import type { PluginSettings } from './types';
/* ---------- Body type interfaces ---------- */
export interface AnthropicMessagesBody {
model: string;
max_tokens: number;
system: string;
messages: Array<{ role: string; content: string }>;
tools?: unknown[];
tool_choice?: { type: string; name: string };
stream?: boolean;
}
export interface OpenAiChatBody {
model: string;
messages: Array<{ role: string; content: string }>;
response_format?: unknown;
stream?: boolean;
[tokenField: string]: unknown;
}
export interface OpenAiResponsesBody {
model: string;
instructions: string;
input: string;
max_output_tokens: number;
text?: unknown;
stream?: boolean;
}
interface GeminiGenerationConfig {
temperature: number;
maxOutputTokens: number;
responseMimeType?: string;
responseJsonSchema?: unknown;
}
export interface GeminiBody {
systemInstruction: { parts: Array<{ text: string }> };
contents: Array<{ role: string; parts: Array<{ text: string }> }>;
generationConfig: GeminiGenerationConfig;
}
/* ---------- Body builders ---------- */
export function tokenLimitFieldForOpenAiChat(settings: PluginSettings): string {
const preset = getApiPreset(settings);
const format = API_FORMATS[getApiFormat(settings)];
return preset.tokenLimitField || format?.tokenLimitField || 'max_tokens';
}
export function buildAnthropicMessagesBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): AnthropicMessagesBody {
const structured = !options || options.structured !== false;
const body: AnthropicMessagesBody = {
model: modelForApi(settings),
max_tokens: Number(settings.apiMaxTokens) || 4096,
system,
messages: [{ role: 'user', content: user }],
};
if (structured) {
body.tools = [anthropicCardTool()];
body.tool_choice = { type: 'tool', name: ANTHROPIC_CARD_TOOL_NAME };
}
return body;
}
export function buildOpenAiChatBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): OpenAiChatBody {
const structured = !options || options.structured !== false;
const body: OpenAiChatBody = {
model: modelForApi(settings),
messages: [
{ role: 'system', content: system },
{ role: 'user', content: user },
],
};
body[tokenLimitFieldForOpenAiChat(settings)] = Number(settings.apiMaxTokens) || 4096;
if (structured) {
body.response_format = openAiJsonSchemaResponseFormat();
}
return body;
}
export function buildOpenAiResponsesBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): OpenAiResponsesBody {
const structured = !options || options.structured !== false;
const body: OpenAiResponsesBody = {
model: modelForApi(settings),
instructions: system,
input: user,
max_output_tokens: Number(settings.apiMaxTokens) || 4096,
};
if (structured) {
body.text = openAiResponsesTextFormat();
}
return body;
}
export function buildGeminiBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): GeminiBody {
const structured = !options || options.structured !== false;
const generationConfig: GeminiGenerationConfig = {
temperature: 0,
maxOutputTokens: Number(settings.apiMaxTokens) || 4096,
};
if (structured) {
generationConfig.responseMimeType = 'application/json';
generationConfig.responseJsonSchema = cardOutputSchema(false);
}
return {
systemInstruction: { parts: [{ text: system }] },
contents: [{ role: 'user', parts: [{ text: user }] }],
generationConfig,
};
}

View file

@ -1,6 +1,12 @@
'use strict';
import { translate } from './i18n';
import {
buildAnthropicMessagesBody,
buildGeminiBody,
buildOpenAiChatBody,
buildOpenAiResponsesBody,
} from './provider-bodies';
import {
cardsFromAnthropicToolUse,
textFromAnthropicMessagesResponse,
@ -8,14 +14,7 @@ import {
textFromOpenAiChatResponse,
textFromOpenAiResponsesResponse,
} from './provider-parsers';
import {
ANTHROPIC_CARD_TOOL_NAME,
anthropicCardTool,
cardOutputSchema,
openAiJsonSchemaResponseFormat,
openAiResponsesTextFormat,
parseCardsJson,
} from './schema';
import { parseCardsJson } from './schema';
import {
API_FORMATS,
getApiAuthType,
@ -28,6 +27,14 @@ import {
import { deltaExtractorForFormat, type StreamProgress, streamingFetch } from './streaming';
import type { PluginSettings, RawCard } from './types';
export {
buildAnthropicMessagesBody,
buildGeminiBody,
buildOpenAiChatBody,
buildOpenAiResponsesBody,
tokenLimitFieldForOpenAiChat,
} from './provider-bodies';
/* ---------- Typed request/response shapes ---------- */
type RequestUrlFunction = (params: {
@ -44,46 +51,6 @@ function requiredDeltaExtractor(format: string) {
return extractor;
}
interface AnthropicMessagesBody {
model: string;
max_tokens: number;
system: string;
messages: Array<{ role: string; content: string }>;
tools?: unknown[];
tool_choice?: { type: string; name: string };
stream?: boolean;
}
interface OpenAiChatBody {
model: string;
messages: Array<{ role: string; content: string }>;
response_format?: unknown;
stream?: boolean;
[tokenField: string]: unknown;
}
interface OpenAiResponsesBody {
model: string;
instructions: string;
input: string;
max_output_tokens: number;
text?: unknown;
stream?: boolean;
}
interface GeminiGenerationConfig {
temperature: number;
maxOutputTokens: number;
responseMimeType?: string;
responseJsonSchema?: unknown;
}
interface GeminiBody {
systemInstruction: { parts: Array<{ text: string }> };
contents: Array<{ role: string; parts: Array<{ text: string }> }>;
generationConfig: GeminiGenerationConfig;
}
/* ---------- Helpers ---------- */
function endpointUrl(baseUrl: string, suffixes: string[]) {
@ -237,94 +204,6 @@ async function requestJsonBodyWithStructuredFallback(
}
}
export function tokenLimitFieldForOpenAiChat(settings: PluginSettings): string {
const preset = getApiPreset(settings);
const format = API_FORMATS[getApiFormat(settings)];
return preset.tokenLimitField || format?.tokenLimitField || 'max_tokens';
}
export function buildAnthropicMessagesBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): AnthropicMessagesBody {
const structured = !options || options.structured !== false;
const body: AnthropicMessagesBody = {
model: modelForApi(settings),
max_tokens: Number(settings.apiMaxTokens) || 4096,
system,
messages: [{ role: 'user', content: user }],
};
if (structured) {
body.tools = [anthropicCardTool()];
body.tool_choice = { type: 'tool', name: ANTHROPIC_CARD_TOOL_NAME };
}
return body;
}
export function buildOpenAiChatBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): OpenAiChatBody {
const structured = !options || options.structured !== false;
const body: OpenAiChatBody = {
model: modelForApi(settings),
messages: [
{ role: 'system', content: system },
{ role: 'user', content: user },
],
};
body[tokenLimitFieldForOpenAiChat(settings)] = Number(settings.apiMaxTokens) || 4096;
if (structured) {
body.response_format = openAiJsonSchemaResponseFormat();
}
return body;
}
export function buildOpenAiResponsesBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): OpenAiResponsesBody {
const structured = !options || options.structured !== false;
const body: OpenAiResponsesBody = {
model: modelForApi(settings),
instructions: system,
input: user,
max_output_tokens: Number(settings.apiMaxTokens) || 4096,
};
if (structured) {
body.text = openAiResponsesTextFormat();
}
return body;
}
export function buildGeminiBody(
system: string,
user: string,
settings: PluginSettings,
options?: { structured?: boolean },
): GeminiBody {
const structured = !options || options.structured !== false;
const generationConfig: GeminiGenerationConfig = {
temperature: 0,
maxOutputTokens: Number(settings.apiMaxTokens) || 4096,
};
if (structured) {
generationConfig.responseMimeType = 'application/json';
generationConfig.responseJsonSchema = cardOutputSchema(false);
}
return {
systemInstruction: { parts: [{ text: system }] },
contents: [{ role: 'user', parts: [{ text: user }] }],
generationConfig,
};
}
async function summarizeViaAnthropicMessages(
requestUrlImpl: RequestUrlFunction,
system: string,