Go · 15807 bytes Raw Blame History
1 package llm
2
3 import (
4 "math"
5 "math/rand"
6 "strings"
7 "time"
8 )
9
10 // AdversarialInsultGenerator implements a GAN-inspired system
11 // Generator creates insults, Critic scores them, iterative improvement
12 type AdversarialInsultGenerator struct {
13 generator *InsultGenerator
14 critic *InsultCritic
15 database *InsultDatabase
16 markov *MarkovGenerator
17
18 // Training state
19 generatorScore float64
20 criticScore float64
21 rounds int
22
23 // Quality thresholds
24 minQuality float64
25 targetQuality float64
26 improvementRate float64
27 }
28
29 // InsultGenerator creates insult candidates
30 type InsultGenerator struct {
31 templates []string
32 components *ComponentLibrary
33 markov *MarkovGenerator
34 creativityMode string // "safe", "balanced", "wild"
35 rng *rand.Rand
36 }
37
38 // InsultCritic scores insult quality
39 type InsultCritic struct {
40 relevanceWeight float64
41 noveltyWeight float64
42 brutalityWeight float64
43 coherenceWeight float64
44 lengthWeight float64
45
46 seenInsults map[string]int // Track seen insults for novelty
47 }
48
49 // ComponentLibrary stores semantic building blocks
50 type ComponentLibrary struct {
51 Subjects []string // "Your code", "This commit", "Your docker build"
52 Verbs []string // "failed harder than", "crashed like", "died faster than"
53 Objects []string // "your career", "a burning dumpster", "your hopes"
54 Punchlines []string // "combined", "on steroids", "in production"
55 Intensifiers []string // "absolutely", "completely", "monumentally"
56 Comparisons []string // "than a", "like a", "as much as"
57 }
58
59 // InsultQualityScore represents multi-dimensional quality metrics
60 type InsultQualityScore struct {
61 Relevance float64 // How well it matches the context
62 Novelty float64 // How original/unique it is
63 Brutality float64 // How savage/brutal it is
64 Coherence float64 // How well it flows/makes sense
65 Length float64 // Appropriate length (not too long/short)
66 Overall float64 // Weighted combination
67 Breakdown string // Human-readable explanation
68 }
69
70 // NewAdversarialInsultGenerator creates a GAN-inspired generator
71 func NewAdversarialInsultGenerator(db *InsultDatabase, markov *MarkovGenerator) *AdversarialInsultGenerator {
72 components := initializeComponentLibrary()
73
74 return &AdversarialInsultGenerator{
75 generator: &InsultGenerator{
76 templates: initializeTemplates(),
77 components: components,
78 markov: markov,
79 creativityMode: "balanced",
80 rng: rand.New(rand.NewSource(time.Now().UnixNano())),
81 },
82 critic: &InsultCritic{
83 relevanceWeight: 0.30,
84 noveltyWeight: 0.25,
85 brutalityWeight: 0.20,
86 coherenceWeight: 0.15,
87 lengthWeight: 0.10,
88 seenInsults: make(map[string]int),
89 },
90 database: db,
91 markov: markov,
92 generatorScore: 0.5,
93 criticScore: 0.5,
94 rounds: 0,
95 minQuality: 0.6,
96 targetQuality: 0.8,
97 improvementRate: 0.05,
98 }
99 }
100
101 // Generate creates an insult using adversarial training
102 func (aig *AdversarialInsultGenerator) Generate(ctx *SmartFallbackContext, personality string) string {
103 const maxAttempts = 5
104 bestInsult := ""
105 bestScore := 0.0
106
107 // Generate multiple candidates and pick the best
108 for attempt := 0; attempt < maxAttempts; attempt++ {
109 // Generator creates candidate
110 candidate := aig.generator.CreateCandidate(ctx, personality)
111
112 // Critic scores it
113 score := aig.critic.Score(candidate, ctx, personality)
114
115 // Track best
116 if score.Overall > bestScore {
117 bestScore = score.Overall
118 bestInsult = candidate
119 }
120
121 // If we hit target quality, stop early
122 if score.Overall >= aig.targetQuality {
123 break
124 }
125 }
126
127 // Record this insult for novelty tracking
128 aig.critic.seenInsults[bestInsult]++
129
130 // Update adversarial scores (simplified GAN-like training)
131 aig.updateScores(bestScore)
132
133 // Only return if above minimum quality
134 if bestScore >= aig.minQuality {
135 return bestInsult
136 }
137
138 return "" // Not good enough
139 }
140
141 // GenerateBatch creates multiple candidates for ensemble selection
142 func (aig *AdversarialInsultGenerator) GenerateBatch(ctx *SmartFallbackContext, personality string, count int) []string {
143 candidates := make([]string, 0, count)
144
145 for i := 0; i < count; i++ {
146 candidate := aig.generator.CreateCandidate(ctx, personality)
147 score := aig.critic.Score(candidate, ctx, personality)
148
149 if score.Overall >= aig.minQuality {
150 candidates = append(candidates, candidate)
151 }
152 }
153
154 return candidates
155 }
156
157 // CreateCandidate generates an insult candidate
158 func (ig *InsultGenerator) CreateCandidate(ctx *SmartFallbackContext, personality string) string {
159 // Choose generation strategy based on creativity mode
160 strategy := ig.rng.Float64()
161
162 switch ig.creativityMode {
163 case "safe":
164 // Mostly template-based (70%), some Markov (30%)
165 if strategy < 0.7 {
166 return ig.generateFromTemplate(ctx, personality)
167 }
168 return ig.markov.Blend(ctx)
169
170 case "wild":
171 // Mostly Markov (60%), some composites (30%), some templates (10%)
172 if strategy < 0.6 {
173 return ig.markov.Blend(ctx)
174 } else if strategy < 0.9 {
175 return ig.generateComposite(ctx, personality)
176 }
177 return ig.generateFromTemplate(ctx, personality)
178
179 default: // "balanced"
180 // Mix of all: 40% template, 40% composite, 20% Markov
181 if strategy < 0.4 {
182 return ig.generateFromTemplate(ctx, personality)
183 } else if strategy < 0.8 {
184 return ig.generateComposite(ctx, personality)
185 }
186 return ig.markov.Blend(ctx)
187 }
188 }
189
190 // generateFromTemplate uses traditional templates
191 func (ig *InsultGenerator) generateFromTemplate(ctx *SmartFallbackContext, personality string) string {
192 template := ig.templates[ig.rng.Intn(len(ig.templates))]
193
194 // Replace variables
195 result := template
196 result = strings.ReplaceAll(result, "{command}", ctx.Command)
197 result = strings.ReplaceAll(result, "{commandType}", ctx.CommandType)
198 result = strings.ReplaceAll(result, "{error}", ctx.ErrorPattern)
199 result = strings.ReplaceAll(result, "{project}", ctx.ProjectType)
200
201 return result
202 }
203
204 // generateComposite builds from semantic components
205 func (ig *InsultGenerator) generateComposite(ctx *SmartFallbackContext, personality string) string {
206 comp := ig.components
207
208 // Build: [Subject] [Verb] [Object] [Punchline]
209 subject := comp.Subjects[ig.rng.Intn(len(comp.Subjects))]
210 verb := comp.Verbs[ig.rng.Intn(len(comp.Verbs))]
211 object := comp.Objects[ig.rng.Intn(len(comp.Objects))]
212
213 // Contextualize subject
214 subject = ig.contextualizeSubject(subject, ctx)
215
216 // Sometimes add intensifier
217 insult := subject + " " + verb + " " + object
218
219 // Sometimes add punchline
220 if ig.rng.Float64() < 0.5 {
221 punchline := comp.Punchlines[ig.rng.Intn(len(comp.Punchlines))]
222 insult += " " + punchline
223 }
224
225 // Ensure it ends with punctuation
226 if !strings.HasSuffix(insult, ".") && !strings.HasSuffix(insult, "!") {
227 insult += "."
228 }
229
230 return insult
231 }
232
233 // contextualizeSubject adapts subject to context
234 func (ig *InsultGenerator) contextualizeSubject(subject string, ctx *SmartFallbackContext) string {
235 // Replace generic "Your code" with specific command references
236 if ctx.Command != "" {
237 subject = strings.ReplaceAll(subject, "Your code", "Your "+ctx.Command)
238 subject = strings.ReplaceAll(subject, "This code", "This "+ctx.Command)
239 }
240
241 if ctx.ProjectType != "" {
242 subject = strings.ReplaceAll(subject, "project", ctx.ProjectType+" project")
243 }
244
245 return subject
246 }
247
248 // Score evaluates insult quality
249 func (ic *InsultCritic) Score(insult string, ctx *SmartFallbackContext, personality string) InsultQualityScore {
250 score := InsultQualityScore{}
251
252 // 1. Relevance: Does it relate to the context?
253 score.Relevance = ic.scoreRelevance(insult, ctx)
254
255 // 2. Novelty: Is it unique/original?
256 score.Novelty = ic.scoreNovelty(insult)
257
258 // 3. Brutality: How savage is it? (adjust for personality)
259 score.Brutality = ic.scoreBrutality(insult, personality)
260
261 // 4. Coherence: Does it make sense?
262 score.Coherence = ic.scoreCoherence(insult)
263
264 // 5. Length: Is it appropriately sized?
265 score.Length = ic.scoreLength(insult)
266
267 // Calculate weighted overall score
268 score.Overall = (score.Relevance * ic.relevanceWeight) +
269 (score.Novelty * ic.noveltyWeight) +
270 (score.Brutality * ic.brutalityWeight) +
271 (score.Coherence * ic.coherenceWeight) +
272 (score.Length * ic.lengthWeight)
273
274 // Generate breakdown
275 score.Breakdown = ic.generateBreakdown(score)
276
277 return score
278 }
279
280 func (ic *InsultCritic) scoreRelevance(insult string, ctx *SmartFallbackContext) float64 {
281 score := 0.0
282 insultLower := strings.ToLower(insult)
283
284 // Check if it mentions the command
285 if ctx.Command != "" && strings.Contains(insultLower, strings.ToLower(ctx.Command)) {
286 score += 0.3
287 }
288
289 // Check if it mentions the command type
290 if ctx.CommandType != "" && strings.Contains(insultLower, strings.ToLower(ctx.CommandType)) {
291 score += 0.2
292 }
293
294 // Check if it mentions the error pattern
295 if ctx.ErrorPattern != "" {
296 errorWords := strings.Split(ctx.ErrorPattern, "_")
297 for _, word := range errorWords {
298 if strings.Contains(insultLower, strings.ToLower(word)) {
299 score += 0.2
300 break
301 }
302 }
303 }
304
305 // Check if it mentions the project type
306 if ctx.ProjectType != "" && strings.Contains(insultLower, strings.ToLower(ctx.ProjectType)) {
307 score += 0.2
308 }
309
310 // Bonus for context-aware terms
311 contextTerms := []string{"failed", "error", "broken", "crash", "disaster"}
312 for _, term := range contextTerms {
313 if strings.Contains(insultLower, term) {
314 score += 0.1
315 break
316 }
317 }
318
319 return math.Min(1.0, score)
320 }
321
322 func (ic *InsultCritic) scoreNovelty(insult string) float64 {
323 // Check how many times we've seen this exact insult
324 useCount := ic.seenInsults[insult]
325
326 // Novelty decays with use
327 if useCount == 0 {
328 return 1.0 // Completely novel
329 } else if useCount == 1 {
330 return 0.8
331 } else if useCount < 5 {
332 return 0.6
333 } else if useCount < 10 {
334 return 0.4
335 }
336 return 0.2 // Heavily overused
337 }
338
339 func (ic *InsultCritic) scoreBrutality(insult string, personality string) float64 {
340 insultLower := strings.ToLower(insult)
341
342 // Count brutal words
343 brutalWords := []string{
344 "disaster", "catastrophe", "failure", "incompetence", "terrible",
345 "awful", "pathetic", "worthless", "garbage", "trash", "nightmare",
346 "doomed", "hopeless", "broken", "destroyed", "ruined", "killed",
347 }
348
349 brutalCount := 0
350 for _, word := range brutalWords {
351 if strings.Contains(insultLower, word) {
352 brutalCount++
353 }
354 }
355
356 // Base brutality score
357 brutality := math.Min(1.0, float64(brutalCount)*0.3)
358
359 // Adjust for personality
360 switch personality {
361 case "mild":
362 // Penalize high brutality
363 if brutality > 0.5 {
364 brutality *= 0.5
365 }
366 case "savage":
367 // Reward high brutality
368 if brutality < 0.5 {
369 brutality *= 0.7
370 }
371 }
372
373 return brutality
374 }
375
376 func (ic *InsultCritic) scoreCoherence(insult string) float64 {
377 // Simple heuristics for coherence
378 score := 1.0
379
380 // Penalize if too many numbers (likely corrupted)
381 digitCount := 0
382 for _, r := range insult {
383 if r >= '0' && r <= '9' {
384 digitCount++
385 }
386 }
387 if digitCount > len(insult)/4 {
388 score *= 0.5
389 }
390
391 // Penalize if too many repeated words
392 words := strings.Fields(insult)
393 uniqueWords := make(map[string]bool)
394 for _, word := range words {
395 uniqueWords[strings.ToLower(word)] = true
396 }
397 if len(uniqueWords) < len(words)/2 {
398 score *= 0.7
399 }
400
401 // Penalize if no spaces (likely corrupted)
402 if !strings.Contains(insult, " ") {
403 score *= 0.3
404 }
405
406 // Reward if it has proper punctuation
407 if strings.HasSuffix(insult, ".") || strings.HasSuffix(insult, "!") {
408 score *= 1.1
409 }
410
411 return math.Min(1.0, score)
412 }
413
414 func (ic *InsultCritic) scoreLength(insult string) float64 {
415 length := len(insult)
416
417 // Optimal length: 40-120 characters
418 if length >= 40 && length <= 120 {
419 return 1.0
420 } else if length >= 20 && length < 40 {
421 return 0.8
422 } else if length > 120 && length <= 150 {
423 return 0.8
424 } else if length < 20 {
425 return 0.5 // Too short
426 } else {
427 return 0.4 // Too long
428 }
429 }
430
431 func (ic *InsultCritic) generateBreakdown(score InsultQualityScore) string {
432 breakdown := ""
433 breakdown += "Relevance: " + formatFloat(score.Relevance) + " "
434 breakdown += "Novelty: " + formatFloat(score.Novelty) + " "
435 breakdown += "Brutality: " + formatFloat(score.Brutality) + " "
436 breakdown += "Coherence: " + formatFloat(score.Coherence) + " "
437 breakdown += "Length: " + formatFloat(score.Length)
438 return breakdown
439 }
440
441 // updateScores performs simplified GAN-like training
442 func (aig *AdversarialInsultGenerator) updateScores(qualityScore float64) {
443 // Generator score: how well it fooled the critic (inverse relationship)
444 aig.generatorScore = 0.7*aig.generatorScore + 0.3*qualityScore
445
446 // Critic score: how well it evaluated quality
447 aig.criticScore = 0.7*aig.criticScore + 0.3*(1.0-math.Abs(qualityScore-0.8))
448
449 aig.rounds++
450
451 // Adjust creativity based on performance
452 if aig.generatorScore < 0.5 && aig.rounds > 10 {
453 // Generator struggling - be more conservative
454 aig.generator.creativityMode = "safe"
455 } else if aig.generatorScore > 0.7 {
456 // Generator doing well - get more creative
457 aig.generator.creativityMode = "wild"
458 } else {
459 aig.generator.creativityMode = "balanced"
460 }
461 }
462
463 // GetStats returns adversarial system statistics
464 func (aig *AdversarialInsultGenerator) GetStats() map[string]interface{} {
465 return map[string]interface{}{
466 "generator_score": aig.generatorScore,
467 "critic_score": aig.criticScore,
468 "training_rounds": aig.rounds,
469 "creativity_mode": aig.generator.creativityMode,
470 "unique_insults": len(aig.critic.seenInsults),
471 }
472 }
473
474 // Initialize component library
475 func initializeComponentLibrary() *ComponentLibrary {
476 return &ComponentLibrary{
477 Subjects: []string{
478 "Your code", "This commit", "Your build", "This deployment",
479 "Your docker container", "This merge", "Your test suite",
480 "Your pull request", "This branch", "Your configuration",
481 "This error", "Your logic", "This attempt", "Your workflow",
482 },
483 Verbs: []string{
484 "failed harder than", "crashed like", "died faster than",
485 "broke worse than", "destroyed", "ruined", "devastated",
486 "collapsed like", "imploded faster than", "crumbled like",
487 "exploded worse than", "failed as badly as", "bombed harder than",
488 },
489 Objects: []string{
490 "your career prospects", "a burning dumpster fire", "your hopes and dreams",
491 "your last relationship", "the Hindenburg", "your job security",
492 "a house of cards", "your professional reputation", "your confidence",
493 "your self-esteem", "a Jenga tower", "your sanity", "reality itself",
494 "everyone's expectations", "your manager's patience",
495 },
496 Punchlines: []string{
497 "combined", "on steroids", "in production", "multiplied",
498 "simultaneously", "at scale", "under load", "in the worst way",
499 "and then some", "by orders of magnitude", "exponentially",
500 },
501 Intensifiers: []string{
502 "absolutely", "completely", "utterly", "monumentally",
503 "catastrophically", "spectacularly", "magnificently",
504 },
505 Comparisons: []string{
506 "than", "like", "worse than", "as much as", "more than",
507 },
508 }
509 }
510
511 // Initialize templates
512 func initializeTemplates() []string {
513 return []string{
514 "{command} failed: The {commandType} gods have rejected you.",
515 "Your {command} encountered a {error}: Fix yourself first.",
516 "{command} crashed harder than your career in {project}.",
517 "Error in {command}: Expected competence, found disaster.",
518 "Your {commandType} skills are as broken as this {command}.",
519 "{command} failed. Your {project} project failed. You failed.",
520 "The {error} error is just {command} protecting itself from you.",
521 "Your {command} in {project} is a monument to failure.",
522 }
523 }
524
525 func formatFloat(f float64) string {
526 // Simple float formatter (0.00 format)
527 intPart := int(f * 100)
528 whole := intPart / 100
529 frac := intPart % 100
530
531 result := intToStr(whole) + "."
532 if frac < 10 {
533 result += "0"
534 }
535 result += intToStr(frac)
536
537 return result
538 }
539