test: add 5 tests for thoughtSignature injection in OpenAI→Gemini path
This commit is contained in:
Generated
+18
-29
@@ -226,7 +226,6 @@
|
||||
"integrity": "sha512-Nt9hri7nbOo0RipAsGjIssHkpLMHHN/P7QqENywAq5TLsoYDzUyJGny8FEiD/9KJUxtGH8blGpMedilI6kK3rA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@algolia/client-common": "5.49.1",
|
||||
"@algolia/requester-browser-xhr": "5.49.1",
|
||||
@@ -379,7 +378,6 @@
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@@ -706,7 +704,6 @@
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -752,7 +749,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -1242,6 +1238,7 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cross-dirname": "^0.1.0",
|
||||
"debug": "^4.3.4",
|
||||
@@ -1263,6 +1260,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
@@ -1279,6 +1277,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
@@ -1293,6 +1292,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
@@ -4443,7 +4443,6 @@
|
||||
"integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@@ -4853,7 +4852,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
|
||||
"integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -4881,7 +4879,6 @@
|
||||
"integrity": "sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
@@ -4913,7 +4910,6 @@
|
||||
"integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.2.2"
|
||||
@@ -5640,7 +5636,6 @@
|
||||
"integrity": "sha512-X3Pp2aRQhg4xUC6PQtkubn5NpRKuUPQ9FPDQlx36SmpFwwH2N0/tw4c+NXV3nw3PsgeUs+BuWGP0gjz3TvENLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@algolia/abtesting": "1.15.1",
|
||||
"@algolia/client-abtesting": "5.49.1",
|
||||
@@ -6075,7 +6070,6 @@
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
@@ -6130,7 +6124,6 @@
|
||||
"integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bindings": "^1.5.0",
|
||||
"prebuild-install": "^7.1.1"
|
||||
@@ -6210,7 +6203,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -6614,7 +6606,6 @@
|
||||
"integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@chevrotain/cst-dts-gen": "11.1.2",
|
||||
"@chevrotain/gast": "11.1.2",
|
||||
@@ -6970,7 +6961,8 @@
|
||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
@@ -7019,7 +7011,6 @@
|
||||
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
@@ -7453,7 +7444,6 @@
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -7888,7 +7878,6 @@
|
||||
"integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.8.1",
|
||||
"builder-util": "26.8.1",
|
||||
@@ -8484,6 +8473,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/asar": "^3.2.1",
|
||||
"debug": "^4.1.1",
|
||||
@@ -8504,6 +8494,7 @@
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -8693,7 +8684,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -9084,7 +9074,6 @@
|
||||
"integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tabbable": "^6.4.0"
|
||||
}
|
||||
@@ -11078,6 +11067,7 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -11122,7 +11112,6 @@
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.18.2.tgz",
|
||||
"integrity": "sha512-UfEShBFAZZEAKjySnTUuE7BgqkYT4mx+RjoJ5aqtmwSSvNcJ/QxQPXz/y3jSxNiVRedPfgccmuBtiPCSiEEytw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.2",
|
||||
"denque": "^2.1.0",
|
||||
@@ -11665,7 +11654,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
|
||||
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.12.0",
|
||||
"pg-pool": "^3.13.0",
|
||||
@@ -11763,7 +11751,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -11944,6 +11931,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"commander": "^9.4.0"
|
||||
},
|
||||
@@ -11961,6 +11949,7 @@
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
@@ -12148,7 +12137,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -12161,7 +12149,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
@@ -12450,6 +12437,7 @@
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -12462,7 +12450,8 @@
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/rimraf/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
@@ -12470,6 +12459,7 @@
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -12482,6 +12472,7 @@
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -12503,6 +12494,7 @@
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -13484,6 +13476,7 @@
|
||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "~2.6.2"
|
||||
@@ -14249,7 +14242,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -14503,7 +14495,6 @@
|
||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
@@ -16645,7 +16636,6 @@
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
@@ -16761,7 +16751,6 @@
|
||||
"integrity": "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.29",
|
||||
"@vue/compiler-sfc": "3.5.29",
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildGeminiGenerateContentRequestFromOpenAi } from './geminiCliCompat.js';
|
||||
|
||||
describe('thoughtSignature injection in OpenAI→Gemini conversion', () => {
|
||||
it('injects thoughtSignature from provider_specific_fields into functionCall parts', () => {
|
||||
const result = buildGeminiGenerateContentRequestFromOpenAi({
|
||||
body: {
|
||||
model: 'gemini-3-flash-preview',
|
||||
messages: [
|
||||
{ role: 'user', content: 'What is the weather?' },
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'Let me check.',
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_123',
|
||||
type: 'function',
|
||||
function: { name: 'get_weather', arguments: '{"city":"Tokyo"}' },
|
||||
provider_specific_fields: { thought_signature: 'real_sig_abc' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: 'tool', tool_call_id: 'call_123', content: '{"temp":"22C"}' },
|
||||
],
|
||||
},
|
||||
modelName: 'gemini-3-flash-preview',
|
||||
}) as Record<string, unknown>;
|
||||
|
||||
const contents = result.contents as Array<Record<string, unknown>>;
|
||||
// Find the model message with functionCall
|
||||
const modelMsgs = contents.filter((c) => c.role === 'model');
|
||||
const fcParts = modelMsgs
|
||||
.flatMap((m) => (m.parts as Array<Record<string, unknown>>))
|
||||
.filter((p) => 'functionCall' in p);
|
||||
|
||||
expect(fcParts.length).toBe(1);
|
||||
expect(fcParts[0].thoughtSignature).toBe('real_sig_abc');
|
||||
});
|
||||
|
||||
it('splits text and signed functionCall parts into separate model messages', () => {
|
||||
const result = buildGeminiGenerateContentRequestFromOpenAi({
|
||||
body: {
|
||||
model: 'gemini-3-flash-preview',
|
||||
messages: [
|
||||
{ role: 'user', content: 'Read the file.' },
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'I will read it.',
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_456',
|
||||
type: 'function',
|
||||
function: { name: 'Read', arguments: '{"path":"/tmp/x"}' },
|
||||
provider_specific_fields: { thought_signature: 'sig_split_test' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: 'tool', tool_call_id: 'call_456', content: 'file content here' },
|
||||
],
|
||||
},
|
||||
modelName: 'gemini-3-flash-preview',
|
||||
}) as Record<string, unknown>;
|
||||
|
||||
const contents = result.contents as Array<Record<string, unknown>>;
|
||||
const modelMsgs = contents.filter((c) => c.role === 'model');
|
||||
|
||||
// Should be 2 model messages: text (unsigned) + functionCall (signed)
|
||||
expect(modelMsgs.length).toBe(2);
|
||||
|
||||
const firstParts = modelMsgs[0].parts as Array<Record<string, unknown>>;
|
||||
const secondParts = modelMsgs[1].parts as Array<Record<string, unknown>>;
|
||||
|
||||
// First: text only, no thoughtSignature
|
||||
expect(firstParts.every((p) => 'text' in p)).toBe(true);
|
||||
expect(firstParts.every((p) => !('thoughtSignature' in p))).toBe(true);
|
||||
|
||||
// Second: functionCall with thoughtSignature
|
||||
expect(secondParts.every((p) => 'functionCall' in p)).toBe(true);
|
||||
expect(secondParts[0].thoughtSignature).toBe('sig_split_test');
|
||||
});
|
||||
|
||||
it('injects dummy sentinel when thinking enabled but no signature available', () => {
|
||||
const result = buildGeminiGenerateContentRequestFromOpenAi({
|
||||
body: {
|
||||
model: 'gemini-3-flash-preview',
|
||||
reasoning_effort: 'high',
|
||||
messages: [
|
||||
{ role: 'user', content: 'Do something.' },
|
||||
{
|
||||
role: 'assistant',
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_no_sig',
|
||||
type: 'function',
|
||||
function: { name: 'Bash', arguments: '{"command":"ls"}' },
|
||||
// No provider_specific_fields — signature missing
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: 'tool', tool_call_id: 'call_no_sig', content: 'file1\nfile2' },
|
||||
],
|
||||
},
|
||||
modelName: 'gemini-3-flash-preview',
|
||||
}) as Record<string, unknown>;
|
||||
|
||||
const contents = result.contents as Array<Record<string, unknown>>;
|
||||
const modelMsgs = contents.filter((c) => c.role === 'model');
|
||||
const fcParts = modelMsgs
|
||||
.flatMap((m) => (m.parts as Array<Record<string, unknown>>))
|
||||
.filter((p) => 'functionCall' in p);
|
||||
|
||||
expect(fcParts.length).toBe(1);
|
||||
// Should have a dummy sentinel, not be missing
|
||||
expect(typeof fcParts[0].thoughtSignature).toBe('string');
|
||||
expect((fcParts[0].thoughtSignature as string).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('does not inject thoughtSignature when thinking is not enabled', () => {
|
||||
const result = buildGeminiGenerateContentRequestFromOpenAi({
|
||||
body: {
|
||||
model: 'gemini-2.5-flash',
|
||||
messages: [
|
||||
{ role: 'user', content: 'Hello' },
|
||||
{
|
||||
role: 'assistant',
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_no_think',
|
||||
type: 'function',
|
||||
function: { name: 'Read', arguments: '{"path":"/x"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: 'tool', tool_call_id: 'call_no_think', content: 'data' },
|
||||
],
|
||||
},
|
||||
modelName: 'gemini-2.5-flash',
|
||||
}) as Record<string, unknown>;
|
||||
|
||||
const contents = result.contents as Array<Record<string, unknown>>;
|
||||
const modelMsgs = contents.filter((c) => c.role === 'model');
|
||||
const fcParts = modelMsgs
|
||||
.flatMap((m) => (m.parts as Array<Record<string, unknown>>))
|
||||
.filter((p) => 'functionCall' in p);
|
||||
|
||||
expect(fcParts.length).toBe(1);
|
||||
// No thinking → no signature injected
|
||||
expect(fcParts[0].thoughtSignature).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves functionResponse count matching functionCall count', () => {
|
||||
const result = buildGeminiGenerateContentRequestFromOpenAi({
|
||||
body: {
|
||||
model: 'gemini-3-flash-preview',
|
||||
messages: [
|
||||
{ role: 'user', content: 'Read two files.' },
|
||||
{
|
||||
role: 'assistant',
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_a',
|
||||
type: 'function',
|
||||
function: { name: 'Read', arguments: '{"path":"/a"}' },
|
||||
provider_specific_fields: { thought_signature: 'sig_a' },
|
||||
},
|
||||
{
|
||||
id: 'call_b',
|
||||
type: 'function',
|
||||
function: { name: 'Read', arguments: '{"path":"/b"}' },
|
||||
provider_specific_fields: { thought_signature: 'sig_b' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ role: 'tool', tool_call_id: 'call_a', content: 'content a' },
|
||||
{ role: 'tool', tool_call_id: 'call_b', content: 'content b' },
|
||||
],
|
||||
},
|
||||
modelName: 'gemini-3-flash-preview',
|
||||
}) as Record<string, unknown>;
|
||||
|
||||
const contents = result.contents as Array<Record<string, unknown>>;
|
||||
|
||||
// Count functionCall and functionResponse parts
|
||||
let fcCount = 0;
|
||||
let frCount = 0;
|
||||
for (const content of contents) {
|
||||
for (const part of (content.parts as Array<Record<string, unknown>>)) {
|
||||
if ('functionCall' in part) fcCount++;
|
||||
if ('functionResponse' in part) frCount++;
|
||||
}
|
||||
}
|
||||
|
||||
expect(fcCount).toBe(2);
|
||||
expect(frCount).toBe(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user