Skip to content

Commit 9858b43

Browse files
Implement Deep-Linguistic Neuro-Symbolic Core
Replaced mock linguistic data with a production-grade Universal Linguistic Engine. - Added `UniversalLinguisticEngine.js` with rule-based G2P phonetics and feature-based constrained grammar. - Updated `App.jsx` to utilize `UniversalLinguisticEngine` for: - Dynamic rhyme detection (replacing hardcoded groups). - Accurate syllable counting via phonetic analysis. - Grammar generation for the CYK parser. - Implemented `UPDATES_STRATEGY.md` outlining the roadmap. - Fixed regex logic in PhoneticEngine to handle multiple occurrences correctly.
1 parent d79a09e commit 9858b43

5 files changed

Lines changed: 390 additions & 44 deletions

File tree

UPDATES_STRATEGY.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# AG-TUNE Strategic Update Plan
2+
3+
This document outlines three major quarter-yearly scale updates envisioned for the AG-TUNE model, aimed at evolving it from a prototype to a production-grade neuro-symbolic poetry engine.
4+
5+
## 1. Deep-Linguistic Neuro-Symbolic Core (Selected for Implementation)
6+
**Goal:** Replace shallow, hardcoded linguistic data with a robust, logic-driven generative engine.
7+
* **Current State:** Uses a tiny hardcoded grammar (~30 words) and a lookup table for rhymes (~16 words).
8+
* **Upgrade:** Implement a feature-based Context-Free Grammar (CFG) with agreement enforcement (Subject-Verb-Object consistency). Replace dictionary lookups with an algorithmic Grapheme-to-Phoneme (G2P) engine for dynamic rhyme and meter detection on *any* English word.
9+
* **Benefit:** Enables infinite vocabulary usage, grammatical correctness, and true poetic structure without relying on massive pre-trained datasets.
10+
11+
## 2. Hierarchical Narrative Planner
12+
**Goal:** Enable the model to "think" in stories rather than just lines.
13+
* **Current State:** Generates line-by-line using a limited look-behind buffer.
14+
* **Upgrade:** Introduce a "Director" agent that plans a stanza-level emotional arc (e.g., "Stanza 1: Loss -> Stanza 2: Bargaining -> Stanza 3: Acceptance"). Use the Rete engine to enforce these high-level constraints during the beam search of each line.
15+
* **Benefit:** Poems will have a cohesive theme and narrative progression.
16+
17+
## 3. Cross-Modal Synesthetic Training
18+
**Goal:** Ground the poem's imagery in sensory reality.
19+
* **Current State:** Embeddings are learned from text co-occurrence only.
20+
* **Upgrade:** Train the emotional embedding space using multimodal data (image-caption pairs or audio-lyrics pairs). Map visual features (brightness, color entropy) to poetic features.
21+
* **Benefit:** The model could generate poetry based on an image or a melody, with grounded metaphors (e.g., describing "yellow" not just as a word, but as a sensation linked to the trained visual concept).
22+
23+
---
24+
25+
## Implementation Selection
26+
We will implement **Update #1: Deep-Linguistic Neuro-Symbolic Core**. This is the most critical foundation; without a robust way to understand and generate language structure, higher-level planning or multimodal inputs cannot be effectively expressed. This update effectively "productionizes" the core generation capability.

dev_log.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
> ag-tune-lapoet@1.0.0 dev
3+
> vite
4+
5+
6+
VITE v7.2.6 ready in 397 ms
7+
8+
➜ Local: http://localhost:3000/
9+
➜ Network: use --host to expose

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.jsx

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818

1919
import React, { useState, useEffect, useCallback, useRef } from 'react';
20+
import { UniversalLinguisticEngine } from './UniversalLinguisticEngine';
2021

