Sab0h AI Memory Core
AI-powered NPCs with memory, personality, accents & speech for FiveM
NPCs observe players, remember their appearance, respond to conversations with unique personalities, and speak with neural Text-to-Speech. Each NPC has its own memory, voice, mood, and accent.
Features
| Feature | Description |
|---|---|
| AI Witness Memory | NPCs observe suspects and generate natural descriptions |
| Memory Decay | Memories fade over time (fresh → partial → weak → forgotten) |
| Neural TTS | 56+ Cartesia Sonic 3.5 voices with 3D Spatial Audio |
| AI Conversations | Players can chat with any NPC — AI responds context-aware |
| Multi-Personality | 22 personality types with 2–5 description variants each |
| Accent & Slang | 18 TTS accent languages, 27 slang languages mixed into speech |
| Mood System | 25 random moods influencing NPC tone and behavior |
| Ambient Chatter | NPCs comment randomly when players walk by |
| Reputation System | NPCs remember how they feel about each player (-100 to +100) |
| Persistent Names/Voices | Each NPC keeps its name and voice across sessions |
| Thinking & Reasoning | NPCs build internal reasoning before responding |
| Vision AI | Optional screenshot-based scene analysis (qwen2.5vl:7b) |
| Multi-Framework | QBX, QBCore, ESX, or Standalone |
| Multi-Language | EN, DE, FR, ES, PT, IT, NL, PL, TR, RU |
Requirements
| Dependency | Required | Purpose |
|---|---|---|
| ox_lib | ✅ | Utility library |
| oxmysql | ✅ | Database driver |
| screenshot-basic | ✅ | Vision AI captures |
| MariaDB / MySQL | ✅ | Data storage |
| sab0h_ai_server | ✅ | Node.js AI middleware (Ollama + Cartesia TTS) |
| Ollama | ✅ | Local LLM inference |
Ollama Models:
llama3.1:8b— Text generation (conversations, descriptions)qwen2.5vl:7b— Vision analysis (optional)
Installation
- Place
ai_memoryin your server's resources folder - Import
sql/install.sqlinto your database - Configure
config.lua(language, TTS, ambient, etc.) - Add to your
server.cfg:cfgensure screenshot-basic ensure ai_memory - Ensure
sab0h_ai_serveris running (default port 3847) - Restart your server
Configuration
All user-configurable options are in config.lua. Core logic is server-side (escrowed).
General
Config.Debug = false
Config.Framework = 'auto' -- 'auto' | 'qbx' | 'qb' | 'esx' | 'standalone'
Config.Locale = 'en' -- 'en' | 'de' | 'fr' | 'es' | 'pt' | 'nl' | 'it' | 'pl' | 'tr' | 'ru'AI
Config.AI = {
enabled = true, -- Master switch for AI system
}Memory Decay
Config.Memory = {
freshDuration = 15, -- Minutes: full detail recall
partialDuration = 45, -- Minutes: some details lost
weakDuration = 90, -- Minutes: only vague impressions
forgetDuration = 180, -- Minutes: memory deleted
decayInterval = 60000, -- ms: decay check interval
maxPerNpc = 5, -- Max memories per NPC
}Witness Questioning
Config.Questioning = {
enabled = true,
cooldown = 30, -- Seconds between questioning attempts
maxDistance = 5.0, -- Max distance (meters) to NPC
requireJob = false, -- true = only allowedJobs can question
allowedJobs = { 'police', 'sheriff', 'trooper', 'bcso', 'sasp', 'ranger', 'fib', 'doj' },
}Text-to-Speech
Config.TTS = {
enabled = true,
volume = 0.5, -- Base volume (0.0 - 1.0)
maxDistance = 15.0, -- Audio falloff in meters
animation = true, -- NPC plays talking animation
mode = 'both', -- 'tts' = audio only | 'bubble' = text only | 'both'
}Ambient Chatter
Config.Ambient = {
enabled = true,
triggerDistance = 8.0, -- Meters: NPC speaks when player in range
cooldownPerNpc = 120, -- Seconds: same NPC won't repeat
globalCooldown = 15, -- Seconds: minimum between ambient speech
chance = 25, -- Percent chance per check
checkInterval = 3000, -- ms: how often to check
maxConcurrent = 1, -- Max simultaneously speaking NPCs
}Conversation
Config.Conversation = {
memoryTTL = 600, -- Seconds: how long NPC remembers conversation
maxHistory = 20, -- Max messages per conversation
}NPC Moods
25 possible moods randomly assigned on first contact, persisting for memoryTTL seconds. Moods influence NPC tone, word choice, and behavior.
You can add, remove, or rename moods freely:
Config.NpcMoods = {
'happy', 'grumpy', 'stressed', 'relaxed', 'bored',
'nervous', 'cheerful', 'annoyed', 'thoughtful', 'tired',
'suspicious', 'curious', 'angry', 'indifferent', 'excited',
'paranoid', 'hungover', 'euphoric', 'sarcastic', 'melancholic',
'reckless', 'absent-minded', 'desperate', 'narcissistic', 'nostalgic',
}Customizable: Add any mood string you want. The AI will adapt its tone accordingly. No limit on how many moods you define.
Accents & Slang
The accent pool controls NPC accent distribution. Each entry is a language code — more entries = higher spawn chance.
Config.Accents = {
pool = {
'de', 'de', 'de', 'de', 'de', 'de', -- 30% native
'en', 'en', -- 10% English
'tr', 'tr', -- 10% Turkish
'ru', 'es', 'fr', 'it', 'pl', -- 5% each
'nl', 'pt', 'ar', -- 5% each
'ja', 'sv', 'hi', 'zh', 'ko', -- 2.5% each
'ro', 'el', -- 2.5% each
},
}18 TTS accent languages: DE, EN, TR, RU, ES, FR, IT, PL, NL, PT, AR, JA, SV, HI, ZH, KO, RO, EL
27 slang languages: All above + SR, UK, VI, CS, HU, AL, BG, PH, TH
When Config.Slang.enabled = true, NPCs mix native-language words into their speech (e.g., a Turkish NPC says "Wallah bro I swear on everything").
Config.Slang = {
enabled = true, -- true = NPCs use accent-based slang | false = clean speech
}What You Can Customize
A complete overview of everything you can change in config.lua:
| Setting | What It Does | How to Change |
|---|---|---|
Config.Debug | Debug prints in console | Set true or false |
Config.Framework | Framework detection | 'auto', 'qbx', 'qb', 'esx', 'standalone' |
Config.Locale | Language of all NPC speech & prompts | 'en', 'de', 'fr', 'es', 'pt', 'nl', 'it', 'pl', 'tr', 'ru' |
Config.AI.enabled | Master AI on/off | true / false |
Config.Memory.freshDuration | Minutes with full detail | Any number (default: 15) |
Config.Memory.partialDuration | Minutes until partial fade | Any number (default: 45) |
Config.Memory.weakDuration | Minutes until vague | Any number (default: 90) |
Config.Memory.forgetDuration | Minutes until deleted | Any number (default: 180) |
Config.Memory.decayInterval | Decay check rate (ms) | Any number (default: 60000) |
Config.Memory.maxPerNpc | Max memories per NPC | Any number (default: 5) |
Config.Questioning.enabled | Witness questioning on/off | true / false |
Config.Questioning.cooldown | Seconds between questions | Any number (default: 30) |
Config.Questioning.maxDistance | Range to NPC in meters | Any number (default: 5.0) |
Config.Questioning.requireJob | Job restriction | true = only allowedJobs / false = everyone |
Config.Questioning.allowedJobs | Which jobs can question | Add/remove job strings |
Config.TTS.enabled | Text-to-Speech on/off | true / false |
Config.TTS.volume | Base volume | 0.0 to 1.0 (default: 0.5) |
Config.TTS.maxDistance | Audio range in meters | Any number (default: 15.0) |
Config.TTS.animation | Talking animation | true / false |
Config.TTS.mode | Output mode | 'tts' / 'bubble' / 'both' |
Config.Ambient.enabled | Ambient chatter on/off | true / false |
Config.Ambient.triggerDistance | Trigger range in meters | Any number (default: 8.0) |
Config.Ambient.cooldownPerNpc | Same-NPC repeat cooldown (sec) | Any number (default: 120) |
Config.Ambient.globalCooldown | Min time between any chatter (sec) | Any number (default: 15) |
Config.Ambient.chance | Trigger chance in percent | 0–100 (default: 25) |
Config.Ambient.checkInterval | Check frequency (ms) | Any number (default: 3000) |
Config.Ambient.maxConcurrent | Max NPCs talking at once | Any number (default: 1) |
Config.Conversation.memoryTTL | How long NPC remembers chat (sec) | Any number (default: 600) |
Config.Conversation.maxHistory | Max messages per conversation | Any number (default: 20) |
Config.NpcMoods | List of possible NPC moods | Add/remove any mood strings |
Config.Accents.pool | Accent distribution | Add/remove language codes, duplicate for higher chance |
Config.Slang.enabled | Slang mixing on/off | true / false |
Accent Pool — How to Adjust
The accent pool is a weighted list. The more often a code appears, the higher the chance:
-- Example: 50% native, 25% Turkish, 25% Russian
Config.Accents = {
pool = { 'en', 'en', 'tr', 'ru' },
}Available accent codes:
| Code | Language | Code | Language | Code | Language |
|---|---|---|---|---|---|
de | German | nl | Dutch | ja | Japanese |
en | English | pt | Portuguese | sv | Swedish |
tr | Turkish | ar | Arabic | hi | Hindi |
ru | Russian | zh | Chinese | ko | Korean |
es | Spanish | ko | Korean | ro | Romanian |
fr | French | ro | Romanian | el | Greek |
it | Italian | el | Greek | ||
pl | Polish |
Moods — How to Adjust
Add any mood string. The AI interprets it freely:
Config.NpcMoods = {
'friendly', 'hostile', 'drunk', 'flirty', 'scared',
'high', 'professional', 'crazy', 'depressed',
-- Add as many as you want
}Questioning Jobs — How to Adjust
Config.Questioning = {
requireJob = true, -- Set to true to restrict
allowedJobs = {
'police', 'sheriff', 'trooper', 'bcso',
'sasp', 'ranger', 'fib', 'doj',
-- Add your custom law enforcement jobs here
},
}Per-NPC Customization (via Exports)
Beyond config.lua, you can customize individual NPCs at runtime:
| Export | What It Does |
|---|---|
SetNpcName(npcId, name) | Override auto-generated name |
SetNpcInfo(npcId, text) | Give NPC custom knowledge/backstory |
SetReputation(npcId, src, val) | Set starting reputation (-100 to +100) |
RegisterNpc(ped, key) | Make NPC persistent across sessions |
Interaction
The system auto-detects your target system:
| System | Behavior |
|---|---|
ox_target | Global ped option "Talk" |
qb-target | Global ped option "Talk" |
| None | E-key fallback with proximity detection |
Exports
Client Exports
-- Register NPC (for persistent tracking)
exports['ai_memory']:RegisterNpc(ped, 'shop_owner_grove')
-- Unregister NPC
exports['ai_memory']:UnregisterNpc(ped)
-- Get NPC ID (registered key or auto-generated)
local id = exports['ai_memory']:GetNpcId(ped)
-- Make witness observe (clothing, props, vehicle, weapon)
exports['ai_memory']:CaptureAppearance(witnessPed, suspectPed, npcId)
-- Start witness questioning
exports['ai_memory']:QuestionWitness(npcPed)
-- Audio playback
exports['ai_memory']:PlayNpcAudio(url, coords, options)
exports['ai_memory']:StopNpcAudio(npcId)
exports['ai_memory']:StopAllAudio()
exports['ai_memory']:SetSpeakerPed(ped)Server Exports — Memory
-- Store observation
local memId = exports['ai_memory']:StoreObservation(npcId, observation, confidence)
-- Get all NPC memories
local memories = exports['ai_memory']:GetMemories(npcId)
-- Generate witness statement
local result = exports['ai_memory']:GetStatement(npcId)
-- Returns: { statement = "...", reliability = "high"|"medium"|"low" }
-- Clear memories
exports['ai_memory']:ClearMemories(npcId)
-- Force immediate decay
local stats = exports['ai_memory']:ForceDecay()
-- Returns: { processed, transitioned, forgotten }Server Exports — Reputation
-- Get reputation
local rep, level, interactions = exports['ai_memory']:GetReputation(npcId, source)
-- level: 'hostile' | 'unfriendly' | 'neutral' | 'friendly' | 'allied'
-- Modify reputation (+/- points)
local newRep, newLevel = exports['ai_memory']:ModifyReputation(npcId, source, amount, event)
-- Set reputation to exact value (-100 to +100)
exports['ai_memory']:SetReputation(npcId, source, value, event)
-- Reset reputation (single player)
exports['ai_memory']:ResetReputation(npcId, source)
-- Reset all reputations for an NPC
exports['ai_memory']:ResetNpcReputation(npcId)
-- List all reputations for an NPC (admin/debug)
local all = exports['ai_memory']:GetAllReputations(npcId)Server Exports — Speech (TTS)
-- NPC says specific text
local success, audioUrl, duration = exports['ai_memory']:NpcSay(
npcId, "Hello!", coords, 'npc_en', { isFemale = false }
)
-- NPC says reputation-based line
local success, text, level = exports['ai_memory']:NpcSayByReputation(
npcId, source, 'greeting', coords
)
-- NPC says AI-generated text based on context
local success, text = exports['ai_memory']:NpcSayAI(
npcId, source, "Player was carrying crates nearby", coords
)
-- Trigger reputation event (changes rep + NPC reacts)
local success, newRep, text = exports['ai_memory']:NpcReact(
npcId, source, 'robbery', coords
)Server Exports — NPC Info
-- Set custom knowledge for NPC (used in conversations)
exports['ai_memory']:SetNpcInfo(npcId, "I'm the owner of Benny's auto shop.")
-- Get custom info
local info = exports['ai_memory']:GetNpcInfo(npcId)
-- Remove custom info
exports['ai_memory']:RemoveNpcInfo(npcId)
-- Set custom name (overrides auto-generated name)
exports['ai_memory']:SetNpcName(npcId, 'Benny')
-- Get current NPC name
local name = exports['ai_memory']:GetNpcName(npcId)Usage Examples
NPCs witness a robbery
-- Client: nearby NPCs become witnesses
local playerPed = PlayerPedId()
for _, npcPed in ipairs(nearbyPeds) do
if HasClearLineOfSight(npcPed, playerPed) then
exports['ai_memory']:CaptureAppearance(npcPed, playerPed)
end
endPolice questions a witness
exports['ai_memory']:QuestionWitness(targetPed)
-- NPC speaks their memory via TTS with spatial audioCustom NPC for a shop script
-- Server-side on spawn:
exports['ai_memory']:SetNpcName(npcId, 'Carlos')
exports['ai_memory']:SetNpcInfo(npcId, "You are Carlos, owner of the 24/7 in Strawberry. You complain about shoplifters.")
-- Players can now talk to Carlos and he responds in characterReputation-based reaction
-- Server: player robs NPC
exports['ai_memory']:NpcReact(npcId, source, 'robbery', npcCoords)
-- → Reputation drops, NPC says aggressive line via TTSTTS Voices
Voices are automatically assigned per NPC-ID hash and remain persistent. The system uses Cartesia Sonic 3.5 with 56+ voice roles across 18 languages.
Each accent has 2 male + 2 female voices (some have 4+ per gender). Voice assignment is deterministic — the same NPC always gets the same voice.
Override per export: NpcSay(npcId, text, coords, 'npc_en')
Memory Stages
| Stage | Duration | Description |
|---|---|---|
fresh | 0–15 min | Full details, high confidence |
partial | 15–45 min | Some details lost |
weak | 45–90 min | Only vague impressions |
forgotten | 90+ min | Memory deleted |
Confidence is additionally affected by: distance, darkness, weather, masks.
Personality System
Each GTA ped model is mapped to one or more personality types. On first interaction, a personality is randomly selected and cached for the NPC's lifetime.
22 personality types: business, criminal, friendly, aggressive, shy, arrogant, street, party, intellectual, paranoid, religious, hipster, athlete, artist, conspiracy, stoner, veteran, tourist, homeless, rich, corrupt, romantic
Each type has 2–5 description variants ensuring unique behavior even among same-type NPCs.
Database
Two tables (auto-created via sql/install.sql):
| Table | Purpose |
|---|---|
npc_memories | Observations, AI descriptions, decay stage, locale |
npc_reputation | Reputation per NPC-player pair (-100 to +100) |
File Structure
ai_memory/
├── config.lua -- Configuration (user settings only)
├── fxmanifest.lua
├── sql/
│ └── install.sql -- Database schema
├── bridge/
│ ├── shared.lua -- Framework detection
│ ├── server.lua -- Server bridge (GetPlayer, IsOfficer)
│ └── client.lua -- Client bridge
├── server/
│ ├── main.lua -- Prompt building, personality, conversation handler
│ ├── accents.lua -- Accent origin/slang data (27 languages)
│ ├── store.lua -- Database CRUD + cache
│ ├── witness.lua -- Witness statement generation
│ ├── ai.lua -- AI description (Vision + Text)
│ ├── reputation.lua -- Reputation system
│ ├── speech.lua -- NPC dialogue
│ ├── decay.lua -- Memory degradation
│ ├── tts.lua -- TTS generation + cache
│ ├── tts_broadcast.lua -- Audio broadcast to players
│ └── ollama.lua -- AI backend connection
├── client/
│ ├── main.lua -- NPC registration, observations, questioning
│ ├── capture.lua -- Appearance capture (clothing, props, screenshot)
│ ├── ambient.lua -- Ambient chatter + NPC interaction
│ └── tts.lua -- Audio playback, 3D spatial audio
└── nui/
└── index.html -- Chat UI + Web Audio APILicense
Proprietary — All rights reserved. Escrow-protected server code.
Made by Sab0h