Skip to content

feat(workbench): configurable entity types (global default + per-KB override) - #200

Merged
KylinMountain merged 3 commits into
mainfrom
feat/workbench-entity-types-config
Jul 22, 2026
Merged

feat(workbench): configurable entity types (global default + per-KB override)#200
KylinMountain merged 3 commits into
mainfrom
feat/workbench-entity-types-config

Conversation

@KylinMountain

Copy link
Copy Markdown
Collaborator

Makes the entity-extraction vocabulary (entity_types) user-configurable — a global default plus a per-KB override — surfacing a setting the compiler already honored but that was previously only editable by hand-writing config.yaml.

Backend (92f8f41)

entity_types now flows through the config read/write API, layering global.yaml → KB config.yaml exactly like the other config scalars:

  • a KB list overrides the global list wholesale; an explicit null inherits; unset falls back to DEFAULT_ENTITY_TYPES (person/organization/place/product/work/event/other).
  • GLOBAL_SCALAR_KEYS gains entity_types (the value-not-None-wins layering + per-key sources tracking is type-agnostic, so it works for a list).
  • GET/PATCH /api/v1/kb/config and GET/PATCH /api/v1/config carry entity_types. Reads report the cleaned effective list (resolve_entity_types: lowercased, charset-restricted, deduped, "other" ensured) plus the raw global value for the inherited badge.
  • The compiler already consumed config["entity_types"] via resolve_entity_types; nothing there changed.

Frontend (9abd2bb)

  • EntityTypesEditor: a shared chips editor — Enter/comma to add, × to remove, "other" rendered as a fixed "always included" chip, IME-safe composition.
  • KB settings sheet: an EntityTypesRow with the same inherit/override Switch as the scalar rows — override on seeds + persists the KB's own list, off reverts via null; the inherited state shows the global/default list as a badge. Each chip change persists and adopts the server-cleaned response.
  • Global Settings (general tab): a global chips editor, order-sensitive diff into the save patch (joins the existing dirty/save-bar flow).
  • Both surfaces show a "changes affect future recompiles only; existing entity pages keep their type" note.

Semantics

  • Per-KB list replaces the global list (not merged) — same as a scalar override.
  • "other" is always ensured server-side (the coercion fallback), shown as non-removable.
  • Changing the vocabulary only affects future compiles; existing entity pages keep their type: until recompiled.

Verification

Backend pytest 1240 passed, mypy/ruff clean; new tests cover KB override (cleaned + source kb) + null revert, global patch, and global-source inheritance. Frontend npm run build green (i18n guard: zh/en key sets identical across common/kbSettings/settings). Manually exercised on a running openkb-web.

Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG

… API

entity_types (the entity-extraction vocabulary) is now surfaced through the
config read/write API, layering global.yaml -> KB config.yaml like the other
scalars: a KB list overrides the global list wholesale, an explicit null
inherits, and unset falls back to DEFAULT_ENTITY_TYPES. The compiler already
consumed config["entity_types"] via resolve_entity_types; this just exposes it.

- GLOBAL_SCALAR_KEYS gains "entity_types" (layering + per-key `sources` tracking;
  the value-not-None-wins rule is type-agnostic, so it works for a list).
- _KbConfigWritable / GlobalConfigValues / KbConfigResponse / GlobalConfigResponse
  carry entity_types; read_kb_config/read_global_config report the cleaned
  EFFECTIVE list (resolve_entity_types) plus the raw global value for the badge.
- PATCH /api/v1/kb/config and PATCH /api/v1/config accept entity_types.

Tests: KB override (cleaned + source 'kb') + null revert, global patch, global
inheritance; updated the global-defaults shape assertion. Frontend UI follows.

Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG
…r-KB override)

- EntityTypesEditor: shared controlled chips editor (Enter/comma to add, x to
  remove; "other" is a fixed always-included chip; IME-safe composition).
- KbSettingsSheet: an EntityTypesRow with the same inherit/override Switch as the
  scalar rows — turning override on seeds+persists the KB's own list, off reverts
  via null; inherited state shows the global/default list as a badge. Each chip
  change persists and adopts the server-cleaned response.
- Settings (general tab): a global entity-types chips editor, order-sensitive
  diff into the save patch (joins the existing dirty/SaveBar flow).
- "changes affect future recompiles only" note on both surfaces.

New keys in common/kbSettings/settings (zh + en, identical sets). Build green
(i18n guard OK). Backend was committed in 92f8f41.

Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG
…y-list inherit, silent reads, set-diff

- config: define DEFAULT_ENTITY_TYPES before DEFAULT_CONFIG and add
  `entity_types` to DEFAULT_CONFIG so the key layers like every other
  GLOBAL_SCALAR_KEY and always appears in the effective config (#1).
- config: resolve_effective_config treats an empty entity_types list the
  same as null → inherit, so a KB that cleared its override doesn't pin an
  empty vocabulary (#2).
- config: resolve_entity_types(config, *, warn=True); config-read paths pass
  warn=False so a plain GET doesn't spam coercion warnings (#4).
- api_config: both read paths call resolve_entity_types(..., warn=False).
- KbSettingsSheet: inherited badge shows the effective list, not
  `globalValue ?? effective`; drop the now-unused globalValue prop (#3).
- Settings: global entity_types diff is order-insensitive — the vocabulary
  is a set, so re-adding a removed type is not a change (#6).

Backend pytest 1240 passed; ruff/format/mypy clean; frontend build green.

Claude-Session: https://claude.ai/code/session_01XMxbhmAkxxVV8CFWCZDBaG
@KylinMountain
KylinMountain merged commit ff54396 into main Jul 22, 2026
4 checks passed
@KylinMountain
KylinMountain deleted the feat/workbench-entity-types-config branch July 22, 2026 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant