Merge pull request #100 from valueonag/feat/cursor-style-feature
fixed cursor feature
This commit is contained in:
commit
312163d34a
4 changed files with 38 additions and 9 deletions
|
|
@ -6,17 +6,39 @@ No complex rules needed - just filter by properties and sort by priority!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Dict, Any, Optional
|
import time
|
||||||
|
from typing import List, Dict, Any, Optional, Tuple
|
||||||
from modules.datamodels.datamodelAi import AiModel, AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
|
from modules.datamodels.datamodelAi import AiModel, AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
|
||||||
|
|
||||||
# Configure logger
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_COOLDOWN_DURATION = 60.0
|
||||||
|
|
||||||
|
|
||||||
class ModelSelector:
|
class ModelSelector:
|
||||||
"""Simple model selector based on properties and priority-based sorting."""
|
"""Model selector with priority scoring and recent-failure cooldown."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logger.info("ModelSelector initialized with simplified approach")
|
self._failureLog: Dict[str, float] = {}
|
||||||
|
logger.info("ModelSelector initialized with failure cooldown support")
|
||||||
|
|
||||||
|
def reportFailure(self, modelName: str):
|
||||||
|
"""Record that a model just failed (rate limit, error, etc.).
|
||||||
|
The model will be deprioritized for COOLDOWN_DURATION seconds."""
|
||||||
|
self._failureLog[modelName] = time.time()
|
||||||
|
logger.info(f"ModelSelector: Recorded failure for {modelName}, cooldown {_COOLDOWN_DURATION}s")
|
||||||
|
|
||||||
|
def _getCooldownPenalty(self, modelName: str) -> float:
|
||||||
|
"""Return a score penalty (0.0 = no penalty, large negative = recently failed)."""
|
||||||
|
failedAt = self._failureLog.get(modelName)
|
||||||
|
if failedAt is None:
|
||||||
|
return 0.0
|
||||||
|
elapsed = time.time() - failedAt
|
||||||
|
if elapsed > _COOLDOWN_DURATION:
|
||||||
|
del self._failureLog[modelName]
|
||||||
|
return 0.0
|
||||||
|
remaining = _COOLDOWN_DURATION - elapsed
|
||||||
|
return -(remaining / _COOLDOWN_DURATION) * 5000.0
|
||||||
|
|
||||||
def selectModel(self,
|
def selectModel(self,
|
||||||
prompt: str,
|
prompt: str,
|
||||||
|
|
@ -129,10 +151,14 @@ class ModelSelector:
|
||||||
maxAllowed = model.contextLength * 0.8 / 4 if model.contextLength > 0 else "unlimited"
|
maxAllowed = model.contextLength * 0.8 / 4 if model.contextLength > 0 else "unlimited"
|
||||||
logger.warning(f" - {model.name}: contextLength={model.contextLength} tokens, maxAllowed={maxAllowed} tokens")
|
logger.warning(f" - {model.name}: contextLength={model.contextLength} tokens, maxAllowed={maxAllowed} tokens")
|
||||||
|
|
||||||
# Step 3: Calculate scores for each model
|
# Step 3: Calculate scores for each model (including cooldown penalties)
|
||||||
scoredModels = []
|
scoredModels = []
|
||||||
for model in promptFiltered:
|
for model in promptFiltered:
|
||||||
score = self._calculateModelScore(model, promptSize, contextSize, totalSize, options)
|
score = self._calculateModelScore(model, promptSize, contextSize, totalSize, options)
|
||||||
|
penalty = self._getCooldownPenalty(model.name)
|
||||||
|
if penalty < 0:
|
||||||
|
logger.debug(f"Model {model.name}: base_score={score:.3f}, cooldown_penalty={penalty:.0f}")
|
||||||
|
score += penalty
|
||||||
scoredModels.append((model, score))
|
scoredModels.append((model, score))
|
||||||
logger.debug(f"Model {model.name}: score={score:.3f}")
|
logger.debug(f"Model {model.name}: score={score:.3f}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class FileContext(BaseModel):
|
||||||
content: Optional[str] = None
|
content: Optional[str] = None
|
||||||
mimeType: str
|
mimeType: str
|
||||||
sizeBytes: int = 0
|
sizeBytes: int = 0
|
||||||
|
modifiedAt: Optional[float] = None
|
||||||
tags: List[str] = Field(default_factory=list)
|
tags: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,14 @@ def listTextFiles(dbManagement) -> List[FileContext]:
|
||||||
|
|
||||||
for fileItem in allFiles:
|
for fileItem in allFiles:
|
||||||
if isTextFile(fileItem.mimeType, fileItem.fileName):
|
if isTextFile(fileItem.mimeType, fileItem.fileName):
|
||||||
|
modifiedAt = getattr(fileItem, "_modifiedAt", None) or getattr(fileItem, "creationDate", None)
|
||||||
textFiles.append(FileContext(
|
textFiles.append(FileContext(
|
||||||
fileId=fileItem.id,
|
fileId=fileItem.id,
|
||||||
fileName=fileItem.fileName,
|
fileName=fileItem.fileName,
|
||||||
content=None,
|
content=None,
|
||||||
mimeType=fileItem.mimeType,
|
mimeType=fileItem.mimeType,
|
||||||
sizeBytes=fileItem.fileSize
|
sizeBytes=fileItem.fileSize,
|
||||||
|
modifiedAt=modifiedAt
|
||||||
))
|
))
|
||||||
|
|
||||||
return textFiles
|
return textFiles
|
||||||
|
|
|
||||||
|
|
@ -135,11 +135,11 @@ class AiObjects:
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
lastError = e
|
lastError = e
|
||||||
logger.warning(f"❌ AI call failed with model {model.name}: {str(e)}")
|
logger.warning(f"AI call failed with model {model.name}: {str(e)}")
|
||||||
|
modelSelector.reportFailure(model.name)
|
||||||
|
|
||||||
# If this is not the last model, try the next one
|
|
||||||
if attempt < len(failoverModelList) - 1:
|
if attempt < len(failoverModelList) - 1:
|
||||||
logger.info(f"🔄 Trying next failover model...")
|
logger.info(f"Trying next failover model...")
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# All models failed
|
# All models failed
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue