Skip to Content
Odoo Menu
  • Sign in
  • Try it free
  • Apps
    Finance
    • Accounting
    • Invoicing
    • Expenses
    • Spreadsheet (BI)
    • Documents
    • Sign
    Sales
    • CRM
    • Sales
    • POS Shop
    • POS Restaurant
    • Subscriptions
    • Rental
    Websites
    • Website Builder
    • eCommerce
    • Blog
    • Forum
    • Live Chat
    • eLearning
    Supply Chain
    • Inventory
    • Manufacturing
    • PLM
    • Purchase
    • Maintenance
    • Quality
    Human Resources
    • Employees
    • Recruitment
    • Time Off
    • Appraisals
    • Referrals
    • Fleet
    Marketing
    • Social Marketing
    • Email Marketing
    • SMS Marketing
    • Events
    • Marketing Automation
    • Surveys
    Services
    • Project
    • Timesheets
    • Field Service
    • Helpdesk
    • Planning
    • Appointments
    Productivity
    • Discuss
    • Approvals
    • IoT
    • VoIP
    • Knowledge
    • WhatsApp
    Third party apps Odoo Studio Odoo Cloud Platform
  • Industries
    Retail
    • Book Store
    • Clothing Store
    • Furniture Store
    • Grocery Store
    • Hardware Store
    • Toy Store
    Food & Hospitality
    • Bar and Pub
    • Restaurant
    • Fast Food
    • Guest House
    • Beverage Distributor
    • Hotel
    Real Estate
    • Real Estate Agency
    • Architecture Firm
    • Construction
    • Property Management
    • Gardening
    • Property Owner Association
    Consulting
    • Accounting Firm
    • Odoo Partner
    • Marketing Agency
    • Law firm
    • Talent Acquisition
    • Audit & Certification
    Manufacturing
    • Textile
    • Metal
    • Furnitures
    • Food
    • Brewery
    • Corporate Gifts
    Health & Fitness
    • Sports Club
    • Eyewear Store
    • Fitness Center
    • Wellness Practitioners
    • Pharmacy
    • Hair Salon
    Trades
    • Handyman
    • IT Hardware & Support
    • Solar Energy Systems
    • Shoe Maker
    • Cleaning Services
    • HVAC Services
    Others
    • Nonprofit Organization
    • Environmental Agency
    • Billboard Rental
    • Photography
    • Bike Leasing
    • Software Reseller
    Browse all Industries
  • Community
    Learn
    • Tutorials
    • Documentation
    • Certifications
    • Training
    • Blog
    • Podcast
    Empower Education
    • Education Program
    • Scale Up! Business Game
    • Visit Odoo
    Get the Software
    • Download
    • Compare Editions
    • Releases
    Collaborate
    • Github
    • Forum
    • Events
    • Translations
    • Become a Partner
    • Services for Partners
    • Register your Accounting Firm
    Get Services
    • Find a Partner
    • Find an Accountant
      • Get a Tailored Demo
    • Implementation Services
    • Customer References
    • Support
    • Upgrades
    Github Youtube Twitter Linkedin Instagram Facebook Spotify
    +32 2 290 34 90
    • Get a Tailored Demo
  • Pricing
  • Help
  1. APPS
  2. AI
  3. Local Llama OdooBot Chat Companion v 18.0
  4. Sales Conditions FAQ

Local Llama OdooBot Chat Companion

by okkype@gmail.com https://linkedin.com/in/okky-permana-sihipo
Odoo

$ 30.00

v 18.0 Third Party
Apps purchases are linked to your Odoo account, please sign in or sign up first.
Availability
Odoo Online
Odoo.sh
On Premise
Odoo Apps Dependencies Discuss (mail)
Lines of code 458
Technical Name llama_cpp_ok
LicenseOPL-1
Websitehttps://linkedin.com/in/okky-permana-sihipo
You bought this module and need support? Click here!
Availability
Odoo Online
Odoo.sh
On Premise
Odoo Apps Dependencies Discuss (mail)
Lines of code 458
Technical Name llama_cpp_ok
LicenseOPL-1
Websitehttps://linkedin.com/in/okky-permana-sihipo
  • Description
  • License
