Download code/moth/backends/engine.py from darioooooo0o/moth-112m: direct link, hf CLI and curl.
- Browser
- Download file 3.53 kB
-
https://huggingface.co/darioooooo0o/moth-112m/resolve/main/code/moth/backends/engine.py
- Command line
-
hf download hf://darioooooo0o/moth-112m/code/moth/backends/engine.py
-
curl -L -o engine.py https://huggingface.co/darioooooo0o/moth-112m/resolve/main/code/moth/backends/engine.py
3.53 kB
| """moth's conversation engine (moth.serve.Moth: history, reactions, sessions, the home tools, the phrase memory) on a | |
| stateless backend (transformers, openai). Moth keeps tiny-agent's KV cache across turns and feeds only new text; | |
| here every turn renders the whole (trimmed) chat and asks the backend for the next assistant turn. Everything above | |
| the model is Moth's own code, unchanged: the same rollback, budget trimming, agent cards, one turn per session. | |
| """ | |
| from __future__ import annotations | |
| from moth.backends.manifest import apply_args as apply_manifest | |
| from moth.norepeat import PhraseMemory | |
| from moth.serve import Moth | |
| READY = "ready" # stands in for the logits / cache of the stateful path: there is nothing to keep between turns | |
| class GenericMoth(Moth): | |
| def __init__(self, a, manifest=None, backend=None): | |
| from moth.backends import load_backend, resolve | |
| self.manifest = manifest or getattr(a, "manifest", None) or resolve(a.model) | |
| a = apply_manifest(a, self.manifest) | |
| self.backend = backend or load_backend(self.manifest, a) | |
| self.tok = self.backend.tokenizer() | |
| self.model = self._kv = None | |
| self.quant = "none" | |
| self._reacting = False | |
| self.setup(a) | |
| if not self.backend.phrase_ban: # no token-level sampling (openai): no phrase ban, no carried-over words | |
| self.phrases = PhraseMemory(0) | |
| # --- what the stateful path does with a KV cache ------------------------------------------------------------ | |
| def _new_cache(self): | |
| return READY | |
| def _feed(self, ids): | |
| return READY # the next _turn renders the whole chat; the budget is checked there | |
| def _prefill_all(self): | |
| """Drop the oldest turns until the chat fits CTX_KEEP-worth of tokens (as Moth._prefill_all).""" | |
| while len(self.messages) > 2 and self.backend.count(self._view()) >= self.keep: | |
| i = next((k for k in range(2, len(self.messages)) if self.messages[k]["role"] == "user"), None) | |
| if i is None: | |
| break | |
| self.messages = self.messages[:1] + self.messages[i:] | |
| self.cache = READY | |
| return READY | |
| def _turn(self, logits, talk_only=False) -> str: | |
| if self.backend.count(self._view()) + self.a.max_turn_tokens > self.budget: | |
| self._prefill_all() | |
| return self.backend.generate( | |
| self._view(), max_tokens=self.a.max_turn_tokens, temperature=self.a.temperature, top_p=self.a.top_p, | |
| rep_penalty=getattr(self.a, "rep_penalty", 1.0), pen_ids=self._pen_ids(), ban_tools=talk_only, | |
| logits_hook=self._hook, stop=self.manifest.stop) | |
| def _view(self) -> list[dict]: | |
| """The chat as the model sees it this turn: moth's bookkeeping keys out, and on a reaction the manifest's | |
| reaction nudge after the agent card (this turn only; history keeps the plain card).""" | |
| msgs = [{k: v for k, v in m.items() if k != "session"} for m in self.messages] | |
| if self._reacting and self.manifest.reaction: | |
| for m in reversed(msgs): | |
| if m["role"] == "user": | |
| m["content"] = f"{m['content']}\n\n{self.manifest.reaction}" | |
| break | |
| return msgs | |
| def react(self, text, hold_for_human=False, item=None): | |
| with self.lock: | |
| self._reacting = True | |
| try: | |
| return super().react(text, hold_for_human=hold_for_human, item=item) | |
| finally: | |
| self._reacting = False | |