Pin the embedding model to the CPU so ZeroGPU stops intercepting itRunning
YasserHaidar
835249d
The background index build died with "ZeroGPU's PyTorch CUDA emulation mode did
not intercept a CUDA operation in your code". sentence-transformers picks a
device by asking torch whether CUDA is available; on ZeroGPU that answers yes,
because CUDA is emulated and only intercepted for calls made on the request
path inside an @spaces.GPU function. Building the index on a background thread
put the call outside that path, so it reached torch._C._cuda_init directly.
Nothing here wants a GPU: it is a 471MB MiniLM embedding 95 short documents
once per restart. Passing device explicitly skips the availability check that
breaks, rather than working around it. KALIM_EMBEDDING_DEVICE overrides it for
a machine with a GPU worth using.
Both Chroma paths are covered — build_index and search each embed through this
model and hand Chroma the finished vectors, so Chroma never runs an embedding
function of its own.
sentence-transformers is not a test dependency, so the constructor is stubbed;
what th…
Stop the conversation trapping the wheel over the wizard step
YasserHaidar
0cbcfbf
Clicking "لنبدأ" on anything shorter than a 900px window put the first
question out of reach. Two causes, both here:
The pane was sized against the whole free height, without counting the wizard
step underneath it, so the step went below the fold — on a 720px-tall window
the "نعم، أعرف رموزي" button started at 765px. It now subtracts whatever sits
under the conversation inside the column. Those controls do not change height
when the pane does, so this cannot oscillate.
And the pane carried `overscroll-behavior: contain`, so a wheel over it never
reached the page — and it covers most of the window, so the pointer was almost
always over it. There was no way to scroll down to the button at all. It
chains at the boundary now, which is what a pane inside a page should do.
A flex item still earns the column's gap at zero height, so the collapsed
footer wrapper is out of flow as well; that was the last 32px of page under
the final element.
Verified at 1440x900, 1280x720, 1440x640 and 390x…
Let the student's scroll position win
YasserHaidar
9b9e618
Pressing anything re-stuck the pane to the bottom, so scrolling to the top and
then tapping an action chip or "show more" threw the student straight back
down. Only the question they typed does that now: it is the one action that
means "and show me the answer", and the composer is docked at the bottom of
the screen anyway.
The follow-down also stopped short of the newest message, because a smooth
scroll fires scroll events the whole way and the listener read those as the
student scrolling away, turning stickiness off mid-flight. Our own scrolls are
ignored while they run.
Verified on the running app: at the top an action chip holds at 0, "show more"
holds at 0, a typed question lands at the bottom (2860 of 2860), and from the
bottom a new answer still follows.
Stop the conversation snapping back to the bottom when you scroll up
YasserHaidar
3f639e9
The auto-scroll fired on any mutation inside the chat pane, and reading
produces plenty of them — opening a result card, Gradio re-rendering rows — so
scrolling up to re-read something pulled you straight back down.
It sticks to the bottom only while the student is already there (80px of
slack), which is right when you are watching the conversation arrive and wrong
the moment you have gone looking for something. Sending a question or
answering a wizard step sets it again, so a reply is never missed; opening a
card does not, because that is the reading they scrolled up to do.
Verified against the running app: scrolled to the top it stays at 0, still 0
after opening a card, holds at 200 mid-pane, and follows to the bottom after a
question.
Put the chat box back on screen, and the shortlist back in the conversation
YasserHaidar
6083af5
Measured on the running app rather than guessed at.
The composer was unreachable. It is the last child of #kalim-main, so
`position: sticky; bottom: 3rem` had no room to lift it — it sat 748px below
the fold on a 1440x900 desktop and 873px below it on a 390px phone. A student
saw ten result cards and no way to ask anything. It is `fixed` now, tracking
the main column, and clears the footer by 8px at both sizes.
Nothing is hidden under the footer any more. The strip wraps to two lines at
390px (54px measured) but the offsets guarding it were a flat 3rem, and 1.2rem
on phones — less than the thing they were clearing. The footer height, the
column's geometry and the composer's height are measured by an observer in
FORCE_LIGHT and published as CSS variables. A gr.HTML whose content is fixed
still leaves its wrappers in flow; the footer's added ~52px of empty page, so
they are collapsed.
The shortlist is part of the conversation. It was a gr.render panel pinned
below the chat, which mean…
Repair the edits the web editor mangled, and the paths they broke
YasserHaidar
6e08541
Six files were edited in the Hugging Face web editor. Three things did not
survive that round trip:
- _CJK_RE's private-use-area range '-' was flattened to a bare
'-', so the class no longer stripped PUA characters and did strip a literal
ASCII hyphen from every model reply ("التسجيل المباشر - المفاضلة" came back
without its dash).
- Four files lost their trailing newline.
- LLM_MAX_TOKENS gained a trailing space.
Two paths the LangChain switch left broken:
- engine.build_llm imported llm.langchain_llm unconditionally, but
requirements-deploy.txt (Streamlit Community Cloud, Docker) does not carry
LangChain, so every chat answer there would have fallen to the deterministic
reply. LangChain is added to that manifest, and an ImportError now falls back
to the provider SDKs — the behaviour requirements.txt already claims in its
own comment.
- Four build_llm tests still patched llm.cloud_llm.CloudLLM, which build_llm no
longer calls. They patch the LangChain path inste…