feat(fusion_accounting_followup): LLM text cache model
Made-with: Cursor
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
from . import fusion_followup_level
|
||||
from . import fusion_followup_run
|
||||
from . import fusion_followup_text_cache
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Cache of AI-generated follow-up text to avoid LLM cost on repeats."""
|
||||
|
||||
import hashlib
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class FusionFollowupTextCache(models.Model):
|
||||
_name = "fusion.followup.text.cache"
|
||||
_description = "Cache of AI-generated follow-up text"
|
||||
_order = "generated_at desc"
|
||||
|
||||
partner_id = fields.Many2one('res.partner', required=True, ondelete='cascade')
|
||||
level_id = fields.Many2one('fusion.followup.level', ondelete='cascade')
|
||||
company_id = fields.Many2one('res.company', required=True,
|
||||
default=lambda self: self.env.company)
|
||||
|
||||
fingerprint = fields.Char(required=True, index=True,
|
||||
help="SHA-256 of input parameters")
|
||||
|
||||
subject = fields.Char()
|
||||
body = fields.Text()
|
||||
tone_used = fields.Selection([
|
||||
('gentle', 'Gentle'), ('firm', 'Firm'), ('legal', 'Legal'),
|
||||
])
|
||||
key_points = fields.Json()
|
||||
|
||||
generated_at = fields.Datetime(default=fields.Datetime.now, required=True)
|
||||
expires_at = fields.Datetime()
|
||||
use_count = fields.Integer(default=0)
|
||||
provider = fields.Char()
|
||||
|
||||
@api.model
|
||||
def compute_fingerprint(self, *, partner_id: int, level_id: int,
|
||||
overdue_amount: float, longest_overdue_days: int,
|
||||
invoice_count: int, tone: str) -> str:
|
||||
"""Stable hash of the inputs that determine the generated text."""
|
||||
s = f"{partner_id}|{level_id}|{round(overdue_amount, 2)}|" \
|
||||
f"{longest_overdue_days}|{invoice_count}|{tone}"
|
||||
return hashlib.sha256(s.encode('utf-8')).hexdigest()
|
||||
|
||||
@api.model
|
||||
def lookup(self, *, partner_id: int, level_id: int,
|
||||
overdue_amount: float, longest_overdue_days: int,
|
||||
invoice_count: int, tone: str):
|
||||
"""Find a cached entry matching these inputs, or empty recordset."""
|
||||
fp = self.compute_fingerprint(
|
||||
partner_id=partner_id, level_id=level_id,
|
||||
overdue_amount=overdue_amount,
|
||||
longest_overdue_days=longest_overdue_days,
|
||||
invoice_count=invoice_count, tone=tone,
|
||||
)
|
||||
return self.search([
|
||||
('partner_id', '=', partner_id),
|
||||
('fingerprint', '=', fp),
|
||||
], limit=1)
|
||||
|
||||
def action_increment_use(self):
|
||||
for rec in self:
|
||||
rec.use_count += 1
|
||||
Reference in New Issue
Block a user