Merge pull request #124 from lemon07r/main

refactor: directly import and use `setLanguage` from i18n module in l…
This commit is contained in:
何夕2077 2025-12-23 10:55:51 +08:00 committed by GitHub
commit db08148137
8 changed files with 105 additions and 7 deletions

5
.gitignore vendored
View file

@ -5,4 +5,7 @@ CLAUDE.md
config.json
provider_pools.json
fetch_system_prompt.txt
input_system_prompt.txt
input_system_prompt.txt
token-store.json
usage-cache.json
*_oauth_creds.json

0
install-and-run.bat Normal file → Executable file
View file

0
install-and-run.sh Normal file → Executable file
View file

View file

@ -1,7 +1,7 @@
export default {
testEnvironment: 'node',
transform: {
'^.+\\.js$': 'babel-jest',
'^.+\\.(js|mjs)$': 'babel-jest',
},
transformIgnorePatterns: [
'/node_modules/(?!(uuid)/)', // uuid is an ESM module that needs to be transformed
@ -15,8 +15,7 @@ export default {
'^(\\.{1,2}/.*)\\.js$': '$1'
},
testMatch: [
'**/tests/api-server.test.js',
'**/tests/api-integration.test.js'
'**/tests/**/*.test.js'
],
collectCoverageFrom: [
'src/**/*.js',

2
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "AIClient2API",
"name": "AIClient-2-API",
"lockfileVersion": 3,
"requires": true,
"packages": {

View file

@ -209,6 +209,7 @@ export class GeminiConverter extends BaseConverter {
}
if (part.functionCall) {
toolCalls.push({
index: toolCalls.length,
id: part.functionCall.id || `call_${uuidv4()}`,
type: 'function',
function: {
@ -231,6 +232,11 @@ export class GeminiConverter extends BaseConverter {
candidate.finishReason.toLowerCase();
}
// 如果包含工具调用,且完成原因为 stop则将完成原因修改为 tool_calls
if (toolCalls.length > 0 && finishReason === 'stop') {
finishReason = 'tool_calls';
}
// 构建delta对象
const delta = {};
if (content) delta.content = content;

View file

@ -219,7 +219,7 @@
</div>
<script type="module">
import { initI18n, t } from './app/i18n.js';
import { initI18n, t, setLanguage } from './app/i18n.js';
import { initLanguageSwitcher } from './app/language-switcher.js';
// 初始化多语言
@ -246,7 +246,7 @@
option.addEventListener('click', (e) => {
e.stopPropagation();
const lang = option.getAttribute('data-lang');
module.setLanguage(lang);
setLanguage(lang);
switcher.querySelector('.current-lang').textContent = lang === 'zh-CN' ? '中文' : 'EN';
languageOptions.forEach(opt => opt.classList.remove('active'));
option.classList.add('active');

View file

@ -0,0 +1,90 @@
import GeminiConverter from '../src/converters/strategies/GeminiConverter.js';
import { jest } from '@jest/globals';
describe('GeminiConverter', () => {
let converter;
beforeEach(() => {
converter = new GeminiConverter();
});
test('toOpenAIStreamChunk adds index to tool_calls', () => {
const geminiChunk = {
candidates: [{
content: {
parts: [{
functionCall: {
name: 'test_tool',
args: { param: 'value' }
}
}]
}
}]
};
const result = converter.toOpenAIStreamChunk(geminiChunk, 'gemini-pro');
expect(result).not.toBeNull();
expect(result.choices[0].delta).toHaveProperty('tool_calls');
expect(result.choices[0].delta.tool_calls).toHaveLength(1);
expect(result.choices[0].delta.tool_calls[0]).toHaveProperty('index');
expect(result.choices[0].delta.tool_calls[0].index).toBe(0);
expect(result.choices[0].delta.tool_calls[0].function.name).toBe('test_tool');
});
test('toOpenAIStreamChunk handles multiple tool_calls with correct indices', () => {
const geminiChunk = {
candidates: [{
content: {
parts: [
{
functionCall: {
name: 'tool_one',
args: {}
}
},
{
functionCall: {
name: 'tool_two',
args: {}
}
}
]
}
}]
};
const result = converter.toOpenAIStreamChunk(geminiChunk, 'gemini-pro');
expect(result).not.toBeNull();
expect(result.choices[0].delta.tool_calls).toHaveLength(2);
expect(result.choices[0].delta.tool_calls[0].index).toBe(0);
expect(result.choices[0].delta.tool_calls[0].function.name).toBe('tool_one');
expect(result.choices[0].delta.tool_calls[1].index).toBe(1);
expect(result.choices[0].delta.tool_calls[1].function.name).toBe('tool_two');
});
test('toOpenAIStreamChunk sets finish_reason to tool_calls when tool calls are present and finishReason is STOP', () => {
const geminiChunk = {
candidates: [{
finishReason: 'STOP',
content: {
parts: [{
functionCall: {
name: 'test_tool',
args: {}
}
}]
}
}]
};
const result = converter.toOpenAIStreamChunk(geminiChunk, 'gemini-pro');
expect(result).not.toBeNull();
expect(result.choices[0].finish_reason).toBe('tool_calls');
});
});