@kdump/code-cli-any-llm
Version:
> A unified gateway for the Gemini, opencode, crush, and Qwen Code AI CLIs
950 lines • 35.8 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var ClaudeCodeProvider_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClaudeCodeProvider = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const crypto_1 = require("crypto");
const node_stream_1 = require("node:stream");
const request_transformer_1 = require("../../transformers/request.transformer");
const response_transformer_1 = require("../../transformers/response.transformer");
const stream_transformer_1 = require("../../transformers/stream.transformer");
const tokenizer_service_1 = require("../../services/tokenizer.service");
const ToolCallProcessor_1 = require("../../utils/zhipu/ToolCallProcessor");
class ClaudeRequestError extends Error {
status;
body;
constructor(status, body) {
super(`Claude Code request failed with status ${status}`);
this.status = status;
this.body = body;
}
}
let ClaudeCodeProvider = ClaudeCodeProvider_1 = class ClaudeCodeProvider {
configService;
requestTransformer;
responseTransformer;
tokenizerService;
toolCallProcessor;
logger = new common_1.Logger(ClaudeCodeProvider_1.name);
config;
enabled = false;
initialized = false;
constructor(configService, requestTransformer, responseTransformer, tokenizerService, toolCallProcessor) {
this.configService = configService;
this.requestTransformer = requestTransformer;
this.responseTransformer = responseTransformer;
this.tokenizerService = tokenizerService;
this.toolCallProcessor = toolCallProcessor;
}
onModuleInit() {
this.ensureInitialized();
}
isEnabled() {
this.ensureInitialized();
return this.enabled;
}
getConfig() {
this.ensureInitialized();
return this.config;
}
async generateFromGemini(geminiRequest, targetModel) {
const config = this.ensureEnabledConfig();
const openAIRequest = this.prepareOpenAIRequest(geminiRequest, targetModel);
const payload = this.buildClaudePayload(openAIRequest, config, false);
const response = await this.sendClaudeMessagesRequest(payload, config, false);
if (!response.ok) {
await this.handleErrorResponse(response);
}
const data = (await response.json());
const openAIResponse = this.convertClaudeResponseToOpenAI(data, config.model);
return this.responseTransformer.transformResponse(openAIResponse);
}
async *streamFromGemini(geminiRequest, targetModel) {
const config = this.ensureEnabledConfig();
const openAIRequest = this.prepareOpenAIRequest(geminiRequest, targetModel);
openAIRequest.stream = true;
const payload = this.buildClaudePayload(openAIRequest, config, true);
const response = await this.sendClaudeMessagesRequest(payload, config, true);
if (!response.ok) {
await this.handleErrorResponse(response);
}
if (!response.body) {
throw new Error('Claude Code streaming response body is empty');
}
const stream = node_stream_1.Readable.fromWeb(response.body);
const context = {
responseId: (0, crypto_1.randomUUID)(),
model: config.model,
created: Math.floor(Date.now() / 1000),
started: false,
toolCallStates: new Map(),
finalChunkSent: false,
};
const streamTransformer = new stream_transformer_1.StreamTransformer(this.tokenizerService, this.toolCallProcessor);
const promptTokenCount = this.computePromptTokens(geminiRequest, targetModel);
streamTransformer.initializeForModel(targetModel, promptTokenCount);
for await (const chunk of this.parseClaudeStream(stream, context)) {
const geminiChunk = streamTransformer.transformStreamChunk(chunk);
yield geminiChunk;
}
const bufferedText = streamTransformer.getBufferedText();
if (bufferedText) {
const finalChunk = {
candidates: [
{
content: {
role: 'model',
parts: [{ text: bufferedText }],
},
index: 0,
finishReason: 'STOP',
},
],
};
streamTransformer.applyUsageMetadata(finalChunk);
yield finalChunk;
}
streamTransformer.reset();
}
async generateContent(request) {
const config = this.ensureEnabledConfig();
const openAIRequest = {
...request,
stream: false,
};
const payload = this.buildClaudePayload(openAIRequest, config, false);
const response = await this.sendClaudeMessagesRequest(payload, config, false);
if (!response.ok) {
await this.handleErrorResponse(response);
}
const data = (await response.json());
return this.convertClaudeResponseToOpenAI(data, config.model);
}
async *generateContentStream(request) {
const config = this.ensureEnabledConfig();
const openAIRequest = {
...request,
stream: true,
};
const payload = this.buildClaudePayload(openAIRequest, config, true);
const response = await this.sendClaudeMessagesRequest(payload, config, true);
if (!response.ok) {
await this.handleErrorResponse(response);
}
if (!response.body) {
throw new Error('Claude Code streaming response body is empty');
}
const stream = node_stream_1.Readable.fromWeb(response.body);
const context = {
responseId: (0, crypto_1.randomUUID)(),
model: config.model,
created: Math.floor(Date.now() / 1000),
started: false,
toolCallStates: new Map(),
finalChunkSent: false,
};
for await (const chunk of this.parseClaudeStream(stream, context)) {
yield chunk;
}
}
listModels() {
const config = this.ensureEnabledConfig();
return Promise.resolve([
{
id: config.model,
object: 'model',
created: Math.floor(Date.now() / 1000),
owned_by: 'claude-code',
},
]);
}
async healthCheck() {
try {
const config = this.ensureEnabledConfig();
const url = this.buildUrl(config.baseURL, 'v1/models?limit=1');
const headers = this.buildHeaders(false, config);
delete headers['content-type'];
const controller = new AbortController();
const timeoutHandle = setTimeout(() => controller.abort(), config.timeout);
try {
const response = await fetch(url.toString(), {
method: 'GET',
headers,
signal: controller.signal,
});
if (!response.ok) {
await this.handleErrorResponse(response);
}
}
finally {
clearTimeout(timeoutHandle);
}
return {
status: 'healthy',
details: {
provider: 'Claude Code',
baseURL: config.baseURL,
model: config.model,
},
};
}
catch (error) {
return {
status: 'unhealthy',
details: {
provider: 'Claude Code',
error: error.message,
},
};
}
}
ensureInitialized() {
if (!this.initialized) {
this.refreshConfig();
}
}
ensureEnabledConfig() {
this.ensureInitialized();
if (!this.enabled || !this.config) {
throw new Error('Claude Code provider is not enabled.');
}
return this.config;
}
prepareOpenAIRequest(geminiRequest, targetModel) {
return this.requestTransformer.transformRequest(geminiRequest, targetModel);
}
refreshConfig() {
const providerInput = this.configService.get('aiProvider') || 'claudeCode';
const normalizedProvider = providerInput.trim().toLowerCase();
if (normalizedProvider !== 'claudecode' &&
normalizedProvider !== 'claude-code') {
this.enabled = false;
this.config = undefined;
this.initialized = true;
return;
}
const rawConfig = this.configService.get('claudeCode');
if (!rawConfig) {
this.logger.error('Claude Code provider configuration missing; disabling provider.');
this.enabled = false;
this.config = undefined;
this.initialized = true;
return;
}
if (!rawConfig.apiKey || !rawConfig.apiKey.trim()) {
this.logger.error('Claude Code provider is missing an API key; disabling provider.');
this.enabled = false;
this.config = undefined;
this.initialized = true;
return;
}
const resolved = {
apiKey: rawConfig.apiKey.trim(),
baseURL: rawConfig.baseURL || 'https://open.bigmodel.cn/api/anthropic',
model: rawConfig.model || 'claude-sonnet-4-5-20250929',
timeout: rawConfig.timeout ?? 1800000,
anthropicVersion: rawConfig.anthropicVersion || '2023-06-01',
beta: rawConfig.beta,
userAgent: rawConfig.userAgent || 'claude-cli/2.0.1 (external, cli)',
xApp: rawConfig.xApp || 'cli',
dangerousDirectBrowserAccess: rawConfig.dangerousDirectBrowserAccess ?? true,
maxOutputTokens: rawConfig.maxOutputTokens,
extraHeaders: rawConfig.extraHeaders,
};
this.config = resolved;
this.enabled = true;
this.initialized = true;
this.logger.log(`Claude Code provider initialized with model: ${resolved.model}`);
}
buildClaudePayload(request, config, stream) {
const { messages, system } = this.transformMessages(request.messages);
const tools = this.transformTools(request.tools);
const payload = {
model: config.model,
messages,
stream,
max_tokens: request.max_tokens ?? config.maxOutputTokens ?? 4096,
};
if (request.temperature !== undefined) {
payload.temperature = request.temperature;
}
if (request.top_p !== undefined) {
payload.top_p = request.top_p;
}
if (system) {
payload.system = system;
}
if (tools && tools.length > 0) {
payload.tools = tools;
}
const toolChoice = this.transformToolChoice(request.tool_choice);
if (toolChoice) {
payload.tool_choice = toolChoice;
}
if (request.stop) {
payload.stop_sequences = Array.isArray(request.stop)
? request.stop
: [request.stop];
}
return payload;
}
transformMessages(messages = []) {
const anthropicMessages = [];
const systemPrompts = [];
let pendingToolResults = [];
const flushToolResults = () => {
if (pendingToolResults.length > 0) {
anthropicMessages.push({
role: 'user',
content: pendingToolResults,
});
pendingToolResults = [];
}
};
for (const message of messages) {
if (message.role === 'system' && typeof message.content === 'string') {
systemPrompts.push(message.content.trim());
continue;
}
if (message.role === 'user') {
flushToolResults();
const textContent = this.extractTextContent(message.content);
if (textContent.trim().length > 0) {
anthropicMessages.push({
role: 'user',
content: textContent,
});
}
continue;
}
if (message.role === 'assistant') {
flushToolResults();
const text = this.extractTextContent(message.content);
const toolCalls = Array.isArray(message.tool_calls)
? message.tool_calls
: [];
if (toolCalls.length === 0) {
if (text.trim().length > 0) {
anthropicMessages.push({
role: 'assistant',
content: text,
});
}
continue;
}
const blocks = [];
if (text.trim()) {
blocks.push({ type: 'text', text });
}
for (const toolCall of toolCalls) {
const id = this.normalizeToAnthropicToolId(toolCall.id || (0, crypto_1.randomUUID)());
let input = {};
if (toolCall.function?.arguments) {
input = this.parseJson(toolCall.function.arguments);
}
blocks.push({
type: 'tool_use',
id,
name: toolCall.function?.name || 'tool',
input,
});
}
anthropicMessages.push({
role: 'assistant',
content: blocks,
});
continue;
}
if (message.role === 'tool') {
const toolResultId = this.normalizeToAnthropicToolId(message.tool_call_id || (0, crypto_1.randomUUID)());
const content = this.normalizeToolResultContent(message.content);
pendingToolResults.push({
type: 'tool_result',
tool_use_id: toolResultId,
content,
});
}
}
flushToolResults();
if (anthropicMessages.length === 0) {
anthropicMessages.push({ role: 'user', content: 'Hello' });
}
if (anthropicMessages[0]?.role !== 'user') {
anthropicMessages.unshift({ role: 'user', content: 'Continue' });
}
const system = systemPrompts.length > 0 ? systemPrompts.join('\n\n') : undefined;
return { messages: anthropicMessages, system };
}
transformTools(tools) {
if (!tools || tools.length === 0) {
return undefined;
}
return tools.map((tool) => {
const inputSchema = tool.function?.parameters &&
typeof tool.function.parameters === 'object'
? { ...tool.function.parameters }
: { type: 'object' };
if (!inputSchema.type) {
inputSchema.type = 'object';
}
return {
name: tool.function?.name || 'tool',
description: tool.function?.description,
input_schema: inputSchema,
};
});
}
extractTextContent(content) {
if (typeof content === 'string') {
return content;
}
if (!content) {
return '';
}
if (Array.isArray(content)) {
return content
.map((part) => this.extractTextFromPart(part))
.filter((value) => value.length > 0)
.join('');
}
const single = this.extractTextFromPart(content);
return single;
}
extractTextFromPart(part) {
if (!part) {
return '';
}
if (typeof part === 'string') {
return part;
}
if (typeof part !== 'object') {
return '';
}
const candidate = part;
if (typeof candidate.text === 'string') {
return candidate.text;
}
if (typeof candidate.value === 'string') {
return candidate.value;
}
if (typeof candidate.input_text === 'string') {
return candidate.input_text;
}
try {
return JSON.stringify(part);
}
catch {
return '';
}
}
transformToolChoice(toolChoice) {
if (!toolChoice || toolChoice === 'auto') {
return undefined;
}
if (toolChoice === 'none') {
return { type: 'none' };
}
if (typeof toolChoice === 'object' &&
toolChoice.type === 'function' &&
toolChoice.function?.name) {
return {
type: 'tool',
name: toolChoice.function.name,
};
}
return undefined;
}
buildHeaders(stream, config) {
const headers = {
'content-type': 'application/json',
accept: stream ? 'text/event-stream' : 'application/json',
'anthropic-version': config.anthropicVersion,
'x-api-key': config.apiKey,
'user-agent': config.userAgent,
'x-app': config.xApp,
'anthropic-dangerous-direct-browser-access': config.dangerousDirectBrowserAccess ? 'true' : 'false',
'x-stainless-lang': 'js',
'x-stainless-package-version': '0.60.0',
'x-stainless-runtime': 'node',
'x-stainless-runtime-version': process.version,
'x-stainless-os': this.normalizeOS(process.platform),
'x-stainless-arch': process.arch,
'x-stainless-timeout': String(Math.ceil((config.timeout ?? 1800000) / 1000)),
'x-stainless-retry-count': '0',
};
if (stream) {
headers['x-stainless-helper-method'] = 'stream';
}
if (config.beta && config.beta.length > 0) {
headers['anthropic-beta'] = config.beta.join(',');
}
if (config.extraHeaders) {
for (const [key, value] of Object.entries(config.extraHeaders)) {
if (value !== undefined && value !== null) {
headers[key] = value;
}
}
}
return headers;
}
sanitizeHeaders(headers) {
const sanitized = { ...headers };
const sensitiveKeys = ['x-api-key', 'authorization'];
for (const key of sensitiveKeys) {
if (sanitized[key]) {
sanitized[key] = '***';
}
}
return sanitized;
}
async sendClaudeMessagesRequest(payload, config, stream) {
const url = this.buildUrl(config.baseURL, 'v1/messages');
const headers = this.buildHeaders(stream, config);
const sanitizedHeaders = this.sanitizeHeaders(headers);
try {
this.logger.verbose(`ClaudeCodeProvider -> 请求: ${url.toString()} ${JSON.stringify(payload)}`);
this.logger.verbose(`ClaudeCodeProvider -> 请求头: ${JSON.stringify(sanitizedHeaders)}`);
}
catch (error) {
this.logger.warn(`ClaudeCodeProvider -> 请求日志序列化失败: ${error.message}`);
}
const controller = new AbortController();
const timeoutHandle = setTimeout(() => controller.abort(), config.timeout);
try {
const response = await fetch(url.toString(), {
method: 'POST',
headers,
body: JSON.stringify(payload),
signal: controller.signal,
});
try {
const responseHeaders = Object.fromEntries(response.headers.entries());
this.logger.verbose(`ClaudeCodeProvider -> 响应状态: ${response.status} ${response.statusText}`);
this.logger.verbose(`ClaudeCodeProvider -> 响应头: ${JSON.stringify(responseHeaders)}`);
if (!stream) {
const cloned = response.clone();
const bodyText = await cloned.text();
this.logger.verbose(`ClaudeCodeProvider -> 响应报文: ${bodyText}`);
}
}
catch (error) {
this.logger.warn(`ClaudeCodeProvider -> 响应日志处理失败: ${error.message}`);
}
return response;
}
finally {
clearTimeout(timeoutHandle);
}
}
async handleErrorResponse(response) {
const status = response.status;
let body = '';
try {
body = await response.text();
}
catch {
body = '';
}
try {
const parsed = body ? JSON.parse(body) : undefined;
if (parsed?.error?.message) {
throw new Error(parsed.error.message);
}
}
catch {
}
throw new ClaudeRequestError(status, body || response.statusText);
}
convertClaudeResponseToOpenAI(message, model) {
const toolCalls = [];
const textParts = [];
for (const block of message.content) {
if (block.type === 'text') {
textParts.push(block.text);
}
else if (block.type === 'tool_use') {
const argumentsString = JSON.stringify(block.input ?? {});
toolCalls.push({
id: this.normalizeToOpenAIToolId(block.id),
type: 'function',
function: {
name: block.name,
arguments: argumentsString,
},
});
}
}
const responseMessage = {
role: 'assistant',
content: textParts.length > 0 ? textParts.join('') : null,
};
if (toolCalls.length > 0) {
responseMessage.tool_calls = toolCalls;
}
const finishReason = this.mapStopReason(message.stop_reason, toolCalls.length > 0);
const usage = message.usage
? {
prompt_tokens: message.usage.input_tokens ?? 0,
completion_tokens: message.usage.output_tokens ?? 0,
total_tokens: message.usage.total_tokens ??
(message.usage.input_tokens ?? 0) +
(message.usage.output_tokens ?? 0),
}
: undefined;
return {
id: message.id || (0, crypto_1.randomUUID)(),
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model,
choices: [
{
index: 0,
message: responseMessage,
finish_reason: finishReason,
},
],
usage,
};
}
computePromptTokens(request, model) {
let totalTokens = this.tokenizerService.countTokensInRequest(request.contents || [], model);
const systemInstruction = request.systemInstruction;
if (typeof systemInstruction === 'string') {
totalTokens += this.tokenizerService.countTokens(systemInstruction, model);
}
else if (systemInstruction?.parts) {
totalTokens += this.tokenizerService.countTokensInRequest([systemInstruction], model);
}
return totalTokens;
}
parseJson(value) {
try {
return JSON.parse(value);
}
catch {
return value;
}
}
normalizeToolResultContent(content) {
if (typeof content !== 'string') {
try {
return JSON.stringify(content ?? {});
}
catch {
return '';
}
}
const trimmed = content.trim();
if (!trimmed) {
return '';
}
try {
const parsed = JSON.parse(trimmed);
return typeof parsed === 'string' ? parsed : JSON.stringify(parsed);
}
catch {
return trimmed;
}
}
normalizeToAnthropicToolId(id) {
if (id.startsWith('toolu_')) {
return id;
}
if (id.startsWith('hist_tool_')) {
return 'toolu_' + id.slice('hist_tool_'.length);
}
if (id.startsWith('call_')) {
return 'toolu_' + id.slice('call_'.length);
}
return id.startsWith('toolu_') ? id : `toolu_${id}`;
}
normalizeToOpenAIToolId(id) {
if (id.startsWith('call_')) {
return id;
}
if (id.startsWith('toolu_')) {
return 'call_' + id.slice('toolu_'.length);
}
if (id.startsWith('hist_tool_')) {
return 'call_' + id.slice('hist_tool_'.length);
}
return `call_${id}`;
}
mapStopReason(reason, hasToolCalls) {
if (!reason) {
return hasToolCalls ? 'tool_calls' : 'stop';
}
switch (reason) {
case 'max_tokens':
return 'length';
case 'tool_use':
return 'tool_calls';
case 'end_turn':
case 'stop_sequence':
return hasToolCalls ? 'tool_calls' : 'stop';
default:
return hasToolCalls ? 'tool_calls' : 'stop';
}
}
normalizeOS(platform) {
switch (platform) {
case 'win32':
return 'Windows';
case 'darwin':
return 'Darwin';
default:
return 'Linux';
}
}
async *parseClaudeStream(stream, context) {
let buffer = '';
for await (const chunk of stream) {
const chunkText = chunk.toString('utf8');
this.logger.verbose(`ClaudeCodeProvider -> 流式片段: ${chunkText.trim() || '[空片段]'}`);
buffer += chunkText;
buffer = buffer.replace(/\r\n/g, '\n');
let boundaryIndex = buffer.indexOf('\n\n');
while (boundaryIndex !== -1) {
const rawEvent = buffer.slice(0, boundaryIndex).trim();
buffer = buffer.slice(boundaryIndex + 2);
boundaryIndex = buffer.indexOf('\n\n');
if (!rawEvent) {
continue;
}
this.logger.verbose(`ClaudeCodeProvider -> SSE事件: ${rawEvent}`);
let eventName;
const dataLines = [];
for (const line of rawEvent.split('\n')) {
if (line.startsWith('event:')) {
eventName = line.slice(6).trim();
}
if (line.startsWith('data:')) {
dataLines.push(line.slice(5).trim());
}
}
if (dataLines.length === 0) {
continue;
}
const dataPayload = dataLines.join('');
if (dataPayload === '[DONE]') {
if (!context.finalChunkSent) {
context.finalChunkSent = true;
yield this.createFinalChunk(context);
}
return;
}
try {
const parsed = JSON.parse(dataPayload);
const chunks = this.handleStreamEvent(eventName || parsed.type, parsed, context);
if (Array.isArray(chunks)) {
for (const item of chunks) {
if (item) {
yield item;
}
}
}
else if (chunks) {
yield chunks;
}
}
catch (error) {
this.logger.warn(`Failed to parse Claude Code SSE chunk: ${error.message}`);
}
}
}
}
handleStreamEvent(eventType, payload, context) {
switch (eventType) {
case 'message_start': {
const message = payload.message;
context.responseId = message?.id || context.responseId || (0, crypto_1.randomUUID)();
context.model = message?.model || context.model;
context.created = Math.floor(Date.now() / 1000);
return undefined;
}
case 'content_block_start': {
const contentBlock = payload.content_block;
const index = payload.index ?? 0;
if (contentBlock?.type === 'tool_use') {
const anthropicId = contentBlock.id || (0, crypto_1.randomUUID)();
const state = {
index,
anthropicId,
openaiId: this.normalizeToOpenAIToolId(anthropicId),
name: contentBlock.name,
arguments: '',
};
context.toolCallStates.set(index, state);
}
return undefined;
}
case 'content_block_delta': {
const index = payload.index ?? 0;
const delta = payload.delta;
if (!delta) {
return undefined;
}
if (delta.type === 'text_delta' && typeof delta.text === 'string') {
return this.createTextChunk(delta.text, context);
}
if (delta.type === 'input_json_delta') {
const state = context.toolCallStates.get(index);
if (state && typeof delta.partial_json === 'string') {
state.arguments += delta.partial_json;
}
}
return undefined;
}
case 'content_block_stop': {
const index = payload.index ?? 0;
const state = context.toolCallStates.get(index);
if (state) {
const argumentsString = this.normalizeArguments(state.arguments);
const chunk = this.createToolCallChunk(state, argumentsString, context);
context.toolCallStates.delete(index);
return chunk;
}
return undefined;
}
case 'message_delta': {
const delta = payload.delta;
const usage = payload.usage;
if (usage) {
context.usage = usage;
}
if (delta?.stop_reason) {
context.finishReason = this.mapStopReason(delta.stop_reason, false);
if (!context.finalChunkSent) {
context.finalChunkSent = true;
return this.createFinalChunk(context);
}
}
return undefined;
}
case 'message_stop': {
if (!context.finalChunkSent) {
context.finalChunkSent = true;
return this.createFinalChunk(context);
}
return undefined;
}
default:
return undefined;
}
}
createTextChunk(text, context) {
const delta = {
content: text,
};
if (!context.started) {
delta.role = 'assistant';
context.started = true;
}
return {
id: context.responseId,
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model: context.model,
choices: [
{
index: 0,
delta,
},
],
};
}
createToolCallChunk(state, argumentsString, context) {
const delta = {
tool_calls: [
{
index: state.index,
id: state.openaiId,
type: 'function',
function: {
name: state.name || 'tool',
arguments: argumentsString,
},
},
],
};
if (!context.started) {
delta.role = 'assistant';
context.started = true;
}
context.finishReason = 'tool_calls';
return {
id: context.responseId,
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model: context.model,
choices: [
{
index: 0,
delta,
},
],
};
}
createFinalChunk(context) {
const usage = context.usage
? {
prompt_tokens: context.usage.input_tokens ?? 0,
completion_tokens: context.usage.output_tokens ?? 0,
total_tokens: context.usage.total_tokens ??
(context.usage.input_tokens ?? 0) +
(context.usage.output_tokens ?? 0),
}
: undefined;
return {
id: context.responseId,
object: 'chat.completion.chunk',
created: context.created,
model: context.model,
choices: [
{
index: 0,
delta: {},
finish_reason: context.finishReason || 'stop',
},
],
usage,
};
}
normalizeArguments(argumentsBuffer) {
const trimmed = argumentsBuffer.trim();
if (!trimmed) {
return '{}';
}
try {
const parsed = JSON.parse(trimmed);
return JSON.stringify(parsed);
}
catch {
return trimmed;
}
}
buildUrl(baseUrl, path) {
const normalizedBase = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
return new URL(path, normalizedBase);
}
};
exports.ClaudeCodeProvider = ClaudeCodeProvider;
exports.ClaudeCodeProvider = ClaudeCodeProvider = ClaudeCodeProvider_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [config_1.ConfigService,
request_transformer_1.RequestTransformer,
response_transformer_1.ResponseTransformer,
tokenizer_service_1.TokenizerService,
ToolCallProcessor_1.ToolCallProcessor])
], ClaudeCodeProvider);
//# sourceMappingURL=claude-code.provider.js.map