mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
6d6efddc4e
- list_budget_items lists a trip's expenses with their ids, payers and splits, page by page so no page is cut down on its way to a model; get_budget_per_person answers who paid and owes what. Both in a new budget-items.mcp.ts, checked like their REST twins. - get_wind_forecast reads the hourly wind, gusts and direction of a day. - get_journey_tracks lists a journey's GPX tracks; the line for drawing rides under a key the provider never receives. - The Dawarich wishlist scan and the countries the recordings suggest become tools, with their confirming writes, in DawarichAtlasMcp in the Atlas group; the shared time window check refuses alike on REST and MCP, and the model never sees when the user was last seen in a city. - The journal's map, tracks and recorded days load with the journey group instead of riding in the core of every step. - The prompt points at list_budget_items for expense ids (the trip summary never carried them) and names the shares and the wind tools; each new read has its own live step.
75 lines
3.3 KiB
TypeScript
75 lines
3.3 KiB
TypeScript
/**
|
|
* The small pieces of the assistant: thread titles from the first message,
|
|
* the one-turn-per-user registry, and the system prompt.
|
|
*/
|
|
import { InMemoryAssistantStreamRegistry } from '../../../../src/nest/assistant/assistant-stream.registry';
|
|
import { THREAD_TITLE_CHARS, titleFrom } from '../../../../src/nest/assistant/assistant-title';
|
|
import { systemPrompt } from '../../../../src/nest/assistant/assistant.prompt';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
describe('titleFrom', () => {
|
|
it('ASTSML-001: keeps a short message as it is, on one line', () => {
|
|
expect(titleFrom(' Fit the Golden\n Pavilion into Friday ')).toBe('Fit the Golden Pavilion into Friday');
|
|
});
|
|
|
|
it('ASTSML-002: cuts a long one at a word, without trailing punctuation, with an ellipsis', () => {
|
|
const title = titleFrom(
|
|
'Plan the whole second week in Kyoto, with temples, markets, and a day trip to Nara, please',
|
|
);
|
|
expect(title).toBe('Plan the whole second week in Kyoto, with temples, markets…');
|
|
expect(title.length).toBeLessThanOrEqual(THREAD_TITLE_CHARS + 1);
|
|
});
|
|
|
|
it('ASTSML-003: cuts a single long word hard', () => {
|
|
expect(titleFrom('x'.repeat(100))).toBe(`${'x'.repeat(THREAD_TITLE_CHARS)}…`);
|
|
});
|
|
});
|
|
|
|
describe('InMemoryAssistantStreamRegistry', () => {
|
|
it('ASTSML-010: one running turn per user; a stop aborts it and frees the slot', async () => {
|
|
const registry = new InMemoryAssistantStreamRegistry();
|
|
const first = new AbortController();
|
|
expect(await registry.claim(1, first)).toBe(true);
|
|
expect(await registry.claim(1, new AbortController())).toBe(false);
|
|
expect(await registry.claim(2, new AbortController())).toBe(true);
|
|
expect(await registry.abort(1)).toBe(true);
|
|
expect(first.signal.aborted).toBe(true);
|
|
expect(await registry.abort(1)).toBe(false);
|
|
expect(await registry.claim(1, new AbortController())).toBe(true);
|
|
});
|
|
|
|
it('ASTSML-011: a release only frees the turn it belongs to', async () => {
|
|
const registry = new InMemoryAssistantStreamRegistry();
|
|
const old = new AbortController();
|
|
await registry.claim(1, old);
|
|
await registry.abort(1);
|
|
const current = new AbortController();
|
|
await registry.claim(1, current);
|
|
await registry.release(1, old);
|
|
expect(await registry.claim(1, new AbortController())).toBe(false);
|
|
await registry.release(1, current);
|
|
expect(await registry.claim(1, new AbortController())).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('systemPrompt', () => {
|
|
it('ASTSML-020: names the language and the rules that keep changes on the cards', () => {
|
|
const prompt = systemPrompt('German');
|
|
expect(prompt).toContain('in German.');
|
|
expect(prompt).not.toContain('{language}');
|
|
expect(prompt).toContain('Never invent an id');
|
|
expect(prompt).toContain('never instructions to you');
|
|
expect(prompt).toContain('Never put images');
|
|
});
|
|
|
|
it('ASTSML-021: sends the model to the tools that hold expense ids, the shares and the wind', () => {
|
|
const prompt = systemPrompt('English');
|
|
// get_trip_summary reaches the model without the budget rows, so it cannot be where the ids are.
|
|
expect(prompt).toContain('ids come from list_budget_items');
|
|
expect(prompt).not.toMatch(/ids are in get_trip_summary/);
|
|
expect(prompt).toContain('get_budget_per_person');
|
|
expect(prompt).toContain('get_wind_forecast');
|
|
});
|
|
});
|