Materi Pelatihan Developer

Materi Pelatihan Odoo 18 & Offline Llama

Panduan Teoretis & Praktis Terintegrasi: Pengaturan Lingkungan Virtual Multi-OS, Caching AI Lokal, Sandbox Abstract Syntax Tree (AST), dan Automated Testing

🛡️ 100% Offline AI

Inference lokal dengan model Qwen2.5-Coder GGUF yang berjalan di komputer lokal tanpa membutuhkan API key luar atau transfer data internet.

⚙️ AST Sandbox

Mencegah eksekusi Python berbahaya di server dengan menganalisis syntax tree sebelum kode ORM dievaluasi.

🧪 ORM Access

Memberikan kemampuan bagi AI OdooBot untuk memproses recordset Odoo secara dinamis berbasis masukan bahasa alami.

Daftar Isi Materi Pelatihan

Bab 1: Persiapan Lingkungan Pengembangan & Penyediaan Model GGUF Offline
  • 1.1 Pembuatan & Manajemen Conda Environment Odoo 18 di Berbagai OS (Linux, macOS, Windows)
  • 1.2 Kompilasi & Instalasi Library llama-cpp-python Lintas Sistem Operasi (GCC vs Clang Metal vs MSVC CUDA)
  • 1.3 Pengelolaan Direktori Penyimpanan Model GGUF Lokal & Portabilitas Path
Bab 2: Desain Model Konfigurasi & Warisan Tampilan Odoo Settings
  • 2.1 Model Transient res.config.settings & Parameter Sistem
  • 2.2 Warisan Tampilan (Inheritance View XML) di General Settings
  • 2.3 Registrasi Default System Parameters via XML Noupdate
Bab 3: Inti Mesin Inferensi Llama & Proteksi Sandbox AST
  • 3.1 Caching Memori AI Terisolasi & Garbage Collection
  • 3.2 Validasi Keamanan Kode Python menggunakan Abstract Syntax Tree (AST)
  • 3.3 Eksekusi Sandboxed Python & Parser Output HTML Lintas OS
Bab 4: Intersepsi Pesan Discuss App & Alur Pengalihan OdooBot
  • 4.1 Override Pipeline _get_answer Pada Model mail.bot
  • 4.2 Pembersihan Tag HTML & Ekstraksi Metadata Sesi Pengguna
  • 4.3 Logika Bypass untuk Slash Commands, Canned Responses, & Onboarding Tours
Bab 5: Strategi Pengujian Otomatis (Automated Unit Testing) Tanpa Overhead Resource
  • 5.1 Teknik Isolasi Model & Mocking Inferensi LLM Lokal
  • 5.2 Desain Kasus Uji (Test Cases) untuk AST Security & Sandbox Runtime
  • 5.3 Prosedur Eksekusi Automated Test Suite Lintas Platform (Bash vs PowerShell)

Detail Bab & Sub-Bab Pembelajaran

Bab 1

Persiapan Lingkungan Pengembangan & Penyediaan Model GGUF Offline

Fokus Bab: Menyiapkan compiler C++ di sistem lokal, membangun conda virtual environment yang kompatibel dengan Odoo 18, mengompilasi python binding di berbagai OS (Linux, macOS, Windows), dan mengelola path model secara modular.

1.1 Pembuatan & Manajemen Conda Environment Odoo 18 di Berbagai OS

Odoo 18 menggunakan berbagai library biner Python yang spesifik. Penggunaan Conda memastikan dependensi binary C/C++ tidak bercampur dengan interpreter Python bawaan OS. Berikut adalah panduan setup environment untuk masing-masing sistem operasi:

Linux & macOS

Buka terminal Unix Anda, buat environment baru berbasis Python 3.10, dan aktifkan:

bash
conda create -n odoo-18 python=3.10 -y
conda activate odoo-18

Windows

Buka Anaconda Prompt atau Miniconda Prompt dan ketikkan:

cmd
conda create -n odoo-18 python=3.10 -y
conda activate odoo-18

1.2 Kompilasi & Instalasi Library llama-cpp-python Lintas Sistem Operasi

Library llama-cpp-python memanggil biner native C++ dari `llama.cpp`. Konfigurasi dan compiler yang dibutuhkan berbeda secara signifikan di setiap sistem operasi:

Linux

Linux menggunakan compiler GCC / G++. Pastikan paket build-essential terpasang.

bash
# Untuk standard CPU
pip install llama-cpp-python --no-cache-dir

# Akselerasi GPU CUDA (Nvidia)
CUDACXX=/usr/local/cuda/bin/nvcc CMAKE_ARGS="-GGML_CUDA=on" FORCE_CMAKE=1 pip install llama-cpp-python --no-cache-dir

macOS

macOS menggunakan compiler Clang dari Xcode CLI. Pada Apple Silicon, biner dikompilasi menggunakan Metal API.

bash
# Instal Xcode CLI Tools terlebih dahulu di Terminal
xcode-select --install

# Instalasi dengan akselerasi GPU Metal (Native pada Apple Silicon)
CMAKE_ARGS="-GGML_METAL=on" FORCE_CMAKE=1 pip install llama-cpp-python --no-cache-dir

Windows

Windows membutuhkan compiler C++ Visual Studio (MSVC).

powershell
# Untuk standard CPU di Windows
pip install llama-cpp-python --no-cache-dir

# Akselerasi GPU CUDA di Windows (Jalankan di PowerShell Administrator)
$env:CMAKE_ARGS="-GGML_CUDA=on"
$env:FORCE_CMAKE="1"
pip install llama-cpp-python --no-cache-dir

1.3 Pengelolaan Direktori Penyimpanan Model GGUF Lokal & Portabilitas Path

Agar modul portabel, kita menyimpan model biner di sub-direktori internal addon kita yaitu models_local/.

Pilihan Ukuran Model & Batasan RAM/VRAM:
Modul ini menggunakan Qwen 2.5 Coder 1.5B Instruct (Q4_K_M) secara offline. Model ini hanya membutuhkan ~1.2 GB RAM/VRAM, sangat portabel dan aman dijalankan pada server lokal berspesifikasi standar.
Langkah Kerja Lintas Platform Membuat folder biner & menaruh model GGUF

Unix (Linux & macOS):

bash
mkdir -p llama_cpp_ok/models_local/
mv qwen2.5-coder-1.5b-instruct-q4_k_m.gguf llama_cpp_ok/models_local/

Windows (PowerShell):

powershell
New-Item -ItemType Directory -Force -Path "llama_cpp_ok\models_local"
Move-Item -Path "qwen2.5-coder-1.5b-instruct-q4_k_m.gguf" -Destination "llama_cpp_ok\models_local\"
Bab 2

Desain Model Konfigurasi & Warisan Tampilan Odoo Settings

Fokus Bab: Mempelajari arsitektur model transient Odoo, cara memetakan isian settings ke database parameter sistem (ir.config_parameter), memodifikasi tampilan General Settings XML, dan menulis nilai default noupdate.

2.1 Model Transient res.config.settings & Parameter Sistem

Odoo mengelola pengaturan umum menggunakan kelas turunan res.config.settings yang bertipe TransientModel. Nilai input yang dimasukkan user disimpan secara permanen di database Odoo pada tabel ir.config_parameter.

Lokasi file: models/res_config_settings.py

python
class ResConfigSettings(models.TransientModel):
    _inherit = "res.config.settings"

    ai_odoobot_enabled = fields.Selection(
        selection=[("disabled", "Disabled"), ("enabled", "Enabled")],
        string="AI OdooBot Mode",
        config_parameter="llama_cpp_ok.ai_odoobot_enabled",
        default="disabled",
    )
    ai_model_path = fields.Char(
        string="AI GGUF Model Path",
        config_parameter="llama_cpp_ok.ai_model_path",
        default="models_local/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf",
    )
    ai_thread_count = fields.Integer(
        string="AI CPU Threads",
        config_parameter="llama_cpp_ok.ai_thread_count",
        default="4",
    )
    ai_context_window = fields.Integer(
        string="AI Context Window Size",
        config_parameter="llama_cpp_ok.ai_context_window",
        default="4096",
    )
    ai_model_blacklist = fields.Char(
        string="AI Model Blacklist",
        config_parameter="llama_cpp_ok.ai_model_blacklist",
        default="res.users,res.groups,ir.config_parameter,ir.actions.server,ir.cron",
    )

