Skip to content
C.W.K.
Lesson 03 of 04 · published

Text Already Met Is Instant

~13 min · corpus, caching, sqlite, one-shot-calls

Level 0First Word
0 XP0/47 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

The engine sees the batch first

The phone's batch lands on POST /api/live/translate: a live moment's id and between 1 and 40 items, each a key and a text of up to 500 characters. The route refuses any moment that is not a live one (a test sends an ordinary moment and expects 400), resolves the live_lane slot, and hands everything to the turn runner. Before any brain is involved, the runner asks the corpus about every item.

One key for "the same text"

Matching starts with source_key: whitespace collapsed to single spaces, then casefold(), Python's aggressive lowercase that is meant for caseless comparison. Every result with a source text carries this key, written when the result is recorded, whether it came from a photo, a conversation or the live camera. The mirror's results table has an index on (source_key, target_language, seq), and known_translation is one indexed query over it: the newest result with this key, in this target language, of kind line, live-line or expression.

Those three kinds are the point. A line I read off a photographed menu, an expression I offered during a conversation, a sign seen live last week: any of them answers the camera today. And because the query takes the newest by seq, a later correction (Track 3's revisions are new results, never overwrites) becomes the answer the next lookup returns. When that next lookup happens is the catch. The phone keeps every answer of a session in memory and never asks the engine twice for one text (lesson 1), so a correction recorded while the camera is open reaches the camera in the next live session, not in the one already running. The architecture recorded what this buys: the same four signs from the measurement came back from the corpus in 20 ms.

A hit is still a record

A cache hit does not just return a string. The runner writes a new live-line result on this live moment, with the text as the scanner read it, the known translation and pronunciation, and cached_from naming the result it copied. It has no turn, because no brain ran. So the live moment ends up holding every text that got an answer, and each borrowed answer keeps its lineage. The where and when belong to the moment itself: the place sample and the time the phone took when the session opened, not a fix for each text.

That is the docstring's promise: text already met answers from the corpus at once, the rest goes to Pippa in one short call, and every text joins the corpus on this live moment. Two parts of it are looser than they read. A text that stays untranslated gets no result at all, so it never joins the corpus (below). And "at once" holds for the lookup, not for the reply. The route answers the phone only when the whole batch is done, so in a batch that mixes known and new text, the known answers wait for my call to finish and land together with mine.

The misses: one short call

Whatever the corpus did not know goes to me together. The runner records a live turn with the brain and the number of items, then builds a one-shot request for cwkPippa's sibling utility door: the misses as a numbered list, a system prompt, the slot's brain and effort, max_tokens: 0 (no cap of Spark's own), persona_soul_id: "pippa" and source: "spark-live". The door runs it with no conversation and tools off. The engine waits up to 60 s.

The system prompt is short and strict. It names the target language, adds the situation and place when the moment has them, asks for one JSON object per line with the number, the translation and, only for short things Dad might say aloud, a pronunciation, and asks that each translation be as short as the sign it translates. The answer is read by parse_json_lines, which takes any line starting with a brace, tolerates a fence or a trailing comma, and skips the rest. Each object is matched back to its item by n.

Every translated miss becomes a live-line under the turn. Whatever stayed untranslated is returned as pending, which the phone frees for a later ask (lesson 1). A pending text writes no result, so the moment does not hold it. At most the turn's item count shows it was asked. The turn finishes as completed if at least one item came back and failed if none did, with the elapsed time, the model, the effort and the error recorded on it.

No machine fallback

On the design day there was one open question about this mode: should an instant first layer from Apple's on-device translation fill the gap, labelled as machine translation, until my answer replaced it seconds later? Dad answered with two words.

A로 가.

"Go with A": Pippa only, here too. So the lane has no second path. If no brain resolves, or the door refuses, the misses come back pending with the error, the phone shows it in the status capsule, and nothing is guessed. New text waits a few seconds. Text already met is instant when its batch holds nothing new, and otherwise arrives with my answer. That pairing is the whole bargain.

A restart is handled in the same spirit. A live turn that was running when the engine stopped is marked interrupted at the next start and never asked after. There is no cwkPippa conversation to ask, and the camera will simply ask again when the text comes back into view.

Principle: When your history already holds the answer, answer from it and write down that you did. A cache that records its own hits is a memory; one that does not is a shortcut.

The test test_live_text_already_met_answers_from_the_corpus_and_the_rest_from_pippa proves it end to end. A text moment reading La cuenta is translated first. Then a live batch sends La CUENTA, with a double space and capitals, and Salida. The first comes from the corpus with cached_from pointing at the original line; only 1. Salida reaches the fake door, at low effort on the live slot. A second batch with lowercase salida comes from the corpus too, and the door has still been called once. SQLite Quest has the indexing side of this in depth.

Code

cwkSpark, backend/turns.py: corpus first, and a hit is recorded on the live moment (verbatim, lines 1478-1508)·python
        """Translate what the live camera found. Text already met answers from
        the corpus at once; the rest goes to Pippa in one short call. Every
        text joins the corpus on this live moment, with where it was seen."""
        target = moment.get("target_language") or "ko"
        answered: list[dict[str, Any]] = []
        misses: list[dict[str, Any]] = []
        for item in items:
            key = source_key(item["text"])
            known = await asyncio.to_thread(
                self.corpus.known_translation, key, target, kinds=LIVE_KNOWN_KINDS
            )
            if known is None:
                misses.append(item)
                continue
            fields = {
                "kind": "live-line",
                "source": item["text"],
                "translation": known.get("translation"),
                "source_key": key,
                "cached_from": known["id"],
            }
            if known.get("pronunciation"):
                fields["pronunciation"] = known["pronunciation"]
            record = await self._write(
                self.corpus.add_result,
                moment["id"],
                turn_id=None,
                target_language=target,
                fields=fields,
            )
            answered.append({"key": item["key"], "from": "corpus", "result": record})
cwkSpark, backend/mirror.py: the newest result that already translated this text (verbatim, lines 700-710)·python
    def known_translation(
        self, key: str, target_language: str, *, kinds: tuple[str, ...]
    ) -> dict[str, Any] | None:
        """The newest result that already translated this text."""
        marks = ",".join("?" for _ in kinds)
        row = self.db.execute(
            "SELECT record FROM results WHERE source_key = ? AND target_language = ? "
            f"AND kind IN ({marks}) ORDER BY seq DESC LIMIT 1",
            (key, target_language, *kinds),
        ).fetchone()
        return json.loads(row[0]) if row else None
cwkSpark, backend/turns.py: the misses go to Pippa in one persona one-shot call (verbatim, lines 1514-1545)·python
            started = await self._write(
                self.corpus.start_turn,
                moment["id"],
                "live",
                brain=picks["brain"],
                model=picks.get("model"),
                items=len(misses),
            )
            turn_record = started[0] if started else None
            body = {
                "prompt": live_prompt(misses),
                "system_prompt": live_system_prompt(moment, scene_language),
                "brain": picks["brain"],
                "effort": picks["effort"],
                "model": picks.get("model"),
                "max_tokens": 0,
                "persona_soul_id": SOUL_ID,
                "source": "spark-live",
            }
            loop = asyncio.get_running_loop()
            started_at = loop.time()
            try:
                envelope = await self.pippa.utility(body, timeout=LIVE_TIMEOUT_SECONDS)
            except PippaError as exc:
                envelope = {"ok": False, "result": "", "error": str(exc)}
            elapsed_ms = int((loop.time() - started_at) * 1000)
            by_number = {}
            for obj in parse_json_lines(str(envelope.get("result") or "")):
                try:
                    by_number[int(obj.get("n"))] = obj
                except (TypeError, ValueError):
                    continue

External links

Exercise

Put a history-first path in front of a slow translation or model call in your own project. Normalize each input into a key, look up the newest earlier answer with the same key and the same target, and record a hit as a new entry that points at the entry it copied. Send only the misses to the model, as one numbered list, ask for one JSON object per line, and match the answers back by number. Return the keys that came back empty. Then write the test: the same text with different spacing and capitals is a hit, and the model is called exactly once across two batches.
Hint
Store the normalized key on every entry when you write it, not only when you look it up, and index it together with the target language and an ordering column. If corrections in your system are new entries, ordering by newest makes them win the lookup for free.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.