mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(ui): write relationship counts with their units
"Incoming relationships: 23 from 12 symbols" and "2 zu 2 Symbolen" read like a score. The summary lines now say "Incoming: 23 relationships from 12 symbols" and "Eingehend: 23 Beziehungen von 12 Symbolen", and the empty, not loaded and cut sides use the same labels. The prompt and the Source block head each edge type as "CALLS (11):" instead of "CALLS from 11:", which the model repeated as a sentence. The K7 hand test and the call feedback acceptance check expect the new wording. Signed-off-by: Bernhard Jackiewicz <bj@techport.io>
This commit is contained in:
1 parent
a1059d9dba
commit
9d6e69fd97
11 files changed
+84
-49
No files matched your search
@@ -914,7 +914,7 @@ describe('graph answers and answer limits', () => {
|
||||
expect(answer).toContain('TESTS (11):');
|
||||
expect(answer).toContain('DEFINES (1): general.py, the file that defines JSONBAgg');
|
||||
expect(answer).toContain('not generated by the model');
|
||||
expect(container.querySelector('.cbm-chat-answer .cbm-chat-source-content')?.textContent).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
expect(container.querySelector('.cbm-chat-answer .cbm-chat-source-content')?.textContent).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
expect([...container.querySelectorAll('button')].some(item => item.textContent === 'Retry')).toBe(false);
|
||||
expect(button('Ask the model')).toBeDefined();
|
||||
expect(container.querySelector('textarea')?.value).toBe('');
|
||||
@@ -1050,7 +1050,7 @@ describe('graph answers and answer limits', () => {
|
||||
const request = runtime.chat.mock.calls[0][0];
|
||||
expect(request.map(message => message.role)).toEqual(['system', 'user']);
|
||||
expect(request[1].content).toContain('Who calls JSONBAgg?');
|
||||
expect(request[1].content).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
expect(request[1].content).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
expect(container.querySelectorAll('.cbm-chat-turn')).toHaveLength(1);
|
||||
expect(container.querySelector('.cbm-chat-answer-text')?.textContent).toBe('Adds the two values.');
|
||||
expect(button('Retry')).toBeDefined();
|
||||
@@ -1227,7 +1227,7 @@ describe('per-selection explanation cache', () => {
|
||||
await settleSelection();
|
||||
expect(runtime.chat).toHaveBeenCalledOnce();
|
||||
// The complete scope's facts are listed in the card; the model reads the symbol's source (K7, K14).
|
||||
expect(card()).toContain('Incoming relationships: 23 from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(card()).toContain('Incoming: 23 relationships from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(runtime.chat.mock.calls[0][0].at(-1)!.content).toContain('class JSONBAgg(OrderableAggMixin, Aggregate):');
|
||||
await render({ ...props, proactive: true, proactiveSelection: jsonbAggEvidence({ state: 'partial' }) });
|
||||
await settleSelection(); expect(runtime.chat).toHaveBeenCalledOnce();
|
||||
@@ -1358,7 +1358,7 @@ describe('grounded automatic explanations (K14, K7)', () => {
|
||||
expect(prompt).toContain('```\nclass JSONBAgg(OrderableAggMixin, Aggregate):\n function = "JSONB_AGG"');
|
||||
expect(prompt).toContain('Describe this class in one short sentence that starts with `JSONBAgg`.');
|
||||
expect(prompt).not.toContain('Source unavailable');
|
||||
expect(card().textContent).toContain('Incoming relationships: 23 from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(card().textContent).toContain('Incoming: 23 relationships from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(card().textContent).toContain('JSONBAgg sets function to "JSONB_AGG" and allows distinct values.');
|
||||
expect(card().textContent).not.toContain('list of integers');
|
||||
expect(card().textContent).toContain('Facts listed from the indexed graph; the last sentence is generated by the model.');
|
||||
@@ -1439,7 +1439,7 @@ describe('grounded automatic explanations (K14, K7)', () => {
|
||||
file_path: `tests/postgres_tests/callers/test_module_number_${index}.py`, x: 0, y: 0, z: 0, size: 1, color: '#999' }));
|
||||
const evidence = jsonbAggEvidence({ edges: callers.map(caller => ({ source: caller.id, target: 32360, type: 'CALLS' })), nodes: callers });
|
||||
await render({ ...props, proactive: true, proactiveSelection: evidence, readSource: vi.fn(async () => snippet) }); await click('Download & load'); await settle();
|
||||
expect(card().textContent).toContain('Incoming relationships: 40 from 40 symbols (CALLS 40).');
|
||||
expect(card().textContent).toContain('Incoming: 40 relationships from 40 symbols (CALLS 40).');
|
||||
expect(card().textContent).not.toContain('too large for the local');
|
||||
});
|
||||
|
||||
@@ -1450,7 +1450,7 @@ describe('grounded automatic explanations (K14, K7)', () => {
|
||||
expect(readSource).toHaveBeenCalledOnce();
|
||||
expect(runtime.countTokens).not.toHaveBeenCalled();
|
||||
expect(runtime.chat).not.toHaveBeenCalled();
|
||||
expect(card().textContent).toContain('Incoming relationships: 23 from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(card().textContent).toContain('Incoming: 23 relationships from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(card().textContent).toContain('Listed from the indexed graph; not generated by the model.');
|
||||
});
|
||||
|
||||
@@ -1541,7 +1541,7 @@ describe('general questions, prompts without a question and configuration files
|
||||
expect(request.at(-1)!.content).toContain('Describe this class in one short sentence that starts with `JSONBAgg`. Answer in German.');
|
||||
expect(request.at(-1)!.content).toContain('function = "JSONB_AGG"');
|
||||
expect(answer()).toContain('Ausgewählt: JSONBAgg (Class) in django/contrib/postgres/aggregates/general.py:50-54.');
|
||||
expect(answer()).toContain('Eingehende Beziehungen: 23 aus 12 Symbolen (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(answer()).toContain('Eingehend: 23 Beziehungen von 12 Symbolen (CALLS 11, TESTS 11, DEFINES 1).');
|
||||
expect(answer()).toContain('Ausschnitt: 1 Schritt in beide Richtungen');
|
||||
expect(answer()).toContain('JSONBAgg setzt function auf "JSONB_AGG".');
|
||||
expect(answer()).not.toContain('Liste von');
|
||||
|
||||
@@ -52,3 +52,33 @@ describe('a scope that finished loading (W4)', () => {
|
||||
expect(listed).toContain('vollständig geladen.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relationship counts that read as counts, not as a score (W2)', () => {
|
||||
it('names the unit beside each number in the summary, in English and German', () => {
|
||||
expect(selectionSummary(jsonbAggEvidence()).slice(1, 3)).toEqual([
|
||||
'Incoming: 23 relationships from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Outgoing: 2 relationships to 2 symbols (INHERITS 2).',
|
||||
]);
|
||||
expect(selectionSummary(jsonbAggEvidence(), 'de').slice(1, 3)).toEqual([
|
||||
'Eingehend: 23 Beziehungen von 12 Symbolen (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Ausgehend: 2 Beziehungen zu 2 Symbolen (INHERITS 2).',
|
||||
]);
|
||||
});
|
||||
|
||||
it('heads each edge type of the prompt with its count in parentheses, so the model has no "CALLS from 11" to repeat', () => {
|
||||
const prompt = formatExplanationEvidence(prepareExplanationContext(undefined, jsonbAggEvidence(), 3200));
|
||||
expect(prompt).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
expect(prompt).toContain('Outgoing: 2 relationships to 2 symbols.');
|
||||
expect(prompt).toMatch(/^- CALLS \(11\): test_default_argument/m);
|
||||
expect(prompt).toMatch(/^- INHERITS \(2\): OrderableAggMixin/m);
|
||||
expect(prompt).not.toMatch(/\b(?:CALLS|TESTS|DEFINES|INHERITS) (?:from|to) \d/);
|
||||
});
|
||||
|
||||
it('uses the same words where a side has nothing, was not loaded or was cut', () => {
|
||||
expect(selectionSummary(jsonbAggEvidence({ direction: 'outbound' }))[1]).toBe('Incoming: not loaded; the scope does not follow incoming edges.');
|
||||
expect(selectionSummary(jsonbAggEvidence({ direction: 'outbound' }), 'de')[1]).toBe('Eingehend: nicht geladen; der Ausschnitt folgt keinen eingehenden Kanten.');
|
||||
const lonely = jsonbAggEvidence({ edges: [] });
|
||||
expect(selectionSummary(lonely).slice(1, 3)).toEqual(['Incoming: no relationships in this scope.', 'Outgoing: no relationships in this scope.']);
|
||||
expect(selectionSummary(lonely, 'de').slice(1, 3)).toEqual(['Eingehend: keine Beziehungen in diesem Ausschnitt.', 'Ausgehend: keine Beziehungen in diesem Ausschnitt.']);
|
||||
});
|
||||
});
|
||||
@@ -146,18 +146,18 @@ describe('bounded explanation evidence', () => {
|
||||
const text = outputText(prepared);
|
||||
expect(text).toContain('Selected: JSONBAgg (Class) in django/contrib/postgres/aggregates/general.py:50-54.');
|
||||
expect(text).toContain('Scope: 1 hop in both directions, all relationship types; 15 symbols and 25 relationships; fully loaded.');
|
||||
expect(text).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
expect(text).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
const line = (type: string) => text.split('\n').find(item => item.startsWith(`- ${type} `)) ?? '';
|
||||
for (const type of ['CALLS', 'TESTS']) {
|
||||
expect(line(type)).toMatch(new RegExp(`^- ${type} from 11: `));
|
||||
expect(line(type)).toMatch(new RegExp(`^- ${type} \\(11\\): `));
|
||||
for (const name of JSONB_AGG_CALLERS) expect(line(type)).toContain(name);
|
||||
expect(line(type)).toContain('(Method, tests/postgres_tests/test_aggregates.py)');
|
||||
}
|
||||
expect(line('DEFINES')).toBe('- DEFINES from 1: general.py (File, django/contrib/postgres/aggregates/general.py)');
|
||||
expect(text).toContain('Outgoing relationships: 2 to 2 symbols.');
|
||||
expect(line('INHERITS')).toMatch(/^- INHERITS to 2: OrderableAggMixin .*Aggregate/);
|
||||
expect(line('DEFINES')).toBe('- DEFINES (1): general.py (File, django/contrib/postgres/aggregates/general.py)');
|
||||
expect(text).toContain('Outgoing: 2 relationships to 2 symbols.');
|
||||
expect(line('INHERITS')).toMatch(/^- INHERITS \(2\): OrderableAggMixin .*Aggregate/);
|
||||
// Callers never appear under the outgoing side.
|
||||
expect(text.slice(text.indexOf('Outgoing relationships'))).not.toContain('test_jsonb_agg');
|
||||
expect(text.slice(text.indexOf('Outgoing:'))).not.toContain('test_jsonb_agg');
|
||||
expect(prepared.capacity).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -170,8 +170,8 @@ describe('bounded explanation evidence', () => {
|
||||
it('lists names until the budget ends, then says how many more there are and reports the capacity', () => {
|
||||
const prepared = prepareExplanationContext(undefined, jsonbAggEvidence(), 1900);
|
||||
const text = outputText(prepared);
|
||||
expect(text).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
const calls = text.split('\n').find(item => item.startsWith('- CALLS from 11: '))!;
|
||||
expect(text).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
const calls = text.split('\n').find(item => item.startsWith('- CALLS (11): '))!;
|
||||
const listed = JSONB_AGG_CALLERS.filter(name => calls.includes(`${name},`) || calls.includes(`${name} (`)).length;
|
||||
expect(listed).toBeGreaterThan(0);
|
||||
expect(calls).toMatch(new RegExp(`; \\+${11 - listed} more$`));
|
||||
@@ -186,14 +186,14 @@ describe('bounded explanation evidence', () => {
|
||||
const context = jsonbAggEvidence({ edges: [...scope.edges, ...many] });
|
||||
const text = outputText(prepareExplanationContext(undefined, context, 6000));
|
||||
// The extra callers are not in the node list, so they are named by id; the count stays exact.
|
||||
expect(text).toContain('- CALLS from 71: ');
|
||||
expect(text).toMatch(/- CALLS from 71: .*; \+47 more/);
|
||||
expect(text).toContain('- CALLS (71): ');
|
||||
expect(text).toMatch(/- CALLS \(71\): .*; \+47 more/);
|
||||
});
|
||||
|
||||
it('keeps a large documented scope readable and says when its snapshot cut relationships', () => {
|
||||
const whole = outputText(prepareExplanationContext(undefined, selectionEvidenceContext(largeFolderScope()), 6000));
|
||||
expect(whole).toContain('Incoming relationships: 150 from 150 symbols.');
|
||||
expect(whole).toContain('Outgoing relationships: 150 to 150 symbols.');
|
||||
expect(whole).toContain('Incoming: 150 relationships from 150 symbols.');
|
||||
expect(whole).toContain('Outgoing: 150 relationships to 150 symbols.');
|
||||
expect(whole).not.toMatch(/Snapshot\.|Selected\.|Relationships\.|incompl/);
|
||||
const evidence = largeFolderScope();
|
||||
const cut = selectionEvidenceContext({ ...evidence, selected: { ...evidence.selected as object, notes: Array.from({ length: 13 }, () => 'n'.repeat(1200)) } });
|
||||
@@ -204,7 +204,7 @@ describe('bounded explanation evidence', () => {
|
||||
|
||||
it('says when a side was not loaded instead of reporting no callers', () => {
|
||||
const text = outputText(prepareExplanationContext(undefined, jsonbAggEvidence({ direction: 'outbound' })));
|
||||
expect(text).toContain('Incoming relationships: not loaded; the scope does not follow incoming edges.');
|
||||
expect(text).toContain('Incoming: not loaded; the scope does not follow incoming edges.');
|
||||
expect(text).toContain('outgoing only');
|
||||
expect(outputText(prepareExplanationContext(undefined, jsonbAggEvidence({ state: 'loading-partial-preview' }))))
|
||||
.toContain('still loading, so this is a partial preview');
|
||||
|
||||
@@ -119,6 +119,6 @@ describe('evidence sections in the prompt', () => {
|
||||
expect(prompt).not.toMatch(/\[(?:graph|source)-\d+\]/);
|
||||
expect(prompt).not.toMatch(/\b(?:graph|source)[- ]\d+\b/i);
|
||||
expect(formatExplanationEvidence(packet)).toMatch(/^Source django\/contrib\/postgres\/aggregates\/general\.py:50-51, a Python source file:$/m);
|
||||
expect(formatExplanationEvidence(packet)).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
expect(formatExplanationEvidence(packet)).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ export const AUTO_OUTPUT_TOKENS = 128;
|
||||
export const CHAT_INPUT_TOKENS = 2048;
|
||||
|
||||
/** Code sections are headed by where they come from; graph facts already begin with
|
||||
* their own words ("Selected:", "Incoming relationships:"). Numbered ids such as
|
||||
* their own words ("Selected:", "Incoming:"). Numbered ids such as
|
||||
* "[graph-1]" stay out: a small model repeats them as "Graph 1" in its answer. */
|
||||
export function formatExplanationEvidence(packet: PreparedExplanationContext): string {
|
||||
const kind = (path: string) => { const name = fileKind(path); return name ? `, a ${name}` : ''; };
|
||||
|
||||
@@ -104,11 +104,13 @@ export function fairShares(natural: readonly number[], budget: number): number[]
|
||||
}
|
||||
|
||||
/** One edge type: complete count, then names until the budget, then an explicit "+N more".
|
||||
* A listed answer heads it "**TESTS (11):**", the prompt "TESTS from 11:" (C2). */
|
||||
export function relationshipLine(group: RelationshipGroup, side: 'incoming' | 'outgoing', budget: number,
|
||||
* A listed answer heads it "**TESTS (11):**", the prompt "TESTS (11):"; "CALLS from 11" was
|
||||
* repeated by the model as if it were a sentence (C2, W2). Both sides now head a line alike;
|
||||
* the side stays in the signature for its callers. */
|
||||
export function relationshipLine(group: RelationshipGroup, _side: 'incoming' | 'outgoing', budget: number,
|
||||
words: RelationshipWords, markdown = false): { text: string; listed: number } {
|
||||
const quote = (value: string) => markdown ? `\`${value.replace(/`/g, "'")}\`` : value;
|
||||
const head = markdown ? `- **${words.typeCount(group.type, group.count)}:** ` : `- ${group.type} ${side === 'incoming' ? words.from : words.to} ${group.count}: `;
|
||||
const head = markdown ? `- **${words.typeCount(group.type, group.count)}:** ` : `- ${words.typeCount(group.type, group.count)}: `;
|
||||
let body = '', listed = 0;
|
||||
const more = (shown: number) => group.count > shown ? `${body ? '; ' : ''}${words.more(group.count - shown)}` : '';
|
||||
if (head.length + more(0).length > budget) return { text: '', listed: 0 };
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('the selected symbol source in a Galaxy explanation (K14)', () => {
|
||||
const packet = prepareExplanationContext(undefined, jsonbAggEvidence(), 3200, source);
|
||||
const prompt = formatExplanationEvidence(packet);
|
||||
expect(prompt).toContain('Source django/contrib/postgres/aggregates/general.py:50-52, a Python source file:\nclass JSONBAgg(OrderableAggMixin, Aggregate):');
|
||||
expect(prompt).toContain('Incoming relationships: 23 from 12 symbols.');
|
||||
expect(prompt).toContain('Incoming: 23 relationships from 12 symbols.');
|
||||
expect(packet.limitations.join('\n')).not.toContain('Source unavailable');
|
||||
expect(prepareExplanationContext(undefined, jsonbAggEvidence(), 3200).limitations.join('\n')).toContain('Source unavailable');
|
||||
});
|
||||
@@ -107,8 +107,8 @@ describe('the selected symbol source in a Galaxy explanation (K14)', () => {
|
||||
it('summarizes the selection from the graph: what it is and its relationships by direction and type', () => {
|
||||
expect(selectionSummary(jsonbAggEvidence())).toEqual([
|
||||
'Selected: JSONBAgg (Class) in django/contrib/postgres/aggregates/general.py:50-54.',
|
||||
'Incoming relationships: 23 from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Outgoing relationships: 2 to 2 symbols (INHERITS 2).',
|
||||
'Incoming: 23 relationships from 12 symbols (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Outgoing: 2 relationships to 2 symbols (INHERITS 2).',
|
||||
'Scope: 1 hop in both directions, all relationship types; 15 symbols and 25 relationships; fully loaded.',
|
||||
]);
|
||||
expect(selectionSummary(djangoAreaEvidence())[0]).toBe('Selected source area: `django` (2310 files · 15299 indexed nodes).');
|
||||
@@ -138,8 +138,8 @@ describe('the selected symbol source in a Galaxy explanation (K14)', () => {
|
||||
it('summarizes the selection in German for a German question (C5)', () => {
|
||||
expect(selectionSummary(jsonbAggEvidence(), 'de')).toEqual([
|
||||
'Ausgewählt: JSONBAgg (Class) in django/contrib/postgres/aggregates/general.py:50-54.',
|
||||
'Eingehende Beziehungen: 23 aus 12 Symbolen (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Ausgehende Beziehungen: 2 zu 2 Symbolen (INHERITS 2).',
|
||||
'Eingehend: 23 Beziehungen von 12 Symbolen (CALLS 11, TESTS 11, DEFINES 1).',
|
||||
'Ausgehend: 2 Beziehungen zu 2 Symbolen (INHERITS 2).',
|
||||
'Ausschnitt: 1 Schritt in beide Richtungen, alle Beziehungstypen; 15 Symbole und 25 Beziehungen; vollständig geladen.',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('listed relationship answers', () => {
|
||||
const markdown = relationshipAnswer('What does postgres call?', [cut])!.markdown;
|
||||
expect(markdown).not.toMatch(/to 0 symbols/);
|
||||
expect(markdown).toContain('the snapshot left part of its relationships out, so counts and names can be incomplete');
|
||||
expect(markdown).toMatch(/Outgoing relationships: left out of this snapshot\.|\d+ outgoing relationships?\./);
|
||||
expect(markdown).toMatch(/Outgoing: relationships left out of this snapshot\.|\d+ outgoing relationships?\./);
|
||||
});
|
||||
|
||||
it('leaves questions about another symbol, attached non-Galaxy evidence and other questions to the model', () => {
|
||||
|
||||
@@ -37,7 +37,6 @@ export const browserAiText = {
|
||||
const en = (value: number) => value.toLocaleString('en-US');
|
||||
const de = (value: number) => value.toLocaleString('de-DE');
|
||||
const englishRelationshipWords = {
|
||||
from: 'from', to: 'to',
|
||||
more: (count: number) => `+${en(count)} more`,
|
||||
hops: (depth: number) => depth === 0 ? 'the selection only' : depth === 1 ? '1 hop' : `${depth} hops`,
|
||||
both: 'in both directions', inbound: 'incoming only', outbound: 'outgoing only',
|
||||
@@ -56,13 +55,16 @@ const englishRelationshipWords = {
|
||||
+ (layer > 1 ? 'so counts and names further out can be incomplete' : 'so counts and names can be incomplete, the direct relationships included'),
|
||||
exhausted: 'nothing further beyond this depth',
|
||||
scope: (shape: string, size: string, state: string) => `Scope: ${shape}; ${size}; ${state}.`,
|
||||
incoming: (total: number, symbols?: number) => `Incoming relationships: ${en(total)}${symbols === undefined ? '' : ` from ${en(symbols)} ${symbols === 1 ? 'symbol' : 'symbols'}`}.`,
|
||||
outgoing: (total: number, symbols?: number) => `Outgoing relationships: ${en(total)}${symbols === undefined ? '' : ` to ${en(symbols)} ${symbols === 1 ? 'symbol' : 'symbols'}`}.`,
|
||||
noIncoming: 'Incoming relationships: none in this scope.',
|
||||
noOutgoing: 'Outgoing relationships: none in this scope.',
|
||||
incomingNotLoaded: 'Incoming relationships: not loaded; the scope does not follow incoming edges.',
|
||||
outgoingNotLoaded: 'Outgoing relationships: not loaded; the scope does not follow outgoing edges.',
|
||||
cut: (side: 'incoming' | 'outgoing') => `${side === 'incoming' ? 'Incoming' : 'Outgoing'} relationships: left out of this snapshot.`,
|
||||
/** Each number with its unit: "Incoming: 23 relationships from 12 symbols", never "23 from 12", which reads like a score (W2). */
|
||||
incoming: (total: number, symbols?: number) => `Incoming: ${en(total)} ${total === 1 ? 'relationship' : 'relationships'}`
|
||||
+ `${symbols === undefined ? '' : ` from ${en(symbols)} ${symbols === 1 ? 'symbol' : 'symbols'}`}.`,
|
||||
outgoing: (total: number, symbols?: number) => `Outgoing: ${en(total)} ${total === 1 ? 'relationship' : 'relationships'}`
|
||||
+ `${symbols === undefined ? '' : ` to ${en(symbols)} ${symbols === 1 ? 'symbol' : 'symbols'}`}.`,
|
||||
noIncoming: 'Incoming: no relationships in this scope.',
|
||||
noOutgoing: 'Outgoing: no relationships in this scope.',
|
||||
incomingNotLoaded: 'Incoming: not loaded; the scope does not follow incoming edges.',
|
||||
outgoingNotLoaded: 'Outgoing: not loaded; the scope does not follow outgoing edges.',
|
||||
cut: (side: 'incoming' | 'outgoing') => `${side === 'incoming' ? 'Incoming' : 'Outgoing'}: relationships left out of this snapshot.`,
|
||||
truncated: 'the snapshot left part of its relationships out, so counts and names can be incomplete',
|
||||
moreTypes: (count: number) => `+${en(count)} more relationship ${count === 1 ? 'type' : 'types'}`,
|
||||
internal: (summary: string) => `Between the selected symbols: ${summary}.`,
|
||||
@@ -104,7 +106,6 @@ export type RelationshipWords = typeof englishRelationshipWords;
|
||||
export const relationshipWords: { en: RelationshipWords; de: RelationshipWords } = {
|
||||
en: englishRelationshipWords,
|
||||
de: {
|
||||
from: 'von', to: 'zu',
|
||||
more: (count: number) => `+${de(count)} weitere`,
|
||||
hops: (depth: number) => depth === 0 ? 'nur die Auswahl' : depth === 1 ? '1 Schritt' : `${depth} Schritte`,
|
||||
both: 'in beide Richtungen', inbound: 'nur eingehend', outbound: 'nur ausgehend',
|
||||
@@ -122,13 +123,15 @@ export const relationshipWords: { en: RelationshipWords; de: RelationshipWords }
|
||||
},
|
||||
exhausted: 'dahinter folgt nichts mehr',
|
||||
scope: (shape: string, size: string, state: string) => `Ausschnitt: ${shape}; ${size}; ${state}.`,
|
||||
incoming: (total: number, symbols?: number) => `Eingehende Beziehungen: ${de(total)}${symbols === undefined ? '' : ` aus ${de(symbols)} ${symbols === 1 ? 'Symbol' : 'Symbolen'}`}.`,
|
||||
outgoing: (total: number, symbols?: number) => `Ausgehende Beziehungen: ${de(total)}${symbols === undefined ? '' : ` zu ${de(symbols)} ${symbols === 1 ? 'Symbol' : 'Symbolen'}`}.`,
|
||||
noIncoming: 'Eingehende Beziehungen: keine in diesem Ausschnitt.',
|
||||
noOutgoing: 'Ausgehende Beziehungen: keine in diesem Ausschnitt.',
|
||||
incomingNotLoaded: 'Eingehende Beziehungen: nicht geladen; der Ausschnitt folgt keinen eingehenden Kanten.',
|
||||
outgoingNotLoaded: 'Ausgehende Beziehungen: nicht geladen; der Ausschnitt folgt keinen ausgehenden Kanten.',
|
||||
cut: (side: 'incoming' | 'outgoing') => `${side === 'incoming' ? 'Eingehende' : 'Ausgehende'} Beziehungen: in diesem Schnappschuss ausgelassen.`,
|
||||
incoming: (total: number, symbols?: number) => `Eingehend: ${de(total)} ${total === 1 ? 'Beziehung' : 'Beziehungen'}`
|
||||
+ `${symbols === undefined ? '' : ` von ${de(symbols)} ${symbols === 1 ? 'Symbol' : 'Symbolen'}`}.`,
|
||||
outgoing: (total: number, symbols?: number) => `Ausgehend: ${de(total)} ${total === 1 ? 'Beziehung' : 'Beziehungen'}`
|
||||
+ `${symbols === undefined ? '' : ` zu ${de(symbols)} ${symbols === 1 ? 'Symbol' : 'Symbolen'}`}.`,
|
||||
noIncoming: 'Eingehend: keine Beziehungen in diesem Ausschnitt.',
|
||||
noOutgoing: 'Ausgehend: keine Beziehungen in diesem Ausschnitt.',
|
||||
incomingNotLoaded: 'Eingehend: nicht geladen; der Ausschnitt folgt keinen eingehenden Kanten.',
|
||||
outgoingNotLoaded: 'Ausgehend: nicht geladen; der Ausschnitt folgt keinen ausgehenden Kanten.',
|
||||
cut: (side: 'incoming' | 'outgoing') => `${side === 'incoming' ? 'Eingehend' : 'Ausgehend'}: Beziehungen in diesem Schnappschuss ausgelassen.`,
|
||||
truncated: 'der Schnappschuss hat einen Teil der Beziehungen ausgelassen, Anzahlen und Namen können unvollständig sein',
|
||||
moreTypes: (count: number) => `+${de(count)} weitere ${count === 1 ? 'Beziehungstyp' : 'Beziehungstypen'}`,
|
||||
internal: (summary: string) => `Zwischen den ausgewählten Symbolen: ${summary}.`,
|
||||
|
||||
@@ -457,7 +457,7 @@ async function chatChecks(page) {
|
||||
const callers = [...new Set(truth.filter((row) => row.type === 'CALLS').map((row) => row.source))];
|
||||
const byType = truth.reduce((acc, row) => { acc[row.type] = (acc[row.type] ?? 0) + 1; return acc; }, {});
|
||||
const card = await page.locator('.cbm-chat-explanation').innerText().catch(() => '');
|
||||
const factsInCard = Object.entries(byType).every(([type, count]) => card.includes(`${type} ${count}`)) && card.includes(`Incoming relationships: ${truth.length}`);
|
||||
const factsInCard = Object.entries(byType).every(([type, count]) => card.includes(`${type} ${count}`)) && card.includes(`Incoming: ${truth.length} relationship`);
|
||||
const sourceInPrompt = autoPrompt.includes('class JSONBAgg(');
|
||||
check('C1', 'Erklaerung: Fakten aus dem Index fest in der Karte, Prompt mit Quelltext und ohne interne Feldnamen', autoPrompt.length > 0 && forbidden.length === 0 && sourceInPrompt && factsInCard,
|
||||
{ forbiddenFound: forbidden, sourceInPrompt, factsInCard, truthByType: byType, callers: callers.length, cardStart: card.slice(0, 400), promptStart: autoPrompt.slice(0, 300) }, [explanationImage]);
|
||||
|
||||
@@ -487,7 +487,7 @@ async function k7(page) {
|
||||
let card = await explanationText(page);
|
||||
outputs.push({ selection: 'JSONBAgg incoming CALLS', card, model: (await answersSince(page, from))[0]?.output, prompt: promptText(request) });
|
||||
await shot(page, 'K7', 'galaxy-incoming-calls', 'JSONBAgg, incoming, CALLS only: listed facts, at most one model sentence, no "list of integers / list of strings".');
|
||||
const galaxyOk = Boolean(request) && /Incoming relationships: 11 from 11 symbols \(CALLS 11\)/.test(card) && !/list of (?:integers|strings)|output is/i.test(card)
|
||||
const galaxyOk = Boolean(request) && /Incoming: 11 relationships from 11 symbols \(CALLS 11\)/.test(card) && !/list of (?:integers|strings)|output is/i.test(card)
|
||||
&& /Never state types, parameters, inputs, outputs/.test(promptText(request)) && /class JSONBAgg\(OrderableAggMixin, Aggregate\)/.test(promptText(request));
|
||||
// Architecture, Overview: the django area (hand test 17:53).
|
||||
await tab(page, 'architecture');
|
||||
|
||||
Reference in new issue
Block a user