feat: publish HoloLake model-native living system source
This commit is contained in:
parent
6ad10edde1
commit
c395dd3a99
2467 changed files with 615073 additions and 0 deletions
456
product-source/hololake-platform/src/utils/ai-context.test.ts
Normal file
456
product-source/hololake-platform/src/utils/ai-context.test.ts
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
resolveTarget,
|
||||
collectLinkedEntries,
|
||||
buildContextualPrompt,
|
||||
buildContextSnapshot,
|
||||
formatPromptWithReferences,
|
||||
} from './ai-context'
|
||||
import type { VaultEntry } from '../types'
|
||||
|
||||
const makeEntry = (overrides: Partial<VaultEntry> = {}): VaultEntry => ({
|
||||
path: '/vault/note/test.md',
|
||||
filename: 'test.md',
|
||||
title: 'Test Note',
|
||||
isA: 'Note',
|
||||
aliases: [],
|
||||
belongsTo: [],
|
||||
relatedTo: [],
|
||||
status: null,
|
||||
archived: false,
|
||||
modifiedAt: 1700000000,
|
||||
createdAt: 1700000000,
|
||||
fileSize: 100,
|
||||
snippet: '',
|
||||
wordCount: 0,
|
||||
relationships: {},
|
||||
icon: null,
|
||||
color: null,
|
||||
order: null,
|
||||
outgoingLinks: [],
|
||||
...overrides,
|
||||
})
|
||||
|
||||
describe('resolveTarget', () => {
|
||||
const entries = [
|
||||
makeEntry({ path: '/vault/a.md', title: 'Alpha', filename: 'a.md' }),
|
||||
makeEntry({ path: '/vault/b.md', title: 'Beta', filename: 'beta-note.md', aliases: ['B'] }),
|
||||
]
|
||||
|
||||
it('resolves by title (case-insensitive)', () => {
|
||||
expect(resolveTarget('alpha', entries)?.path).toBe('/vault/a.md')
|
||||
expect(resolveTarget('Alpha', entries)?.path).toBe('/vault/a.md')
|
||||
})
|
||||
|
||||
it('resolves by alias (case-insensitive)', () => {
|
||||
expect(resolveTarget('B', entries)?.path).toBe('/vault/b.md')
|
||||
expect(resolveTarget('b', entries)?.path).toBe('/vault/b.md')
|
||||
})
|
||||
|
||||
it('resolves by filename stem', () => {
|
||||
expect(resolveTarget('beta-note', entries)?.path).toBe('/vault/b.md')
|
||||
})
|
||||
|
||||
it('returns undefined for unresolvable target', () => {
|
||||
expect(resolveTarget('nonexistent', entries)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('collectLinkedEntries', () => {
|
||||
const entryA = makeEntry({ path: '/vault/a.md', title: 'Alpha' })
|
||||
const entryB = makeEntry({ path: '/vault/b.md', title: 'Beta' })
|
||||
const entryC = makeEntry({ path: '/vault/c.md', title: 'Gamma' })
|
||||
const entryD = makeEntry({ path: '/vault/d.md', title: 'Delta' })
|
||||
const allEntries = [entryA, entryB, entryC, entryD]
|
||||
|
||||
it('returns empty array when active has no links', () => {
|
||||
expect(collectLinkedEntries(entryA, allEntries)).toEqual([])
|
||||
})
|
||||
|
||||
it('collects entries from outgoingLinks', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
outgoingLinks: ['Alpha', 'Beta'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Alpha', 'Beta'])
|
||||
})
|
||||
|
||||
it('collects entries from relationships', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
relationships: { relatedTo: ['[[Alpha]]', '[[Gamma]]'] },
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Alpha', 'Gamma'])
|
||||
})
|
||||
|
||||
it('collects entries from belongsTo', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
belongsTo: ['[[Delta]]'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Delta'])
|
||||
})
|
||||
|
||||
it('collects entries from relatedTo', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
relatedTo: ['[[Beta]]'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Beta'])
|
||||
})
|
||||
|
||||
it('deduplicates entries across all link sources', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
outgoingLinks: ['Alpha', 'Beta'],
|
||||
relationships: { people: ['[[Alpha]]'] },
|
||||
belongsTo: ['[[Beta]]'],
|
||||
relatedTo: ['[[Alpha]]'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Alpha', 'Beta'])
|
||||
})
|
||||
|
||||
it('excludes the active note itself', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/a.md', title: 'Alpha',
|
||||
outgoingLinks: ['Alpha'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, allEntries)
|
||||
expect(linked).toEqual([])
|
||||
})
|
||||
|
||||
it('ignores unresolvable links', () => {
|
||||
const active = makeEntry({
|
||||
path: '/vault/main.md', title: 'Main',
|
||||
outgoingLinks: ['Alpha', 'Nonexistent'],
|
||||
})
|
||||
const linked = collectLinkedEntries(active, [...allEntries, active])
|
||||
expect(linked.map(e => e.title)).toEqual(['Alpha'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildContextualPrompt', () => {
|
||||
it('includes active note title and type', () => {
|
||||
const active = makeEntry({ path: '/vault/a.md', title: 'Alpha', isA: 'Project' })
|
||||
const prompt = buildContextualPrompt(active, [])
|
||||
expect(prompt).toContain('Alpha')
|
||||
expect(prompt).toContain('Project')
|
||||
})
|
||||
|
||||
it('includes linked note titles', () => {
|
||||
const active = makeEntry({ path: '/vault/a.md', title: 'Alpha' })
|
||||
const linked = makeEntry({ path: '/vault/b.md', title: 'Beta', isA: 'Person' })
|
||||
const prompt = buildContextualPrompt(active, [linked])
|
||||
expect(prompt).toContain('Beta')
|
||||
expect(prompt).toContain('Person')
|
||||
expect(prompt).toContain('Linked Notes')
|
||||
})
|
||||
|
||||
it('includes the system preamble', () => {
|
||||
const active = makeEntry({ path: '/vault/a.md', title: 'Alpha' })
|
||||
const prompt = buildContextualPrompt(active, [])
|
||||
expect(prompt).toContain('current AI instance operating the Guanghu language-personality system inside HoloLake Era')
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildContextSnapshot', () => {
|
||||
const active = makeEntry({ path: '/vault/a.md', title: 'Alpha', isA: 'Project', status: 'active', properties: { Owner: 'Alice' } })
|
||||
const entries = [
|
||||
active,
|
||||
makeEntry({ path: '/vault/b.md', title: 'Beta', isA: 'Person' }),
|
||||
makeEntry({ path: '/vault/c.md', title: 'Gamma', isA: 'Note' }),
|
||||
]
|
||||
|
||||
it('includes activeNote with body and frontmatter', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries, activeNoteContent: '---\ntitle: Alpha\n---\n# Alpha\nProject content.' })
|
||||
expect(result).toContain('Alpha')
|
||||
expect(result).toContain('Project content.')
|
||||
expect(result).toContain('"type": "Project"')
|
||||
expect(result).toContain('"status": "active"')
|
||||
expect(result).toContain('"owner": "Alice"')
|
||||
})
|
||||
|
||||
it('includes system preamble', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries })
|
||||
expect(result).toContain('current AI instance operating the Guanghu language-personality system')
|
||||
expect(result).toContain('Context Snapshot')
|
||||
})
|
||||
|
||||
it('does not instruct a direct API model to invent vault read tools', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active,
|
||||
entries,
|
||||
capabilities: 'embedded-context-only',
|
||||
})
|
||||
|
||||
expect(result).not.toContain('get_note')
|
||||
expect(result).not.toContain('read_file')
|
||||
expect(result).toContain('Only call tools explicitly provided by the API')
|
||||
})
|
||||
|
||||
it('includes vault summary with types and totalNotes', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries })
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.vault.totalNotes).toBe(3)
|
||||
expect(json.vault.types).toContain('Project')
|
||||
expect(json.vault.types).toContain('Person')
|
||||
expect(json.vault.types).toContain('Note')
|
||||
})
|
||||
|
||||
it('includes openTabs excluding active note', () => {
|
||||
const tab = makeEntry({ path: '/vault/b.md', title: 'Beta', isA: 'Person' })
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
openTabs: [active, tab],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.openTabs).toHaveLength(1)
|
||||
expect(json.openTabs[0].title).toBe('Beta')
|
||||
})
|
||||
|
||||
it('omits openTabs when none besides active', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
openTabs: [active],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.openTabs).toBeUndefined()
|
||||
})
|
||||
|
||||
it('includes noteListFilter when present', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
noteListFilter: { type: 'Project', query: 'search' },
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteListFilter.type).toBe('Project')
|
||||
expect(json.noteListFilter.query).toBe('search')
|
||||
})
|
||||
|
||||
it('omits noteListFilter when empty', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
noteListFilter: { type: null, query: '' },
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteListFilter).toBeUndefined()
|
||||
})
|
||||
|
||||
it('includes referencedNotes metadata', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
references: [
|
||||
{ title: 'Beta', path: '/vault/b.md', type: 'Person' },
|
||||
{ title: 'Gamma', path: '/vault/c.md', type: null },
|
||||
],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.referencedNotes).toHaveLength(2)
|
||||
expect(json.referencedNotes[0].title).toBe('Beta')
|
||||
expect(json.referencedNotes[1].type).toBe('Note') // null fallback
|
||||
})
|
||||
|
||||
it('embeds explicit referenced note bodies when available', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
references: [
|
||||
{
|
||||
title: 'Beta',
|
||||
path: '/vault/b.md',
|
||||
type: 'Person',
|
||||
content: '---\ntitle: Beta\n---\n\n# Beta\nReferenced body.',
|
||||
},
|
||||
],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.referencedNotes[0].body).toBe('# Beta\nReferenced body.')
|
||||
expect(json.referencedNotes[0].body).not.toContain('title: Beta')
|
||||
})
|
||||
|
||||
it('omits referencedNotes when no references provided', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries })
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.referencedNotes).toBeUndefined()
|
||||
})
|
||||
|
||||
it('includes noteList when provided', () => {
|
||||
const noteList = [
|
||||
{ path: '/vault/a.md', title: 'Alpha', type: 'Project' },
|
||||
{ path: '/vault/b.md', title: 'Beta', type: 'Person' },
|
||||
]
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
noteList,
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toHaveLength(2)
|
||||
expect(json.noteList[0].title).toBe('Alpha')
|
||||
expect(json.noteList[1].type).toBe('Person')
|
||||
})
|
||||
|
||||
it('truncates noteList at 100 items', () => {
|
||||
const noteList = Array.from({ length: 150 }, (_, i) => ({
|
||||
path: `/vault/note-${i}.md`, title: `Note ${i}`, type: 'Note',
|
||||
}))
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
noteList,
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toHaveLength(100)
|
||||
expect(json.noteListTruncated).toEqual({ shown: 100, total: 150 })
|
||||
})
|
||||
|
||||
it('omits noteList when empty', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active, entries,
|
||||
noteList: [],
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.noteList).toBeUndefined()
|
||||
})
|
||||
|
||||
it('strips frontmatter from activeNoteContent before setting body', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active,
|
||||
entries,
|
||||
activeNoteContent: '---\ntitle: Alpha\n---\n\n# Alpha\nProject content from tab.',
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toContain('Project content from tab.')
|
||||
expect(json.activeNote.body).not.toContain('---')
|
||||
expect(json.activeNote.body).not.toContain('title: Alpha')
|
||||
})
|
||||
|
||||
it('returns empty body when raw content is frontmatter-only (bug case)', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active,
|
||||
entries,
|
||||
activeNoteContent: '---\ntitle: Alpha\nis_a: Project\nstatus: active\n---\n',
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toBe('')
|
||||
})
|
||||
|
||||
it('uses activeNoteContent for body', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active,
|
||||
entries,
|
||||
activeNoteContent: '---\ntitle: Alpha\n---\nFresh editor content',
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toBe('Fresh editor content')
|
||||
})
|
||||
|
||||
it('compacts large active note bodies and points agents at the full note tool', () => {
|
||||
const largeBody = [
|
||||
'Opening section '.repeat(900),
|
||||
'Middle section that should not be fully embedded '.repeat(900),
|
||||
'Closing section '.repeat(900),
|
||||
].join('\n')
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: makeEntry({
|
||||
path: '/vault/large-note.md',
|
||||
title: 'Large Note',
|
||||
wordCount: 2700,
|
||||
}),
|
||||
entries,
|
||||
activeNoteContent: largeBody,
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
|
||||
expect(json.activeNote.body.length).toBeLessThan(largeBody.length)
|
||||
expect(json.activeNote.body).toContain('Opening section')
|
||||
expect(json.activeNote.body).toContain('Closing section')
|
||||
expect(json.activeNote.body).toContain('get_note("/vault/large-note.md")')
|
||||
expect(json.activeNote.bodyTruncated).toEqual({
|
||||
shownChars: expect.any(Number),
|
||||
totalChars: largeBody.trim().length,
|
||||
strategy: 'head-tail',
|
||||
})
|
||||
})
|
||||
|
||||
it('returns empty body when no activeNoteContent', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries })
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toBe('')
|
||||
})
|
||||
|
||||
it('includes wordCount in activeNote', () => {
|
||||
const entryWithWords = makeEntry({ path: '/vault/a.md', title: 'Alpha', wordCount: 206 })
|
||||
const result = buildContextSnapshot({ activeEntry: entryWithWords, entries })
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.wordCount).toBe(206)
|
||||
})
|
||||
|
||||
it('handles content with no frontmatter (plain markdown)', () => {
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: active,
|
||||
entries,
|
||||
activeNoteContent: '# Just a heading\n\nSome plain content.',
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toBe('# Just a heading\n\nSome plain content.')
|
||||
})
|
||||
|
||||
it('includes defensive body when body is empty but wordCount > 0', () => {
|
||||
const entryWithWords = makeEntry({
|
||||
path: '/vault/a.md', title: 'Alpha', wordCount: 206,
|
||||
})
|
||||
const result = buildContextSnapshot({
|
||||
activeEntry: entryWithWords,
|
||||
entries,
|
||||
activeNoteContent: '---\ntitle: Alpha\n---\n',
|
||||
})
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.body).toContain('get_note')
|
||||
expect(json.activeNote.body).toContain('206 words')
|
||||
})
|
||||
|
||||
it('includes wikilink instruction in preamble', () => {
|
||||
const result = buildContextSnapshot({ activeEntry: active, entries })
|
||||
expect(result).toContain('[[Note Title]]')
|
||||
expect(result).toContain('wikilink')
|
||||
})
|
||||
|
||||
it('includes belongsTo and relatedTo in frontmatter', () => {
|
||||
const entryWithRels = makeEntry({
|
||||
path: '/vault/a.md', title: 'Alpha',
|
||||
belongsTo: ['[[Parent]]'],
|
||||
relatedTo: ['[[Sibling]]'],
|
||||
relationships: { people: ['[[Alice]]'] },
|
||||
})
|
||||
const result = buildContextSnapshot({ activeEntry: entryWithRels, entries })
|
||||
const json = JSON.parse(result.split('```json\n')[1].split('\n```')[0])
|
||||
expect(json.activeNote.frontmatter.belongsTo).toEqual(['[[Parent]]'])
|
||||
expect(json.activeNote.frontmatter.relatedTo).toEqual(['[[Sibling]]'])
|
||||
expect(json.activeNote.frontmatter.relationships).toEqual({ people: ['[[Alice]]'] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatPromptWithReferences', () => {
|
||||
it('adds referenced note context to the current prompt', () => {
|
||||
const result = formatPromptWithReferences('Summarize this', [
|
||||
{
|
||||
title: 'Beta',
|
||||
path: '/vault/b.md',
|
||||
type: 'Person',
|
||||
content: '---\ntitle: Beta\n---\n\nImportant referenced content.',
|
||||
},
|
||||
])
|
||||
|
||||
expect(result).toContain('Summarize this')
|
||||
expect(result).toContain('Referenced Notes')
|
||||
expect(result).toContain('Important referenced content.')
|
||||
expect(result).toContain('/vault/b.md')
|
||||
})
|
||||
|
||||
it('leaves prompts without references unchanged', () => {
|
||||
expect(formatPromptWithReferences('Plain prompt')).toBe('Plain prompt')
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue