Phase 1 prerequisite for local LLM support. Adapters now declare capability flags (supports_tool_calling, max_context_tokens, etc.) so the engine can reason about what backend is available. OpenAI adapter accepts fusion_accounting.openai_base_url config -- point it at LM Studio (http://host.docker.internal:1234/v1) or Ollama (http://host.docker.internal:11434/v1) and the existing OpenAI adapter works unchanged. Implementation note: existing Odoo AbstractModel adapters (fusion.accounting.adapter.openai/claude) are preserved untouched to avoid breaking the chat panel; the new plain-Python OpenAIAdapter and ClaudeAdapter classes (LLMProvider subclasses) are added alongside them. Made-with: Cursor
46 lines
2.3 KiB
Python
46 lines
2.3 KiB
Python
from odoo.tests.common import TransactionCase, tagged
|
|
from odoo.addons.fusion_accounting_ai.services.adapters._base import LLMProvider
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestLLMProviderContract(TransactionCase):
|
|
"""Every LLM adapter must satisfy the LLMProvider contract."""
|
|
|
|
def test_base_class_defines_capability_attrs(self):
|
|
self.assertTrue(hasattr(LLMProvider, 'supports_tool_calling'))
|
|
self.assertTrue(hasattr(LLMProvider, 'supports_streaming'))
|
|
self.assertTrue(hasattr(LLMProvider, 'max_context_tokens'))
|
|
self.assertTrue(hasattr(LLMProvider, 'supports_embeddings'))
|
|
|
|
def test_openai_adapter_implements_contract(self):
|
|
from odoo.addons.fusion_accounting_ai.services.adapters.openai_adapter import OpenAIAdapter
|
|
self.assertTrue(issubclass(OpenAIAdapter, LLMProvider))
|
|
adapter = OpenAIAdapter(self.env)
|
|
self.assertIsInstance(adapter.supports_tool_calling, bool)
|
|
self.assertIsInstance(adapter.max_context_tokens, int)
|
|
|
|
def test_claude_adapter_implements_contract(self):
|
|
from odoo.addons.fusion_accounting_ai.services.adapters.claude import ClaudeAdapter
|
|
self.assertTrue(issubclass(ClaudeAdapter, LLMProvider))
|
|
adapter = ClaudeAdapter(self.env)
|
|
self.assertIsInstance(adapter.supports_tool_calling, bool)
|
|
self.assertIsInstance(adapter.max_context_tokens, int)
|
|
|
|
def test_openai_adapter_uses_configurable_base_url(self):
|
|
self.env['ir.config_parameter'].sudo().set_param(
|
|
'fusion_accounting.openai_base_url', 'http://localhost:1234/v1')
|
|
self.env['ir.config_parameter'].sudo().set_param(
|
|
'fusion_accounting.openai_api_key', 'lm-studio-test-key')
|
|
from odoo.addons.fusion_accounting_ai.services.adapters.openai_adapter import OpenAIAdapter
|
|
adapter = OpenAIAdapter(self.env)
|
|
self.assertEqual(str(adapter.client.base_url).rstrip('/'),
|
|
'http://localhost:1234/v1')
|
|
|
|
def test_openai_adapter_default_base_url_when_unset(self):
|
|
self.env['ir.config_parameter'].sudo().search([
|
|
('key', '=', 'fusion_accounting.openai_base_url')
|
|
]).unlink()
|
|
from odoo.addons.fusion_accounting_ai.services.adapters.openai_adapter import OpenAIAdapter
|
|
adapter = OpenAIAdapter(self.env)
|
|
self.assertIn('api.openai.com', str(adapter.client.base_url))
|