2122
// ============================================================================
2223
// CORE ALGORITHM IMPLEMENTATIONS
@@ -481,7 +482,8 @@ class AGTuneEngine {
481482
this.valueEstimator = new TDValueEstimator(24, 0.01, 0.95, 0.8);
482483
this.rng = new LaggedFibonacciGenerator();
483484
this.rete = new ReteEngine();
484-
this.parser = new CYKParser(this._getGrammar());
485+
this.ule = new UniversalLinguisticEngine(); // Integration of Production-Grade Logic
486+
this.parser = new CYKParser(this.ule.getGrammar());
485487
this.vocabulary = new Set();
486488
this.embeddings = new Map();
487489
this.emotionalSpace = new Map();
@@ -491,34 +493,19 @@ class AGTuneEngine {
491493
this._initializeReteRules();
492494
}
493495

494-
/**
495-
* Define grammatical rules for syntactic validation
496-
*/
497-
_getGrammar() {
498-
return {
499-
'S': [['NP', 'VP'], ['VP'], ['S', 'PP']],
500-
'NP': [['Det', 'N'], ['N'], ['Adj', 'N'], ['NP', 'PP']],
501-
'VP': [['V', 'NP'], ['V'], ['Adv', 'VP'], ['VP', 'PP']],
502-
'PP': [['P', 'NP']],
503-
'Det': ['the', 'a', 'an', 'this', 'that', 'my', 'thy'],
504-
'Adj': ['bright', 'dark', 'sweet', 'cold', 'warm', 'soft', 'hard', 'gentle', 'fierce'],
505-
'N': ['heart', 'soul', 'love', 'death', 'life', 'night', 'day', 'sun', 'moon', 'star', 'sea', 'sky', 'wind', 'fire', 'light', 'shadow'],
506-
'V': ['beats', 'breaks', 'shines', 'falls', 'rises', 'calls', 'dies', 'lives', 'loves', 'hates', 'waits', 'seeks', 'finds', 'loses', 'gives', 'takes'],
507-
'Adv': ['softly', 'gently', 'swiftly', 'slowly', 'deeply', 'bright', 'dark'],
508-
'P': ['in', 'on', 'at', 'with', 'without', 'against', 'beneath', 'above', 'through', 'beyond']
509-
};
510-
}
511-
512496
/**
513497
* Initialize Rete constraint checking rules
514498
*/
515499
_initializeReteRules() {
516-
// Rhyme scheme constraint
500+
// Rhyme scheme constraint: NOW USES DYNAMIC PHONETICS
517501
this.rete.addRule('rhymeCheck', [
518502
{ key: 'lastWord', test: (w) => typeof w === 'string' && w.length > 0 },
519-
{ key: 'rhymeScheme', test: (s) => ['A', 'B'].includes(s) }
503+
{ key: 'rhymeTarget', test: (t) => typeof t === 'string' }
520504
], (facts) => {
521-
return facts.rhymeScheme === this._getRhymeGroup(facts.lastWord);
505+
if (!facts.rhymeTarget) return true;
506+
const w1 = this.ule.analyze(facts.lastWord);
507+
const w2 = this.ule.analyze(facts.rhymeTarget);
508+
return w1.rhymePart === w2.rhymePart;
522509
});
523510

524511
// Syllable count constraint (8-10 for iambic pentameter)
@@ -534,21 +521,6 @@ class AGTuneEngine {
534521
], () => true);
535522
}
536523

537-
/**
538-
* Map word to rhyme group
539-
*/
540-
_getRhymeGroup(word) {
541-
const rhymes = {
542-
'A': ['day', 'way', 'say', 'stay', 'pray', 'grey', 'may', 'play'],
543-
'B': ['night', 'light', 'bright', 'sight', 'fight', 'flight', 'height', 'right']
544-
};
545-
546-
for (const [group, words] of Object.entries(rhymes)) {
547-
if (words.includes(word.toLowerCase())) return group;
548-
}
549-
return null;
550-
}
551-
552524
/**
553525
* Simple tokenization
554526
*/
@@ -590,10 +562,10 @@ class AGTuneEngine {
590562
}
591563