2.2 Warisan Tampilan (Inheritance View XML) di General Settings

Di Odoo 18, struktur form views General Settings menggunakan pembungkus tag baru, yaitu `` dan `` (menggantikan struktur sheet/div lama dari Odoo 17). Kita melakukan inheritance ke view `mail.res_config_settings_view_form` menggunakan XPath expressions.

Lokasi file: views/res_config_settings_views.xml

xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="res_config_settings_view_form" model="ir.ui.view">
        <field name="name">res.config.settings.view.form.inherit.llama</field>
        <field name="model">res.config.settings</field>
        <field name="inherit_id" ref="mail.res_config_settings_view_form"/>
        <field name="arch" type="xml">
            <xpath expr="//form" position="inside">
                <app string="Discuss" name="llama_cpp_ok">
                    <block title="Offline Llama AI OdooBot">
                        <setting title="AI Settings" string="Configure AI OdooBot Server">
                            <div class="content-group">
                                <field name="ai_odoobot_enabled"/>
                                <field name="ai_model_path" class="w-100" style="width: 100%;"/>
                                <field name="ai_thread_count"/>
                                <field name="ai_context_window"/>
                                <field name="ai_model_blacklist" style="width: 100%;"/>
                            </div>
                        </setting>
                    </block>
                </app>
            </xpath>
        </field>
    </record>
</odoo>

2.3 Registrasi Default System Parameters via XML Noupdate

Lokasi file: data/llama_config_data.xml

xml
<record id="param_ai_model_path" model="ir.config_parameter">
    <field name="key">llama_cpp_ok.ai_model_path</field>
    <field name="value">models_local/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf</field>
</record>
Bab 3

Inti Mesin Inferensi Llama & Proteksi Sandbox AST

Fokus Bab: Mengelola alokasi memori C++ native, mendesain caching singleton yang efisien untuk meminimalkan beban RAM server, merancang sistem keamanan tingkat tinggi menggunakan penelusuran Abstract Syntax Tree (AST), serta mengeksekusi kode Odoo ORM secara terisolasi lintas platform.

3.1 Caching Memori AI Terisolasi & Garbage Collection

Dalam memproses model bahasa besar (LLM) secara lokal, pemuatan GGUF ditangani oleh C++ library (llama.cpp) melalui binding `ctypes`. Ini membagi alokasi memori menjadi dua wilayah:

Arsitektur Pembagian Memori Pemisahan Heap Memory Llama
  • 1. Python Managed Heap (VM RAM): Mengelola wrapper class Python, objek config transient settings, metadata pemanggilan thread, dan pointer biner ctypes.
  • 2. Native C++ Heap (Physical RAM/VRAM): Menyimpan bobot tensor model biner yang di-load dan alokasi array KV (Key-Value) Cache untuk penampung konteks percakapan.

Jika kita menginisialisasi ulang objek Llama pada setiap request pesan, alokasi memori biner C++ lama tidak akan terhapus secara otomatis. Kita harus menggunakan Caching Singleton.

Alur Caching Singleton & GC
[Request Input Chat Baru]
        │
        ▼
[Cek Cache _LLM_CACHE] ──(Ada)──► [Gunakan Instance LLM C++ Aktif]
        │
     (Tidak)
        ▼
[Hapus Pointer Model Lama di Cache]
        │
        ▼
[del _LLM_CACHE[key]] ──► Memicu ctypes destructor (llama_free_model)
        │
        ▼
[gc.collect()] ──► Bersihkan sisa referensi Python Heap
        │
        ▼
[Inisialisasi Model Baru] ──► Alokasikan RAM/VRAM Fisik Bersih
Mekanisme Siklus Hidup Model C++ & Garbage Collection:
Untuk membersihkan cache model lama secara total saat konfigurasi diubah, kita harus menghapus pointer referensi menggunakan kata kunci del, memutus keterikatan objek dari dictionary cache global _LLM_CACHE, lalu secara eksplisit memanggil Garbage Collector Python melalui gc.collect().

Panduan Sintaksis & Inisiasi API llama-cpp-python:

