[docs]@dataclassclassCreditTransaction:timestamp:datetimeusername:stramount:float# Positive for allocations, negative for usagebalance:float# Running balance after this transactiontype:str# 'allocation', 'usage', 'refund', 'expiration'source:str# 'purchase', 'admin_grant', 'usage_deduction', etc.reference_id:str# Links to usage event or purchase/admin actionmetadata:Dict[str,Any]=field(default_factory=dict)transaction_id:str=field(default_factory=lambda:str(uuid.uuid4()))
[docs]classCreditRatioConfig:"""Manages the hierarchical credit ratio configuration"""def__init__(self,base_path:str):self.base_path=Path(base_path)/'config'self.config_file=self.base_path/'ratio_config.json'self.base_path.mkdir(parents=True,exist_ok=True)asyncdef_load_config(self)->Dict:"""Load configuration from file"""try:withopen(self.config_file,'r')asf:returnjson.load(f)except(FileNotFoundError,json.JSONDecodeError):# Return default config if file doesn't exist or is invalidreturn{'default_ratio':100.0,# 1 cent = 100 credits by default'plugins':{}}asyncdef_save_config(self,config:Dict)->None:"""Save configuration to file"""withopen(self.config_file,'w')asf:json.dump(config,f,indent=2)
[docs]asyncdefset_ratio(self,ratio:float,plugin_id:Optional[str]=None,cost_type_id:Optional[str]=None,model_id:Optional[str]=None)->None:"""Set a credit ratio at the specified level"""ifratio<=0:raiseValueError("Ratio must be positive")config=awaitself._load_config()ifnotplugin_id:config['default_ratio']=ratioawaitself._save_config(config)returnifplugin_idnotinconfig['plugins']:config['plugins'][plugin_id]={'default_ratio':None,'cost_types':{},'models':{}}plugin_config=config['plugins'][plugin_id]ifmodel_id:ifmodel_idnotinplugin_config['models']:plugin_config['models'][model_id]={'default_ratio':None,'cost_types':{}}ifcost_type_id:plugin_config['models'][model_id]['cost_types'][cost_type_id]=ratioelse:plugin_config['models'][model_id]['default_ratio']=ratioelifcost_type_id:plugin_config['cost_types'][cost_type_id]=ratioelse:plugin_config['default_ratio']=ratioawaitself._save_config(config)
[docs]asyncdefget_ratio(self,plugin_id:str,cost_type_id:str,model_id:Optional[str]=None)->float:"""Get the appropriate credit ratio following the resolution order"""config=awaitself._load_config()plugin_config=config['plugins'].get(plugin_id,{})# 1. Check model-specific cost type ratioifmodel_idandmodel_idinplugin_config.get('models',{}):model_config=plugin_config['models'][model_id]ifcost_type_idinmodel_config.get('cost_types',{}):returnmodel_config['cost_types'][cost_type_id]# 2. Check model default ratioifmodel_config.get('default_ratio')isnotNone:returnmodel_config['default_ratio']# 3. Check plugin cost type ratioifcost_type_idinplugin_config.get('cost_types',{}):returnplugin_config['cost_types'][cost_type_id]# 4. Check plugin default ratioifplugin_config.get('default_ratio')isnotNone:returnplugin_config['default_ratio']# 5. Use global default ratioreturnconfig['default_ratio']
[docs]asyncdefget_config(self)->Dict:"""Get the full configuration"""returnawaitself._load_config()
[docs]asyncdefset_config(self,config:Dict)->None:"""Set the full configuration"""# Basic validationif'default_ratio'notinconfig:raiseValueError("Configuration must include default_ratio")ifconfig['default_ratio']<=0:raiseValueError("Default ratio must be positive")awaitself._save_config(config)