592564
/**
593-
* Heuristic syllable counting (vowel groups)
565+
* Advanced syllable counting via ULE
594566
*/
595567
_countSyllables(word) {
596-
return word.toLowerCase().match(/[aeiouy]{1,3}/g)?.length || 1;
568+
return this.ule.analyze(word).syllables;
597569
}
598570

599571
/**
@@ -907,7 +879,7 @@ class AGTuneEngine {
907879
/**
908880
* Generate single line of poetry using A*-guided beam search
909881
*/
910-
generateLine(prompt, targetSyllables = 10, beamWidth = 5, maxIter = 50) {
882+
generateLine(prompt, targetSyllables = 10, beamWidth = 5, rhymeTarget = null, maxIter = 50) {
911883
if (!this.isTrained) throw new Error('Model must be trained before generation');
912884

913885
const context = this._tokenize(prompt);
@@ -933,7 +905,15 @@ class AGTuneEngine {
933905

934906
// H(n): heuristic cost (multi-objective)
935907
const meterScore = FFTMeterAnalyzer.analyzeStressPattern(this._getStressPattern(newLine));
936-
const rhymeScore = this._getRhymeGroup(word) ? 1 : 0.5;
908+
909+
// Use logic-based rhyming preference if we are near the end of the line
910+
const analysis = this.ule.analyze(word);
911+
let rhymeBonus = 0;
912+
if (rhymeTarget && newSyllableCount >= targetSyllables - 2) {
913+
const targetAnalysis = this.ule.analyze(rhymeTarget);
914+
if (analysis.rhymePart === targetAnalysis.rhymePart) rhymeBonus = 2.0;
915+
}
916+
937917
const cycle = FloydCycleDetector.detect(newContext);
938918
const cyclePenalty = cycle.detected ? cycle.length * 2 : 0;
939919

@@ -943,7 +923,7 @@ class AGTuneEngine {
943923
// Multi-objective: β1*Structure + β2*Theme + β3*V(S)
944924
const β1 = 0.3, β2 = 0.3, β3 = 0.4;
945925
const H = β1 * (1 - meterScore + cyclePenalty) +
946-
β2 * (1 - rhymeScore) +
926+
β2 * (1 - rhymeBonus) +
947927
β3 * Math.max(0, 2 - aestheticValue);
948928

949929
const totalScore = g + H;
@@ -965,7 +945,7 @@ class AGTuneEngine {
965945
// Rete constraint verification
966946
const facts = {
967947
lastWord: chosen.word,
968-
rhymeScheme: this._getRhymeGroup(chosen.word),
948+
rhymeTarget: (syllableCount >= targetSyllables - 2) ? rhymeTarget : null, // Only enforce at end of line
969949
syllableCount,
970950
history: context
971951
};
@@ -994,14 +974,25 @@ class AGTuneEngine {
994974
const allTokens = this._tokenize(prompt);
995975
const states = [];
996976
const rewards = [];
977+
let previousEndWord = null;
997978

998979
for (let i = 0; i < lines; i++) {
980+
// Simple AABB or ABAB logic could be implemented here.
981+
// For now, let's try to make every even line rhyme with the previous one (AABB style approximation)
982+
const rhymeTarget = (i % 2 === 1) ? previousEndWord : null;
983+
999984
const lineObj = this.generateLine(
1000985
poem.join(' ') + ' ' + prompt,
1001986
10, // Iambic pentameter ~10 syllables
1002-
beamWidth
987+
beamWidth,
988+
rhymeTarget
1003989
);
1004990

991+
const lineTokens = this._tokenize(lineObj.line);
992+
if (lineTokens.length > 0) {
993+
previousEndWord = lineTokens[lineTokens.length - 1];
994+
}
995+
1005996
poem.push(lineObj.line);
1006997
allTokens.push(...this._tokenize(lineObj.line));
1007998
states.push(lineObj.finalState);

0 commit comments

Comments
 (0)