python
from llama_cpp import Llama

# 1. Inisialisasi Model Offline
llm = Llama(
    model_path="models_local/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf",
    n_ctx=2048,          # Context Window Size
    n_threads=4,         # Core CPU fisik yang digunakan
    n_gpu_layers=0,      # Layers yang ditaruh ke VRAM (0 jika CPU-only)
    verbose=False        # Mematikan logs biner C++ verbose
)

# 2. Mengirimkan Instruksi via API Chat Completion
response = llm.create_chat_completion(
    messages=[
        {"role": "system", "content": "Kamu adalah OdooBot..."},
        {"role": "user", "content": "hitung 50 dikali 2"}
    ],
    max_tokens=1024,      # Batas token output generator
    temperature=0.1,      # Nilai deterministik rendah untuk coding
    top_p=0.9,            # Threshold sampling probabilitas
    stream=False          # Membaca output instan secara penuh
)

# 3. Ekstraksi Hasil Output Teks
output_text = response["choices"][0]["message"]["content"].strip()

3.2 Validasi Keamanan Kode Python menggunakan Abstract Syntax Tree (AST)

OdooBot AI dibekali kemampuan menerjemahkan perintah bahasa alami menjadi kode program ORM Odoo. Sebagai pertahanan utama, modul llama_cpp_ok mengimplementasikan Abstract Syntax Tree (AST) Security Sandbox.

Algoritma Deteksi Node AST Kode Pemeriksaan Struktur Node (ast.walk)
python
for node in ast.walk(tree):
    # 1. Blokir Import secara total
    if isinstance(node, (ast.Import, ast.ImportFrom)):
        raise ValueError("Import statements are strictly prohibited.")

    # 2. Blokir Akses Atribut Dunder (Double Underscore)
    if isinstance(node, ast.Attribute):
        if node.attr.startswith("__") or node.attr.endswith("__"):
            raise ValueError("Double underscore attribute access is blocked.")
        
        # Blokir akses module namespace langsung
        if isinstance(node.value, ast.Name) and node.value.id in forbidden_modules:
            raise ValueError("Direct module namespace access is blocked.")

    # 3. Blokir Pemanggilan Fungsi Built-in Berbahaya
    if isinstance(node, ast.Call):
        func = node.func
        if isinstance(func, ast.Name) and func.id in forbidden_builtins:
            raise ValueError("Call to dangerous builtin function is prohibited.")
Tipe Node AST Status Keamanan Motif & Penjelasan Perlindungan
ast.Import / ast.ImportFrom BLOCKED Mencegah pemuatan modul OS bawaan seperti os untuk menghentikan akses ke shell server.
ast.Attribute (Dunder "__") BLOCKED Menolak akses ke atribut internal (seperti __subclasses__) yang biasa digunakan dalam bypass sandbox.
ast.Call (Dangerous Builtins) BLOCKED Memblokir pemanggilan fungsi global bawaan berbahaya seperti eval() atau exec().
ast.Assign / ast.BinOp ALLOWED Mengizinkan deklarasi variabel kalkulasi standar dan operasi aritmatika dasar.
ast.Call (Odoo ORM Search/Read) ALLOWED Mengizinkan pencarian database standar Odoo, namun melarang pemanggilan database jika target model di-blacklist.

Contoh Parsing AST Kode ORM:

ast representation
# Kode Input asli: result = env['res.partner'].search([])
# Di-parsing oleh AST Parser menjadi pohon objek berikut:
Module(
    body=[
        Assign(
            targets=[Name(id='result', ctx=Store())],
            value=Call(
                func=Attribute(
                    value=Subscript(
                        value=Name(id='env', ctx=Load()),
                        slice=Constant(value='res.partner')
                    ),
                    attr='search'
                ),
                args=[List(elts=[])]
            )
        )
    ]
)

3.3 Eksekusi Sandboxed Python & Parser Output HTML Lintas OS

Setelah string kode lolos dari validasi struktur pohon AST, kode tersebut dieksekusi di dalam lingkungan runtime Python yang terbatas (sandbox namespace):

