Files
momentry_core/docs_v1.0/DESIGN/IdentityUpdateAndAliasSystem.md
M5Max128 701e71463d feat: identity PATCH update, alias system, name UNIQUE removal
- Add PATCH /api/v1/identity/:identity_uuid endpoint
- Migration 030: remove name UNIQUE, add tmdb_id index
- TMDb upsert: ON CONFLICT (name) -> ON CONFLICT (tmdb_id)
- get_or_create_identity: pre-check by name
- upload_identity: ON CONFLICT (name) -> ON CONFLICT (uuid)
- Search: include aliases in identity text search
- Add scripts/llm_metadata_enhancer.py
- Add DESIGN/IdentityUpdateAndAliasSystem.md
2026-05-22 08:35:32 +08:00

6.1 KiB

document_type, service, title, version, date, author, status
document_type service title version date author status
design_doc MOMENTRY_CORE Identity Update & Alias System V1.0 2026-05-22 M5 draft

Identity Update & Alias System

Item Value
Scope Identity CRUD expansion, alias system, LLM-enhanced metadata
Status Draft
Key principle uuid is the true identity key; name is display label (no longer UNIQUE)

Overview

Currently, identity records have no update endpoint and name is constrained to UNIQUE. This design adds:

  1. PATCH /api/v1/identity/:identity_uuid — partial update for name, metadata, aliases, status, type
  2. Remove name UNIQUE constraint — allow multiple identities with the same display name
  3. TMDb upsert key changes from name to tmdb_id
  4. Alias system with BCP 47 locale tagging stored in metadata.aliases
  5. LLM background task for metadata structuring and alias generation

Schema Changes

Migration 030

File: migrations/030_remove_identity_name_unique.sql

-- Phase 1: Remove name UNIQUE (keep NOT NULL)
ALTER TABLE identities DROP CONSTRAINT IF EXISTS identities_name_key;

-- Phase 2: Partial unique index for TMDb-sourced identities
CREATE UNIQUE INDEX IF NOT EXISTS idx_identities_tmdb_id
    ON identities(tmdb_id) WHERE tmdb_id IS NOT NULL;

Aliases Storage

Aliases are stored in identities.metadata::jsonb under the key aliases:

{
  "aliases": [
    {"locale": "en", "name": "John Smith"},
    {"locale": "zh-TW", "name": "約翰·史密斯"},
    {"locale": "ja", "name": "ジョン・スミス"}
  ],
  "summary": "American actor and producer...",
  "nationality": "American",
  "birth_date": "1970-01-15",
  "profession": ["actor", "producer"]
}

No schema change needed — metadata JSONB DEFAULT '{}' already supports this.


API Changes

PATCH /api/v1/identity/:identity_uuid

Request:

{
  "name": "John Smith",
  "aliases": [
    {"locale": "en", "name": "John Smith"},
    {"locale": "zh-TW", "name": "約翰·史密斯"}
  ],
  "metadata": {"summary": "American actor..."},
  "status": "confirmed",
  "identity_type": "people"
}

All fields are optional. Only provided fields are updated.

Response (200):

{
  "success": true,
  "identity_uuid": "abc-...",
  "updated_fields": ["name", "aliases", "metadata"]
}

Processing flow:

  1. Lookup identity by UUID → 404 if not found
  2. Dynamic UPDATE SQL (COALESCE for optional fields)
  3. If name changed → update _index.json
  4. If name changed → update Qdrant face point payloads
  5. Call save_identity_file_by_pool() → sync identity.json to disk
  6. Return updated identity detail

Alias System

Locale Tagging (BCP 47)

Standard tags for alias entries:

Locale Tag Example
English en John Smith
Traditional Chinese zh-TW 約翰·史密斯
Simplified Chinese zh-CN 约翰·史密斯
Japanese ja ジョン・スミス
Korean ko 존 스미스
Cantonese yue 約翰·史密夫
French fr Jean Smith
Spanish es Juan Smith

Frontend Display Logic

function getDisplayName(identity, preferredLocale) {
  // 1. Try exact locale match
  const match = identity.aliases?.find(a => a.locale === preferredLocale);
  if (match) return match.name;

  // 2. Try language-only match (zh-TW → zh)
  const lang = preferredLocale.split('-')[0];
  const langMatch = identity.aliases?.find(a => a.locale.startsWith(lang));
  if (langMatch) return langMatch.name;

  // 3. Fallback to identity.name
  return identity.name;
}

Search Integration

Identity search should include aliases:

SELECT * FROM identities
WHERE name ILIKE $1
   OR metadata->'aliases' @> $2::jsonb
ORDER BY name

Upsert Key Changes

Location Current Key New Key Rationale
tmdb/probe.rs ON CONFLICT (name) ON CONFLICT (tmdb_id) WHERE tmdb_id IS NOT NULL TMDb ID is the true identity for TMDb sources
tmdb/ingest.rs ON CONFLICT (name) ON CONFLICT (tmdb_id) Same as above
postgres_db.rs:get_or_create_identity ON CONFLICT (name) DO UPDATE Query by name first, then INSERT with uuid Maintain backward compatibility for name lookup
identity_api.rs:upload_identity ON CONFLICT (name) ON CONFLICT (uuid) Upload provides uuid; uuid is the true key

LLM Metadata Enhancer

Script: scripts/llm_metadata_enhancer.py

Trigger: Called as background task after TMDb registration or manual PATCH.

Input:

{
  "name": "John Smith",
  "biography": "John Smith (born January 15, 1970) is an American actor...",
  "existing_metadata": {"tmdb_id": 123, "tmdb_profile": "..."}
}

Output:

{
  "metadata": {
    "summary": "American actor and producer known for...",
    "nationality": "American",
    "birth_date": "1970-01-15",
    "profession": ["actor", "producer"],
    "aliases": [
      {"locale": "en", "name": "John Smith"},
      {"locale": "zh-TW", "name": "約翰·史密斯"},
      {"locale": "ja", "name": "ジョン・スミス"}
    ]
  }
}

LLM Prompt Design

The prompt defines a fixed output schema for the LLM to follow:

  1. Read biography text + identity name
  2. Extract structured fields: summary, nationality, birth_date, profession
  3. Generate locale-tagged aliases based on known translations
  4. Output as JSON only (no extra text)

Affected Files

File Change Complexity
migrations/030_remove_identity_name_unique.sql New Low
src/api/identity_api.rs Add PATCH route + handler Medium
src/core/tmdb/probe.rs Change upsert key Low
src/core/db/postgres_db.rs Change get_or_create_identity Low
src/api/identity_api.rs (upload) Change upsert key Low
scripts/llm_metadata_enhancer.py New script Medium
src/api/tmdb_api.rs or background LLM task integration Low

Version History

Version Date Author Description
V1.0 2026-05-22 M5 Initial design