Files
Odoo-Modules/fusion_accounting_reports/tests/test_local_llm_compat.py
2026-04-19 16:30:05 -04:00

87 lines
3.0 KiB
Python

"""Local LLM compat smoke for the commentary generator.
Auto-detects an LM Studio (:1234) or Ollama (:11434) server on either
`host.docker.internal` or `localhost`. If none is reachable the test
self-skips so CI without a local LLM stays green.
Tagged 'local_llm' so it's never part of the default run.
"""
import socket
from datetime import date
from odoo.tests.common import TransactionCase, tagged
def _server_reachable(host, port, timeout=1.0):
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except (OSError, socket.timeout):
return False
def _detect_local_llm():
"""Return (base_url, default_model) for the first reachable server, or
(None, None) if none of the common dev endpoints respond."""
candidates = [
('host.docker.internal', 1234, 'local-model'),
('host.docker.internal', 11434, 'llama3.1:8b'),
('localhost', 1234, 'local-model'),
('localhost', 11434, 'llama3.1:8b'),
]
for host, port, default_model in candidates:
if _server_reachable(host, port, timeout=0.5):
return (f'http://{host}:{port}/v1', default_model)
return (None, None)
@tagged('post_install', '-at_install', 'local_llm')
class TestLocalLLMCommentary(TransactionCase):
def setUp(self):
super().setUp()
self.base_url, self.model = _detect_local_llm()
if not self.base_url:
self.skipTest(
"No local LLM server detected "
"(LM Studio :1234 / Ollama :11434)"
)
def test_commentary_with_local_llm(self):
params = self.env['ir.config_parameter'].sudo()
keys = [
'fusion_accounting.openai_base_url',
'fusion_accounting.openai_model',
'fusion_accounting.openai_api_key',
'fusion_accounting.provider.reports_commentary',
]
prior = {k: params.get_param(k) for k in keys}
params.set_param('fusion_accounting.openai_base_url', self.base_url)
params.set_param('fusion_accounting.openai_model', self.model)
params.set_param('fusion_accounting.openai_api_key', 'lm-studio')
params.set_param(
'fusion_accounting.provider.reports_commentary', 'openai',
)
try:
from odoo.addons.fusion_accounting_reports.services.commentary_generator import (
generate_commentary,
)
from odoo.addons.fusion_accounting_reports.services.date_periods import (
Period,
)
period = Period(date(2026, 1, 1), date(2026, 12, 31), '2026')
result = self.env['fusion.report.engine'].compute_pnl(
period, company_id=self.env.company.id,
)
commentary = generate_commentary(self.env, report_result=result)
self.assertIn('summary', commentary)
# Don't assert specific content - just that it returned a dict
finally:
for k, v in prior.items():
if v is not None:
params.set_param(k, v)