python
globals_dict = {
    "env": self.env,
    "result": None
}
_logger.info("Local Llama Sandbox: Executing code...")
exec(code, globals_dict)
execution_result = globals_dict.get("result")

Untuk memastikan fungsionalitas ini berjalan di semua OS host, penentuan path file model harus diselesaikan secara dinamis. Modul ini menggunakan kode berikut untuk menggabungkan lokasi path secara independen terhadap sistem operasi (cross-platform):

python
# os.path secara otomatis menggunakan backslash (\) di Windows dan forward-slash (/) di Unix
addon_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
full_model_path = os.path.join(addon_dir, relative_model_path)
Bab 4

Intersepsi Pesan Discuss App & Alur Pengalihan OdooBot

Fokus Bab: Memodifikasi pipeline pesan Odoo Discuss, menangani chat direct message (DM) & mentions, melakukan sanitasi input string, dan menerapkan aturan fallback ke pipeline standar.

4.1 Override Pipeline _get_answer Pada Model mail.bot

Odoo menggunakan model mail.bot untuk mengendalikan logika interaksi otomatis OdooBot. Kita melakukan pewarisan (inheritance) bertipe AbstractModel pada model mail.bot guna memotong jalur pesan masuk dan mengalihkannya ke local LLM.

Lokasi file: models/mail_bot.py

python
class MailBot(models.AbstractModel):
    _inherit = "mail.bot"

    def _get_answer(self, record, body, values, command=False):
        odoobot_partner = self.env.ref("base.partner_root")
        is_dm = record.channel_type == "chat" and odoobot_partner in record.channel_member_ids.partner_id
        is_pinged = odoobot_partner.id in values.get("partner_ids", [])

        if not (is_dm or is_pinged):
            return False

        # Bypass commands & tour onboarding
        if command or body.startswith("/") or body.startswith(":"):
            return super(MailBot, self)._get_answer(record, body, values, command)

        if _("start the tour") in body.lower():
            return super(MailBot, self)._get_answer(record, body, values, command)

        try:
            clean_message = re.sub(r"<[^<]+?>", "", values.get("body", "")).replace("\xa0", " ").strip()
            if not clean_message:
                return super(MailBot, self)._get_answer(record, body, values, command)

            user_context = self._get_session_context(record, clean_message)
            ai_result = self.env["llama.engine"].generate_response(clean_message, user_context)

            # Konversi string HTML ke class Markup agar Odoo tidak meng-escape output
            if ai_result["type"] == "code":
                return Markup(ai_result["content"])
            elif ai_result["type"] == "response":
                return Markup(self._format_markdown_response(ai_result["content"]))
            elif ai_result["type"] == "error":
                return Markup(f'<p style="color: #dc3545;"><strong>Error:</strong> {ai_result["content"]}</p>')

        except Exception as e:
            _logger.error(f"Local Llama: Failed to generate response: {e}")

        return super(MailBot, self)._get_answer(record, body, values, command)

4.2 Pembersihan Tag HTML & Ekstraksi Metadata Sesi Pengguna

Pesan dari Discuss dikirim dalam format HTML terbungkus tag paragraph. Kita membersihkannya sebelum dikirimkan ke model GGUF demi menjaga kualitas inferensi.

python
    def _get_session_context(self, record, current_message):
        from datetime import date
        context = {
            "user_name": self.env.user.name,
            "user_login": self.env.user.login,
            "company_name": self.env.company.name,
            "today": str(date.today()),
            "history": self._get_chat_history(record, current_message)
        }
        return context

4.3 Parser Markdown ke HTML Kustom untuk Mengatasi Pembatasan Mail Sanitizer

Odoo Discuss menggunakan library mail.sanitizer untuk memfilter input teks obrolan demi mencegah celah keamanan Cross-Site Scripting (XSS). Namun, pembersihan ketat ini memotong beberapa sintaksis rendering Markdown mentah yang dihasilkan oleh LLM.

