Files
NetMesh/infrastructure/ai/providerContinuation.test.ts

429 lines
13 KiB
TypeScript
Raw Permalink Normal View History

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyOpenAIChatContinuationToBody,
extractProviderContinuationFromRawChunk,
getOpenAIChatAssistantFieldsForHistoryMessage,
isProviderContinuationForSource,
mergeProviderContinuation,
normalizeProviderContinuationOptions,
rawOpenAIChatChunkHasToolCalls,
repairOpenAIChatToolResultPairsInBody,
withProviderContinuationSource,
} from './providerContinuation';
test('extracts OpenAI-compatible reasoning deltas from raw provider chunks', () => {
const first = extractProviderContinuationFromRawChunk({
choices: [
{
delta: {
reasoning_content: 'check ',
},
},
],
});
const second = extractProviderContinuationFromRawChunk({
choices: [
{
delta: {
reasoning_content: 'tools',
},
},
],
});
const merged = mergeProviderContinuation(first, second);
assert.equal(merged?.openAIChatAssistantFields?.reasoning_content, 'check tools');
assert.deepEqual(merged?.reasoningParts, [{ text: 'check tools' }]);
});
test('patches OpenAI-compatible assistant tool-call messages with saved continuation fields', () => {
const body = JSON.stringify({
model: 'deepseek-v4-flash',
stream: true,
messages: [
{ role: 'system', content: 'system' },
{ role: 'user', content: 'inspect the host' },
{
role: 'assistant',
content: '',
tool_calls: [
{
id: 'call_1',
type: 'function',
function: { name: 'run_command', arguments: '{}' },
},
],
},
{ role: 'tool', tool_call_id: 'call_1', content: '{"ok":true}' },
],
});
const patched = JSON.parse(
applyOpenAIChatContinuationToBody(body, [
{ reasoning_content: 'need shell context' },
]),
);
assert.equal(patched.messages[2].reasoning_content, 'need shell context');
});
test('patches the final assistant message after a tool result with saved continuation fields', () => {
const body = JSON.stringify({
model: 'deepseek-v4-flash',
stream: true,
messages: [
{ role: 'user', content: 'inspect the host' },
{
role: 'assistant',
content: '',
tool_calls: [
{
id: 'call_1',
type: 'function',
function: { name: 'run_command', arguments: '{}' },
},
],
},
{ role: 'tool', tool_call_id: 'call_1', content: '{"ok":true}' },
{ role: 'assistant', content: 'host is healthy' },
{ role: 'user', content: 'continue' },
],
});
const patched = JSON.parse(
applyOpenAIChatContinuationToBody(body, [
{ reasoning_content: 'need shell context' },
{ reasoning_content: 'summarize result' },
]),
);
assert.equal(patched.messages[1].reasoning_content, 'need shell context');
assert.equal(patched.messages[3].reasoning_content, 'summarize result');
});
test('rebuilds OpenAI-compatible continuation fields from saved thinking for legacy history', () => {
const source = { providerConfigId: 'deepseek-custom', providerType: 'custom', modelId: 'deepseek-v4-flash' };
assert.deepEqual(
getOpenAIChatAssistantFieldsForHistoryMessage(
{
thinking: 'legacy visible reasoning',
providerId: 'custom',
model: 'deepseek-v4-flash',
},
source,
),
{ reasoning_content: 'legacy visible reasoning' },
);
});
test('does not rebuild continuation fields from thinking when provider or model differs', () => {
const source = { providerConfigId: 'deepseek-custom', providerType: 'custom', modelId: 'deepseek-v4-flash' };
assert.equal(
getOpenAIChatAssistantFieldsForHistoryMessage(
{
thinking: 'other provider reasoning',
providerId: 'openai',
model: 'deepseek-v4-flash',
},
source,
),
undefined,
);
assert.equal(
getOpenAIChatAssistantFieldsForHistoryMessage(
{
thinking: 'other model reasoning',
providerId: 'custom',
model: 'another-model',
},
source,
),
undefined,
);
assert.equal(
getOpenAIChatAssistantFieldsForHistoryMessage(
{
thinking: 'missing provider metadata',
model: 'deepseek-v4-flash',
},
source,
),
undefined,
);
assert.equal(
getOpenAIChatAssistantFieldsForHistoryMessage(
{
thinking: 'missing model metadata',
providerId: 'custom',
},
source,
),
undefined,
);
});
test('detects OpenAI-compatible tool calls in raw chunks', () => {
assert.equal(rawOpenAIChatChunkHasToolCalls({
choices: [
{
delta: {
tool_calls: [
{
id: 'call_1',
type: 'function',
function: { name: 'run_command', arguments: '{}' },
},
],
},
},
],
}), true);
assert.equal(rawOpenAIChatChunkHasToolCalls({
choices: [{ delta: { reasoning_content: 'think' } }],
}), false);
assert.equal(rawOpenAIChatChunkHasToolCalls('[DONE]'), false);
});
test('merges provider reasoning metadata into the reasoning part it belongs to', () => {
const merged = mergeProviderContinuation(
{ reasoningParts: [{ text: 'consider options' }] },
{ reasoningParts: [{ text: '', providerOptions: { anthropic: { signature: 'sig-1' } } }] },
);
assert.deepEqual(merged?.reasoningParts, [
{
text: 'consider options',
providerOptions: { anthropic: { signature: 'sig-1' } },
},
]);
});
test('normalizes provider metadata without unsafe object keys', () => {
const unsafeMetadata = JSON.parse('{"google":{"thoughtSignature":"sig-1","__proto__":{"polluted":true},"nested":{"constructor":{"bad":true},"value":"safe"}},"__proto__":{"ignored":true}}');
const normalized = normalizeProviderContinuationOptions(unsafeMetadata);
assert.deepEqual(normalized, {
google: {
thoughtSignature: 'sig-1',
nested: { value: 'safe' },
},
});
assert.equal(Object.prototype.hasOwnProperty.call(normalized?.google ?? {}, '__proto__'), false);
});
test('merges equivalent provider options without depending on key order', () => {
const merged = mergeProviderContinuation(
{ reasoningParts: [{ text: 'one ', providerOptions: { google: { b: 2, a: 1 } } }] },
{ reasoningParts: [{ text: 'two', providerOptions: { google: { a: 1, b: 2 } } }] },
);
assert.deepEqual(merged?.reasoningParts, [
{
text: 'one two',
providerOptions: { google: { b: 2, a: 1 } },
},
]);
});
test('cleans nested unsafe provider option keys when merging saved data', () => {
const unsafeOptions = JSON.parse('{"google":{"nested":{"prototype":{"bad":true},"value":"safe"}}}');
const merged = mergeProviderContinuation(
{ reasoningParts: [{ text: 'one ', providerOptions: unsafeOptions }] },
{ reasoningParts: [{ text: 'two' }] },
);
assert.deepEqual(merged?.reasoningParts, [
{
text: 'one ',
providerOptions: { google: { nested: { value: 'safe' } } },
},
{ text: 'two' },
]);
});
test('tracks continuation source so provider switches do not replay hidden context', () => {
const source = { providerConfigId: 'deepseek-custom', providerType: 'custom', modelId: 'deepseek-v4-flash' };
const continuation = withProviderContinuationSource(
{ openAIChatAssistantFields: { reasoning_content: 'think' } },
source,
);
assert.equal(isProviderContinuationForSource(continuation, source), true);
assert.equal(
isProviderContinuationForSource(continuation, {
providerConfigId: 'openai',
providerType: 'openai',
modelId: 'gpt-5',
}),
false,
);
});
test('drops old hidden context instead of relabeling it when sources differ', () => {
const deepseek = { providerConfigId: 'deepseek-custom', providerType: 'custom', modelId: 'deepseek-v4-flash' };
const openai = { providerConfigId: 'openai', providerType: 'openai', modelId: 'gpt-5' };
const merged = mergeProviderContinuation(
{ source: deepseek, openAIChatAssistantFields: { reasoning_content: 'old' } },
{ source: openai, reasoningParts: [{ text: 'new' }] },
);
assert.deepEqual(merged, {
source: openai,
reasoningParts: [{ text: 'new' }],
});
});
test('merges tool-call provider options by tool call id', () => {
const merged = mergeProviderContinuation(
{ toolCallProviderOptionsById: { call_1: { google: { thoughtSignature: 'sig-1' } } } },
{ toolCallProviderOptionsById: { call_1: { google: { extra: true } } } },
);
assert.deepEqual(merged?.toolCallProviderOptionsById, {
call_1: {
google: {
thoughtSignature: 'sig-1',
extra: true,
},
},
});
});
test('skips plain assistant messages that are not part of a tool loop', () => {
const body = JSON.stringify({
stream: true,
messages: [
{ role: 'assistant', content: 'plain answer' },
{
role: 'assistant',
content: '',
tool_calls: [
{
id: 'call_1',
type: 'function',
function: { name: 'run_command', arguments: '{}' },
},
],
},
],
});
const patched = JSON.parse(
applyOpenAIChatContinuationToBody(body, [
{ reasoning_content: 'tool reasoning' },
]),
);
assert.equal(patched.messages[0].reasoning_content, undefined);
assert.equal(patched.messages[1].reasoning_content, 'tool reasoning');
});
test('keeps assistant tool-call continuation fields aligned with message order', () => {
const toolCall = (id: string) => ({
id,
type: 'function',
function: { name: 'run_command', arguments: '{}' },
});
const body = JSON.stringify({
stream: true,
messages: [
{ role: 'assistant', content: '', tool_calls: [toolCall('call_1')] },
{ role: 'tool', tool_call_id: 'call_1', content: '{"ok":true}' },
{ role: 'assistant', content: '', tool_calls: [toolCall('call_2')] },
],
});
const patched = JSON.parse(
applyOpenAIChatContinuationToBody(body, [
undefined,
{ reasoning_content: 'second reasoning' },
]),
);
assert.equal(patched.messages[0].reasoning_content, undefined);
assert.equal(patched.messages[2].reasoning_content, 'second reasoning');
});
test('leaves invalid or unchanged OpenAI-compatible request bodies alone', () => {
assert.equal(applyOpenAIChatContinuationToBody('{', []), '{');
const body = JSON.stringify({ stream: true, messages: [{ role: 'user', content: 'hi' }] });
assert.equal(applyOpenAIChatContinuationToBody(body, [{ reasoning_content: 'unused' }]), body);
});
test('leaves complete OpenAI-compatible tool result pairs unchanged', () => {
const body = JSON.stringify({
stream: true,
messages: [
{
role: 'assistant',
content: '',
tool_calls: [
{ id: 'call_1', type: 'function', function: { name: 'terminal_execute', arguments: '{}' } },
{ id: 'call_2', type: 'function', function: { name: 'terminal_execute', arguments: '{}' } },
],
},
{ role: 'tool', tool_call_id: 'call_1', content: 'one' },
{ role: 'tool', tool_call_id: 'call_2', content: 'two' },
{ role: 'user', content: 'continue' },
],
});
assert.equal(repairOpenAIChatToolResultPairsInBody(body), body);
});
test('repairs partial OpenAI-compatible tool result pairs before sending history', () => {
const body = JSON.stringify({
stream: true,
messages: [
{
role: 'assistant',
content: '',
tool_calls: [
{ id: 'call_1', type: 'function', function: { name: 'terminal_execute', arguments: '{}' } },
{ id: 'call_2', type: 'function', function: { name: 'terminal_execute', arguments: '{}' } },
],
},
{ role: 'tool', tool_call_id: 'call_2', content: 'two' },
{ role: 'user', content: 'continue' },
],
});
const repaired = JSON.parse(repairOpenAIChatToolResultPairsInBody(body));
assert.deepEqual(
repaired.messages[0].tool_calls.map((toolCall: { id: string }) => toolCall.id),
['call_2'],
);
assert.equal(repaired.messages[1].role, 'tool');
assert.equal(repaired.messages[1].tool_call_id, 'call_2');
assert.equal(repaired.messages[2].role, 'user');
});
test('drops orphaned OpenAI-compatible tool calls that have no results', () => {
const body = JSON.stringify({
stream: true,
messages: [
{
role: 'assistant',
content: '',
tool_calls: [
{ id: 'call_1', type: 'function', function: { name: 'terminal_execute', arguments: '{}' } },
],
},
{ role: 'user', content: 'are you there?' },
],
});
const repaired = JSON.parse(repairOpenAIChatToolResultPairsInBody(body));
assert.deepEqual(repaired.messages, [
{ role: 'user', content: 'are you there?' },
]);
});