Klienten fra kunnskapsassistenten-frontend erstatter src/apps/web - #129
Draft
larsekhansen wants to merge 22 commits into
Draft
larsekhansen wants to merge 22 commits into
larsekhansen wants to merge 22 commits into
Conversation
Kopi av larsekhansen/kunnskapsassistenten-frontend på 07ccee23b55b48564c905ea121c5ec1aa5dc8e72, i én commit og uten historikken. Historikken står i det repoet. README-en sier det samme øverst. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… BFF-en - Klienten heter @ka/web, og låsefila står bare i src/. - typecheck kjører klientens egen tsc -b. - Hookene finner verktøyene npm løfter til src/node_modules. - Prettier i src/ hopper over det klienten genererer. - CI får en jobb for klienten (lint, tokens:verify og e2e i mock) og en som bygger bildet og spør det. - Bildet bygger klienten mot BFF-en (VITE_API_MODE=bff), ikke mot mocken. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Against this backend every response/chunk is the agent's plan, followed by an agent/thinking with the same words, and the answer arrives whole in the final frame. The deltas were shown as the answer and the final frame was skipped, so /api/ask showed the plan and lost the answer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lent cut The fields were code (type, orgs_long, concerned_years), so the BFF only worked against Kudos, and the client had to be built with the same list. KA_FILTER_FIELDS and KA_DATASETS now say it per dataset, in the client's own grammar, and /api/facets and /api/capabilities hand it on: id, field, value type and label per field, and the dataset's name. - Every value: max_facet_values 2000 and no CAP. Kudos has 457 organisations and 46 years; 200 and 22 came through. - Years: value-type integer on the filter. Without it 2024 finds nothing (0 chunks against 4, measured 29.09). The year span now follows the id. - A field with every known value selected is no filter on it. - More than 100 values in one field is a 400 with a code and a Norwegian sentence, not a quiet cut to 100. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From KA CC's review of dd3b3c1. - /api/ask reads the facets only from the cache, and warms a cold one for the next question. A Typesense that never answered held every question for 10.5 s. The call to Typesense also has a 5 s timeout now. - The cache is keyed by the fields it was fetched for. - A value longer than 256 characters, or with a backtick, a backslash or a control character, is refused with 400 filter-invalid-value, as #15 does, instead of being dropped. - Tests against a Typesense that returns only max_facet_values values, as the real one does: 8 types, 457 organisations and 46 of 1006 year values come through. And a test that /api/capabilities names the dataset. The grammar of KA_FILTER_FIELDS is the client's VITE_KA_FILTER_FIELDS plus an optional fourth part, the label. The client does not read that part, so a label set in VITE_KA_FILTER_FIELDS does nothing there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From KA CC's second review. cachedFacets gave nothing once the cache was ten minutes old, so «all 457 selected» became a 400 on the first question after a quiet spell. It now gives the old cache and fetches a fresh one behind it. The filter handling of /api/ask is askFilter, synchronous so it cannot wait on Typesense, and tested as such. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From KA CC's third review: each call to cachedFacets on an expired cache started a fetch of its own, so ten questions made ten calls to Typesense. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A plan or an allocation letter names the year it runs to, so concerned_years holds years no document is from yet. With 2035 as the fixed end, the filter offered 2027–2035 (measured against Kudos 30.09, 27 to 88 documents each). The end is now the current year in Norway, read on every fetch, so it moves on New Year without a deploy. Oslo and not the server's clock, because the container runs in UTC. The Kudos-shaped test lays its years out from this year, so it says the same thing next year. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From KA CC's review of ca5a158: on a Mac in Norway the test passed with or without the Oslo time zone in currentYear, because the machine's own zone was already Oslo. It now sets TZ to UTC while it runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found by #4 in the pod: a filter keyed by the facet ids (year, documentType) instead of the field names got 200 and an answer from the whole corpus, with nothing to say the filter had gone. cleanFilter now refuses any key that is not in KA_FILTER_FIELDS with 400 filter-unknown-field, naming the key and the fields there are. Our client sends the field names, so this is for other clients. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A timeout or a backend error settled the probe on filters: false for the life of the process. Measured 101 s for the two searches in a row against a 60 s cap. The two now run together, and an attempt that gives no answer is tried again after 1 and 5 minutes before the probe settles on no. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
/assets/ had no Cache-Control, ETag or Last-Modified, so the 1.6 MB bundle could not be cached. Files there now get a year and immutable, index.html gets no-cache, and a name that is not there is 404 instead of index.html. An /api/ path no route takes was 200 with index.html; it is 404 with JSON. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
KA_CAPABILITIES was read by the server and set nowhere. With the probe slower than its cap, filters stayed off in a deployment until someone redeployed; kaCapabilities=filters now says so from the template. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The impossible filter named Kudos' type field, so the probe only meant something against Kudos. It now takes a configured text field, or a year with its value type, and with no fields configured there is nothing to filter on: filters are off without asking the backend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#15 refuses a filter it cannot serialise with a JSON-RPC error and no result, not with isError (measured on 8090). The probe read that as zero chunks, so a refused impossible filter would have said filters: yes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
refused() had tests, but nothing showed the probe used it: with the old isError check back in retrieveOnly all 90 stayed green (KA CC). This one runs probe() over its own fetch with a refused filtered search, and is red with the old check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The agent's [N] is a 1-based index into its flat chunk list. toSources grouped the chunks by document and numbered the documents, so the two agreed only when every document gave exactly one chunk. Measured against kudos-full on 2026-09-29: one question returned eight chunks, all from document 347922, and the answer cited [1] through [8]. The grouped form offered a single source with marker 1, so [2] through [8] resolved to nothing and were drawn as plain text, and [1] pointed at all eight passages joined with a blank line. A chunk with neither doc_num nor url is no longer dropped: dropping one shifts every marker after it. It keeps its place with an empty docNum. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed one The client writes the reader's two sentences from a code, so without one it has to guess from English prose and everything it cannot place becomes the same catch-all sentence. Three things kept a code from reaching it. The backend names the condition in the result's _meta.code (digdir/mcp/tools.clj, error->tool-result). It was read for its text only and the code was dropped. A tools/call that named something nonexistent comes back as a JSON-RPC error with the code in data.code (digdir/mcp/transport.clj, invalid-params). Nothing read those frames at all, so the loop ran out and the reader was told the connection broke. A backend that could not be reached threw out of the generator and became the route's catch-all «Uventet feil mot backend.», which says nothing about what happened. It is now its own event with backend_unreachable, while an abort stays an exception so the route can still tell the two apart. Measured 2026-09-29: with the backend down the reader saw «Svaret kom ikke fram — Tjenesten sa ikke hva», and a faked 404 said the same. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kudos answers them at different addresses (measured 30.09): /documents/<number> is a 301 to the document, /documents/<uuid> is a 404, and /dokument/<uuid> is the document. The Kudos API now gives only UUIDs (headless-rag #25), so a corpus loaded again after that fix has them. A number in neither shape gets no link rather than a guess, which also keeps anything but digits or a UUID out of the path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`stage` says which phase the agent is in and nothing about what it did. That was all this server sent, so the client drew four fixed sentences, while the live client — reading the same backend frames directly — showed the agent's reasoning in its own Norwegian, what each tool call found, and how long it took. Measured against the backend 2026-09-29 with one question: four `agent/thinking` frames with reasoning, and eight tool calls across five `agent/turn-completed` frames, each with `result-summary`, `duration-ms` and either queries or chunk ids. None of it left this server. One `tool-call` event per call and not per frame: a single frame carries several, and three `read_chunks` in a row is ordinary. Nothing here is turned into display text. The sentences the reader sees are Norwegian and belong to the client, which already writes them for the live path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Endringene fra 07ccee23b55b48564c905ea121c5ec1aa5dc8e72 til efe51d9bbdd29ece3cc39813848d913117101948. Det meste rydder i varslene CodeQL fant i denne PR-en: - stisjekken i static.ts med path.relative, med samme oppførsel - verdier fra brukeren skrives med JSON.stringify i loggen - reserven til bruker-id-en bruker crypto.getRandomValues - fjerningen av tagger i utdragene gjentas til teksten står stille - testen for videolenka leser vertsnavnet I tillegg tester for like titler og oppskriften for backenden. README-en peker på den nye commiten. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gjelder #127.
Klienten fra larsekhansen/kunnskapsassistenten-frontend erstatter
src/apps/web, bak BFF-en som står. Utkast, så klientene kan sammenlignes.Klienten kan prøves i testmiljøet: https://ka-frontend-test.bravecoast-68900303.norwayeast.azurecontainerapps.io (krever tilgangslenke). Der går den rett mot headless-rag, ikke gjennom BFF-en.
Commitene
07ccee2, i én commit og uten historikken.@ka/web, én låsefil, hooks og Prettier frasrc/,tsc -bi typecheck, CI for klienten (lint, tokens og e2e i mock) og bildet bygget mot BFF-en./assets/og 404 for ukjente/api/-stierdoc_numKontrakten i
packages/contracthar fått feltene BFF-en sender.