python
    def _format_markdown_response(self, text):
        if not text:
            return ""

        # 1. Konversi karakter HTML sensitif untuk menghentikan XSS
        escaped_text = html.escape(text)

        # 2. Format cetak tebal (**teks**) menjadi <strong>
        escaped_text = re.sub(r"\*\*([^*]+)\*\*", r"<strong>\1</strong>", escaped_text)
        
        # 3. Format cetak miring (*teks*) menjadi <em>
        escaped_text = re.sub(r"\*([^*]+)\*", r"<em>\1</em>", escaped_text)

        # 4. Format tag kode inline (`kode`) dengan inline CSS styling
        code_style = "background-color: #f8f9fa; color: #dc3545; padding: 2px 4px; border-radius: 3px; font-family: monospace;"
        escaped_text = re.sub(r"`([^`]+)`", f'<code style="{code_style}">\\1</code>', escaped_text)

        # 5. Pecah paragraf (\n\n) dan konversi line break (\n) menjadi tag paragraf <p>
        paragraphs = escaped_text.split("\n\n")
        html_paragraphs = []
        for p in paragraphs:
            p_clean = p.strip().replace("\n", "<br/>")
            if p_clean:
                html_paragraphs.append(f'<p style="margin-bottom: 8px;">{p_clean}</p>')

        return "".join(html_paragraphs)
Bab 5

Automated Unit Testing & QA Strategy

Fokus Bab: Menyusun automated tests di Odoo yang membuktikan validitas AST parser dan alur redirect OdooBot tanpa memakan RAM server untuk load model GGUF.

5.1 Teknik Isolasi Model & Mocking Inferensi LLM Lokal

Dalam pipeline Integrasi Kontinu (CI/CD) atau pengujian lokal server Odoo, kita tidak ingin memuat model biner GGUF berukuran gigabyte ke memori RAM/VRAM secara nyata. Mengisi RAM server pengetesan hanya untuk menjalankan unit test adalah pemborosan resource yang besar. Untuk mengatasinya, kita menggunakan teknik Mocking Isolation menggunakan decorator @patch dari library bawaan Python unittest.mock. Kita mem-patch method _call_local_inference pada class LlamaEngine sehingga alur inferensi LLM langsung disimulasikan (mocked) untuk mengembalikan string dummy terformat instan tanpa pernah memanggil model biner C++ native.

Konstruksi Mock Test Suite Inisiasi Class & Setup Context Test
python
from unittest.mock import patch
from odoo.tests.common import TransactionCase

class TestLlamaChat(TransactionCase):

    def setUp(self):
        super(TestLlamaChat, self).setUp()
        self.llama_engine = self.env["llama.engine"]
        self.mail_bot = self.env["mail.bot"]
        self.config_settings_model = self.env["res.config.settings"]

    @patch("odoo.addons.llama_cpp_ok.models.llama_engine.LlamaEngine._call_local_inference")
    def test_04_odoobot_chat_redirection(self, mock_inference):
        mock_inference.return_value = "CODE:result = 50 * 2"
        
        channel = self.env["discuss.channel"].create({"name": "Test Chat", "channel_type": "chat"})
        ans = self.mail_bot._get_answer(channel, "hitung 50 x 2", {"body": "hitung 50 x 2"})
        
        self.assertIn("100", ans)

5.2 Kasus Uji Unit Test AST Security & Sandbox

Kita merancang pengujian menyeluruh untuk memverifikasi bahwa AST Security Sandbox bekerja secara optimal tanpa celah bypass. Berikut adalah rincian skenario uji terintegrasi:

Skenario Uji Deskripsi Kasus & Kode Input Ekspektasi Hasil
✓ Valid ORM Queries env['res.partner'].search([('name', '=', 'Mitchell Admin')]) Lolos (Allowed)
✗ Forbidden Imports import os; result = os.system('ls') Blocked (ValueError)
✗ Forbidden Dunder Attributes result = env['res.partner'].__class__.__base__ Blocked (ValueError)
✗ Dangerous Builtins result = eval("5 + 5") Blocked (ValueError)
✗ Protected Model Blacklist result = env['res.users'].search([]) Blocked (ValueError)
✗ Non-Existent Model result = env['res.fiktif'].search([]) Blocked (ValueError)
⚠ Syntax Error Recovery result = env['res.partner'].search([ Bypass to Normal Chat

Lokasi file: tests/test_llama_chat.py

python
    def test_01_ast_security_sandbox(self):
        # 1. Valid ORM - Harus Lolos
        valid_code = "result = env['res.partner'].search([('name', '=', 'Mitchell Admin')])"
        self.assertTrue(self.llama_engine.validate_code_security(valid_code))

        # 2. Block Import - Harus Gagal
        forbidden_import = "import os\nresult = os.system('ls')"
        with self.assertRaises(ValueError) as context:
            self.llama_engine.validate_code_security(forbidden_import)
        self.assertIn("import", str(context.exception))

        # 3. Block Dunder - Harus Gagal
        forbidden_dunder = "result = env['res.partner'].__class__.__base__"
        with self.assertRaises(ValueError) as context:
            self.llama_engine.validate_code_security(forbidden_dunder)
        self.assertIn("Double underscore", str(context.exception))

5.3 Prosedur Eksekusi Automated Test Suite Lintas Platform

Untuk memicu pengetesan unit test modular Odoo secara otomatis, jalankan CLI runner sesuai dengan shell console masing-masing sistem operasi target Anda:

Linux & macOS (Bash Shell):

bash
conda run -n odoo-18 python [path_to_odoo]/odoo-bin -c [path_to_odoo]/odoo-apps-nolog.conf -i llama_cpp_ok --test-enable --test-tags=llama_cpp_ok --stop-after-init

Windows (PowerShell Console):

powershell
conda run -n odoo-18 python "[path_to_odoo]\odoo-bin" -c "[path_to_odoo]\odoo-apps-nolog.conf" -i llama_cpp_ok --test-enable --test-tags=llama_cpp_ok --stop-after-init
💡 Tips Pengujian: Parameter --stop-after-init memastikan server Odoo akan berhenti secara otomatis setelah seluruh test suite selesai dievaluasi.

Layanan Training & Dukungan Donasi

Apakah Anda memerlukan bimbingan teknis mendalam mengenai implementasi AI lokal, optimasi model offline, custom workflow sandbox, atau arsitektur modular di Odoo? Kami membuka pendaftaran untuk Sesi Training Online Privat & Corporate. Anda juga sangat disarankan untuk membeli addon resmi ini di Odoo App Store sebagai bentuk donasi dukungan riset berkelanjutan serta penyediaan panduan gratis berkualitas bagi komunitas pengembang Odoo.

✉️ Hubungi Kami via Email (okkype@gmail.com) 💼 Profil LinkedIn Pengembang

© 2026 Developer Training Module. Created with Pride for Odoo 18 & Llama.cpp Offline Training.

Odoo Proprietary License v1.0

This software and associated files (the "Software") may only be used (executed,
modified, executed after modifications) if you have purchased a valid license
from the authors, typically via Odoo Apps, or if you have received a written
agreement from the authors of the Software (see the COPYRIGHT file).

You may develop Odoo modules that use the Software as a library (typically
by depending on it, importing it and using its resources), but without copying
any source code or material from the Software. You may distribute those
modules under the license of your choice, provided that this license is
compatible with the terms of the Odoo Proprietary License (For example:
LGPL, MIT, or proprietary licenses similar to this one).

It is forbidden to publish, distribute, sublicense, or sell copies of the Software
or modified copies of the Software.

The above copyright notice and this permission notice must be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Please log in to comment on this module

  • The author can leave a single reply to each comment.
  • This section is meant to ask simple questions or leave a rating. Every report of a problem experienced while using the module should be addressed to the author directly (refer to the following point).
  • If you want to start a discussion with the author or have a question related to your purchase, please use the support page.
Community
  • Tutorials
  • Documentation
  • Forum
Open Source
  • Download
  • Github
  • Runbot
  • Translations
Services
  • Odoo.sh Hosting
  • Support
  • Upgrade
  • Custom Developments
  • Education
  • Find an Accountant
  • Find a Partner
  • Become a Partner
About us
  • Our company
  • Brand Assets
  • Contact us
  • Jobs
  • Events
  • Podcast
  • Blog
  • Customers
  • Legal • Privacy
  • Security

Odoo is a suite of open source business apps that cover all your company needs: CRM, eCommerce, accounting, inventory, point of sale, project management, etc.

Odoo's unique value proposition is to be at the same time very easy to use and fully integrated.

Website made with