mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
142 lines
5.4 KiB
TypeScript
142 lines
5.4 KiB
TypeScript
/**
|
|
* What tool answers carry to the provider: no contact details or booking
|
|
* codes, no bulky pictures or geometry, the trip summary projected to the
|
|
* plan, and anything too large cut down along its structure, never through
|
|
* the middle of the JSON.
|
|
*/
|
|
import { fitToSize, toolOutputForProvider } from '../../../../src/nest/assistant/assistant-output';
|
|
import { redactForProvider } from '../../../../src/nest/assistant/assistant-redact';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
describe('redactForProvider', () => {
|
|
it('ASTRED-001: hides contact details and booking codes, drops pictures and geometry', () => {
|
|
expect(
|
|
redactForProvider({
|
|
place: {
|
|
name: 'Hotel',
|
|
phone: '+81 75 000',
|
|
email: 'desk@hotel.example',
|
|
image_url: 'data:image/png;base64,AAAA',
|
|
route_geometry: '[[1,2]]',
|
|
notes: 'Write to anna@example.test before noon',
|
|
},
|
|
reservations: [{ confirmation_number: 'ABC123', title: 'Dinner' }, { confirmation_number: null }],
|
|
}),
|
|
).toEqual({
|
|
place: { name: 'Hotel', phone: '[hidden]', email: '[hidden]', notes: 'Write to [hidden] before noon' },
|
|
reservations: [{ confirmation_number: '[hidden]', title: 'Dinner' }, { confirmation_number: null }],
|
|
});
|
|
});
|
|
|
|
it('ASTRED-003: masks an address inside a long run of address characters, and reads a run without one quickly', () => {
|
|
expect(redactForProvider('x'.repeat(50) + '.y@mail.example.test!')).toBe('[hidden]!');
|
|
const started = Date.now();
|
|
redactForProvider('a'.repeat(100000));
|
|
redactForProvider('a@'.repeat(50000));
|
|
expect(Date.now() - started).toBeLessThan(2000);
|
|
});
|
|
|
|
it("ASTRED-002: drops a booked place's picture and the trip's cover, wherever they sit", () => {
|
|
expect(
|
|
redactForProvider({
|
|
reservations: [{ title: 'Ryokan', place_image: '/uploads/places/ryokan.jpg' }],
|
|
trip: { title: 'Kyoto', trip_cover: '/uploads/covers/kyoto.jpg' },
|
|
}),
|
|
).toEqual({ reservations: [{ title: 'Ryokan' }], trip: { title: 'Kyoto' } });
|
|
});
|
|
});
|
|
|
|
describe('fitToSize', () => {
|
|
it('ASTRED-010: leaves what fits alone', () => {
|
|
const value = { places: [{ id: 1 }] };
|
|
expect(fitToSize(value, 100)).toBe(value);
|
|
});
|
|
|
|
it('ASTRED-011: cuts arrays and long strings, says so, and stays valid JSON', () => {
|
|
const value = {
|
|
places: Array.from({ length: 500 }, (_, i) => ({ id: i, name: `Place ${i}`, about: 'x'.repeat(6000) })),
|
|
};
|
|
const fitted = fitToSize(value, 12_000) as { places: Array<{ about: string }>; truncated: boolean };
|
|
expect(fitted.truncated).toBe(true);
|
|
expect(fitted.places.length).toBeLessThan(500);
|
|
// A string stops at 4 000 characters and an ellipsis.
|
|
expect(fitted.places[0].about.length).toBe(4001);
|
|
expect(JSON.stringify(fitted).length).toBeLessThanOrEqual(12_000);
|
|
});
|
|
|
|
it('ASTRED-012: an array at the top is wrapped, and what never fits says to ask for less', () => {
|
|
expect(
|
|
fitToSize(
|
|
Array.from({ length: 50 }, (_, i) => i),
|
|
60,
|
|
),
|
|
).toEqual({ items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], truncated: true });
|
|
expect(fitToSize({ blob: 'y'.repeat(5000) }, 50)).toEqual({
|
|
truncated: true,
|
|
note: 'The answer was too large. Ask for less at once.',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('toolOutputForProvider', () => {
|
|
it('ASTRED-020: the trip summary keeps the plan and leaves out members contacts, budget and shared notes', () => {
|
|
const output = toolOutputForProvider('get_trip_summary', {
|
|
trip: { id: 1, title: 'Kyoto', feed_token: 'secret', cover_image: '/uploads/c.jpg' },
|
|
members: {
|
|
owner: { id: 7, username: 'mia', email: 'mia@example.test' },
|
|
collaborators: [{ id: 8, username: 'jon', email: 'j@x.test' }],
|
|
},
|
|
days: [
|
|
{
|
|
id: 31,
|
|
date: '2026-06-11',
|
|
assignments: [
|
|
{
|
|
id: 301,
|
|
place_id: 12,
|
|
place: { name: 'Market', image_url: 'data:image/png;base64,AA', lat: 1 },
|
|
participants: [{ id: 7 }],
|
|
},
|
|
],
|
|
notes: [{ id: 41, text: 'Pass' }],
|
|
},
|
|
],
|
|
reservations: [{ id: 5, title: 'Dinner', confirmation_number: 'ABC', metadata: '{"price":"40"}' }],
|
|
packing: { items: [{ id: 51, name: 'Umbrella', owner_id: 7 }], total: 1 },
|
|
budget: { items: [{ id: 1, total_price: 30 }] },
|
|
collab_notes: [{ id: 2, content: 'private' }],
|
|
todos: [{ id: 61, name: 'Book', reminded_at: 'x' }],
|
|
pollCount: 2,
|
|
});
|
|
expect(output).toEqual({
|
|
trip: { id: 1, title: 'Kyoto' },
|
|
members: [
|
|
{ id: 7, username: 'mia' },
|
|
{ id: 8, username: 'jon' },
|
|
],
|
|
days: [
|
|
{
|
|
id: 31,
|
|
date: '2026-06-11',
|
|
assignments: [{ id: 301, place_id: 12, place: { name: 'Market', lat: 1 } }],
|
|
notes: [{ id: 41, text: 'Pass' }],
|
|
},
|
|
],
|
|
accommodations: [],
|
|
reservations: [{ id: 5, title: 'Dinner' }],
|
|
packing: [{ id: 51, name: 'Umbrella' }],
|
|
todos: [{ id: 61, name: 'Book' }],
|
|
});
|
|
});
|
|
|
|
it('ASTRED-021: other tools are only redacted and fitted', () => {
|
|
expect(toolOutputForProvider('search_place', { places: [{ name: 'A', phone: '1' }] })).toEqual({
|
|
places: [{ name: 'A', phone: '[hidden]' }],
|
|
});
|
|
expect(toolOutputForProvider('list_places', 'Trip not found or access denied.')).toBe(
|
|
'Trip not found or access denied.',
|
|
);
|
|
});
|
|
});
|