File size: 2,438 Bytes
0ed8124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b4f163f
0ed8124
 
 
b4f163f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ed8124
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { describe, expect, it } from 'vitest';
import { ToolCallAccumulator } from './tool-call-accumulator';

describe('ToolCallAccumulator', () => {
  it('assembles interleaved tool calls by index', () => {
    const accumulator = new ToolCallAccumulator();
    accumulator.append([
      { index: 1, id: 'call_2', type: 'function', function: { name: 'storage.', arguments: '{"scope":' } },
      { index: 0, id: 'call_1', type: 'function', function: { name: 'runtime.', arguments: '{"deep":' } },
    ]);
    accumulator.append([
      { index: 0, function: { name: 'inspect', arguments: 'true}' } },
      { index: 1, function: { name: 'estimate', arguments: '"model"}' } },
    ]);

    expect(accumulator.finish(true)).toEqual([
      {
        id: 'call_1',
        type: 'function',
        function: { name: 'runtime.inspect', arguments: '{"deep":true}' },
      },
      {
        id: 'call_2',
        type: 'function',
        function: { name: 'storage.estimate', arguments: '{"scope":"model"}' },
      },
    ]);
  });

  it('rejects incomplete or malformed structured calls', () => {
    const missing = new ToolCallAccumulator();
    missing.append([{ index: 1, id: 'call_2', function: { name: 'inspect', arguments: '{}' } }]);
    expect(() => missing.finish(true)).toThrow(/missing index 0/);

    const malformed = new ToolCallAccumulator();
    malformed.append([{ index: 0, id: 'call_1', function: { name: 'inspect', arguments: '{' } }]);
    expect(() => malformed.finish(true)).toThrow(/not valid JSON/);
  });

  it('reports live argument growth and safely drops an unfinished non-tool finish', () => {
    const accumulator = new ToolCallAccumulator();
    expect(accumulator.append([{
      index: 0,
      id: 'call_artifact',
      type: 'function',
      function: { name: 'html_artifact', arguments: '{"html":"<main>' },
    }])).toEqual([{
      index: 0,
      id: 'call_artifact',
      name: 'html_artifact',
      argumentCharacters: 15,
    }]);
    expect(accumulator.append([{
      index: 0,
      function: { arguments: '<h1>Draft</h1>' },
    }])).toEqual([{
      index: 0,
      id: 'call_artifact',
      name: 'html_artifact',
      argumentCharacters: 29,
    }]);
    expect(accumulator.finish(false)).toEqual([]);
  });

  it('requires a call when finish_reason is tool_calls', () => {
    expect(() => new ToolCallAccumulator().finish(true)).toThrow(/contained no tool calls/);
  });
});