fix: antwortendes Modell erfassen; 0.040, LaTeX-Dezimalkomma und Teilfragen-Überschriften
- CallRecord.responded_model / LLMUsage.responded_models: tatsächlich antwortendes
Modell (beim Alias ~deepseek/deepseek-flash-latest) in Log, Report und Benchmark
- normalize_number: führende Null ist nie Tausender (0.040 wurde als 40 gelesen)
- numbers_in: LaTeX-Dezimalkomma 0{,}08 als eine Zahl
- question_ids_in: '### Teil N' als Teilfrage; ausdrückliche Marker haben Vorrang
vor nummerierten Listen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
88adcff586
commit
8bd3c2e7e1
2 changed files with 88 additions and 10 deletions
15
core/llm.py
15
core/llm.py
|
|
@ -146,6 +146,13 @@ class LLMSession:
|
|||
provider = getattr(completion, "provider", None) # OpenRouter: tatsächlich antwortender Anbieter
|
||||
if provider:
|
||||
self.usage.providers[provider] = self.usage.providers.get(provider, 0) + 1
|
||||
# Beim Alias (~deepseek/deepseek-flash-latest) steht erst in der Antwort, welches Modell gerechnet hat;
|
||||
# ohne diese Angabe wären Läufe vor und nach einem Modellwechsel des Alias nicht zu unterscheiden.
|
||||
antwortend = getattr(completion, "model", None) or getattr(completion, "model_version", None)
|
||||
if not isinstance(antwortend, str):
|
||||
antwortend = None
|
||||
if antwortend:
|
||||
self.usage.responded_models[antwortend] = self.usage.responded_models.get(antwortend, 0) + 1
|
||||
usage = getattr(completion, "usage", None)
|
||||
if usage is not None: # OpenAI-Format (OpenRouter)
|
||||
self.usage.prompt_tokens += usage.prompt_tokens or 0
|
||||
|
|
@ -157,7 +164,7 @@ class LLMSession:
|
|||
cost = getattr(usage, "cost", None)
|
||||
if cost is not None:
|
||||
self.usage.cost_usd = (self.usage.cost_usd or 0.0) + cost
|
||||
self._log_call(step, seconds, before, getattr(completion, "provider", None), model)
|
||||
self._log_call(step, seconds, before, getattr(completion, "provider", None), model, antwortend)
|
||||
return
|
||||
meta = getattr(completion, "usage_metadata", None)
|
||||
if meta is not None: # Gemini-Format, keine Kostenangabe
|
||||
|
|
@ -165,11 +172,11 @@ class LLMSession:
|
|||
self.usage.completion_tokens += meta.candidates_token_count or 0
|
||||
self.usage.reasoning_tokens += meta.thoughts_token_count or 0
|
||||
self.usage.cached_tokens += getattr(meta, "cached_content_token_count", 0) or 0
|
||||
self._log_call(step, seconds, before, getattr(completion, "provider", None), model)
|
||||
self._log_call(step, seconds, before, getattr(completion, "provider", None), model, antwortend)
|
||||
|
||||
def _log_call(self, step, seconds, before, provider, model=None) -> None:
|
||||
def _log_call(self, step, seconds, before, provider, model=None, responded_model=None) -> None:
|
||||
self.usage.call_records.append(CallRecord(
|
||||
step=step, seconds=round(seconds, 1), provider=provider, model=model,
|
||||
step=step, seconds=round(seconds, 1), provider=provider, model=model, responded_model=responded_model,
|
||||
completion_tokens=self.usage.completion_tokens - before[0],
|
||||
reasoning_tokens=self.usage.reasoning_tokens - before[1],
|
||||
prompt_tokens=self.usage.prompt_tokens - before[2],
|
||||
|
|
|
|||
|
|
@ -427,7 +427,9 @@ class CallRecord(BaseModel):
|
|||
prompt_tokens: int = 0
|
||||
cached_tokens: int = Field(default=0, description="Input-Tokens aus dem Anbieter-Cache (billiger)")
|
||||
provider: Optional[str] = None
|
||||
model: Optional[str] = Field(default=None, description="Tatsächlich benutztes Modell (kann je Stufe abweichen)")
|
||||
model: Optional[str] = Field(default=None, description="Angefragtes Modell (kann je Stufe abweichen)")
|
||||
responded_model: Optional[str] = Field(
|
||||
default=None, description="Modell laut Antwort – bei Aliasen wie ~deepseek/deepseek-flash-latest das tatsächliche")
|
||||
|
||||
|
||||
class LLMUsage(BaseModel):
|
||||
|
|
@ -440,6 +442,8 @@ class LLMUsage(BaseModel):
|
|||
cached_tokens: int = Field(default=0, description="davon Input-Tokens aus dem Anbieter-Cache")
|
||||
cost_usd: Optional[float] = Field(default=None, description="nur OpenRouter liefert Kosten")
|
||||
providers: Dict[str, int] = Field(default_factory=dict, description="Antwortender Anbieter -> Anzahl Aufrufe (OpenRouter)")
|
||||
responded_models: Dict[str, int] = Field(
|
||||
default_factory=dict, description="Tatsächlich antwortendes Modell -> Anzahl Aufrufe (bei Aliasen vergleichsentscheidend)")
|
||||
call_records: List[CallRecord] = Field(default_factory=list)
|
||||
|
||||
def by_step(self) -> Dict[str, dict]:
|
||||
|
|
|
|||
Loading…
Reference in a new issue